diff --git a/app/Helpers/Temp.php b/app/Helpers/Temp.php index 3c417e2..e65328d 100644 --- a/app/Helpers/Temp.php +++ b/app/Helpers/Temp.php @@ -6,14 +6,15 @@ /** * Generate a temporary file path. * - * @return str The filtered array. + * @param string $extension The file extension to use. + * @return string The filtered array. */ -function generateTempFilePath(): string +function generateTempFilePath(string $extension = ''): string { $temporaryDir = storage_path('app/tmp'); if (is_dir($temporaryDir) === false) { mkdir($temporaryDir, 0777, true); } - return $temporaryDir . DIRECTORY_SEPARATOR . uniqid('upload_', true); + return $temporaryDir . DIRECTORY_SEPARATOR . uniqid('upload_', true) . ($extension !== '' ? ".{$extension}" : ''); } diff --git a/app/Http/Controllers/Api/MediaController.php b/app/Http/Controllers/Api/MediaController.php index 001de9c..1aab7a3 100644 --- a/app/Http/Controllers/Api/MediaController.php +++ b/app/Http/Controllers/Api/MediaController.php @@ -84,10 +84,10 @@ class MediaController extends ApiController $request->merge([ 'title' => $request->get('title', ''), - 'name' => '', + 'name' => $file->getClientOriginalName(), 'size' => $file->getSize(), 'mime_type' => $file->getMimeType(), - 'status' => '', + 'status' => 'Creating Media', ]); // We store images by default locally @@ -147,6 +147,7 @@ class MediaController extends ApiController } } + $medium->status('Updating Media'); $medium->update($request->except(['file','transform'])); $transformData = []; @@ -168,6 +169,8 @@ class MediaController extends ApiController if (count($transformData) > 0) { $medium->transform($transformData); + } else { + $medium->ok(); } return $this->respondAsResource(MediaConductor::model($request, $medium)); diff --git a/app/Jobs/MediaJob.php b/app/Jobs/MediaJob.php index c3e2453..ecfeb7c 100644 --- a/app/Jobs/MediaJob.php +++ b/app/Jobs/MediaJob.php @@ -29,6 +29,13 @@ class MediaJob implements ShouldQueue */ protected $media; + /** + * Actions should be silent (not update the status field) + * + * @var boolean + */ + protected $silent; + /** * Actions to make on the Media * @@ -40,13 +47,15 @@ class MediaJob implements ShouldQueue /** * Create a new job instance. * - * @param Media $media The media model. - * @param array $actions The media actions to make. + * @param Media $media The media model. + * @param array $actions The media actions to make. + * @param boolean $silent Update the media status with progress. * @return void */ - public function __construct(Media $media, array $actions) + public function __construct(Media $media, array $actions, bool $silent = false) { $this->media = $media; + $this->silent = $silent; $this->actions = $actions; } @@ -72,6 +81,10 @@ class MediaJob implements ShouldQueue // convert HEIC files to JPG $fileExtension = File::extension($filePath); if ($fileExtension === 'heic') { + if ($this->silent === false) { + $this->media->status('Converting image'); + } + // Get the path without the file name $uploadedFileDirectory = dirname($filePath); @@ -90,11 +103,12 @@ class MediaJob implements ShouldQueue $filePath = $jpgFilePath; $this->media->name = $jpgFileName; $this->media->save(); - } + }//end if // Check if file already exists if (Storage::disk($this->media->storage)->exists($this->media->name) === true) { if (array_key_exists('replace', $uploadData) === false || isTrue($uploadData['replace']) === false) { + $this->media->error("File already exists in storage"); $errorStr = "cannot upload file {$this->media->storage} " . // phpcs:ignore "/ {$this->media->name} as it already exists"; Log::info($errorStr); @@ -110,24 +124,37 @@ class MediaJob implements ShouldQueue // Modifications if (strpos($this->media->mime_type, 'image/') === 0) { - $image = Image::make($filePath); + $modified = false; + $image = Image::make($this->media->getStagingFilePath()); // ROTATE if (array_key_exists("rotate", $this->actions) === true) { $rotate = intval($this->actions["rotate"]); if ($rotate !== 0) { + if ($this->silent === false) { + $this->media->status('Rotating image'); + } $image = $image->rotate($rotate); + $modified = true; } } // FLIP-H/V if (array_key_exists('flip', $this->actions) === true) { if (stripos($this->actions['flip'], 'h') !== false) { + if ($this->silent === false) { + $this->media->status('Flipping image'); + } $image = $image->flip('h'); + $modified = true; } if (stripos($this->actions['flip'], 'v') !== false) { + if ($this->silent === false) { + $this->media->status('Flipping image'); + } $image = $image->flip('v'); + $modified = true; } } @@ -139,10 +166,16 @@ class MediaJob implements ShouldQueue $x = intval(arrayDefaultValue("x", $cropData, 0)); $y = intval(arrayDefaultValue("y", $cropData, 0)); + if ($this->silent === false) { + $this->media->status('Cropping image'); + } $image = $image->crop($width, $height, $x, $y); + $modified = true; }//end if - $image->save($filePath); + if ($modified === true) { + $image->save(); + } } elseif (strpos($this->media->mime_type, 'video/') === 0) { $ffmpeg = FFMpeg\FFMpeg::create(); $video = $ffmpeg->open($this->media->getStagingFilePath()); @@ -157,6 +190,10 @@ class MediaJob implements ShouldQueue $rotate = (round($rotate / 90) * 90); // round to nearest 90% if ($rotate > 0) { + if ($this->silent === false) { + $this->media->status('Rotating video'); + } + if ($rotate === 90) { $filters->rotate(FFMpeg\Filters\Video\RotateFilter::ROTATE_90); } elseif ($rotate === 190) { @@ -170,10 +207,16 @@ class MediaJob implements ShouldQueue // FLIP-H/V if (array_key_exists('flip', $this->actions) === true) { if (stripos($this->actions['flip'], 'h') !== false) { + if ($this->silent === false) { + $this->media->status('Flipping video'); + } $filters->hflip()->synchronize(); } if (stripos($this->actions['flip'], 'v') !== false) { + if ($this->silent === false) { + $this->media->status('Flipping video'); + } $filters->vflip()->synchronize(); } } @@ -190,6 +233,9 @@ class MediaJob implements ShouldQueue $cropDimension = new Dimension($width, $height); + if ($this->silent === false) { + $this->media->status('Cropping video'); + } $filters->crop($cropDimension, $x, $y)->synchronize(); }//end if @@ -213,11 +259,17 @@ class MediaJob implements ShouldQueue } // Finish media object - $this->media->saveStagingFile(); + $this->media->saveStagingFile(true, $this->silent); $this->media->ok(); + $this->media->save(); } catch (\Exception $e) { + $this->media->deleteStagingFile(); + + if (strpos($this->media->status, 'Error') !== 0) { + $this->media->error('Failed to process'); + } + Log::error($e->getMessage()); - $this->media->error("Failed"); $this->fail($e); }//end try } diff --git a/app/Models/Media.php b/app/Models/Media.php index 3df33c8..271afe0 100644 --- a/app/Models/Media.php +++ b/app/Models/Media.php @@ -374,10 +374,11 @@ class Media extends Model /** * Transform the media through the Media Job Queue * - * @param array $transform The transform data. + * @param array $transform The transform data. + * @param boolean $silent Update the medium progress through its status field. * @return void */ - public function transform(array $transform): void + public function transform(array $transform, bool $silent = false): void { foreach ($transform as $key => $value) { if (is_string($value) === true) { @@ -398,7 +399,7 @@ class Media extends Model } try { - MediaJob::dispatch($this, $transform)->onQueue('media'); + MediaJob::dispatch($this, $transform, $silent)->onQueue('media'); } catch (\Exception $e) { $this->error('Failed to transform media'); throw $e; @@ -692,9 +693,10 @@ class Media extends Model */ public function createStagingFile(): bool { - if ($this->stagingFilePath !== "") { + if ($this->stagingFilePath === "") { $readStream = Storage::disk($this->storageDisk)->readStream($this->name); - $filePath = tempnam(sys_get_temp_dir(), 'download-'); + $filePath = generateTempFilePath(pathinfo($this->name, PATHINFO_EXTENSION)); + $writeStream = fopen($filePath, 'w'); while (feof($readStream) !== true) { fwrite($writeStream, fread($readStream, 8192)); @@ -703,7 +705,7 @@ class Media extends Model fclose($writeStream); $this->stagingFilePath = $filePath; - } + }//end if return $this->stagingFilePath !== ""; } @@ -712,9 +714,10 @@ class Media extends Model * Save the Staging File to storage * * @param boolean $delete Delete the existing staging file. + * @param boolean $silent Update the status field with the progress. * @return void */ - public function saveStagingFile(bool $delete = true): void + public function saveStagingFile(bool $delete = true, bool $silent = false): void { if (strlen($this->storage) > 0 && strlen($this->name) > 0) { if (Storage::disk($this->storage)->exists($this->name) === true) { @@ -723,10 +726,20 @@ class Media extends Model /** @var Illuminate\Filesystem\FilesystemAdapter */ $fileSystem = Storage::disk($this->storage); + if ($silent === false) { + $this->status('Uploading to CDN'); + } $fileSystem->putFileAs('/', $this->stagingFilePath, $this->name); } + if ($silent === false) { + $this->status('Generating Thumbnail'); + } $this->generateThumbnail(); + + if ($silent === false) { + $this->status('Generating Variants'); + } $this->generateVariants(); if ($delete === true) { @@ -780,7 +793,7 @@ class Media extends Model } } - $filePath = $this->createStagingFile(); + $filePath = $this->getStagingFilePath(); $fileExtension = File::extension($this->name); $tempImagePath = tempnam(sys_get_temp_dir(), 'thumb'); @@ -894,12 +907,12 @@ class Media extends Model */ public function generateVariants(): void { - if (strpos($this->media->mime_type, 'image/') === 0) { + if (strpos($this->mime_type, 'image/') === 0) { // Generate additional image sizes $sizes = Media::getObjectVariants('image'); // download original from CDN if no local file - $filePath = $this->createStagingFile(); + $filePath = $this->getStagingFilePath(); // delete existing variants if (is_array($this->variants) === true) { diff --git a/resources/js/components/SMImageGallery.vue b/resources/js/components/SMImageGallery.vue index 621b482..3c204fd 100644 --- a/resources/js/components/SMImageGallery.vue +++ b/resources/js/components/SMImageGallery.vue @@ -1,82 +1,86 @@