feat(metadata): overview falls back to Chinese variants only (MD-2)

- adapters: overviewLanguages() limits overview/tagline chain to Chinese
  variants (zh-CN→zh-SG→zh-TW→zh-HK); titles keep full chain; season
  supplement fetch skips languages useless for overview
- setting metadata.overviewNonChineseFallback (default off) + UI switch
- media detail shows "暂无简介" for empty entity/episode overview
- scripts/overview-recompute.ts: recompute stored non-Chinese overviews
  from kv_cache only (no TMDB requests), dry-run by default, --apply
- tests: chain unit tests, Heart Signal (103147) fixture, server
  recompute test, MP differential for episode overview
- runbook + CHANGELOG

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-30 18:04:23 +08:00
1 parent 081db4651c
commit f9901b514b
22 files changed
+887 -38

No files matched your search

+12 -2
View File
@@ -123,6 +123,17 @@ export interface TmdbSharedCache {
set(key: string, value: unknown, ttlMs: number): Promise<void>;
}
function requestQuery(params: Record<string, string | undefined>): URLSearchParams {
const qs = new URLSearchParams();
for (const k of Object.keys(params).sort()) if (params[k] !== undefined) qs.set(k, params[k]!);
return qs;
}
/** 请求缓存键 `path?query`(参数按名排序、不含凭证);只读缓存的离线重算(MD-2)据此找已缓存的响应 */
export function tmdbRequestKey(path: string, params: Record<string, string | undefined> = {}): string {
return `${path}?${requestQuery(params)}`;
}
/** 空结果(搜索 0 条)用短 TTL(对照 MP EMPTY_RESULT_CACHE_TTL) */
const isEmptyResult = (v: unknown) =>
Array.isArray((v as Json | null)?.results) && ((v as Json).results as unknown[]).length === 0;
@@ -163,8 +174,7 @@ export class TmdbClient {
}
private async get<T>(path: string, params: Record<string, string | undefined>): Promise<T> {
const qs = new URLSearchParams();
for (const k of Object.keys(params).sort()) if (params[k] !== undefined) qs.set(k, params[k]!);
const qs = requestQuery(params);
const key = `${path}?${qs}`;
const emptyTtl = this.cfg.cache?.emptyTtlMs ?? 30 * 60_000;
const baseTtl = this.cfg.cache?.ttlMs ?? 24 * 3600_000;