ai-chatbot/lib/video-canvas/story-script.ts

139 lines
5.1 KiB
TypeScript

const normalizeText = (value: unknown) => String(value || "").trim();
function normalizeStoryShot(value: unknown, index: number) {
const text = normalizeText(value);
if (/^\d+$/.test(text)) return text.padStart(2, "0");
return text || String(index + 1).padStart(2, "0");
}
type NormalizedStoryScriptRow = {
shot: string;
duration: string;
scene: string;
sceneDescription: string;
shotSize: string;
lighting: string;
dialogue: string;
backgroundMusic: string;
voiceAndSfx: string;
cameraMovement: string;
imagePrompt: string;
motionPrompt: string;
keyframeUrl: string;
};
export function normalizeStoryScriptRows(value: unknown): NormalizedStoryScriptRow[] {
if (!Array.isArray(value)) return [];
return value
.map((item, index) => {
if (Array.isArray(item)) {
const cells = item.map((cell) => normalizeText(cell));
if (!cells.some(Boolean)) return null;
const shot = normalizeStoryShot(cells[0], index);
const secondCellLooksLikeDuration = /^\d+(\.\d+)?\s*(s|秒)?$/i.test(cells[1] || "");
if (!(secondCellLooksLikeDuration ? cells.slice(2) : cells.slice(1)).some(Boolean)) return null;
const duration = secondCellLooksLikeDuration ? cells[1] : "5s";
const scene = secondCellLooksLikeDuration ? cells[2] || `镜头 ${shot}` : cells[1] || `镜头 ${shot}`;
const sceneDescription = secondCellLooksLikeDuration ? cells[2] || scene : cells[2] || cells[3] || scene;
return {
shot,
duration,
scene,
sceneDescription,
shotSize: (secondCellLooksLikeDuration ? cells[3] : "") || "中景",
lighting: (secondCellLooksLikeDuration ? cells[4] : cells[4]) || "电影感光影",
dialogue: (secondCellLooksLikeDuration ? cells[5] : "") || "+",
backgroundMusic: "无",
voiceAndSfx: (secondCellLooksLikeDuration ? cells[6] : cells[3]) || "无",
cameraMovement: (secondCellLooksLikeDuration ? cells[7] : "") || "缓慢推镜头",
imagePrompt: (secondCellLooksLikeDuration ? cells[8] : cells[5]) || sceneDescription,
motionPrompt:
(secondCellLooksLikeDuration ? cells[9] || cells[7] : cells[6]) ||
"保持镜头运动自然、连贯,围绕主体动作推进。",
keyframeUrl: "",
};
}
const raw =
item && typeof item === "object" && !Array.isArray(item)
? (item as Record<string, unknown>)
: {};
const shot = normalizeStoryShot(raw.shot || raw.shotNumber || raw.index || raw.id, index);
const duration = normalizeText(raw.duration || raw.length || raw.time || raw.seconds);
const scene = normalizeText(raw.scene || raw.location || raw.title);
const sceneDescription = normalizeText(
raw.sceneDescription ||
raw.description ||
raw.visual ||
raw.picture ||
raw.screen ||
raw.summary,
);
const shotSize = normalizeText(
raw.shotSize || raw.frameSize || raw.shot_type || raw.sceneScale || raw.cameraShot,
);
const lighting = normalizeText(
raw.lighting || raw.light || raw.atmosphere || raw.mood || raw.lightMood,
);
const dialogue = normalizeText(
raw.dialogue || raw.dialog || raw.narration || raw.voiceover || raw.monologue || raw.line,
);
const cameraMovement = normalizeText(
raw.cameraMovement || raw.cameraMove || raw.movement || raw.lensMovement || raw.motion,
);
const imagePrompt = normalizeText(
raw.imagePrompt ||
raw.image_generation_prompt ||
raw.image ||
raw.keyframePrompt ||
raw.keyFramePrompt,
);
const motionPrompt = normalizeText(
raw.motionPrompt ||
raw.videoPrompt ||
raw.video_motion_prompt ||
raw.motion ||
raw.cameraMovement,
);
const backgroundMusic = normalizeText(
raw.backgroundMusic || raw.music || raw.bgm || raw.soundtrack,
);
const voiceAndSfx = normalizeText(
raw.voiceAndSfx || raw.voice || raw.sound || raw.audio || raw.sfx,
);
if (
!scene &&
!sceneDescription &&
!imagePrompt &&
!motionPrompt &&
!backgroundMusic &&
!voiceAndSfx
) {
return null;
}
return {
shot,
duration: duration || "5s",
scene,
sceneDescription: sceneDescription || imagePrompt || scene || `镜头 ${shot}`,
shotSize: shotSize || "中景",
lighting: lighting || "电影感光影",
dialogue: dialogue || "+",
backgroundMusic: backgroundMusic || "无",
voiceAndSfx: voiceAndSfx || "无",
cameraMovement: cameraMovement || "缓慢推镜头",
imagePrompt: imagePrompt || sceneDescription || scene || `镜头 ${shot}关键帧`,
motionPrompt: motionPrompt || cameraMovement || "保持镜头运动自然、连贯,围绕主体动作推进。",
keyframeUrl: "",
};
})
.filter(
(
item,
): item is NormalizedStoryScriptRow => Boolean(item),
)
.slice(0, 18);
}