Skip to content

Commit 1ab05d3

Browse files
committed
add path generator
1 parent db8c09d commit 1ab05d3

File tree

10 files changed

+249
-64
lines changed

10 files changed

+249
-64
lines changed

README.md

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -91,68 +91,107 @@ This is the contents of the published config file:
9191
```php
9292
use Elegantly\Media\Jobs\DeleteModelMediaJob;
9393
use Elegantly\Media\Models\Media;
94+
use Elegantly\Media\Models\MediaConversion;
95+
use Elegantly\Media\PathGenerators\UuidPathGenerator;
96+
use Elegantly\Media\UrlFormatters\DefaultUrlFormatter;
9497

9598
return [
9699
/**
97-
* The media model
98-
* Define your own model here by extending \Elegantly\Media\Models\Media::class
100+
* The media model.
101+
* Define your own model here by extending \Elegantly\Media\Models\Media::class.
99102
*/
100103
'model' => Media::class,
101104

102105
/**
103-
* The path used to store temporary file copy for conversions
104-
* This will be used with storage_path() function
106+
* The MediaConversion model.
107+
* Define your own model here by extending \Elegantly\Media\Models\MediaConversion::class.
108+
*/
109+
'media_conversion_model' => MediaConversion::class,
110+
111+
/**
112+
* The path used to store temporary file copies for conversions.
113+
* This will be used with the storage_path() function.
105114
*/
106115
'temporary_storage_path' => 'app/tmp/media',
107116

108117
/**
109-
* The default disk used for storing files
118+
* The default disk used for storing files.
110119
*/
111120
'disk' => env('MEDIA_DISK', env('FILESYSTEM_DISK', 'local')),
112121

113122
/**
114123
* Determine if media should be deleted with the model
115-
* when using the HasMedia Trait
124+
* when using the HasMedia Trait.
116125
*/
117126
'delete_media_with_model' => true,
118127

119128
/**
120129
* Determine if media should be deleted with the model
121-
* when it is soft deleted
130+
* when it is soft deleted.
122131
*/
123132
'delete_media_with_trashed_model' => false,
124133

125134
/**
126-
* Deleting a large number of media attached to a model can be time-consuming
127-
* or even fail (e.g., cloud API error, permissions, etc.)
128-
* For performance and monitoring, when a model with the HasMedia trait is deleted,
129-
* each media is individually deleted inside a job.
135+
* Job class responsible for deleting media when the model is deleted.
136+
* This helps with performance and monitoring by queuing media deletions.
130137
*/
131138
'delete_media_with_model_job' => DeleteModelMediaJob::class,
132139

133140
/**
134-
* The default collection name
141+
* The default collection name assigned media.
135142
*/
136143
'default_collection_name' => 'default',
137144

138145
/**
139-
* Prefix for the generated path of files
140-
* Set to null if you do not want any prefix
141-
* To fully customize the generated default path, extend the Media model and override the generateBasePath method
146+
* The default URL formatter class.
147+
* Used when calling `$media->getUrl()`.
148+
*/
149+
'default_url_formatter' => DefaultUrlFormatter::class,
150+
151+
/**
152+
* The default path generator class.
153+
* Used when storing new files.
154+
*/
155+
'default_path_generator' => UuidPathGenerator::class,
156+
157+
/**
158+
* Prefix for the generated file path.
159+
* Set to null to disable the prefix.
160+
* Override the generateBasePath method in the Media model for full customization.
142161
*/
143162
'generated_path_prefix' => null,
144163

145164
/**
146-
* Customize the queue connection used when dispatching conversion jobs
165+
* Queue connection name to use when dispatching media conversion jobs.
147166
*/
148167
'queue_connection' => env('QUEUE_CONNECTION', 'sync'),
149168

150169
/**
151-
* Customize the queue used when dispatching conversion jobs
152-
* null will fall back to the default Laravel queue
170+
* Queue name to use for media conversion jobs.
171+
* Set to null to use the default Laravel queue.
153172
*/
154173
'queue' => null,
155174

175+
/**
176+
* Configuration for FFmpeg processing.
177+
*/
178+
'ffmpeg' => [
179+
/**
180+
* The binary path to the FFmpeg executable.
181+
*/
182+
'ffmpeg_binaries' => env('FFMPEG_BINARIES', 'ffmpeg'),
183+
184+
/**
185+
* The binary path to the FFprobe executable.
186+
*/
187+
'ffprobe_binaries' => env('FFPROBE_BINARIES', 'ffprobe'),
188+
189+
/**
190+
* Optional log channel for FFmpeg operations.
191+
* Set to null to disable logging.
192+
*/
193+
'log_channel' => null,
194+
],
156195
];
157196
```
158197

config/media.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Elegantly\Media\Jobs\DeleteModelMediaJob;
66
use Elegantly\Media\Models\Media;
77
use Elegantly\Media\Models\MediaConversion;
8+
use Elegantly\Media\PathGenerators\UuidPathGenerator;
89
use Elegantly\Media\UrlFormatters\DefaultUrlFormatter;
910

1011
return [
@@ -60,6 +61,12 @@
6061
*/
6162
'default_url_formatter' => DefaultUrlFormatter::class,
6263

64+
/**
65+
* The default path generator class.
66+
* Used when storing new files.
67+
*/
68+
'default_path_generator' => UuidPathGenerator::class,
69+
6370
/**
6471
* Prefix for the generated file path.
6572
* Set to null to disable the prefix.

src/FileDownloaders/HttpFileDownloader.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Elegantly\Media\HttpFileDownloaders;
5+
namespace Elegantly\Media\FileDownloaders;
66

77
use Elegantly\Media\Helpers\File;
88
use Illuminate\Support\Facades\Http;

src/Models/Media.php

Lines changed: 11 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
use Elegantly\Media\Enums\MediaType;
1414
use Elegantly\Media\Events\MediaConversionAddedEvent;
1515
use Elegantly\Media\Events\MediaFileStoredEvent;
16+
use Elegantly\Media\FileDownloaders\HttpFileDownloader;
1617
use Elegantly\Media\Helpers\File;
17-
use Elegantly\Media\HttpFileDownloaders\HttpFileDownloader;
1818
use Elegantly\Media\MediaConversionDefinition;
19+
use Elegantly\Media\PathGenerators\AbstractPathGenerator;
1920
use Elegantly\Media\TemporaryDirectory;
2021
use Elegantly\Media\Traits\HasUuid;
2122
use Elegantly\Media\UrlFormatters\AbstractUrlFormatter;
@@ -162,7 +163,10 @@ public function storeFileFromHttpFile(
162163
?Closure $before = null,
163164
): static {
164165

165-
$destination ??= $this->getDefaultPath();
166+
/** @var class-string<AbstractPathGenerator> */
167+
$pathGenerator = config('media.default_path_generator');
168+
169+
$destination ??= (new $pathGenerator)->media($this)->value();
166170
$name ??= File::name($file) ?? Str::random(6);
167171
$disk ??= config()->string('media.disk', config()->string('filesystems.default', 'local'));
168172

@@ -417,6 +421,10 @@ public function addConversion(
417421
array $attributes = [],
418422
bool $deleteChildren = false
419423
): MediaConversion {
424+
425+
/** @var class-string<AbstractPathGenerator> */
426+
$pathGenerator = config('media.default_path_generator');
427+
420428
/**
421429
* Prefix name with parent if not already done
422430
*/
@@ -446,7 +454,7 @@ public function addConversion(
446454

447455
$conversion->storeFile(
448456
file: $file,
449-
destination: $destination ?? $this->getDefaultPath($conversionName),
457+
destination: $destination ?? (new $pathGenerator)->conversion($this, $conversion)->value(),
450458
name: $name,
451459
disk: $disk ?? $this->disk
452460
);
@@ -558,45 +566,6 @@ public function deleteChildrenConversions(string $conversionName): static
558566

559567
// \ Managing Conversions ----------------------------------------------------------
560568

561-
/** @deprecated use getDefaultPath */
562-
public function makeFreshPath(
563-
?string $conversion = null,
564-
?string $fileName = null
565-
): string {
566-
return $this->getDefaultPath($conversion, $fileName);
567-
}
568-
569-
/**
570-
* Default path where files will be put
571-
* Ex: root files
572-
* /{prefix}/uuid/{fileName}
573-
* Ex: conversion files
574-
* /{prefix}/uuid/conversions/{conversion}/{fileName}
575-
*/
576-
public function getDefaultPath(
577-
?string $conversion = null,
578-
?string $fileName = null
579-
): string {
580-
/** @var string $prefix */
581-
$prefix = config('media.generated_path_prefix') ?? '';
582-
583-
$root = Str::of($prefix)
584-
->when($prefix, fn ($string) => $string->finish('/'))
585-
->append($this->uuid)
586-
->finish('/');
587-
588-
if ($conversion) {
589-
return $root
590-
->append('conversions/')
591-
->append(str_replace('.', '/conversions/', $conversion))
592-
->finish('/')
593-
->append($fileName ?? '')
594-
->value();
595-
}
596-
597-
return $root->append($fileName ?? '')->value();
598-
}
599-
600569
/**
601570
* @param array<array-key, float|int|string> $keys
602571
* @param null|(Closure(null|int $previous): int) $sequence

src/Models/MediaConversion.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
use Elegantly\Media\Enums\MediaConversionState;
1111
use Elegantly\Media\Enums\MediaType;
1212
use Elegantly\Media\Events\MediaFileStoredEvent;
13+
use Elegantly\Media\FileDownloaders\HttpFileDownloader;
1314
use Elegantly\Media\Helpers\File;
14-
use Elegantly\Media\HttpFileDownloaders\HttpFileDownloader;
1515
use Elegantly\Media\TemporaryDirectory;
1616
use Elegantly\Media\Traits\HasUuid;
1717
use Exception;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Elegantly\Media\PathGenerators;
6+
7+
use Elegantly\Media\Models\Media;
8+
use Elegantly\Media\Models\MediaConversion;
9+
use Illuminate\Support\Stringable;
10+
11+
abstract class AbstractPathGenerator
12+
{
13+
public readonly ?string $prefix;
14+
15+
public function __construct(?string $prefix = null)
16+
{
17+
/** @var ?string */
18+
$config = config('media.generated_path_prefix');
19+
20+
$this->prefix = $prefix ?? $config;
21+
}
22+
23+
abstract public function media(Media $media): Stringable;
24+
25+
abstract public function conversion(Media $media, MediaConversion $mediaConversion): Stringable;
26+
27+
public function generate(
28+
Media $media,
29+
?MediaConversion $mediaConversion,
30+
string $fileName,
31+
): string {
32+
if ($mediaConversion) {
33+
return $this
34+
->conversion($media, $mediaConversion)
35+
->append($fileName)
36+
->value();
37+
}
38+
39+
return $this
40+
->media($media)
41+
->append($fileName)
42+
->value();
43+
}
44+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Elegantly\Media\PathGenerators;
6+
7+
use Elegantly\Media\Models\Media;
8+
use Elegantly\Media\Models\MediaConversion;
9+
use Illuminate\Support\Stringable;
10+
11+
class UuidPathGenerator extends AbstractPathGenerator
12+
{
13+
/**
14+
* Ex: {prefix}/{uuid}/
15+
*/
16+
public function media(Media $media): Stringable
17+
{
18+
if ($this->prefix) {
19+
return str($this->prefix)
20+
->finish('/')
21+
->append($media->uuid)
22+
->finish('/');
23+
}
24+
25+
return str($media->uuid)->finish('/');
26+
}
27+
28+
/**
29+
* Adding the mediaConversion uuid ensure the cache is not used
30+
*
31+
* @example {prefix}/{uuid}/conversions/poster/{uuid}/
32+
* @example {prefix}/{uuid}/conversions/poster/conversions/360/{uuid}
33+
*/
34+
public function conversion(
35+
Media $media,
36+
MediaConversion $mediaConversion,
37+
): Stringable {
38+
39+
return $this->media($media)
40+
->append('conversions/')
41+
->append(str_replace('.', '/conversions/', $mediaConversion->conversion_name))
42+
->finish('/')
43+
->append($mediaConversion->uuid)
44+
->finish('/');
45+
}
46+
}

tests/Feature/MediaTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@
168168
expect($conversion->media_id)->toBe($media->id);
169169
expect($conversion->id)->not->toBe(null);
170170
expect($conversion->exists)->toBe(true);
171-
expect($conversion->path)->toBe("{$media->uuid}/conversions/poster/poster.jpg");
171+
expect($conversion->path)->toBe("{$media->uuid}/conversions/poster/{$conversion->uuid}/poster.jpg");
172172
expect($conversion->name)->toBe('poster');
173173
expect($conversion->extension)->toBe('jpg');
174174
expect($conversion->file_name)->toBe('poster.jpg');

tests/Unit/FileDownloaderTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
use Elegantly\Media\HttpFileDownloaders\HttpFileDownloader;
5+
use Elegantly\Media\FileDownloaders\HttpFileDownloader;
66
use Spatie\TemporaryDirectory\TemporaryDirectory;
77

88
it('download a file from an url as a temporary file', function () {

0 commit comments

Comments
 (0)