猫抓 Cat-Catch:从资源嗅探到 M3U8 引擎的 6 次架构选型
【免费下载链接】cat-catch猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch
猫抓(Cat-Catch)是一个浏览器资源嗅探扩展,当前版本 2.7.2,运行在 Manifest V3 之上。它要解决的核心痛点是:网页里正在播放或加载的媒体资源(mp4、m3u8、mpd 等),用户往往拿不到直链,或者直链带 Referer 校验无法直接下载。猫抓的能力边界是:被动嗅探 + 主动捕获 + M3U8/MPD 解析合并 + WebRTC 录制,覆盖 Chromium 系和 Firefox,不支持 Safari。
后台为什么不会死:Service Worker 的三条保活路径
MV3 把 background 从常驻页面改成 Service Worker,空闲约 5 分钟即被浏览器终止。对普通扩展这不重要,但对嗅探工具是致命的——webRequest回调注册在 Worker 里,Worker 一死,所有请求就都漏掉了。猫抓在 js/background.js 里做了三件事:
// 心跳保活:popup 打开时建立 Port,Worker 内 4 分 10 秒后主动断开 chrome.runtime.onConnect.addListener(function (Port) { if (chrome.runtime.lastError || Port.name !== "HeartBeat") return; Port.postMessage("HeartBeat"); const interval = setInterval(function () { clearInterval(interval); Port.disconnect(); }, 250000); });为什么是 250000ms(4 分 10 秒)而不是 300000ms?因为浏览器给的终止窗口约 5 分钟,留 50 秒余量应对系统调度抖动,断开后由 popup 重新连接形成循环。这是第二、三条路径之外的兜底,前两条都更"免费":
| 保活手段 | 原理 | 触发条件 | 代价 |
|---|---|---|---|
注册webNavigation空监听 | 导航事件本身就是活动信号 | 用户在浏览网页时持续有效 | 无 |
setInterval(getPlatformInfo, 25s) | 定时调用 API 阻止休眠判定 | Worker 存活期间 | 每 25 秒一次轻量调用 |
| 心跳 Port + 500ms 自唤醒 | 显式通信延长寿命;被杀后首个事件回调里setTimeout重新初始化 | 事件驱动 | 被杀瞬间可能丢一条数据 |
第三条路径值得单独说:findMedia入口检查全局初始化标志,未完成就 500ms 后重试自身——Worker 被杀后靠下一条网络请求"把自己叫醒"。这等于承认了保活不可能 100% 成功,转而保证"死了也能立刻复活"。保活方案本身不追求精确,追求的是恢复路径足够短。
模块职责:谁负责抓,谁负责下,谁负责存
猫抓没有做微内核那一套,实际是"一个中心 + 几个专用页"的结构。核心判断标准只有一条:数据能不能离开 background。
| 模块 | 核心职责 | 对外接口 | 依赖谁 |
|---|---|---|---|
| js/background.js | 嗅探主逻辑、请求头缓存、数据持久化 | webRequest回调、runtime消息 | 全部模块的数据源头 |
| catch-script/catch.js | 页内主动捕获(代理 MediaSource)、捕获面板 UI | 注入页面,经runtime回传数据 | background 的开关与设置 |
| js/m3u8.downloader.js | 切片并发下载、合并、转码调度 | m3u8.html 页面内 | hls.js、mux、m3u8-decrypt(见 lib/) |
| js/m3u8.js / js/mpd.js | M3U8/DASH 解析页逻辑、参数拼装 | 各自独立 HTML 页面 | background 的嗅探数据 |
| js/popup.js | 资源列表、筛选、正则匹配、预览入口 | popup.html / sidePanel | background 存储的数据 |
| catch-script/search.js、recorder*.js、webrtc.js | 深度搜索、视频缓存录制、WebRTC 录制 | 按需注入的独立脚本 | 页面环境 |
边界的取舍很实际:解析器、下载器、预览器都做成独立 HTML 页面而不是塞进 popup,因为每个页面有自己的第三方库(hls.js、mpd-parser、StreamSaver),独立页面天然隔离内存与脚本生命周期;代价是多几次页面间消息往返。主动捕获脚本放在catch-script/而非js/,区分"注入网页执行的代码"和"扩展自有页面代码",这个目录约定在 review 时比注释更省解释成本。模块划分的第一原则是让重逻辑(解析、下载)远离生命周期最短的上下文(Worker 和 popup)。
图:M3U8 解析器页面,切片列表、自定义请求头、密钥验证与 ffmpeg 参数都集中在这一个独立页面里
性能与稳定性:三个场景的阈值怎么定
并发下载。2.4.7 版本把 m3u8 解析器最大下载线程调整为 6。没有做动态线程池,原因是切片下载是 IO 密集且单任务体量小,固定 6 并发在大多数网络下已接近带宽上限,而动态调度在 Service Worker 被杀的场景下状态难以恢复。稳定性补丁都花在恢复路径上:2.4.8 修"下载失败丢失线程",2.6.2 加录制失败重试,2.7.1 加下载出错自动重试——与其优化正常路径的速度,不如保证异常路径不卡死。
内存。三个阈值各管一层:
| 场景 | 阈值 | 版本 | 解决的问题 |
|---|---|---|---|
| 每页面最大储存 | 9999 条 | 2.5.9 | 广告密集的页面把缓存无限撑大 |
| popup 加载 | 超过 500 条可中断 | 2.4.2 | 长列表渲染卡死 popup |
| 资源去重 | urlMap + 重复资源排除 | 2.6.2 重构 | 同一视频跨页重抓造成内存翻倍 |
IO。2.5.3 把运行时数据从storage.local迁到storage.session,官方 changelog 的原话是"减少 IO 错误导致扩展无法使用",要求 Chrome 104+。持久化不再每次嗅探都写盘,而是靠chrome.alarms定时把内存里的cacheData快照写一次,代码里就一行回退:
(chrome.storage.session ?? chrome.storage.local).set({ MediaData: cacheData });??直接覆盖了 Firefox 和旧版本 Chromium 两种不支持的场景。写盘频率从"每个请求"降到"每个 alarm 周期",磁盘写入量降了一到两个数量级。稳定性阈值宁可保守一点(9999 而非 999),因为溢出被丢弃的是一条资源记录,而内存撑爆是整个扩展不可用。
Chrome 与 Firefox:一份代码,两套 manifest
猫抓的 Firefox 适配策略是"能兼容就兼容,不能兼容就换 manifest",而不是做运行时 API 桥接层。两份入口配置对比:
| 能力 | Chromium | Firefox | 降级方案 |
|---|---|---|---|
| background 加载 | MV3 service_worker | MV2background.scripts(见 manifest.firefox.json) | Firefox 直接脚本加载,background.js开头用typeof G === 'undefined'判断是否重复加载 |
| 侧边栏 sidePanel | 支持(Chromium 114+,2.6.3 修了低版本无法安装的问题) | 不支持 | 回退到 popup / 弹出新窗口两种模式 |
| storage.session | 支持(104+) | 不支持 | 回退storage.local |
| 注入类脚本(深度搜索/录制) | 全版本 | 128+ 才开放(2.5.7) | 低版本隐藏入口 |
| blob url 下载(m3u8 切片) | 正常 | 曾无法下载,2.7.0 修复 | 无,只能修 |
2.5.7 让 Firefox 也升到 MV3 结构,但保留了 V2 式的 scripts 加载方式——因为 Firefox 的 MV3 background 当时同样受限,与其等 Firefox 补齐,不如自己选更可控的加载路径。Chromium 侧的最低版本 93 写死在 manifest 的minimum_chrome_version,低于 102/114 的功能靠运行时检测隐藏按钮。多端适配的结论是:差异写在构建产物(manifest)层,而不是 if-else 层,这样每个分支的行为可以单独验证。
图:Edge 等 Chromium 系浏览器的安装引导,走同一套 MV3 manifest
安全与隐私:只做了三件低成本的事
猫抓不声称有体系化安全架构,实际投入集中在三个具体问题上:
| 问题 | 方案 | 位置 |
|---|---|---|
| 注入的 UI 被页面 CSP(Trusted Types)拦截 | 运行时探测:先试写innerHTML,失败再创建catCatchPolicy策略,都不行就跳过 | catch-script/catch.js |
| 部分网站不希望被嗅探 | 屏蔽列表(可白名单模式),2.6.5 加全局强制屏蔽列表 | background 的blockUrlSet |
| 扩展页面自身 | 显式声明script-src 'self'; object-src 'self'CSP | manifest |
Trusted Types 那段探测逻辑值得看一眼它的姿态:
// 先试探页面是否开启 Trusted Types,未开启则直接透传 try { const fakeDiv = document.createElement('div'); fakeDiv.innerHTML = string; createHTML = (string) => string; } catch (e) { /* 页面开启时创建 policy 拦截 innerHTML 赋值 */ }这是典型的"探测优先"写法:不预设环境,先试一次再决定路径。隐私侧的边界更简单——数据只存本地 storage,MQTT、aria2 RPC、数据发送这些出站能力全部是用户手动配置的目标地址,扩展不内置任何上报端点。安全投入与嗅探工具的实际风险匹配:页面会杀你的 UI,但不会偷你的数据。
版本演进与选型逻辑:六个关键节点回看
| 版本 | 关键变化 | 为什么这么选 |
|---|---|---|
| 1.0.24 | HeartBeat 首次加入 | MV3 转型期最小代价止血,先让扩展可用 |
| 2.0.0 | Worker 被杀后 500ms 自唤醒;视频主动捕获 | 接受"保活会失败",把重心挪到恢复速度;被动嗅探拿不到的源主动抓 |
| 2.2.2 | 解析器换 hls.js,下载器与解析器分离 | 自研解析的 bug 面太大,引入成熟库;分离后两者可独立迭代 |
| 2.4.7 | 下载线程固定 6 | 放弃动态调度,换取异常恢复的确定性 |
| 2.5.3 / 2.5.9 | storage.session;9999 条上限 + 屏蔽列表 | 用存储层换 IO 错误率;用硬上限换内存下界 |
| 2.6.2 / 2.6.8 | 侧边栏模式;EXT-X-BYTERANGE 支持 | 侧边栏是体验升级但绑定 Chromium 114+,故做成可选;BYTERANGE 是长尾 M3U8 结构,不做就覆盖不了 |
收束成四条判断:
- 恢复优先于保活。与其花力气让 Worker 永不休眠,不如让被杀后的复活路径短到 500ms。
- 阈值是防溢出用的,不是优化用的。9999、6 线程、500 条,这些数字的取值逻辑都是"超过就坏",而非"超过就慢"。
- 浏览器差异写进 manifest,不写进代码。运行时 if-else 分支会随版本腐烂,双 manifest 则每个分支独立可测。
- 第三方库只引入不重造。hls.js、mux、mpd-parser 全部走 lib/ 现成方案,自研集中在"扩展特有"的嗅探与调度层。
这套取舍的共同点是:每个决定都优先保证"坏了能恢复、能关掉、能回退",功能扩张始终排在稳定性之后。对于同样要跨浏览器、跨版本长期维护的扩展项目,这个顺序本身比任何单个方案都值得借鉴。
【免费下载链接】cat-catch猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考