diff --git a/app/Http/Controllers/StoryComposeController.php b/app/Http/Controllers/StoryComposeController.php index dd82c5009..44352d2a1 100644 --- a/app/Http/Controllers/StoryComposeController.php +++ b/app/Http/Controllers/StoryComposeController.php @@ -19,6 +19,7 @@ use App\Services\StoryService; use App\Services\UserRoleService; use App\Status; use App\Story; +use App\Util\Media\ImageDriverManager; use FFMpeg; use Illuminate\Http\Request; use Illuminate\Support\Str; @@ -33,18 +34,7 @@ class StoryComposeController extends Controller public function __construct() { - $driver = match (config('image.driver')) { - 'imagick' => \Intervention\Image\Drivers\Imagick\Driver::class, - 'vips' => \Intervention\Image\Drivers\Vips\Driver::class, - default => \Intervention\Image\Drivers\Gd\Driver::class - }; - $this->imageManager = new ImageManager( - $driver, - autoOrientation: true, - decodeAnimation: true, - blendingColor: 'ffffff', - strip: true - ); + $this->imageManager = ImageDriverManager::createImageManager(); } public function apiV1Add(Request $request) diff --git a/app/Jobs/AvatarPipeline/AvatarOptimize.php b/app/Jobs/AvatarPipeline/AvatarOptimize.php index fafa0d75f..54b6ff3c3 100644 --- a/app/Jobs/AvatarPipeline/AvatarOptimize.php +++ b/app/Jobs/AvatarPipeline/AvatarOptimize.php @@ -4,6 +4,7 @@ namespace App\Jobs\AvatarPipeline; use App\Avatar; use App\Profile; +use App\Util\Media\ImageDriverManager; use Cache; use Carbon\Carbon; use Illuminate\Bus\Queueable; @@ -57,19 +58,7 @@ class AvatarOptimize implements ShouldQueue $fileInfo = pathinfo($file); $extension = strtolower($fileInfo['extension'] ?? 'jpg'); - $driver = match(config('image.driver')) { - 'imagick' => \Intervention\Image\Drivers\Imagick\Driver::class, - 'vips' => \Intervention\Image\Drivers\Vips\Driver::class, - default => \Intervention\Image\Drivers\Gd\Driver::class - }; - - $imageManager = new ImageManager( - $driver, - autoOrientation: true, - decodeAnimation: true, - blendingColor: 'ffffff', - strip: true - ); + $imageManager = ImageDriverManager::createImageManager(); $quality = config_cache('pixelfed.image_quality'); diff --git a/app/Jobs/GroupsPipeline/ImageResizePipeline.php b/app/Jobs/GroupsPipeline/ImageResizePipeline.php index 68d999194..b70a00b2c 100644 --- a/app/Jobs/GroupsPipeline/ImageResizePipeline.php +++ b/app/Jobs/GroupsPipeline/ImageResizePipeline.php @@ -3,6 +3,7 @@ namespace App\Jobs\GroupsPipeline; use App\Models\GroupMedia; +use App\Util\Media\ImageDriverManager; use Exception; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; @@ -75,19 +76,7 @@ class ImageResizePipeline implements ShouldQueue ]; try { - $driver = match (config('image.driver')) { - 'imagick' => \Intervention\Image\Drivers\Imagick\Driver::class, - 'vips' => \Intervention\Image\Drivers\Vips\Driver::class, - default => \Intervention\Image\Drivers\Gd\Driver::class - }; - - $imageManager = new ImageManager( - $driver, - autoOrientation: true, - decodeAnimation: true, - blendingColor: 'ffffff', - strip: true - ); + $imageManager = ImageDriverManager::createImageManager(); $img = $imageManager->read($file); @@ -97,10 +86,7 @@ class ImageResizePipeline implements ShouldQueue $orientation = $aspect === 1 ? 'square' : ($aspect > 1 ? 'landscape' : 'portrait'); $ratio = $orientations[$orientation]; - $img = $img->resize($ratio['width'], $ratio['height'], function ($constraint) { - $constraint->aspectRatio(); - $constraint->upsize(); - }); + $img = $img->scaleDown($ratio['width'], $ratio['height']); $extension = pathinfo($file, PATHINFO_EXTENSION); if (in_array(strtolower($extension), ['jpg', 'jpeg'])) { @@ -110,7 +96,7 @@ class ImageResizePipeline implements ShouldQueue } $encoded = $img->encode($encoder); - file_put_contents($file, $encoded); + file_put_contents($file, $encoded->toString()); } catch (Exception $e) { Log::error($e); diff --git a/app/Util/Media/Image.php b/app/Util/Media/Image.php index e8941c035..d47dd3421 100644 --- a/app/Util/Media/Image.php +++ b/app/Util/Media/Image.php @@ -4,6 +4,7 @@ namespace App\Util\Media; use App\Media; use App\Services\StatusService; +use App\Util\Media\ImageDriverManager; use Cache; use Intervention\Image\Encoders\JpegEncoder; use Intervention\Image\Encoders\PngEncoder; @@ -52,19 +53,7 @@ class Image $this->defaultDisk = config('filesystems.default'); - $driver = match (config('image.driver')) { - 'imagick' => \Intervention\Image\Drivers\Imagick\Driver::class, - 'vips' => \Intervention\Image\Drivers\Vips\Driver::class, - default => \Intervention\Image\Drivers\Gd\Driver::class - }; - - $this->imageManager = new ImageManager( - $driver, - autoOrientation: true, - decodeAnimation: true, - blendingColor: 'ffffff', - strip: true - ); + $this->imageManager = ImageDriverManager::createImageManager(); } public function orientations() diff --git a/app/Util/Media/ImageDriverManager.php b/app/Util/Media/ImageDriverManager.php new file mode 100644 index 000000000..e92fdaf2b --- /dev/null +++ b/app/Util/Media/ImageDriverManager.php @@ -0,0 +1,44 @@ + \Intervention\Image\Drivers\Gd\Driver::class, + 'imagick' => \Intervention\Image\Drivers\Imagick\Driver::class, + 'vips' => \Intervention\Image\Drivers\Vips\Driver::class, + default => \Intervention\Image\Drivers\Gd\Driver::class + }; + } + + /** + * Create a new ImageManager instance with the configured driver. + * + * @param array $options Additional options for ImageManager + * @return ImageManager + */ + public static function createImageManager(array $options = []): ImageManager + { + $configOptions = config('image.options', []); + + $options = array_merge($configOptions, $options); + + return new ImageManager( + self::getDriverClass(), + autoOrientation: (bool) ($options['autoOrientation'] ?? true), + decodeAnimation: (bool) ($options['decodeAnimation'] ?? true), + blendingColor: (string) ($options['blendingColor'] ?? 'ffffff'), + strip: (bool) ($options['strip'] ?? true) + ); + } +} diff --git a/composer.json b/composer.json index cd428dbf9..aa2362090 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "buzz/laravel-h-captcha": "^1.0.4", "endroid/qr-code": "^6.0", "guzzlehttp/guzzle": "^7.10", - "intervention/image": "^3.11.2", + "intervention/image-laravel": "^1.5", "intervention/image-driver-vips": "^1.0", "jenssegers/agent": "^2.6", "laravel-notification-channels/expo": "^2.0.0", diff --git a/composer.lock b/composer.lock index 7f36813c2..595bd3595 100644 --- a/composer.lock +++ b/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": "1d3b4afc0a00a1eadb2bddc3be9211c9", + "content-hash": "5ae8f00abedb4f895e08da165267f346", "packages": [ { "name": "aws/aws-crt-php", @@ -62,16 +62,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.359.9", + "version": "3.359.11", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "754b25dae2f50b568b55735931a3cd73263ac5ae" + "reference": "c04a8b3c40bca26da591a8ff14bcc390d26c1644" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/754b25dae2f50b568b55735931a3cd73263ac5ae", - "reference": "754b25dae2f50b568b55735931a3cd73263ac5ae", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c04a8b3c40bca26da591a8ff14bcc390d26c1644", + "reference": "c04a8b3c40bca26da591a8ff14bcc390d26c1644", "shasum": "" }, "require": { @@ -153,9 +153,9 @@ "support": { "forum": "https://github.com/aws/aws-sdk-php/discussions", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.359.9" + "source": "https://github.com/aws/aws-sdk-php/tree/3.359.11" }, - "time": "2025-11-10T19:14:56+00:00" + "time": "2025-11-12T19:18:02+00:00" }, { "name": "bacon/bacon-qr-code", @@ -1945,6 +1945,90 @@ ], "time": "2025-10-15T15:19:56+00:00" }, + { + "name": "intervention/image-laravel", + "version": "1.5.6", + "source": { + "type": "git", + "url": "https://github.com/Intervention/image-laravel.git", + "reference": "056029431400a5cc56036172787a578f334683c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/image-laravel/zipball/056029431400a5cc56036172787a578f334683c4", + "reference": "056029431400a5cc56036172787a578f334683c4", + "shasum": "" + }, + "require": { + "illuminate/http": "^8|^9|^10|^11|^12", + "illuminate/routing": "^8|^9|^10|^11|^12", + "illuminate/support": "^8|^9|^10|^11|^12", + "intervention/image": "^3.11", + "php": "^8.1" + }, + "require-dev": { + "ext-fileinfo": "*", + "orchestra/testbench": "^8.18 || ^9.9", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Image": "Intervention\\Image\\Laravel\\Facades\\Image" + }, + "providers": [ + "Intervention\\Image\\Laravel\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Intervention\\Image\\Laravel\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "Laravel Integration of Intervention Image", + "homepage": "https://image.intervention.io/", + "keywords": [ + "gd", + "image", + "imagick", + "laravel", + "resize", + "thumbnail", + "watermark" + ], + "support": { + "issues": "https://github.com/Intervention/image-laravel/issues", + "source": "https://github.com/Intervention/image-laravel/tree/1.5.6" + }, + "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-04-04T15:09:55+00:00" + }, { "name": "jaybizzle/crawler-detect", "version": "v1.3.6", @@ -2273,16 +2357,16 @@ }, { "name": "laravel/framework", - "version": "v12.37.0", + "version": "v12.38.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "3c3c4ad30f5b528b164a7c09aa4ad03118c4c125" + "reference": "7f3012af6059f5f64a12930701cd8caed6cf7c17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/3c3c4ad30f5b528b164a7c09aa4ad03118c4c125", - "reference": "3c3c4ad30f5b528b164a7c09aa4ad03118c4c125", + "url": "https://api.github.com/repos/laravel/framework/zipball/7f3012af6059f5f64a12930701cd8caed6cf7c17", + "reference": "7f3012af6059f5f64a12930701cd8caed6cf7c17", "shasum": "" }, "require": { @@ -2400,7 +2484,7 @@ "phpstan/phpstan": "^2.0", "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", "predis/predis": "^2.3|^3.0", - "resend/resend-php": "^0.10.0", + "resend/resend-php": "^0.10.0|^1.0", "symfony/cache": "^7.2.0", "symfony/http-client": "^7.2.0", "symfony/psr-http-message-bridge": "^7.2.0", @@ -2434,7 +2518,7 @@ "predis/predis": "Required to use the predis connector (^2.3|^3.0).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", - "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0|^1.0).", "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", @@ -2488,7 +2572,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-11-04T15:39:33+00:00" + "time": "2025-11-13T02:12:47+00:00" }, { "name": "laravel/helpers", @@ -2549,16 +2633,16 @@ }, { "name": "laravel/horizon", - "version": "v5.39.0", + "version": "v5.40.0", "source": { "type": "git", "url": "https://github.com/laravel/horizon.git", - "reference": "bf8f5242f48cff5870e7b30620d872077effe9da" + "reference": "bc15081bd94a150ca5f299e6b3a5217a05708f97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/horizon/zipball/bf8f5242f48cff5870e7b30620d872077effe9da", - "reference": "bf8f5242f48cff5870e7b30620d872077effe9da", + "url": "https://api.github.com/repos/laravel/horizon/zipball/bc15081bd94a150ca5f299e6b3a5217a05708f97", + "reference": "bc15081bd94a150ca5f299e6b3a5217a05708f97", "shasum": "" }, "require": { @@ -2623,9 +2707,9 @@ ], "support": { "issues": "https://github.com/laravel/horizon/issues", - "source": "https://github.com/laravel/horizon/tree/v5.39.0" + "source": "https://github.com/laravel/horizon/tree/v5.40.0" }, - "time": "2025-11-04T14:35:53+00:00" + "time": "2025-11-11T22:41:12+00:00" }, { "name": "laravel/passport", @@ -5658,16 +5742,16 @@ }, { "name": "predis/predis", - "version": "v2.4.0", + "version": "v2.4.1", "source": { "type": "git", "url": "https://github.com/predis/predis.git", - "reference": "f49e13ee3a2a825631562aa0223ac922ec5d058b" + "reference": "07105e050622ed80bd60808367ced9e379f31530" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/predis/predis/zipball/f49e13ee3a2a825631562aa0223ac922ec5d058b", - "reference": "f49e13ee3a2a825631562aa0223ac922ec5d058b", + "url": "https://api.github.com/repos/predis/predis/zipball/07105e050622ed80bd60808367ced9e379f31530", + "reference": "07105e050622ed80bd60808367ced9e379f31530", "shasum": "" }, "require": { @@ -5708,7 +5792,7 @@ ], "support": { "issues": "https://github.com/predis/predis/issues", - "source": "https://github.com/predis/predis/tree/v2.4.0" + "source": "https://github.com/predis/predis/tree/v2.4.1" }, "funding": [ { @@ -5716,7 +5800,7 @@ "type": "github" } ], - "time": "2025-04-30T15:16:02+00:00" + "time": "2025-11-12T18:00:11+00:00" }, { "name": "psr/cache", @@ -8270,16 +8354,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.3.6", + "version": "v7.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "6379e490d6ecfc5c4224ff3a754b90495ecd135c" + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6379e490d6ecfc5c4224ff3a754b90495ecd135c", - "reference": "6379e490d6ecfc5c4224ff3a754b90495ecd135c", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/db488a62f98f7a81d5746f05eea63a74e55bb7c4", + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4", "shasum": "" }, "require": { @@ -8329,7 +8413,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.6" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.7" }, "funding": [ { @@ -8349,20 +8433,20 @@ "type": "tidelift" } ], - "time": "2025-11-06T11:05:57+00:00" + "time": "2025-11-08T16:41:12+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.6", + "version": "v7.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "f9a34dc0196677250e3609c2fac9de9e1551a262" + "reference": "10b8e9b748ea95fa4539c208e2487c435d3c87ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f9a34dc0196677250e3609c2fac9de9e1551a262", - "reference": "f9a34dc0196677250e3609c2fac9de9e1551a262", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/10b8e9b748ea95fa4539c208e2487c435d3c87ce", + "reference": "10b8e9b748ea95fa4539c208e2487c435d3c87ce", "shasum": "" }, "require": { @@ -8447,7 +8531,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.6" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.7" }, "funding": [ { @@ -8467,7 +8551,7 @@ "type": "tidelift" } ], - "time": "2025-11-06T20:58:12+00:00" + "time": "2025-11-12T11:38:40+00:00" }, { "name": "symfony/mailer", diff --git a/config/image.php b/config/image.php index abff3474c..a136e46b8 100644 --- a/config/image.php +++ b/config/image.php @@ -7,12 +7,41 @@ return [ | Image Driver |-------------------------------------------------------------------------- | - | Intervention Image supports "GD Library" or "Imagick" to process - | images internally. You may choose one of them according to your PHP - | configuration. By default PHP's "GD Library" implementation is used. + | Intervention Image supports “GD Library” and “Imagick” to process images + | internally. Depending on your PHP setup, you can choose one of them. | - | Supported: "gd", "imagick" + | Included options: + | - gd = \Intervention\Image\Drivers\Gd\Driver::class + | - imagick = \Intervention\Image\Drivers\Imagick\Driver::class + | - vips = \Intervention\Image\Drivers\vips\Driver::class | */ + 'driver' => env('IMAGE_DRIVER', 'gd'), + + /* + |-------------------------------------------------------------------------- + | Configuration Options + |-------------------------------------------------------------------------- + | + | These options control the behavior of Intervention Image. + | + | - "autoOrientation" controls whether an imported image should be + | automatically rotated according to any existing Exif data. + | + | - "decodeAnimation" decides whether a possibly animated image is + | decoded as such or whether the animation is discarded. + | + | - "blendingColor" Defines the default blending color. + | + | - "strip" controls if meta data like exif tags should be removed when + | encoding images. + */ + + 'options' => [ + 'autoOrientation' => true, + 'decodeAnimation' => true, + 'blendingColor' => env('IMAGE_BLENDINGCOLOR', 'ffffff'), + 'strip' => env('IMAGE_STRIP', true), + ] ];