import type { ParsedRelease } from "../release-parser/types.ts"; import { compareRank, currentPri, qualityTier, softRank } from "./evaluate.ts"; import type { QualityFacts, SlotSpec } from "./types.ts"; /** 缺少关键规格时不能声称同套;集号、文件名、种子 ID 不属于版本身份。 */ export function seasonReleaseKey(q: QualityFacts): string | null { if (!q.resolution || !q.source || !q.videoCodec || !q.releaseGroup?.name) return null; return JSON.stringify([ q.resolution, q.source, q.videoCodec, q.releaseGroup.name .toLowerCase() .split("@") .map((g) => g.trim()) .sort(), [...(q.hdr ?? [])].sort(), q.cut ?? "default", ]); } export function upgradeSpec(spec: SlotSpec): SlotSpec { return { ...spec, ...(spec.upgradeRules ? { rules: spec.upgradeRules } : {}), ...(spec.upgradeHard ? { hard: spec.upgradeHard } : {}), }; } /** 优先级、软偏好、规范化规格形成稳定顺序;同档对齐不允许质量降档。 */ export function selectSeasonRelease(spec: SlotSpec, versions: readonly QualityFacts[]): QualityFacts | undefined { let target: QualityFacts | undefined; const rank = (q: QualityFacts) => { const tier = qualityTier(q, spec); return [ currentPri(spec, q), tier.source, tier.resolution, ...softRank(spec, { raw: "", hdr: [], audio: [], ...q } as unknown as ParsedRelease), ]; }; for (const q of versions) { const key = seasonReleaseKey(q); if (!key) continue; const compared = target ? compareRank(rank(q), rank(target)) : 1; if (compared > 0 || (compared === 0 && key < seasonReleaseKey(target!)!)) target = q; } return target; }