Files
MeowOS 997f89f879 ncm 三期三批: 歌曲对象瘦身(1324→608B)+滑动窗口(NCM_SONG_WINDOW=256, 取消1000截断支撑任意长度歌单)+播放快照解耦 — agent-251
- 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%)
- 未部署真机(设备音频线排查中, 部署留主会话)
2026-09-01 04:03:01 +08:00

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;
}