Files
UHD_Media_Tool/apps/server/src/modules/file-manager/candidate.ts
T
rootandClaude Opus 5.5 f9901b514b feat(metadata): overview falls back to Chinese variants only (MD-2)
- adapters: overviewLanguages() limits overview/tagline chain to Chinese
  variants (zh-CN→zh-SG→zh-TW→zh-HK); titles keep full chain; season
  supplement fetch skips languages useless for overview
- setting metadata.overviewNonChineseFallback (default off) + UI switch
- media detail shows "暂无简介" for empty entity/episode overview
- scripts/overview-recompute.ts: recompute stored non-Chinese overviews
  from kv_cache only (no TMDB requests), dry-run by default, --apply
- tests: chain unit tests, Heart Signal (103147) fixture, server
  recompute test, MP differential for episode overview
- runbook + CHANGELOG

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 18:04:23 +08:00

48 lines
1.9 KiB
TypeScript

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<FilesEntityFromCandidateResult> {
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}`;