From 4d127a038bf768eded68aae3cfa5c1b12241bdac Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Fri, 27 Mar 2026 00:55:15 +0100 Subject: chore: normalize line endings --- apps/web/src/services/renderer/scene-builder.ts | 544 ++++++++++++------------ 1 file changed, 272 insertions(+), 272 deletions(-) (limited to 'apps/web/src/services/renderer/scene-builder.ts') diff --git a/apps/web/src/services/renderer/scene-builder.ts b/apps/web/src/services/renderer/scene-builder.ts index 7d5f9fe9..1c3e6ad2 100644 --- a/apps/web/src/services/renderer/scene-builder.ts +++ b/apps/web/src/services/renderer/scene-builder.ts @@ -1,272 +1,272 @@ -import type { TimelineTrack } from "@/lib/timeline"; -import type { MediaAsset } from "@/lib/media/types"; -import { RootNode } from "./nodes/root-node"; -import { VideoNode } from "./nodes/video-node"; -import { ImageNode } from "./nodes/image-node"; -import { TextNode } from "./nodes/text-node"; -import { StickerNode } from "./nodes/sticker-node"; -import { GraphicNode } from "./nodes/graphic-node"; -import { ColorNode } from "./nodes/color-node"; -import { BlurBackgroundNode } from "./nodes/blur-background-node"; -import { EffectLayerNode } from "./nodes/effect-layer-node"; -import type { BaseNode } from "./nodes/base-node"; -import type { TBackground, TCanvasSize } from "@/lib/project/types"; -import { DEFAULT_BLUR_INTENSITY } from "@/constants/project-constants"; -import { isMainTrack } from "@/lib/timeline"; - -const PREVIEW_MAX_IMAGE_SIZE = 2048; - -function getVisibleSortedElements({ track }: { track: TimelineTrack }) { - return track.elements - .filter((element) => !("hidden" in element && element.hidden)) - .slice() - .sort((a, b) => { - if (a.startTime !== b.startTime) return a.startTime - b.startTime; - return a.id.localeCompare(b.id); - }); -} - -function buildTrackNodes({ - tracks, - mediaMap, - canvasSize, - isPreview, -}: { - tracks: TimelineTrack[]; - mediaMap: Map; - canvasSize: TCanvasSize; - isPreview?: boolean; -}): BaseNode[] { - const nodes: BaseNode[] = []; - - for (const track of tracks) { - const elements = getVisibleSortedElements({ track }); - - for (const element of elements) { - if (element.type === "effect") { - nodes.push( - new EffectLayerNode({ - effectType: element.effectType, - effectParams: element.params, - timeOffset: element.startTime, - duration: element.duration, - }), - ); - continue; - } - - if (element.type === "video" || element.type === "image") { - const mediaAsset = mediaMap.get(element.mediaId); - if (!mediaAsset?.file || !mediaAsset?.url) { - continue; - } - - if (element.type === "video" && mediaAsset.type === "video") { - nodes.push( - new VideoNode({ - mediaId: mediaAsset.id, - url: mediaAsset.url, - file: mediaAsset.file, - duration: element.duration, - timeOffset: element.startTime, - trimStart: element.trimStart, - trimEnd: element.trimEnd, - retime: element.retime, - transform: element.transform, - animations: element.animations, - opacity: element.opacity, - blendMode: element.blendMode, - effects: element.effects, - masks: element.masks, - }), - ); - } - if (element.type === "image" && mediaAsset.type === "image") { - nodes.push( - new ImageNode({ - url: mediaAsset.url, - duration: element.duration, - timeOffset: element.startTime, - trimStart: element.trimStart, - trimEnd: element.trimEnd, - transform: element.transform, - animations: element.animations, - opacity: element.opacity, - blendMode: element.blendMode, - effects: element.effects, - masks: element.masks, - ...(isPreview && { - maxSourceSize: PREVIEW_MAX_IMAGE_SIZE, - }), - }), - ); - } - } - - if (element.type === "text") { - nodes.push( - new TextNode({ - ...element, - canvasCenter: { x: canvasSize.width / 2, y: canvasSize.height / 2 }, - canvasHeight: canvasSize.height, - textBaseline: "middle", - effects: element.effects, - }), - ); - } - - if (element.type === "sticker") { - nodes.push( - new StickerNode({ - stickerId: element.stickerId, - intrinsicWidth: element.intrinsicWidth, - intrinsicHeight: element.intrinsicHeight, - duration: element.duration, - timeOffset: element.startTime, - trimStart: element.trimStart, - trimEnd: element.trimEnd, - transform: element.transform, - animations: element.animations, - opacity: element.opacity, - blendMode: element.blendMode, - effects: element.effects, - }), - ); - } - - if (element.type === "graphic") { - nodes.push( - new GraphicNode({ - definitionId: element.definitionId, - params: element.params, - duration: element.duration, - timeOffset: element.startTime, - trimStart: element.trimStart, - trimEnd: element.trimEnd, - transform: element.transform, - animations: element.animations, - opacity: element.opacity, - blendMode: element.blendMode, - effects: element.effects, - masks: element.masks, - }), - ); - } - } - } - - return nodes; -} - -function buildBlurBackgroundNodes({ - track, - mediaMap, - blurIntensity, -}: { - track: TimelineTrack | undefined; - mediaMap: Map; - blurIntensity: number; -}): BaseNode[] { - if (!track) { - return []; - } - - const nodes: BaseNode[] = []; - const elements = getVisibleSortedElements({ track }); - - for (const element of elements) { - if (element.type !== "video" && element.type !== "image") { - continue; - } - - const mediaAsset = mediaMap.get(element.mediaId); - if ( - !mediaAsset?.file || - !mediaAsset?.url || - (mediaAsset.type !== "video" && mediaAsset.type !== "image") - ) { - continue; - } - - nodes.push( - new BlurBackgroundNode({ - mediaId: mediaAsset.id, - url: mediaAsset.url, - file: mediaAsset.file, - mediaType: mediaAsset.type, - duration: element.duration, - timeOffset: element.startTime, - trimStart: element.trimStart, - trimEnd: element.trimEnd, - retime: element.type === "video" ? element.retime : undefined, - blurIntensity, - }), - ); - } - - return nodes; -} - -export type BuildSceneParams = { - canvasSize: TCanvasSize; - tracks: TimelineTrack[]; - mediaAssets: MediaAsset[]; - duration: number; - background: TBackground; - isPreview?: boolean; -}; - -export function buildScene({ - canvasSize, - tracks, - mediaAssets, - duration, - background, - isPreview, -}: BuildSceneParams) { - const rootNode = new RootNode({ duration }); - const mediaMap = new Map(mediaAssets.map((m) => [m.id, m])); - - const visibleTracks = tracks.filter( - (track) => !("hidden" in track && track.hidden), - ); - - const orderedTracksTopToBottom = [ - ...visibleTracks.filter((track) => !isMainTrack(track)), - ...visibleTracks.filter((track) => isMainTrack(track)), - ]; - - const orderedTracksBottomToTop = orderedTracksTopToBottom.slice().reverse(); - const mainTrack = orderedTracksBottomToTop.find((track) => - isMainTrack(track), - ); - - const allNodes = buildTrackNodes({ - tracks: orderedTracksBottomToTop, - mediaMap, - canvasSize, - isPreview, - }); - - if (background.type === "blur") { - const blurNodes = buildBlurBackgroundNodes({ - track: mainTrack, - mediaMap, - blurIntensity: background.blurIntensity ?? DEFAULT_BLUR_INTENSITY, - }); - for (const node of blurNodes) { - rootNode.add(node); - } - } else if ( - background.type === "color" && - background.color !== "transparent" - ) { - rootNode.add(new ColorNode({ color: background.color })); - } - - for (const node of allNodes) { - rootNode.add(node); - } - - return rootNode; -} +import type { TimelineTrack } from "@/lib/timeline"; +import type { MediaAsset } from "@/lib/media/types"; +import { RootNode } from "./nodes/root-node"; +import { VideoNode } from "./nodes/video-node"; +import { ImageNode } from "./nodes/image-node"; +import { TextNode } from "./nodes/text-node"; +import { StickerNode } from "./nodes/sticker-node"; +import { GraphicNode } from "./nodes/graphic-node"; +import { ColorNode } from "./nodes/color-node"; +import { BlurBackgroundNode } from "./nodes/blur-background-node"; +import { EffectLayerNode } from "./nodes/effect-layer-node"; +import type { BaseNode } from "./nodes/base-node"; +import type { TBackground, TCanvasSize } from "@/lib/project/types"; +import { DEFAULT_BLUR_INTENSITY } from "@/constants/project-constants"; +import { isMainTrack } from "@/lib/timeline"; + +const PREVIEW_MAX_IMAGE_SIZE = 2048; + +function getVisibleSortedElements({ track }: { track: TimelineTrack }) { + return track.elements + .filter((element) => !("hidden" in element && element.hidden)) + .slice() + .sort((a, b) => { + if (a.startTime !== b.startTime) return a.startTime - b.startTime; + return a.id.localeCompare(b.id); + }); +} + +function buildTrackNodes({ + tracks, + mediaMap, + canvasSize, + isPreview, +}: { + tracks: TimelineTrack[]; + mediaMap: Map; + canvasSize: TCanvasSize; + isPreview?: boolean; +}): BaseNode[] { + const nodes: BaseNode[] = []; + + for (const track of tracks) { + const elements = getVisibleSortedElements({ track }); + + for (const element of elements) { + if (element.type === "effect") { + nodes.push( + new EffectLayerNode({ + effectType: element.effectType, + effectParams: element.params, + timeOffset: element.startTime, + duration: element.duration, + }), + ); + continue; + } + + if (element.type === "video" || element.type === "image") { + const mediaAsset = mediaMap.get(element.mediaId); + if (!mediaAsset?.file || !mediaAsset?.url) { + continue; + } + + if (element.type === "video" && mediaAsset.type === "video") { + nodes.push( + new VideoNode({ + mediaId: mediaAsset.id, + url: mediaAsset.url, + file: mediaAsset.file, + duration: element.duration, + timeOffset: element.startTime, + trimStart: element.trimStart, + trimEnd: element.trimEnd, + retime: element.retime, + transform: element.transform, + animations: element.animations, + opacity: element.opacity, + blendMode: element.blendMode, + effects: element.effects, + masks: element.masks, + }), + ); + } + if (element.type === "image" && mediaAsset.type === "image") { + nodes.push( + new ImageNode({ + url: mediaAsset.url, + duration: element.duration, + timeOffset: element.startTime, + trimStart: element.trimStart, + trimEnd: element.trimEnd, + transform: element.transform, + animations: element.animations, + opacity: element.opacity, + blendMode: element.blendMode, + effects: element.effects, + masks: element.masks, + ...(isPreview && { + maxSourceSize: PREVIEW_MAX_IMAGE_SIZE, + }), + }), + ); + } + } + + if (element.type === "text") { + nodes.push( + new TextNode({ + ...element, + canvasCenter: { x: canvasSize.width / 2, y: canvasSize.height / 2 }, + canvasHeight: canvasSize.height, + textBaseline: "middle", + effects: element.effects, + }), + ); + } + + if (element.type === "sticker") { + nodes.push( + new StickerNode({ + stickerId: element.stickerId, + intrinsicWidth: element.intrinsicWidth, + intrinsicHeight: element.intrinsicHeight, + duration: element.duration, + timeOffset: element.startTime, + trimStart: element.trimStart, + trimEnd: element.trimEnd, + transform: element.transform, + animations: element.animations, + opacity: element.opacity, + blendMode: element.blendMode, + effects: element.effects, + }), + ); + } + + if (element.type === "graphic") { + nodes.push( + new GraphicNode({ + definitionId: element.definitionId, + params: element.params, + duration: element.duration, + timeOffset: element.startTime, + trimStart: element.trimStart, + trimEnd: element.trimEnd, + transform: element.transform, + animations: element.animations, + opacity: element.opacity, + blendMode: element.blendMode, + effects: element.effects, + masks: element.masks, + }), + ); + } + } + } + + return nodes; +} + +function buildBlurBackgroundNodes({ + track, + mediaMap, + blurIntensity, +}: { + track: TimelineTrack | undefined; + mediaMap: Map; + blurIntensity: number; +}): BaseNode[] { + if (!track) { + return []; + } + + const nodes: BaseNode[] = []; + const elements = getVisibleSortedElements({ track }); + + for (const element of elements) { + if (element.type !== "video" && element.type !== "image") { + continue; + } + + const mediaAsset = mediaMap.get(element.mediaId); + if ( + !mediaAsset?.file || + !mediaAsset?.url || + (mediaAsset.type !== "video" && mediaAsset.type !== "image") + ) { + continue; + } + + nodes.push( + new BlurBackgroundNode({ + mediaId: mediaAsset.id, + url: mediaAsset.url, + file: mediaAsset.file, + mediaType: mediaAsset.type, + duration: element.duration, + timeOffset: element.startTime, + trimStart: element.trimStart, + trimEnd: element.trimEnd, + retime: element.type === "video" ? element.retime : undefined, + blurIntensity, + }), + ); + } + + return nodes; +} + +export type BuildSceneParams = { + canvasSize: TCanvasSize; + tracks: TimelineTrack[]; + mediaAssets: MediaAsset[]; + duration: number; + background: TBackground; + isPreview?: boolean; +}; + +export function buildScene({ + canvasSize, + tracks, + mediaAssets, + duration, + background, + isPreview, +}: BuildSceneParams) { + const rootNode = new RootNode({ duration }); + const mediaMap = new Map(mediaAssets.map((m) => [m.id, m])); + + const visibleTracks = tracks.filter( + (track) => !("hidden" in track && track.hidden), + ); + + const orderedTracksTopToBottom = [ + ...visibleTracks.filter((track) => !isMainTrack(track)), + ...visibleTracks.filter((track) => isMainTrack(track)), + ]; + + const orderedTracksBottomToTop = orderedTracksTopToBottom.slice().reverse(); + const mainTrack = orderedTracksBottomToTop.find((track) => + isMainTrack(track), + ); + + const allNodes = buildTrackNodes({ + tracks: orderedTracksBottomToTop, + mediaMap, + canvasSize, + isPreview, + }); + + if (background.type === "blur") { + const blurNodes = buildBlurBackgroundNodes({ + track: mainTrack, + mediaMap, + blurIntensity: background.blurIntensity ?? DEFAULT_BLUR_INTENSITY, + }); + for (const node of blurNodes) { + rootNode.add(node); + } + } else if ( + background.type === "color" && + background.color !== "transparent" + ) { + rootNode.add(new ColorNode({ color: background.color })); + } + + for (const node of allNodes) { + rootNode.add(node); + } + + return rootNode; +} -- cgit v1.3.1