getCacheKey(); Log::info('GenerateThumbnail: Starting thumbnail generation', [ 'source' => $this->sourcePath, 'dimensions' => "{$this->width}x{$this->height}", 'attempt' => $this->attempts(), ]); try { // Clear queued flag Cache::forget($cacheKey.':queued'); // Create LazyThumbnail instance with options $lazyThumbnail = $this->createLazyThumbnail(); // Check if source still exists and is processable if (! $lazyThumbnail->canGenerate($this->sourcePath)) { Log::warning('GenerateThumbnail: Source cannot be processed', [ 'source' => $this->sourcePath, ]); return; } // Generate the thumbnail synchronously $result = $lazyThumbnail->generate($this->sourcePath, $this->width, $this->height); if ($result !== null) { Log::info('GenerateThumbnail: Thumbnail generated successfully', [ 'source' => $this->sourcePath, 'thumbnail' => $result, 'dimensions' => "{$this->width}x{$this->height}", ]); } else { Log::warning('GenerateThumbnail: Thumbnail generation returned null', [ 'source' => $this->sourcePath, 'dimensions' => "{$this->width}x{$this->height}", ]); } } catch (\Throwable $e) { Log::error('GenerateThumbnail: Exception during generation', [ 'source' => $this->sourcePath, 'error' => $e->getMessage(), 'attempt' => $this->attempts(), ]); throw $e; } } /** * Handle a job failure. */ public function failed(?\Throwable $exception): void { $cacheKey = $this->getCacheKey(); // Clear all cache flags Cache::forget($cacheKey.':queued'); Cache::forget($cacheKey.':generating'); // Store failure flag briefly to prevent immediate re-queueing Cache::put($cacheKey.':failed', true, 300); Log::error('GenerateThumbnail: Job failed after all retries', [ 'source' => $this->sourcePath, 'dimensions' => "{$this->width}x{$this->height}", 'error' => $exception?->getMessage(), ]); } /** * Calculate the number of seconds to wait before retrying the job. * * @return array */ public function backoff(): array { return [10, 30, 60]; } /** * Get the cache key for this thumbnail. */ protected function getCacheKey(): string { $sourceDisk = $this->options['source_disk'] ?? 'public'; return 'lazy_thumb:'.md5("{$sourceDisk}:{$this->sourcePath}:{$this->width}x{$this->height}"); } /** * Create a LazyThumbnail instance with configured options. */ protected function createLazyThumbnail(): LazyThumbnail { $lazyThumbnail = new LazyThumbnail( $this->options['source_disk'] ?? null, $this->options['thumbnail_disk'] ?? null ); if (isset($this->options['prefix'])) { $lazyThumbnail->prefix($this->options['prefix']); } if (isset($this->options['quality'])) { $lazyThumbnail->quality($this->options['quality']); } return $lazyThumbnail; } /** * Get the tags that should be assigned to the job. * * @return array */ public function tags(): array { return [ 'media', 'thumbnail', 'source:'.$this->sourcePath, ]; } }