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