Files
flatrender/src/lib/studio-scene-data.ts
T

152 lines
4.7 KiB
TypeScript
Raw Normal View History

import {
DEFAULT_SCENE_ACCENT_COLOR,
DEFAULT_SCENE_BACKGROUND_COLOR,
} from "@/lib/studio-color-palettes";
import type { Layer, Scene, SceneTransition } from "@/lib/studio-types";
import { DEFAULT_SCENE_DURATION } from "@/lib/studio-types";
const SCENE_TRANSITIONS: SceneTransition[] = [
"none",
"fade",
"slide-left",
"zoom",
];
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function parseLayer(value: unknown): Layer | null {
if (!isRecord(value)) return null;
if (typeof value.id !== "string" || typeof value.type !== "string") return null;
if (
typeof value.x !== "number" ||
typeof value.y !== "number" ||
typeof value.width !== "number" ||
typeof value.height !== "number"
) {
return null;
}
return {
id: value.id,
type: value.type as Layer["type"],
name: typeof value.name === "string" ? value.name : undefined,
visible: typeof value.visible === "boolean" ? value.visible : undefined,
x: value.x,
y: value.y,
width: value.width,
height: value.height,
rotation: typeof value.rotation === "number" ? value.rotation : 0,
opacity: typeof value.opacity === "number" ? value.opacity : 1,
zIndex: typeof value.zIndex === "number" ? value.zIndex : 0,
props: isRecord(value.props) ? value.props : {},
};
}
function parseScene(value: unknown): Scene | null {
if (!isRecord(value)) return null;
if (typeof value.id !== "string" || typeof value.name !== "string") return null;
if (!Array.isArray(value.layers)) return null;
const layers = value.layers
.map(parseLayer)
.filter((layer): layer is Layer => layer !== null);
const transitionType =
typeof value.transitionType === "string" &&
SCENE_TRANSITIONS.includes(value.transitionType as SceneTransition)
? (value.transitionType as SceneTransition)
: "none";
return {
id: value.id,
name: value.name,
duration:
typeof value.duration === "number" ? value.duration : DEFAULT_SCENE_DURATION,
layers,
transitionType,
};
}
export function parseVideoScenes(value: unknown): Scene[] {
if (!Array.isArray(value)) return [];
return value.map(parseScene).filter((scene): scene is Scene => scene !== null);
}
export function isVideoSceneDataEmpty(sceneData: Record<string, unknown>): boolean {
const scenes = parseVideoScenes(sceneData.scenes);
return scenes.length === 0;
}
/** Omit generated thumbnails — they are re-created in the editor */
export function scenesForPersistence(scenes: Scene[]): Scene[] {
return scenes.map((scene) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- strip before save
const { thumbnailUrl, ...rest } = scene;
return rest;
});
}
export interface VideoPersistedSceneData {
scenes: Scene[];
activeSceneId: string;
currentTime?: number;
pxPerSecond?: number;
audioFileName?: string | null;
audioSrc?: string | null;
audioVolume?: number;
sceneBackgroundColor?: string;
sceneAccentColor?: string;
}
export function buildVideoSceneDataPayload(
input: VideoPersistedSceneData
): Record<string, unknown> {
return {
scenes: scenesForPersistence(input.scenes),
activeSceneId: input.activeSceneId,
currentTime: input.currentTime,
pxPerSecond: input.pxPerSecond,
audioFileName: input.audioFileName,
audioSrc: input.audioSrc,
audioVolume: input.audioVolume,
sceneBackgroundColor: input.sceneBackgroundColor,
sceneAccentColor: input.sceneAccentColor,
};
}
export function parseVideoSceneData(
sceneData: Record<string, unknown>
): VideoPersistedSceneData | null {
const scenes = parseVideoScenes(sceneData.scenes);
if (scenes.length === 0) return null;
const activeSceneId =
typeof sceneData.activeSceneId === "string" &&
scenes.some((scene) => scene.id === sceneData.activeSceneId)
? sceneData.activeSceneId
: scenes[0].id;
return {
scenes,
activeSceneId,
currentTime:
typeof sceneData.currentTime === "number" ? sceneData.currentTime : 0,
pxPerSecond:
typeof sceneData.pxPerSecond === "number" ? sceneData.pxPerSecond : undefined,
audioFileName:
typeof sceneData.audioFileName === "string" ? sceneData.audioFileName : null,
audioSrc: typeof sceneData.audioSrc === "string" ? sceneData.audioSrc : null,
audioVolume:
typeof sceneData.audioVolume === "number" ? sceneData.audioVolume : 100,
sceneBackgroundColor:
typeof sceneData.sceneBackgroundColor === "string"
? sceneData.sceneBackgroundColor
: DEFAULT_SCENE_BACKGROUND_COLOR,
sceneAccentColor:
typeof sceneData.sceneAccentColor === "string"
? sceneData.sceneAccentColor
: DEFAULT_SCENE_ACCENT_COLOR,
};
}