feat(ncm): 三期第一批 — 歌单真分页(取消256截断)+专辑封面ncm_cache+歌词后台加载补锁
[agent-239] 仅改 patch_v2/ncm_app/。 1. 歌单分页(REPORT §14.1): track/all 全 404(§13.1) 弃用, 回 detail(n=1)全量 trackIds + song_detail 分批(≤NCM_SONG_BATCH=100) 触底逐页加载。appstate_load_playlist_songs_page offset=0 重建 song_ids[]/track_ids_count 载第0页, offset>0 追加(失败保留已载); playlist.c g_pl_more=(song_count<track_ids_count && <NCM_SONG_MAX), 触底 g_ploffset=song_count 起下一页(保留滚动), 首屏失败整页空态+点按重试, 追加失败 snack+保留已载。NCM_SONG_MAX 256→1000(单页渲染上限, bss +~1MB), NCM_SONG_BATCH 64→100, 删 NCM_PLAYLIST_PAGE(track/all 遗留)。 2. 专辑封面(§14.2): cover.c 封面改走 ncm_cache(LRU, 键=djb2(url)→song_cov<h>.jpg 同池), 缓存命中免下载/未命中下载→open/write/commit 落盘, 仍写 cover_current.jpg 供 launcher; 顺带修真 bug: save_cover_raw 在已持 g_cm 临界区内再 lock(非递归死锁)→封面永不刷新, 改调用方持锁。 歌单列表/详情头块封面保持现状。 3. 歌词后台加载(§14.3): lyric_worker 已异步(§11)未改线程模型; 补 lyric.c g_lyric_mu 数据竞争锁, 写侧 lyric_parse 整段持锁(清空用 lyric_clear_nolock 免二次取锁), 读侧 lyric_count/line_at/ line_text/line_tl 逐次取锁, 消除 UI/IPC 与 worker 间数据竞争(原 §10.9 FM4 良性竞态)。 验证(0 真实 NCM 请求): bench/pagination_sim.c 离线 stub 驱动真实 appstate 分页函数全 PASS (130首读全程/5191首封顶1000/空/越界无操作); mipsel build_ncm.sh 0 warning(text 842820/bss 2052068); 宿主 build_ncm_host.sh+MEOW_UI_DUMP home/playlist/player/lands 渲染正常无崩溃; host SDK_SRC 补 meow_pinyin(拼音IME py_predict 漏编致链接断)。ncm md5 b34bc1ed, ncm_host md5 1a2327e0。 未动 sdk/(主会话改 meow_ui_ime.c)/meow_launcherd/meow_amsd/vendor/core; 未 commit 它们。
This commit is contained in:
1 parent
5fac5e380d
commit
ee104f1b18
10 files changed
+471
-87
No files matched your search
@@ -126,34 +126,44 @@ int appstate_load_playlist_songs_page(int idx, int offset, int limit)
|
||||
if (limit <= 0 || limit > NCM_SONG_BATCH) limit = NCM_SONG_BATCH;
|
||||
if (offset < 0) offset = 0;
|
||||
|
||||
/* 二期曾改走 /api/playlist/track/all 服务端分页(REPORT §12.3),但真机实测该端点
|
||||
* (eapi/weapi、v6/无v6 四种路由)全部返回 code 404「接口未找到」——NCM 服务端无此路由,
|
||||
* 故回退到 REPORT §10.11 已验证的 /api/v6/playlist/detail(trackIds) + /api/v3/song/detail 分批。
|
||||
* detail 一次取回前 NCM_SONG_MAX 首 trackIds;song_detail 分批取歌曲对象(含 al.picUrl)。
|
||||
* offset==0 重头清空并整批载入;offset>0(触底“加页”)时 detail 已一次取满,无独立分页,
|
||||
* 直接返回 NCM_OK(歌单数 ≤NCM_SONG_MAX 时已全部在列,song_count 不变)。 */
|
||||
if (offset != 0) return NCM_OK;
|
||||
/* REPORT §13.1:/api/playlist/track/all 服务端分页端点真机全 404(NCM 无此路由),弃用。
|
||||
* 回到 §10.11 已验证路径并做**真分页**:detail(n=1)取【全量】trackIds 入 song_ids[],
|
||||
* song_detail 按 ≤NCM_SONG_BATCH 分批拉歌曲对象(含 al.picUrl),offset 为「下一页」起始;
|
||||
* 列表触底驱动 offset 前进,song_count 逐页累加(不再一次性拉满,大歌单不冻 UI,
|
||||
* 也不把「第 N 页之后的歌」挡在 256 之外——NCM_SONG_MAX 上限提升到 1000,见 §14)。 */
|
||||
|
||||
memset(g_st.song_ids, 0, sizeof g_st.song_ids);
|
||||
int idn = 0;
|
||||
int rc = ncm_api_playlist_detail(g_st.ctx.device_id, g_st.ctx.cookie, pl->id,
|
||||
NULL, 0, NULL, 0,
|
||||
g_st.song_ids, NCM_SONG_MAX, &idn);
|
||||
if (rc != NCM_OK) { g_st.song_count = 0; return rc; }
|
||||
|
||||
memset(g_st.songs, 0, sizeof g_st.songs);
|
||||
int outn = 0;
|
||||
for (int off = 0; off < idn; off += NCM_SONG_BATCH) {
|
||||
int batch = idn - off;
|
||||
if (batch > NCM_SONG_BATCH) batch = NCM_SONG_BATCH;
|
||||
int bn = 0;
|
||||
rc = ncm_api_song_detail(g_st.ctx.device_id, g_st.ctx.cookie, &g_st.song_ids[off],
|
||||
batch, &g_st.songs[off], NCM_SONG_MAX - off, &bn);
|
||||
if (rc != NCM_OK) break;
|
||||
outn += bn;
|
||||
if (bn < batch) break;
|
||||
if (offset == 0) {
|
||||
/* 重头:重建 trackIds 索引 + 清空已物化歌曲(song_count 无论成败都归零)。 */
|
||||
memset(g_st.song_ids, 0, sizeof g_st.song_ids);
|
||||
memset(g_st.songs, 0, sizeof g_st.songs);
|
||||
g_st.song_count = 0;
|
||||
g_st.track_ids_count = 0;
|
||||
int idn = 0;
|
||||
int rc = ncm_api_playlist_detail(g_st.ctx.device_id, g_st.ctx.cookie, pl->id,
|
||||
NULL, 0, NULL, 0,
|
||||
g_st.song_ids, NCM_SONG_MAX, &idn);
|
||||
if (rc != NCM_OK) return rc;
|
||||
g_st.track_ids_count = idn;
|
||||
/* 首屏只需第 0 页:song_detail 只拉前 limit 首,其余待滚动触底再拉(分页驱动)。 */
|
||||
} else if (g_st.track_ids_count == 0) {
|
||||
/* 防御:offset>0 但没有索引(不应发生;主路径先走 offset=0)。 */
|
||||
return NCM_ERR_ARG;
|
||||
}
|
||||
g_st.song_count = outn;
|
||||
|
||||
if (offset >= g_st.track_ids_count) return NCM_OK; /* 已到尾部/上限 */
|
||||
if (offset >= NCM_SONG_MAX) return NCM_OK; /* 内存上限:不再物化更多 */
|
||||
|
||||
int can = g_st.track_ids_count - offset;
|
||||
int room = NCM_SONG_MAX - offset;
|
||||
if (can > limit) can = limit; /* 每页 ≤ limit(保守 100) */
|
||||
if (can > room) can = room; /* 不越界 song_ids[]/songs[] */
|
||||
|
||||
int bn = 0;
|
||||
int rc = ncm_api_song_detail(g_st.ctx.device_id, g_st.ctx.cookie, &g_st.song_ids[offset],
|
||||
can, &g_st.songs[offset], NCM_SONG_MAX - offset, &bn);
|
||||
if (rc != NCM_OK) return rc; /* 失败:song_count 不变(保留已载页) */
|
||||
|
||||
g_st.song_count = offset + bn; /* 成功:累加本页 */
|
||||
appstate_apply_playlist_cover(idx);
|
||||
return NCM_OK;
|
||||
}
|
||||
@@ -161,15 +171,16 @@ int appstate_load_playlist_songs_page(int idx, int offset, int limit)
|
||||
int appstate_load_playlist_songs(int idx)
|
||||
{
|
||||
/* 兼容入口:整批载入(内部按页循环到 NCM_SONG_MAX 或歌单尾部)。 */
|
||||
int rc, loaded = 0;
|
||||
do {
|
||||
rc = appstate_load_playlist_songs_page(idx, loaded, NCM_PLAYLIST_PAGE);
|
||||
int rc = appstate_load_playlist_songs_page(idx, 0, NCM_SONG_BATCH);
|
||||
if (rc != NCM_OK) return rc;
|
||||
int off = g_st.song_count;
|
||||
while (off < g_st.track_ids_count && off < NCM_SONG_MAX) {
|
||||
rc = appstate_load_playlist_songs_page(idx, off, NCM_SONG_BATCH);
|
||||
if (rc != NCM_OK) return rc;
|
||||
int before = loaded;
|
||||
loaded = g_st.song_count;
|
||||
if (loaded - before < NCM_PLAYLIST_PAGE) break; /* 尾部:已载完 */
|
||||
if (loaded >= NCM_SONG_MAX) break;
|
||||
} while (1);
|
||||
int nxt = g_st.song_count;
|
||||
if (nxt <= off) break; /* 无进展:防死循环 */
|
||||
off = nxt;
|
||||
}
|
||||
return NCM_OK;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user