import type { VideoCanvasRolePreset } from "@/lib/video-canvas/config"; export const VIDEO_CANVAS_ROLE_MATERIALS = [ { key: "full", label: "全身正面" }, { key: "face", label: "近景" }, { key: "expression", label: "九宫格" }, { key: "threeView", label: "设定图" }, ] as const; export type VideoCanvasRoleMaterialKind = (typeof VIDEO_CANVAS_ROLE_MATERIALS)[number]["key"]; export type VideoCanvasAttachedRole = { assetId: string; compliant: boolean; id: string; imageUrl: string; materialKind: VideoCanvasRoleMaterialKind; materialLabel: string; name: string; prompt: string; tags: string; }; export function composeVideoCanvasRolePrompt( prompt: string, roles: Array>, ) { return [ prompt.trim(), ...roles.map((role) => [ `角色引用「${role.name}」${role.assetId ? `(合规资产 ${role.assetId})` : ""}。`, role.prompt, ] .filter(Boolean) .join(" "), ), ] .filter(Boolean) .join(" "); } function isRecord(value: unknown): value is Record { return Boolean(value) && typeof value === "object" && !Array.isArray(value); } function readString(record: Record, key: string) { return typeof record[key] === "string" ? record[key].trim() : ""; } function isRoleMaterialKind(value: string): value is VideoCanvasRoleMaterialKind { return VIDEO_CANVAS_ROLE_MATERIALS.some((item) => item.key === value); } export function toVideoCanvasAttachedRole( preset: VideoCanvasRolePreset, materialKind: VideoCanvasRoleMaterialKind, ): VideoCanvasAttachedRole { const material = VIDEO_CANVAS_ROLE_MATERIALS.find( (item) => item.key === materialKind, ) || VIDEO_CANVAS_ROLE_MATERIALS[0]; return { assetId: preset.assetId, compliant: preset.compliant, id: preset.id, imageUrl: preset.images[material.key], materialKind: material.key, materialLabel: material.label, name: preset.name, prompt: preset.prompt, tags: preset.tags, }; } export function getVideoCanvasAttachedRole( draftValues: Record | null | undefined, ): VideoCanvasAttachedRole | null { if (!draftValues) return null; const params = isRecord(draftValues.params) ? draftValues.params : {}; const paramsRole = Array.isArray(params.roles) && isRecord(params.roles[0]) ? params.roles[0] : null; const source = isRecord(draftValues.videoRole) ? draftValues.videoRole : paramsRole; if (!source) return null; const id = readString(source, "id"); const imageUrl = readString(source, "imageUrl"); const materialKindValue = readString(source, "materialKind"); const name = readString(source, "name"); if (!id || !imageUrl || !name || !isRoleMaterialKind(materialKindValue)) return null; const fallbackLabel = VIDEO_CANVAS_ROLE_MATERIALS.find( (item) => item.key === materialKindValue, )?.label || "角色素材"; return { assetId: readString(source, "assetId"), compliant: source.compliant !== false, id, imageUrl, materialKind: materialKindValue, materialLabel: readString(source, "materialLabel") || fallbackLabel, name, prompt: readString(source, "prompt"), tags: readString(source, "tags"), }; } export function setVideoCanvasAttachedRole( draftValues: Record | null | undefined, preset: VideoCanvasRolePreset | null, materialKind: VideoCanvasRoleMaterialKind = "full", ) { const current = { ...(draftValues || {}) }; const params = isRecord(current.params) ? current.params : {}; if (preset) { const role = toVideoCanvasAttachedRole(preset, materialKind); return { ...current, params: { ...params, roles: [role], }, videoRole: role, }; } const { videoRole: _removedRole, ...nextDraftValues } = current; const { roles: _removedRoles, ...nextParams } = params; return { ...nextDraftValues, params: nextParams, }; }