diff --git a/app/Jobs/CommonFunctions.php b/app/Jobs/CommonFunctions.php new file mode 100644 index 0000000..9362b76 --- /dev/null +++ b/app/Jobs/CommonFunctions.php @@ -0,0 +1,81 @@ +toString(); + $pngPath = Storage::disk('public')->path('/images/generated/'.$uuid.'.png'); + $bmpPath = Storage::disk('public')->path('/images/generated/'.$uuid.'.bmp'); + + // Generate PNG + if (config('app.puppeteer_mode') === 'sidecar-aws') { + try { + BrowsershotLambda::html($markup) + ->windowSize($device->width ?? 800, $device->height ?? 480) + ->save($pngPath); + } catch (\Exception $e) { + throw new \RuntimeException('Failed to generate PNG: '.$e->getMessage(), 0, $e); + } + } else { + try { + Browsershot::html($markup) + ->setOption('args', config('app.puppeteer_docker') ? ['--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu'] : []) + ->windowSize($device->width ?? 800, $device->height ?? 480) + ->save($pngPath); + } catch (\Exception $e) { + throw new \RuntimeException('Failed to generate PNG: '.$e->getMessage(), 0, $e); + } + } + + try { + CommonFunctions::convertToBmpImageMagick($pngPath, $bmpPath); + } catch (\ImagickException $e) { + throw new \RuntimeException('Failed to convert image to BMP: '.$e->getMessage(), 0, $e); + } + return $uuid; + } + + /** + * @throws \ImagickException + */ + private static function convertToBmpImageMagick(string $pngPath, string $bmpPath): void + { + $imagick = new \Imagick($pngPath); + $imagick->setImageType(\Imagick::IMGTYPE_GRAYSCALE); + $imagick->quantizeImage(2, \Imagick::COLORSPACE_GRAY, 0, true, false); + $imagick->setImageDepth(1); + $imagick->stripImage(); + $imagick->setFormat('BMP3'); + $imagick->writeImage($bmpPath); + $imagick->clear(); + } + + public static function cleanupFolder(): void + { + $activeDeviceImageUuids = Device::pluck('current_screen_image')->filter()->toArray(); + $activePlaylistImageUuids = PlaylistItem::pluck('current_image')->filter()->toArray(); + $activeImageUuids = array_merge($activeDeviceImageUuids, $activePlaylistImageUuids); + + $files = Storage::disk('public')->files('/images/generated/'); + foreach ($files as $file) { + if (basename($file) === '.gitignore') { + continue; + } + // Get filename without path and extension + $fileUuid = pathinfo($file, PATHINFO_FILENAME); + // If the UUID is not in use by any device, move it to archive + if (! in_array($fileUuid, $activeImageUuids)) { + Storage::disk('public')->delete($file); + } + } + } +} diff --git a/app/Jobs/GeneratePlaylistItemJob.php b/app/Jobs/GeneratePlaylistItemJob.php new file mode 100644 index 0000000..374f95d --- /dev/null +++ b/app/Jobs/GeneratePlaylistItemJob.php @@ -0,0 +1,37 @@ +markup); + + PlaylistItem::find($this->playlistItemId)->update(['current_image' => $newImageUuid]); + \Log::info("Playlist item $this->playlistItemId: updated with new image: $newImageUuid"); + + CommonFunctions::cleanupFolder(); + } +} + diff --git a/app/Jobs/GenerateScreenJob.php b/app/Jobs/GenerateScreenJob.php index 4dcf876..166af8d 100644 --- a/app/Jobs/GenerateScreenJob.php +++ b/app/Jobs/GenerateScreenJob.php @@ -8,10 +8,6 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Storage; -use Ramsey\Uuid\Uuid; -use Spatie\Browsershot\Browsershot; -use Wnx\SidecarBrowsershot\BrowsershotLambda; class GenerateScreenJob implements ShouldQueue { @@ -30,72 +26,11 @@ class GenerateScreenJob implements ShouldQueue */ public function handle(): void { - $device = Device::find($this->deviceId); - $uuid = Uuid::uuid4()->toString(); - $pngPath = Storage::disk('public')->path('/images/generated/'.$uuid.'.png'); - $bmpPath = Storage::disk('public')->path('/images/generated/'.$uuid.'.bmp'); + $newImageUuid = CommonFunctions::generateImage($this->markup); - // Generate PNG - if (config('app.puppeteer_mode') === 'sidecar-aws') { - try { - BrowsershotLambda::html($this->markup) - ->windowSize($device->width ?? 800, $device->height ?? 480) - ->save($pngPath); - } catch (\Exception $e) { - throw new \RuntimeException('Failed to generate PNG: '.$e->getMessage(), 0, $e); - } - } else { - try { - Browsershot::html($this->markup) - ->setOption('args', config('app.puppeteer_docker') ? ['--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu'] : []) - ->windowSize($device->width ?? 800, $device->height ?? 480) - ->save($pngPath); - } catch (\Exception $e) { - throw new \RuntimeException('Failed to generate PNG: '.$e->getMessage(), 0, $e); - } - } + Device::find($this->deviceId)->update(['current_screen_image' => $newImageUuid]); + \Log::info("Device $this->deviceId: updated with new image: $newImageUuid"); - try { - $this->convertToBmpImageMagick($pngPath, $bmpPath); - } catch (\ImagickException $e) { - throw new \RuntimeException('Failed to convert image to BMP: '.$e->getMessage(), 0, $e); - } - $device->update(['current_screen_image' => $uuid]); - \Log::info("Device $this->deviceId: updated with new image: $uuid"); - - $this->cleanupFolder(); - } - - /** - * @throws \ImagickException - */ - private function convertToBmpImageMagick(string $pngPath, string $bmpPath): void - { - $imagick = new \Imagick($pngPath); - $imagick->setImageType(\Imagick::IMGTYPE_GRAYSCALE); - $imagick->quantizeImage(2, \Imagick::COLORSPACE_GRAY, 0, true, false); - $imagick->setImageDepth(1); - $imagick->stripImage(); - $imagick->setFormat('BMP3'); - $imagick->writeImage($bmpPath); - $imagick->clear(); - } - - private function cleanupFolder(): void - { - $activeImageUuids = Device::pluck('current_screen_image')->filter()->toArray(); - - $files = Storage::disk('public')->files('/images/generated/'); - foreach ($files as $file) { - if (basename($file) === '.gitignore') { - continue; - } - // Get filename without path and extension - $fileUuid = pathinfo($file, PATHINFO_FILENAME); - // If the UUID is not in use by any device, move it to archive - if (! in_array($fileUuid, $activeImageUuids)) { - Storage::disk('public')->delete($file); - } - } + CommonFunctions::cleanupFolder(); } } diff --git a/app/Models/PlaylistItem.php b/app/Models/PlaylistItem.php index 4eba877..88fb65a 100644 --- a/app/Models/PlaylistItem.php +++ b/app/Models/PlaylistItem.php @@ -15,6 +15,7 @@ class PlaylistItem extends Model protected $casts = [ 'is_active' => 'boolean', 'last_displayed_at' => 'datetime', + 'current_image' => 'string', ]; public function playlist(): BelongsTo diff --git a/database/migrations/2025_05_01_083230_add_playlist_item_cache.php b/database/migrations/2025_05_01_083230_add_playlist_item_cache.php new file mode 100644 index 0000000..1a58e7f --- /dev/null +++ b/database/migrations/2025_05_01_083230_add_playlist_item_cache.php @@ -0,0 +1,28 @@ +string('current_image')->nullable()->after('is_active'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('playlist_items', function (Blueprint $table) { + $table->dropColumn('current_image'); + }); + } +}; diff --git a/routes/api.php b/routes/api.php index f884059..4aabc4d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,6 +1,7 @@ mirrorDevice?->current_screen_image) { $refreshTimeOverride = null; + $nextPlaylistItem = $device->getNextPlaylistItem(); // Skip if cloud proxy is enabled for the device - if (! $device->proxy_cloud || $device->getNextPlaylistItem()) { - $playlistItem = $device->getNextPlaylistItem(); - - if ($playlistItem) { - $refreshTimeOverride = $playlistItem->playlist()->first()->refresh_time; - - $plugin = $playlistItem->plugin; + if (! $device->proxy_cloud || $nextPlaylistItem) { + if ($nextPlaylistItem) { + $refreshTimeOverride = $nextPlaylistItem->playlist()->first()->refresh_time; + $plugin = $nextPlaylistItem->plugin; // Check and update stale data if needed - if ($plugin->isDataStale()) { + if ($plugin->isDataStale() || $nextPlaylistItem->last_displayed_at == null) { $plugin->updateDataPayload(); - } - $playlistItem->update(['last_displayed_at' => now()]); - if ($plugin->render_markup) { - $markup = Blade::render($plugin->render_markup, ['data' => $plugin->data_payload]); - } elseif ($plugin->render_markup_view) { - $markup = view($plugin->render_markup_view, ['data' => $plugin->data_payload])->render(); - } + if ($plugin->render_markup) { + $markup = Blade::render($plugin->render_markup, ['data' => $plugin->data_payload]); + } elseif ($plugin->render_markup_view) { + $markup = view($plugin->render_markup_view, ['data' => $plugin->data_payload])->render(); + } - GenerateScreenJob::dispatchSync($device->id, $markup); + GeneratePlaylistItemJob::dispatchSync($nextPlaylistItem->id, $markup); + } + } + + $nextPlaylistItem->refresh(); + + if ($nextPlaylistItem->current_image != null) + { + $nextPlaylistItem->update(['last_displayed_at' => now()]); + $device->update(['current_screen_image' => $nextPlaylistItem->current_image]); } }