Merge branch 'md1'
# Conflicts: # packages/contracts/src/settings.ts
This commit is contained in:
commit
b633eb5379
30 files changed
+3240
-222
No files matched your search
@@ -5,14 +5,39 @@
|
||||
export interface TmdbFixtureEpisode {
|
||||
episode_number: number;
|
||||
air_date: string | null;
|
||||
/** 按语言的集名;请求语言没有时 mock 返回 TMDB 式占位名("第 N 集" / "Episode N") */
|
||||
names: Record<string, string>;
|
||||
/** MD-1:按语言的简介;没有时返回空串 */
|
||||
overviews?: Record<string, string>;
|
||||
runtime?: number | null;
|
||||
still_path?: string | null;
|
||||
vote_average?: number;
|
||||
vote_count?: number;
|
||||
}
|
||||
export interface TmdbFixtureSeason {
|
||||
season_number: number;
|
||||
air_date: string | null;
|
||||
names: Record<string, string>;
|
||||
overviews?: Record<string, string>;
|
||||
poster_path?: string | null;
|
||||
episodes: TmdbFixtureEpisode[];
|
||||
}
|
||||
/** MD-1 富详情字段(credits / 类型 / 评分 / 状态…);缺省不返回 */
|
||||
export interface TmdbFixtureRich {
|
||||
genres?: string[];
|
||||
vote_average?: number;
|
||||
vote_count?: number;
|
||||
status?: string;
|
||||
runtime?: number;
|
||||
episode_run_time?: number[];
|
||||
last_air_date?: string;
|
||||
backdrop_path?: string;
|
||||
networks?: string[];
|
||||
tagline?: Record<string, string>;
|
||||
cast?: { id: number; name: string; character: string; profile_path: string | null }[];
|
||||
crew?: { id: number; name: string; job: string; profile_path: string | null }[];
|
||||
created_by?: { id: number; name: string; profile_path: string | null }[];
|
||||
}
|
||||
export interface TmdbFixture {
|
||||
type: "movie" | "tv";
|
||||
id: number;
|
||||
@@ -25,8 +50,9 @@ export interface TmdbFixture {
|
||||
popularity: number;
|
||||
alternative_titles: { iso_3166_1: string; title: string; type: string }[];
|
||||
external_ids: { imdb_id?: string | null; tvdb_id?: number | null };
|
||||
/** 额外译名(titles 中的语言会自动生成 translations) */
|
||||
extra_translations?: { iso_639_1: string; iso_3166_1: string; title: string }[];
|
||||
/** 额外译名(titles 中的语言会自动生成 translations);title 为空串表示该语言只有简介没有译名(TMDB 常见) */
|
||||
extra_translations?: { iso_639_1: string; iso_3166_1: string; title: string; overview?: string }[];
|
||||
rich?: TmdbFixtureRich;
|
||||
seasons?: TmdbFixtureSeason[];
|
||||
/** 海报路径(拼 image.tmdb.org);缺省 null */
|
||||
poster_path?: string | null;
|
||||
@@ -219,4 +245,86 @@ export const TMDB_FIXTURES: TmdbFixture[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
// MD-1 语言回退链:zh-CN 译名为空(只有简介)、CN 地区别名是简中名、zh-TW / zh-HK 有译名(对照 TMDB tv/95396 真实形态)
|
||||
{
|
||||
type: "tv",
|
||||
id: 95396,
|
||||
original_title: "Severance",
|
||||
original_language: "en",
|
||||
origin_country: ["US"],
|
||||
date: "2022-02-17",
|
||||
titles: { "en-US": "Severance", "zh-TW": "人生切割術", "zh-HK": "生活割離術" },
|
||||
overviews: { "en-US": "Mark leads a team of office workers.", "zh-TW": "馬克帶領的辦公團隊成員全都接受手術。" },
|
||||
popularity: 95.1,
|
||||
alternative_titles: [
|
||||
{ iso_3166_1: "CN", title: "人生切割术", type: "" },
|
||||
{ iso_3166_1: "HK", title: "生活割離術", type: "" },
|
||||
],
|
||||
external_ids: { imdb_id: "tt11280740", tvdb_id: 371980 },
|
||||
extra_translations: [
|
||||
{ iso_639_1: "zh", iso_3166_1: "CN", title: "", overview: "故事发生在一家名为卢蒙的超级大公司内。" },
|
||||
{ iso_639_1: "zh", iso_3166_1: "SG", title: "", overview: "马克领导着一个办公室里的团队。" },
|
||||
],
|
||||
poster_path: "/severance.jpg",
|
||||
rich: {
|
||||
genres: ["剧情", "悬疑", "Sci-Fi & Fantasy"],
|
||||
vote_average: 8.395,
|
||||
vote_count: 2400,
|
||||
status: "Returning Series",
|
||||
episode_run_time: [],
|
||||
last_air_date: "2025-03-21",
|
||||
backdrop_path: "/severance-bg.jpg",
|
||||
networks: ["Apple TV"],
|
||||
cast: [
|
||||
{ id: 4495, name: "亚当·斯科特", character: "Mark Scout", profile_path: "/adam.jpg" },
|
||||
{ id: 1319469, name: "布丽特·洛薇尔", character: "Helly Riggs", profile_path: null },
|
||||
],
|
||||
created_by: [{ id: 3442409, name: "丹·埃里克森", profile_path: "/dan.jpg" }],
|
||||
},
|
||||
seasons: [
|
||||
{
|
||||
season_number: 1,
|
||||
air_date: "2022-02-17",
|
||||
names: { "zh-CN": "第 1 季", "en-US": "Season 1" },
|
||||
overviews: { "zh-CN": "在卢蒙工业公司,员工们接受了一种名为“分离”的手术。" },
|
||||
poster_path: "/sev-s1.jpg",
|
||||
episodes: [
|
||||
{
|
||||
episode_number: 1,
|
||||
air_date: "2022-02-17",
|
||||
names: { "zh-CN": "有关地狱的好消息", "en-US": "Good News About Hell" },
|
||||
overviews: { "zh-CN": "马克升职成为部门主管。", "en-US": "Mark is promoted." },
|
||||
runtime: 59,
|
||||
still_path: "/sev-101.jpg",
|
||||
vote_average: 8.2,
|
||||
vote_count: 171,
|
||||
},
|
||||
{
|
||||
episode_number: 2,
|
||||
air_date: "2022-02-17",
|
||||
names: { "zh-TW": "半循環", "en-US": "Half Loop" },
|
||||
overviews: { "zh-TW": "馬克帶領新員工參觀。", "en-US": "Mark gives Helly a tour." },
|
||||
runtime: 53,
|
||||
still_path: "/sev-102.jpg",
|
||||
vote_average: 8.0,
|
||||
vote_count: 150,
|
||||
},
|
||||
{
|
||||
episode_number: 3,
|
||||
air_date: "2022-02-25",
|
||||
names: { "en-US": "In Perpetuity" },
|
||||
overviews: {},
|
||||
runtime: 52,
|
||||
still_path: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
season_number: 2,
|
||||
air_date: "2099-01-17",
|
||||
names: { "zh-CN": "第 2 季", "en-US": "Season 2" },
|
||||
episodes: [{ episode_number: 1, air_date: "2099-01-17", names: {} }],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -107,17 +107,27 @@ export async function createMockTmdb(opts: MockTmdbOptions = {}): Promise<MockTm
|
||||
const f = find("tv", m[1]!);
|
||||
const s = f?.seasons?.find((x) => String(x.season_number) === m[2]);
|
||||
if (!f || !s) return notFound;
|
||||
const lang = q.language ?? "en-US";
|
||||
const zh = lang.startsWith("zh");
|
||||
return {
|
||||
json: {
|
||||
id: f.id * 100 + s.season_number,
|
||||
season_number: s.season_number,
|
||||
name: pick(s.names, q.language, `Season ${s.season_number}`),
|
||||
overview: s.overviews?.[lang] ?? "",
|
||||
poster_path: s.poster_path ?? null,
|
||||
air_date: s.air_date,
|
||||
episodes: s.episodes.map((e) => ({
|
||||
episode_number: e.episode_number,
|
||||
season_number: s.season_number,
|
||||
name: pick(e.names, q.language, ""),
|
||||
// TMDB 缺该语言译名时给占位名
|
||||
name: e.names[lang] ?? (zh ? `第 ${e.episode_number} 集` : `Episode ${e.episode_number}`),
|
||||
overview: e.overviews?.[lang] ?? "",
|
||||
air_date: e.air_date,
|
||||
runtime: e.runtime ?? null,
|
||||
still_path: e.still_path ?? null,
|
||||
vote_average: e.vote_average ?? 0,
|
||||
vote_count: e.vote_count ?? 0,
|
||||
})),
|
||||
},
|
||||
};
|
||||
@@ -130,6 +140,27 @@ export async function createMockTmdb(opts: MockTmdbOptions = {}): Promise<MockTm
|
||||
if (!f) return notFound;
|
||||
const append = new Set((q.append_to_response ?? "").split(",").filter(Boolean));
|
||||
const out: Record<string, unknown> = { ...brief(f, q.language), origin_country: f.origin_country };
|
||||
const r = f.rich;
|
||||
if (r) {
|
||||
out.genres = (r.genres ?? []).map((name, i) => ({ id: i + 1, name }));
|
||||
out.vote_average = r.vote_average ?? 0;
|
||||
out.vote_count = r.vote_count ?? 0;
|
||||
out.status = r.status ?? null;
|
||||
out.backdrop_path = r.backdrop_path ?? null;
|
||||
out.tagline = r.tagline?.[q.language ?? "en-US"] ?? "";
|
||||
if (f.type === "movie") out.runtime = r.runtime ?? null;
|
||||
else {
|
||||
out.episode_run_time = r.episode_run_time ?? [];
|
||||
out.last_air_date = r.last_air_date ?? null;
|
||||
out.networks = (r.networks ?? []).map((name, i) => ({ id: i + 1, name }));
|
||||
out.created_by = r.created_by ?? [];
|
||||
out.number_of_episodes = (f.seasons ?? [])
|
||||
.filter((x) => x.season_number > 0)
|
||||
.reduce((a, x) => a + x.episodes.length, 0);
|
||||
}
|
||||
if (append.has("credits"))
|
||||
out.credits = { cast: (r.cast ?? []).map((c, order) => ({ ...c, order })), crew: r.crew ?? [] };
|
||||
}
|
||||
if (f.type === "tv") {
|
||||
out.number_of_seasons = f.seasons?.filter((s) => s.season_number > 0).length ?? 0;
|
||||
out.seasons = (f.seasons ?? []).map((s) => ({
|
||||
@@ -137,6 +168,8 @@ export async function createMockTmdb(opts: MockTmdbOptions = {}): Promise<MockTm
|
||||
episode_count: s.episodes.length,
|
||||
name: pick(s.names, q.language, ""),
|
||||
air_date: s.air_date,
|
||||
overview: s.overviews?.[q.language ?? "en-US"] ?? "",
|
||||
poster_path: s.poster_path ?? null,
|
||||
}));
|
||||
}
|
||||
if (append.has("alternative_titles")) {
|
||||
@@ -150,12 +183,16 @@ export async function createMockTmdb(opts: MockTmdbOptions = {}): Promise<MockTm
|
||||
translations: [
|
||||
...Object.entries(f.titles).map(([lang, t]) => {
|
||||
const [l, r] = lang.split("-");
|
||||
return { iso_639_1: l, iso_3166_1: r ?? "", data: { [key]: t } };
|
||||
return {
|
||||
iso_639_1: l,
|
||||
iso_3166_1: r ?? "",
|
||||
data: { [key]: t, overview: f.overviews?.[lang] ?? "" },
|
||||
};
|
||||
}),
|
||||
...(f.extra_translations ?? []).map((x) => ({
|
||||
iso_639_1: x.iso_639_1,
|
||||
iso_3166_1: x.iso_3166_1,
|
||||
data: { [key]: x.title },
|
||||
data: { [key]: x.title, overview: x.overview ?? "" },
|
||||
})),
|
||||
],
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user