diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-13 04:40:38 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-13 05:01:51 +0200 |
| commit | 108d3f4ecad4c0ead14941c2067a21546750fedc (patch) | |
| tree | 8969775b3292ac2570b95e7848774958caadbc8b /apps/web/src/services/storage/migrations/transformers | |
| parent | 8230faf082c405c9fd0fa640f4ce2284315782ae (diff) | |
| download | opencut-108d3f4ecad4c0ead14941c2067a21546750fedc.tar.gz | |
feat: position animated independently per axis
Made-with: Cursor
Diffstat (limited to 'apps/web/src/services/storage/migrations/transformers')
| -rw-r--r-- | apps/web/src/services/storage/migrations/transformers/v24-to-v25.ts | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/apps/web/src/services/storage/migrations/transformers/v24-to-v25.ts b/apps/web/src/services/storage/migrations/transformers/v24-to-v25.ts new file mode 100644 index 00000000..b3b48f42 --- /dev/null +++ b/apps/web/src/services/storage/migrations/transformers/v24-to-v25.ts @@ -0,0 +1,140 @@ +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV24ToV25({ + project, +}: { + project: ProjectRecord; +}): MigrationResult<ProjectRecord> { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + const version = project.version; + if (typeof version !== "number") { + return { project, skipped: true, reason: "invalid version" }; + } + if (version >= 25) { + return { project, skipped: true, reason: "already v25" }; + } + if (version !== 24) { + return { project, skipped: true, reason: "not v24" }; + } + + return { + project: { + ...migrateProject({ project }), + version: 25, + }, + skipped: false, + }; +} + +function migrateProject({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + if (!Array.isArray(project.scenes)) { + return project; + } + + return { + ...project, + scenes: project.scenes.map((scene) => migrateScene({ scene })), + }; +} + +function migrateScene({ scene }: { scene: unknown }): unknown { + if (!isRecord(scene) || !isRecord(scene.tracks)) { + return scene; + } + + const tracks = scene.tracks; + const nextTracks: ProjectRecord = { ...tracks }; + + if (isRecord(tracks.main)) { + nextTracks.main = migrateTrack({ track: tracks.main }); + } + + if (Array.isArray(tracks.overlay)) { + nextTracks.overlay = tracks.overlay.map((track) => + migrateTrack({ track }), + ); + } + + if (Array.isArray(tracks.audio)) { + nextTracks.audio = tracks.audio.map((track) => migrateTrack({ track })); + } + + return { ...scene, tracks: nextTracks }; +} + +function migrateTrack({ track }: { track: unknown }): unknown { + if (!isRecord(track) || !Array.isArray(track.elements)) { + return track; + } + + return { + ...track, + elements: track.elements.map((element) => migrateElement({ element })), + }; +} + +function migrateElement({ element }: { element: unknown }): unknown { + if (!isRecord(element) || !isRecord(element.animations)) { + return element; + } + + const nextAnimations = migrateAnimations({ animations: element.animations }); + if (nextAnimations === element.animations) { + return element; + } + + return { ...element, animations: nextAnimations }; +} + +function migrateAnimations({ + animations, +}: { + animations: ProjectRecord; +}): ProjectRecord { + if (!isRecord(animations.bindings) || !isRecord(animations.channels)) { + return animations; + } + + const positionBinding = animations.bindings["transform.position"]; + if (!isRecord(positionBinding) || positionBinding.kind !== "vector2") { + return animations; + } + + const xChannel = animations.channels["transform.position:x"]; + const yChannel = animations.channels["transform.position:y"]; + + const nextBindings: ProjectRecord = { ...animations.bindings }; + const nextChannels: ProjectRecord = { ...animations.channels }; + + delete nextBindings["transform.position"]; + delete nextChannels["transform.position:x"]; + delete nextChannels["transform.position:y"]; + + nextBindings["transform.positionX"] = { + path: "transform.positionX", + kind: "number", + components: [{ key: "value", channelId: "transform.positionX:value" }], + }; + nextBindings["transform.positionY"] = { + path: "transform.positionY", + kind: "number", + components: [{ key: "value", channelId: "transform.positionY:value" }], + }; + + if (isRecord(xChannel)) { + nextChannels["transform.positionX:value"] = xChannel; + } + if (isRecord(yChannel)) { + nextChannels["transform.positionY:value"] = yChannel; + } + + return { ...animations, bindings: nextBindings, channels: nextChannels }; +} |
