
Cat-Catch浏览器资源嗅探与M3U8下载背后的工程取舍全拆解【免费下载链接】cat-catch猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch猫抓 Cat-Catch 是一个浏览器资源嗅探扩展把页面发出的网络请求拦下来变成一份可下载的资源列表。它最硬的工程难点是Manifest V3 的 Service Worker 随时被浏览器休眠嗅探数据不能因此丢失。本文覆盖 v1.0.24 到 v2.7.2。它到底在解决什么问题没有嗅探扩展时用户想存一个视频要先打开 DevTools 的 Network 面板手动过滤几百条请求把 URL 复制给下载工具再把 Referer、cookie 手工补进请求头——直播页基本到此为止。具体到三个最痛的场景直链能复制但带上鉴权头的地址换到下载器里就是 403请求头无法随链接一起带走视频src是blob:地址真实媒体走 MediaSource 管道页面上根本不存在可复制的 URLM3U8 直播由几百个 TS 切片加轮转密钥组成另存为对这类流没有任何意义。所以 Cat-Catch 的技术路线本质上是在两个约束之间做取舍被动嗅探webRequest 监听零页面侵入但看不到 blob 流和主动捕获往页面注入脚本能抓住 MediaSource但有破坏页面的风险。它两条路都做了拆成 webRequest 主通道和注入脚本旁路两个通道并行。完整文档见 官方 README核心引擎拆解资源嗅探是怎么跑起来的数据流页面请求 →js/background.js的 webRequest 事件onSendHeaders 记请求头、onResponseStarted 取响应头→findMedia()按扩展名、MIME、大小、正则过滤 → 去重后写入 Service Worker 内存里的cacheData→ 防抖落盘到 storage.session → popup/侧边栏实时渲染。blob 地址不走这条路catch-script/catch.js代理MediaSource.addSourceBuffer把真实地址经 postMessage 回传由addMedia通道补进同一份缓存。关键实现下面这段来自js/background.js的findMedia()是所有嗅探结果的唯一收口非核心分支已省略。// js/background.js —— findMedia()所有嗅探入口的收口函数 function findMedia(data, isRegex, filter, timer) { // SW 被强杀后每 500ms 重试直到全局初始化完成防止丢首条请求 if (!G || !G.initSyncComplete || !G.initLocalComplete) { timer || setTimeout(() findMedia(data, isRegex, filter, true), 500); return; } if (!G.enable || isBlocked(data.tabId) || data.method OPTIONS) return; if (!isRegex) { data.header getResponseHeadersValue(data); // 解析 content-length / content-type filter ?? CheckExtension(ext, data.header?.size); // 扩展名 大小规则 filter ?? CheckType(data.header.type, data.header?.size); // MIME 大小规则 } if (!filter) return; data.tabId data.tabId -1 ? G.tabId : data.tabId; if (cacheData[data.tabId].length G.maxLength) { // 9999 条上限 cacheData[data.tabId] []; return; } if (dedupHit(data.tabId, data.url)) return; // urlMap 指纹去重 const info { name, url: data.url, size, ext, type, tabId, requestHeaders }; cacheData[data.tabId].push(info); save(info.tabId); // 防抖满 100 条等 5 秒写一次 storage }代码位置做了什么为什么这么做setTimeout 500ms轮询SW 被杀重启后等待初始化再处理webRequest 回调先于全局就绪到达直接 return 就永久丢这条资源CheckExtension/CheckType扩展名、MIME 双重过滤大小支持表达式v2.6.8 起大小可写100 KB、500-1000 MB由operatorCheck求值G.maxLength9999单标签超上限直接清空列表内存封顶v2.5.9 引入代价是长视频页前段数据被丢弃G.urlMap指纹 SetURL 去重集合满 500 条即清空替代 v2.6.2 前的线性扫描超 500 条强制关去重压 CPU各模块分工如下js/background.js是唯一的协调中枢模块职责对外暴露的接口数js/background.jswebRequest 拦截、过滤、缓存存储、消息路由3 个主事件入口js/content-script.js维持心跳 Port、postMessage 中继、媒体控制5 个 postMessage 动作catch-script/catch.jsMediaSource 代理捕获 blob 流1 个addSourceBuffer 代理js/m3u8.jsjs/m3u8.downloader.jsM3U8 解析与多线程合并下载Downloader 事件总线on/emitjs/popup.js资源列表渲染、筛选、下载触发runtime.sendMessage 消息版本考古5 个改变方向的决策v1.0.242022-03用MV3 心跳机制维持 Service Worker 存活当时面临的选择A接受 SW 五分钟休眠醒来后丢的数据靠重新触发还是 B让 content-script 开一条长连接 Port每 4 分 10 秒唤醒一次后台。最终选了 B原因是 webRequest 事件不可重放丢了就是丢了。代价是每个存活标签页都挂着一条 Port另加每 25 秒一次getPlatformInfo轮询兜底。这个机制到 v2.7.2 仍在运行从未被替代。v2.0.02022-06整体迁移 Manifest V3注入脚本补位当时面临的选择A留在 MV2 的常驻后台页稳定但被 Chrome 强制淘汰还是 B迁 MV3用注入脚本补齐能力。最终选了 B没有别的选项。代价是 v1.0.17 到 v2.0.0 之间反复修复SW 重启丢数据而 webRequest 看不到的 MSE 流要靠catch.js代理 MediaSource 去抓等于往每个页面注入近千行脚本。这笔页面侵入的账在 v2.2.0 的 Trusted Types 和 v2.6.2 的 Shadow DOM 加固里被逐笔偿还。v2.4.72022-11重写 M3U8 解析器下载器并发线程定在 6当时面临的选择A继续修补旧下载器#274 全量重下、#276 丢线程连着出还是 B重写 Downloader管线 事件总线 每切片独立 AbortController。最终选了 B因为旧结构四次大改都撞到天花板。代价是 v2.4.8 整个版本在修新下载器的 bug#272/#274/#276 及 2G 边界。旧下载器保留为Test version开关稳定跑了五个版本后才在 v2.6.2 删除。v2.5.32023-02storage.local 迁移到 storage.session当时面临的选择A保留 local 的磁盘持久化还是 B资源缓存全部改走 session 内存存储。最终选了 B原因是 changelog 原话减少 IO 错误导致扩展无法使用——磁盘写入偶发报错会让整个扩展失效。代价是浏览器退出后资源列表清空且下限抬到 Chrome 104。这个可接受丢失后来成了特性Cat-Catch 只保证会话内不丢不承诺跨会话。v2.7.02025-03M3U8 解析器从缓存读数据解决一次性 URL当时面临的选择A解析时重新请求 m3u8还是 B优先读浏览器 HTTP 缓存里的那份。最终选了 B因为直播流的签名 URL 用完即失效重新请求直接 403。代价是依赖浏览器缓存保留策略不可控Firefox 下blob:地址还要先 fetch 成文本存进m3u8Text中转同版修复。这个决策让解析器不再依赖 URL 的时效性。把这些决策串起来看项目的技术取向其实是不在浏览器的新规面前放弃后台能力而是用用户态的绕行方案补齐 API 缺口且每笔代价都用明确的版本锚点偿还从不靠一次大重写解决。工程权衡账本表 1资源与性能画像指标项目中的值/区间测量方式或推断依据同类型项目常见值内存占用单标签资源列表≤9999 条超限整体清空findMedia中G.maxLength判断推断多数同类嗅探器无上限长视频页可致 OOM存储写入频率满 100 条防抖 5s 写一次间隔小于 500ms 时 2s 写一次save()防抖逻辑推断逐条实时写IO 量约为其 520 倍M3U8 并发下载线程默认 6用户可调js/m3u8.js的#thread默认值v2.4.7 确定48单切片重试最多 3 次退避 500ms × 重试次数m3u8.downloader.js的MAX_RETRIES代码确认01 次多数不重试MV3 心跳机制开销每标签 1 条 Port 每 25s 一次 API 调用js/background.js心跳段代码确认无接受 SW 休眠丢数据表 2兼容性/降级矩阵能力Chromium 114Chromium 93113Firefox 128降级策略sidePanel 侧边栏可用v2.6.3 前直接不可用不支持设置切回 popup 模式storage.session 会话存储Chrome 104回退 local回退 localstorage.session ?? storage.local模拟手机端 UAChrome 102declarativeNetRequest按钮隐藏MAIN world 改写 UAdefineProperty注入深度搜索/录制脚本scripting APIChrome 102按钮隐藏v2.5.7 起支持按版本隐藏功能项M3U8 blob 地址下载fetch 直接下载fetch 直接下载先 fetch 入m3u8Text再解析js/content-script.js分支处理表 3已知边界与未解决问题问题描述触发条件当前状态源码位置资源超 9999 条直接清空而非分页或落盘单标签捕获数超上限官方未修复前段数据丢失js/background.jsfindMediaSW 强杀后 500ms 轮询窗口期浏览器频繁杀 SW全局未就绪官方未修复高频杀进程时可能丢资源js/background.jstimer 分支指纹集满 500 条后关闭去重同标签大量重复请求已接受列表可能重复js/background.jsG.urlMap安全与边界项目主动选择不做的事嗅探工具的攻击面天然很大它必须看见页面全部请求及头部host_permissions因此是all_urls。真正的风险不在它能做什么而在它把不该留的敏感数据留在了哪里。围绕这一点项目有几处明确的不做默认不向任何远端上传嗅探数据send2local、MQTTv2.6.4等数据通道全部由用户手动开启G.send2local默认关闭不保留历史资源库v2.5.3 选 session 存储后列表随浏览器退出即清空没有跨会话数据库不采集任何使用统计或遥测源码中未找到相关实现不全量保存请求头getRequestHeaders只保留 12 个鉴权相关头名加x-前缀关键词命中其余丢弃捕获面板对页面隔离catch.js用 closed Shadow DOM还从临时 iframe 里取原生attachShadow防页面脚本篡改issue #693。manifest.json的权限声明如下permissions: [ tabs, webRequest, downloads, storage, webNavigation, alarms, declarativeNetRequest, scripting, sidePanel, contextMenus ], host_permissions: [*://*/*, all_urls]这份清单没有冗余项——webRequest 管嗅探、scripting 管注入、alarms 管清理、contextMenus 是 v2.7.0 右键菜单——但all_urls加全量网络可见性使它成为高价值目标这是所有嗅探器都躲不开的成本权限清单本身解决不了。如果由你来加一个功能最有价值的一个功能是 M3U8 下载断点续传直播录制场景下意义最大现在errorIndexes只记失败切片浏览器一关录了一半的流只能从头来。改动应插在js/m3u8.downloader.js的Downloader和js/m3u8.js的allCompleted处理之间复用js/background.js里现成的 alarm 落盘通道把已完成索引写进 storage.session不新增任何模块。接口示意// js/m3u8.downloader.js —— 断点续传接口示意 class Downloader { async start() { const saved (await chrome.storage.session.get(m3u8Resume))[this.jobKey]; if (saved) saved.done.forEach(i this.buffer[i] null); // 留洞等待补抓 for (let i 0; i this.thread; i) this.downloader(); } // downloader() 成功分支内追加每 50 个切片落盘一次 onSaved() { if (this.success % 50 0) chrome.storage.session.set({ m3u8Resume: { [this.jobKey]: this.successIndexes() } }); } }影响面是m3u8.html的全部重新下载按钮要拆成断点续传 / 重下两个入口sequentialPush的顺序推送逻辑对留洞切片天然兼容基本不用动。这个功能的最大风险是直播切片 URL 带会过期的签名参数断点记录可能指向死链必须与 v2.7.0 的从缓存读数据逻辑联动否则续传会退化成整段重下。MV3 后台生命周期问题目前被缓解到约八成心跳 Port 加 session 存储让常规嗅探几乎不丢数据9999 条上限把内存钉死在可预期的位置。但强杀后的 500ms 轮询窗口和超限清空丢数据这两处仍是未修复状态。Cat-Catch 没有解决这个难题它只是把数据丢失压缩到用户感知不到的量级。【免费下载链接】cat-catch猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考