summaryrefslogtreecommitdiff
path: root/apps/web/src/timeline/element-utils.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/timeline/element-utils.ts')
-rw-r--r--apps/web/src/timeline/element-utils.ts169
1 files changed, 76 insertions, 93 deletions
diff --git a/apps/web/src/timeline/element-utils.ts b/apps/web/src/timeline/element-utils.ts
index 676bc823..10c759d9 100644
--- a/apps/web/src/timeline/element-utils.ts
+++ b/apps/web/src/timeline/element-utils.ts
@@ -11,7 +11,6 @@ import {
type CreateStickerElement,
type CreateUploadAudioElement,
type CreateLibraryAudioElement,
- type TextBackground,
type TextElement,
type SceneTracks,
type TimelineElement,
@@ -28,7 +27,12 @@ import type { MediaType } from "@/media/types";
import { buildDefaultEffectInstance } from "@/effects";
import { buildDefaultGraphicInstance } from "@/graphics";
import type { ParamValues } from "@/params";
+import {
+ buildDefaultParamValues,
+ getBuiltInElementParams,
+} from "@/params/registry";
import { capitalizeFirstLetter } from "@/utils/string";
+import { type MediaTime, ZERO_MEDIA_TIME } from "@/wasm";
export function canElementHaveAudio(
element: TimelineElement,
@@ -86,20 +90,12 @@ export function requiresMediaId({
);
}
-function buildTextBackground(
- raw: Partial<TextBackground> | undefined,
-): TextBackground {
- const color = raw?.color ?? DEFAULTS.text.element.background.color;
- const enabled = raw?.enabled ?? color !== "transparent";
- return {
- enabled,
- color,
- cornerRadius: raw?.cornerRadius,
- paddingX: raw?.paddingX,
- paddingY: raw?.paddingY,
- offsetX: raw?.offsetX,
- offsetY: raw?.offsetY,
- };
+function buildDefaultElementParams({
+ type,
+}: {
+ type: TimelineElement["type"];
+}): ParamValues {
+ return buildDefaultParamValues(getBuiltInElementParams({ type }));
}
export function buildTextElement({
@@ -107,31 +103,21 @@ export function buildTextElement({
startTime,
}: {
raw: Partial<Omit<TextElement, "type" | "id">>;
- startTime: number;
+ startTime: MediaTime;
}): CreateTimelineElement {
const t = raw as Partial<TextElement>;
return {
type: "text",
name: t.name ?? DEFAULTS.text.element.name,
- content: t.content ?? DEFAULTS.text.element.content,
duration: t.duration ?? DEFAULT_NEW_ELEMENT_DURATION,
startTime,
- trimStart: 0,
- trimEnd: 0,
- fontSize: t.fontSize ?? DEFAULTS.text.element.fontSize,
- fontFamily: t.fontFamily ?? DEFAULTS.text.element.fontFamily,
- color: t.color ?? DEFAULTS.text.element.color,
- background: buildTextBackground(t.background),
- textAlign: t.textAlign ?? DEFAULTS.text.element.textAlign,
- fontWeight: t.fontWeight ?? DEFAULTS.text.element.fontWeight,
- fontStyle: t.fontStyle ?? DEFAULTS.text.element.fontStyle,
- textDecoration: t.textDecoration ?? DEFAULTS.text.element.textDecoration,
- letterSpacing: t.letterSpacing ?? DEFAULTS.text.element.letterSpacing,
- lineHeight: t.lineHeight ?? DEFAULTS.text.element.lineHeight,
- transform: t.transform ?? DEFAULTS.text.element.transform,
- opacity: t.opacity ?? DEFAULTS.text.element.opacity,
- blendMode: t.blendMode ?? DEFAULTS.element.blendMode,
+ trimStart: ZERO_MEDIA_TIME,
+ trimEnd: ZERO_MEDIA_TIME,
+ params: {
+ ...buildDefaultElementParams({ type: "text" }),
+ ...(t.params ?? {}),
+ },
};
}
@@ -141,8 +127,8 @@ export function buildEffectElement({
duration,
}: {
effectType: string;
- startTime: number;
- duration?: number;
+ startTime: MediaTime;
+ duration?: MediaTime;
}): CreateEffectElement {
const instance = buildDefaultEffectInstance({ effectType });
return {
@@ -152,8 +138,8 @@ export function buildEffectElement({
params: instance.params,
duration: duration ?? DEFAULT_NEW_ELEMENT_DURATION,
startTime,
- trimStart: 0,
- trimEnd: 0,
+ trimStart: ZERO_MEDIA_TIME,
+ trimEnd: ZERO_MEDIA_TIME,
};
}
@@ -166,7 +152,7 @@ export function buildStickerElement({
}: {
stickerId: string;
name?: string;
- startTime: number;
+ startTime: MediaTime;
intrinsicWidth?: number;
intrinsicHeight?: number;
}): CreateStickerElement {
@@ -180,14 +166,9 @@ export function buildStickerElement({
intrinsicHeight,
duration: DEFAULT_NEW_ELEMENT_DURATION,
startTime,
- trimStart: 0,
- trimEnd: 0,
- transform: {
- ...DEFAULTS.element.transform,
- position: { ...DEFAULTS.element.transform.position },
- },
- opacity: DEFAULTS.element.opacity,
- blendMode: DEFAULTS.element.blendMode,
+ trimStart: ZERO_MEDIA_TIME,
+ trimEnd: ZERO_MEDIA_TIME,
+ params: buildDefaultElementParams({ type: "sticker" }),
};
}
@@ -199,7 +180,7 @@ export function buildGraphicElement({
}: {
definitionId: string;
name?: string;
- startTime: number;
+ startTime: MediaTime;
params?: Partial<ParamValues>;
}): CreateGraphicElement {
const instance = buildDefaultGraphicInstance({ definitionId });
@@ -207,20 +188,36 @@ export function buildGraphicElement({
type: "graphic",
name: name ?? capitalizeFirstLetter({ string: instance.definitionId }),
definitionId: instance.definitionId,
- params: { ...instance.params, ...(params ?? {}) } as ParamValues,
+ params: mergeParamValues({
+ base: {
+ ...buildDefaultElementParams({ type: "graphic" }),
+ ...instance.params,
+ },
+ overrides: params,
+ }),
duration: DEFAULT_NEW_ELEMENT_DURATION,
startTime,
- trimStart: 0,
- trimEnd: 0,
- transform: {
- ...DEFAULTS.element.transform,
- position: { ...DEFAULTS.element.transform.position },
- },
- opacity: DEFAULTS.element.opacity,
- blendMode: DEFAULTS.element.blendMode,
+ trimStart: ZERO_MEDIA_TIME,
+ trimEnd: ZERO_MEDIA_TIME,
};
}
+function mergeParamValues({
+ base,
+ overrides,
+}: {
+ base: ParamValues;
+ overrides?: Partial<ParamValues>;
+}): ParamValues {
+ const result: ParamValues = { ...base };
+ for (const [key, value] of Object.entries(overrides ?? {})) {
+ if (value !== undefined) {
+ result[key] = value;
+ }
+ }
+ return result;
+}
+
function buildVideoElement({
mediaId,
name,
@@ -229,8 +226,8 @@ function buildVideoElement({
}: {
mediaId: string;
name: string;
- duration: number;
- startTime: number;
+ duration: MediaTime;
+ startTime: MediaTime;
}): CreateVideoElement {
return {
type: "video",
@@ -238,19 +235,12 @@ function buildVideoElement({
name,
duration,
startTime,
- trimStart: 0,
- trimEnd: 0,
+ trimStart: ZERO_MEDIA_TIME,
+ trimEnd: ZERO_MEDIA_TIME,
sourceDuration: duration,
- muted: false,
isSourceAudioEnabled: true,
hidden: false,
- transform: {
- ...DEFAULTS.element.transform,
- position: { ...DEFAULTS.element.transform.position },
- },
- opacity: DEFAULTS.element.opacity,
- blendMode: DEFAULTS.element.blendMode,
- volume: DEFAULTS.element.volume,
+ params: buildDefaultElementParams({ type: "video" }),
};
}
@@ -262,8 +252,8 @@ function buildImageElement({
}: {
mediaId: string;
name: string;
- duration: number;
- startTime: number;
+ duration: MediaTime;
+ startTime: MediaTime;
}): CreateImageElement {
return {
type: "image",
@@ -271,15 +261,10 @@ function buildImageElement({
name,
duration,
startTime,
- trimStart: 0,
- trimEnd: 0,
+ trimStart: ZERO_MEDIA_TIME,
+ trimEnd: ZERO_MEDIA_TIME,
hidden: false,
- transform: {
- ...DEFAULTS.element.transform,
- position: { ...DEFAULTS.element.transform.position },
- },
- opacity: DEFAULTS.element.opacity,
- blendMode: DEFAULTS.element.blendMode,
+ params: buildDefaultElementParams({ type: "image" }),
};
}
@@ -292,8 +277,8 @@ function buildUploadAudioElement({
}: {
mediaId: string;
name: string;
- duration: number;
- startTime: number;
+ duration: MediaTime;
+ startTime: MediaTime;
buffer?: AudioBuffer;
}): CreateUploadAudioElement {
const element: CreateUploadAudioElement = {
@@ -303,11 +288,10 @@ function buildUploadAudioElement({
name,
duration,
startTime,
- trimStart: 0,
- trimEnd: 0,
+ trimStart: ZERO_MEDIA_TIME,
+ trimEnd: ZERO_MEDIA_TIME,
sourceDuration: duration,
- volume: DEFAULTS.element.volume,
- muted: false,
+ params: buildDefaultElementParams({ type: "audio" }),
};
if (buffer) {
element.buffer = buffer;
@@ -326,8 +310,8 @@ export function buildElementFromMedia({
mediaId: string;
mediaType: MediaType;
name: string;
- duration: number;
- startTime: number;
+ duration: MediaTime;
+ startTime: MediaTime;
buffer?: AudioBuffer;
}): CreateTimelineElement {
switch (mediaType) {
@@ -355,8 +339,8 @@ export function buildLibraryAudioElement({
}: {
sourceUrl: string;
name: string;
- duration: number;
- startTime: number;
+ duration: MediaTime;
+ startTime: MediaTime;
buffer?: AudioBuffer;
}): CreateLibraryAudioElement {
const element: CreateLibraryAudioElement = {
@@ -366,11 +350,10 @@ export function buildLibraryAudioElement({
name,
duration,
startTime,
- trimStart: 0,
- trimEnd: 0,
+ trimStart: ZERO_MEDIA_TIME,
+ trimEnd: ZERO_MEDIA_TIME,
sourceDuration: duration,
- volume: DEFAULTS.element.volume,
- muted: false,
+ params: buildDefaultElementParams({ type: "audio" }),
};
if (buffer) {
element.buffer = buffer;
@@ -410,8 +393,8 @@ export function getElementFontFamilies({
const families = new Set<string>();
for (const track of [...tracks.overlay, tracks.main, ...tracks.audio]) {
for (const element of track.elements) {
- if (element.type === "text" && element.fontFamily) {
- families.add(element.fontFamily);
+ if (element.type === "text" && typeof element.params.fontFamily === "string") {
+ families.add(element.params.fontFamily);
}
if ("masks" in element) {
for (const mask of element.masks ?? []) {