124 lines
3.7 KiB
PHP
124 lines
3.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\File;
|
|
|
|
class ConvertImagesToWebP extends Command
|
|
{
|
|
protected $signature = 'images:convert-webp
|
|
{--path=img/assets : Relative path inside public/}
|
|
{--quality=85 : WebP quality (1-100)}
|
|
{--force : Overwrite existing WebP files}
|
|
{--dry-run : Show what would be converted without doing it}';
|
|
|
|
protected $description = 'Convert JPG/JPEG/PNG images to WebP format using GD';
|
|
|
|
public function handle(): int
|
|
{
|
|
$relativePath = $this->option('path');
|
|
$quality = (int) $this->option('quality');
|
|
$force = (bool) $this->option('force');
|
|
$dryRun = (bool) $this->option('dry-run');
|
|
$basePath = public_path($relativePath);
|
|
|
|
if (! File::isDirectory($basePath)) {
|
|
$this->error("Directory not found: {$basePath}");
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$extensions = ['jpg', 'jpeg', 'png'];
|
|
$files = collect(File::allFiles($basePath))
|
|
->filter(fn ($file) => in_array(strtolower($file->getExtension()), $extensions));
|
|
|
|
if ($files->isEmpty()) {
|
|
$this->info('No images found to convert.');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
$this->info(sprintf('Found %d images in %s', $files->count(), $relativePath));
|
|
|
|
$converted = 0;
|
|
$skipped = 0;
|
|
$savedBytes = 0;
|
|
|
|
foreach ($files as $file) {
|
|
$webpPath = preg_replace('/\.(jpe?g|png)$/i', '.webp', $file->getPathname());
|
|
|
|
if (File::exists($webpPath) && ! $force) {
|
|
$skipped++;
|
|
|
|
continue;
|
|
}
|
|
|
|
if ($dryRun) {
|
|
$this->line(" Would convert: {$file->getRelativePathname()}");
|
|
$converted++;
|
|
|
|
continue;
|
|
}
|
|
|
|
$result = $this->convertToWebP($file->getPathname(), $webpPath, $quality);
|
|
|
|
if ($result) {
|
|
$originalSize = $file->getSize();
|
|
$webpSize = filesize($webpPath);
|
|
$saving = $originalSize - $webpSize;
|
|
$savedBytes += $saving;
|
|
$percent = $originalSize > 0 ? round(($saving / $originalSize) * 100, 1) : 0;
|
|
|
|
$this->line(sprintf(
|
|
' <info>✓</info> %s → %s KB → %s KB (<comment>-%s%%</comment>)',
|
|
$file->getRelativePathname(),
|
|
round($originalSize / 1024, 1),
|
|
round($webpSize / 1024, 1),
|
|
$percent
|
|
));
|
|
$converted++;
|
|
} else {
|
|
$this->warn(" ✗ Failed: {$file->getRelativePathname()}");
|
|
}
|
|
}
|
|
|
|
$this->newLine();
|
|
$this->info(sprintf(
|
|
'Done: %d converted, %d skipped, %.1f KB saved',
|
|
$converted,
|
|
$skipped,
|
|
$savedBytes / 1024
|
|
));
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
private function convertToWebP(string $sourcePath, string $destPath, int $quality): bool
|
|
{
|
|
$extension = strtolower(pathinfo($sourcePath, PATHINFO_EXTENSION));
|
|
|
|
$image = match ($extension) {
|
|
'jpg', 'jpeg' => @imagecreatefromjpeg($sourcePath),
|
|
'png' => @imagecreatefrompng($sourcePath),
|
|
default => false,
|
|
};
|
|
|
|
if ($image === false) {
|
|
return false;
|
|
}
|
|
|
|
if ($extension === 'png') {
|
|
imagepalettetotruecolor($image);
|
|
imagealphablending($image, true);
|
|
imagesavealpha($image, true);
|
|
}
|
|
|
|
$result = imagewebp($image, $destPath, $quality);
|
|
imagedestroy($image);
|
|
|
|
return $result;
|
|
}
|
|
}
|