- core/ncm_api.h: ncm_song_t 瘦身 id24/name128/art96/al96/pic256/dt int32 (1324B→608B) - appstate.c 滑动窗口: song_ids 全量 trackIds 索引(16384) + songs[256] 窗口感知 (song_valid 位图/window_base/滑窗 memmove 保留重叠), ensure_range 批网格(100)对齐 预取, songmeta.c 盘缓存 <cache_dir>/meta/<id>.bin (回滚/二次进入 0 网络请求) - playlist.c 触底分页退役→视口 ensure(±24 行), 占位行可直点(播放线程 fetch 物化), footer 改视口区间/总数 - 播放兼容: g_play.play_song 快照(mu), run_song 起播物化后全链路(下载/歌词/IPC META/ 播放页 UI)读快照——窗口滑出当前播放曲不崩; 边界 song_count→track_ids_count - 验证(0 真实请求): pagination_sim 全 PASS(5191 无截断/52 批); window_sim 6000 首 全程下滚 750 步视口全物化/song_detail 恰 60 次/回滚+0 请求/播放 fetch 不滑窗; core 宿主+qemu 全 PASS; mipsel -Wall -Wextra 0 warning, bss 2052068→1080900(-47%) - 未部署真机(设备音频线排查中, 部署留主会话)
69 lines
2.9 KiB
C
69 lines
2.9 KiB
C
/* logic/songmeta.c — 歌曲元数据本地缓存(REPORT §17 滑动窗口配套)。
|
|
*
|
|
* 拉取成功的 song_detail 结果逐首序列化到 <cache_dir>/meta/<id>.bin;滑动窗口补载时
|
|
* 先读盘(命中免 song_detail 请求),离线/二次进入歌单滚动全程零网络。
|
|
*
|
|
* 取舍(刻意从简):
|
|
* - 不进 ncm_cache LRU 池:meta 每首 ≈600B,一万首 ≈6MB,不参与 10GB 级 LRU 淘汰,
|
|
* 也不被 ncm_cache_clear(只删 song_* 顶层文件)误清——清音频缓存不丢浏览元数据。
|
|
* - 格式 = 头(magic/ver/size) + ncm_song_t 原始内存镜像:同机同构读写,跨架构/结构体
|
|
* 改版由 ver+size 失配判 miss(缓存可再生,最坏结果 = 回退 song_detail 网络拉取)。
|
|
*/
|
|
#include "../ncm_app_internal.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#define SONGMETA_MAGIC 0x4D4D434Eu /* "NCMM" */
|
|
#define SONGMETA_VER 1
|
|
|
|
typedef struct {
|
|
uint32_t magic;
|
|
uint16_t ver;
|
|
uint16_t size; /* sizeof(ncm_song_t),失配判 miss */
|
|
} songmeta_hdr_t;
|
|
|
|
static int songmeta_path(const char *dir, const char *id, char *out, size_t sz)
|
|
{
|
|
if (!dir || !dir[0] || !id || !id[0]) return -1;
|
|
int n = snprintf(out, sz, "%s/meta/%s.bin", dir, id);
|
|
return (n > 0 && (size_t)n < sz) ? 0 : -1;
|
|
}
|
|
|
|
int songmeta_load(const char *cache_dir, const char *song_id, ncm_song_t *out)
|
|
{
|
|
char path[NCM_CACHE_DIR_MAX + 48];
|
|
if (songmeta_path(cache_dir, song_id, path, sizeof path) != 0) return NCM_ERR_NOENT;
|
|
FILE *f = fopen(path, "rb");
|
|
if (!f) return NCM_ERR_NOENT;
|
|
songmeta_hdr_t h;
|
|
int ok = (fread(&h, 1, sizeof h, f) == sizeof h) &&
|
|
h.magic == SONGMETA_MAGIC && h.ver == SONGMETA_VER &&
|
|
h.size == sizeof(ncm_song_t) &&
|
|
(fread(out, 1, sizeof(ncm_song_t), f) == sizeof(ncm_song_t));
|
|
fclose(f);
|
|
return ok ? NCM_OK : NCM_ERR_NOENT;
|
|
}
|
|
|
|
int songmeta_save(const char *cache_dir, const char *song_id, const ncm_song_t *s)
|
|
{
|
|
if (!cache_dir || !cache_dir[0] || !song_id || !song_id[0] || !s) return NCM_ERR_ARG;
|
|
char dir[NCM_CACHE_DIR_MAX + 16];
|
|
int n = snprintf(dir, sizeof dir, "%s/meta", cache_dir);
|
|
if (n <= 0 || (size_t)n >= sizeof dir) return NCM_ERR_ARG;
|
|
mkdir(cache_dir, 0755); /* 幂等(缓存目录通常已由 ncm_cache_init 建好) */
|
|
mkdir(dir, 0755);
|
|
|
|
char path[NCM_CACHE_DIR_MAX + 48];
|
|
if (songmeta_path(cache_dir, song_id, path, sizeof path) != 0) return NCM_ERR_ARG;
|
|
FILE *f = fopen(path, "wb");
|
|
if (!f) return NCM_ERR_IO;
|
|
songmeta_hdr_t h = { SONGMETA_MAGIC, SONGMETA_VER, (uint16_t)sizeof(ncm_song_t) };
|
|
int ok = (fwrite(&h, 1, sizeof h, f) == sizeof h) &&
|
|
(fwrite(s, 1, sizeof(ncm_song_t), f) == sizeof(ncm_song_t));
|
|
fclose(f);
|
|
if (!ok) remove(path); /* 半截文件不留(下次判 miss 重拉) */
|
|
return ok ? NCM_OK : NCM_ERR_IO;
|
|
}
|