mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-01-13 23:18:10 +00:00
197 lines
6.9 KiB
PHP
197 lines
6.9 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\DeviceModel;
|
|
use Bnussbau\TrmnlPipeline\Stages\BrowserStage;
|
|
use Bnussbau\TrmnlPipeline\Stages\ImageStage;
|
|
use Bnussbau\TrmnlPipeline\TrmnlPipeline;
|
|
use Exception;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use InvalidArgumentException;
|
|
use RuntimeException;
|
|
use Wnx\SidecarBrowsershot\BrowsershotLambda;
|
|
|
|
class GenerateDefaultImagesCommand extends Command
|
|
{
|
|
/**
|
|
* The name and signature of the console command.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $signature = 'images:generate-defaults {--force : Force regeneration of existing images}';
|
|
|
|
/**
|
|
* The console command description.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $description = 'Generate default images (setup-logo and sleep) for all device models from Blade templates';
|
|
|
|
/**
|
|
* Execute the console command.
|
|
*/
|
|
public function handle(): int
|
|
{
|
|
$this->info('Starting generation of default images for all device models...');
|
|
|
|
$deviceModels = DeviceModel::all();
|
|
|
|
if ($deviceModels->isEmpty()) {
|
|
$this->warn('No device models found in the database.');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
$this->info("Found {$deviceModels->count()} device models to process.");
|
|
|
|
// Create the target directory
|
|
$targetDir = 'images/default-screens';
|
|
if (! Storage::disk('public')->exists($targetDir)) {
|
|
Storage::disk('public')->makeDirectory($targetDir);
|
|
$this->info("Created directory: {$targetDir}");
|
|
}
|
|
|
|
$successCount = 0;
|
|
$skipCount = 0;
|
|
$errorCount = 0;
|
|
|
|
foreach ($deviceModels as $deviceModel) {
|
|
$this->info("Processing device model: {$deviceModel->label} (ID: {$deviceModel->id})");
|
|
|
|
try {
|
|
// Process setup-logo
|
|
$setupResult = $this->transformImage('setup-logo', $deviceModel, $targetDir);
|
|
if ($setupResult) {
|
|
++$successCount;
|
|
} else {
|
|
++$skipCount;
|
|
}
|
|
|
|
// Process sleep
|
|
$sleepResult = $this->transformImage('sleep', $deviceModel, $targetDir);
|
|
if ($sleepResult) {
|
|
++$successCount;
|
|
} else {
|
|
++$skipCount;
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$this->error("Error processing device model {$deviceModel->label}: ".$e->getMessage());
|
|
++$errorCount;
|
|
}
|
|
}
|
|
|
|
$this->info("\nGeneration completed!");
|
|
$this->info("Successfully processed: {$successCount} images");
|
|
$this->info("Skipped (already exist): {$skipCount} images");
|
|
$this->info("Errors: {$errorCount} images");
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Transform a single image for a device model using Blade templates
|
|
*/
|
|
private function transformImage(string $imageType, DeviceModel $deviceModel, string $targetDir): bool
|
|
{
|
|
// Generate filename: {width}_{height}_{bit_depth}_{rotation}.{extension}
|
|
$extension = $deviceModel->mime_type === 'image/bmp' ? 'bmp' : 'png';
|
|
$filename = "{$deviceModel->width}_{$deviceModel->height}_{$deviceModel->bit_depth}_{$deviceModel->rotation}.{$extension}";
|
|
$targetPath = "{$targetDir}/{$imageType}_{$filename}";
|
|
|
|
// Check if target already exists and force is not set
|
|
if (Storage::disk('public')->exists($targetPath) && ! $this->option('force')) {
|
|
$this->line(" Skipping {$imageType} - already exists: {$filename}");
|
|
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// Create custom Browsershot instance if using AWS Lambda
|
|
$browsershotInstance = null;
|
|
if (config('app.puppeteer_mode') === 'sidecar-aws') {
|
|
$browsershotInstance = new BrowsershotLambda();
|
|
}
|
|
|
|
// Generate HTML from Blade template
|
|
$html = $this->generateHtmlFromTemplate($imageType, $deviceModel);
|
|
// dump($html);
|
|
|
|
$browserStage = new BrowserStage($browsershotInstance);
|
|
$browserStage->html($html);
|
|
$browserStage
|
|
->width($deviceModel->width)
|
|
->height($deviceModel->height);
|
|
|
|
$browserStage->setBrowsershotOption('waitUntil', 'networkidle0');
|
|
|
|
if (config('app.puppeteer_docker')) {
|
|
$browserStage->setBrowsershotOption('args', ['--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']);
|
|
}
|
|
|
|
$outputPath = Storage::disk('public')->path($targetPath);
|
|
|
|
$imageStage = new ImageStage();
|
|
$imageStage->format($extension)
|
|
->width($deviceModel->width)
|
|
->height($deviceModel->height)
|
|
->colors($deviceModel->colors)
|
|
->bitDepth($deviceModel->bit_depth)
|
|
->rotation($deviceModel->rotation)
|
|
// ->offsetX($deviceModel->offset_x)
|
|
// ->offsetY($deviceModel->offset_y)
|
|
->outputPath($outputPath);
|
|
|
|
(new TrmnlPipeline())->pipe($browserStage)
|
|
->pipe($imageStage)
|
|
->process();
|
|
|
|
if (! file_exists($outputPath)) {
|
|
throw new RuntimeException('Image file was not created: '.$outputPath);
|
|
}
|
|
|
|
if (filesize($outputPath) === 0) {
|
|
throw new RuntimeException('Image file is empty: '.$outputPath);
|
|
}
|
|
|
|
$this->line(" ✓ Generated {$imageType}: {$filename}");
|
|
|
|
return true;
|
|
|
|
} catch (Exception $e) {
|
|
$this->error(" ✗ Failed to generate {$imageType} for {$deviceModel->label}: ".$e->getMessage());
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate HTML from Blade template for the given image type and device model
|
|
*/
|
|
private function generateHtmlFromTemplate(string $imageType, DeviceModel $deviceModel): string
|
|
{
|
|
// Map image type to template name
|
|
$templateName = match ($imageType) {
|
|
'setup-logo' => 'default-screens.setup',
|
|
'sleep' => 'default-screens.sleep',
|
|
default => throw new InvalidArgumentException("Invalid image type: {$imageType}")
|
|
};
|
|
|
|
// Determine device properties from DeviceModel
|
|
$deviceVariant = $deviceModel->name ?? 'og';
|
|
$colorDepth = $deviceModel->color_depth ?? '1bit'; // Use the accessor method
|
|
$scaleLevel = $deviceModel->scale_level; // Use the accessor method
|
|
$darkMode = $imageType === 'sleep'; // Sleep mode uses dark mode, setup uses light mode
|
|
|
|
// Render the Blade template
|
|
return view($templateName, [
|
|
'noBleed' => false,
|
|
'darkMode' => $darkMode,
|
|
'deviceVariant' => $deviceVariant,
|
|
'colorDepth' => $colorDepth,
|
|
'scaleLevel' => $scaleLevel,
|
|
])->render();
|
|
}
|
|
}
|