Major Features: - Rebrand from Cinema Studio Pro to Lux Studio - New logo and branding (orange/gold theme) - Video generation with Veo 3.1 (I2V and T2V) - Project-first workflow with IndexedDB storage - AI prompt optimizer with intelligent inference Video Gen: - Duration (4s/6s/8s), aspect ratio (16:9/9:16), resolution - Reference image support for I2V - Aspect ratio mismatch warnings - Frame extraction from generated videos - Video streaming with HTTP Range support UI/UX: - Tab navigation with underline style - Portrait format adaptive display - Editable optimized prompts with [AI suggestions] - Auto-save to active project Security: - Remove config.php from tracking (contains API key) - Add comprehensive .gitignore - Remove uploaded session files from repo 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
98 lines
2.5 KiB
PHP
98 lines
2.5 KiB
PHP
<?php
|
|
/**
|
|
* Video Streaming with Range Request Support
|
|
* Enables proper video seeking in browsers
|
|
*/
|
|
|
|
$videoDir = __DIR__ . '/generated_videos';
|
|
|
|
// Get the requested video file
|
|
$file = isset($_GET['file']) ? basename($_GET['file']) : null;
|
|
|
|
if (!$file) {
|
|
http_response_code(400);
|
|
die('No file specified');
|
|
}
|
|
|
|
$filePath = $videoDir . '/' . $file;
|
|
|
|
// Security: ensure file is within video directory
|
|
$realPath = realpath($filePath);
|
|
$realVideoDir = realpath($videoDir);
|
|
|
|
if ($realPath === false || strpos($realPath, $realVideoDir) !== 0) {
|
|
http_response_code(404);
|
|
die('File not found');
|
|
}
|
|
|
|
if (!file_exists($filePath)) {
|
|
http_response_code(404);
|
|
die('File not found');
|
|
}
|
|
|
|
$fileSize = filesize($filePath);
|
|
$mimeType = 'video/mp4';
|
|
|
|
// Check for Range header
|
|
$range = isset($_SERVER['HTTP_RANGE']) ? $_SERVER['HTTP_RANGE'] : null;
|
|
|
|
if ($range) {
|
|
// Parse Range header
|
|
list($unit, $rangeSpec) = explode('=', $range, 2);
|
|
|
|
if ($unit !== 'bytes') {
|
|
http_response_code(416);
|
|
header("Content-Range: bytes */$fileSize");
|
|
die('Invalid range unit');
|
|
}
|
|
|
|
// Parse the range values
|
|
$ranges = explode(',', $rangeSpec);
|
|
$rangeSpec = trim($ranges[0]); // Only handle first range
|
|
|
|
list($start, $end) = explode('-', $rangeSpec);
|
|
|
|
$start = $start === '' ? 0 : intval($start);
|
|
$end = $end === '' ? $fileSize - 1 : intval($end);
|
|
|
|
// Validate range
|
|
if ($start < 0 || $start >= $fileSize || $end >= $fileSize || $start > $end) {
|
|
http_response_code(416);
|
|
header("Content-Range: bytes */$fileSize");
|
|
die('Invalid range');
|
|
}
|
|
|
|
$length = $end - $start + 1;
|
|
|
|
// Send partial content headers
|
|
http_response_code(206);
|
|
header('Content-Type: ' . $mimeType);
|
|
header('Accept-Ranges: bytes');
|
|
header("Content-Range: bytes $start-$end/$fileSize");
|
|
header("Content-Length: $length");
|
|
header('Cache-Control: public, max-age=86400');
|
|
|
|
// Output the requested range
|
|
$fp = fopen($filePath, 'rb');
|
|
fseek($fp, $start);
|
|
|
|
$bytesRemaining = $length;
|
|
$bufferSize = 8192;
|
|
|
|
while ($bytesRemaining > 0 && !feof($fp)) {
|
|
$readSize = min($bufferSize, $bytesRemaining);
|
|
echo fread($fp, $readSize);
|
|
$bytesRemaining -= $readSize;
|
|
flush();
|
|
}
|
|
|
|
fclose($fp);
|
|
} else {
|
|
// No range requested - send full file
|
|
header('Content-Type: ' . $mimeType);
|
|
header('Accept-Ranges: bytes');
|
|
header("Content-Length: $fileSize");
|
|
header('Cache-Control: public, max-age=86400');
|
|
|
|
readfile($filePath);
|
|
}
|