Greasy Fork is available in English.
将CCTV视频解析成HLS地址.
当前为
// ==UserScript==
// @name:en-US CCTV-HLS
// @name CCTV视频解析
// @description:en-US parse cctv video to hls url.
// @description 将CCTV视频解析成HLS地址.
// @namespace http://greasyfork.icu/users/135090
// @version 1.4.2
// @author [ZWB](http://greasyfork.icu/zh-CN/users/863179)
// @license CC
// @grant none
// @run-at document-end
// @match https://v.cctv.com/2*/V*.shtml*
// @match https://v.cctv.cn/2*/V*.shtml*
// @match https://tv.cctv.cn/2*/V*.shtml*
// @match https://tv.cctv.com/2*/V*.shtml*
// @match https://vdn.apps.cntv.cn/api/getHttpVideoInfo*
// @icon https://tv.cctv.cn/favicon.ico
// ==/UserScript==
(function () {
if (location.hostname.indexOf("v.cctv.com") > -1 || location.hostname.indexOf("v.cctv.cn") > -1) {
var apiUrl = `https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid=${guid}`;
var jp = prompt("是否跳转到", apiUrl);
if (jp == null) { console.info("取消"); } else {
location.href = (apiUrl);
}
}
if (location.hostname.indexOf("vdn.apps.cntv.cn") > -1) {
var data = JSON.parse(document.body.textContent);
var hlsUrl = data?.manifest?.hls_h5e_url.replaceAll("main", "2000");;
var title = data?.title;
if (data?.play_channel.indexOf("4K") > 0) {
hlsUrl = data?.hls_url.replaceAll("main", "4000");
}
console.info(hlsUrl);
downloadM3U8Video(hlsUrl, title + '.ts', {
onProgress: (current, total) => {
var cotp = `${Math.round((current / total) * 100)}`;
document.body.textContent = title + "---下载进程" + cotp + "%";
console.info(`Progress: ${current}/${total} (${cotp}%)`);
}
});
}
async function downloadM3U8Video(m3u8Url, outputFilename = 'video.m2ts', options = {}) {
try {
// 1. 获取并解析M3U8文件
//void (alert('开始'));
const response = await fetch(m3u8Url);
if (!response.ok) throw new Error(`Failed to fetch M3U8: ${response.status}`);
const m3u8Content = await response.text();
const lines = m3u8Content.split('\n');
const baseUrl = m3u8Url.substring(0, m3u8Url.lastIndexOf("/") + 1);
const segments = [];
// 解析TS分片URL
for (const line of lines) {
if (line && !line.startsWith('#') && (line.endsWith('.ts') || line.match(/\.ts\?/))) {
const segmentUrl = line.startsWith('http') ? line : new URL(line, baseUrl).href;
segments.push(segmentUrl);
// return;
}
}
if (segments.length === 0) throw new Error('No TS segments found in the M3U8 file');
console.log(`Found ${segments.length} TS segments`);
// 2. 下载所有分片
console.log('Downloading segments...');
const blobs = [];
const { onProgress } = options;
for (let i = 0; i < segments.length; i++) {
try {
const segmentResponse = await fetch(segments[i]);
if (!segmentResponse.ok) throw new Error(`Failed to fetch segment: ${segmentResponse.status}`);
const blob = await segmentResponse.blob();
blobs.push(blob);
// 调用进度回调
if (typeof onProgress === 'function') {
onProgress(i + 1, segments.length);
}
} catch (error) {
console.error(`Error downloading segment ${segments[i]}:`, error);
throw error; // 可以选择继续或抛出错误
}
}
// 3. 合并并下载
console.log('Merging and downloading...');
const mergedBlob = new Blob(blobs, { type: 'video/mp2t' });
const url = URL.createObjectURL(mergedBlob);
const a = document.createElement('a');
a.href = url;
a.download = outputFilename;
document.body.appendChild(a);
a.click();
// 清理
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 100);
console.log('Download completed!');
return true;
} catch (error) {
console.error('Error downloading M3U8 video:', error);
throw error;
}
}
})();