feat(file-manager): v2 backend gaps — TMDB candidate entities, min size, on-existing, naming source (W9-H)
- G1 createEntityFromTmdbCandidate (sources-identity service): entity + accepted
manual external identity + season/episode units, idempotent per TMDB id;
POST /files/entity-from-candidate and organize items with `external`
- G2 minSizeBytes for organize (excluded below_min_size) and identify
(skipped + excludedBySize)
- G3 onExisting passed to planImport; dryRun marks each file
(same/skip/replace/conflict); replace = explicit grant
- G4 naming template|keep; planImport namingMode/namingSource, frozen
plan.naming {mode, templateSource, templateVersion}; targetSegments
- G5 identify prior-record hints, GET /files/record-sources, organize
refreshMetadata, POST /files/scrape-batch (dedupe by entity)
- G8 GET /files/tree (parent, subdirs hasChildren, video/registered counts)
- G9 POST /files/suggest-name returns 501 until the naming engine lands
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
88bbed409c
commit
13132e7ad8
12 files changed
+977
-36
No files matched your search
@@ -0,0 +1,34 @@
|
||||
import type { FilesEntityFromCandidateResult, FilesExternalCandidate } from "@umt/contracts";
|
||||
import type { Principal } from "../../http/auth.ts";
|
||||
import { CandidateError, createEntityFromTmdbCandidate } from "../sources-identity/service/entity-from-candidate.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 r = await createEntityFromTmdbCandidate(d.ctx.db, d.tmdb, ref, actorOf(principal));
|
||||
if (r.created) await audit(d, principal, "entity_from_candidate", { ...ref, mediaEntityId: 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}`;
|
||||
Reference in new issue
Block a user