央视视频下载2025年11月更新

首先感谢@xiaoxi-ij478大神的贡献,新方法目前已经在Windows、Macos、Linux、Termux跑通。为了让它多活一段时间,我会等域名挂掉再公开这个方法。

此方法仅供学习研究使用!

以下是原文:

这次先来看一下vdn返回的响应吧。

先拿一个示例请求来看看,链接是https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid=04a9d0abc9234716b574504290a15352

{
  "ack": "yes",
  "status": "001",
  "is_preview": "0",
  "tag": "健康之路,冀玉华,当归,黄芪,肉苁蓉,养生药膳",
  "title": "《健康之路》 20251128 用对补药来养生(上)",
  "play_channel": "CCTV-10高清",
  "produce": "",
  "editer_name": "liyuanyuan",
  "produce_id": "wxsb01",
  "column": "健康之路高清",
  "f_pgmtime": "2025-11-28 18:53:54",
  "segments": [
      {
          "guid": "24f71c11a3914d05b4b9c1c143d37b42",
          "title": "[健康之路]用对补药来养生(上) 当归",
          "start": 56680,
          "end": 765640
      },
      {
          "guid": "0d90c19c2d9642ccb21d0e2b67e8c09d",
          "title": "[健康之路]用对补药来养生(上) 黄芪",
          "start": 778240,
          "end": 1738720
      },
      {
          "guid": "5651559754f043c6aec1a631ca351e2a",
          "title": "[健康之路]用对补药来养生(上) 肉苁蓉",
          "start": 1754400,
          "end": 2298640
      }
  ],
  "image": "https://p5.img.cctvpic.com/fmspic/2025/11/28/04a9d0abc9234716b574504290a15352-1.jpg",
  "cdn_info": {
      "cdn_vip": "vod.cntv.lxdns.com",
      "cdn_code": "VOD-MP4-CDN-CNC",
      "cdn_name": "3rd网宿"
  },
  "video": 此处是视频分块,太长了省略
  "hls_cdn_info": {
      "cdn_vip": "hls.cntv.lxdns.com",
      "cdn_code": "VOD-HLS-CDN-CNCNEW",
      "cdn_name": "3rd网宿-新"
  },
  "hls_url": "https://hls.cntv.lxdns.com/asp/hls/main/0303000a/3/default/04a9d0abc9234716b574504290a15352/main.m3u8?maxbr=2048",
  "asp_error_code": "0",
  "manifest": {
      "audio_mp3": "https://dhlsaliwx01.v.cntv.cn/asp/audio/0/4/a/9/04a9d0abc9234716b574504290a15352/mp3/main.m3u8",
      "hls_audio_url": "https://dhlsaliwx01.v.cntv.cn/asp/hlsaudio/hls/main/0303000a/3/default/04a9d0abc9234716b574504290a15352/main.m3u8",
      "hls_enc_url": "https://dhlsaliwx01.v.cntv.cn/asp/enc/hls/main/0303000a/3/default/04a9d0abc9234716b574504290a15352/main.m3u8?maxbr=2048&contentid=18120319242338",
      "hls_h5e_url": "https://dh5wswx02.v.cntv.cn/asp/h5e/hls/main/0303000a/3/default/04a9d0abc9234716b574504290a15352/main.m3u8?maxbr=2048&contentid=15120519184043",
      "hls_enc2_url": "https://dhls2.cntv.cdn20.com/asp/enc2/hls/main/0303000a/3/default/04a9d0abc9234716b574504290a15352/main.m3u8?maxbr=2048&contentid=16121029231855"
  },
  "client_sid": "6m9b7S7mpR/idtHcCinq+Gt6rgHi6pgkBYLN8ByFlm0=",
  "public": "1",
  "is_invalid_copyright": "0",
  "is_protected": "0",
  "is_fn_hot": "true",
  "is_p2p_use": true,
  "default_stream": "chapters3",
  "lc": {
      "isp_code": "",
      "city_code": "",
      "provice_code": "",
      "country_code": "CN",
      "ip": "100.125.151.10"
  },
  "is_ipad_support": "true",
  "version": "0.2",
  "embed": "",
  "is_fn_multi_stream": false
}

先看一下它的结构吧

"ack": "yes"代表请求被成功处理

"status": "001"是视频状态,目前有四种状态代码001-004。

001代表正常,002代表前端隐藏,003代表后端删除,004代表GUID没有存在过,可能是你打错了。

"is_preview": "0"预览状态

"tag": "健康之路,冀玉华,当归,黄芪,肉苁蓉,养生药膳"标签

"title": "《健康之路》 20251128 用对补药来养生(上)"标题

"play_channel": "CCTV-10高清"频道

"produce": ""生产编号

"editer_name": "liyuanyuan"编辑姓名

"wxsb01"生产ID,这里面的wx指的应该是江苏省无锡市新吴区CNTV央视国际网络有限公司

"column": "健康之路高清"栏目名称

"f_pgmtime": "2025-11-28 18:53:54"节目时间

"segments"相关视频

"image"视频封面链接

下面是CDN信息了

CCTV现在有四种m3u8链接和一种mp4链接。我不知道为什么要编码这么多相同码率重复的版本。

  • "cdn_info"MP4格式的CDN链接,原来有华为和腾讯云之类的CDN,现在只剩网宿了。而且全都403了。(注意只是CDN层面的限制,后端依然有文件)
  • "video"视频分块,就是原来的mp4video63链接,现在已经不返回了。
  • "hls_url": "https://hls.cntv.lxdns.com/asp/hls/main/0303000a/3/default/04a9d0abc9234716b574504290a15352/main.m3u8?maxbr=2048"HLS的视频播放CDN链接,这个链接是给投屏用的。现在好像只剩网宿和腾讯云的CDN了,而且会强制返回最低的分辨率。最早大家就是用这种方法下载的。
  • "audio_mp3": "https://dhlsaliwx01.v.cntv.cn/asp/audio/0/4/a/9/04a9d0abc9234716b574504290a15352/mp3/main.m3u8"音频文件的MP3链接,应该是用于央视影音客户端听视频功能的。
  • "hls_audio_url": "https://dhlsaliwx01.v.cntv.cn/asp/hlsaudio/hls/main/0303000a/3/default/04a9d0abc9234716b574504290a15352/main.m3u8"音频文件的M3U8链接,应该是用于央视影音客户端听视频功能的。
  • "hls_enc_url": "https://dhlsaliwx01.v.cntv.cn/asp/enc/hls/main/0303000a/3/default/04a9d0abc9234716b574504290a15352/main.m3u8?maxbr=2048&contentid=18120319242338"用于央视影音电脑版APP和手机版APP在线播放视频的链接。目前无法解密。
  • "hls_h5e_url": "https://dh5wswx02.v.cntv.cn/asp/h5e/hls/main/0303000a/3/default/04a9d0abc9234716b574504290a15352/main.m3u8?maxbr=2048&contentid=15120519184043"用于www.cctv.comwww.cntv.cn在线播放视频的链接。2025年8月前可以解密,2025年8月后改用wasm和jvm加密,解密难度高。目前无法解密。
  • "hls_enc2_url": "https://dhls2.cntv.cdn20.com/asp/enc2/hls/main/0303000a/3/default/04a9d0abc9234716b574504290a15352/main.m3u8?maxbr=2048&contentid=16121029231855"用于央视影音PC版与手机版下载或缓存文件的链接。2025年11月强制降级分辨率为360p,即使用电脑或手机版本的央视影音缓存下来也是360p。可以解密。另外这个CDN拉黑了CCTVVideoDownloader这个项目所使用的UA,是专门给这个软件拉黑的,因为你把UA里面的x64改成x86都能200OK。

再后面的内容就是一些播放信息了。不过要注意的是,CCTV已经全面启用PCDN,所以你可以看到网页端有个"is_p2p_use": true。当然这个PCDN网页端很少遇到,因为现在主流浏览器都有强制HTTPS,不过要是客户端的话,那就多了,甚至还有用UDP播放视频的。

好了,讲了这么多现在讲一下怎么下载视频吧。

首先是我们目前有5个链接能下载。

  • mp4是不加密的分片,用于最早的flash播放器。我不知道flash已经退役这么多年为什么CCTV还在留着这些视频。
  • hls是不加密的,CDN限制了返回的分辨率。CDN回源有签名,所以不能回源改hosts下载。
  • dhls/enc是完全花屏的而且所有解密逻辑都在客户端里,没有有效的方式。CDN回源无签名,但是你解不了所以没啥用。
  • dhls2/enc2目前可解,但是CDN限制了返回的分辨率。CDN回源无签名,但是估计马上就要加签名。
  • dh5是25年8月以前的可以解密,8月以后的视频更新了算法,新算法解密难度高,解不了。CDN回源无签名,但是你解不了所以没啥用。

对于不同时期的视频所采取的下载方法也不一样

  • 下载视频之前先检查一下最原始的hls链接的视频是否花屏,如果不花屏的话算你捡到了,直接找个m3u8下载器下载然后ffmpeg合并就行。
  • 2007年-2009年的分段flv和mp4视频,需要在评论区留下GUID我帮你下,或发信至[email protected],不保证成功率。
  • 2009年年的m3u8链接,可以直接使用最早期不加密低分辨率hls域名下载,因为这些视频本来也没有高分辨率。
  • 2010年-2025年7月的m3u8链接,最好使用dh5域名,配上老版本的CCTVVideoDownloader下载。
  • 2025年8月至今的m3u8链接,只能使用本文中给出的软件下载,而且我相信这个方法也会很快挂掉。
  • 2009年-2011年的mp4链接,由于缺少url中的unix时间戳,无法下载。
  • 2012年至今的mp4链接,需要在评论区留下GUID我帮你下,或发信至[email protected],不保证成功率。
  • 前端隐藏或者因为旧版播放器失效放不出来的视频,需要在评论区留下GUID我帮你下,或发信至[email protected],不保证成功率。

剩余的存活域名

最后上文件

所以最后上文件点我下载使用方法还是一样的。另外我觉得它活不到年底。到时候再说吧。

还有这种方法有解不了的视频比如说https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid=054c22cc80b343489b8b87a71ef09f41(而且你会发现这个GUID少了一个m3u8链接),这种视频只能我帮你下。或者去用猫抓的浏览器缓存模式下载。

已知guid可以通过这个链接找回原来的视频播放界面。https://app.cctv.com/special/m/livevod/index.html?guid=054c22cc80b343489b8b87a71ef09f41&vtype=2

评论

  1. CV
    Windows Chrome 128.0.0.0
    2 月前
    2025-11-29 16:45:56

    希望此方案坚持些日子,博主超众。如若方便加v以请教:601196626,谢~

  2. TH
    Windows Edge 109.0.1518.140
    2 月前
    2025-11-29 23:20:54

    建议hls补充
    如果视频在网页端的布局非常简陋,则能直接获取高分辨率视频(仅能获取2024年5月31日之后的)

    • TH
      TH
      Windows Edge 109.0.1518.140
      2 月前
      2025-11-29 23:24:51
      • 博主
        TH
        Windows Chrome 142.0.0.0
        2 月前
        2025-11-29 23:35:11

        其实两个都限制码率了,只不过第一个限制到270p 450Kbps,第二个限制到720p 1200Kbps。这两个视频的原始码率都是2000Kbps。

        • weaponjang
          Sky
          Windows Chrome 137.0.0.0
          2 月前
          2025-12-01 14:22:19

          第一个其实限制到了850kbps,你可以试试把450.M3U8换成850.M3U8,前面的hls/450/中的450也要换成850,这个850的流是可以直接播放的

        • weaponjang
          Sky
          Windows Chrome 137.0.0.0
          2 月前
          2025-12-01 16:02:44

          第二个是没有限制到1200kbps的,用2000.m3u8也是可以的,只不过还是720P罢了

      • weaponjang
        TH
        Windows Chrome 137.0.0.0
        2 月前
        2025-12-01 14:08:50

        你这个非常简陋应该是指没有评论框和播放列表吧

    • weaponjang
      TH
      Windows Chrome 137.0.0.0
      2 月前
      2025-12-01 14:19:10

      这些个布局简陋的column属性和tag属性有点特殊,tag里包含特定人名的会没有评论和播放列表,column里写着”XX高清精切”的则能直接获取到不花屏的720P的M3U8链接

      • CV
        weaponjang
        Windows Chrome 128.0.0.0
        1 月前
        2025-12-02 19:32:27

        有720高清的,hls_url链接内容里,直接就写明了有1200.m3u8,没高清的只有个450.m3u8

        • weaponjang
          CV
          Windows Chrome 130.0.0.0
          1 月前
          2025-12-02 20:49:23

          感觉他们很精,有1200的其实就有2000的,只是他们故意不显示,改成2000是能用的,默认450的其实可以改成850,

          • 博主
            weaponjang
            Android Chrome 143.0.7499.34
            1 月前
            2025-12-03 10:46:26

            这个应该是CDN侧的一个查找替换,也就是说当网宿发现一个请求是到2000/1200/850,会自动改成450然后发送到源站。所以说你在URL里面看到的码率不一定是真的码率,很有可能是被CDN动过手脚的。

          • weaponjang
            Sky
            Windows Chrome 137.0.0.0
            1 月前
            2025-12-03 14:29:56

            我下载试过,码率是对应的码率,不过只在所谓网页画面简陋的视频生效,其他的普通视频则会被替换成低分辨率的

  3. weaponjang
    Windows Chrome 137.0.0.0
    2 月前
    2025-12-01 14:00:57

    你最后说的解不了的视频,其实只能用4000这个流,比如https://newcntv.qcloudcdn.com/asp/hls/4000/0303000a/3/default/054c22cc80b343489b8b87a71ef09f41/4000.m3u8
    原理是play_channel这个属性如果是CCTV-4K的,需要用4000.m3u8这个流才能访问,不过只有1080P的,且没有其他清晰度可选.
    经过CCTV这波屏蔽升级,现在基于cbox的解密工具的意义已经不大了,
    新的视频除了标题或内容包含现任主席的视频可以通过替换hls_url中的main成2000,获取到不花屏的720P视频,其他的视频要获取到不花屏的720P恐怕只能用缓存捕捉或录屏了,
    可以直接使用https://gf.qytechs.cn/zh-CN/scripts/466482这个脚本,能分析出网页能下载到的不花屏的最高清晰度,因为脚本内置下载功能,所以会直接提示是否立即下载,如果点击取消后还想用浏览器下载的,只要重新刷新一下就会提示是否下载了.

  4. 丁丁
    Windows Chrome 143.0.0.0
    1 月前
    2025-12-10 17:16:16

    有个叫小滑轮下载器的现在还能正常解密央视的视频,但是是收费的,是electron开发的,解压看了一下,没有找到cbox那玩意,只看到一些js文件,好像是js解密的,但是没学过js看不懂他的代码逻辑,大佬看有空能不能研究一下,https://www.feiaci.com/,这是下载地址

    • weaponjang
      丁丁
      Windows Chrome 137.0.0.0
      1 月前
      2025-12-11 12:08:08

      貌似解密逻辑在他的程序resourcesappsrcmainvideoDownloadcctvVideoPlayhtmlTem.js 有177KB大小,5000多行,有点难绷

      • 博主
        weaponjang
        Android Chrome 144.0.7559.3
        1 月前
        2025-12-11 13:56:22

        h5e可以用的,能跑通,就是解密速度比较慢

    • xiaoxi-ij478
      丁丁
      Linux Firefox 147.0
      1 月前
      2025-12-17 0:44:05

      他用的基本是现在网页端解密的方法,放到了一个看不见的浏览器窗口里,顺便还抄了点https://scz.617.cn/web/202408231518.txt里的代码

  5. weaponjang
    Windows Chrome 137.0.0.0
    1 月前
    2025-12-13 11:59:39

    从猫抓扩展提取了缓存捕捉功能,并转化成油猴脚本,match 属性现在只包含了CCTV的视频页,如有其他页面的需求,请自行添加网址
    去掉了调用在线ffmpeg合并文件的代码,只支持下载音频和视频分离的文件,需要下载后自行合并
    适合无法安装猫抓扩展,或者猫抓版本不兼容的浏览器

    // ==UserScript==
    // @name         猫抓媒体捕获工具 (CatCatcher)
    // @name:en      CatCatcher Media Capture Tool
    // @namespace    https://scriptcat.org/
    // @version      1.0.0
    // @description  捕获网页中的媒体流数据,支持视频/音频下载
    // @author       Original: CatCatcher, Adapted: YourName
    // @match        *://*.cctv.cn/*/V*.shtml*
    // @match        *://*.cctv.com/*/V*.shtml*
    // @grant        none
    // @run-at       document-start
    // @license      MIT
    // ==/UserScript==
    
    (function() {
        'use strict';
    
        // 等待DOM加载完成
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', initScript);
        } else {
            initScript();
        }
    
        function initScript() {
            // 避免重复注入
            if (window.catCatcherInstance) {
                console.log('CatCatcher 已存在,跳过初始化');
                return;
            }
    
            class CatCatcher {
            constructor() {
                console.log("catch.js Start");
    
                // 初始化属性
                this.enable = true;  // 捕获开关
                this.isComplete = false; // 捕获完成标志
                this.catchMedia = [];   // 捕获的媒体数据
                this.mediaSize = 0; // 捕获的媒体数据大小
                this.setFileName = null;    // 文件名
                this.catCatch = null; // UI元素
    
                // 移动面板相关属性
                this.x = 0;
                this.y = 0;
    
                // 初始化组件
                // 删除iframe sandbox属性 避免 issues #576
                this.setupIframeProcessing();
    
                // 初始化 Trusted Types
                this.initTrustedTypes();
    
                // 创建和设置UI
                this.createUI();
    
                // 代理MediaSource方法
                this.proxyMediaSourceMethods();
    
                // 自动跳转到缓冲尾
                if (localStorage.getItem("CatCatchCatch_autoToBuffered") == "checked") {
                    const autoToBufferedInterval = setInterval(() => {
                        const videos = document.querySelectorAll('video');
                        if (videos.length > 0 && Array.from(videos).some(video => !video.paused && video.readyState > 2)) {
                            const autoToBufferedElement = this.catCatch.querySelector("#autoToBuffered");
                            if (autoToBufferedElement) {
                                autoToBufferedElement.click();
                                clearInterval(autoToBufferedInterval);
                            }
                        }
                    }, 1000);
                }
            }
    
            /**
             * 设置iframe处理,删除sandbox属性
             * 解决 issues #576
             */
            setupIframeProcessing() {
                document.addEventListener('DOMContentLoaded', () => {
                    const processIframe = (iframe) => {
                        if (iframe && iframe.hasAttribute && iframe.hasAttribute('sandbox')) {
                            const clonedIframe = iframe.cloneNode(true);
                            clonedIframe.removeAttribute('sandbox');
                            if (iframe.parentNode) {
                                iframe.parentNode.replaceChild(clonedIframe, iframe);
                            }
                        }
                    };
    
                    document.querySelectorAll('iframe').forEach(processIframe);
    
                    const observer = new MutationObserver((mutationsList) => {
                        for (const mutation of mutationsList) {
                            if (mutation.type === 'childList') {
                                mutation.addedNodes.forEach(node => {
                                    if (node.nodeName === 'IFRAME') {
                                        processIframe(node);
                                    } else if (node.querySelectorAll) {
                                        node.querySelectorAll('iframe').forEach(processIframe);
                                    }
                                });
                            }
                        }
                    });
                    observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
                });
            }
    
            /**
             * 初始化 Trusted Types
             */
            initTrustedTypes() {
                let createHTML = (string) => {
                    try {
                        const fakeDiv = document.createElement('div');
                        fakeDiv.innerHTML = string;
                        createHTML = (string) => string;
                    } catch (e) {
                        if (typeof trustedTypes !== 'undefined') {
                            const policy = trustedTypes.createPolicy('catCatchPolicy', { createHTML: (s) => s });
                            createHTML = (string) => policy.createHTML(string);
                            const _innerHTML = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
                            Object.defineProperty(Element.prototype, 'innerHTML', {
                                set: function (value) {
                                    _innerHTML.set.call(this, createHTML(value));
                                }
                            });
                        } else {
                            console.warn("trustedTypes不可用,跳过安全策略设置");
                        }
                    }
                };
                createHTML("<div></div>");
            }
    
            /**
             * 创建UI元素
             */
            createUI() {
                const buttonStyle = 'style="border:solid 1px #000;margin:2px;padding:2px;background:#fff;border-radius:4px;border:solid 1px #c7c7c780;color:#000;"';
                const checkboxStyle = 'style="-webkit-appearance: auto;"';
    
                this.catCatch = document.createElement("div");
                this.catCatch.setAttribute("id", "CatCatchCatch");
                const style = `
                    display: flex;
                    flex-direction: column;
                    align-items: flex-start;`;
                this.catCatch.innerHTML = `
                <div id="catCatch" style="{style}">
                    <div id="tips"></div>
                    <button id="download"{buttonStyle}>下载已捕获的数据</button>
                    <button id="clean" {buttonStyle}>删除已捕获数据</button>
                    <div><button id="hide"{buttonStyle}>隐藏</button><button id="close" {buttonStyle}>关闭</button></div>
                    <label><input type="checkbox" id="autoDown" checked=checked{checkboxStyle}>完成捕获自动下载</label>
                    <label><input type="checkbox" id="autoToBuffered" {checkboxStyle} checked=checked>自动跳转缓冲尾</label>
                    <label><input type="checkbox" id="checkHead"{checkboxStyle} checked=checked>清理多余头部数据</label>
                    <label><input type="checkbox" id="completeClearCache" {localStorage.getItem("CatCatchCatch_completeClearCache") || ""}{checkboxStyle}>下载完成后清空数据</label>
                    <details>
                        <summary id="summary">文件名设置</summary>
                        <div style="font-weight:bold;">文件名: </div><div id="fileName"></div>
                        <div style="font-weight:bold;">表达式: </div><div id="selector">Null</div>
                        <div style="font-weight:bold;">正则: </div><div id="regular">Null</div>
                        <button id="setSelector" {buttonStyle}>表达式提取</button>
                        <button id="setRegular"{buttonStyle}>正则提取</button>
                        <button id="setFileName" {buttonStyle}>手动填写</button>
                    </details>
                    <details>
                    <summary>test</summary>
                        <button id="test"{buttonStyle}>test</button>
                        <button id="restart" {buttonStyle}>从头捕获</button>
                        <label>
                          <input type="checkbox" id="restartAlways"{localStorage.getItem("CatCatchCatch_restart") || ""} {checkboxStyle} checked=checked>
                          始终从头捕获(beta)
                        </label>
                    </details>
                </div>`;
                this.catCatch.style = `
                    position: fixed;
                    z-index: 999999;
                    top: 10%;
                    left: 90%;
                    background: rgb(255 255 255 / 85%);
                    border: solid 1px #c7c7c7;
                    border-radius: 4px;
                    color: rgb(26, 115, 232);
                    padding: 5px 5px 5px 5px;
                    font-size: 12px;
                    font-family: "Microsoft YaHei", "Helvetica", "Arial", sans-serif;
                    user-select: none;`;
    
                // 创建 Shadow DOM
                this.createShadowRoot();
    
                // 初始化UI元素引用
                this.tips = this.catCatch.querySelector("#tips");
                this.fileName = this.catCatch.querySelector("#fileName");
                this.selector = this.catCatch.querySelector("#selector");
                this.regular = this.catCatch.querySelector("#regular");
    
                if (!this.tips || !this.fileName || !this.selector || !this.regular) {
                    console.error("UI元素初始化失败,找不到必要的DOM元素");
                }
    
                // 初始化显示
                this.tips.innerHTML = "等待视频播放";
                this.selector.innerHTML = localStorage.getItem("CatCatchCatch_selector") ?? "Null";
                this.regular.innerHTML = localStorage.getItem("CatCatchCatch_regular") ?? "Null";
    
                // 绑定事件
                this.bindEvents();
    
                // 自动从头捕获设置
                if (localStorage.getItem("CatCatchCatch_restart") == "checked") {
                    this.setupAutoRestart();
                }
            }
    
            /**
             * 创建 Shadow DOM
             * 解决 issues #693 安全使用attachShadow 从iframe中获取原生方法
             */
            createShadowRoot() {
                try {
                    // 解决 issues #693 安全使用attachShadow 从iframe中获取原生方法
                    const createSecureShadowRoot = (element, mode = 'closed') => {
                        const getPristineAttachShadow = () => {
                            try {
                                const iframe = document.createElement('iframe');
                                const parentNode = document.body || document.documentElement;
                                parentNode.appendChild(iframe);
                                const pristineMethod = iframe.contentDocument.createElement('div').attachShadow;
                                iframe.remove();
                                if (pristineMethod) return pristineMethod;
                            } catch (e) {
                                console.log("获取原生attachShadow方法失败:", e);
                            }
                            return Element.prototype.attachShadow;
                        };
    
                        const executor = Element.prototype.attachShadow.toString().includes('[native code]')
                            ? Element.prototype.attachShadow.bind(element)
                            : getPristineAttachShadow().bind(element);
    
                        try {
                            return executor({ mode });
                        } catch (e) {
                            console.error('Shadow DOM 创建失败:', e);
                            // 应急处理:降级方案
                            return document.createElement('div');
                        }
                    };
    
                    // 创建 Shadow DOM 放入CatCatch
                    const divShadow = document.createElement('div');
                    const shadowRoot = createSecureShadowRoot(divShadow);
                    shadowRoot.appendChild(this.catCatch);
    
                    // 页面插入Shadow DOM
                    const htmlElement = document.getElementsByTagName('html')[0];
                    if (htmlElement) {
                        htmlElement.appendChild(divShadow);
                    } else {
                        document.appendChild(divShadow);
                    }
                } catch (error) {
                    console.error("创建Shadow DOM失败:", error);
                    // 降级方案:直接添加到body
                    try {
                        const body = document.body || document.documentElement;
                        body.appendChild(this.catCatch);
                    } catch (e) {
                        console.error("降级添加UI也失败:", e);
                    }
                }
            }
    
            /**
             * 绑定事件处理函数
             */
            bindEvents() {
                // 移动面板相关事件
                this.catCatch.addEventListener('mousedown', this.handleDragStart.bind(this));
    
                // 设置选项相关事件
                const autoDown = this.catCatch.querySelector("#autoDown");
                if (autoDown) autoDown.addEventListener('change', this.handleAutoDownChange.bind(this));
    
                const restartAlways = this.catCatch.querySelector("#restartAlways");
                if (restartAlways) restartAlways.addEventListener('change', this.handleRestartAlwaysChange.bind(this));
    
                // 按钮相关事件
                const clean = this.catCatch.querySelector("#clean");
                if (clean) clean.addEventListener('click', this.handleClean.bind(this));
    
                const download = this.catCatch.querySelector("#download");
                if (download) download.addEventListener('click', this.handleDownload.bind(this));
    
                const hide = this.catCatch.querySelector("#hide");
                if (hide) hide.addEventListener('click', this.handleHide.bind(this));
    
                const img = this.catCatch.querySelector("img");
                if (img) img.addEventListener('click', this.handleHide.bind(this));
    
                const close = this.catCatch.querySelector("#close");
                if (close) close.addEventListener('click', this.handleClose.bind(this));
    
                const restart = this.catCatch.querySelector("#restart");
                if (restart) restart.addEventListener('click', this.handleRestart.bind(this));
    
                const setFileName = this.catCatch.querySelector("#setFileName");
                if (setFileName) setFileName.addEventListener('click', this.handleSetFileName.bind(this));
    
                const test = this.catCatch.querySelector("#test");
                if (test) test.addEventListener('click', this.handleTest.bind(this));
    
                const summary = this.catCatch.querySelector("#summary");
                if (summary) summary.addEventListener('click', this.getFileName.bind(this));
    
                const completeClearCache = this.catCatch.querySelector("#completeClearCache");
                if (completeClearCache) completeClearCache.addEventListener('click', this.handleCompleteClearCache.bind(this));
    
                // 自动跳转到缓冲节点
                // this.autoToBufferedFlag = true;
                const autoToBuffered = this.catCatch.querySelector("#autoToBuffered");
                if (autoToBuffered) autoToBuffered.addEventListener('click', this.handleAutoToBuffered.bind(this));
    
                // 文件名设置相关事件
                const setSelector = this.catCatch.querySelector("#setSelector");
                if (setSelector) setSelector.addEventListener('click', this.handleSetSelector.bind(this));
    
                const setRegular = this.catCatch.querySelector("#setRegular");
                if (setRegular) setRegular.addEventListener('click', this.handleSetRegular.bind(this));
            }
    
            /**
             * 处理面板拖动事件
             * @param {MouseEvent} event
             */
            handleDragStart(event) {
                this.x = event.pageX - this.catCatch.offsetLeft;
                this.y = event.pageY - this.catCatch.offsetTop;
    
                const moveHandler = this.handleMove.bind(this);
                document.addEventListener('mousemove', moveHandler);
    
                document.addEventListener('mouseup', () => {
                    document.removeEventListener('mousemove', moveHandler);
                }, { once: true });
            }
    
            /**
             * 处理面板移动事件
             * 通过鼠标事件更新面板位置
             * @param {MouseEvent} event         */
            handleMove(event) {
                if (!this.catCatch) return;
                this.catCatch.style.left = (event.pageX - this.x) + 'px';
                this.catCatch.style.top = (event.pageY - this.y) + 'px';
            }
    
            handleAutoDownChange(event) {
                localStorage.setItem("CatCatchCatch_autoDown", event.target.checked ? "checked" : "");
            }
    
            handleRestartAlwaysChange(event) {
                localStorage.setItem("CatCatchCatch_restart", event.target.checked ? "checked" : "");
            }
    
            /**
             * 处理清理缓存事件
             * @param {MouseEvent} event         */
            handleClean(event) {
                if (window.confirm("确认清除缓存?")) {
                    this.clearCache();
                    constclean = this.catCatch.querySelector("#clean");
                    if (!clean) return;clean.innerHTML = "清理完成!";
                    setTimeout(() => {
                        if (clean)clean.innerHTML = "清理缓存";
                    }, 1000);
                }
            }
    
            /**
             * 处理下载事件
             * @param {MouseEvent} event 
             */
            handleDownload(event) {
                try {
                    if (this.isComplete || window.confirm("提前下载可能会造成数据混乱.确认?")) {
                        this.catchDownload();
                    }
                } catch (error) {
                    console.error("下载处理失败:", error);
                    alert("下载过程中出错,请查看控制台");
                }
            }
    
            handleHide(event) {
                const catCatchElement = this.catCatch.querySelector('#catCatch');
                if (catCatchElement.style.display === "none") {
                    catCatchElement.style.display = "flex";
                    this.catCatch.style.opacity = "";
                } else {
                    catCatchElement.style.display = "none";
                    this.catCatch.style.opacity = "0.5";
                }
            }
    
            handleClose(event) {
                if (this.isComplete || window.confirm("确认关闭?")) {
                    this.clearCache();
                    this.enable = false;
                    this.catCatch.style.display = "none";
                    window.postMessage({ action: "catCatchToBackground", Message: "script", script: "catch.js", refresh: false });
                }
            }
    
            /**
             * 从头捕获
             * @param {MouseEvent} event 
             */
            handleRestart(event) {
                const checkHead = this.catCatch.querySelector("#checkHead");
                if (checkHead) checkHead.checked = true;
    
                this.clearCache();
                document.querySelectorAll("video").forEach((element) => {
                    element.currentTime = 0;
                    element.play();
                });
            }
    
            handleSetFileName(event) {
                this.setFileName = window.prompt("输入文件名, 不包含扩展名", this.setFileName ?? "");
                this.getFileName();
            }
    
            handleTest(event) {
                console.log("捕获的媒体数据:", this.catchMedia);
            }
    
            handleCompleteClearCache(event) {
                localStorage.setItem("CatCatchCatch_completeClearCache", event.target.checked ? "checked" : "");
            }
    
            /**
             * 自动缓冲尾
             * @param {MouseEvent} event 
             */
            handleAutoToBuffered(event) {
                // if (!this.autoToBufferedFlag) return;
                // this.autoToBufferedFlag = false;
    
                const autoToBuffered = this.catCatch.querySelector("#autoToBuffered");
                if (!autoToBuffered) return;
    
                localStorage.setItem("CatCatchCatch_autoToBuffered", event.target.checked ? "checked" : "");
    
                const videos = document.querySelectorAll("video");
                for (let video of videos) {
                    video.addEventListener("progress", (event) => {
                        try {
                            if (video.buffered && video.buffered.length > 0) {
                                const bufferedEnd = video.buffered.end(0);
                                if (autoToBuffered.checked && bufferedEndautoToBuffered.checked = false;
                    });
                }
            }
    
            /**
             * CSS选择器 提取文件名
             * @param {MouseEvent} event 
             */
            handleSetSelector(event) {
                const result = window.prompt("Selector", localStorage.getItem("CatCatchCatch_selector") ?? "");
                if (result == null) return;
    
                if (result == "") {
                    this.clearFileName("selector");
                    return;
                }
    
                let title;
                try {
                    title = document.querySelector(result);
                } catch (e) {
                    this.clearFileName("selector", "选择器语法错误!");
                    return;
                }
    
                if (title && title.innerHTML) {
                    this.selector.innerHTML = this.stringModify(result);
                    localStorage.setItem("CatCatchCatch_selector", result);
                    this.getFileName();
                } else {
                    this.clearFileName("selector", "表达式错误, 无法获取或内容为空!");
                }
            }
            /**
             * 正则 提取文件名
             * @param {MouseEvent} event 
             */
            handleSetRegular(event) {
                let result = window.prompt("文件名获取正则", localStorage.getItem("CatCatchCatch_regular") ?? "");
                if (result == null) return;
    
                if (result == "") {
                    this.clearFileName("regular");
                    return;
                }
    
                try {
                    new RegExp(result);
                    this.regular.innerHTML = this.stringModify(result);
                    localStorage.setItem("CatCatchCatch_regular", result);
                    this.getFileName();
                } catch (e) {
                    this.clearFileName("regular", "正则表达式错误");
                    console.log(e);
                }
            }
    
            /**
             * 核心函数 代理MediaSource方法
             */
            proxyMediaSourceMethods() {
                // 代理 addSourceBuffer 方法
                window.MediaSource.prototype.addSourceBuffer = new Proxy(window.MediaSource.prototype.addSourceBuffer, {
                    apply: (target, thisArg, argumentsList) => {
                        try {
                            const result = Reflect.apply(target, thisArg, argumentsList);
    
                            // 标题获取
                            setTimeout(() => { this.getFileName(); }, 2000);
                            this.tips.innerHTML = "捕获数据中...";
    
                            this.catchMedia.push({ mimeType: argumentsList[0], bufferList: [] });
                            const index = this.catchMedia.length - 1;
    
                            // 代理 appendBuffer 方法
                            result.appendBuffer = new Proxy(result.appendBuffer, {
                                apply: (target, thisArg, argumentsList) => {
                                    Reflect.apply(target, thisArg, argumentsList);
    
                                    if (this.enable && argumentsList[0]) {
                                        this.mediaSize += argumentsList[0].byteLength || 0;
                                        if (this.tips) {
                                            this.tips.innerHTML = "捕获数据中...: " + this.byteToSize(this.mediaSize);
                                        }
                                        this.catchMedia[index].bufferList.push(argumentsList[0]);
                                    }
                                }
                            });
    
                            return result;
                        } catch (error) {
                            console.error("addSourceBuffer 代理错误:", error);
                            return Reflect.apply(target, thisArg, argumentsList);
                        }
                    }
                });
    
                // 代理 endOfStream 方法
                window.MediaSource.prototype.endOfStream = new Proxy(window.MediaSource.prototype.endOfStream, {
                    apply: (target, thisArg, argumentsList) => {
                        try {
                            Reflect.apply(target, thisArg, argumentsList);
    
                            if (this.enable) {
                                this.isComplete = true;
                                if (this.tips) {
                                    this.tips.innerHTML = "捕获完成";
                                }
    
                                if (localStorage.getItem("CatCatchCatch_autoDown") == "checked") {
                                    setTimeout(() => this.catchDownload(), 500);
                                }
                            }
                        } catch (error) {
                            console.error("endOfStream 代理错误:", error);
                            return Reflect.apply(target, thisArg, argumentsList);
                        }
                    }
                });
            }
    
            /**
             * 自动从头捕获
             * 监控DOM变化,自动重置视频播放位置
             */
            setupAutoRestart() {
                document.addEventListener('DOMContentLoaded', () => {
                    document.querySelectorAll('video').forEach((video) => this.resetVideoPlayback(video));
    
                    // 监控 DOM
                    const observer = new MutationObserver(mutations => {
                        mutations.forEach(mutation => {
                            mutation.addedNodes.forEach(node => {
                                try {
                                    if (node.tagName === 'VIDEO') {
                                        this.resetVideoPlayback(node);
                                    } else if (node.querySelectorAll) {
                                        node.querySelectorAll('video').forEach(video => this.resetVideoPlayback(video));
                                    }
                                } catch (error) {
                                    console.error("处理新添加的视频节点失败:", error);
                                }
                            });
                        });
                    });
    
                    observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
                });
            }
    
            /**
             * 重置视频播放位置
             * @param {Object} video 
             */
            resetVideoPlayback(video) {
                if (!video) return;
                const timer = setInterval(() => {
                    if (!video.paused) {
                        video.currentTime = 0;
                        const checkHead = this.catCatch.querySelector("#checkHead");
                        if (checkHead) checkHead.checked = true;
                        this.clearCache();
                        clearInterval(timer);
                    }
                }, 500);
    
                // 5秒后如果还没有检测到播放,就清除定时器
                setTimeout(() => clearInterval(timer), 5000);
    
                video.addEventListener('play', () => {
                    if (!video.isResetCatCatch) {
                        video.isResetCatCatch = true;
                        video.currentTime = 0;
                        const checkHead = this.catCatch.querySelector("#checkHead");
                        if (checkHead) checkHead.checked = true;
                        this.clearCache();
                    }
                }, { once: true });
            }
    
            /**
             * 下载捕获的数据
             */
            catchDownload() {
                if (this.catchMedia.length == 0) {
                    alert("没抓到有效数据");
                    return;
                }
    
    
                /**
                 * 检查文件
                 * 检查是否有头部文件 没有头部文件则提示"不使用ffmpeg合并"
                 * 检查是否有多个头部文件 根据用户选项 是否清理多于头部数据
                 */
                const checkHead = this.catCatch.querySelector("#checkHead");
                // 仅确认一次是否清除多余头部数据
                let userConfirmedHeadChoice = false;
    
                for (let key in this.catchMedia) {
                    if (!this.catchMedia[key]?.bufferList || this.catchMedia[key].bufferList.length <= 1) continue;
                    let lastHeaderIndex = -1;
    
                    // 遍历所有 buffer 寻找最后一个头部
                    for (let i = 0; i < this.catchMedia[key].bufferList.length; i++) {
                        const data = new Uint8Array(this.catchMedia[key].bufferList[i]);
    
                        // 检查MP4格式的头部 (ftyp)
                        if (data.length > 8 &&
                            data[4] === 0x66 && // 'f'
                            data[5] === 0x74 && // 't'
                            data[6] === 0x79 && // 'y'
                            data[7] === 0x70)   // 'p'
                        {
                            lastHeaderIndex = i; // 持续更新直到找到最后一个头部
                        }
                        // 检查WebM格式的头部 (1A 45 DF A3)
                        else if (data.length > 4 &&
                            data[0] === 0x1A &&
                            data[1] === 0x45 &&
                            data[2] === 0xDF &&
                            data[3] === 0xA3) {
                            lastHeaderIndex = i; // 持续更新直到找到最后一个WebM头部
                        }
                    }
                    if (lastHeaderIndex == -1) {
                        alert("没有检测到视频头部数据, 请使用本地工具处理");
                    }
                    if (lastHeaderIndex > 0) {
                        // 只有第一次遇到多余头部且用户尚未选择时才提示
                        if (!userConfirmedHeadChoice && !checkHead.checked) {
                            checkHead.checked = window.confirm("检测到多余头部数据, 是否清除?");
                            userConfirmedHeadChoice = true; // 标记已经询问过用户
                        }
    
                        if (checkHead.checked) {
                            this.catchMedia[key].bufferList.splice(0, lastHeaderIndex); // 移除最后一个头部之前的所有元素
                        }
                    }
                }
    
                this.downloadDirect();
    
                if (this.isComplete) {
                    if (localStorage.getItem("CatCatchCatch_completeClearCache") == "checked") { this.clearCache(); }
                    if (this.tips) {
                        this.tips.innerHTML = "下载完毕...";
                    }
                }
            }
            /**
             * 直接下载捕获的数据
             */
            downloadDirect() {
                const a = document.createElement('a');
                let downloadCount = 0;
    
                for (let item of this.catchMedia) {
                    if (!item || !item.bufferList || item.bufferList.length === 0) continue;
    
                    const mime = (item.mimeType && item.mimeType.split(';')[0]) || 'video/mp4';
                    const type = mime.split('/')[0] == "video" ? "m4v" : "m4a";
                    const fileBlob = new Blob(item.bufferList, { type: mime });
    
                    a.href = URL.createObjectURL(fileBlob);
                    a.download = `{this.fileName ? this.fileName.innerHTML.trim() : document.title}.{type}`;
                    a.click();
    
                    // 释放URL对象以避免内存泄漏
                    setTimeout(() => URL.revokeObjectURL(a.href), 100);
                    downloadCount++;
                }
    
                a.remove();
    
                if (downloadCount === 0) {
                    alert("没有有效数据可下载");
                }
            }
    
            clearFileName(obj = "selector", warning = "") {
                localStorage.removeItem("CatCatchCatch_" + obj);
                const element = obj == "selector" ? this.selector : this.regular;
                if (element) element.innerHTML = "未设置";
                this.getFileName();
                if (warning) alert(warning);
            }
    
            /**
             * 清理缓存
             */
            clearCache() {
                this.mediaSize = 0;
                if (this.isComplete) {
                    this.catchMedia = [];
                    this.isComplete = false;
                    return;
                }
    
                for (let key in this.catchMedia) {
                    const media = this.catchMedia[key];
                    if (media && media.bufferList && media.bufferList.length > 0) {
                        // 保留第一个buffer块,清除其余的
                        const firstBuffer = media.bufferList[0];
                        media.bufferList = [firstBuffer];
                        this.mediaSize += firstBuffer ? (firstBuffer.byteLength || 0) : 0;
                    }
                }
            }
    
            byteToSize(byte) {
                if (!byte || byte < 1024) return "0KB";
                if (byte < 1024 * 1024) {
                    return (byte / 1024).toFixed(1) + "KB";
                } else if (byte < 1024 * 1024 * 1024) {
                    return (byte / 1024 / 1024).toFixed(1) + "MB";
                } else {
                    return (byte / 1024 / 1024 / 1024).toFixed(1) + "GB";
                }
            }
    
            /**
             * 获取文件名
             */
            getFileName() {
                try {
                    if (!this.fileName) return;
    
                    if (this.setFileName) {
                        this.fileName.innerHTML = this.stringModify(this.setFileName);
                        return;
                    }
    
                    let name = "";
                    const selectorKey = localStorage.getItem("CatCatchCatch_selector");
                    if (selectorKey) {
                        const title = document.querySelector(selectorKey);
                        if (title && title.innerHTML) {
                            name = title.innerHTML;
                        }
                    }
    
                    const regularKey = localStorage.getItem("CatCatchCatch_regular");
                    if (regularKey) {
                        const str = name == "" ? document.documentElement.outerHTML : name;
                        const reg = new RegExp(regularKey, "g");
                        let result = str.match(reg);
                        if (result) {
                            result = result.filter((item) => item !== "");
                            name = result.join("_");
                        }
                    }
    
                    this.fileName.innerHTML = name ? this.stringModify(name) : this.stringModify(document.title);
                } catch (error) {
                    console.error("获取文件名失败:", error);
                    if (this.fileName) this.fileName.innerHTML = this.stringModify(document.title);
                }
            }
    
            stringModify(str) {
                if (!str) return "untitled";
    
                // eslint-disable-next-line no-useless-escape
                return str.replace(/[\u0027\u005C\u003A\u002A\u003F\u0022\u003C\u002F\u003E\u007C\u007E]/g, function (m) {
                    return {
                        "'": '&#39;',
                        '\\': '&#92;',
                        '/': '&#47;',
                        ':': '&#58;',
                        '*': '&#42;',
                        '?': '&#63;',
                        '"': '&quot;',
                        '<': '&lt;',
                        '>': '&gt;',
                        '|': '&#124;',
                        '~': '_'
                    }[m];
                });
            }
        }
    
            try {
                // 创建并启动CatCatcher实例
                window.catCatcherInstance = new CatCatcher();
                console.log('CatCatcher 油猴脚本启动成功');
            } catch (error) {
                console.error('CatCatcher 初始化失败:', error);
            }
        }
    
        // 添加错误处理
        window.addEventListener('error', function(e) {
            if (e.error && e.error.message && e.error.message.includes('CatCatcher')) {
                console.error('CatCatcher 相关错误:', e.error);
            }
        });
    
    })();
  6. weaponjang
    Windows Chrome 137.0.0.0
    3 周前
    2025-12-24 10:50:47

    dh5cntv.a.bdydns.com这个域名已经G了,感觉ENC2接口下载失效只是时间问题,还是缓存捕捉靠谱

    • 博主
      weaponjang
      Windows Chrome 143.0.0.0
      3 周前
      2025-12-24 22:53:50

      用这个吧

      • weaponjang
        Sky
        Windows Chrome 137.0.0.0
        3 周前
        2025-12-26 11:47:47

        这么快就链接失效了,命令行用着还是稍微有点吃力,我打算用nwjs包装一下

      • weaponjang
        Sky
        Windows Chrome 137.0.0.0
        3 周前
        2025-12-26 14:59:55

        貌似不太方便用nwjs包装,只能把run.js和download.js整合成一个文件了,看着清爽些

      • weaponzhang
        Sky
        Android Chrome 114.0.5735.196
        3 周前
        2025-12-26 17:21:08

        内置的ffmpeg体积有点大,其实可以用小点的版本,我的才56MB。node.msi要安装过,无法做到开箱即用,建议用单个的node.exe文件而不是安装包

    • TH
      weaponjang
      Android Chrome 131.0.6778.200
      3 周前
      2025-12-27 11:10:12

      现在这两个都没了

      • TH
        TH
        Android Chrome 131.0.6778.200
        3 周前
        2025-12-27 11:11:14

        两个cdn现在均无法访问,客户端缓存现在还是标清画质(下载时大小还是高清画质的大小)

        • 博主
          TH
          Windows Chrome 143.0.0.0
          3 周前
          2025-12-27 12:08:37

          它禁止CDN把自己的客户端都给误伤了

          • weaponzhang
            Sky
            Android Chrome 114.0.5735.196
            3 周前
            2025-12-27 17:36:27

            你发的工具不完美,合并后的视频中间会出现卡顿和绿屏

          • weaponzhang
            Sky
            Android Chrome 114.0.5735.196
            3 周前
            2025-12-27 17:42:03

            估计他觉得用客户端下载视频的人少吧。反正能看就行。

          • 博主
            weaponzhang
            Windows Chrome 143.0.0.0
            3 周前
            2025-12-27 17:59:19

            你发个guid我看看是哪个视频啊

          • weaponjang
            Sky
            Windows Chrome 130.0.0.0
            3 周前
            2025-12-27 19:00:41

            基于你的工具做了一个只需要输入视频页网址的,不需要手动获取GUID的,放到你的工具文件夹里运行带x86的那个exe文件
            你可以试试效果,顺便看下有没有什么bug,
            https://pan.baidu.com/s/1O8UTel-UAuftdamsaWIYVw?pwd=52pj

          • weaponzhang
            Sky
            Android Chrome 114.0.5735.196
            3 周前
            2025-12-29 5:24:34

            我找不到那个视频了,电脑重装回10了,浏览记录没了,依稀记得是cctv-15那里的某个素人唱歌的视频,但是当我在用我基于你的工具开发的工具时又无法复现这个现象了,是有点奇怪的。

          • 博主
            weaponzhang
            Windows Chrome 143.0.0.0
            2 周前
            2025-12-29 20:53:38

            win11确实烂,好多莫名其妙的bug。

  7. Jason
    Windows Chrome 142.0.0.0
    3 周前
    2025-12-29 16:54:15

    博主你好,看你的方法研究了半天也没弄明白,输入对应的指令程序直接关掉了。。。GUID 是 dff5f6717ca44a06a0161153b2935e56 , 请问可以帮我下载一下最高画质么?谢谢!

    • 博主
      Jason
      Windows Chrome 143.0.0.0
      2 周前
      2025-12-29 20:51:46

      通过网盘分享的文件:1767012010.mp4
      链接: https://pan.baidu.com/s/1RarzFZQt_t1ohYC67HoUrw?pwd=r2dv 提取码: r2dv 复制这段内容后打开百度网盘手机App,操作更方便哦
      –来自百度网盘超级会员v5的分享

  8. 博主
    Windows Chrome 143.0.0.0
    2 周前
    2025-12-29 20:59:00

    drm.cntv.vod.dnsv1.com
    dhls.cntv.baishancdnx.cn.bsgslb.cn

    • 博主
      Sky
      Windows Chrome 143.0.0.0
      2 周前
      2025-12-29 21:02:26

      新域名

      • TH
        Sky
        Android Chrome 131.0.6778.200
        2 周前
        2025-12-30 0:06:09

        感觉这域名活不过春节那会

        • 博主
          TH
          Windows Chrome 143.0.0.0
          2 周前
          2025-12-30 0:30:25

          活不到元旦结束的

          • weaponjang
            Sky
            Windows Chrome 137.0.0.0
            2 周前
            2025-12-30 12:46:58

            感觉找新域名的意义不大了,估计再搞下去可能就要会员或者实时更新的token了,你发的那个工具在Win10上很完美,就是不好包装成单文件,我暂时只把run.js和download.js合成了到一个index,js,用go开发的接受网址的程序感觉可以搞成图形界面,黑框框的观感还是不太好看

    • LastD
      Sky
      Windows Chrome 122.0.6261.95
      2 周前
      2026-1-03 0:38:52

      大佬,这个CDN地址要去哪里才能抓取到啊?

  9. 王平安
    Windows Chrome 143.0.0.0
    1 周前
    2026-1-08 11:13:59

    现在下载是花屏了

    • weaponjang
      王平安
      Windows Chrome 137.0.0.0
      7 天前
      2026-1-09 9:58:33

      还是缓存法靠谱点,就是略慢,可以看看评论区里我发的那个油猴脚本

发送评论 编辑评论


				
上一篇