|
|
<?php
$channel_mapping = [
'bjws' => ['id' => 'b2e730bcb7e8468ea028135c602a561d', 'name' => '北京卫视'],
'tjws' => ['id' => 'f34309e518fb41579c46cbef4d56c51d', 'name' => '天津卫视'],
'hbws' => ['id' => '39f6908c16d74aeab67c78eeb37e17dd', 'name' => '河北卫视'],
'sxws' => ['id' => '39c3c7226f754f3e9cbebd8b5f0996ee', 'name' => '山西卫视'],
'lnws' => ['id' => '196d1fb7186845c8ab06945beb60f428', 'name' => '辽宁卫视'],
'nmgws' => ['id' => '74dd173b302c4aa9abf142e4034e0c87', 'name' => '内蒙古卫视'],
'jlws' => ['id' => '0bb934949fe549e9be4ea3ff519eaec4', 'name' => '吉林卫视'],
'hljws' => ['id' => 'c77741c71cbb4f61bc9876826e267b51', 'name' => '黑龙江卫视'],
'dfws' => ['id' => 'b03d633a64f14712b1b78ad5336fac2c', 'name' => '东方卫视'],
'jsws' => ['id' => 'f485de429c064333b7cce20c6bbbc905', 'name' => '江苏卫视'],
'zjws' => ['id' => '0b0936946bd7416fa80aaeb4072f369a', 'name' => '浙江卫视'],
'ahws' => ['id' => '633fd677b4ce40ad85b127db4307921a', 'name' => '安徽卫视'],
'dnws' => ['id' => '74cd6a0403ff4ce7829f04d4cdf4c0a5', 'name' => '东南卫视'],
'jxws' => ['id' => '0621912339cf40109a945afafc3069e2', 'name' => '江西卫视'],
'sdws' => ['id' => '15d651d2389d46ba86eda710fcc1f68b', 'name' => '山东卫视'],
'henanws' => ['id' => '47ebcda850e74c83897dd90ca72db658', 'name' => '河南卫视'],
'hubeiws' => ['id' => '894c6f103e094ab58f51cf0f96870a3d', 'name' => '湖北卫视'],
'gdws' => ['id' => '6766ec7717d64744bdbfd1c2068ebae6', 'name' => '广东卫视'],
'gxws' => ['id' => 'd45e1b2dbae6465a9b2e7a14f7749119', 'name' => '广西卫视'],
'hainanws' => ['id' => '54e5f81a605e428188bf7ff21c0df64c', 'name' => '海南卫视'],
'scws' => ['id' => '90edcdb6a18446d594228039a519b779', 'name' => '四川卫视'],
'gzws' => ['id' => 'd6fc64f8f5e34444ba703bb79fda5da8', 'name' => '贵州卫视'],
'ynws' => ['id' => 'f79e9c261fda4c3ba35d075a51814bbd', 'name' => '云南卫视'],
'xzws' => ['id' => 'c52bd0a9ac294559841fa39d28d2c195', 'name' => '西藏卫视'],
'shanxiws' => ['id' => '63fbcee70e0140c991541b5412c9552d', 'name' => '陕西卫视'], // 使用shanxiws避免与山西卫视冲突
'gsws' => ['id' => 'dc13ed51077a4200a8a41e5e02457fdb', 'name' => '甘肃卫视'],
'qhws' => ['id' => '49718a9c8d364f6b8c6e666739e90780', 'name' => '青海卫视'],
'nxws' => ['id' => '782bc32aad614b6c9c078fd7bea95488', 'name' => '宁夏卫视'],
'xjws' => ['id' => '95b30ad2b3c14b32ac7f915769d080bb', 'name' => '新疆卫视'],
'btws' => ['id' => '16bcdb12d690488286ffec580c9ca249', 'name' => '兵团卫视'],
'hxws' => ['id' => '2eb43b20727e4218af6f26850b51ccee', 'name' => '海峡卫视']
];
/**
* 显示所有可用的频道列表
*/
function show_channel_list() {
global $channel_mapping;
header('Content-Type: text/html; charset=utf-8');
echo '<!DOCTYPE html>';
echo '<html>';
echo '<head>';
echo '<meta charset="UTF-8">';
echo '<title>可用频道列表</title>';
echo '<style>';
echo 'body { font-family: Arial, sans-serif; margin: 20px; }';
echo 'h1 { color: #333; }';
echo 'table { border-collapse: collapse; width: 100%; max-width: 800px; }';
echo 'th, td { text-align: left; padding: 12px; border-bottom: 1px solid #ddd; }';
echo 'th { background-color: #f2f2f2; }';
echo 'tr:hover { background-color: #f5f5f5; }';
echo 'a { color: #0066cc; text-decoration: none; }';
echo 'a:hover { text-decoration: underline; }';
echo '</style>';
echo '</head>';
echo '<body>';
echo '<h1>学习强国可用频道列表</h1>';
echo '<p>请选择以下频道进行观看:</p>';
echo '<table>';
echo '<tr><th>频道名称</th><th>短ID</th><th>访问链接</th></tr>';
foreach ($channel_mapping as $short_id => $channel) {
$url = $_SERVER['PHP_SELF'] . '?id=' . $short_id;
echo '<tr>';
echo '<td>' . $channel['name'] . '</td>';
echo '<td>' . $short_id . '</td>';
echo '<td><a href="' . $url . '" target="_blank">点击观看</a></td>';
echo '</tr>';
}
echo '</table>';
echo '</body>';
echo '</html>';
exit;
}
$id = isset($_GET['id']) ? $_GET['id'] : '';
if (empty($id)) {
show_channel_list();
}
$original_id = $id;
if (array_key_exists($id, $channel_mapping)) {
$id = $channel_mapping[$id]['id'];
if (isset($_GET['debug']) && $_GET['debug'] == 'true') {
header('Content-Type: text/plain; charset=utf-8');
echo "短ID: $original_id (" . $channel_mapping[$original_id]['name'] . ")\n";
echo "转换为长ID: $id\n";
}
}
get_m3u8_url_and_content($id, $u, $c);
$c = replace_ts_urls($u, $c);
echo_content($c);
function get_m3u8_url_and_content($id, &$url, &$content)
{
$url = load_from_cache($id);
if ($url) {
$content = send_request($url, null, null, $code);
if ($code == 200)
return true;
}
$url = get_m3u8_url_from_web($id);
$content = send_request($url);
save_to_cache($id, $url);
return true;
}
function get_m3u8_url_from_web($id) {
return get_m3u8_url_from_web_core($id);
}
function save_to_cache($id, $m3u8_url)
{
$a['m3u8_url'] = $m3u8_url;
array_to_file($a, "xxqg_cache/$id.txt");
}
function load_from_cache($id)
{
$cacheFile = "xxqg_cache/$id.txt";
$expireSeconds = 60 * 30;
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $expireSeconds)) {
file_to_array($cacheFile, $a);
return $a['m3u8_url'];
} else {
return '';
}
}
function file_to_array($filename, &$array) {
$array = [];
if (file_exists($filename)) {
$handle = fopen($filename, 'r');
if (flock($handle, LOCK_SH)) { // 共享锁,允许其他进程读但禁止写
$data = file_get_contents($filename);
$array = unserialize($data);
flock($handle, LOCK_UN);
}
fclose($handle);
}
return true;
}
function array_to_file($array, $filename) {
$dir = dirname($filename);
if (!is_dir($dir) && is_writable(dirname($dir))) {
if (!@mkdir($dir, 0755, true))
return false;
}
$data = serialize($array);
file_put_contents($filename, $data, LOCK_EX);
return true;
}
function replace_ts_urls($m3u8_url, $m3u8_content)
{
$dest_ts_path = dirname($m3u8_url)."/";
return preg_replace_callback("/^((?!#).+)$/im",
function ($matches) use ($dest_ts_path) {
if (!is_absolute_url($matches[1]))
$ts = $dest_ts_path.$matches[1];
else
$ts = $matches[1];
return $ts;
},
$m3u8_content
);
}
function is_absolute_url($url) {
return stripos($url, 'http:') === 0 || stripos($url, 'https:') === 0;
}
function echo_content($content)
{
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/vnd.apple.mpegurl");
echo $content;
}
function send_request($url,$header=null,$post_data=null,&$response_code=null){
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
if ($header) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
}
if ($post_data) {
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
}
$res = curl_exec($ch);
if (func_num_args() > 3)
$response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $res;
}
function get_m3u8_url_from_web_core($id)
{
$h[] = 'content-type: application/json';
$h[] = 'svver: 2';
$t = round(microtime(true) * 1000);
$r = mt_rand(1000, 9999);
$h[] = "x-lwp-uid: $t$r@pc-live";
$u = "https://www.xuexi.cn/lgdata/$id.json";
$c = file_get_contents($u);
preg_match('/"itemId":"([^"]+)"/', $c, $m);
$item_id = $m[1];
preg_match('/"videoLink":"([^"]+)"/', $c, $m);
$res_url = $m[1];
$r = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyz'), 0, 11);
$t1 = round(microtime(true) * 1000);
$t2 = $t1 + mt_rand(100, 999);
$y_uid = $request_id = "{$id}_{$r}_{$t1}_{$t2}";
$res_type = 201;
$y_token = hash('sha256', "{$res_url}_{$res_type}_{$request_id}_1d66f6b2-442a-4834-ba8c-c265e8769931");
$p = json_encode([
"res_type" => $res_type,
"res_url" => $res_url,
"request_id" => $request_id,
"item_id" => $item_id,
"y_uid" => $y_uid,
"y_token" => $y_token,
], JSON_UNESCAPED_SLASHES);
$u = 'https://gw-proxy-api.xuexi.cn/v1/api/exchangeAuthUrl';
$c = file_get_contents($u, false, stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => implode("\r\n", $h),
'content' => $p,
)
)));
$j = json_decode($c, true);
$d = $j['data'];
$key = hash('sha256', base64_encode($d['timestamp'].$y_token), true);
$iv = md5($key, true);
$l = openssl_decrypt($d['auth_url'], 'AES-256-CBC', $key, 0, $iv);
return $l;
} |
|