ai-chatbot/lib/video-canvas/video-roles.ts

136 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<Pick<VideoCanvasAttachedRole, "assetId" | "name" | "prompt">>,
) {
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<string, unknown> {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function readString(record: Record<string, unknown>, 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<string, unknown> | 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<string, unknown> | 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,
};
}