Lara Media
Lara Media is a media library for Laravel. It provides a simple way to store, convert, and manipulate media files.

Features
- Store media information in MongoDB (other connection is not tested)
- Multiple disk storage
- Multiple album/collection for each model
- Convert media files to other formats / other resolution
- Detect main color of photo
- Add watermark to photo
- Add watermark to video
- Get thumbnail of media
- Get multiple screenshot of video
- Get multiple resolution of photo
- Dashboard to manage media files, view statistic, tracking conversion status via queue
- Url version
- Temporary url
Requirements
Installation
composer require nhattuanbl/lara-media
php artisan vendor:publish --provider="Nhattuanbl\LaraMedia\LaraMediaProvider" --tag="config"
php artisan vendor:publish --provider="Nhattuanbl\LaraMedia\LaraMediaProvider" --tag="migrations"
php artisan vendor:publish --provider="Nhattuanbl\LaraMedia\LaraMediaProvider" --tag="assets"
php artisan vendor:publish --provider="Nhattuanbl\LaraMedia\LaraMediaProvider" --tag="views"
php artisan migrate
php artisan storage:link
Config
return [
'connection' => env('LARA_MEDIA_CONNECTION', 'mongodb'),
'model' => \Nhattuanbl\LaraMedia\Models\LaraMedia::class,
'table_name' => 'media',
'web' => [
'enabled' => true,
'domain' => null,
'prefix' => 'lara-media',
'middleware' => ['web'],
],
'public_assets' => [
'root' => env('LARA_MEDIA_PUBLIC_ROOT', 'app'),
'url' => env('LARA_MEDIA_PUBLIC_URL', '/assets/media'),
],
'temporary' => [
'enabled' => true,
'route_path' => 'download',
'route_name' => 'web.download',
],
'disk' => env('LARA_MEDIA_DISK', 'local'),
'store_path' => 'Y/m/d',
'conversion' => [
'connection' => env('LARA_MEDIA_QUEUE_CONNECTION', 'mongodb'),
'queue' => env('LARA_MEDIA_QUEUE_NAME', 'conversion'),
'temp' => storage_path('temp'),
'keep_original' => env('LARA_MEDIA_KEEP_ORIGINAL', true),
'threads' => env('LARA_MEDIA_THREAD', 16),
'ffmpeg_path' => env('LARA_MEDIA_FFMPEG_PATH', '/usr/bin/ffmpeg'),
'ffprobe_path' => env('LARA_MEDIA_FFPROBE_PATH', '/usr/bin/ffprobe'),
],
'watermark' => [
'path' => null,
'position' => \Nhattuanbl\LaraMedia\Enums\PositionEnum::Center,
'opacity' => 0.2,
'height_percent' => 50,
],
'photo' => [
'detect_main_color' => true,
],
'video' => [
'2pass_encoding' => false,
'timeout' => 7200,
'encoding' => [
'mp4' => ['libx264', 'aac'],
'webm' => ['libvpx', 'libvorbis'],
'ts' => ['libx264', 'aac'],
'3gp' => ['libx264', 'aac'],
'3g2' => ['libx264', 'aac'],
'mov' => ['libx264', 'aac'],
'fli' => ['libx264', 'aac'],
'mkv' => ['libx264', 'aac'],
'asf' => ['wmv2', 'wmav2'],
'wmv' => ['wmv2', 'wmav2'],
],
],
'audio' => [
'timeout' => 7200,
'encoding' => [
'aac' => 'aac',
'm4a' => 'aac',
'mp3' => 'libmp3lame',
'flac' => 'flac',
'wav' => 'pcm_s16le',
],
],
];
Usage
Prepare your model
use MongoDB\Laravel\Eloquent\HybridRelations;
use Nhattuanbl\LaraMedia\Contracts\AudioRegister;
use Nhattuanbl\LaraMedia\Contracts\HasMedia;
use Nhattuanbl\LaraMedia\Contracts\MediaRegister;
use Nhattuanbl\LaraMedia\Contracts\VideoRegister;
use Nhattuanbl\LaraMedia\Enums\PositionEnum;
use Nhattuanbl\LaraMedia\Enums\ResolutionEnum;
class User extends Model
{
use HasMedia, HybridRelations;
...
public function registerMediaAlbum(): MediaRegister
{
return $this->addMediaAlbum()
->acceptsMimeTypes(['image/*'])
->album('defaultalbum')
->limit(3);
}
public function registerVideoAlbum(): VideoRegister
{
return $this->addVideoAlbum([ResolutionEnum::H240, ResolutionEnum::H360])
->limit(5)
->album('videoalbum')
->onQueue('default')
->onConnection('sync')
->format('mp4')
->quality(55)
->keepOrigin(false)
->description('some desc')
->watermark(
storage_path('watermark.png'),
PositionEnum::BottomLeft,
0.5,
50
)
->posters(
5, //required - number of screenshot
300, //optional - width of screenshot
)
->thumbnail(4, 4)
->conversionDisk('s3')
->onDisk('local')
;
}
public function registerPhotoAlbum(): PhotoRegister
{
return $this->addPhotoAlbum()
->album('photoalbum')
->limit(3)
->responsive([600, 300])
->onQueue('default')
->onConnection('sync')
->watermark()
;
}
public function registerAudioAlbum(): AudioRegister
{
return $this->addAudioAlbum()
->album('audioalbum')
->limit(5)
->description('some desc')
->responsive(['wav', 'mp3'])
->conversionDisk('s3')
->onDisk('local')
->keepOrigin(false)
->quality(80)
;
}
}
Prepare your controller
use Nhattuanbl\LaraMedia\Models\LaraMedia;
$model->addMedia(storage_path('photo.webp'))->toAlbum();
$model->addMedia('https://example.com/photo.webp')
->onDisk('override model register disk')
->toAlbum('override model register album name');
$model->addMedia('data:image/png;base64,....')
->onQueue('override model register')
->toAlbum();
$model->addMedia(LaraMedia::findOrFail($id))
->withoutResponsive()
->toAlbum();
$model->addMedia(request()->file)->toAlbum();
LaraMedia::findOrFail($id)->url;
LaraMedia::findOrFail($id)->getUrlAttribute('400');
LaraMedia::findOrFail($id)->temporaryUrl(
now()->addHours(1)->timestamp, //expire time
$version, //optional - null for original version
['views' => 1] //optional - allow accessible only 1 time
);
Web access
http://localhost/lara-media
Custom uploaded file naming
public function boot()
{
MediaRegister::$sanitizeFilename = function (string $filename) {
$invalidChars = [
'\\', '/', ':', '*', '?', '"', '<', '>', '|',
"\0", "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08",
"\x09", "\x0A", "\x0B", "\x0C", "\x0D", "\x0E", "\x0F", "\x10", "\x11", "\x12",
"\x13", "\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1A", "\x1B", "\x1C",
"\x1D", "\x1E", "\x1F"
];
$sanitized = str_replace($invalidChars, '_', $filename);
$sanitized = trim($sanitized, " \t\n\r\0\x0B.");
return strlen($sanitized) ? $sanitized : floor(microtime(true) * 1000);
};
}
Custom media model
namespace App\Models;
class CustomMedia extends \Nhattuanbl\LaraMedia\Models\LaraMedia
{
protected $table = 'custom_media';
protected $connection = 'mysql';
public function get_idAttribute(): int|string
{
return $this->id;
}
}
...
'model' => \App\Models\CustomMedia::class,
Troubleshooting
In migration file you will see that im added 2 new column to customize imtigger/laravel-job-status package for tracking media conversion status so this migration file should run after imtigger/laravel-job-status migration file. Have fun!