Add PHP Video Converter Suite with live stream pipelines and nuclear control panel UI

Full-featured video conversion platform with:
- FFmpeg-based pipeline system with composable stages (transcode, scale, filter, audio, bitrate, framerate, trim, deinterlace, denoise, stabilize)
- Live stream management with real-time format switching (RTMP/RTSP/HTTP)
- Industrial/nuclear power plant control room themed UI with gauges, switches, LED indicators
- Format switchboard for instant conversion between 16+ video/audio formats
- Pipeline designer with visual flow editor and drag-and-drop stage composition
- Job queue with priority scheduling and batch conversion
- WebSocket server for real-time progress broadcasting
- REST API for all operations (upload, convert, streams, pipelines, queue)
- System monitoring (CPU, memory, disk) with animated gauge displays
- Docker Compose setup with web, websocket, and worker services

https://claude.ai/code/session_01WxmHGnVFXGm2bwbFREHkHb
This commit is contained in:
Claude
2026-02-07 18:11:04 +00:00
parent 282d8b70fc
commit 6c56306873
27 changed files with 4746 additions and 0 deletions
@@ -0,0 +1,106 @@
<?php
namespace VideoConverter\Process;
class MediaProbe
{
private string $ffprobe;
public function __construct()
{
$config = require __DIR__ . '/../../config/app.php';
$this->ffprobe = $config['ffmpeg']['ffprobe'];
}
public function analyze(string $filePath): array
{
$cmd = sprintf(
'%s -v quiet -print_format json -show_format -show_streams %s',
$this->ffprobe,
escapeshellarg($filePath)
);
$output = shell_exec($cmd);
$data = json_decode($output ?: '{}', true);
if (!$data) {
return ['error' => 'Could not analyze file'];
}
return $this->parseProbeData($data);
}
public function getDuration(string $filePath): float
{
$info = $this->analyze($filePath);
return (float)($info['duration'] ?? 0);
}
public function getThumbnail(string $filePath, string $outputPath, string $time = '00:00:01'): bool
{
$config = require __DIR__ . '/../../config/app.php';
$cmd = sprintf(
'%s -y -i %s -ss %s -vframes 1 -vf scale=320:-1 %s 2>/dev/null',
$config['ffmpeg']['binary'],
escapeshellarg($filePath),
escapeshellarg($time),
escapeshellarg($outputPath)
);
exec($cmd, $output, $exitCode);
return $exitCode === 0;
}
private function parseProbeData(array $data): array
{
$result = [
'format' => $data['format']['format_long_name'] ?? 'Unknown',
'format_name' => $data['format']['format_name'] ?? '',
'duration' => (float)($data['format']['duration'] ?? 0),
'size' => (int)($data['format']['size'] ?? 0),
'bitrate' => (int)($data['format']['bit_rate'] ?? 0),
'streams' => [],
'video' => null,
'audio' => null,
];
foreach (($data['streams'] ?? []) as $stream) {
$type = $stream['codec_type'] ?? '';
$info = [
'index' => $stream['index'],
'type' => $type,
'codec' => $stream['codec_name'] ?? 'unknown',
'codec_long' => $stream['codec_long_name'] ?? '',
];
if ($type === 'video') {
$info['width'] = (int)($stream['width'] ?? 0);
$info['height'] = (int)($stream['height'] ?? 0);
$info['fps'] = $this->parseFps($stream['r_frame_rate'] ?? '0/1');
$info['pix_fmt'] = $stream['pix_fmt'] ?? '';
$info['bitrate'] = (int)($stream['bit_rate'] ?? 0);
$info['profile'] = $stream['profile'] ?? '';
$info['level'] = $stream['level'] ?? '';
if (!$result['video']) $result['video'] = $info;
} elseif ($type === 'audio') {
$info['sample_rate'] = (int)($stream['sample_rate'] ?? 0);
$info['channels'] = (int)($stream['channels'] ?? 0);
$info['channel_layout'] = $stream['channel_layout'] ?? '';
$info['bitrate'] = (int)($stream['bit_rate'] ?? 0);
if (!$result['audio']) $result['audio'] = $info;
}
$result['streams'][] = $info;
}
return $result;
}
private function parseFps(string $frac): float
{
$parts = explode('/', $frac);
if (count($parts) === 2 && (int)$parts[1] > 0) {
return round((int)$parts[0] / (int)$parts[1], 2);
}
return (float)$frac;
}
}