feat(metadata): field-level episode metadata, titleZh and refresh?full=1 (MD-1)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-29 17:28:16 +08:00
1 parent fc1856c2e3
commit 9cd5a28943
12 files changed
+1104 -27

No files matched your search

+53 -1
View File
@@ -1,6 +1,6 @@
import { z } from "zod";
import { Id, NonNegInt, Timestamp } from "./common.ts";
import { MediaEntityStatus, MediaKind } from "./content.ts";
import { MediaCredit, MediaEntityStatus, MediaKind } from "./content.ts";
import { ManualAcquisitionRequest } from "./execution.ts";
import { InventoryEntry, MediaServerInventory, SlotStatusCounts } from "./organization.ts";
@@ -338,6 +338,8 @@ export const TmdbMediaSeason = z.object({
airDate: z.string().nullable(),
/** 本地已入库的集数(整季资产按全季计);未收录为 null */
localHave: NonNegInt.nullable(),
overview: z.string().nullable().optional(),
posterPath: z.string().nullable().optional(),
});
export type TmdbMediaSeason = z.infer<typeof TmdbMediaSeason>;
@@ -367,5 +369,55 @@ export const TmdbMediaDetail = z.object({
mediaServers: MediaServerInventory.nullable(),
/** 搜资源时服务端会用的查询词 */
queries: z.array(z.string()),
// MD-1:按语言回退链取的详情字段(媒体详情页与媒体库详情共用展示)
/** 中文名:回退链中第一个中文值(zh-CN 优先);没有为 null */
titleZh: z.string().nullable().optional(),
tagline: z.string().nullable().optional(),
genres: z.array(z.string()).optional(),
voteAverage: z.number().nullable().optional(),
voteCount: z.number().int().nullable().optional(),
/** TMDB status 原文(Returning Series / Ended / Released …) */
airStatus: z.string().nullable().optional(),
/** 分钟:电影片长;剧集为单集时长 */
runtime: z.number().int().nullable().optional(),
episodeCount: z.number().int().nullable().optional(),
lastAirDate: z.string().nullable().optional(),
platform: z.string().nullable().optional(),
cast: z.array(MediaCredit).optional(),
crew: z.array(MediaCredit).optional(),
});
export type TmdbMediaDetail = z.infer<typeof TmdbMediaDetail>;
/** 一集的 TMDB 信息(标题 / 简介按语言回退链,language 记实际取到的语言) */
export const MediaEpisodeInfo = z.object({
seasonNumber: z.number().int(),
episodeNumber: z.number().int(),
name: z.string().nullable(),
nameLanguage: z.string().nullable(),
overview: z.string().nullable(),
overviewLanguage: z.string().nullable(),
airDate: z.string().nullable(),
runtime: z.number().int().nullable(),
stillPath: z.string().nullable(),
voteAverage: z.number().nullable(),
voteCount: z.number().int().nullable(),
});
export type MediaEpisodeInfo = z.infer<typeof MediaEpisodeInfo>;
export const TmdbSeasonParams = z.object({
id: z.string().regex(/^\d{1,12}$/),
season: z.coerce.number().int().min(0).max(999),
});
export type TmdbSeasonParams = z.infer<typeof TmdbSeasonParams>;
/** GET /media/tmdb/tv/{id}/season/{season}:季详情与集列表(仅 TMDB 作品详情页按季展开时拉取) */
export const TmdbSeasonDetailView = z.object({
tvId: z.string(),
seasonNumber: z.number().int(),
name: z.string().nullable(),
overview: z.string().nullable(),
airDate: z.string().nullable(),
posterPath: z.string().nullable(),
episodes: z.array(MediaEpisodeInfo),
});
export type TmdbSeasonDetailView = z.infer<typeof TmdbSeasonDetailView>;