import type { FilesEntityFromCandidateResult, FilesExternalCandidate } from "@umt/contracts"; import type { Principal } from "../../http/auth.ts"; import { CandidateError, createEntityFromTmdbCandidate, enqueueNewEntityRefresh, } from "../sources-identity/service/entity-from-candidate.ts"; import { metadataLanguages, metadataTextOptions } from "../sources-identity/service/metadata-rich.ts"; import { actorOfP, FilesError, type FmDeps } from "./common.ts"; import { audit } from "./ledger.ts"; // G1:整理向导选中"仅 TMDB 候选"作品 → 就地建本地作品 + 外部映射(accepted/manual)+ 季集单元,返回 entityId。 // 幂等(同 TMDB id 已有映射即复用);/files/organize 的 external 项与独立端点共用。 const actorOf = (p: Principal | undefined) => ({ type: p?.kind === "api_key" ? ("api_key" as const) : ("user" as const), id: actorOfP(p), }); export async function entityFromCandidate( d: FmDeps, principal: Principal | undefined, ref: FilesExternalCandidate, ): Promise { try { const languages = await metadataLanguages(d.ctx); const r = await createEntityFromTmdbCandidate(d.ctx.db, d.tmdb, ref, actorOf(principal), { languages, ...(await metadataTextOptions(d.ctx)), }); if (r.created) { await audit(d, principal, "entity_from_candidate", { ...ref, mediaEntityId: r.mediaEntityId }); // 订阅建档 / 搜索建档 / 整理向导都走这里:建档即入队 full 刷新(每集信息、富字段) await enqueueNewEntityRefresh(d.ctx, r.mediaEntityId); } return r; } catch (e) { if (e instanceof CandidateError) throw new FilesError( e.reason === "not_found" ? "not_found" : e.reason === "upstream" ? "upstream" : "bad_request", e.message, ); throw e; } } export const externalKey = (x: FilesExternalCandidate) => `${x.provider}:${x.kind}:${x.externalId}`;