docs: README 对齐 v1.1.3 并补实战验收与 2 条踩坑

- 文件表标注脚本版本,成品视频由 gitignore 挡在库外
- 实现要点补 @connect 全白名单与网络容错三层防线
- 验收结果补 2026-10-08 实战全集下载成功
- 新增踩坑 11: @connect 漏域名被油猴静默拦截的特征与解法
- 新增踩坑 12: 代理节点瞬断指纹(remote.ip 为空 + 对齐重启时间)
This commit is contained in:
2026-10-08 17:47:13 +08:00
parent 19a3828b18
commit ff0e2a478e
+10 -6
View File
@@ -21,8 +21,9 @@ uv sync # 自动建 .venv 并装依赖(pycryptodome)
|---|---| |---|---|
| `hls_dl.py` | 通用 HLS 下载器:拉 m3u8 → 并发下分片 → AES-128 解密 → ffmpeg 合并 | | `hls_dl.py` | 通用 HLS 下载器:拉 m3u8 → 并发下分片 → AES-128 解密 → ffmpeg 合并 |
| `reqable_mcp.py` | Reqable MCP 客户端:命令行翻抓包记录、调用 Reqable 的 50+ 个工具 | | `reqable_mcp.py` | Reqable MCP 客户端:命令行翻抓包记录、调用 Reqable 的 50+ 个工具 |
| `huangguo_video_dl.user.js` | **油猴脚本**:详情页一键全集下载,打包 ZIP 存到 ~/Downloads | | `huangguo_video_dl.user.js` | **油猴脚本 v1.1.3**:详情页一键全集下载,打包 ZIP 存到 ~/Downloads |
| `陪玩假女友_1.mp4` | 成品:123s / 720×1280 / H.264+AAC / 21.93 MB |
> 成品视频(`*.mp4`/`*.zip`)由 `.gitignore` 挡在库外,不入库。
## 用法一:HLS 直接下载 ## 用法一:HLS 直接下载
@@ -91,7 +92,8 @@ d = json.loads(c.call('capture_live_get_by_id', {'id': 4289})['content'][0]['tex
| 环节 | 方案 | | 环节 | 方案 |
|---|---| |---|---|
| 跨域请求 | `GM_xmlhttpRequest`(`@connect huangguo.me` + `*.rlkfus.cn`) | | 跨域请求 | `GM_xmlhttpRequest`(`@connect huangguo.me` + `*.rlkfus.cn` + `*.cloudfront.net` + `*` 兜底) |
| 网络容错 | 失败重试 2 次(1s)→ 降级原生 `fetch`(CDN 带 `ACAO: *`)→ 完整错误显示到面板 |
| 剧名/集数 | `[data-drama-title]` / `data-total-episode` / `a.detail-episode[data-ep]` | | 剧名/集数 | `[data-drama-title]` / `data-total-episode` / `a.detail-episode[data-ep]` |
| m3u8 地址 | play 页 `.dplayer-video` 的 `data-url`,**拼 `/h5/m3u8/` 前缀** | | m3u8 地址 | play 页 `.dplayer-video` 的 `data-url`,**拼 `/h5/m3u8/` 前缀** |
| AES-128 解密 | WebCrypto `subtle.decrypt`,IV = media sequence 大端 | | AES-128 解密 | WebCrypto `subtle.decrypt`,IV = media sequence 大端 |
@@ -99,9 +101,9 @@ d = json.loads(c.call('capture_live_get_by_id', {'id': 4289})['content'][0]['tex
| 打包 | **手写 STORE ZIP**(每集 CRC32 后转 `Blob` 卸到堆外,只拼头尾字节) | | 打包 | **手写 STORE ZIP**(每集 CRC32 后转 `Blob` 卸到堆外,只拼头尾字节) |
| 付费集 | 识别 `data-coin-dialog-open`,自动跳过并在完成提示里列出 | | 付费集 | 识别 `data-coin-dialog-open`,自动跳过并在完成提示里列出 |
**已验证**(Node 仿真真实管线): **已验证**:
- 下载解密转封装:第 1 集 21.9MB/3.7s、第 2 集 14.5MB/2.4s,解压后文件名与时长均正确 - Node 仿真管线:下载解密转封装 21.9MB/3.7s;200MB 打包 **1 秒**,`testzip()` CRC 全过,RSS 294MB
- 打包:10 集 × 20MB = 200MB 压测,**1 秒内完成**,Python `testzip()` CRC 逐字节全过,内存 RSS 294MB - **实战验收**(2026-10-08):《高三爱情故事》全集一键下载成功,ZIP 落入 ~/Downloads ✅
## 踩坑记录 ## 踩坑记录
@@ -115,3 +117,5 @@ d = json.loads(c.call('capture_live_get_by_id', {'id': 4289})['content'][0]['tex
8. **m3u8 单次即全集**:每次请求返回的就是该集完整列表(如第 1 集 18 段);之前遇到的"3 段广告"是另一个轮换位,别混淆。 8. **m3u8 单次即全集**:每次请求返回的就是该集完整列表(如第 1 集 18 段);之前遇到的"3 段广告"是另一个轮换位,别混淆。
9. **`mux.js` 全局名**:浏览器 UMD 分支挂的是 **`muxjs`**(不是 `mux`,vm 实测),脚本里写 `window.muxjs || window.mux` 兼容;Node 加载需 `npm i global` 提供 shim,入口 `mux.mp4.Transmuxer`。 9. **`mux.js` 全局名**:浏览器 UMD 分支挂的是 **`muxjs`**(不是 `mux`,vm 实测),脚本里写 `window.muxjs || window.mux` 兼容;Node 加载需 `npm i global` 提供 shim,入口 `mux.mp4.Transmuxer`。
10. **JSZip `generateAsync` 卡 90%**:Firefox 下多集大文件(每集 ~20MB×N)会永久挂死——所有 `Uint8Array` 压在 JS 堆上引发 GC 风暴。解法是弃用 JSZip,每集算完 CRC 立即 `new Blob([bytes])` 卸到堆外,ZIP 组装只拼 local header/central dir/EOCD 字节 + Blob 引用,200MB 1 秒完成。 10. **JSZip `generateAsync` 卡 90%**:Firefox 下多集大文件(每集 ~20MB×N)会永久挂死——所有 `Uint8Array` 压在 JS 堆上引发 GC 风暴。解法是弃用 JSZip,每集算完 CRC 立即 `new Blob([bytes])` 卸到堆外,ZIP 组装只拼 local header/central dir/EOCD 字节 + Blob 引用,200MB 1 秒完成。
11. **`@connect` 漏域名 = 油猴静默拦截**:同一站点不同剧集可能走不同 CDN(《陪玩假女友》→ `rlkfus.cn`,《高三爱情故事》→ `cloudfront.net`),白名单没盖住时请求**不发也不报权限错**,只是 GM `onerror` 网络错误。特征:Reqable 里 key 请求有、分片零记录。解法:`@connect *` 兜底。
12. **代理节点瞬断的指纹**:Reqable 记录 `connection.remote.ip` **为空** = 请求进了代理但上游没建连(网络层问题,不是脚本问题);拿记录时间戳对 `ps -o lstart -p <mihomo_pid>` 看重启时间即可破案。此时段国内域名 200、国外 CDN 全挂,正是分流节点故障的典型特征。