diff --git a/app/Jobs/Media/GenerateVariants.php b/app/Jobs/Media/GenerateVariants.php index 154a719..a9373fa 100644 --- a/app/Jobs/Media/GenerateVariants.php +++ b/app/Jobs/Media/GenerateVariants.php @@ -4,12 +4,15 @@ namespace App\Jobs\Media; use App\Models\Media; use App\Helpers; +use FFMpeg\Coordinate\TimeCode; +use FFMpeg\FFMpeg; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use Intervention\Image\ImageManager; use Intervention\Image\Drivers\Imagick\Driver; @@ -108,6 +111,85 @@ class GenerateVariants implements ShouldQueue $media->addVariant($variantName, 'image/webp', 'webp', $variantFile); }//end foreach + } else if($matchingMimeType === 'text/plain') { + /* Text */ + $width = $variantData['thumbnail']['width']; + $height = $variantData['thumbnail']['height']; + + $manager = new ImageManager(new Driver()); + $image = $manager->create($width, $height)->fill('fff'); + + // Read the first few lines of the text file + $numLines = 5; + $text = file_get_contents($temp); + $lines = explode("\n", $text); + $previewText = implode("\n", array_slice($lines, 0, $numLines)); + + // Center the text on the image + $fontSize = 8; + $textColor = '#000000'; // Black text color + + // Calculate the position to start drawing the text + $x = 10; // Left padding + $y = 10; // Top padding + + // Draw the text on the canvas with text wrapping + $lines = explode("\n", wordwrap($previewText, 30, "\n", true)); + foreach ($lines as $line) { + $image->text($line, $x, $y, function ($font) use ($fontSize, $textColor) { + $font->file(1); + $font->size($fontSize); + $font->color($textColor); + }); + + // Move to the next line + $y += ($fontSize + 4); // Add some vertical spacing between lines (adjust as needed) + } + + $variantFile = $tempDir . '/' . $media->hash . '-thumbnail.webp'; + $image->save($variantFile, quality: 75); + $media->addVariant('thumbnail', 'image/webp', 'webp', $variantFile); + + } else if($matchingMimeType === 'application/pdf') { + /* PDF */ + $width = $variantData['thumbnail']['width']; + $height = $variantData['thumbnail']['height']; + + $manager = new ImageManager(new Driver()); + $image = $manager->read($temp); + $image->scaleDown($width, $height); + + $variantFile = $tempDir . '/' . $media->hash . '-thumbnail.webp'; + $image->save($variantFile, quality: 75); + $media->addVariant('thumbnail', 'image/webp', 'webp', $variantFile); + + } else if($matchingMimeType === 'video/*') { + /* Video */ + $tempImage = $tempDir . '/' . $media->hash . '-temp-frame.jpg'; + $variantFile = $tempDir . '/' . $media->hash . '-thumbnail.webp'; + + try { + $ffmpeg = FFMpeg::create(); + $video = $ffmpeg->open($temp); + $frame = $video->frame(TimeCode::fromSeconds(5)); + $frame->save($variantFile); + + $width = $variantData['thumbnail']['width']; + $height = $variantData['thumbnail']['height']; + + $manager = new ImageManager(new Driver()); + $image = $manager->read($tempImage); + $image->scaleDown($width, $height); + $image->save($variantFile, quality: 75); + + $media->addVariant('thumbnail', 'image/webp', 'webp', $variantFile); + } catch (\Exception $e) { + Log::error($e); + } + + if(file_exists($tempImage)) { + unlink($tempImage); + } } $media->status = 'ready'; diff --git a/app/Models/Media.php b/app/Models/Media.php index 31f3e01..1f496e3 100644 --- a/app/Models/Media.php +++ b/app/Models/Media.php @@ -82,7 +82,16 @@ class Media extends Model 'xl' => ['width' => 1536, 'height' => 1152], '2xl' => ['width' => 2048, 'height' => 1536], 'scaled' => ['width' => 2560, 'height' => 1920] - ] + ], + 'text/plain' => [ + 'thumbnail' => ['width' => 250, 'height' => 250] + ], + 'application/pdf' => [ + 'thumbnail' => ['width' => 250, 'height' => 250] + ], + 'video/*' => [ + 'thumbnail' => ['width' => 250, 'height' => 250] + ], ]; public static function boot() diff --git a/composer.json b/composer.json index c1ba357..91dcf3c 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,8 @@ "intervention/image": "^3.5", "laravel/framework": "^11.0", "laravel/tinker": "^2.9", - "livewire/livewire": "^3.4" + "livewire/livewire": "^3.4", + "php-ffmpeg/php-ffmpeg": "^1.2" }, "require-dev": { "fakerphp/faker": "^1.23", diff --git a/composer.lock b/composer.lock index b40b32b..b87f1e8 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": "190b7a67985eda8dc061d7e7654cf5e9", + "content-hash": "b23c7501471a4cdcbd1ba04950392725", "packages": [ { "name": "brick/math", @@ -506,6 +506,53 @@ ], "time": "2023-10-06T06:47:41+00:00" }, + { + "name": "evenement/evenement", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Evenement\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" + }, + "time": "2023-08-08T05:53:35+00:00" + }, { "name": "fruitcake/php-cors", "version": "v1.3.0", @@ -2688,6 +2735,95 @@ ], "time": "2024-03-06T16:17:14+00:00" }, + { + "name": "php-ffmpeg/php-ffmpeg", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-FFMpeg/PHP-FFMpeg.git", + "reference": "785a5ba05dd88b3b8146f85f18476b259b23917c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/785a5ba05dd88b3b8146f85f18476b259b23917c", + "reference": "785a5ba05dd88b3b8146f85f18476b259b23917c", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0", + "php": "^8.0 || ^8.1 || ^8.2 || ^8.3", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "spatie/temporary-directory": "^2.0", + "symfony/cache": "^5.4 || ^6.0 || ^7.0", + "symfony/process": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "phpunit/phpunit": "^9.5.10" + }, + "suggest": { + "php-ffmpeg/extras": "A compilation of common audio & video drivers for PHP-FFMpeg" + }, + "type": "library", + "autoload": { + "psr-4": { + "FFMpeg\\": "src/FFMpeg", + "Alchemy\\BinaryDriver\\": "src/Alchemy/BinaryDriver" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Romain Neutron", + "email": "imprec@gmail.com", + "homepage": "http://www.lickmychip.com/" + }, + { + "name": "Phraseanet Team", + "email": "info@alchemy.fr", + "homepage": "http://www.phraseanet.com/" + }, + { + "name": "Patrik Karisch", + "email": "patrik@karisch.guru", + "homepage": "http://www.karisch.guru" + }, + { + "name": "Romain Biard", + "email": "romain.biard@gmail.com", + "homepage": "https://www.strime.io/" + }, + { + "name": "Jens Hausdorf", + "email": "hello@jens-hausdorf.de", + "homepage": "https://jens-hausdorf.de" + }, + { + "name": "Pascal Baljet", + "email": "pascal@protone.media", + "homepage": "https://protone.media" + } + ], + "description": "FFMpeg PHP, an Object Oriented library to communicate with AVconv / ffmpeg", + "keywords": [ + "audio", + "audio processing", + "avconv", + "avprobe", + "ffmpeg", + "ffprobe", + "video", + "video processing" + ], + "support": { + "issues": "https://github.com/PHP-FFMpeg/PHP-FFMpeg/issues", + "source": "https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/v1.2.0" + }, + "time": "2024-01-02T10:37:01+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.2", @@ -2763,6 +2899,55 @@ ], "time": "2023-11-12T21:59:55+00:00" }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, { "name": "psr/clock", "version": "1.0.0", @@ -3479,6 +3664,239 @@ ], "time": "2024-04-27T21:32:50+00:00" }, + { + "name": "spatie/temporary-directory", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/temporary-directory.git", + "reference": "76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a", + "reference": "76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\TemporaryDirectory\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Vanderbist", + "email": "alex@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Easily create, use and destroy temporary directories", + "homepage": "https://github.com/spatie/temporary-directory", + "keywords": [ + "php", + "spatie", + "temporary-directory" + ], + "support": { + "issues": "https://github.com/spatie/temporary-directory/issues", + "source": "https://github.com/spatie/temporary-directory/tree/2.2.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-12-25T11:46:58+00:00" + }, + { + "name": "symfony/cache", + "version": "v7.0.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "2d0d3f92c74c445410d05374908b03e0a1131e2b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/2d0d3f92c74c445410d05374908b03e0a1131e2b", + "reference": "2d0d3f92c74c445410d05374908b03e0a1131e2b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3", + "symfony/var-exporter": "^6.4|^7.0" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/dependency-injection": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/var-dumper": "<6.4" + }, + "provide": { + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/filesystem": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "classmap": [ + "Traits/ValueWrapper.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "support": { + "source": "https://github.com/symfony/cache/tree/v7.0.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-03-27T19:55:25+00:00" + }, + { + "name": "symfony/cache-contracts", + "version": "v3.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache-contracts.git", + "reference": "2c9db6509a1b21dad229606897639d3284f54b2a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/2c9db6509a1b21dad229606897639d3284f54b2a", + "reference": "2c9db6509a1b21dad229606897639d3284f54b2a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Cache\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v3.4.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T14:51:35+00:00" + }, { "name": "symfony/clock", "version": "v7.0.5", @@ -5776,6 +6194,82 @@ ], "time": "2024-03-19T11:57:22+00:00" }, + { + "name": "symfony/var-exporter", + "version": "v7.0.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "c74c568d2a15a1d407cf40d61ea82bc2d521e27b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/c74c568d2a15a1d407cf40d61ea82bc2d521e27b", + "reference": "c74c568d2a15a1d407cf40d61ea82bc2d521e27b", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v7.0.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-03-20T21:25:22+00:00" + }, { "name": "tijsverkoyen/css-to-inline-styles", "version": "v2.2.7",