fix(media): 详情页标语只显示简体中文,简中没有时不显示

- adapters 新增 simplifiedChineseTagline:取值语言为繁中 / 非中文、或文字是繁体特有字时不显示;原语言为中文(zh / cn)的作品按简中处理
- TMDB 富详情带回 taglineLanguage;发现页详情的 tagline 按上述口径给出
- 媒体库作品详情新增 displayTagline(存储的标语语言为 null,只能按文字判断);metadata.tagline 存储不变
- 补 adapters 单测与作品详情接口测试

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-07 01:32:00 +08:00
1 parent 895c039a40
commit 6549ab92c3
9 files changed
+124 -2

No files matched your search

@@ -1,4 +1,5 @@
import { join } from "node:path";
import { simplifiedChineseTagline } from "@umt/adapters";
import {
type AcquisitionPlan,
type AcquisitionSlot,
@@ -405,6 +406,7 @@ export function registerLibraryRoutes(app: ModuleApp, d: AoDeps) {
finishedAt: r.finished_at ? new Date(r.finished_at).toISOString() : null,
})),
scrape: await latestEntityScrape(d, id),
displayTagline: heroTagline(e!.metadata),
};
return detail;
},
@@ -642,3 +644,11 @@ export function registerLibraryRoutes(app: ModuleApp, d: AoDeps) {
{ tag: TAG },
);
}
/** 详情页头部的标语:只显示简体中文(见 simplifiedChineseTagline);存储的标语与语言不变 */
function heroTagline(metadata: unknown): string | null {
const m = (metadata ?? {}) as Record<string, { value?: unknown; language?: string | null } | undefined>;
const text = typeof m.tagline?.value === "string" ? m.tagline.value : null;
const orig = typeof m.originalLanguage?.value === "string" ? m.originalLanguage.value : null;
return simplifiedChineseTagline(text, m.tagline?.language ?? null, orig);
}
+2 -1
View File
@@ -3,6 +3,7 @@ import {
fetchRichDetail,
fetchRichSeason,
type MetadataCandidate,
simplifiedChineseTagline,
TmdbError,
type TmdbRichDetail,
} from "@umt/adapters";
@@ -453,7 +454,7 @@ export function registerSearchRoutes(app: ModuleApp, ctx: ServerContext, d: Sear
out.title = rich.title || out.title;
out.overview = rich.overview ?? out.overview;
out.titleZh = rich.titleZh;
out.tagline = rich.tagline;
out.tagline = simplifiedChineseTagline(rich.tagline, rich.taglineLanguage, rich.originalLanguage);
out.genres = rich.genres;
out.voteAverage = rich.voteAverage;
out.voteCount = rich.voteCount;
@@ -336,4 +336,32 @@ describe.skipIf(!hasPg)("LIBV-1 入库版本", () => {
expect(response.body.data.downloads).toHaveLength(105);
expect(response.body.data.droppedPlans).toHaveLength(51);
});
it("作品详情头部标语只给简体中文(displayTagline);存储的 metadata.tagline 不变", async () => {
const field = (value: unknown, language: string | null = null) => ({
value,
source: "tmdb",
language,
at: "2026-10-01T00:00:00.000Z",
locked: false,
});
const cases: [string, string, string | null][] = [
["我们注定要离开地球", "en", "我们注定要离开地球"],
["我們註定要離開地球", "en", null],
["Mankind was born on Earth.", "en", null],
["我們註定要離開地球", "zh", "我們註定要離開地球"],
];
for (const [tagline, orig, want] of cases) {
const entityId = await seedEntity(h.db, { kind: "movie", title: `标语 ${tagline}`, year: 2026 });
await h.db
.updateTable("media_entity")
.set({ metadata: JSON.stringify({ tagline: field(tagline), originalLanguage: field(orig) }) })
.where("id", "=", entityId)
.execute();
const r = await h.api("GET", `/library/${entityId}`);
expect(r.status).toBe(200);
expect(r.body.data.displayTagline).toBe(want);
expect(r.body.data.entity.metadata.tagline.value).toBe(tagline);
}
});
});
+2 -1
View File
@@ -1091,7 +1091,8 @@ function factsOf(d: LibraryDetail, entity: MediaEntityDetail | undefined): Media
platform: strOf(m("platform")),
originCountry: Array.isArray(country) ? country.filter((x): x is string => typeof x === "string") : [],
overview: strOf(m("overview")),
tagline: strOf(m("tagline")),
// 头部标语只显示简体中文,由服务端按取值语言 / 文字判断(存储的 metadata.tagline 不变)
tagline: d.displayTagline ?? null,
posterPath: posterPathOf(e.metadata),
backdropPath: strOf(m("backdropPath")),
externalLinks: externalLinks({