extension === null && $this->width === null && $this->height === null) { return new ActionResult( ActionStatus::SKIPPED, 'No optimization parameters provided, nothing to process.' ); } try { DB::beginTransaction(); $directory = File::dirname($this->image->path); $optimizedImage = $this->handleFFmpeg(); if ($optimizedImage === null) { DB::rollBack(); return new ActionResult( ActionStatus::FAILED, "Failed to optimize image '{$this->image->path}'.", ); } $path = $this->uploadImage( Storage::disk('local')->path($optimizedImage), $directory ); // Delete the old image from the bucket. Storage::disk(Config::get(ImageConstants::DISKS_QUALIFIED))->delete($this->image->path); $this->image->update([ Image::ATTRIBUTE_PATH => $path, ]); DB::commit(); } catch (Exception $e) { DB::rollBack(); Log::error($e->getMessage()); throw $e; } return new ActionResult(ActionStatus::PASSED); } protected function handleFFmpeg(): ?string { try { Storage::disk('local')->put( $this->image->path, Storage::disk(Config::get(ImageConstants::DISKS_QUALIFIED))->get($this->image->path), ); $imagePath = $this->image->path; if ($this->extension !== null) { $imagePath = $this->convertImage($imagePath); } if ($this->width !== null && $this->height !== null) { return $this->downscaleImage($imagePath); } return $imagePath; } catch (Exception $e) { Log::error($e->getMessage()); } finally { Storage::disk('local')->delete($this->image->path); } return null; } /** * Convert the image to given extension. * * @throws Exception */ protected function convertImage(string $path): string { [$command, $imagePath] = match ($this->extension) { 'avif' => static::getAvifCommand($path), default => throw new Exception("Unsupported image extension: {$this->extension}"), }; Process::run($command)->throw(); return $imagePath; } /** * Downscale the image to given width and height. * * @throws Exception */ protected function downscaleImage(string $path): string { [$command, $imagePath] = static::getDownscaleCommand($path, $this->width, $this->height); Process::run($command)->throw(); return $imagePath; } /** * Upload the image to the bucket. */ protected function uploadImage(string $image, string $directory): string { /** @var \Illuminate\Filesystem\FilesystemAdapter $fs */ $fs = Storage::disk(Config::get(ImageConstants::DISKS_QUALIFIED)); $fsFile = $fs->putFile($directory, $image); Log::info("Uploading optimized Image {$fsFile}"); return $fsFile; } /** * @return array{0:array, 1:string} */ public static function getAvifCommand(string $path): array { $imagePath = Storage::disk('local')->path( $newPath = Str::replaceLast(File::extension($path), 'avif', $path) ); return [ [ 'ffmpeg', '-i', Storage::disk('local')->path($path), '-c:v', 'libaom-av1', '-crf', '30', '-pix_fmt', 'yuv420p', $imagePath, ], $newPath, ]; } /** * @return array{0:array, 1:string} */ public static function getDownscaleCommand(string $path, int $width = 100, int $height = 150): array { $tempPath = Storage::disk('local')->path( $newPath = Str::replaceLast('.', '_scaled.', $path) ); return [ [ 'ffmpeg', '-y', '-i', Storage::disk('local')->path($path), '-vf', "scale={$width}:{$height}", $tempPath, ], $newPath, ]; } }