75 lines
2.4 KiB
TypeScript
75 lines
2.4 KiB
TypeScript
import type { Node, Edge } from '@vue-flow/core'
|
|
import type { StudioNodeData, GraphJson } from './types'
|
|
import { NODE_TYPES } from './nodeTypes'
|
|
|
|
const FPS_MAP: Record<string, [number, number]> = {
|
|
'23.98': [24000, 1001],
|
|
'24': [24, 1],
|
|
'25': [25, 1],
|
|
'29.97': [30000, 1001],
|
|
'30': [30, 1],
|
|
'50': [50, 1],
|
|
'59.94': [60000, 1001],
|
|
'60': [60, 1],
|
|
}
|
|
|
|
function parseFps(fps: string): { fps_num: number; fps_den: number } {
|
|
const [num, den] = FPS_MAP[fps] ?? [25, 1]
|
|
return { fps_num: num, fps_den: den }
|
|
}
|
|
|
|
function parseRes(res: string): { width: number; height: number } {
|
|
const [w, h] = (res || '1920x1080').split('x').map(Number)
|
|
return { width: w || 1920, height: h || 1080 }
|
|
}
|
|
|
|
// Converts a Vue Flow handle id to the NODE_CONFIG port key used in graph JSON
|
|
function handleToPort(handle: string): string {
|
|
return handle.replace(/-(?:in|out)(?:-\d+)?$/, '_flow_id')
|
|
// 'video-out' → 'video_flow_id', 'audio-in' → 'audio_flow_id'
|
|
// 'video-in-1' → 'video_flow_id', 'audio-in-2' → 'audio_flow_id'
|
|
}
|
|
|
|
export function buildGraph(vfNodes: Node[], vfEdges: Edge[]): GraphJson {
|
|
const nodeMap = new Map(vfNodes.map(n => [n.id, n.data as StudioNodeData]))
|
|
|
|
const nodes = vfNodes.map(n => {
|
|
const d = n.data as StudioNodeData
|
|
const typeDef = NODE_TYPES[d.nodeType]
|
|
// Only include non-format params in the graph JSON node params
|
|
const params: Record<string, unknown> = {}
|
|
if (typeDef) {
|
|
for (const p of typeDef.params) {
|
|
if (!p.isFormat && d.params[p.key] != null) {
|
|
params[p.key] = d.params[p.key]
|
|
}
|
|
}
|
|
}
|
|
return { id: n.id, type: d.nodeType, params }
|
|
})
|
|
|
|
const edges = vfEdges.map((e) => {
|
|
const srcData = nodeMap.get(e.source)
|
|
const srcHandle = e.sourceHandle ?? 'video-out'
|
|
const tgtHandle = e.targetHandle ?? 'video-in'
|
|
const kind = srcHandle.startsWith('video') ? 'video' : 'audio'
|
|
|
|
let format: Record<string, unknown> = { kind }
|
|
if (kind === 'video') {
|
|
const { width, height } = parseRes(String(srcData?.params.res ?? '1920x1080'))
|
|
const { fps_num, fps_den } = parseFps(String(srcData?.params.fps ?? '25'))
|
|
format = { kind, width, height, fps_num, fps_den }
|
|
} else {
|
|
format = { kind, sample_rate: 48000, channels: 2, bit_depth: 32 }
|
|
}
|
|
|
|
return {
|
|
from: e.source, from_port: handleToPort(srcHandle),
|
|
to: e.target, to_port: handleToPort(tgtHandle),
|
|
format,
|
|
}
|
|
})
|
|
|
|
return { nodes, edges }
|
|
}
|