mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
fix: repair scene tracks broken by v23-to-v24 migration
This commit is contained in:
@@ -24,10 +24,11 @@ import { V21toV22Migration } from "./v21-to-v22";
|
|||||||
import { V22toV23Migration } from "./v22-to-v23";
|
import { V22toV23Migration } from "./v22-to-v23";
|
||||||
import { V23toV24Migration } from "./v23-to-v24";
|
import { V23toV24Migration } from "./v23-to-v24";
|
||||||
import { V24toV25Migration } from "./v24-to-v25";
|
import { V24toV25Migration } from "./v24-to-v25";
|
||||||
|
import { V25toV26Migration } from "./v25-to-v26";
|
||||||
export { runStorageMigrations } from "./runner";
|
export { runStorageMigrations } from "./runner";
|
||||||
export type { MigrationProgress } from "./runner";
|
export type { MigrationProgress } from "./runner";
|
||||||
|
|
||||||
export const CURRENT_PROJECT_VERSION = 25;
|
export const CURRENT_PROJECT_VERSION = 26;
|
||||||
|
|
||||||
export const migrations = [
|
export const migrations = [
|
||||||
new V0toV1Migration(),
|
new V0toV1Migration(),
|
||||||
@@ -55,4 +56,5 @@ export const migrations = [
|
|||||||
new V22toV23Migration(),
|
new V22toV23Migration(),
|
||||||
new V23toV24Migration(),
|
new V23toV24Migration(),
|
||||||
new V24toV25Migration(),
|
new V24toV25Migration(),
|
||||||
|
new V25toV26Migration(),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -51,9 +51,7 @@ function migrateScene({ scene }: { scene: unknown }): unknown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mainTrack = findMainTrack({ tracks: scene.tracks });
|
const mainTrack = findMainTrack({ tracks: scene.tracks });
|
||||||
if (!mainTrack) {
|
const finalMainTrack = mainTrack ?? buildEmptyMainTrack();
|
||||||
return scene;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...scene,
|
...scene,
|
||||||
@@ -65,7 +63,7 @@ function migrateScene({ scene }: { scene: unknown }): unknown {
|
|||||||
(track): track is ProjectRecord =>
|
(track): track is ProjectRecord =>
|
||||||
isRecord(track) && track.type !== "audio",
|
isRecord(track) && track.type !== "audio",
|
||||||
),
|
),
|
||||||
main: migrateTrack({ track: mainTrack }),
|
main: migrateTrack({ track: finalMainTrack }),
|
||||||
audio: scene.tracks
|
audio: scene.tracks
|
||||||
.map((track) => migrateTrack({ track }))
|
.map((track) => migrateTrack({ track }))
|
||||||
.filter(
|
.filter(
|
||||||
@@ -76,6 +74,17 @@ function migrateScene({ scene }: { scene: unknown }): unknown {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildEmptyMainTrack(): ProjectRecord {
|
||||||
|
return {
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
name: "Main",
|
||||||
|
type: "video",
|
||||||
|
elements: [],
|
||||||
|
muted: false,
|
||||||
|
hidden: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function findMainTrack({
|
function findMainTrack({
|
||||||
tracks,
|
tracks,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
import type { MigrationResult, ProjectRecord } from "./types";
|
||||||
|
import { getProjectId, isRecord } from "./utils";
|
||||||
|
|
||||||
|
export function transformProjectV25ToV26({
|
||||||
|
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 >= 26) {
|
||||||
|
return { project, skipped: true, reason: "already v26" };
|
||||||
|
}
|
||||||
|
if (version !== 25) {
|
||||||
|
return { project, skipped: true, reason: "not v25" };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
project: {
|
||||||
|
...migrateProject({ project }),
|
||||||
|
version: 26,
|
||||||
|
},
|
||||||
|
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)) {
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tracks = scene.tracks;
|
||||||
|
|
||||||
|
// Already in the correct shape — nothing to repair
|
||||||
|
if (isRecord(tracks) && Array.isArray(tracks.overlay) && Array.isArray(tracks.audio)) {
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconstruct the flat track array from whatever broken state it's in.
|
||||||
|
// v24-to-v25 spread a flat array into a numeric-keyed object, so
|
||||||
|
// Object.values recovers the original elements. A true flat array is
|
||||||
|
// also handled for safety.
|
||||||
|
let trackArray: unknown[];
|
||||||
|
if (Array.isArray(tracks)) {
|
||||||
|
trackArray = tracks;
|
||||||
|
} else if (isRecord(tracks)) {
|
||||||
|
trackArray = Object.values(tracks);
|
||||||
|
} else {
|
||||||
|
trackArray = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const mainTrack = findMainTrack({ tracks: trackArray });
|
||||||
|
const finalMainTrack = mainTrack ?? buildEmptyMainTrack();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...scene,
|
||||||
|
tracks: {
|
||||||
|
main: migrateTrack({ track: finalMainTrack }),
|
||||||
|
overlay: trackArray
|
||||||
|
.filter((track) => track !== mainTrack)
|
||||||
|
.map((track) => migrateTrack({ track }))
|
||||||
|
.filter(
|
||||||
|
(track): track is ProjectRecord =>
|
||||||
|
isRecord(track) && track.type !== "audio",
|
||||||
|
),
|
||||||
|
audio: trackArray
|
||||||
|
.map((track) => migrateTrack({ track }))
|
||||||
|
.filter(
|
||||||
|
(track): track is ProjectRecord =>
|
||||||
|
isRecord(track) && track.type === "audio",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function findMainTrack({
|
||||||
|
tracks,
|
||||||
|
}: {
|
||||||
|
tracks: unknown[];
|
||||||
|
}): ProjectRecord | null {
|
||||||
|
for (const track of tracks) {
|
||||||
|
if (!isRecord(track)) continue;
|
||||||
|
if (track.type === "video" && track.isMain === true) return track;
|
||||||
|
}
|
||||||
|
for (const track of tracks) {
|
||||||
|
if (!isRecord(track)) continue;
|
||||||
|
if (track.type === "video") return track;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateTrack({ track }: { track: unknown }): unknown {
|
||||||
|
if (!isRecord(track)) return track;
|
||||||
|
const nextTrack = { ...track };
|
||||||
|
delete nextTrack.isMain;
|
||||||
|
return nextTrack;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildEmptyMainTrack(): ProjectRecord {
|
||||||
|
return {
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
name: "Main",
|
||||||
|
type: "video",
|
||||||
|
elements: [],
|
||||||
|
muted: false,
|
||||||
|
hidden: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { StorageMigration } from "./base";
|
||||||
|
import type { ProjectRecord } from "./transformers/types";
|
||||||
|
import { transformProjectV25ToV26 } from "./transformers/v25-to-v26";
|
||||||
|
|
||||||
|
export class V25toV26Migration extends StorageMigration {
|
||||||
|
from = 25;
|
||||||
|
to = 26;
|
||||||
|
|
||||||
|
async transform(project: ProjectRecord): Promise<{
|
||||||
|
project: ProjectRecord;
|
||||||
|
skipped: boolean;
|
||||||
|
reason?: string;
|
||||||
|
}> {
|
||||||
|
return transformProjectV25ToV26({ project });
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user