feat(*): 分镜与资产 - 完善五类资产、候选去重、搜索、中文镜头编号与分类整理
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
type ProjectDetail,
|
||||
type ShotAssetReference,
|
||||
} from "./projects";
|
||||
import { normalizeAssetCategory, type AssetCategory } from "@/lib/asset-category";
|
||||
|
||||
export const projectAgentToolNames = [
|
||||
"project_get_context",
|
||||
@@ -47,7 +48,7 @@ export async function runProjectAgentTool(name: ProjectAgentToolName, rawInput:
|
||||
}
|
||||
if (name === "project_extract_asset_candidates") {
|
||||
const candidates = Array.isArray(rawInput.candidates) ? rawInput.candidates : [];
|
||||
return createProjectAssetCandidates(projectId, candidates.filter(isCandidateInput));
|
||||
return createProjectAssetCandidates(projectId, candidates.map(normalizeAgentCandidateInput).filter((candidate): candidate is NonNullable<typeof candidate> => Boolean(candidate)), "agent");
|
||||
}
|
||||
if (name === "project_confirm_asset_candidate") {
|
||||
return confirmProjectAssetCandidate(projectId, String(rawInput.candidateId || ""), String(rawInput.assetId || "") || undefined);
|
||||
@@ -68,7 +69,7 @@ export async function runProjectAgentTool(name: ProjectAgentToolName, rawInput:
|
||||
return updateWorkflowStep(projectId, String(rawInput.stepId || ""), { status: "running" });
|
||||
}
|
||||
if (name === "project_link_asset") {
|
||||
return linkProjectAsset(projectId, { assetId: String(rawInput.assetId || ""), category: String(rawInput.category || "other") });
|
||||
return linkProjectAsset(projectId, { assetId: String(rawInput.assetId || ""), category: normalizeAssetCategory(rawInput.category) });
|
||||
}
|
||||
if (name === "project_upsert_asset_version") {
|
||||
return createProjectAssetVersion(projectId, String(rawInput.assetId || ""), { prompt: String(rawInput.prompt || ""), definitionJson: typeof rawInput.definitionJson === "string" ? rawInput.definitionJson : undefined, note: String(rawInput.note || "") });
|
||||
@@ -79,10 +80,21 @@ export async function runProjectAgentTool(name: ProjectAgentToolName, rawInput:
|
||||
throw new Error(`未知项目工具:${name}`);
|
||||
}
|
||||
|
||||
function isCandidateInput(value: unknown): value is { unitId?: string; shotId?: string; name: string; category: string; details?: Record<string, unknown> } {
|
||||
if (!value || typeof value !== "object") return false;
|
||||
const agentCandidateCategories = new Set<AssetCategory>(["environment", "prop", "material", "other"]);
|
||||
|
||||
function normalizeAgentCandidateInput(value: unknown): { unitId?: string; shotId?: string; name: string; category: AssetCategory; details?: Record<string, unknown> } | null {
|
||||
if (!value || typeof value !== "object") return null;
|
||||
const item = value as Record<string, unknown>;
|
||||
return typeof item.name === "string" && typeof item.category === "string";
|
||||
if (typeof item.name !== "string" || typeof item.category !== "string") return null;
|
||||
const category = normalizeAssetCategory(item.category);
|
||||
if (!agentCandidateCategories.has(category)) return null;
|
||||
return {
|
||||
name: item.name,
|
||||
category,
|
||||
...(typeof item.unitId === "string" ? { unitId: item.unitId } : {}),
|
||||
...(typeof item.shotId === "string" ? { shotId: item.shotId } : {}),
|
||||
...(item.details && typeof item.details === "object" && !Array.isArray(item.details) ? { details: item.details as Record<string, unknown> } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function isShotInput(value: unknown): value is { id?: string; unitId?: string; title: string; description?: string; position?: number; durationMs?: number; status?: string } {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { apiClient, request } from "@/services/api/request";
|
||||
import { normalizeAssetCategory, type AssetCategory } from "@/lib/asset-category";
|
||||
import type { GenerationTask } from "@/services/api/task-center";
|
||||
|
||||
const api = apiClient;
|
||||
@@ -56,7 +57,7 @@ export type ProjectAsset = {
|
||||
id: string;
|
||||
title: string;
|
||||
mediaType: string;
|
||||
category: string;
|
||||
category: AssetCategory;
|
||||
status: string;
|
||||
primaryVersionId?: string;
|
||||
versionCount: number;
|
||||
@@ -121,7 +122,7 @@ export type ProjectAssetCandidate = {
|
||||
unitId?: string;
|
||||
shotId?: string;
|
||||
name: string;
|
||||
category: string;
|
||||
category: AssetCategory;
|
||||
status: "pending_confirmation" | "confirmed" | "ignored" | string;
|
||||
detailsJson: string;
|
||||
resolvedAssetId?: string;
|
||||
@@ -365,21 +366,30 @@ export function listProjectAssetsPage(projectId: string, options: { page?: numbe
|
||||
status: options.status || undefined,
|
||||
folder_id: options.folderId,
|
||||
q: options.query || undefined,
|
||||
} }));
|
||||
} })).then((page) => ({ ...page, assets: page.assets.map(normalizeProjectAsset) }));
|
||||
}
|
||||
|
||||
export function listProjectAssets(projectId: string) {
|
||||
return request<{ assets: ProjectAsset[] }>(api.get(`/projects/${encodeURIComponent(projectId)}/assets`));
|
||||
return request<{ assets: ProjectAsset[] }>(api.get(`/projects/${encodeURIComponent(projectId)}/assets`)).then((result) => ({ assets: result.assets.map(normalizeProjectAsset) }));
|
||||
}
|
||||
|
||||
export function listProjectAssetCandidates(projectId: string, options: { page?: number; pageSize?: number; unitId?: string; status?: string; category?: string } = {}) {
|
||||
export function listProjectAssetCandidates(projectId: string, options: { page?: number; pageSize?: number; unitId?: string; status?: string; category?: string; query?: string } = {}) {
|
||||
return request<ProjectAssetCandidatePage>(api.get(`/projects/${encodeURIComponent(projectId)}/asset-candidates`, { params: {
|
||||
page: options.page || 1,
|
||||
page_size: options.pageSize || 100,
|
||||
unit_id: options.unitId || undefined,
|
||||
status: options.status || undefined,
|
||||
category: options.category || undefined,
|
||||
} }));
|
||||
q: options.query || undefined,
|
||||
} })).then((page) => ({ ...page, candidates: page.candidates.map(normalizeProjectAssetCandidate) }));
|
||||
}
|
||||
|
||||
function normalizeProjectAsset(asset: ProjectAsset): ProjectAsset {
|
||||
return { ...asset, category: normalizeAssetCategory(asset.category), usages: Array.isArray(asset.usages) ? asset.usages : [] };
|
||||
}
|
||||
|
||||
function normalizeProjectAssetCandidate(candidate: ProjectAssetCandidate): ProjectAssetCandidate {
|
||||
return { ...candidate, category: normalizeAssetCategory(candidate.category) };
|
||||
}
|
||||
|
||||
function normalizeProjectDetail(detail: ProjectDetail): ProjectDetail {
|
||||
@@ -391,8 +401,7 @@ function normalizeProjectDetail(detail: ProjectDetail): ProjectDetail {
|
||||
: [];
|
||||
const assets = Array.isArray(detail.assets)
|
||||
? detail.assets.map((asset) => ({
|
||||
...asset,
|
||||
usages: Array.isArray(asset.usages) ? asset.usages : [],
|
||||
...normalizeProjectAsset(asset),
|
||||
...(asset.character ? {
|
||||
character: {
|
||||
...asset.character,
|
||||
@@ -415,7 +424,7 @@ function normalizeProjectDetail(detail: ProjectDetail): ProjectDetail {
|
||||
shotRevisions: Array.isArray(detail.shotRevisions) ? detail.shotRevisions : [],
|
||||
shotArtifacts: Array.isArray(detail.shotArtifacts) ? detail.shotArtifacts : [],
|
||||
shotReferences: Array.isArray(detail.shotReferences) ? detail.shotReferences : [],
|
||||
assetCandidates: Array.isArray(detail.assetCandidates) ? detail.assetCandidates : [],
|
||||
assetCandidates: Array.isArray(detail.assetCandidates) ? detail.assetCandidates.map(normalizeProjectAssetCandidate) : [],
|
||||
tasks: Array.isArray(detail.tasks) ? detail.tasks : [],
|
||||
};
|
||||
}
|
||||
@@ -468,7 +477,7 @@ export function unlinkCanvasProject(projectId: string, canvasId: string) {
|
||||
return request<{ canvasId: string }>(api.delete(`/projects/${encodeURIComponent(projectId)}/canvases/${encodeURIComponent(canvasId)}`));
|
||||
}
|
||||
|
||||
export function linkProjectAsset(projectId: string, input: { assetId: string; category: string; folderId?: string }, signal?: AbortSignal) {
|
||||
export function linkProjectAsset(projectId: string, input: { assetId: string; category: AssetCategory; folderId?: string }, signal?: AbortSignal) {
|
||||
return request<{ asset: ProjectAsset }>(api.post(`/projects/${encodeURIComponent(projectId)}/assets`, input, { signal }));
|
||||
}
|
||||
|
||||
@@ -476,7 +485,7 @@ export function unlinkProjectAsset(projectId: string, assetId: string) {
|
||||
return request<{ id: string }>(api.delete(`/projects/${encodeURIComponent(projectId)}/assets/${encodeURIComponent(assetId)}`));
|
||||
}
|
||||
|
||||
export function updateProjectAssetCategory(projectId: string, assetId: string, category: string, signal?: AbortSignal) {
|
||||
export function updateProjectAssetCategory(projectId: string, assetId: string, category: AssetCategory, signal?: AbortSignal) {
|
||||
return request<{ asset: ProjectAsset }>(api.patch(`/projects/${encodeURIComponent(projectId)}/assets/${encodeURIComponent(assetId)}`, { category }, { signal }));
|
||||
}
|
||||
|
||||
@@ -560,8 +569,8 @@ export function createShotRevision(projectId: string, shotId: string, input: Sho
|
||||
return request<{ shot: ProjectShot; revision: ShotRevision }>(api.post(`/projects/${encodeURIComponent(projectId)}/shots/${encodeURIComponent(shotId)}/revisions`, input));
|
||||
}
|
||||
|
||||
export function createProjectAssetCandidates(projectId: string, candidates: Array<{ unitId?: string; shotId?: string; name: string; category: string; details?: Record<string, unknown> }>) {
|
||||
return request<{ candidates: ProjectAssetCandidate[] }>(api.post(`/projects/${encodeURIComponent(projectId)}/asset-candidates`, { candidates }));
|
||||
export function createProjectAssetCandidates(projectId: string, candidates: Array<{ unitId?: string; shotId?: string; name: string; category: AssetCategory; details?: Record<string, unknown> }>, source?: "chapter_character_extract" | "agent") {
|
||||
return request<{ candidates: ProjectAssetCandidate[] }>(api.post(`/projects/${encodeURIComponent(projectId)}/asset-candidates`, { candidates, source }));
|
||||
}
|
||||
|
||||
export function confirmProjectAssetCandidate(projectId: string, candidateId: string, assetId?: string) {
|
||||
|
||||
Reference in New Issue
Block a user