updated to laravel 11

This commit is contained in:
2024-04-22 18:16:33 +10:00
parent 5fbca80a3c
commit 5b7da699bd
503 changed files with 9672 additions and 73262 deletions

View File

@@ -0,0 +1,113 @@
<?php
namespace App\Jobs\Media;
use App\Models\Media;
use App\Helpers;
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\Storage;
use Intervention\Image\ImageManager;
use Intervention\Image\Drivers\Imagick\Driver;
class GenerateVariants implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Media ID
*
* @var String
*/
public $media_name;
/**
* Overwrite existing
*
* @var bool
*/
public $overwrite;
/**
* Create a new job instance.
*
* @param Media $media The media to process
*/
public function __construct(Media $media, bool $overwrite = true)
{
$this->media_name = $media->name;
$this->overwrite = $overwrite;
}
/**
* Get the middleware the job should pass through.
*
* @return array<int, object>
*/
public function middleware(): array
{
return [new WithoutOverlapping($this->media_name)];
}
/**
* Execute the job.
*/
public function handle(): void
{
$media = Media::find($this->media_name);
if ($media === null) {
return;
}
if(Storage::disk('media')->exists($media->hash) === false) {
return;
}
$variantData = $media->getVariantTypes($matchingMimeType);
if(count($variantData) === 0) {
return;
}
$temp = $media->getAsTempFile();
if($temp === null) {
return;
}
$tempDir = pathinfo($temp, PATHINFO_DIRNAME);
$media->deleteAllVariants();
/* Images */
if($matchingMimeType === 'image/*') {
$manager = new ImageManager(new Driver());
$image = $manager->read($temp);
$isPortrait = $image->height() > $image->width();
foreach ($variantData as $variantName => $size) {
$image = $manager->read($temp);
if($isPortrait === true) {
$width = $size['height'];
$height = $size['width'];
} else {
$width = $size['width'];
$height = $size['height'];
}
if($variantName !== 'scaled' && ($image->height() < $height || $image->width() < $width)) {
continue;
}
$image->scaleDown($width, $height);
$variantFile = $tempDir . '/' . $media->hash . '-' . $variantName . '.webp';
$image->save($variantFile, quality: 75);
$media->addVariant($variantName, 'image/webp', 'webp', $variantFile);
}//end foreach
}
}
}

View File

@@ -1,393 +0,0 @@
<?php
namespace App\Jobs;
use App\Models\Media;
use App\Models\MediaJob;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use FFMpeg;
use FFMpeg\Coordinate\Dimension;
use FFMpeg\FFProbe;
use FFMpeg\Format\VideoInterface;
use Intervention\Image\Facades\Image;
/** @property on $format */
class MediaWorkerJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/**
* MediaJob item
*
* @var MediaJob
*/
protected $mediaJob;
/**
* Create a new job instance.
*
* @param MediaJob $mediaJob The mediaJob model.
* @return void
*/
public function __construct(MediaJob $mediaJob)
{
$this->mediaJob = $mediaJob;
}
/**
* Execute the job.
*
* @return void
*/
public function handle(): void
{
$media = $this->mediaJob->media()->first();
$newMedia = false;
$data = json_decode($this->mediaJob->data, true);
try {
// FILE
if (array_key_exists('file', $data) === true) {
if (file_exists($data['file']) === false) {
$this->throwMediaJobFailure('temporary upload file no longer exists');
}
// convert HEIC files to JPG
$fileExtension = File::extension($data['file']);
if ($fileExtension === 'heic') {
$this->mediaJob->setStatusProcessing(0, 0, 'converting image');
// Get the path without the file name
$uploadedFileDirectory = dirname($data['file']);
// Convert the HEIC file to JPG
$jpgFileName = pathinfo($data['file'], PATHINFO_FILENAME) . '.jpg';
$jpgFilePath = $uploadedFileDirectory . '/' . $jpgFileName;
if (file_exists($jpgFilePath) === true) {
$this->throwMediaJobFailure('file already exists on server');
}
Image::make($data['file'])->save($jpgFilePath);
// Update the uploaded file path and file name
unlink($data['file']);
$data['file'] = $jpgFileName;
}//end if
// get security
$security = [];
if ($media === null) {
if (array_key_exists('security', $data) === true) {
$security = $data['security'];
}
} else {
$security['type'] = $media->security_type;
$security['data'] = $media->security_data;
}
// get storage
$storage = '';
if ($media === null) {
if (array_key_exists('storage', $data) === true) {
$storage = $data['storage'];
}
} else {
$storage = $media->storage;
}
if ($storage === '') {
if (count($security) === 0 || $security['type'] === '') {
if (strpos($data['mime_type'], 'image/') === 0) {
$storage = 'local';
} else {
$storage = 'cdn';
}
} else {
$storage = 'private';
}
}
// Check if file already exists
$exists = Storage::disk($storage)->exists($data['name']);
if ($exists === true) {
if (array_key_exists('noreplace', $data) === true && isTrue($data['noreplace']) === true) {
$this->throwMediaJobFailure('file already exists on server');
}
}
if ($exists === true) {
$pathInfo = pathinfo($data['name']);
$basename = $pathInfo['filename'];
$extension = $pathInfo['extension'];
$index = 0;
do {
$index++;
$data['name'] = $basename . '-' . $index . '.' . $extension;
} while (Storage::disk($storage)->exists($data['name']) === true);
}
if ($media === null) {
$newMedia = true;
$media = new Media([
'user_id' => $this->mediaJob->user_id,
'title' => $data['title'],
'name' => $data['name'],
'mime_type' => $data['mime_type'],
'size' => $data['size'],
'security_type' => $data['security']['type'],
'security_data' => $data['security']['data'],
'storage' => $storage,
]);
}//end if
$media->setStagingFile($data['file']);
} else {
if ($media === null) {
$this->throwMediaJobFailure('The media item no longer exists');
}
}//end if
if (array_key_exists('transform', $data) === true) {
$media->createStagingFile();
// Modifications
if (strpos($media->mime_type, 'image/') === 0) {
$modified = false;
$image = Image::make($media->getStagingFilePath());
// ROTATE
if (array_key_exists("rotate", $data['transform']) === true) {
$rotate = intval($data['transform']['rotate']);
if ($rotate !== 0) {
$this->mediaJob->setStatusProcessing(0, 0, 'rotating image');
$image = $image->rotate($rotate);
$modified = true;
}
}
// FLIP-H/V
if (array_key_exists('flip', $data['transform']) === true) {
if (stripos($data['transform']['flip'], 'h') !== false) {
$this->mediaJob->setStatusProcessing(0, 0, 'flipping image');
$image = $image->flip('h');
$modified = true;
}
if (stripos($data['transform']['flip'], 'v') !== false) {
$this->mediaJob->setStatusProcessing(0, 0, 'flipping image');
$image = $image->flip('v');
$modified = true;
}
}
// CROP
if (array_key_exists("crop", $data['transform']) === true) {
$cropData = $data['transform']['crop'];
$width = intval(arrayDefaultValue("width", $cropData, $image->getWidth()));
$height = intval(arrayDefaultValue("height", $cropData, $image->getHeight()));
$x = intval(arrayDefaultValue("x", $cropData, 0));
$y = intval(arrayDefaultValue("y", $cropData, 0));
$this->mediaJob->setStatusProcessing(0, 0, 'cropping image');
$image = $image->crop($width, $height, $x, $y);
$modified = true;
}//end if
if ($modified === true) {
$image->save();
}
} elseif (strpos($data['mime_type'], 'video/') === 0) {
$stagingFilePath = $media->getStagingFilePath();
$ffmpeg = FFMpeg\FFMpeg::create();
$video = $ffmpeg->open($stagingFilePath);
$format = $this->detectVideoFormat($stagingFilePath);
$modified = false;
if ($format === null) {
$this->mediaJob->setStatusFailed('Unsupported video format');
return;
}
/** @var FFMpeg\Media\Video::filters */
$filters = $video->filters();
// ROTATE
if (array_key_exists("rotate", $data['transform']) === true) {
$rotate = intval($data['transform']['rotate']);
$rotate = (($rotate % 360 + 360) % 360); // remove excess rotations
$rotate = intval(round($rotate / 90) * 90); // round to nearest 90%
if ($rotate > 0) {
$this->mediaJob->setStatusProcessing(0, 0, 'rotating video');
if ($rotate === 90) {
$filters->rotate(FFMpeg\Filters\Video\RotateFilter::ROTATE_270);
$modified = true;
} elseif ($rotate === 180) {
$filters->rotate(FFMpeg\Filters\Video\RotateFilter::ROTATE_180);
$modified = true;
} elseif ($rotate === 270) {
$filters->rotate(FFMpeg\Filters\Video\RotateFilter::ROTATE_90);
$modified = true;
}
}
}
// FLIP-H/V
if (array_key_exists('flip', $data['transform']) === true) {
if (stripos($data['transform']['flip'], 'h') !== false) {
$this->mediaJob->setStatusProcessing(0, 0, 'flipping video');
$filters->hflip()->synchronize();
$modified = true;
}
if (stripos($data['transform']['flip'], 'v') !== false) {
$this->mediaJob->setStatusProcessing(0, 0, 'flipping video');
$filters->vflip()->synchronize();
$modified = true;
}
}
// CROP
if (array_key_exists("crop", $data['transform']) === true) {
$cropData = $data['transform']['crop'];
$videoStream = $video->getStreams()->videos()->first();
$width = intval(arrayDefaultValue("width", $cropData, $videoStream->get('width')));
$height = intval(arrayDefaultValue("height", $cropData, $videoStream->get('height')));
$x = intval(arrayDefaultValue("x", $cropData, 0));
$y = intval(arrayDefaultValue("y", $cropData, 0));
$cropDimension = new Dimension($width, $height);
$this->mediaJob->setStatusProcessing(0, 0, 'cropping video');
$filters->crop($cropDimension, $x, $y)->synchronize();
$modified = true;
}//end if
$tempFilePath = generateTempFilePath(pathinfo($stagingFilePath, PATHINFO_EXTENSION));
if (method_exists($format, 'on') === true) {
$mediaJob = $this->mediaJob;
$format->on('progress', function ($video, $format, $percentage) use ($mediaJob) {
$mediaJob->setStatusProcessing($percentage, 100, 'transcoded');
});
}
if ($modified === true) {
$video->save($format, $tempFilePath);
$media->changeStagingFile($tempFilePath);
}
}//end if
}//end if
// Update attributes
if (array_key_exists('title', $data) === true) {
$media->title = $data['title'];
}
// Relocate file (if requested)
if (array_key_exists('security', $data) === true && array_key_exists('type', $data['security']) === true) {
$media->security_type = $data['security']['type'];
$media->security_data = $data['security']['data'];
}
if (array_key_exists('storage', $data) === true) {
if ($media->storage !== $data['storage']) {
$media->createStagingFile();
Storage::disk($media->storage)->delete($media->name);
$media->storage = $data['storage'];
}
}
// Finish media object
if ($media->hasStagingFile() === true) {
$this->mediaJob->setStatusProcessing(0, 0, 'transferring to cdn');
$media->deleteFile();
$media->saveStagingFile(true);
}
$media->save();
$this->mediaJob->media_id = $media->id;
$this->mediaJob->setStatusComplete();
} catch (\Exception $e) {
if ($this->mediaJob->status !== 'failed') {
$this->mediaJob->setStatusFailed('Unexpected server error occurred');
}
if ($media !== null) {
$media->deleteStagingFile();
if ($newMedia === true) {
$media->delete();
}
}
Log::error($e->getMessage() . "\n" . $e->getFile() . " - " . $e->getLine() . "\n" . $e->getTraceAsString());
$this->fail($e);
}//end try
}
/**
* Detects the format of a video using FFProbe
*
* @param string $videoPath The video file path.
* @return VideoInterface | null
*/
public function detectVideoFormat(string $videoPath): VideoInterface | null
{
$ffprobe = FFProbe::create();
$videoStream = $ffprobe
->streams($videoPath) // Provide the path to the video file
->videos() // Filter video streams
->first();
$codecName = $videoStream->get('codec_name');
$codecToFormatClass = [
'h264' => 'FFMpeg\Format\Video\X264',
'wmv2' => 'FFMpeg\Format\Video\WMV',
'vp9' => 'FFMpeg\Format\Video\WebM',
'theora' => 'FFMpeg\Format\Video\Ogg',
'mpeg4' => 'FFMpeg\Format\Video\Mpeg4',
// Add more mappings as needed
];
if (isset($codecToFormatClass[$codecName]) === false) {
Log::info("Unsupported codec: $codecName");
return null;
}
$formatClassName = $codecToFormatClass[$codecName];
if (class_exists($formatClassName) === false) {
Log::info("Format class does not exist: $formatClassName");
return null;
}
return new $formatClassName();
}
/**
* Set failure status of MediaJob and throw exception.
*
* @param string $error The failure message.
* @return void
*/
private function throwMediaJobFailure(string $error): void
{
$this->mediaJob->setStatusFailed($error);
throw new \Exception($error);
}
}

43
app/Jobs/ProcessMedia.php Normal file
View File

@@ -0,0 +1,43 @@
<?php
namespace App\Jobs;
use App\Models\Media;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
class ProcessMedia implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* File path
*
* @var String
*/
public $file;
/**
* Create a new job instance.
*
* @param String $file The file path
*/
public function __construct(string $file)
{
$this->file = $file;
}
/**
* Execute the job.
*/
public function handle(): void
{
if(Storage::disk('temp')->exists($this->media->name)) {
}
}
}

View File

@@ -10,12 +10,9 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
class SendEmailJob implements ShouldQueue
class SendEmail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Mail to receipt
@@ -31,7 +28,6 @@ class SendEmailJob implements ShouldQueue
*/
public $mailable;
/**
* Create a new job instance.
*