feat(ncm): 缓存分目录(songs//covers/)+ 陈旧 mp3 缓存修复
任务1 缓存分目录(用户定案):\n- ncm_cache_init mkdir songs/ 与 covers/ 子目录 + 一次性迁移\n (根混放 song_*/cov*/cover_current.jpg 挪进两子目录,幂等,legacy_migrated 计数)\n- ncm_cache_path 按 id 前缀路由(cov*→covers/,其余→songs/)\n- LRU(evict/total_bytes/7天 .part 回收)只扫 songs/;clear 清 songs/+covers/ 的 song_*\n- cover.c save_cover_raw 落 covers/cover_current.jpg\n\n任务2 陈旧 mp3 缓存修复(真机实锤:hires 档键下存 mp3,播放命中旧档):\n- song_local_file 缓存命中且用户档>EXHIGH(无损+)且实体是 .mp3 = 静默降级产物\n → 迁移重命名到 exhigh 键 song_<id>_exhigh.mp3(选极高仍可用;目标已存在删旧)\n → 本档视为未命中走下载(311 flac 门禁拿真无损/正确降级)\n\n验证:cache_evict_sim case10(目录迁移一次成型)+P1-9;player_flow_sim P13(迁移+miss)\n+P1-12 零回归;stream/window/search/degrade/cover_chain/test_ncm_core(host+qemu) 全 PASS;\nbuild_ncm.sh -Wall -Wextra 0 warning。0 真实请求。新 ncm md5 a90ba8c3 (text1028458/bss1364484)。
This commit is contained in:
1 parent
986e3814bc
commit
de18cef02c
11 files changed
+299
-64
No files matched your search
@@ -87,6 +87,11 @@ int appstate_init(void)
|
||||
else
|
||||
ncm_ctx_default_cache_dir(g_st.data_dir, g_st.cache_dir, sizeof g_st.cache_dir);
|
||||
|
||||
/* §NEW 缓存分目录一次性迁移:旧的 song_*、cov* 混放根 → songs/ 与 covers/(init 已搬完,此处只披露)。 */
|
||||
if (g_st.ctx.cache.legacy_migrated > 0)
|
||||
meow_log(MEOW_LOG_INFO, "ncm: cache legacy migration moved %d file(s) into songs/ and covers/",
|
||||
g_st.ctx.cache.legacy_migrated);
|
||||
|
||||
/* 风控加固 §1:接口级 JSON 响应短 TTL 缓存的盘层挂到 <data_dir>/ncm_api_cache/。
|
||||
* (内存层由 core 初始化默认启用;盘层跨进程/热更保留,启动清一次过期盘文件。) */
|
||||
ncm_apicache_init(g_st.data_dir);
|
||||
|
||||
@@ -277,13 +277,16 @@ static meow_img *download_cover(const char *url, ncm_cache_writer_t *w, size_t *
|
||||
|
||||
/* 封面原始文件路径写入(供 launcher 媒体卡片读;兼容保留)。调用方须已持有 g_cm。
|
||||
* 从 cache 写者临时文件把已下载字节复制到 cover_current.jpg(早前版本在此内再 lock g_cm
|
||||
* → 非递归锁自锁死——cover worker 永久卡死、封面永不出现)。 */
|
||||
* → 非递归锁自锁死——cover worker 永久卡死、封面永不出现)。
|
||||
* §NEW 缓存分目录:与歌曲分池,cover_current.jpg 落 covers/(封面条目 song_cov* 也在 covers/)。 */
|
||||
static void save_cover_raw(const char *path, size_t len)
|
||||
{
|
||||
if (!path || len == 0) return;
|
||||
if (g_st.cache_dir[0]) {
|
||||
const char *covdir = g_st.ctx.cache.covers_dir[0] ? g_st.ctx.cache.covers_dir
|
||||
: g_st.cache_dir;
|
||||
if (covdir[0]) {
|
||||
char dst[NCM_CACHE_DIR_MAX + 32];
|
||||
snprintf(dst, sizeof dst, "%s/cover_current.jpg", g_st.cache_dir);
|
||||
snprintf(dst, sizeof dst, "%s/cover_current.jpg", covdir);
|
||||
/* 文件复制(源=临时文件,目标=cover_current.jpg)。 */
|
||||
FILE *in = fopen(path, "rb");
|
||||
FILE *out = NULL;
|
||||
|
||||
@@ -486,21 +486,43 @@ static int song_local_file(const ncm_song_t *s, int idx, int seq, song_src_t *ss
|
||||
if (stat(np, &st) != 0) return NCM_ERR_NOENT;
|
||||
}
|
||||
}
|
||||
if (stream_src_open_static(&ss->src, path, (uint64_t)st.st_size) != 0)
|
||||
return NCM_ERR_IO;
|
||||
ss->total = (int64_t)st.st_size;
|
||||
*bytes = ss->total;
|
||||
/* 生效档推断(缓存键=用户档,降级产物归用户档键,无法精确还原):
|
||||
* mp3 实体 + 用户档 > 极高 → 必为降级产物,标 EXHIGH;其余按用户档显示。
|
||||
* (flac 实体视为与用户档一致;hires→lossless 档内降级会高估显示,已知取舍。) */
|
||||
const char *dot = strrchr(path, '.');
|
||||
int eff = g_level;
|
||||
if (dot && ncm_streq(dot, "mp3") && g_level > NCM_LEVEL_EXHIGH)
|
||||
eff = NCM_LEVEL_EXHIGH;
|
||||
pthread_mutex_lock(&g_play.mu);
|
||||
g_play.eff_level = eff;
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
return NCM_OK;
|
||||
/* §NEW 任务2:静默降级时代产物修复(用户实锤:hires 档键下存着 mp3,播放命中旧档)。
|
||||
* 用户档 > EXHIGH(无损/更高)且缓存实体是 .mp3 = flac 门禁前把 exhigh 的 mp3 写进了
|
||||
* 无损域键(当年降级产物归用户档键)。迁移重命名到 exhigh 键 song_<id>_exhigh.mp3
|
||||
* (选「极高」档时仍可用;目标已存在则删旧),然后本档视为未命中走下载——
|
||||
* 311 的 flac 门禁会拿到真无损或正确降级。放在 hit==2 迁移块之后、静态流打开之前。 */
|
||||
const char *mdot = strrchr(path, '.');
|
||||
if (g_level > NCM_LEVEL_EXHIGH && mdot && ncm_streq(mdot, ".mp3")) {
|
||||
char kh[64], ph[512];
|
||||
audio_cache_key(kh, sizeof kh, id, NCM_LEVEL_EXHIGH);
|
||||
if (ncm_cache_path(&g_st.ctx.cache, kh, "mp3", ph, sizeof ph) == NCM_OK) {
|
||||
struct stat hst;
|
||||
if (stat(ph, &hst) == 0) unlink(path); /* exhigh 目标已在:删旧(降级产物) */
|
||||
else rename(path, ph);
|
||||
meow_log(MEOW_LOG_INFO,
|
||||
"play: stale-degraded id=%s lv=%s entity=mp3 -> exhigh key; redownload",
|
||||
id, ncm_api_level_key(g_level));
|
||||
hit = 0; /* 本档不符 → 走下载(flac 门禁取真档) */
|
||||
}
|
||||
}
|
||||
if (hit) {
|
||||
if (stream_src_open_static(&ss->src, path, (uint64_t)st.st_size) != 0)
|
||||
return NCM_ERR_IO;
|
||||
ss->total = (int64_t)st.st_size;
|
||||
*bytes = ss->total;
|
||||
/* 生效档推断(缓存键=用户档,降级产物归用户档键,无法精确还原):
|
||||
* mp3 实体 + 用户档 > 极高 → 必为降级产物,标 EXHIGH;其余按用户档显示。
|
||||
* (flac 实体视为与用户档一致;hires→lossless 档内降级会高估显示,已知取舍。)
|
||||
* 注:mp3 + 用户档 > 极高 的组合已被上方迁移块拦截走下载,此处仅存防御。 */
|
||||
const char *dot = strrchr(path, '.');
|
||||
int eff = g_level;
|
||||
if (dot && ncm_streq(dot, ".mp3") && g_level > NCM_LEVEL_EXHIGH)
|
||||
eff = NCM_LEVEL_EXHIGH;
|
||||
pthread_mutex_lock(&g_play.mu);
|
||||
g_play.eff_level = eff;
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
return NCM_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* fee 门禁:VIP 才能听(1=VIP 歌曲 / 4=付费数字专辑)且非会员 → 不请求 url。
|
||||
|
||||
Reference in new issue
Block a user