фвыфывфывфывфы
<?php
error_reporting(0);
ini_set('display_errors', 0);
set_time_limit(0);
ini_set('memory_limit', '256M');
// Отдача готового файла
if (isset($_GET['file'])) {
$filePath = __DIR__ . '/videos/' . basename($_GET['file']);
if (!file_exists($filePath)) {
http_response_code(404);
exit('Файл не найден');
}
$fileName = $_GET['name'] ?? 'video.mp4';
$fileSize = filesize($filePath);
header('Content-Type: video/mp4');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Content-Length: ' . $fileSize);
header('Accept-Ranges: bytes');
header('Cache-Control: no-cache');
while (ob_get_level()) ob_end_clean();
readfile($filePath);
exit;
}
// Сборка видео
if (isset($_GET['download'])) {
$videoUrl = $_GET['video_url'] ?? '';
$title = $_GET['title'] ?? 'video';
if (!$videoUrl) exit('Нет URL');
$title = preg_replace('/[^\w\s\p{Cyrillic}\-]/ui', '', $title) ?: 'video';
$title = str_replace(['/', '\\', '"', "'", '?', '*', ':', '|', '<', '>'], '', $title);
// Папка videos рядом с downloader.php
$tmpDir = __DIR__ . '/videos/';
if (!is_dir($tmpDir)) mkdir($tmpDir, 0755, true);
// Защита от прямого доступа
if (!file_exists($tmpDir . '.htaccess')) {
file_put_contents($tmpDir . '.htaccess', "Require all denied\n");
}
// Удаляем старые файлы (старше 1 часа)
$oldFiles = glob($tmpDir . '*.mp4');
foreach ($oldFiles as $oldFile) {
if (filemtime($oldFile) < time() - 3600) {
@unlink($oldFile);
@unlink(str_replace('.mp4', '.json', $oldFile));
}
}
$fileHash = md5($videoUrl);
$tmpFile = $tmpDir . $fileHash . '.mp4';
$metaFile = $tmpDir . $fileHash . '.json';
// Если уже собран
if (file_exists($tmpFile) && filesize($tmpFile) > 0) {
echo json_encode([
'success' => true,
'file' => $fileHash . '.mp4',
'name' => $title . '.mp4',
'size' => filesize($tmpFile),
'cached' => true
]);
exit;
}
function getUrl($url) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_TIMEOUT => 60,
CURLOPT_CONNECTTIMEOUT => 15,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0',
CURLOPT_TCP_NODELAY => 1,
CURLOPT_BUFFERSIZE => 65536
]);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
$m3u8 = getUrl($videoUrl);
if (!$m3u8) exit('Ошибка загрузки');
$lines = explode("\n", $m3u8);
$segments = [];
$base = dirname($videoUrl);
foreach ($lines as $line) {
$line = trim($line);
if ($line && $line[0] !== '#' && (stripos($line, '.ts') !== false || stripos($line, '.mp4') !== false)) {
if (!preg_match('/^https?:\/\//', $line)) $line = rtrim($base, '/') . '/' . ltrim($line, '/');
$segments[] = $line;
}
}
if (empty($segments)) {
for ($i = 0; $i < count($lines); $i++) {
if (strpos($lines[$i], '#EXT-X-STREAM-INF') !== false) {
$next = trim($lines[$i + 1] ?? '');
if ($next && $next[0] !== '#') {
if (!preg_match('/^https?:\/\//', $next)) $next = rtrim($base, '/') . '/' . ltrim($next, '/');
$sub = getUrl($next);
if ($sub) {
$subLines = explode("\n", $sub);
$subBase = dirname($next);
foreach ($subLines as $sl) {
$sl = trim($sl);
if ($sl && $sl[0] !== '#') {
if (!preg_match('/^https?:\/\//', $sl)) $sl = rtrim($subBase, '/') . '/' . ltrim($sl, '/');
$segments[] = $sl;
}
}
}
break;
}
}
}
}
if (empty($segments)) exit('Нет сегментов');
$fp = fopen($tmpFile, 'w+');
if (!$fp) exit('Не удалось создать файл');
foreach ($segments as $segUrl) {
$data = getUrl($segUrl);
if ($data) fwrite($fp, $data);
}
fclose($fp);
if (!file_exists($tmpFile) || filesize($tmpFile) === 0) exit('Не удалось сохранить');
file_put_contents($metaFile, json_encode(['title' => $title, 'created' => time()]));
echo json_encode([
'success' => true,
'file' => $fileHash . '.mp4',
'name' => $title . '.mp4',
'size' => filesize($tmpFile),
'cached' => false
]);
exit;
}
// API
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
$url = $_GET['url'] ?? '';
if (!$url) die(json_encode(['s'=>false,'e'=>'Введите ссылку']));
if (!str_contains($url, 'rutube.ru')) die(json_encode(['s'=>false,'e'=>'Только RuTube']));
preg_match('/video\/([a-f0-9]+)/i', $url, $m);
if (!isset($m[1])) die(json_encode(['s'=>false,'e'=>'Неверная ссылка']));
$id = $m[1];
$ch = curl_init("https://rutube.ru/api/play/options/$id/?no_404=true");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true,CURLOPT_FOLLOWLOCATION=>true,CURLOPT_SSL_VERIFYPEER=>false,CURLOPT_TIMEOUT=>10,CURLOPT_USERAGENT=>'Mozilla/5.0']);
$j = curl_exec($ch); curl_close($ch);
if (!$j) die(json_encode(['s'=>false,'e'=>'API не отвечает']));
$d = json_decode($j, true);
if (!$d||isset($d['detail'])||empty($d['video_balancer']['m3u8'])) die(json_encode(['s'=>false,'e'=>'Видео недоступно']));
$title = $d['title']??'Видео'; $m3u8 = $d['video_balancer']['m3u8'];
$ch2 = curl_init($m3u8);
curl_setopt_array($ch2, [CURLOPT_RETURNTRANSFER=>true,CURLOPT_FOLLOWLOCATION=>true,CURLOPT_SSL_VERIFYPEER=>false,CURLOPT_TIMEOUT=>10,CURLOPT_USERAGENT=>'Mozilla/5.0']);
$mc = curl_exec($ch2); curl_close($ch2);
$lines = explode("\n", $mc); $qualities = []; $base = dirname($m3u8);
for ($i=0;$i if (strpos($lines[$i],'#EXT-X-STREAM-INF')!==false) {
$res='';$bw='';$bwr=0;
if (preg_match('/RESOLUTION=(\d+x\d+)/',$lines[$i],$rm)) $res=$rm[1];
if (preg_match('/BANDWIDTH=(\d+)/',$lines[$i],$bm)){$bwr=(int)$bm[1];$bw=' ('.round($bwr/1000000,1).' Mbps)';}
$nxt=trim($lines[$i+1]??'');
if ($nxt&&$nxt[0]!=='#') {
if (!str_starts_with($nxt,'http')) $nxt=rtrim($base,'/').'/'.ltrim($nxt,'/');
$qualities[($res?:'Поток').$bw]=['url'=>$nxt,'resolution'=>$res,'bandwidth'=>$bwr];
}
}
}
if (empty($qualities)) $qualities['Стандартное']=['url'=>$m3u8,'resolution'=>'','bandwidth'=>0];
echo json_encode(['s'=>true,'d'=>['title'=>$title,'qualities'=>$qualities]],JSON_UNESCAPED_UNICODE);
вфывфывфвфывф фыв фывф ывфыв фвы
ыфвфывфыв
фы
вф
ыв
фыв
фы
вфывфывфыв ф