fix(ncm): 封面链分诊与修复 —— CNMPlayer 头(Referer+UA 解403)/webp 魔数内容型先判不解码走兜底/URL 追加 param=300y300 缩略图/403 与 webp 单独分诊日志不盲重试

- cover.c: 请求头对齐 CNMPlayer(Referer https://music.163.com/ + UA Mozilla/5.0 CNMPlayer/0.1);
  下载后先判 content-type 含 webp 或临时文件魔数 RIFF....WEBP(stb_image 不支持),回 webp=1 不解码,
  worker 打 'webp unsupported' 立即清目标走兜底(保留歌单封面),不再当 fetch failed 盲目指数重试;
  403 单独日志(anti-hotlink/forbidden, no retry),确定性 4xx 不重试(沿用 311)。
- cover_request 入口 cover_norm_url 追加 ?param=300y300(已有 param= 尊重原样, 溢出保原 URL),
  缓存键/去重/下载 URL 同参版(省内存+出 jpeg)。
- ncm_app_internal.h: NCM_COVER_USE_PARAM/NCM_COVER_PARAM 共享宏(cover.c 与 sim 同源)。
- bench/cover_chain_sim.c: 补 ncm_http_download/meow_img_load_file/meow_img_downscale 桩对齐现行
  cover.c(旧 ncm_http_request 桩已过时), url_hash 同参数规则; 新增 T8 webp/T9 403 用例。
- 验证: cover_chain_sim T1-T9 ALL PASS + stream/player_flow/window/search/cache_evict/degrade/mem
  sim 全 PASS + core host+mipsel(qemu) PASS + build_ncm.sh 0 warning(text 1019834/bss 1362500 持平)
  + ncm_host UI dump exit=0。0 新增真实 CDN 请求; 设备端 CDN 矩阵因设备不可达未执行(见 REPORT 34.6)。

新 ncm md5 a7c22bd4ee3bcd02fb24046d27a33242 (未部署, 留主会话上卡)
This commit is contained in:
AskaEth committed 2026-09-02 10:37:24 +08:00
1 parent c7ef1ebb04
commit aeb9176101
6 files changed
+308 -20

No files matched your search

+94
View File
@@ -2994,3 +2994,97 @@ cb8187868f71a9fa155075d0624711d6 ncm # mipsel 静态 App(text 101711
凭 `play: dl` 日志定位;②hires 档静默给 mp3 显示「已降级至 极高 HQ」而非 Hi-Res;③封面 404
立即跳过不空转;④我创建的歌单无收藏按钮;⑤缓存目录名 `song_<id>_<level>.<ext>` 无双点;
⑥home topbar 三动作显示位图图标(非豆腐)。
---
## 34. 封面链分诊与修复:防盗链 403 / webp 解码失败 / 尺寸参数 / 分诊日志(2026-09-02)
> 真机已实锤三种封面失败形态(详见下「真机三分型」):①404 死链(CDN 源站无对象,`X-Error-Info: Origin`,
> 182B JSON 错误体,311/§33.3 已做 4xx 不重试);②**403 防盗链**(p3.music.126.net 返 150B text/html,
> 缺 Referer/UA 被拦嫌疑大);③**200 但解码失败**(rc=0 status=200 仍 fetch failed——响应体疑为 webp,
> stb_image 不支持)。
### 34.1 真机三分型结论(本批未能在真机复测——设备 10.78.193.50 全程不可达,见 34.6 诚实出入)
本批无法执行**设备端 CDN 行为矩阵**(设备 sleep/WiFi 休眠,`No route to host`),故三种分型的鉴定依据:
①404 由 §30.5/§33.3 已实测锚定;②403 与③200-webp 由本批任务书给定、代码静态分析 + 宿主 mock 链
验证(stb_image v2.18 无 webp 解码器,`meow_img_load_file` 对 webp 必返 NULL → 旧逻辑当成
「fetch failed」盲指数退避重试)。**UA/Referer/param 三参数取值**:对齐 CNMPlayer 客户端参考实现
(`Referer: https://music.163.com/` + `User-Agent: Mozilla/5.0 CNMPlayer/0.1`,见 CNMPlayer `src/app/mod.rs
App::new` 的 `default_headers`);`?param=300y300` 为网易云图片 CDN 标准缩略参数(ncm-api-rs 亦以其为
已知格式)。真机矩阵留 34.6 后续确认。
### 34.2 修法(`app/logic/cover.c` + `app/ncm_app_internal.h`)
- **请求头对齐 CNMPlayer**:`req.extra_hdrs = "Referer: https://music.163.com/\r\nUser-Agent: Mozilla/5.0 CNMPlayer/0.1"`
(常量宏 `NCM_COVER_REFERER`/`NCM_COVER_UA`,改 UA 只动一处)。修 403 防盗链(②)。
- **webp 先判不解码**:`download_cover` 成功后、`meow_img_load_file` 前,检查 `resp.content_type`
含 webp 或临时文件魔数 `RIFF....WEBP`(`file_is_webp`,读前 12 字节一次小 fopen)→ 回 `*out_webp=1`
返回 NULL;worker 判 webp 单独打日志 `cover: webp unsupported url=.. (no decode)`,立即清目标走兜底
(播放页保留歌单封面 `g_st.cover_url`),**不盲目指数重试**(修 ③)。
- **封面 URL 追加尺寸参数**:`cover_norm_url`(`cover_request` 入口处规范化)对不含 `param=` 的 URL 追加
`?param=300y300`(已有 `?` 用 `&`;溢出 512 缓冲则放弃追加保原 URL)。`NCM_COVER_USE_PARAM=1`;
`NCM_COVER_PARAM` 宏放共享头 `ncm_app_internal.h`,与 `bench/cover_chain_sim.c` 的 URL 期望哈希同源,
避免字符串漂移。省内存(300×300 ≈20KB vs 全尺寸 1.34MB)+ CDN 出 jpeg(避 webp)+ 命中率高。
- **分诊日志强化**:403 单独打 `cover: fetch fail url=.. rc=.. status=403 (anti-hotlink/forbidden, no retry)`;
404 及其它确定性 4xx `(4xx, no retry)`;webp `(no decode)`;瞬时错误才指数退避。确定性 4xx + webp
一律立即清目标不重试(已有 4xx 逻辑,本批补 403 区分日志 + webp 分支)。
### 34.3 worker 队列评估(304 建议:单槽→有界 FIFO+优先级)
**结论:不改。** 当前 cover worker 为「单槽 latest-wins」(`g_tgt_url` 被最新 `cover_request` 覆写)——
对封面这种**仅当前播放页需要**的瞬态展示资产,语义已最优:切歌即最新目标,旧请求在途完成时因
`g_tgt_url` 变更被丢弃(`if (strcmp(g_tgt_url, url)==0)` 判元),天然取消过期封面。改成有界 FIFO+
优先级会为**不会显示的过期封面**白付解码 CPU + 驻留内存(设备 RAM 仅 56MB),净负收益。304 原始
关切(4xx 空转重试占 worker)已由本批「确定性 4xx/webp 不重试 + 瞬时退避」根治。留后续共识,不迁移。
### 34.4 验证记录(宿主 + qemu;0 新增真实请求——矩阵测试因设备不可达未执行)
| 项 | 方式 | 结果 |
|---|---|---|
| cover 链(含新 webp/403) | `cover_chain_sim`(驱动真实 cover.c + ncm_cache,HTTP 本地 stub;T1-T7 存量 + 新增 T8 webp 魔数 / T9 403) | **T1-T9 ALL PASS**(T8:webp 单次下载不重试+旧封面兜底+日志 `webp unsupported`;T9:403 单次下载不重试+日志含 403) |
| 缓存 LRU/隔离 | `cache_evict_sim` | **PASS** |
| 播放状态机 | `player_flow_sim` P1-P12 | **PASS** |
| 大歌单滑窗 | `window_sim` | **PASS** |
| 搜索 | `search_sim` | **PASS** |
| 边下边播 | `stream_sim` 全矩阵 | **PASS** |
| 降级链 | `degrade_sim`(含真机场景) | **ALL PASS** |
| 内存瘦身 | `mem_sim`(MEM_SIM=1) | **PASS** |
| core | `build_core.sh` → `test_ncm_core_host` + `qemu test_ncm_core_mipsel` | host + mipsel **PASS** |
| 构建 | `build_ncm.sh`(自有代码 -Wall -Wextra) | **0 warning**;`text 1019834 / data 1648 / bss 1362500`(bss 与 §33 持平,封面改动未涨静态内存) |
| 宿主构建 | `build_ncm_host.sh`(自有代码) | 0 warning(仅 SDK `meow_ui_vol.c` 存量 `system()` 未用结果告警,非本批) |
| UI 渲染 | `build_ncm_host.sh` + `NCM_PAGE=home/player/playlist MEOW_UI_DUMP` | exit=0,dump 正常 |
### 34.5 md5(本批产物)
```
a7c22bd4ee3bcd02fb24046d27a33242 ncm # 新(未部署,留主会话上卡)
f870b49b11ad8c0bb67cd47d2f6c2162 ncm_host # 宿主调试二进制(重链)
97bb2bdb44a2f446877787d9261b4f02 test/test_ncm_core_host # 宿主 core 测试(重编,内容未变)
```
改动文件(本批,仅这些):
```
修改:
app/logic/cover.c # 头对齐 CNMPlayer(Referer+UA) + webp 魔数/content-type 先判 + URL 尺寸参数 + 403/webp 分诊日志 + 兜底不盲重试(34.2)
app/ncm_app_internal.h # NCM_COVER_USE_PARAM / NCM_COVER_PARAM 共享宏(cover.c 与 sim 同源)
bench/cover_chain_sim.c # 修 sim 对齐现行 cover.c(ncm_http_download/meow_img_load_file/downscale 桩 + url_hash 参数规则)+ 新增 T8 webp / T9 403 用例
ncm # mipsel 产物(重链)
ncm_host # 宿主产物(重链)
```
(未动 sdk/、meow_launcherd/、其它 App、devdocs/;0 新增真实 CDN 请求;未部署——留主会话统一上卡。)
### 34.6 诚实出入 / 遗留
- **设备端 CDN 矩阵未执行**:设备 10.78.193.50 本批全程 `No route to host`(sleep/WiFi 休眠,宿主机
10.78.193.150 同网段 ARP 无应答,多次重试 + 后台 ping 监视 600s 均未恢复)。故③「200 解码失败是否
webp」未能在真机用 curl 矩阵直接证实(由 stb_image 无 webp 解码器 + magic/content-type 先判兜住);
UA 三变体(桌面浏览器 / iPhone NeteaseMusic 9.0.90·5038 / CNMPlayer 口径)与 `?param=300y300` 的
实际 CDN 出图效果未实测。**建议真机恢复后补跑**:取日志失败 URL,`curl` 单次比对 裸请求 /
`+Referer` / `+UA 变体` / `?param=300y300`,记 http code + content-type + 魔数(RIFF=webp / FFD8=jpeg),
据实微调 `NCM_COVER_UA` 与 `NCM_COVER_USE_PARAM`(宏已就位,改一处即生效——UA 在 cover.c 宏、
param 在 ncm_app_internal.h 宏)。
- **`?param=300y300` 潜在风险**:个别封面若仅存在全尺寸对象,param 版可能 404——已由确定性 4xx
不重试兜底(回退歌单封面),不劣于基线;但缓存键随之变化(旧全尺寸 `cov<rawhash>.jpg` 成为孤儿,
LRU 正常淘汰)。
- **`http→443` 潜性缺陷不修**(承 §33.3/§30.8 结论;现状封面 URL 均为 https,不受影响)。