fix(ncm §33): 流媒专项批六项——流式SIGSEGV防御日志/档位保真(无损级必须flac+显示档按实际)/封面4xx不重试/收藏显隐/缓存命名迁移/topbar位图图标

- §2① ncm_api.c level_attempt: 无损级(hires/lossless及以上)命中必须 type=flac,返回 mp3 视为该档不可用续降
  (修「hires 档静默给 mp3 被当命中」)
- §2② player_manager.c eff_level_for_resp: 显示档按响应实际格式/码率——mp3 绝不显示 Hi-Res(≤128k→标准, 否则极高),
  flac 采信链 eff; br 未知钳 ≤EXHIGH
- §1 流式 SIGSEGV(《十面埋伏》sig=11) 审计+UBSAN 探针未复现 → 落地防御日志(dl start/done/interrupt + play done fin/exp/fail/underrun)
- §3 cover.c: download_cover 回填 rc/status; 确定性 4xx(404等)不重试立即跳下一张, 仅瞬时错误退避(rc=%d status=%d 日志)
- §4 playlist.c: pl_show_fav 我创建(is_collected==0)隐藏收藏钮, 我收藏保留, 搜索歌单保守显示(评论槽并入无空洞)
- §5 ncm_cache.c 双点→单点迁移 + player_manager song_local_file 旧键→当前档位键(不重下)
- §6 home.c: topbar 动作 ⚙↻⌕ 迁移 sb_icons 位图(meow_topappbar_set_item_icon, 修 4k 字库豆腐)
- 测试: test_ncm_core 新 test_grade_flac_gate + test_cache 双点迁移断言; degrade_sim/player_flow_sim 桩 flac 化
- 验证: stream/player_flow/window/search/cache_evict/degrade sim 全 PASS + test_ncm_core host+mipsel(qemu) + mem_sim + build_ncm.sh 0 warning
- 新 ncm md5 cb818786 (text 1017110/bss 1362500), 未部署留主会话; 0 真实 NCM 业务请求
This commit is contained in:
AskaEth committed 2026-09-02 04:20:46 +08:00
1 parent 68ce46a5d8
commit 44a790dc1d
13 files changed
+425 -24

No files matched your search

+7 -1
View File
@@ -329,11 +329,17 @@ static int level_attempt(const char *device_id, const char *cookie, const char *
if (rc != NCM_OK) return 0;
int ok = ncm_streq(p.type, "mp3") || ncm_streq(p.type, "flac");
if (!p.type[0]) {
ok = 1;
const char *dot = strrchr(p.url, '.');
if (dot && ncm_streq(dot, ".flac")) ncm_strlcpy(p.type, "flac", sizeof p.type);
else ncm_strlcpy(p.type, "mp3", sizeof p.type); /* 默认 mp3(原语义),供格式门槛判定 */
ok = 1;
}
if (!(p.url[0] && ok)) return 0; /* url 为 null 或未知格式:继续降级 */
/* §档位保真(REPORT §NEW 任务2①):SQ 及以上(lossless/hires/母带/全景声)
* 命中判定必须 type=flac——无损/Hi-Res 等档返回 mp3 一律视为「该档不可用」继续降级
* (服务端静默降档场景,修「hires 档静默给 mp3 被当命中」);mp3 只在
* <lossless(standard/higher/exhigh)接受(mp3 码率上限即极高,不可能达无损档)。 */
if (lv >= NCM_LEVEL_LOSSLESS && ncm_streq(p.type, "mp3")) return 0;
*out = p;
if (out_level) {
int rl = level_from_str(p.level);
+16 -2
View File
@@ -88,12 +88,26 @@ int ncm_cache_lookup(const ncm_cache_t *c, const char *id, const char *ext,
struct stat st;
if (stat(path, &st) != 0 || !S_ISREG(st.st_mode)) {
/* §19 温和兼容:历史双点名 `song_<id>..<ext>`(旧版 ext 带前导点所致)。
* 命中照常刷新 LRU;不主动改名/删除,由 LRU 自然淘汰。 */
* REPORT §NEW 任务5:命中即**迁移重命名**到单点名(同 id+ext,不重下)——
* 之后本条目走单点主路径,双点存量自然消亡(lookup/evict 定期见到)。 */
const char *e = ext;
if (e && e[0] == '.') e++;
int n = snprintf(path, sizeof path, "%s/song_%s..%s", c->dir, id, e ? e : "bin");
const char *ee = e ? e : "bin";
int n = snprintf(path, sizeof path, "%s/song_%s..%s", c->dir, id, ee);
if (n < 0 || (size_t)n >= sizeof path) return NCM_ERR_FULL;
if (stat(path, &st) != 0 || !S_ISREG(st.st_mode)) return NCM_ERR_NOENT;
char fixed[NCM_CACHE_DIR_MAX + 32];
if (ncm_cache_path(c, id, ee, fixed, sizeof fixed) == NCM_OK) {
struct stat fst;
if (stat(fixed, &fst) == 0) unlink(path); /* 目标已(在/被并发建):删旧双点 */
else rename(path, fixed);
if (stat(fixed, &st) == 0 && S_ISREG(st.st_mode)) {
ncm_strlcpy(path, fixed, sizeof path);
} else {
ncm_strlcpy(path, fixed, sizeof path); /* 迁移失败(权限等):回到固定路径 */
return NCM_ERR_NOENT;
}
}
}
utime(path, NULL); /* 刷新最后访问(按 mtime LRU) */
ncm_strlcpy(out, path, out_sz);