diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-03-27 00:55:15 +0100 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-03-29 15:56:48 +0200 |
| commit | 4d127a038bf768eded68aae3cfa5c1b12241bdac (patch) | |
| tree | 16bbb801caf6770546cff4fdb37b6408ec9ead98 /apps/web/src/services/storage/migrations/transformers/v11-to-v12.ts | |
| parent | 82817cb50cb0487350573d6129130aec705f924b (diff) | |
| download | opencut-4d127a038bf768eded68aae3cfa5c1b12241bdac.tar.gz | |
chore: normalize line endings
Diffstat (limited to 'apps/web/src/services/storage/migrations/transformers/v11-to-v12.ts')
| -rw-r--r-- | apps/web/src/services/storage/migrations/transformers/v11-to-v12.ts | 392 |
1 files changed, 196 insertions, 196 deletions
diff --git a/apps/web/src/services/storage/migrations/transformers/v11-to-v12.ts b/apps/web/src/services/storage/migrations/transformers/v11-to-v12.ts index fa1bd371..4efc5b25 100644 --- a/apps/web/src/services/storage/migrations/transformers/v11-to-v12.ts +++ b/apps/web/src/services/storage/migrations/transformers/v11-to-v12.ts @@ -1,196 +1,196 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV11ToV12({ - project, -}: { - project: ProjectRecord; -}): MigrationResult<ProjectRecord> { - if (!getProjectId({ project })) { - return { project, skipped: true, reason: "no project id" }; - } - - if (typeof project.version === "number" && project.version >= 12) { - return { project, skipped: true, reason: "already v12" }; - } - - const migratedProject = migrateProjectPosition({ project }); - - return { - project: { ...migratedProject, version: 12 }, - skipped: false, - }; -} - -function migrateProjectPosition({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) return project; - - const migratedScenes = scenesValue.map((scene) => - migrateScenePosition({ scene }), - ); - - return { ...project, scenes: migratedScenes }; -} - -function migrateScenePosition({ scene }: { scene: unknown }): unknown { - if (!isRecord(scene)) return scene; - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) return scene; - - const migratedTracks = tracksValue.map((track) => - migrateTrackPosition({ track }), - ); - - return { ...scene, tracks: migratedTracks }; -} - -function migrateTrackPosition({ track }: { track: unknown }): unknown { - if (!isRecord(track)) return track; - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) return track; - - const migratedElements = elementsValue.map((element) => - migrateElementPosition({ element }), - ); - - return { ...track, elements: migratedElements }; -} - -function migrateElementPosition({ element }: { element: unknown }): unknown { - if (!isRecord(element)) return element; - - const animations = element.animations; - if (!isRecord(animations) || !isRecord(animations.channels)) return element; - - const channels = animations.channels as Record<string, unknown>; - const xChannel = channels["transform.position.x"]; - const yChannel = channels["transform.position.y"]; - - if (!xChannel && !yChannel) return element; - - const baseTransform = isRecord(element.transform) ? element.transform : {}; - const basePosition = isRecord(baseTransform.position) - ? baseTransform.position - : {}; - const baseX = typeof basePosition.x === "number" ? basePosition.x : 0; - const baseY = typeof basePosition.y === "number" ? basePosition.y : 0; - - const xKeyframes = getKeyframes({ channel: xChannel }); - const yKeyframes = getKeyframes({ channel: yChannel }); - - const allTimes = Array.from( - new Set([ - ...xKeyframes.map((kf) => kf.time), - ...yKeyframes.map((kf) => kf.time), - ]), - ).sort((a, b) => a - b); - - const vectorKeyframes = allTimes.map((time) => { - const xKf = xKeyframes.find((kf) => Math.abs(kf.time - time) < 0.001); - const yKf = yKeyframes.find((kf) => Math.abs(kf.time - time) < 0.001); - const x = - typeof xKf?.value === "number" - ? xKf.value - : interpolateScalarAtTime({ - keyframes: xKeyframes, - time, - fallback: baseX, - }); - const y = - typeof yKf?.value === "number" - ? yKf.value - : interpolateScalarAtTime({ - keyframes: yKeyframes, - time, - fallback: baseY, - }); - const interpolation = xKf?.interpolation ?? yKf?.interpolation ?? "linear"; - return { - id: xKf?.id ?? yKf?.id ?? crypto.randomUUID(), - time, - value: { x, y }, - interpolation, - }; - }); - - const newChannels = { ...channels }; - delete newChannels["transform.position.x"]; - delete newChannels["transform.position.y"]; - newChannels["transform.position"] = { - valueKind: "vector", - keyframes: vectorKeyframes, - }; - - return { - ...element, - animations: { ...animations, channels: newChannels }, - }; -} - -interface ScalarKeyframe { - id: string; - time: number; - value: number; - interpolation: string; -} - -function getKeyframes({ channel }: { channel: unknown }): ScalarKeyframe[] { - if (!isRecord(channel)) return []; - if (!Array.isArray(channel.keyframes)) return []; - return channel.keyframes.flatMap((kf) => { - if (!isRecord(kf)) return []; - if ( - typeof kf.id !== "string" || - typeof kf.time !== "number" || - typeof kf.value !== "number" - ) - return []; - return [ - { - id: kf.id, - time: kf.time, - value: kf.value, - interpolation: - typeof kf.interpolation === "string" ? kf.interpolation : "linear", - }, - ]; - }); -} - -function interpolateScalarAtTime({ - keyframes, - time, - fallback, -}: { - keyframes: ScalarKeyframe[]; - time: number; - fallback: number; -}): number { - if (keyframes.length === 0) return fallback; - - const sorted = [...keyframes].sort((a, b) => a.time - b.time); - const first = sorted[0]; - const last = sorted[sorted.length - 1]; - - if (!first || !last) return fallback; - if (time <= first.time) return first.value; - if (time >= last.time) return last.value; - - for (let i = 0; i < sorted.length - 1; i++) { - const left = sorted[i]; - const right = sorted[i + 1]; - if (time < left.time || time > right.time) continue; - if (left.interpolation === "hold") return left.value; - const t = (time - left.time) / (right.time - left.time); - return left.value + (right.value - left.value) * t; - } - - return last.value; -} +import type { MigrationResult, ProjectRecord } from "./types";
+import { getProjectId, isRecord } from "./utils";
+
+export function transformProjectV11ToV12({
+ project,
+}: {
+ project: ProjectRecord;
+}): MigrationResult<ProjectRecord> {
+ if (!getProjectId({ project })) {
+ return { project, skipped: true, reason: "no project id" };
+ }
+
+ if (typeof project.version === "number" && project.version >= 12) {
+ return { project, skipped: true, reason: "already v12" };
+ }
+
+ const migratedProject = migrateProjectPosition({ project });
+
+ return {
+ project: { ...migratedProject, version: 12 },
+ skipped: false,
+ };
+}
+
+function migrateProjectPosition({
+ project,
+}: {
+ project: ProjectRecord;
+}): ProjectRecord {
+ const scenesValue = project.scenes;
+ if (!Array.isArray(scenesValue)) return project;
+
+ const migratedScenes = scenesValue.map((scene) =>
+ migrateScenePosition({ scene }),
+ );
+
+ return { ...project, scenes: migratedScenes };
+}
+
+function migrateScenePosition({ scene }: { scene: unknown }): unknown {
+ if (!isRecord(scene)) return scene;
+
+ const tracksValue = scene.tracks;
+ if (!Array.isArray(tracksValue)) return scene;
+
+ const migratedTracks = tracksValue.map((track) =>
+ migrateTrackPosition({ track }),
+ );
+
+ return { ...scene, tracks: migratedTracks };
+}
+
+function migrateTrackPosition({ track }: { track: unknown }): unknown {
+ if (!isRecord(track)) return track;
+
+ const elementsValue = track.elements;
+ if (!Array.isArray(elementsValue)) return track;
+
+ const migratedElements = elementsValue.map((element) =>
+ migrateElementPosition({ element }),
+ );
+
+ return { ...track, elements: migratedElements };
+}
+
+function migrateElementPosition({ element }: { element: unknown }): unknown {
+ if (!isRecord(element)) return element;
+
+ const animations = element.animations;
+ if (!isRecord(animations) || !isRecord(animations.channels)) return element;
+
+ const channels = animations.channels as Record<string, unknown>;
+ const xChannel = channels["transform.position.x"];
+ const yChannel = channels["transform.position.y"];
+
+ if (!xChannel && !yChannel) return element;
+
+ const baseTransform = isRecord(element.transform) ? element.transform : {};
+ const basePosition = isRecord(baseTransform.position)
+ ? baseTransform.position
+ : {};
+ const baseX = typeof basePosition.x === "number" ? basePosition.x : 0;
+ const baseY = typeof basePosition.y === "number" ? basePosition.y : 0;
+
+ const xKeyframes = getKeyframes({ channel: xChannel });
+ const yKeyframes = getKeyframes({ channel: yChannel });
+
+ const allTimes = Array.from(
+ new Set([
+ ...xKeyframes.map((kf) => kf.time),
+ ...yKeyframes.map((kf) => kf.time),
+ ]),
+ ).sort((a, b) => a - b);
+
+ const vectorKeyframes = allTimes.map((time) => {
+ const xKf = xKeyframes.find((kf) => Math.abs(kf.time - time) < 0.001);
+ const yKf = yKeyframes.find((kf) => Math.abs(kf.time - time) < 0.001);
+ const x =
+ typeof xKf?.value === "number"
+ ? xKf.value
+ : interpolateScalarAtTime({
+ keyframes: xKeyframes,
+ time,
+ fallback: baseX,
+ });
+ const y =
+ typeof yKf?.value === "number"
+ ? yKf.value
+ : interpolateScalarAtTime({
+ keyframes: yKeyframes,
+ time,
+ fallback: baseY,
+ });
+ const interpolation = xKf?.interpolation ?? yKf?.interpolation ?? "linear";
+ return {
+ id: xKf?.id ?? yKf?.id ?? crypto.randomUUID(),
+ time,
+ value: { x, y },
+ interpolation,
+ };
+ });
+
+ const newChannels = { ...channels };
+ delete newChannels["transform.position.x"];
+ delete newChannels["transform.position.y"];
+ newChannels["transform.position"] = {
+ valueKind: "vector",
+ keyframes: vectorKeyframes,
+ };
+
+ return {
+ ...element,
+ animations: { ...animations, channels: newChannels },
+ };
+}
+
+interface ScalarKeyframe {
+ id: string;
+ time: number;
+ value: number;
+ interpolation: string;
+}
+
+function getKeyframes({ channel }: { channel: unknown }): ScalarKeyframe[] {
+ if (!isRecord(channel)) return [];
+ if (!Array.isArray(channel.keyframes)) return [];
+ return channel.keyframes.flatMap((kf) => {
+ if (!isRecord(kf)) return [];
+ if (
+ typeof kf.id !== "string" ||
+ typeof kf.time !== "number" ||
+ typeof kf.value !== "number"
+ )
+ return [];
+ return [
+ {
+ id: kf.id,
+ time: kf.time,
+ value: kf.value,
+ interpolation:
+ typeof kf.interpolation === "string" ? kf.interpolation : "linear",
+ },
+ ];
+ });
+}
+
+function interpolateScalarAtTime({
+ keyframes,
+ time,
+ fallback,
+}: {
+ keyframes: ScalarKeyframe[];
+ time: number;
+ fallback: number;
+}): number {
+ if (keyframes.length === 0) return fallback;
+
+ const sorted = [...keyframes].sort((a, b) => a.time - b.time);
+ const first = sorted[0];
+ const last = sorted[sorted.length - 1];
+
+ if (!first || !last) return fallback;
+ if (time <= first.time) return first.value;
+ if (time >= last.time) return last.value;
+
+ for (let i = 0; i < sorted.length - 1; i++) {
+ const left = sorted[i];
+ const right = sorted[i + 1];
+ if (time < left.time || time > right.time) continue;
+ if (left.interpolation === "hold") return left.value;
+ const t = (time - left.time) / (right.time - left.time);
+ return left.value + (right.value - left.value) * t;
+ }
+
+ return last.value;
+}
|
