Files
UHD_Media_Tool/packages/adapters/test/differential/tmdb-overview.test.ts
T
rootandClaude Opus 5.5 f9901b514b 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]>
2026-09-30 18:04:23 +08:00

77 lines
3.5 KiB
TypeScript

/**
* 每集简介差分(MD-2):MP `TmdbApi.get_tv_season_detail`(e6820a8,app/modules/themoviedb/tmdbapi.py)只按配置语言
* (TMDB_LOCALE = zh)请求季详情,episodes[].overview 原样使用,为空就为空;本项目 `fetchRichSeason` 按语言链补拉。
* 响应按《心动的信号》(TMDB tv/103147)各语言季详情的真实形态裁剪(每期标题有 zh-CN,简介只有 en-US)。
* 期望:简介与 MP 一致(中文变体都没有 → 空);标题允许按链回退(登记偏离 intentional:MD-1 占位集名按链补标题)。
*/
import { describe, expect, it } from "vitest";
import { fetchRichSeason } from "../../src/tmdb/index.ts";
type Ep = { episode_number: number; air_date: string; name: string; overview: string };
const season = (episodes: Ep[]) => ({
season_number: 1,
name: "第 1 季",
overview: "",
air_date: "2018-08-01",
episodes,
});
/** 各语言的 /tv/103147/season/1 响应(裁剪) */
const RECORDED: Record<string, ReturnType<typeof season>> = {
"zh-CN": season([
{ episode_number: 1, air_date: "2018-08-01", name: "心动不如行动,大胆亮出底牌!", overview: "" },
{ episode_number: 2, air_date: "2018-08-08", name: "第 2 集", overview: "" },
]),
"zh-SG": season([
{ episode_number: 1, air_date: "2018-08-01", name: "第 1 集", overview: "" },
{ episode_number: 2, air_date: "2018-08-08", name: "第 2 集", overview: "" },
]),
"zh-TW": season([
{ episode_number: 1, air_date: "2018-08-01", name: "第 1 集", overview: "" },
{ episode_number: 2, air_date: "2018-08-08", name: "第 2 集", overview: "" },
]),
"zh-HK": season([
{ episode_number: 1, air_date: "2018-08-01", name: "第 1 集", overview: "" },
{ episode_number: 2, air_date: "2018-08-08", name: "第 2 集", overview: "" },
]),
"en-US": season([
{
episode_number: 1,
air_date: "2018-08-01",
name: "Episode 1",
overview: "If Your Heart Skips a Beat, Play Your Cards Openly!",
},
{ episode_number: 2, air_date: "2018-08-08", name: "Who Is the One?", overview: "Who Is the One?" },
]),
};
const client = {
getJson: <T>(_path: string, params: Record<string, string | undefined> = {}) =>
Promise.resolve(RECORDED[params.language ?? "en-US"] as T),
};
/** MP 侧:只取配置语言(zh-CN)的季详情,集名 / 简介原样(空串 = 没有) */
function mpEpisodes(lang = "zh-CN") {
return RECORDED[lang]!.episodes.map((e) => ({ ep: e.episode_number, name: e.name, overview: e.overview || null }));
}
const CHAIN = ["zh-CN", "zh-SG", "zh-TW", "zh-HK", "en-US"];
const DEVIATIONS: Record<string, { kind: "intentional"; note: string }> = {
"E2.name": { kind: "intentional", note: "MD-1:zh-CN 是占位名「第 2 集」时按链补标题(可到 en-US)" },
};
describe("每集简介差分(MP get_tv_season_detail ↔ fetchRichSeason)", () => {
it("简介与 MP 一致:中文变体都没有时为空,不回退英文;标题偏离已登记", async () => {
const ours = await fetchRichSeason(client, 103147, 1, CHAIN, { now: new Date("2026-01-01") });
const mismatches: string[] = [];
for (const mp of mpEpisodes()) {
const e = ours.episodes.find((x) => x.episodeNumber === mp.ep)!;
if (e.overview !== mp.overview) mismatches.push(`E${mp.ep}.overview`);
if (e.name !== mp.name) mismatches.push(`E${mp.ep}.name`);
}
expect(mismatches.filter((m) => !DEVIATIONS[m])).toEqual([]);
expect(Object.keys(DEVIATIONS).every((k) => mismatches.includes(k))).toBe(true);
expect(ours.episodes.map((e) => e.overview)).toEqual([null, null]);
});
});