Laravel Shaka
Queue Integration
Queues
Packaging a long video takes minutes, so run it in a queued job, not in a request.
A packaging job
namespace App\Jobs;
use App\Models\Video;
use Foxws\Shaka\Facades\Shaka;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Throwable;
class PackageVideo implements ShouldQueue
{
use Queueable;
public $timeout = 3600;
public $tries = 2;
public function __construct(public Video $video) {}
public function handle(): void
{
$packager = Shaka::fromDisk('media')->open($this->video->path);
try {
$packager
->addVideoStream($this->video->path, 'video.mp4')
->addAudioStream($this->video->path, 'audio.mp4')
->withMpdOutput('index.mpd')
->withHlsMasterPlaylist('master.m3u8')
->export()
->toDisk('s3')
->toPath("streams/{$this->video->id}/")
->afterSaving(fn () => $this->video->markAsReady())
->save();
} finally {
$packager->cleanupTemporaryFiles();
}
}
public function failed(Throwable $exception): void
{
$this->video->markAsFailed();
}
}
PackageVideo::dispatch($video)->onQueue('media');
Timeouts
Three timeouts need to line up:
PACKAGER_TIMEOUTstops the Shaka Packager process. The default is 14400 seconds (4 hours).- The job's
$timeoutstops the worker. Keep it at or abovePACKAGER_TIMEOUT, or the worker is killed while Shaka Packager still runs. - The queue connection's
retry_aftermust be longer than the job's$timeout. Otherwise another worker picks up the same job while the first one is still packaging.
// config/queue.php
'media' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'media',
'retry_after' => 3660,
],
How many at once
Packaging mostly uses disk and network, and every running job needs space in temporary_files_root for its whole output. Limit how many run at the same time. With Horizon:
// config/horizon.php
'supervisor-media' => [
'connection' => 'media',
'queue' => ['media'],
'maxProcesses' => 2,
'timeout' => 3600,
'tries' => 2,
],
If temporary_files_root is a size-limited mount, turn on the storage guards. A job then fails right away instead of halfway through.
Long-running workers
Queue workers live for many jobs, so:
- Always call
cleanupTemporaryFiles()infinally. A failed job otherwise leaves its files behind. - Don't change the shared driver with
app(ShakaPackager::class)->setTimeout()inside a job. The driver is a singleton, so the change sticks for every later job in that worker. - Use
WithoutOverlappingorShouldBeUniqueif the same video can be queued twice.
