405 lines
10 KiB
PHP
405 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Helpers;
|
|
use App\Jobs\Media\GenerateVariants;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use function PHPUnit\Framework\stringStartsWith;
|
|
|
|
class Media extends Model
|
|
{
|
|
use HasFactory;
|
|
|
|
/**
|
|
* The attributes that are mass assignable.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $fillable = [
|
|
'name',
|
|
'title',
|
|
'mime_type',
|
|
'size',
|
|
'user_id',
|
|
'hash',
|
|
'password',
|
|
'status'
|
|
];
|
|
|
|
/**
|
|
* The primary key for the model.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $primaryKey = 'name';
|
|
|
|
/**
|
|
* The key type for the model.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $keyType = 'string';
|
|
|
|
/**
|
|
* Indicates if the IDs are auto-incrementing.
|
|
*
|
|
* @var bool
|
|
*/
|
|
public $incrementing = false;
|
|
|
|
/**
|
|
* The attributes that should be cast.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $casts = [
|
|
'variants' => 'array'
|
|
];
|
|
|
|
/**
|
|
* The "booted" method of the model.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected $appends = [
|
|
'url',
|
|
'thumbnail',
|
|
'file_type'
|
|
];
|
|
|
|
/**
|
|
* Media variant details.
|
|
*
|
|
* @var int[][][]
|
|
*/
|
|
protected static $variants = [
|
|
'image/*' => [
|
|
'thumbnail' => ['width' => 250, 'height' => 250],
|
|
'sm' => ['width' => 300, 'height' => 225],
|
|
'md' => ['width' => 768, 'height' => 576],
|
|
'lg' => ['width' => 1024, 'height' => 768],
|
|
'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()
|
|
{
|
|
parent::boot();
|
|
|
|
static::deleting(function($media) {
|
|
$hash = $media->hash;
|
|
if(Media::where('hash', $hash)->count() > 1) {
|
|
return;
|
|
}
|
|
|
|
$disk = Storage::disk('media');
|
|
if($disk->exists($hash)) {
|
|
$disk->delete($hash);
|
|
}
|
|
|
|
$media->deleteAllVariants();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get the URL of the media.
|
|
*/
|
|
public function getUrlAttribute(): string
|
|
{
|
|
return Storage::disk('media')->url($this->name);
|
|
}
|
|
|
|
public function url($variant, $strict = false): string
|
|
{
|
|
if(!$strict) {
|
|
$data = $this->getClosestVariant($variant);
|
|
} else {
|
|
if($this->variants === null || !array_key_exists($variant, $this->variants)) {
|
|
return '';
|
|
}
|
|
|
|
$data = [
|
|
'variant' => $variant,
|
|
'name' => pathinfo($this->name, PATHINFO_FILENAME) . '-' . $variant . '.' . $this->variants[$variant]['extension'],
|
|
'mime_type' => $this->variants[$variant]['mime_type'],
|
|
'file' => $this->path() . '-' . $variant
|
|
];
|
|
}
|
|
|
|
|
|
return Storage::disk('media')->url($this->name) . ($data['variant'] !== '' ? '?' . $data['variant'] : '');
|
|
}
|
|
|
|
/**
|
|
* Get the thumbnail of the media.
|
|
*/
|
|
public function getThumbnailAttribute(): string
|
|
{
|
|
if($this->password === null || Auth::user()?->isAdmin()) {
|
|
if ($this->hasVariant('thumbnail')) {
|
|
$url = $this->url('thumbnail', true);
|
|
if ($url !== '') {
|
|
return $url;
|
|
}
|
|
}
|
|
}
|
|
|
|
$thumbnail = '/thumbnails/' . pathinfo($this->name, PATHINFO_EXTENSION) . '.webp';
|
|
|
|
if(file_exists(public_path($thumbnail))) {
|
|
return asset($thumbnail);
|
|
}
|
|
|
|
return asset('/thumbnails/unknown.webp');
|
|
}
|
|
|
|
public function getFileTypeAttribute(): string
|
|
{
|
|
$extension = strtolower(pathinfo($this->name, PATHINFO_EXTENSION));
|
|
|
|
if(str_starts_with($this->mime_type, 'image/')) {
|
|
return 'Image (' . $extension . ')';
|
|
} else if(str_starts_with($this->mime_type, 'video/')) {
|
|
return 'Video (' . $extension . ')';
|
|
} else if(str_starts_with($this->mime_type, 'audio/')) {
|
|
return 'Audio (' . $extension . ')';
|
|
} else if($this->mime_type === 'application/pdf') {
|
|
return 'PDF Document';
|
|
} else if($this->mime_type === 'text/plain') {
|
|
return 'Text Document';
|
|
} else if($extension === 'sb3') {
|
|
return 'Scratch 3 Project';
|
|
} else if($extension === 'stopmotionstudio' || $extension === 'stopmotionstudiomobile') {
|
|
return 'Stop Motion Studio Project';
|
|
}
|
|
|
|
return 'File (' . $extension . ')';
|
|
}
|
|
|
|
/**
|
|
* Get the user that owns the media.
|
|
*/
|
|
public function user()
|
|
{
|
|
return $this->belongsTo(User::class, 'user_id');
|
|
}
|
|
|
|
/**
|
|
* Get all the models attached to the media.
|
|
*/
|
|
public function mediable()
|
|
{
|
|
return $this->morphTo();
|
|
}
|
|
|
|
/**
|
|
* Get the media as a temp file.
|
|
*
|
|
* @return string|null The temporary file path or null.
|
|
*/
|
|
public function getAsTempFile(): string|null
|
|
{
|
|
if($this->hash === null) {
|
|
return null;
|
|
}
|
|
|
|
$file = tempnam(sys_get_temp_dir(), 'media_');
|
|
$disk = Storage::disk('media');
|
|
if($disk->exists($this->hash) === false) {
|
|
return null;
|
|
}
|
|
|
|
$stream = $disk->getDriver()->readStream($this->hash);
|
|
is_resource($stream) && file_put_contents($file, stream_get_contents($stream), FILE_APPEND);
|
|
return $file;
|
|
}
|
|
|
|
/**
|
|
* Set the media from a file.
|
|
*
|
|
* @param string $file The file to set.
|
|
*/
|
|
public function storeFromTempFile(string $file): void
|
|
{
|
|
Storage::disk('media')->put($this->name, fopen($file, 'r+'));
|
|
}
|
|
|
|
/**
|
|
* Generate variants for this media.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function generateVariants(bool $overwrite = true): void
|
|
{
|
|
$this->status = 'processing';
|
|
$this->save();
|
|
dispatch(new GenerateVariants($this, $overwrite))->onQueue('media');
|
|
}
|
|
|
|
public function path(): string|null
|
|
{
|
|
$disk = Storage::disk('media');
|
|
if(!$disk->exists($this->hash)) {
|
|
return null;
|
|
}
|
|
|
|
return $disk->path($this->hash);
|
|
}
|
|
|
|
/**
|
|
* Add a variant to the media.
|
|
*
|
|
* @param string $name The name of the variant.
|
|
* @param string $mime_type The mime type of the variant.
|
|
* @param string $file The file to store.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function addVariant(string $name, string $mime_type, string $extension, string $file): void
|
|
{
|
|
$name = strtolower($name);
|
|
$storage = Storage::disk('media');
|
|
|
|
if (isset($this->variants[$name])) {
|
|
if ($storage->exists($this->hash . '-' . $name)) {
|
|
$storage->delete($this->hash . '-_' . $name);
|
|
}
|
|
}
|
|
|
|
$storage->putFileAs('/', $file, $this->hash . '-' . $name);
|
|
|
|
$variants = $this->variants;
|
|
$variants[$name] = [
|
|
'mime_type' => $mime_type,
|
|
'extension' => $extension
|
|
];
|
|
$this->variants = $variants;
|
|
|
|
$this->save();
|
|
}
|
|
|
|
/**
|
|
* Does a variant of the media exist.
|
|
*
|
|
* @param string $variant The variant to check.
|
|
*
|
|
* @return bool True if the variant exists, false otherwise.
|
|
*/
|
|
public function hasVariant($variant): bool
|
|
{
|
|
$variant = strtolower($variant);
|
|
$storage = Storage::disk('media');
|
|
|
|
return $storage->exists($this->hash . '-' . $variant);
|
|
}
|
|
|
|
/**
|
|
* Delete a variant of the media.
|
|
*
|
|
* @param string $variant The variant to delete.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function deleteVariant($variant): void
|
|
{
|
|
$variant = strtolower($variant);
|
|
$storage = Storage::disk('media');
|
|
|
|
if(isset($this->variants[$variant])) {
|
|
if($storage->exists($this->hash . '-' . $variant)) {
|
|
$storage->delete($this->hash . '-' . $variant);
|
|
}
|
|
}
|
|
|
|
unset($this->variants[$variant]);
|
|
|
|
$this->save();
|
|
}
|
|
|
|
/**
|
|
* Delete all variants of the media.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function deleteAllVariants(): void
|
|
{
|
|
$storage = Storage::disk('media');
|
|
if($this->variants === null) {
|
|
return;
|
|
}
|
|
|
|
foreach($this->variants as $variant => $file) {
|
|
if($storage->exists($this->hash . '-' . $variant)) {
|
|
$storage->delete($this->hash . '-' . $variant);
|
|
}
|
|
}
|
|
|
|
$this->variants = null;
|
|
$this->save();
|
|
}
|
|
|
|
/**
|
|
* Get the variant types for the media.
|
|
*
|
|
* @param string $matchingKey The matching key.
|
|
*
|
|
* @return array The variant types.
|
|
*/
|
|
public function getVariantTypes(&$matchingKey = null)
|
|
{
|
|
$key = Helpers::findMatchingMimeTypeKey($this->mime_type, Media::$variants);
|
|
if($key === false) {
|
|
$matchingKey = null;
|
|
return [];
|
|
}
|
|
|
|
$matchingKey = $key;
|
|
return Media::$variants[$key];
|
|
}
|
|
|
|
public function getClosestVariant($key)
|
|
{
|
|
$variants = $this->getVariantTypes();
|
|
|
|
if($this->variants && count($variants) > 0) {
|
|
$found = false;
|
|
foreach ($variants as $variant => $data) {
|
|
if($variant === $key) {
|
|
$found = true;
|
|
}
|
|
|
|
if($found && array_key_exists($variant, $this->variants)) {
|
|
return [
|
|
'variant' => $variant,
|
|
'name' => pathinfo($this->name, PATHINFO_FILENAME) . '-' . $variant . '.' . $this->variants[$variant]['extension'],
|
|
'mime_type' => $this->variants[$variant]['mime_type'],
|
|
'file' => $this->path() . '-' . $variant
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return [
|
|
'variant' => null,
|
|
'name' => $this->name,
|
|
'mime_type' => $this->mime_type,
|
|
'file' => $this->path()
|
|
];
|
|
}
|
|
}
|