64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
export type VideoCanvasMotionMention = {
|
|
custom: boolean;
|
|
id: string;
|
|
index: number;
|
|
label: string;
|
|
motionId: string;
|
|
offset: number;
|
|
previewUrl: string;
|
|
prompt: string;
|
|
};
|
|
|
|
function normalizeMotionMentionId(value: unknown) {
|
|
return String(value || "")
|
|
.trim()
|
|
.replace(/[^A-Za-z0-9_-]+/g, "-")
|
|
.slice(0, 120);
|
|
}
|
|
|
|
export function normalizeVideoCanvasMotionMentions(
|
|
value: unknown,
|
|
): VideoCanvasMotionMention[] {
|
|
if (!Array.isArray(value)) return [];
|
|
|
|
return value.slice(0, 100).flatMap((item, position) => {
|
|
if (!item || typeof item !== "object") return [];
|
|
const record = item as Record<string, unknown>;
|
|
const label = String(record.label || "").trim().slice(0, 100);
|
|
if (!label) return [];
|
|
const motionId =
|
|
normalizeMotionMentionId(record.motionId) || `motion-${position + 1}`;
|
|
|
|
return [{
|
|
custom: record.custom === true,
|
|
id:
|
|
normalizeMotionMentionId(record.id) ||
|
|
`motion-mention-${motionId}-${position + 1}`,
|
|
index: Math.max(
|
|
1,
|
|
Math.min(100, Math.round(Number(record.index) || position + 1)),
|
|
),
|
|
label,
|
|
motionId,
|
|
offset: Math.max(0, Math.round(Number(record.offset) || 0)),
|
|
previewUrl: String(record.previewUrl || "").trim().slice(0, 4096),
|
|
prompt: String(record.prompt || label).trim().slice(0, 2000) || label,
|
|
}];
|
|
});
|
|
}
|
|
|
|
export function composeVideoCanvasMotionPrompt(
|
|
prompt: string,
|
|
motionMentions: VideoCanvasMotionMention[],
|
|
) {
|
|
return [
|
|
String(prompt || "").trim(),
|
|
...motionMentions.map((mention) => {
|
|
const instruction = mention.prompt.trim() || mention.label;
|
|
return `运镜要求「${mention.label}」:${instruction}`;
|
|
}),
|
|
]
|
|
.filter(Boolean)
|
|
.join(" ");
|
|
}
|