Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
68 lines
3.1 KiB
TypeScript
68 lines
3.1 KiB
TypeScript
import type { EpisodeGroupList, EpisodeStructureSeason } from "@umt/contracts";
|
|
|
|
// 季集结构(TMDB 默认季 / 剧集组)的界面纯函数:轨道选项、季卡片文案、切换确认文案。学 MoviePilot 的订阅选季对话框:
|
|
// 横向轨道选「TMDB 默认 / 各剧集组」,下面按所选结构显示季卡片(海报、季名、首播年、集数、已入库)。
|
|
|
|
export interface StructureOption {
|
|
/** 剧集组 id;null = TMDB 默认季 */
|
|
groupId: string | null;
|
|
label: string;
|
|
/** 第二行:类型 · 季数 · 集数 */
|
|
meta: string | null;
|
|
current: boolean;
|
|
}
|
|
|
|
export const DEFAULT_STRUCTURE_LABEL = "TMDB 默认季";
|
|
|
|
/** 轨道选项:TMDB 默认季在最前,其后按 TMDB 顺序列剧集组;当前选择打标 */
|
|
export function structureOptions(list: EpisodeGroupList | undefined): StructureOption[] {
|
|
const cur = list?.current?.groupId ?? null;
|
|
const out: StructureOption[] = [{ groupId: null, label: DEFAULT_STRUCTURE_LABEL, meta: null, current: cur === null }];
|
|
for (const g of list?.groups ?? []) {
|
|
out.push({
|
|
groupId: g.id,
|
|
label: g.name,
|
|
meta: [g.typeLabel, `${g.groupCount} 季`, `${g.episodeCount} 集`].filter(Boolean).join(" · "),
|
|
current: cur === g.id,
|
|
});
|
|
}
|
|
// 当前选的剧集组已从 TMDB 删除:仍列出来,免得界面上看不到当前状态
|
|
if (cur && !out.some((o) => o.groupId === cur))
|
|
out.push({ groupId: cur, label: list?.current?.name ?? cur, meta: "TMDB 上已不存在", current: true });
|
|
return out;
|
|
}
|
|
|
|
/** 有剧集组可选时才显示轨道 */
|
|
export const hasStructureChoice = (list: EpisodeGroupList | undefined) =>
|
|
!!list?.applicable && (list.groups.length > 0 || !!list.current);
|
|
|
|
export function seasonTitle(s: Pick<EpisodeStructureSeason, "seasonNumber" | "name">): string {
|
|
return s.name ?? (s.seasonNumber === 0 ? "特别篇" : `第 ${s.seasonNumber} 季`);
|
|
}
|
|
|
|
/** 季卡片第二行:首播年 · 集数 */
|
|
export function seasonLine(s: Pick<EpisodeStructureSeason, "airDate" | "episodeCount">): string {
|
|
return [s.airDate ? s.airDate.slice(0, 4) : null, `${s.episodeCount} 集`].filter(Boolean).join(" · ");
|
|
}
|
|
|
|
/** 已整理 x/y;一集都没有时返回 null(只列非零项) */
|
|
export function inLibraryText(s: Pick<EpisodeStructureSeason, "inLibrary" | "episodeCount">): string | null {
|
|
return s.inLibrary > 0 ? `已整理 ${s.inLibrary}/${s.episodeCount}` : null;
|
|
}
|
|
|
|
const pad = (n: number) => String(n).padStart(2, "0");
|
|
|
|
/** 剧集组里的集与 TMDB 默认季集号不同时给「原 S01E14」对照 */
|
|
export function originText(
|
|
seasonNumber: number,
|
|
e: { episodeNumber: number; tmdbSeason: number; tmdbEpisode: number },
|
|
): string | null {
|
|
if (e.tmdbSeason === seasonNumber && e.tmdbEpisode === e.episodeNumber) return null;
|
|
return `原 S${pad(e.tmdbSeason)}E${pad(e.tmdbEpisode)}`;
|
|
}
|
|
|
|
/** 切换确认的说明:改的是作品设置,影响季表、集数映射、订阅与刮削 */
|
|
export function switchDescription(target: string): string {
|
|
return `改按「${target}」分季后,已整理文件随季集一起调整;集数映射、订阅和刮削都按新季集,已绑定的资源会重新换算。之后可以再切回来。`;
|
|
}
|