diff --git a/app/Jobs/ImportPipeline/ImportMediaToCloudPipeline.php b/app/Jobs/ImportPipeline/ImportMediaToCloudPipeline.php index f884e7f2a..cdf91e376 100644 --- a/app/Jobs/ImportPipeline/ImportMediaToCloudPipeline.php +++ b/app/Jobs/ImportPipeline/ImportMediaToCloudPipeline.php @@ -14,6 +14,7 @@ use App\Models\ImportPost; use App\Media; use App\Services\MediaStorageService; use Illuminate\Support\Facades\Storage; +use App\Jobs\VideoPipeline\VideoThumbnailToCloudPipeline; class ImportMediaToCloudPipeline implements ShouldQueue, ShouldBeUniqueUntilProcessing { @@ -118,7 +119,11 @@ class ImportMediaToCloudPipeline implements ShouldQueue, ShouldBeUniqueUntilProc } if($res === 'success') { - Storage::disk('local')->delete($media->media_path); + if($media->mime === 'video/mp4') { + VideoThumbnailToCloudPipeline::dispatch($media)->onQueue('low'); + } else { + Storage::disk('local')->delete($media->media_path); + } } } } diff --git a/app/Jobs/VideoPipeline/VideoThumbnailToCloudPipeline.php b/app/Jobs/VideoPipeline/VideoThumbnailToCloudPipeline.php new file mode 100644 index 000000000..87931bd7a --- /dev/null +++ b/app/Jobs/VideoPipeline/VideoThumbnailToCloudPipeline.php @@ -0,0 +1,147 @@ +media->id; + } + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [(new WithoutOverlapping("media:video-thumb-to-cloud:id-{$this->media->id}"))->shared()->dontRelease()]; + } + + /** + * Create a new job instance. + */ + public function __construct(Media $media) + { + $this->media = $media; + } + + /** + * Execute the job. + */ + public function handle(): void + { + if((bool) config_cache('pixelfed.cloud_storage') === false) { + return; + } + + $media = $this->media; + + if($media->mime != 'video/mp4') { + return; + } + + if($media->profile_id === null || $media->status_id === null) { + return; + } + + if($media->thumbnail_url) { + return; + } + + $base = $media->media_path; + $path = explode('/', $base); + $name = last($path); + + try { + $t = explode('.', $name); + $t = $t[0].'_thumb.jpeg'; + $i = count($path) - 1; + $path[$i] = $t; + $save = implode('/', $path); + $video = FFMpeg::open($base) + ->getFrameFromSeconds(1) + ->export() + ->toDisk('local') + ->save($save); + + if(!$save) { + return; + } + + $media->thumbnail_path = $save; + $p = explode('/', $media->media_path); + array_pop($p); + $pt = explode('/', $save); + $thumbname = array_pop($pt); + $storagePath = implode('/', $p); + $thumb = storage_path('app/' . $save); + $thumbUrl = ResilientMediaStorageService::store($storagePath, $thumb, $thumbname); + $media->thumbnail_url = $thumbUrl; + $media->save(); + + $blurhash = Blurhash::generate($media); + if($blurhash) { + $media->blurhash = $blurhash; + $media->save(); + } + + if(str_starts_with($save, 'public/m/_v2/') && str_ends_with($save, '.jpeg')) { + Storage::delete($save); + } + + if(str_starts_with($media->media_path, 'public/m/_v2/') && str_ends_with($media->media_path, '.mp4')) { + Storage::disk('local')->delete($media->media_path); + } + } catch (Exception $e) { + } + + if($media->status_id) { + Cache::forget('status:transformer:media:attachments:' . $media->status_id); + MediaService::del($media->status_id); + Cache::forget('status:thumb:nsfw0' . $media->status_id); + Cache::forget('status:thumb:nsfw1' . $media->status_id); + Cache::forget('pf:services:sh:id:' . $media->status_id); + StatusService::del($media->status_id); + } + } +}