A fluent Laravel package for optimizing videos - resize, convert formats (MP4, WebM, AVI), compress and reduce file size while maintaining quality
christyoga123/video-optimizer is a Laravel package for a fluent laravel package for optimizing videos - resize, convert formats (mp4, webm, avi), compress and reduce file size while maintaining quality.
It currently has 0 GitHub stars and 924 downloads on Packagist (latest version v1.1.0).
Install it with composer require christyoga123/video-optimizer.
Discover more Laravel packages by christyoga123
or browse all Laravel packages to compare alternatives.
Last updated
A fluent Laravel package for optimizing videos - resize, convert formats (MP4, WebM, AVI), compress and reduce file size while maintaining quality.
π¬ Format Conversion - Convert videos to MP4, WebM, OGG, MOV, MKV, AVI
π Smart Resize - Resize with max width/height while maintaining aspect ratio
ποΈ Quality Control - Adjustable bitrate, CRF, and preset settings
π§ Fluent API - Chain methods for clean, readable code
βοΈ Configurable - Publish and customize default settings
πΌοΈ Thumbnail Generation - Extract thumbnails from videos
π Audio Control - Option to remove or keep audio
π΅ Smart Audio Codecs - Auto-detects best audio codecs (fixes WebM/Ogg issues on macOS/Homebrew)
π Video Info - Get duration, dimensions, codec info
π§Ή Auto Cleanup - Automatic temporary file cleanup with dontCleanup() option
π¦ Queueable Job - Built-in job for background processing
macOS:
brew install ffmpeg
Ubuntu/Debian:
sudo apt update
sudo apt install ffmpeg
Windows: Download from ffmpeg.org and add to PATH.
Install the package via Composer:
composer require christyoga123/video-optimizer
The package will automatically register its service provider.
php artisan vendor:publish --tag=video-optimizer-config
This will create a config/video-optimizer.php file with all available options.
use Christyoga123\VideoOptimizer\VideoOptimizer;
// Process uploaded file and get optimized temp path
$tempPath = VideoOptimizer::make()
->toMp4()
->maxDimensions(1920, 1080)
->videoBitrate(1500)
->process($uploadedFile);
// Do something with the optimized file
Storage::disk('public')->put('videos/video.mp4', file_get_contents($tempPath));
use Christyoga123\VideoOptimizer\Facades\VideoOptimizer;
$tempPath = VideoOptimizer::make()
->toMp4()
->crf(23)
->preset('fast')
->process($request->file('video'));
$tempPath = VideoOptimizer::make()
->toWebm()
->videoBitrate(2000)
->processFromRequest('video');
if ($tempPath) {
// File was uploaded and processed
$post->update(['video' => Storage::putFile('videos', $tempPath)]);
}
$tempPath = VideoOptimizer::make()
->toMp4()
->maxDimensions(1280, 720)
->crf(28)
->processFromPath('/path/to/original/video.mov');
Apply all defaults from config at once:
$tempPath = VideoOptimizer::make()
->withDefaults()
->process($uploadedFile);
$optimizer = VideoOptimizer::make()
->toMp4()
->withThumbnail(5); // Capture at 5 seconds
$videoPath = $optimizer->process($uploadedFile);
$thumbnailPath = $optimizer->getThumbnailPath();
// Store both video and thumbnail
Storage::disk('public')->put('videos/video.mp4', file_get_contents($videoPath));
Storage::disk('public')->put('thumbnails/thumb.jpg', file_get_contents($thumbnailPath));
$tempPath = VideoOptimizer::make()
->toMp4()
->noAudio()
->process($uploadedFile);
$optimizer = VideoOptimizer::make();
// Get video info before processing
$info = $optimizer->getVideoInfo($uploadedFile->getRealPath());
// Result:
// [
// 'duration' => 120.5,
// 'bitrate' => 2500000,
// 'size' => 37500000,
// 'format' => 'mov,mp4,m4a,3gp,3g2,mj2',
// 'width' => 1920,
// 'height' => 1080,
// 'video_codec' => 'h264',
// 'audio_codec' => 'aac',
// 'frame_rate' => '30/1',
// ]
$optimizer = VideoOptimizer::make()
->toMp4()
->crf(28)
->maxDimensions(1280, 720);
$tempPath = $optimizer->process($uploadedFile);
$comparison = $optimizer->getSizeComparison($uploadedFile->getRealPath());
// Result:
// [
// 'original_size' => 50000000,
// 'optimized_size' => 12500000,
// 'saved_bytes' => 37500000,
// 'saved_percentage' => 75.0
// ]
echo "Saved: " . $optimizer->formatFileSize($comparison['saved_bytes']);
// Output: "Saved: 35.76 MB"
$optimizer = VideoOptimizer::make();
// Get sanitized filename for storage
$filename = $optimizer->getOptimizedFilename($uploadedFile);
// "my_video.mp4"
// Get current format
$format = $optimizer->getFormat();
// "mp4"
// Get temp file path
$path = $optimizer->getTempPath();
// Get thumbnail path (if generated)
$thumbnail = $optimizer->getThumbnailPath();
// Get video duration
$duration = $optimizer->getDuration($uploadedFile->getRealPath());
// Get video dimensions
$dimensions = $optimizer->getDimensions($uploadedFile->getRealPath());
// ['width' => 1920, 'height' => 1080]
// Manual cleanup (usually not needed - destructor handles this)
$optimizer->cleanup();
| Method | Description |
| ------------------------ | ---------------------------------------- |
| format(string $format) | Set output format (mp4, webm, ogg, etc.) |
| toMp4() | Convert to MP4 format (H.264 + AAC) |
| toWebm() | Convert to WebM format (VP9 + Vorbis) |
| toOgg() | Convert to OGG format (Theora + Vorbis) |
| toMov() | Convert to MOV format (H.264 + AAC) |
| toMkv() | Convert to MKV format (H.264 + AAC) |
| toAvi() | Convert to AVI format (H.264 + MP3) |
| Method | Description |
| --------------------------- | --------------- |
| videoCodec(string $codec) | Set video codec |
| audioCodec(string $codec) | Set audio codec |
| Method | Description |
| ---------------------------------------- | ------------------------------------------- |
| maxWidth(int $width) | Set maximum width (maintains aspect ratio) |
| maxHeight(int $height) | Set maximum height (maintains aspect ratio) |
| maxDimensions(int $width, int $height) | Set both max width and height |
| Method | Description |
| ------------------------- | ------------------------------------------- |
| videoBitrate(int $kbps) | Set video bitrate in kbps |
| audioBitrate(int $kbps) | Set audio bitrate in kbps |
| crf(int $value) | Set CRF (0-51, lower = better quality) |
| preset(string $preset) | Set encoding preset (ultrafast to veryslow) |
| tempDir(string $path) | Set custom temporary directory |
| timeout(int $seconds) | Set processing timeout |
| threads(int $count) | Set number of threads |
| withDefaults() | Apply all defaults from config |
| Method | Description |
| ------------- | ----------------------- |
| noAudio() | Remove audio from video |
| withAudio() | Keep audio in video |
| Method | Description |
| ------------------------------ | -------------------------------------------- |
| withThumbnail(int $atSecond) | Enable thumbnail generation at specific time |
| getThumbnailPath() | Get generated thumbnail path |
| Method | Description |
| ----------------------------------------- | ------------------------------- |
| process(UploadedFile $file) | Process uploaded file |
| processUploadedFile(UploadedFile $file) | Alias for process() |
| processFromPath(string $path) | Process from file path |
| processFromRequest(string $inputName) | Process from request input name |
| Method | Description |
| ----------------------------- | ----------------------------- |
| getDuration(string $path) | Get video duration in seconds |
| getDimensions(string $path) | Get video width and height |
| getVideoInfo(string $path) | Get complete video info |
After publishing, you can customize these options in config/video-optimizer.php:
return [
// FFmpeg binary paths (null = auto-detect)
'ffmpeg_path' => env('VIDEO_OPTIMIZER_FFMPEG_PATH', null),
'ffprobe_path' => env('VIDEO_OPTIMIZER_FFPROBE_PATH', null),
// Processing timeout in seconds
'timeout' => env('VIDEO_OPTIMIZER_TIMEOUT', 3600),
// Number of threads (0 = auto)
'threads' => env('VIDEO_OPTIMIZER_THREADS', 0),
// Default output format
'format' => env('VIDEO_OPTIMIZER_FORMAT', 'mp4'),
// Default codecs
'video_codec' => env('VIDEO_OPTIMIZER_VIDEO_CODEC', 'libx264'),
'audio_codec' => env('VIDEO_OPTIMIZER_AUDIO_CODEC', 'aac'),
// Default bitrates
'video_bitrate' => env('VIDEO_OPTIMIZER_VIDEO_BITRATE', 1500),
'audio_bitrate' => env('VIDEO_OPTIMIZER_AUDIO_BITRATE', 128),
// Default max dimensions
'max_width' => env('VIDEO_OPTIMIZER_MAX_WIDTH', 1920),
'max_height' => env('VIDEO_OPTIMIZER_MAX_HEIGHT', 1080),
// CRF (Constant Rate Factor) - lower = better quality
'crf' => env('VIDEO_OPTIMIZER_CRF', 23),
// Encoding preset
'preset' => env('VIDEO_OPTIMIZER_PRESET', 'medium'),
// Temporary directory
'temp_dir' => env('VIDEO_OPTIMIZER_TEMP_DIR', storage_path('app/temp')),
// Thumbnail generation
'generate_thumbnail' => env('VIDEO_OPTIMIZER_GENERATE_THUMBNAIL', false),
'thumbnail_time' => env('VIDEO_OPTIMIZER_THUMBNAIL_TIME', 1),
];
VideoOptimizer::make()
->toMp4()
->crf(23)
->preset('medium')
->maxDimensions(1920, 1080)
->process($file);
VideoOptimizer::make()
->toMp4()
->crf(28)
->preset('slow')
->maxDimensions(1280, 720)
->videoBitrate(1000)
->process($file);
VideoOptimizer::make()
->toMp4()
->crf(18)
->preset('slower')
->videoBitrate(4000)
->process($file);
VideoOptimizer::make()
->toMp4()
->preset('ultrafast')
->process($file);
use Christyoga123\VideoOptimizer\VideoOptimizer;
// Optimize before adding to media collection
$tempPath = VideoOptimizer::make()
->toMp4()
->maxDimensions(1920, 1080)
->crf(23)
->process($request->file('video'));
$model->addMedia($tempPath)
->usingFileName('optimized-video.mp4')
->toMediaCollection('videos');
$optimizer = VideoOptimizer::make()
->toMp4()
->crf(23)
->withThumbnail(3);
$tempPath = $optimizer->process($request->file('video'));
$filename = $optimizer->getOptimizedFilename($request->file('video'));
// Store video
$videoPath = Storage::disk('public')->putFileAs('uploads/videos', $tempPath, $filename);
// Store thumbnail
$thumbnailFilename = pathinfo($filename, PATHINFO_FILENAME) . '.jpg';
$thumbnailPath = Storage::disk('public')->putFileAs('uploads/thumbnails', $optimizer->getThumbnailPath(), $thumbnailFilename);
return response()->json([
'video' => $videoPath,
'thumbnail' => $thumbnailPath,
]);
// app/Http/Requests/StoreVideoRequest.php
public function passedValidation()
{
if ($this->hasFile('video')) {
$optimizer = VideoOptimizer::make()
->withDefaults()
->withThumbnail(1);
$tempPath = $optimizer->process($this->file('video'));
$this->merge([
'optimized_video_path' => $tempPath,
'thumbnail_path' => $optimizer->getThumbnailPath(),
]);
}
}
}
}
The package includes a ready-to-use job for background processing:
use Christyoga123\VideoOptimizer\Jobs\OptimizeVideoJob;
// Dispatch job
OptimizeVideoJob::dispatch(
$sourcePath, // Full path to source file
$destinationPath, // Full path to save result (optional, overwrites source if null)
[ // Options array
'format' => 'mp4',
'crf' => 23,
'preset' => 'fast'
],
true // Delete source file after optimization? (default: false)
);
By default, the optimizer cleans up temporary files when the object is destroyed. If you need to keep the file longer (e.g., passing between jobs), use dontCleanup():
$optimizer = VideoOptimizer::make()
->toMp4()
->dontCleanup() // Prevent auto-deletion
->process($file);
// ... do something time consuming ...
// Manually cleanup when done
$optimizer->cleanup();
// app/Jobs/OptimizeVideo.php
class OptimizeVideo implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
public string $videoPath,
public int $postId
) {}
public function handle(): void
{
$optimizer = VideoOptimizer::make()
->toMp4()
->crf(23)
->maxDimensions(1920, 1080)
->withThumbnail(3);
$tempPath = $optimizer->processFromPath($this->videoPath);
// Store optimized video
$filename = 'video_' . $this->postId . '.mp4';
Storage::disk('public')->put('videos/' . $filename, file_get_contents($tempPath));
// Store thumbnail
$thumbFilename = 'thumb_' . $this->postId . '.jpg';
Storage::disk('public')->put('thumbnails/' . $thumbFilename, file_get_contents($optimizer->getThumbnailPath()));
// Update post
Post::find($this->postId)->update([
'video_path' => 'videos/' . $filename,
'thumbnail_path' => 'thumbnails/' . $thumbFilename,
'video_processed' => true,
]);
// Delete original file
@unlink($this->videoPath);
}
}
composer test
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
If you discover any security related issues, please email [email protected] instead of using the issue tracker.
The MIT License (MIT). Please see License File for more information.