/* logic/songmeta.c — 歌曲元数据本地缓存(REPORT §17 滑动窗口配套)。 * * 拉取成功的 song_detail 结果逐首序列化到 /meta/.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 #include #include #include #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; }