您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
将CCTV视频解析成HLS地址.
当前为
// ==UserScript== // @name:en-US CCTV-HLS-Client // @name CCTV客户端视频解析 // @description:en-US parse cctv video to hls url. // @description 将CCTV视频解析成HLS地址. // @namespace http://greasyfork.icu/users/135090 // @version 1.5.0 // @author [ZWB](http://greasyfork.icu/zh-CN/users/863179) // @license CC // @grant none // @run-at document-end // @match https://*.cctv.com/2*/VID*.shtml* // @match https://*.cctv.cn/2*/VID*.shtml* // @match https://vdn.apps.cntv.cn/api/getHttpVideoInfo* // @icon https://tv.cctv.cn/favicon.ico // ==/UserScript== (function () { if (location.hostname.indexOf(".cctv.com") > 0 || location.hostname.indexOf(".cctv.cn") > 0) { var base = "https://vdn.apps.cntv.cn"; var pathname = "/api/getHttpVideoInfo.do"; var apihref = base + pathname + `?client=flash&im=0&pid=${guid}`; // 跳转到api页 location.href = (apihref); } if (location.hostname.indexOf("vdn.apps.cntv.cn") > -1) { var data = JSON.parse(document.body.textContent); //解析成JSON对象 var title = data?.title; //视频标题 var hlsUrl = data?.manifest?.hls_enc2_url.replaceAll("main", "2000"); //普通视频 var tsnlen = hlsUrl.split("/").length - 2; //在api页获取guid值在播放链接中的索引 var tsn = hlsUrl.split("/")[tsnlen]; //根据索引得到guid值,其实也可以用var tsn = location.search.split('&')[2].slice(4); console.info("guid:"+tsn); title = title ? title.replaceAll(" ","_") : tsn + "_guid.ts"; //标题空格用_代替 document.title = title; //网页标题由视频标题提供 if (data?.play_channel.indexOf("4K") > 0) { hlsUrl = data?.hls_url.replaceAll("main", "4000"); //4K频道只有一种清晰度可选,直接使用此清晰度 } var hlsfull = hlsUrl.replaceAll('&','%26'); //防止复制到命令行的地址产生歧义 document.body.innerHTML = `<h2><a href="${hlsfull}" alt="">${hlsfull}</a></h2><hr>`; // 创建实时更新的进度-代码块开始> var txtctt = document.createElement("h2"); txtctt.textContent = title; document.body.appendChild(txtctt); downloadM3U8Video(hlsUrl, title.concat(".ts"), { onProgress: (current, total) => { var cotp = `${Math.round((current / total) * 100)}`; txtctt.textContent = title + "---下载进程" + cotp + "%"; console.info(`进度: ${current}/${total} (${cotp}%)`); } }); // 创建实时更新的进度-代码块结束< } async function downloadM3U8Video(m3u8Url, outputFilename = 'video.ts', options = {}) { try { // 1. 获取并解析M3U8文件 const response = await fetch(m3u8Url); if (!response.ok) throw new Error(`无法访问 M3U8: ${response.status}`); const m3u8Content = await response.text(); const lines = m3u8Content.split('\n'); const baseUrl = m3u8Url.substring(0, m3u8Url.lastIndexOf("/") + 1); // 🐱猫抓扩展直接调用cbox时需要填写的参数设置>> cbox:"${url}" "%PUBLIC%\Downloads\cctv_${now}.MP4" //if (outputFilename.length > 4) return; // 要用[o(≡°ェ°≡)m]猫抓扩展直接调用cbox时,请解除本行注释,👆 if (!confirm('开始下载'+outputFilename)) {return;} // 不想要被询问,想直接用浏览器下载时,请注释本行 // 1.1 解析TS分片URL const segments = []; 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); } } if (segments.length === 0) throw new Error('在 M3U8 文件中未找到分片.'); console.log(`找到 ${segments.length} 个ts分片.`); // 2. 下载所有分片,采用流式合并 console.log('正在下载分片...'); 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(`无法访问分片: ${segmentResponse.status}`); const blob = await segmentResponse.blob(); blobs.push(blob); // 调用进度回调 if (typeof onProgress === 'function') { onProgress(i + 1, segments.length); } } catch (error) { console.error(`下载分片时出现错误 ${segments[i]}:`, error); throw error; // 可以选择继续或抛出错误 } } // 3. 下载合并完成后的视频 console.log('正在创建完整视频的下载链接...'); const mergedBlob = new Blob(blobs, { type: 'video/mp2t' }); const url = URL.createObjectURL(mergedBlob); const a = document.createElement('a'); a.href = url; a.download = outputFilename; a.style.display = "none"; document.body.appendChild(a); a.click(); // 4. 清理临时链接 setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 100); console.log('下载完成!'); return true; } catch (error) { console.error('下载完整视频时发生错误:', error); throw error; } } })();