Update mytv.php

This commit is contained in:
rad168
2025-07-27 05:00:13 +08:00
committed by GitHub
parent c40ace3308
commit 4598c37f13

View File

@@ -1,11 +1,13 @@
<?php <?php
// 获取请求的 URL 并修正编码 //error_reporting(E_ALL);
//ini_set('display_errors', '1');
$request_url = isset($_GET['url']) ? urldecode($_GET['url']) : ''; $request_url = isset($_GET['url']) ? urldecode($_GET['url']) : '';
if (empty($request_url)) { if (empty($request_url)) {
die('缺少 url 参数'); die('缺少 url 参数');
} }
// 允许代理的域名列表 // 可选域名白名单
$allowed_domains = [ $allowed_domains = [
'aktv.top', 'aktv.top',
'php.jdshipin.com', 'php.jdshipin.com',
@@ -20,51 +22,66 @@ $allowed_domains = [
'cdn9.163189.xyz' 'cdn9.163189.xyz'
]; ];
// 是否启用域名检查(设为 false 表示允许任何域名/IP
$enable_domain_check = false;
$parsed_url = parse_url($request_url); $parsed_url = parse_url($request_url);
//说明:如果需要限制仅可代理指定域名的直播源,可以删除注释( /* 和 */ ),并在上面列表中添加你需要代理的网站域名。 $host = $parsed_url['host'] ?? '';
/*
if (!$parsed_url || !isset($parsed_url['host']) || !in_array($parsed_url['host'], $allowed_domains)) { if ($enable_domain_check && !in_array($host, $allowed_domains)) {
die('非法请求的域名'); die('非法请求的域名');
} }
*/
//自定义 getallheaders() 函数,使得代码可以兼容 FastCGI 模式 // 兼容 getallheaders() 函数
if (!function_exists('getallheaders')) { if (!function_exists('getallheaders')) {
function getallheaders() { function getallheaders() {
$headers = []; $headers = [];
foreach ($_SERVER as $name => $value) { foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') { if (strpos($name, 'HTTP_') === 0) {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; $key = str_replace('_', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
$headers[$key] = $value;
} }
} }
return $headers; return $headers;
} }
} }
// 处理 HTTP 头信息 // 兼容 str_starts_with() 函数
if (!function_exists('str_starts_with')) {
function str_starts_with($haystack, $needle) {
return substr($haystack, 0, strlen($needle)) === $needle;
}
}
// 兼容 str_contains 函数
if (!function_exists('str_contains')) {
function str_contains($haystack, $needle) {
return $needle !== '' && strpos($haystack, $needle) !== false;
}
}
// 构造请求头
$headers = []; $headers = [];
foreach (getallheaders() as $name => $value) { foreach (getallheaders() as $name => $value) {
if (strtolower($name) !== 'host') { if (strtolower($name) !== 'host') {
$headers[] = "$name: $value"; $headers[] = "$name: $value";
} }
} }
$headers[] = "Host: {$parsed_url['host']}"; $headers[] = "Host: $host";
$headers[] = "User-Agent: AppleCoreMedia/1.0.0.7B367 (iPad; U; CPU OS 4_3_3 like Mac OS X)"; $headers[] = "User-Agent: AppleCoreMedia/1.0.0.7B367 (iPad; U; CPU OS 4_3_3 like Mac OS X)";
$headers[] = "Referer: https://{$parsed_url['host']}/"; $headers[] = "Referer: https://$host/";
$headers[] = "Accept-Encoding: gzip, deflate"; $headers[] = "Accept-Encoding: gzip, deflate";
// 发请求 // 发请求
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request_url); curl_setopt($ch, CURLOPT_URL, $request_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true); // 获取完整响应头 curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); // 禁用自动跳转 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_ENCODING, ""); curl_setopt($ch, CURLOPT_ENCODING, "");
// 禁用 HTTP/2强制使用 HTTP/1.1
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
@@ -78,29 +95,26 @@ $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch); curl_close($ch);
// 分离响应头和响应 // 拆分头和主
$headers = substr($response, 0, $header_size); $headers_raw = substr($response, 0, $header_size);
$body = substr($response, $header_size); $body = substr($response, $header_size);
// 解析响应 // 解析头
$response_headers = []; $response_headers = [];
foreach (explode("\r\n", $headers) as $line) { foreach (explode("\r\n", $headers_raw) as $line) {
if (strpos($line, 'HTTP/') === 0) { if (stripos($line, 'HTTP/') === 0) {
$response_headers[] = $line; $response_headers[] = $line;
continue; continue;
} }
$parts = explode(': ', $line, 2); $parts = explode(': ', $line, 2);
if (count($parts) === 2) { if (count($parts) === 2) {
$name = strtolower($parts[0]); $response_headers[strtolower($parts[0])] = $parts[1];
$response_headers[$name] = $parts[1];
} }
} }
// 处理重定向 // 重定向处理
if (in_array($http_code, [301, 302, 303, 307, 308]) && isset($response_headers['location'])) { if (in_array($http_code, [301, 302, 303, 307, 308]) && isset($response_headers['location'])) {
$location = $response_headers['location']; $location = $response_headers['location'];
// 处理相对路径
if (!parse_url($location, PHP_URL_SCHEME)) { if (!parse_url($location, PHP_URL_SCHEME)) {
$base = $parsed_url['scheme'] . '://' . $parsed_url['host']; $base = $parsed_url['scheme'] . '://' . $parsed_url['host'];
if (isset($parsed_url['port'])) { if (isset($parsed_url['port'])) {
@@ -108,44 +122,73 @@ if (in_array($http_code, [301, 302, 303, 307, 308]) && isset($response_headers['
} }
$location = $base . '/' . ltrim($location, '/'); $location = $base . '/' . ltrim($location, '/');
} }
// 生成代理地址并跳转
header("Location: mytv.php?url=" . urlencode($location), true, $http_code); header("Location: mytv.php?url=" . urlencode($location), true, $http_code);
exit(); exit();
} }
// 保留原始 Content-Type // 设置 content-type
if (isset($response_headers['content-type'])) { if (isset($response_headers['content-type'])) {
header('Content-Type: ' . $response_headers['content-type']); header('Content-Type: ' . $response_headers['content-type']);
} }
// 设置 HTTP 响应状态码 // 设置状态码
http_response_code($http_code); http_response_code($http_code);
// 出错输出
if ($response === false) { if ($response === false) {
die("CURL ERROR: " . $curl_error); die("CURL ERROR: " . $curl_error);
} }
// 处理 m3u8 文件 // ========== m3u8 替换逻辑 ==========
if (strpos($request_url, '.m3u8') !== false || (isset($response_headers['content-type']) && strpos($response_headers['content-type'], 'application/vnd.apple.mpegurl') !== false)) { $is_m3u8 = false;
$base_url = dirname($request_url) . '/'; $content_type = $response_headers['content-type'] ?? '';
$allowed_domains_regex = implode('|', array_map(function($domain) {
return preg_quote($domain, '/'); if (
}, $allowed_domains)); strpos($request_url, '.m3u8') !== false ||
stripos($content_type, 'mpegurl') !== false ||
stripos($content_type, 'application/x-mpegurl') !== false ||
stripos($content_type, 'text/plain') !== false ||
strpos(ltrim($body), '#EXTM3U') === 0
) {
$is_m3u8 = true;
}
if ($is_m3u8) {
$base_root = $parsed_url['scheme'] . '://' . $parsed_url['host'] .
(isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '');
$base_dir = $base_root . dirname($parsed_url['path']) . '/';
$body = preg_replace_callback( $body = preg_replace_callback(
'/(https?:\/\/(?:' . $allowed_domains_regex . ')\/[^\s"\']+\.ts)|([^\s"\']+\.ts)/', // 更稳健的正则表达式:匹配 URL、绝对路径、相对路径允许 query 参数,允许无后缀
function ($matches) use ($base_url) { '/(?P<url>(https?:\/\/[^\s"\']+)|((\/|\.\.?\/)?[^\s"\']+))/i',
if (!empty($matches[1])) { function ($matches) use ($base_root, $base_dir) {
return 'mytv.php?url=' . urlencode($matches[1]); $url = trim($matches['url']);
} elseif (!empty($matches[2])) {
$ts_url = $base_url . ltrim($matches[2], '/'); // 跳过 m3u8 语法行
return 'mytv.php?url=' . urlencode($ts_url); if (str_starts_with($url, '#')) return $url;
// 跳过 data uri 等
if (str_starts_with($url, 'data:')) return $url;
// 跳过已经被代理过的
if (str_contains($url, 'mytv.php?url=')) return $url;
// 完整 URL
if (preg_match('/^https?:\/\//i', $url)) {
return 'mytv.php?url=' . urlencode($url);
} }
return $matches[0];
// 以 / 开头(绝对路径)
if (str_starts_with($url, '/')) {
return 'mytv.php?url=' . urlencode($base_root . $url);
}
// 处理 ../ 或 ./ 等相对路径
return 'mytv.php?url=' . urlencode($base_dir . $url);
}, },
$body $body
); );
header('Content-Disposition: inline; filename=index.m3u8'); header('Content-Disposition: inline; filename=index.m3u8');
} }