Skip to content

Instantly share code, notes, and snippets.

@aldoyh
Created April 24, 2024 00:16
Show Gist options
  • Save aldoyh/ae0300c523e31d28a1fb0dac8f4a8d72 to your computer and use it in GitHub Desktop.
Save aldoyh/ae0300c523e31d28a1fb0dac8f4a8d72 to your computer and use it in GitHub Desktop.
Leonardo.ai Codes
<?php
namespace App\Console\Commands;
use App\Models\Artwork;
use App\Models\Image;
use App\Services\LeonardoService;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Casts\Json;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Helper\TableStyle;
use Illuminate\Support\Str;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\info;
use function Laravel\Prompts\intro;
use function Laravel\Prompts\outro;
use function Laravel\Prompts\progress;
use function Laravel\Prompts\select;
use function Laravel\Prompts\spin;
use function Laravel\Prompts\table;
use function Laravel\Prompts\text;
use function Psy\debug;
class LeonardoGenerationsCommand extends Command
{
private $path;
private $upscaled_image;
private $generation;
protected $count = 0;
protected $generations = [];
protected $leonardo;
protected $apiKey;
protected $url;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'leo {cmd?} {id?} {--count=} {--all}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Run a new prediction using Leonardo.ai API, list all latest predictions, or get a single prediction by id.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
$this->leonardo = new LeonardoService();
$this->apiKey = config('services.leo.key');
$this->url = config('services.leo.url');
$this->count = 10;
}
/**
* Execute the console command.
*/
public function handle()
{
if (empty($this->apiKey)) {
$this->error('LEONARDO_API_KEY environment variable is not set');
}
$cmd = $this->argument('cmd');
$leonardo = new LeonardoService();
$this->generation = $this->argument('id') ?? null;
$this->count = $this->option('count') ?? $this->count;
$user_info = Cache::remember('user_info', 15 * 60, function () use ($leonardo) {
$user = $leonardo->getUserInfo();
$user['renewalDays'] = Carbon::parse($user['tokenRenewalDate'])->diffInDays();
$user['credits'] = $user['subscriptionTokens'];
return $user;
});
intro("Welcome to Leonardo.ai CLI 🧙🏻‍♂️ 🪄 ♌️");
intro("User info: " . $user_info['user']['username'] . " having a total of " . $user_info['subscriptionTokens'] . " credits and " . $user_info['renewalDays'] . " days left.");
/**
* LIST & UPSCALE COMMAND
*
*
*
*
*
*
*
*
*
*
*/
if ($cmd == 'list') {
// $this->info('User info:');
// $this->table(
// ['ID', 'Credits', 'Days Left'],
// [
// ['ID' => $user_info['user']['id']],
// ['Credits' => $user_info['subscriptionTokens']],
// ['Days Left' => $user_info['renewalDays']],
// ]
// );
$latest_gens = $leonardo->getGenerations();
$latest_gens = array_shift($latest_gens);
$ctr = 0;
$this->alert('Total generations: ' . count($latest_gens));
$rows = [];
foreach ($latest_gens as $gen) {
$gen_job = $gen['generated_images'];
foreach ($gen_job as $job) {
$url = count($job['generated_image_variation_generic']) ? $job['generated_image_variation_generic'][0]['url'] : $job['url'];
$rows[] = [
$ctr++,
$gen['id'],
$job['id'],
Carbon::parse($gen['createdAt'])->diffForHumans(),
($gen['photoReal'] ? '📷' : ''),
count($job['generated_image_variation_generic']) ? '🔭' : '',
// shortened url
// str($url)->limit(50),
$url,
];
}
}
$this->info('Total images: ' . count($rows));
// $this->table(
// ['#', 'ID', 'PID', 'Created At', 'photoReal'],
// $rows
// );
foreach ($rows as $row) {
// $this->info($row[0] . ' ' . $row[1] . ' ' . $row[2] . ' ' . $row[3] . ' ' . $row[4] . ' ' . $row[5] . ' ' . $row[6]);
// $row['full_url'] = new TableCell($row[6], ['colspan' => 2]);
unset($row[6]);
table(
[
'#',
'ID',
'PID',
'Created At',
'photoReal',
'Variations',
'URL'
],
[$row]
);
}
// upscale each image
if (
confirm(
label: 'Do you want to upscale the images?',
default: true,
)
) {
foreach ($rows as $row) {
$this->info('Upscaling image: ' . $row[2] . '...');
$response = $leonardo->upscaleImage($row[2]);
sleep(2);
$generationId = $response['sdUpscaleJob']['id'];
$this->info('Upscaled image: ' . $generationId . '...');
$this->alert('Getting upscaled image: ' . $generationId . '...');
$response = $leonardo->getVariations($generationId);
while (!isset($response['generated_image_variation_generic'][0]['url'])) {
$this->info('Waiting for variations to be generated333...' . print_r($response, true));
sleep(5);
$response = $leonardo->getVariations($generationId);
if (isset($response['generated_image_variation_generic'][0]['url'])) {
$this->info('URL: ' . $response['generated_image_variation_generic'][0]['url']);
break;
}
}
$gen_url = $response['generated_image_variation_generic'][0]['url'];
$filename = Str::afterLast($gen_url, '/');
if (empty($filename) || empty($gen_url) || $gen_url == $filename) {
$this->error('Invalid URL: ' . $gen_url . ' ID: ' . $generationId . '...');
continue;
}
$upscale_image_path = $leonardo->downloadImage(
$gen_url,
$filename
);
Artwork::create([
'prompt' => $row[1],
'model_id' => $row[1],
'predict_id' => $row[2],
'user_id' => 1,
'is_public' => false,
'status' => 'upscaled',
'gen_id' => $generationId,
'path' => $upscale_image_path,
'data' => $response,
'published' => false,
'published_at' => Carbon::now(config('app.timezone')),
// 'tags' => $generation['tags'],
])->save();
$this->info('Saved image: ' . $generationId . ' to storage...');
}
outro(
'<fg=green>Done upscaling images...</>'
);
}
/**
* UPSCALE COMMAND
*
*
*
*
*
*
*
*/
} elseif ($cmd == 'upscale' or $cmd == 'up') {
intro("Upscaling image...🧙🏻‍♂️ 🪄 ♌️");
// TODO: get the selected images from the database
// $selected = (new LeonardoService())->getSelectedImages();
$id = $this->argument('id');
if (!$id) {
/**
*
* Display a table of selected images
*
*
*/
// $this->table(
// ['Image ID'],
// [$selected]
// );
// $id = $this->choice(
// 'Which image do you want to upscale?',
// $selected,
// 0,
// 3,
// true
// );
// $this->info('You selected: ' . is_array($id) ? implode(', ', $id) : $id);
$this->info('Upscaling latest image generated...');
// $last_image = Image::where('gen_id', '!=', null)->orderBy('id', 'desc')->first();
// $last_image = Artwork::where('predict_id', '!=', null)
// $last_image = Image::where('predict_id', '!=', null)
$last_image = Image::where('status', '!=', 'upscaled')
->orderByDesc('id')
->first();
if (!$last_image) {
$this->error('No images found in the database...(checking jsons/gens#.json)');
$this->generations = $leonardo->getGenerations();
} else {
$this->info('Last image: ' . $last_image->gen_id);
$this->generations = [
$leonardo->getSingleGeneration($last_image->predict_id)
];
}
$gens = (count($this->generations) == 1) ? array_shift($this->generations) : $leonardo->getGenerations();
$gensOptions = [];
$this->info(
PHP_EOL . PHP_EOL
. '<fg=cyan>Total generations: ' . count($this->generations) . '</>'
);
$gensOptions[0] = 'All ⏳ 🔴';
foreach ($gens as $gen) {
$gensOptions[$gen['id']] = Str::limit($gen['prompt'], 10) . ' 🗓️ ' . Carbon::parse($gen['createdAt'])->diffForHumans();
}
// check for -a pr --all option to upscale all images
if ($this->option('all')) {
$this->info('Upscaling all images...');
$selectedGens = 0;
} else {
$selectedGens = select(
'Select the generations to upscale',
$gensOptions,
0,
15,
null,
'Select a generation to upscale'
);
}
// $this->info('You selected: ' . $selectedGens);
if ($selectedGens == 0) {
$this->info('Upscaling all generations...');
$gens = $leonardo->getGenerations();
$gens = array_shift($gens);
} else {
$gens = $leonardo->getSingleGeneration($selectedGens);
}
foreach ($gens as $singleGen) {
if (!is_array($singleGen)) {
$this->warn('Image: ' . $singleGen . '...');
}
$gens = $leonardo->getSingleGeneration($singleGen['id']);
$prompt = $gens['prompt'];
$modelId = $gens['modelId'];
$predictId = $gens['id'];
$file_ext = pathinfo($gens['generated_images'][0]['url'], PATHINFO_EXTENSION);
$userId = 1;
foreach ($gens['generated_images'] as $gen) {
$upscaleGenId = $this->canUpscaleGen($gen);
if (!$upscaleGenId) {
$this->info('😲 Image already upscaled: ' . $gen['id']);
continue;
}
$upscaledReq = $leonardo->checkImageVariations($upscaleGenId);
sleep(1);
$counter = 0;
$upscaledReq = Arr::first($upscaledReq);
do {
$upscaledReq = $leonardo->checkImageVariations($upscaleGenId)[0];
sleep(2);
$this->info($counter++ . PHP_EOL . ' Waiting for variations to be generated...' . print_r($upscaledReq, true));
} while (isset($upscaledReq['status']) && $upscaledReq['status'] == 'PENDING');
$var_generated = $leonardo->checkImageVariations($upscaleGenId)[0];
$filename = Arr::last(explode('/', $var_generated['url']));
$this->path = $leonardo->downloadImage(
$var_generated['url'],
$filename,
null,
$gens['createdAt']
);
$this->info('Saving 🎑 ' . $filename . PHP_EOL
. '🔗 ' . $var_generated['url'] . PHP_EOL);
// $image = Image::where('gen_id', $variationId)->first();
$image_record = Artwork::where('gen_id', $var_generated['id'])->first();
if (!$image_record) {
$this->info('📇 Adding to DB ...');
$artwork = [
'prompt' => $prompt,
'model_id' => $modelId,
'predict_id' => $predictId,
'user_id' => 1,
'is_public' => false,
'status' => 'UPSCALED',
'gen_id' => $var_generated['id'],
'path' => 'downloaded_images/' . $filename,
'data' => $var_generated,
'published' => false,
'published_at' => Carbon::now(config('app.timezone')),
// 'tags' => $generation['tags'],
];
Artwork::create($artwork)
->saveOrFail();
$this->info('Saved ' . $var_generated['id'] . ' to database...');
} else {
// update the path if the image already exists
$file_path = 'downloaded_images/' . $filename;
if (File::exists(storage_path('app/public/' . $file_path))) {
$this->info("📯 Dumper skipped " . $filename . " because it already exists!!");
continue;
} else {
// download the image from the URL and save it to the public disk
$this->path = $leonardo->downloadImage($var_generated['url'], $filename);
$this->info("📯 Dumper saved " . $filename . " to " . $this->path);
$image_record->save();
}
}
$this->info('Saved ' . $var_generated['id'] . ' to storage...');
}
}
} else { // ******** UPSCALING AN IMAGE w/ ID ********
intro("Upscaling image ID $id...🧙🏻‍♂️ 🪄 ♌️");
$response = $leonardo->getSingleGeneration($id);
$this->info('Upscaling image under PredictionID: ' . $response['id'] . ' having a total of ' . count($response['generated_images']) . ' images');
foreach ($response['generated_images'] as $image) {
if (count($image['generated_image_variation_generics'])) {
$this->info('😲 Image already upscaled: ' . $image['id']);
continue;
}
$this->info('Upscaling image: ' . $image['id'] . '...');
$upscale_req = $leonardo->upscaleImage($image['id']);
$this->info('Upscaled image: ' . $image['id'] . '...');
while (!count($leonardo->getVariations($image['id']))) {
$this->info('Waiting for variations to be generated11...' . print_r($upscale_req, true));
sleep(2);
}
$upscaleGenId = $leonardo->checkImageVariations($image['id']);
// foreach ($upscale_resp['generated_image_variation_generics'] as $variation) {
// $this->info('Downloading image: ' . $variation['id'] . '...');
// $this->path = $leonardo->downloadImage($variation['url'], $variation['id'] . '.jpg');
// $this->info('Saved image: ' . $variation['id'] . ' to storage...');
// $this->info('Saving to database...');
// // $image = Image::where('gen_id', $variation['id'])->first();
// // Save to Artworks
// $image = Artwork::where('gen_id', $variation['id'])->first();
// if (!$image) {
// $this->info('Image does not exist in database...');
// $image = Artwork::create([
// 'prompt' => $image->prompt,
// 'model_id' => $image->modelId,
// 'predict_id' => $image->id,
// 'user_id' => 2,
// 'is_public' => false,
// 'status' => 'upscaled',
// 'gen_id' => $variation['id'],
// 'path' => 'downloaded_images/' . $variation['id'] . '.jpg',
// 'data' => $variation,
// // 'tags' => $generation['tags'],
// ]);
// $this->info('Saved ' . $variation['id'] . ' to database...');
// } else {
// $this->info('Image already exists in database...');
// }
// }
$filename = Arr::last(explode('/', $upscaleGenId['url']));
$this->info('Downloading image: ' . $upscaleGenId . '...');
$upscaleJob = $leonardo->checkImageVariations($upscaleGenId);
$this->path = $leonardo->downloadImage(
$upscaleJob['url'],
$filename
);
$this->info('Saved image: ' . $upscaleGenId . ' to storage...');
}
}
/**
* THE UPLOAD COMMAND
*
*
*
*
*
*/
} elseif ($cmd == 'upload' or $cmd == 'u') {
intro("Uploading image...🧙🏻‍♂️ 🪄 ♌️");
$this->showCredits();
$this->info('Uploading image...');
$images_path = $this->ask(
'Enter the path to the image:',
public_path('assets/stands/')
);
$this->info('You entered: ' . $images_path);
$images = glob($images_path . '*.{jpg,png,gif}', GLOB_BRACE);
$this->info('🎞️ Total images: ' . count($images));
$selected_images = [];
foreach ($images as $image) {
if (
Artwork::where('url', 'NOT LIKE', '%i.ibb.co%')
->where('path', $image)
->first()
) {
$this->info('<fg=bright-magenta>Image already exists in database: ' . $image . '</>');
continue;
}
$selected_images[] = $image;
}
$this->info('Total images: ' . count($selected_images)
. ' Maximum: ' . $this->count);
if (
confirm(
label: 'Do you want to upload the images?',
default: true,
)
) {
foreach ($selected_images as $image) {
if ($this->count-- <= 0) {
break;
}
if (Artwork::where('url', 'LIKE', '%' . $image . '%')->first()) {
$this->info('<fg=purple>Image already exists in database: ' . $image . '</>');
// check if the image exists in the storage
if (!Storage::disk('public')->exists($image)) {
$this->info('<fg=red>Image does not exist in storage: ' . $image . '</>');
} else {
$this->info('<fg=green>Image exists in storage: ' . $image . '</>');
}
continue;
}
$this->info('Uploading image: ' . $image . '... ' . $this->count . ' left');
$response = $leonardo->uploadImage($image);
$this->info('<fg=blue>'
. 'Uploaded image: ' . $response['data']['url']
. '</>');
// update the image url in the database
$artwork = Artwork::where('path', $image)->first();
if ($artwork) {
$artwork->url = $response['data']['url'];
$artwork->save();
} else {
$this->info('<fg=bright-cyan>'
. 'Image not found in database: ' . $image);
$uploadedId = $response['data']['id'];
$uploadedUrl = $response['data']['url'];
$uploadedViewUrl = $response['data']['url_viewer'];
Artwork::create([
'name' => basename($image),
'path' => $image,
'url' => $uploadedUrl,
'user_id' => 1,
'prompt' => $uploadedId,
'predict_id' => $response['data']['id'],
'gen_id' => $response['data']['id'],
'is_public' => false,
'model_id' => 1,
'data' => $response['data'],
])
->save();
$this->info('<fg=bright-magenta>Saved image: ' . $uploadedId . ' to storage...' . PHP_EOL
. 'URL: ' . $uploadedUrl . PHP_EOL
. 'View URL: ' . $uploadedViewUrl . PHP_EOL
. '</>');
}
}
}
} else {
$gens = $leonardo->getGenerations();
$this->info('<fg=bright-red>🧙🏻‍♂️ 🪄 ♌️</> <fg=yellow>Leonardo.ai CLI</> <fg=bright-green>🧙🏻‍♂️ 🪄 ♌️</>');
// if (confirm(
// label: 'Do you want to save the generations to a JSON file?',
// default: true,
// )) {
// $grand_spinner = new Spinner("Saving a total of " . count($gens) . " generations to a JSON file");
Storage::disk('public')->put("jsons/generations.json", json_encode($gens));
// }
// Storage::disk('public')->put("jsons/generations.json", json_encode($generations));
$table_data = [];
$db_table = [];
$downloaded_images = [];
$ctr = 0;
$total_gens = count($gens);
$count = $this->count;
// foreach ($generations as $page => $gen) {
// info("Processing page: $page");
$gens = array_slice($gens, 0, $this->count);
foreach ($gens as $pages) {
intro("Processing page: " . $ctr++ . " with total images: " . count($pages) . " and total gens: $total_gens ");
foreach ($pages as $generation) {
$generated_images = $generation['generated_images'];
$prompt = $generation['prompt'];
$createdAt = $generation['createdAt'];
// $photoReal = $generation['photoReal'];
// $images = array_map(function ($image) use ($generation, $total_gens, $count) {
foreach ($generated_images as $image) {
// download the image from the URL and save it to the public disk
$filename = Arr::last(explode('/', $image['url']));
if (Artwork::where('gen_id', $image['id'])->first()) {
$this->info("Image already exists in DB: " . $image['id']);
$file_path = 'downloaded_images/' . $filename;
if (!Storage::disk('public')->exists($file_path)) {
$this->info("Image does not exist in storage: " . $file_path);
$this->path = $this->leonardo->downloadImage(
$image['url'],
$filename,
null,
$createdAt
);
} else {
info("Image exists in storage: " . $file_path);
$this->info("<bg=green>Image exists in storage: " . $file_path . "</>");
$this->info("📯 Dumper skipped " . $filename . " because it already exists!!");
}
} else {
$this->path = $this->leonardo->downloadImage(
$image['url'],
$filename,
null,
$createdAt
);
$this->info("📯 Dumper saved " . $filename . " to " . $this->path);
$this->info("📇 Adding to DB ...");
Artwork::create([
'prompt' => $prompt,
'model_id' => $generation['modelId'],
'predict_id' => $generation['id'],
'user_id' => 1,
'is_public' => false,
'status' => $generation['status'],
'gen_id' => $image['id'],
'path' => 'downloaded_images/' . $filename,
'data' => $generation,
// 'tags' => $generation['tags'],
])->saveOrFail();
$this->info("Saved " . $image['id'] . " to database...");
// $db_table[] = [
// 'prompt' => $prompt,
// 'model_id' => $generation['modelId'],
// 'predict_id' => $generation['id'],
// 'user_id' => 1,
// 'is_public' => false,
// 'status' => $generation['status'],
// 'gen_id' => $image['id'],
// 'path' => 'downloaded_images/' . $filename,
// 'data' => $generation,
// // 'tags' => $generation['tags'],
// ];
}
}
}
}
outro("Done saving to database...Total: " . count($db_table));
}
}
/**
*
*
*/
public function canUpscaleGen(array $gen)
{
if ($gen['nsfw'] === true) {
Log::info('NSFW image');
return false;
}
if (isset($gen['generated_image_variation_generic']) && $gen['generated_image_variation_generic'] !== null) {
return array_shift($gen['generated_image_variation_generic']);
}
$genId = $gen['id'];
$upscalingReqId = $this->leonardo->upscaleImage($genId);
sleep(2);
$upscaleGenId = $upscalingReqId['id'];
$upscale = $this->leonardo->checkImageVariations($upscaleGenId);
$counterr = 0;
while (isset($upscale['status']) && $upscale['status'] !== 'COMPLETED') {
Log::info('Waiting for variations to be generated...' . print_r($upscale, true) . ' Counter: ' . $counterr++);
sleep(5);
$upscale = $this->leonardo->checkImageVariations($upscaleGenId);
}
return $upscale;
}
/**
* to be decided later!!
*
* @param string $url
*/
public function getsAllGensbyTheUser()
{
// list all generations for the user
$leonardo_user_info = new LeonardoService();
$leo_user = $leonardo_user_info->getUserInfo();
$this->info('User info:');
$this->table(
['Tokens', 'GptTokens', 'RenewalDate', 'DaysLeft'],
[
// $leo_user['user']['id'],
// $leo_user['user']['username'],
// $leo_user['subscriptionTokens'],
// $leo_user['subscriptionGptTokens'],
// $leo_user['tokenRenewalDate'],
// ['ID' => $leo_user['user']['id']],
// ['Name' => $leo_user['user']['username']],
['Tokens' => $leo_user['subscriptionTokens']],
['GptTokens' => $leo_user['subscriptionGptTokens']],
['RenewalDate' => $leo_user['tokenRenewalDate']],
['DaysLeft' => Carbon::parse($leo_user['tokenRenewalDate'])->diffInDays()],
]
);
$leo_id = $leo_user['user']['id'];
$this->info('Getting all generations for user: ' . $leo_id);
$leonardo = new LeonardoService();
$pages = 10;
$offset = 1;
$generations = [];
for ($i = 0; $i < $pages; $i++) {
$this->info('Getting generations page: ' . $i . ' offset: ' . $offset);
$response = $leonardo->getGenerations($offset ?? null);
File::put(database_path('jsons/gens' . $i . '.json'), json_encode($response['generations']));
$generations[] = $response['generations'];
Log::info('Getting generations page: ' . $i . ' offset: ' . $offset);
$offset = 50 * $i;
}
// merge the first level of arrays into one
$generations = array_merge(...$generations);
$this->info('Total generations: ' . count($generations));
return $generations;
}
/**
* Performs the upscale operation
*
* @param string $image_id
*/
public function upscaleSingleImage($predict_id)
{
// spin(
// function () use ($image_id) {
// $leonardo = new LeonardoService();
// $response = $leonardo->upscaleImage($image_id);
// $this->upscaled_image = $response['generated_image_variation_generic'];
// return $response;
// },
// 'Upscaling image: ' . $image_id,
// );
$leonardo = new LeonardoService();
$response = $leonardo->upscaleImage($predict_id);
// show a progress bar for the download
progress(
label: 'Upscaling image: ' . $predict_id,
steps: 10,
callback: function ($step) use ($predict_id, &$leonardo, &$response) {
$upscaleGenId = $leonardo->checkImageVariations($predict_id);
$this->info('Waiting for variations to be generated...' . print_r($response, true) . ' Step: ' . $step);
sleep(2);
$response = $leonardo->getVariations($predict_id);
if (isset ($response['generated_image_variation_generic'][0]['url'])) {
$this->info('URL: ' . $response['generated_image_variation_generic'][0]['url']);
}
return $upscaleGenId;
},
);
$upscaled_image = $this->upscaled_image ?? [];
if (!count($upscaled_image)) {
$this->error('🔴 Error upscaling ');
return;
}
foreach ($upscaled_image as $ups) {
$this->info('🔭 Upscaled ' . $ups . '...');
// debug here
Storage::disk('public')->put("downloaded_images/" . $ups . '.jpg', Http::get($ups['url']));
$this->info('✅ Saved ' . $ups['id'] . ' to storage...');
$this->info('🔭 Saving to database...');
// $image = Image::where('gen_id', $ups['id'])->first();
$image = Artwork::where('gen_id', $ups['id'])->first();
if (!$image) {
$this->info('🔭 Image does not exist in database...');
$image = Artwork::create([
'prompt' => $image->prompt,
'model_id' => $image->modelId,
'predict_id' => $image->id,
'user_id' => 1,
'is_public' => false,
'status' => 'upscaled',
'gen_id' => $ups['id'],
'path' => 'downloaded_images/' . Arr::last(explode('/', $ups['url'])),
'data' => $ups,
// 'tags' => $generation['tags'],
]);
$this->info('✅ Saved ' . $ups['id'] . ' to database...');
} else {
$this->info('🔴 Image already exists in database...');
$image->save();
}
}
}
/**
* Show the user's credits
*/
public function showCredits()
{
$user_info = $this->leonardo->getUserInfo();
$this->info('User info:');
$this->table(
['ID', 'Credits', 'Days Left'],
[
['ID' => $user_info['user']['id']],
['Credits' => $user_info['subscriptionTokens']],
['Days Left' => Carbon::parse($user_info['tokenRenewalDate'])->diffInDays()],
]
);
}
}
<?php
// namespace App\Providers;
namespace App\Services;
use App\Mail\NotifyAdminMail;
use App\Models\Artwork;
use App\Models\Image;
use App\Models\Prompt;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Imagick\Color;
use Symfony\Component\Console\Helper\ProgressBar;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\outro;
use function Laravel\Prompts\progress;
use function Laravel\Prompts\warning;
use function Termwind\ask;
class LeonardoService
{
private $apiKey;
private $apiUrl;
protected $client;
private $authToken;
public $userId;
public function __construct()
{
$this->client = new \GuzzleHttp\Client();
$this->apiKey = config('services.leo.key');
$this->apiUrl = config('services.leo.url');
$this->userId = config('services.leo.user_id');
// $this->authToken = env('LEONARDO_API_TOKEN');
}
public static function getUserId()
{
return config('services.leo.user_id');
}
/**
* Make an API request to Leonardo
*
* @param string $prompt
* @return array
*/
public function makeGenerationRequest($prompt, $presetStyle = 'CINEMATIC', $numImages = 4)
{
if (!$prompt) {
Log::error('No prompt provided');
// getting a random one
$prompt = Prompt::inRandomOrder()->first()->prompt;
}
$response = $this->makeApiRequest('/generations', [
'height' => 1360,
'width' => 768,
'prompt' => $prompt,
'alchemy' => true,
'photoReal' => true,
'photoRealStrength' => 0.5,
'presetStyle' => $presetStyle,
'num_images' => $numImages,
], true);
// Process the API response and return the generated image
// Replace this with your actual response processing code
// $image = $this->processApiResponse($response);
return $response;
}
public function getUserInfo()
{
$user_details = $this->makeApiRequest('/me', [], false);
return $user_details['user_details'][0];
}
public function postGenerations($params)
{
return $this->makeApiRequest('/generations', $params, true);
}
public function getSingleGeneration($generationId): array
{
return $this->makeApiRequest('/generations/' . $generationId, [], false);
}
public function generateImage($prompt)
{
// Get the prompt from the request
if (!$prompt) {
Log::error('No prompt provided');
$prompt = Prompt::inRandomOrder()->first()->prompt;
Log::info('Using random prompt: ' . $prompt);
if (!$prompt) {
Log::error('No prompt found');
return;
}
}
// Make API request to generate image using the prompt
// Replace this with your actual API request code
$response = $this->makeApiRequest('/generations', [
'height' => 1360,
'width' => 768,
'prompt' => $prompt,
'alchemy' => true,
'photoReal' => true,
'photoRealStrength' => 0.3,
'presetStyle' => 'CINEMATIC',
], true);
// Process the API response and return the generated image
// Replace this with your actual response processing code
$image = $this->processApiResponse($response);
// TODO: save the image to the database and return the image URL
// dd($image);
// send a notification mail to the Admin
// Mail::to('doydevlabs@gmail.com')
// ->send(new NotifyAdminMail());
return $image;
}
public static function getGenerations($offset = 0)
{
// /generations/user/userId?offset=0&limit=10
$url = '/generations/user/' . self::getUserId() . '?offset=' . $offset . '&limit=50';
Log::info('URL: ' . $url);
$gens = self::makeApiRequest($url, [
'offset' => $offset,
'limit' => 50,
], false);
return $gens;
}
/**
* Makes an API request to the Leonardo API. if methos is true then it is a POST request.
*
* @param mixed $endpoint
* @param mixed $data
* @param mixed $method
* @return mixed
*/
public static function makeApiRequest($endpoint, $data, $method = false)
{
// Make the API request using the provided endpoint and data
// Replace this with your actual API request code
$apiKey = config('services.leo.key');
$apiUrl = config('services.leo.url');
$userId = config('services.leo.user_id');
if (!$apiUrl) {
Log::error('No API URL found');
return;
}
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $apiKey,
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'User-Agent' => 'RedScarf AI v1.0 by HD@doy.tech (https://doy.tech)',
'X-User-Id' => $userId,
]);
if ($method) {
$response = $response->post($apiUrl . $endpoint, $data);
} else {
$response = $response->get($apiUrl . $endpoint, $data);
}
sleep(2);
// Log::info('Method: ' . $method ? 'P' : 'G' . ' Endpoint: ' . $endpoint . ' Data: ' . print_r($data, true));
// $response_chunk = $response->collect();
if ($response->status() !== 200) {
dump('🚧 Error: ' . $response);
Log::error('Error: ' . $response->status());
return;
}
$response_chunk = $response->json();
if (isset($response_chunk['sdGenerationJob']['generationId'])) {
$generationId = $response_chunk['sdGenerationJob']['generationId'];
Log::info('GenerationId: ' . $generationId);
return [
'generationId' => $generationId,
'prompt' => $data['prompt'],
'status' => 'STARTED',
];
} elseif (isset($response_chunk['generations_by_pk'])) {
$response = $response_chunk['generations_by_pk'];
$generationId = $response['id'];
Log::info('GenerationId: ' . $generationId);
return [
'generationId' => $generationId,
'prompt' => $response['prompt'],
'status' => $response['status'],
'generated_images' => $response['generated_images'],
...$response
];
} else {
return $response_chunk;
}
}
/**
* Downloads the image from the URL and saves it to the public disk
*
* @param string $url
* @param string $filename
* @return string
*/
public function downloadImage($url, $filename, $folder = 'downloaded_images')
{
// Download the image from the URL and save it to the public disk
// Replace this with your actual image download code
try {
$image = Http::get($url, [
'User-Agent' => 'RedScarf AI v1.0 by HD',
]);
} catch (\Exception $e) {
Log::error('Error downloading image: ' . $filename);
return null;
}
$path = Storage::disk('public')->put("$folder/$filename", $image);
if (!$path) {
Log::error('Error saving image: ' . $filename);
return null;
}
Log::info('Image saved: ' . $filename);
return $folder . '/' . $filename;
}
/**
* Process the API response and return the generated image
* @param mixed $response
* @return mixed
*/
private function processApiResponse($response)
{
return json_decode($response, true);
}
/**
* Downloads the generated images and saves them to the public disk
*
* @param array $images
*/
public static function downloadImages($generation_resp, $user_id = 2)
{
if (!$generation_resp || !isset($generation_resp['generated_images']) || !isset($generation_resp['prompt'])) {
info('No generation_resp provided');
return;
}
$gen_images = $generation_resp['generated_images'];
$prompt = $generation_resp['prompt'];
// $photoReal = $generation_resp['photoReal'];
foreach ($gen_images as $image) {
$filename = basename($image['url']);
$path = storage_path('app/public/gen_images/' . $filename);
if (!file_exists($path)) {
Storage::disk('public')->put("gen_images/$filename", Http::get($image['url']));
$image = [
'id' => $image['id'],
'gen_id' => $image['id'],
'modelId' => $generation_resp['modelId'],
'prompt' => $prompt,
'predict_id' => $generation_resp['id'],
'is_public' => false,
'user_id' => $user_id,
'name' => $filename,
'url' => $image['url'],
'created_at' => $generation_resp['createdAt'],
];
// Image::create($image);
$artwork = Artwork::create($image);
Log::info('👍 Image downloaded: ' . $artwork->id);
} else {
Log::info("👎 Image already exists: " . $filename);
}
}
}
/**
* Request an upscale of the image
*
* @param string $imageId
*/
public static function upscaleImage($imageId)
{
if (!$imageId) {
info('No imageId provided');
return;
}
$leo = new LeonardoService();
$response = $leo->makeApiRequest('/variations/upscale', [
'id' => $imageId,
], true);
if (isset($response['sdUpscaleJob'])) {
return $response['sdUpscaleJob'];
} else {
return $response;
}
}
/**
* Check the image variations
*
*
* @param string $requestId
*/
public static function checkImageVariations($requestId)
{
if (!$requestId) {
info('No requestId provided');
return;
}
if (is_array($requestId)) {
if (
isset($requestId['generated_image_variation_generic'])
&& count($requestId['generated_image_variation_generic'])
) {
return $requestId['generated_image_variation_generic'];
} else {
return $requestId;
}
} else {
$leo = new LeonardoService();
$response = $leo->makeApiRequest('/variations/' . $requestId, []);
sleep(2);
}
if (isset($response['generated_image_variation_generic']) && count($response['generated_image_variation_generic'])) {
return $response['generated_image_variation_generic'];
} else {
return $response;
}
}
/**
* gets all images from all generations and saves them to the public disk
*
* @param array $images
*/
public static function downloadAllImages($generations)
{
$images = [];
foreach ($generations as $generation) {
$images = array_merge($images, $generation['generated_images']);
}
$images = array_map(function ($image) {
return $image['url'];
}, $images);
$images = array_unique($images);
info('Total images: ' . count($images));
$progress_bar = progress(
label: 'Downloading images...',
steps: count($images),
callback: function ($user) use ($images) {
$bar = progress(
label: 'Downloading images...',
steps: count($images),
callback: function ($user) use ($images) {
foreach ($images as $image) {
$filename = basename($image);
$path = storage_path('app/public/gen_images/' . $filename);
if (!file_exists($path)) {
file_put_contents($path, Http::get($image));
}
}
},
);
},
hint: 'This shouldnt take long ...',
);
}
/**
* Gets a list of selected images to mark as public
*
*/
public static function getSelectedImages()
{
$glob_pattern = storage_path('app/public/gen_images/*stand*');
// Log::info('glob_pattern: ' . $glob_pattern);
$selectedImages = glob($glob_pattern);
$selectedImages = array_map(function ($image) {
return basename($image);
}, $selectedImages);
$selectedImages = array_map(function ($image) {
return str_replace('.jpg', '', $image);
}, $selectedImages);
return $selectedImages;
}
/**
* Marks the selected images as public
*
*/
public static function markSelectedImagesAsPublic($makePublic = true)
{
$selectedImages = self::getSelectedImages();
$images = Image::whereIn('name', $selectedImages)->get();
$images->each(function ($image) use ($makePublic) {
// $image->is_public = $makePublic;
$image->is_public = !$image->is_public;
$image->save();
});
Log::info('Images marked as public: ' . count($images));
}
/**
* Gets the variations by ID
*
* @param string $variationId
*/
public static function getVariations($variationId)
{
if (!$variationId) {
info('No variationId provided');
return;
}
$leo = new LeonardoService();
$response = $leo->makeApiRequest('/variations/' . $variationId, []);
return $response['sdUpscaleJob'];
}
/**
*
* checks the status of the generation
*
* @param string $generationId
*/
public static function checkGenerationStatus($generationId)
{
if (!$generationId) {
info('No generationId provided');
return;
}
$leo = new LeonardoService();
$response = $leo->makeApiRequest('/generations/' . $generationId, []);
return $response['status'];
}
/**
* Uploads the image to imgBB
*
* Sample rsonse;
*
*
// array:3 [
// "data" => array:14 [
// "id" => "MCr8gCg"
// "title" => "3ad6a1c1a396"
// "url_viewer" => "https://ibb.co/MCr8gCg"
// "url" => "https://i.ibb.co/nL93wLw/3ad6a1c1a396.jpg"
// "display_url" => "https://i.ibb.co/kSj3GSG/3ad6a1c1a396.jpg"
// "width" => 768
// "height" => 1152
// "size" => 488066
// "time" => 1706728072
// "expiration" => 0
// "image" => array:5 [
// "filename" => "3ad6a1c1a396.jpg"
// "name" => "3ad6a1c1a396"
// "mime" => "image/jpeg"
// "extension" => "jpg"
// "url" => "https://i.ibb.co/nL93wLw/3ad6a1c1a396.jpg"
// ]
// "thumb" => array:5 [
// "filename" => "3ad6a1c1a396.jpg"
// "name" => "3ad6a1c1a396"
// "mime" => "image/jpeg"
// "extension" => "jpg"
// "url" => "https://i.ibb.co/MCr8gCg/3ad6a1c1a396.jpg"
// ]
// "medium" => array:5 [
// "filename" => "3ad6a1c1a396.jpg"
// "name" => "3ad6a1c1a396"
// "mime" => "image/jpeg"
// "extension" => "jpg"
// "url" => "https://i.ibb.co/kSj3GSG/3ad6a1c1a396.jpg"
// ]
// "delete_url" => "https://ibb.co/MCr8gCg/1ff48b62fb46aa383c47273cf0ca180f"
// ]
// "success" => true
// "status" => 200
// ]
*
* @param string $imagePath
*
* @return string | null
*/
public static function uploadImage($imagePath)
{
if (!$imagePath) {
info('No imagePath provided');
return;
}
// if (
// // ask('Is imgBB details correct? ' . config('services.leo.imgbb_key') . ' ' . config('services.leo.imgbb_url'))
// confirm('Is imgBB details correct? ' . config('services.leo.imgbb_key') . ' ' . config('services.leo.imgbb_url') . PHP_EOL . 'Path: ' . $imagePath)
// ) {
// info('imgBB details are correct');
// } else {
// info('imgBB details are incorrect');
// return;
// }
if (!file_exists($imagePath)) {
info('Image not found: ' . $imagePath);
return;
}
// $response = Http::post(config('services.leo.imgbb_url') . '?key=' . config('services.leo.imgbb_key'), [
// // 'key' => config('services.leo.imgbb_key'),
// 'image' => base64_encode(file_get_contents($imagePath)),
// ])
// ->throw()->json();
try {
$the_image = base64_encode(file_get_contents($imagePath));
$response = Http::attach(
'image',
$the_image,
// 'image.jpg' // optional FILENAME
null,
[
'content-type' => 'image/jpeg',
]
)
->post(config('services.leo.imgbb_url')
. '?key=' . config('services.leo.imgbb_key'));
} catch (\Exception $e) {
info('Error uploading image: ' . $e->getMessage());
return;
}
unset($the_image);
$response = $response->json();
Log::info('imgBB response: ' . print_r($response, true));
return $response;
}
/**
*
* Updates the image with the imgBB URL
*
* @param string $imageId
* @param string $imgbbUrl
* @return boolean | null
*/
public static function updateImage($imageId, $imgbbUrl)
{
if (!$imageId) {
info('No imageId provided');
return;
}
if (!$imgbbUrl) {
info('No imgbbUrl provided');
return;
}
// update the image url in the database
$artwork = Artwork::find($imageId)->first();
if ($artwork) {
$artwork->url = $imgbbUrl;
$artwork->save();
info('Artwork updated: ' . $imageId);
return true;
} else {
info('Artwork not found: ' . $imageId);
// create a new record
$artwork = Artwork::create([
'id' => $imageId,
'url' => $imgbbUrl,
'is_public' => true,
'user_id' => auth()->user()->id ?? 2,
'name' => $imageId,
'created_at' => now(),
]);
info('Artwork created: ' . $imageId);
return true;
}
}
/**
* Get the images where not uploaded to imgBB yet
*
*
* @return array
*/
public static function getImagesNotUploaded()
{
$images = Artwork::where('url', 'not like', '%ibb.co%')->get();
return $images;
}
/**
* Converts hex colors to words using thesaurus
*
* @param string $hex
*
* @return string
*/
public static function hexToNames($hexColor)
{
$colors = json_decode(file_get_contents(database_path('jsons/colorBrewer.json')), true);
$hexColorWithoutHash = str_replace('#', '', $hexColor);
$colorNames = [];
// foreach ($colors as $colorName => $colorPalette) {
// foreach ($colorPalette as $color) {
// $colorWithoutHash = str_replace('#', '', $color);
// if ($hexColorWithoutHash === $colorWithoutHash) {
// $colorNames[] = $colorName;
// }
// }
// }
// search for the color in the colorBrewer.json file
$namedArr = array_keys($colors, $hexColorWithoutHash);
dd($namedArr);
if (count($colorNames) > 0) {
return response()->json([
'hexColor' => $hexColor,
'englishNames' => $colorNames
]);
} else {
return response()->json([
'hexColor' => $hexColor,
'englishNames' => ['Unknown']
]);
}
}
public static function convertHexToWords($colors)
{
// Initialize Guzzle HTTP client
// $client = new Client();
$topColors = [];
foreach ($colors as $color) {
$hexColor = $color['hex'];
// convert hex to int
$hexColor = hexdec($hexColor);
// Check if the color name is already cached
$cacheKey = 'color_name_' . $hexColor;
$colorName = Cache::get($cacheKey);
if (!$colorName) {
// If not cached, call the API and cache the result
try {
// $response = $client->request('GET', "http://thecolorapi.com/id?hex=" . ltrim($hexColor, '#'));
$hex_api = "https://thecolorapi.com/id?hex=" . ltrim($hexColor, '#');
$body = Http::get($hex_api)->body();
Log::info('Body: ' . $body . ' ' . $hex_api);
$data = json_decode($body);
$colorName = $data->name->value ?? 'XX';
// Cache the color name. You can specify the duration for caching as per your requirements
Cache::put($cacheKey, $colorName, now()->addYears(1)); // Caching for 1 year
} catch (\Exception $e) {
$colorName = 'Error determining color';
}
}
$topColors[] = [
'hex' => $hexColor,
'name' => $colorName
];
}
// # Set the hexadecimal color code
// color_code="#FF0000"
// # Make a cURL request to the ColorTag API
// curl -s "http://api.colortag.io/tag-url?palette=simple&hex={{color_code}}"
// # Parse the JSON response and extract the color name
$response = Http::get("http://api.colortag.io/tag-url?palette=simple&hex=" . ltrim($hexColor, '#'));
$body = $response->getBody();
$data = json_decode($body);
$topColors['color-tag'] = $data;
return $topColors;
}
/**
* Converts hex color to word using thesaurus
*
* @param string $hex
*
* @return string
*/
#REGION: Masonry
/**
* Create a collage from the generated images in masonry style
*/
public static function createMasonryCollage($outputPath = null, $total = 30)
{
$images = glob(storage_path() . '/app/public/gen_images/*.jp*');
info('Images: ' . print_r($images, true));
if (!$outputPath) {
$outputPath = storage_path('app/public/gen_images/collage-' .
date('Y-m-d-H-i-s') . '.jpg');
}
Log::info('Creating collage at ' . $outputPath);
// Define the dimensions of the collage
$collageWidth = 2900;
$collageHeight = 5360;
$collage = imagecreatetruecolor($collageWidth, $collageHeight);
// Set background color
$white = imagecolorallocate($collage, 255, 255, 255);
imagefill($collage, 0, 0, $white);
$x = 0; // Horizontal position
$y = 0; // Vertical position
shuffle($images);
foreach ($images as $imagePath) {
if (!file_exists($imagePath) || !is_file($imagePath)) {
warning('Image not found: ' . $imagePath);
continue;
} else {
info('Image found: ' . $imagePath);
}
$image = imagecreatefromjpeg($imagePath);
// Get image dimensions
$imgWidth = imagesx($image);
$imgHeight = imagesy($image);
// Resize image if needed
$scale = min(($collageWidth / 5) / $imgWidth, ($collageHeight / 4) / $imgHeight);
$newWidth = $imgWidth * $scale;
$newHeight = $imgHeight * $scale;
// Create resized image
$resizedImage = imagecreatetruecolor($newWidth, $newHeight);
imagecopyresampled($resizedImage, $image, 0, 0, 0, 0, $newWidth, $newHeight, $imgWidth, $imgHeight);
// Check for space and reset positions if necessary
if ($x + $newWidth > $collageWidth) {
$x = 0;
$y += $newHeight; // Move to next row
}
if ($y + $newHeight > $collageHeight) {
break; // No more space in collage
}
// Place resized image onto the collage
imagecopy($collage, $resizedImage, $x, $y, 0, 0, $newWidth, $newHeight);
// Update position for next image
$x += $newWidth;
imagedestroy($image); // Free memory
imagedestroy($resizedImage); // Free memory of resized image
$total--;
if ($total == 0) {
break;
}
}
// Save the collage
imagejpeg($collage, $outputPath);
imagedestroy($collage);
outro('Collage created at ' . $outputPath);
return $outputPath;
}
public static function createGalleryMasonry($images)
{
// Create an array to store image data
$gallery = [];
foreach ($images as $image) {
// Calculate the width and height of each image
list($width, $height) = getimagesize($image);
// Determine the orientation (portrait or landscape) of each image
if ($width > $height) {
$orientation = 'landscape';
} else {
$orientation = 'portrait';
}
// Calculate the west and height of each image based on the orientation
if ($orientation === 'landscape') {
$gallery[] = [
'width' => $width,
'height' => $height,
'west' => 0,
'top' => $height,
];
} else {
// Adjust the west value to minimize the gaps between images
if ($gallery) {
$lastImage = end($gallery);
if ($width > $lastImage['west']) {
$gallery[] = [
'width' => $width,
'height' => $height,
'west' => $lastImage['west'],
'top' => 0,
];
} else {
$gallery[] = [
'width' => $width,
'height' => $height,
'west' => $lastImage['west'] + ($gallery[$lastImage['top'] - 1]['width'] > $width ? $gallery[$lastImage['top'] - 1]['width'] : $width),
'top' => 0,
];
}
} else {
$gallery[] = [
'width' => $width,
'height' => $height,
'west' => 0,
'top' => 0,
];
}
}
}
// Sort the images based on their west value to achieve masonry effect
usort($gallery, function ($a, $b) {
if ($a['west'] === $b['west']) {
return $a['top'] <=> $b['top'];
} else {
return $a['west'] <=> $b['west'];
}
});
// Return the sorted gallery masonry data
return $gallery;
}
public static function createCollage($outputPath = null, $total = 30)
{
// $images = glob(storage_path() . '/app/public/gen_images/*.jp*');
$images = glob(base_path('json_db/raw_images/*.jp*'));
Log::info('Images: ' . count($images));
shuffle($images);
// extract 10 images from the array
$images = array_slice($images, 0, 20);
if (!$outputPath) {
$outputPath = storage_path('app/public/gen_images/collage-' .
date('Y-m-d-H-i-s') . '.jpg');
}
$gallery = self::createGalleryMasonry($images);
// Calculate the total height of all images
$totalHeight = 0;
foreach ($gallery as $image) {
$totalHeight += $image['height'];
}
// Create a new image with the maximum width and total height
$width = max(...array_column($gallery, 'width')); // Get the maximum width
$image = imagecreatetruecolor($width, $totalHeight);
// Draw each image onto the canvas
foreach ($gallery as $index => $imageData) {
// Calculate the x and y position of the image on the canvas based on its masonry data
$x = ($imageData['west'] === 0) ? 0 : ($gallery[$index - 1]['width'] + $imageData['west']);
$y = $totalHeight - $imageData['height']; // Adjust the y-coordinate here
// Draw the image onto the canvas
$sourceImage = imagecreatefromjpeg($images[$index]);
imagecopyresampled($image, $sourceImage, $x, $y, 0, 0, $imageData['width'], $imageData['height'], imagesx($sourceImage), imagesy($sourceImage));
imagedestroy($sourceImage);
}
// save to disk
imagejpeg($image, $outputPath);
imagedestroy($image); // Free memory used by the image
}
#ENDREGION
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment