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

79 lines
3.1 KiB
TypeScript

const normalizeText = (value: unknown) =>
typeof value === "string" || typeof value === "number" ? String(value).trim() : "";
export function normalizeVideoAnalysisRows(
value: unknown,
framePreviews: string[] = [],
nodeId = "video-story",
) {
if (!Array.isArray(value)) return [];
return value
.map((item, index) => {
const raw =
item && typeof item === "object" && !Array.isArray(item)
? (item as Record<string, unknown>)
: {};
const sceneDescription = normalizeText(
raw.sceneDescription || raw.scene || raw.title || raw.description,
);
const narrative = normalizeText(
raw.narrative || raw.story || raw.storyContent || raw.narration || raw.summary,
);
const shot = normalizeText(raw.shot || raw.shotNumber || raw.index || index + 1);
const startTime = normalizeText(raw.startTime ?? raw.start ?? raw.start_time);
const endTime = normalizeText(raw.endTime ?? raw.end ?? raw.end_time);
const duration = normalizeText(raw.duration ?? raw.durationSeconds ?? raw.duration_seconds);
const shotSize = normalizeText(
raw.shotSize || raw.frameSize || raw.shot_type || raw.sceneScale,
);
const cameraAngle = normalizeText(raw.cameraAngle || raw.angle || raw.camera_angle);
const cameraMovement = normalizeText(
raw.cameraMovement || raw.cameraMove || raw.camera_motion || raw.motionType,
);
const focusDepth = normalizeText(
raw.focusDepth || raw.depthOfField || raw.focalDepth || raw.focus_depth,
);
const lighting = normalizeText(raw.lighting || raw.light || raw.lightDesign);
const backgroundMusic = normalizeText(raw.backgroundMusic || raw.music || raw.bgm);
const voiceAndSfx = normalizeText(raw.voiceAndSfx || raw.sound || raw.audio || raw.sfx);
const imagePrompt = normalizeText(
raw.imagePrompt || raw.image_generation_prompt || raw.image,
);
const motionPrompt = normalizeText(
raw.motionPrompt || raw.motion || raw.videoPrompt || raw.video_motion_prompt,
);
const keyframeIndex = Number(
raw.keyframeIndex || raw.frameIndex || raw.key_frame_index || index + 1,
);
if (!sceneDescription && !narrative && !imagePrompt) return null;
const normalizedFrameIndex =
Number.isFinite(keyframeIndex) && keyframeIndex > 0
? Math.max(0, Math.min(framePreviews.length - 1, Math.round(keyframeIndex) - 1))
: index % Math.max(framePreviews.length, 1);
return {
id: normalizeText(raw.id) || `${nodeId}-analysis-${index}`,
shot: shot || String(index + 1),
startTime,
endTime,
duration,
sceneDescription,
narrative,
shotSize,
cameraAngle,
cameraMovement,
focusDepth,
lighting,
backgroundMusic,
voiceAndSfx,
imagePrompt,
motionPrompt,
keyframeUrl:
normalizeText(raw.keyframeUrl || raw.previewUrl || raw.imageUrl) ||
framePreviews[normalizedFrameIndex] ||
"",
};
})
.filter((item): item is NonNullable<typeof item> => Boolean(item))
.slice(0, 120);
}