Ver código fonte

Fix heic, avif, webp support and add libvips driver

Daniel Supernault 1 mês atrás
pai
commit
4e938a8ffa

+ 2 - 0
app/Http/Controllers/Api/ApiV1Controller.php

@@ -1911,6 +1911,7 @@ class ApiV1Controller extends Controller
             case 'image/jpeg':
             case 'image/png':
             case 'image/webp':
+            case 'image/heic':
             case 'image/avif':
                 ImageOptimize::dispatch($media)->onQueue('mmo');
                 break;
@@ -2142,6 +2143,7 @@ class ApiV1Controller extends Controller
             case 'image/jpeg':
             case 'image/png':
             case 'image/webp':
+            case 'image/heic':
             case 'image/avif':
                 ImageOptimize::dispatch($media)->onQueue('mmo');
                 break;

+ 3 - 0
app/Http/Controllers/Api/ApiV1Dot1Controller.php

@@ -1310,6 +1310,9 @@ class ApiV1Dot1Controller extends Controller
             case 'image/jpg':
             case 'image/jpeg':
             case 'image/png':
+            case 'image/webp':
+            case 'image/heic':
+            case 'image/avif':
                 ImageOptimize::dispatch($media)->onQueue('mmo');
                 break;
 

+ 4 - 1
app/Http/Controllers/Api/ApiV2Controller.php

@@ -309,9 +309,12 @@ class ApiV2Controller extends Controller
         $media->save();
 
         switch ($media->mime) {
-            case 'image/jpeg':
             case 'image/jpg':
+            case 'image/jpeg':
             case 'image/png':
+            case 'image/webp':
+            case 'image/heic':
+            case 'image/avif':
                 ImageOptimize::dispatch($media)->onQueue('mmo');
                 break;
 

+ 1 - 0
app/Http/Controllers/ComposeController.php

@@ -132,6 +132,7 @@ class ComposeController extends Controller
             case 'image/jpeg':
             case 'image/png':
             case 'image/webp':
+            case 'image/heic':
             case 'image/avif':
                 ImageOptimize::dispatch($media)->onQueue('mmo');
                 break;

+ 26 - 23
app/Util/Media/Image.php

@@ -10,6 +10,7 @@ use Intervention\Image\Encoders\AvifEncoder;
 use Intervention\Image\Encoders\PngEncoder;
 use Cache, Log, Storage;
 use App\Util\Media\Blurhash;
+use App\Services\StatusService;
 
 class Image
 {
@@ -75,20 +76,15 @@ class Image
         ];
     }
 
-    public function getAspectRatio($mediaPath, $thumbnail = false)
+    public function getAspect($width, $height, $isThumbnail)
     {
-        if ($thumbnail) {
+        if ($isThumbnail) {
             return [
                 'dimensions'  => $this->thumbnail,
                 'orientation' => 'thumbnail',
             ];
         }
 
-        if (!is_file($mediaPath)) {
-            throw new \Exception('Invalid Media Path');
-        }
-
-        list($width, $height) = getimagesize($mediaPath);
         $aspect = $width / $height;
         $orientation = $aspect === 1 ? 'square' :
         ($aspect > 1 ? 'landscape' : 'portrait');
@@ -129,13 +125,11 @@ class Image
         if (!in_array($media->mime, $this->acceptedMimes)) {
             return;
         }
-        $ratio = $this->getAspectRatio($file, $thumbnail);
-        $aspect = $ratio['dimensions'];
-        $orientation = $ratio['orientation'];
 
         try {
             $fileInfo = pathinfo($file);
             $extension = strtolower($fileInfo['extension'] ?? 'jpg');
+            $outputExtension = $extension;
 
             $metadata = null;
             if (!$thumbnail && config('media.exif.database', false) == true) {
@@ -184,6 +178,10 @@ class Image
 
             $img = $this->imageManager->read($file);
 
+            $ratio = $this->getAspect($img->width(), $img->height(), $thumbnail);
+            $aspect = $ratio['dimensions'];
+            $orientation = $ratio['orientation'];
+
             if ($thumbnail) {
                 $img = $img->coverDown(
                     $aspect['width'],
@@ -201,9 +199,6 @@ class Image
                 }
             }
 
-            $converted = $this->setBaseName($path, $thumbnail, $extension);
-            $newPath = storage_path('app/'.$converted['path']);
-
             $quality = config_cache('pixelfed.image_quality');
 
             $encoder = null;
@@ -211,25 +206,32 @@ class Image
                 case 'jpeg':
                 case 'jpg':
                     $encoder = new JpegEncoder($quality);
+                    $outputExtension = 'jpg';
                     break;
                 case 'png':
                     $encoder = new PngEncoder();
+                    $outputExtension = 'png';
                     break;
                 case 'webp':
                     $encoder = new WebpEncoder($quality);
+                    $outputExtension = 'webp';
                     break;
                 case 'avif':
-                    $encoder = new AvifEncoder($quality);
+                    $encoder = new JpegEncoder($quality);
+                    $outputExtension = 'jpg';
                     break;
                 case 'heic':
                     $encoder = new JpegEncoder($quality);
-                    $extension = 'jpg';
+                    $outputExtension = 'jpg';
                     break;
                 default:
                     $encoder = new JpegEncoder($quality);
-                    $extension = 'jpg';
+                    $outputExtension = 'jpg';
             }
 
+            $converted = $this->setBaseName($path, $thumbnail, $outputExtension);
+            $newPath = storage_path('app/'.$converted['path']);
+
             $encoded = $encoder->encode($img);
 
             file_put_contents($newPath, $encoded->toString());
@@ -242,7 +244,7 @@ class Image
                 $media->height = $img->height();
                 $media->orientation = $orientation;
                 $media->media_path = $converted['path'];
-                $media->mime = 'image/' . $extension;
+                $media->mime = 'image/' . $outputExtension;
             }
 
             $media->save();
@@ -251,8 +253,11 @@ class Image
                 $this->generateBlurhash($media);
             }
 
-            Cache::forget('status:transformer:media:attachments:'.$media->status_id);
-            Cache::forget('status:thumb:'.$media->status_id);
+            if($media->status_id) {
+                Cache::forget('status:transformer:media:attachments:'.$media->status_id);
+                Cache::forget('status:thumb:'.$media->status_id);
+                StatusService::del($media->status_id);
+            }
 
         } catch (\Exception $e) {
             $media->processed_at = now();
@@ -263,13 +268,11 @@ class Image
 
     public function setBaseName($basePath, $thumbnail, $extension)
     {
-        $png = false;
         $path = explode('.', $basePath);
         $name = ($thumbnail == true) ? $path[0].'_thumb' : $path[0];
-        $ext = last($path);
-        $basePath = "{$name}.{$ext}";
+        $basePath = "{$name}.{$extension}";
 
-        return ['path' => $basePath, 'png' => $png];
+        return ['path' => $basePath, 'png' => false];
     }
 
     protected function generateBlurhash($media)

+ 1 - 0
composer.json

@@ -19,6 +19,7 @@
         "doctrine/dbal": "^3.0",
         "endroid/qr-code": "^6.0",
         "intervention/image": "^3.11.2",
+        "intervention/image-driver-vips": "^1.0",
         "jenssegers/agent": "^2.6",
         "laravel-notification-channels/expo": "^2.0.0",
         "laravel-notification-channels/webpush": "^10.2",

+ 136 - 1
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "fc7f320209f9bd3f731d3de6e6f1755f",
+    "content-hash": "80e41279435abaff339524fb0a150bc6",
     "packages": [
         {
             "name": "aws/aws-crt-php",
@@ -2216,6 +2216,80 @@
             ],
             "time": "2025-02-27T13:08:55+00:00"
         },
+        {
+            "name": "intervention/image-driver-vips",
+            "version": "1.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Intervention/image-driver-vips.git",
+                "reference": "080de0e638bcf508b5e79c2d88e82b0fd91b12b0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Intervention/image-driver-vips/zipball/080de0e638bcf508b5e79c2d88e82b0fd91b12b0",
+                "reference": "080de0e638bcf508b5e79c2d88e82b0fd91b12b0",
+                "shasum": ""
+            },
+            "require": {
+                "intervention/image": "^3.11.0",
+                "jcupitt/vips": "^2.4",
+                "php": "^8.1"
+            },
+            "require-dev": {
+                "ext-fileinfo": "*",
+                "phpstan/phpstan": "^2",
+                "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0",
+                "slevomat/coding-standard": "~8.0",
+                "squizlabs/php_codesniffer": "^3.8"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Intervention\\Image\\Drivers\\Vips\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Oliver Vogel",
+                    "email": "oliver@intervention.io",
+                    "homepage": "https://intervention.io/"
+                },
+                {
+                    "name": "Thomas Picquet",
+                    "email": "thomas@sctr.net"
+                }
+            ],
+            "description": "libvips driver for Intervention Image",
+            "homepage": "https://image.intervention.io/",
+            "keywords": [
+                "image",
+                "libvips",
+                "vips"
+            ],
+            "support": {
+                "issues": "https://github.com/Intervention/image-driver-vips/issues",
+                "source": "https://github.com/Intervention/image-driver-vips/tree/1.0.5"
+            },
+            "funding": [
+                {
+                    "url": "https://paypal.me/interventionio",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/Intervention",
+                    "type": "github"
+                },
+                {
+                    "url": "https://ko-fi.com/interventionphp",
+                    "type": "ko_fi"
+                }
+            ],
+            "time": "2025-05-05T13:53:52+00:00"
+        },
         {
             "name": "jaybizzle/crawler-detect",
             "version": "v1.3.4",
@@ -2268,6 +2342,67 @@
             },
             "time": "2025-03-05T23:12:10+00:00"
         },
+        {
+            "name": "jcupitt/vips",
+            "version": "v2.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/libvips/php-vips.git",
+                "reference": "a54c1cceea581b592a199edd61a7c06f44a24c08"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/libvips/php-vips/zipball/a54c1cceea581b592a199edd61a7c06f44a24c08",
+                "reference": "a54c1cceea581b592a199edd61a7c06f44a24c08",
+                "shasum": ""
+            },
+            "require": {
+                "ext-ffi": "*",
+                "php": ">=7.4",
+                "psr/log": "^1.1.3|^2.0|^3.0"
+            },
+            "require-dev": {
+                "php-parallel-lint/php-parallel-lint": "^1.3",
+                "phpdocumentor/shim": "^3.3",
+                "phpunit/phpunit": "^9.5",
+                "squizlabs/php_codesniffer": "^3.7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Jcupitt\\Vips\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "John Cupitt",
+                    "email": "jcupitt@gmail.com",
+                    "homepage": "https://github.com/jcupitt",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A high-level interface to the libvips image processing library.",
+            "homepage": "https://github.com/libvips/php-vips",
+            "keywords": [
+                "image",
+                "libvips",
+                "processing"
+            ],
+            "support": {
+                "issues": "https://github.com/libvips/php-vips/issues",
+                "source": "https://github.com/libvips/php-vips/tree/v2.5.0"
+            },
+            "time": "2025-04-04T17:10:13+00:00"
+        },
         {
             "name": "jenssegers/agent",
             "version": "v2.6.4",

+ 9 - 1
config/image-optimizer.php

@@ -5,6 +5,7 @@ use Spatie\ImageOptimizer\Optimizers\Jpegoptim;
 use Spatie\ImageOptimizer\Optimizers\Optipng;
 use Spatie\ImageOptimizer\Optimizers\Pngquant;
 use Spatie\ImageOptimizer\Optimizers\Svgo;
+use Spatie\ImageOptimizer\Optimizers\Cwebp;
 
 return [
     /*
@@ -15,7 +16,7 @@ return [
 
         Jpegoptim::class => [
             '-m' . (int) env('IMAGE_QUALITY', 80),
-            '--strip-all',  // this strips out all text information such as comments and EXIF data
+            '--strip-exif',  // this strips out EXIF data
             '--all-progressive',  // this will make sure the resulting image is a progressive one
         ],
 
@@ -38,6 +39,13 @@ return [
             '-b', // required parameter for this package
             '-O3', // this produces the slowest but best results
         ],
+
+        Cwebp::class => [
+            '-m 6', // for the slowest compression method in order to get the best compression.
+            '-pass 10', // for maximizing the amount of analysis pass.
+            '-mt', // multithreading for some speed improvements.
+            '-q 90', // quality factor that brings the least noticeable changes.
+        ],
     ],
 
     /*