laravel-hls-converter maintained by zhitoo
Laravel HLS Converter
A Laravel package for converting videos to HLS (HTTP Live Streaming) format via the HLS Converter microservice.
Requirements
- PHP 8.1+
- Laravel 10 or 11
- PHP
zipextension (for extraction features)
Installation
composer require zhitoo/laravel-hls-converter
Publish the config file:
php artisan vendor:publish --tag=hls-converter-config
Configuration
Add the following to your .env:
HLS_CONVERTER_BASE_URL=https://hls.yourdomain.com
HLS_CONVERTER_API_KEY=your-secret-api-key
HLS_CONVERTER_TIMEOUT=30
HLS_CONVERTER_DOWNLOAD_TIMEOUT=120
# Optional: temp directory for ZIP download/extract (default: storage/app/hls-temp)
HLS_CONVERTER_TEMP_PATH=/tmp/hls-temp
Usage
Submit a conversion job
use Zhitoo\HlsConverter\Facades\HlsConverter;
$taskId = HlsConverter::convert(
videoUrl: 'https://example.com/video.mp4',
resolutions: [1080, 720, 480], // omit for original quality only
chunkDuration: 10,
audioChannels: 2,
);
Check status
$status = HlsConverter::status($taskId);
echo $status->status; // Pending | Processing | Completed | Failed
echo $status->progress; // 0–100
if ($status->isCompleted()) {
echo $status->masterPlaylist; // "master.m3u8"
foreach ($status->qualities as $q) {
echo $q['label'] . ' → ' . $q['playlist'];
// 720p → 720p/output.m3u8
}
}
Download ZIP
// Save to an explicit path
HlsConverter::downloadToFile($taskId, storage_path("hls/{$taskId}.zip"));
// Or get a PSR-7 stream
$stream = HlsConverter::download($taskId);
file_put_contents(storage_path("hls/{$taskId}.zip"), $stream);
Extract ZIP to a local directory
Downloads the ZIP, extracts it, deletes the ZIP, and returns the list of extracted file paths.
$files = HlsConverter::extractTo($taskId, storage_path("hls/{$taskId}"));
// $files is an array of absolute paths:
// [
// '/var/www/storage/app/hls/<taskId>/master.m3u8',
// '/var/www/storage/app/hls/<taskId>/720p/output.m3u8',
// '/var/www/storage/app/hls/<taskId>/720p/segment_000.ts',
// ...
// ]
Transfer HLS files to a Laravel storage disk
Downloads the ZIP, extracts it, uploads every file to the chosen storage disk, and cleans up all temp files automatically.
// Transfer to a specific disk (local, s3, ftp, etc.)
$result = HlsConverter::transferToStorage(
taskId: $taskId,
storagePath: "videos/hls/{$taskId}",
disk: 's3',
);
// Or use the default filesystem disk
$result = HlsConverter::transferToDefaultStorage(
taskId: $taskId,
storagePath: "videos/hls/{$taskId}",
);
echo $result->disk; // "s3"
echo $result->basePath; // "videos/hls/<taskId>"
echo $result->masterPlaylistPath(); // "videos/hls/<taskId>/master.m3u8"
foreach ($result->files as $path) {
echo Storage::disk($result->disk)->url($path);
}
// Get as array
$array = $result->toArray();
// [
// 'disk' => 's3',
// 'base_path' => 'videos/hls/<taskId>',
// 'master_playlist' => 'videos/hls/<taskId>/master.m3u8',
// 'files' => [...],
// ]
Polling until done then transferring
use Zhitoo\HlsConverter\Facades\HlsConverter;
use Zhitoo\HlsConverter\Exceptions\HlsConverterException;
$taskId = HlsConverter::convert('https://example.com/video.mp4', [720, 480]);
do {
sleep(5);
$status = HlsConverter::status($taskId);
} while ($status->isPending() || $status->isProcessing());
if ($status->isFailed()) {
$log = HlsConverter::logs($taskId);
throw new \RuntimeException("Conversion failed.\n{$log}");
}
$result = HlsConverter::transferToStorage($taskId, "videos/hls/{$taskId}", 's3');
Dependency injection
use Zhitoo\HlsConverter\HlsConverter;
use Zhitoo\HlsConverter\DTOs\TransferResult;
class VideoService
{
public function __construct(private HlsConverter $hls) {}
public function process(string $url, string $taskId): TransferResult
{
return $this->hls->transferToStorage($taskId, "hls/{$taskId}", 's3');
}
}
Exception Handling
All methods throw Zhitoo\HlsConverter\Exceptions\HlsConverterException on failure.
use Zhitoo\HlsConverter\Exceptions\HlsConverterException;
try {
$result = HlsConverter::transferToStorage($taskId, 'videos/hls', 's3');
} catch (HlsConverterException $e) {
logger()->error('HLS transfer failed', ['error' => $e->getMessage()]);
}
API Reference
| Method | Description | Returns |
|---|---|---|
convert($url, $resolutions, $chunkDuration, $audioChannels) |
Submit a conversion job | string task_id |
status($taskId) |
Get task status | TaskStatus |
logs($taskId) |
Get raw FFmpeg log | string |
download($taskId) |
Get ZIP as a PSR-7 stream | StreamInterface |
downloadToFile($taskId, $path) |
Save ZIP to a local file | void |
extractTo($taskId, $destination) |
Download + extract ZIP to a local dir | string[] file paths |
transferToStorage($taskId, $storagePath, $disk) |
Download + extract + upload to storage disk | TransferResult |
transferToDefaultStorage($taskId, $storagePath) |
Same, using filesystems.default disk |
TransferResult |
TaskStatus DTO
| Property | Type | Description |
|---|---|---|
taskId |
string | Task UUID |
status |
string | Pending, Processing, Completed, Failed |
progress |
int | 0–100 |
currentStep |
string | Current processing step |
retryCount |
int | Number of retries so far |
createdAt |
string | ISO 8601 |
updatedAt |
string | ISO 8601 |
masterPlaylist |
string|null | master.m3u8 — only when Completed |
qualities |
array | [{height, label, playlist}] — only when Completed |
Helper methods: isPending(), isProcessing(), isCompleted(), isFailed()
TransferResult DTO
| Property | Type | Description |
|---|---|---|
disk |
string | Storage disk name |
basePath |
string | Base path inside the disk |
masterPlaylist |
string|null | master.m3u8 relative name, or null |
files |
string[] | Full storage paths of all uploaded files |
Methods: masterPlaylistPath() — full path including basePath, toArray()
License
MIT