import type { RenderScene, RenderSettings } from "../src/lib/render-schemas"; import { RESOLUTION_DIMENSIONS } from "../src/lib/render-schemas"; export interface NexrenderAsset { type: string; layerName?: string; composition?: string; property?: string; value?: string | number; src?: string; width?: number; height?: number; time?: number; [key: string]: unknown; } export interface NexrenderJob { template: { src: string; composition: string; frameStart?: number; frameEnd?: number; }; assets: NexrenderAsset[]; actions?: { postrender?: Array>; }; onRenderProgress?: string; metadata?: Record; } function layerToAsset( layer: RenderScene["layers"][number], scene: RenderScene, sceneIndex: number ): NexrenderAsset | null { const time = sceneIndex * scene.duration; switch (layer.type) { case "text": { const text = typeof layer.props.text === "string" ? layer.props.text : "Text"; return { type: "data", layerName: `Scene${sceneIndex + 1}_Text`, composition: scene.name, property: "Source Text", value: text, time, }; } case "image": { const src = typeof layer.props.src === "string" ? layer.props.src : ""; if (!src) return null; return { type: "image", layerName: `Scene${sceneIndex + 1}_Image`, composition: scene.name, src, width: layer.width, height: layer.height, time, }; } case "video": { const src = typeof layer.props.src === "string" ? layer.props.src : ""; if (!src) return null; return { type: "video", layerName: `Scene${sceneIndex + 1}_Video`, composition: scene.name, src, time, }; } case "shape": return { type: "data", layerName: `Scene${sceneIndex + 1}_Shape`, composition: scene.name, property: "Opacity", value: Math.round(layer.opacity * 100), time, }; default: return null; } } export function buildNexrenderJob( scenes: RenderScene[], settings: RenderSettings, jobId: string, outputPath: string ): NexrenderJob { const templateSrc = process.env.NEXRENDER_TEMPLATE_SRC ?? "file:///templates/creatorstudio-base.aep"; const composition = process.env.NEXRENDER_COMPOSITION ?? "CreatorStudio_Main"; const { width, height } = RESOLUTION_DIMENSIONS[settings.resolution]; const totalDuration = scenes.reduce((sum, scene) => sum + scene.duration, 0); const frameEnd = Math.ceil(totalDuration * settings.fps); const assets: NexrenderAsset[] = [ { type: "data", layerName: "Settings", composition, property: "Width", value: width, }, { type: "data", layerName: "Settings", composition, property: "Height", value: height, }, { type: "data", layerName: "Settings", composition, property: "Frame Rate", value: settings.fps, }, ]; scenes.forEach((scene, sceneIndex) => { assets.push({ type: "data", layerName: `Scene${sceneIndex + 1}`, composition, property: "Duration", value: scene.duration, time: sceneIndex * scene.duration, }); const sortedLayers = [...scene.layers].sort( (a, b) => a.zIndex - b.zIndex ); sortedLayers.forEach((layer) => { const asset = layerToAsset(layer, scene, sceneIndex); if (asset) assets.push(asset); }); }); return { template: { src: templateSrc, composition, frameStart: 0, frameEnd, }, assets, actions: { postrender: [ { module: "@nexrender/action-copy", output: outputPath, }, ], }, metadata: { jobId, resolution: settings.resolution, fps: settings.fps, format: settings.format, }, }; }