christyoga123/video-optimizer

A fluent Laravel package for optimizing videos - resize, convert formats (MP4, WebM, AVI), compress and reduce file size while maintaining quality

Maintainers

Package info

github.com/ChristYoga123/video-optimizer

Homepage

pkg:composer/christyoga123/video-optimizer

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.1.0 2026-01-20 08:02 UTC

This package is auto-updated.

Last update: 2026-02-20 08:17:32 UTC


README

A fluent Laravel package for optimizing videos - resize, convert formats (MP4, WebM, AVI), compress and reduce file size while maintaining quality.

Latest Version on Packagist License

Features

  • ๐ŸŽฌ 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

Requirements

  • PHP 8.1+
  • Laravel 10.x, 11.x, or 12.x
  • FFmpeg installed on your server

Installing FFmpeg

macOS:

brew install ffmpeg

Ubuntu/Debian:

sudo apt update
sudo apt install ffmpeg

Windows: Download from ffmpeg.org and add to PATH.

Installation

Install the package via Composer:

composer require christyoga123/video-optimizer

The package will automatically register its service provider.

Publish Configuration (Optional)

php artisan vendor:publish --tag=video-optimizer-config

This will create a config/video-optimizer.php file with all available options.

Usage

Basic Usage

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));

Using Facade

use Christyoga123\VideoOptimizer\Facades\VideoOptimizer;

$tempPath = VideoOptimizer::make()
    ->toMp4()
    ->crf(23)
    ->preset('fast')
    ->process($request->file('video'));

From Request Input

$tempPath = VideoOptimizer::make()
    ->toWebm()
    ->videoBitrate(2000)
    ->processFromRequest('video');

if ($tempPath) {
    // File was uploaded and processed
    $post->update(['video' => Storage::putFile('videos', $tempPath)]);
}

From File Path

$tempPath = VideoOptimizer::make()
    ->toMp4()
    ->maxDimensions(1280, 720)
    ->crf(28)
    ->processFromPath('/path/to/original/video.mov');

With Default Settings

Apply all defaults from config at once:

$tempPath = VideoOptimizer::make()
    ->withDefaults()
    ->process($uploadedFile);

With Thumbnail Generation

$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));

Remove Audio

$tempPath = VideoOptimizer::make()
    ->toMp4()
    ->noAudio()
    ->process($uploadedFile);

Get Video Information

$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',
// ]

Get Size Comparison

$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"

Helper Methods

$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();

Available Methods

Format Methods

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)

Codec Methods

Method Description
videoCodec(string $codec) Set video codec
audioCodec(string $codec) Set audio codec

Resize Methods

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

Quality & Settings

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

Audio Control

Method Description
noAudio() Remove audio from video
withAudio() Keep audio in video

Thumbnail Methods

Method Description
withThumbnail(int $atSecond) Enable thumbnail generation at specific time
getThumbnailPath() Get generated thumbnail path

Process Methods

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

Info Methods

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

Configuration

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),
];

Recommended Settings

For Web Streaming (Balance)

VideoOptimizer::make()
    ->toMp4()
    ->crf(23)
    ->preset('medium')
    ->maxDimensions(1920, 1080)
    ->process($file);

For Small File Size

VideoOptimizer::make()
    ->toMp4()
    ->crf(28)
    ->preset('slow')
    ->maxDimensions(1280, 720)
    ->videoBitrate(1000)
    ->process($file);

For High Quality

VideoOptimizer::make()
    ->toMp4()
    ->crf(18)
    ->preset('slower')
    ->videoBitrate(4000)
    ->process($file);

For Fast Processing

VideoOptimizer::make()
    ->toMp4()
    ->preset('ultrafast')
    ->process($file);

Integration Examples

With Spatie Media Library

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');

With Laravel Storage

$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,
]);

In Form Request

// 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(),
        ]);
    }
}
    }
}

Queueable Job

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)
);

Manual Cleanup Control

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();

Custom Job Implementation

In Queue Job

// 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);
    }
}

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email christianuswibisono@gmail.com instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.