summaryrefslogtreecommitdiff
path: root/apps/web/src/services/storage/migrations/v1-to-v2.ts
blob: d593d8a1f11a1f6e8639972b7c7079ab2ea42d3c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import {
	IndexedDBAdapter,
	deleteDatabase,
} from "@/services/storage/indexeddb-adapter";
import type { MediaAssetData } from "@/services/storage/types";
import { StorageMigration } from "./base";
import {
	getProjectId,
	transformProjectV1ToV2,
	type TransformV1ToV2Options,
} from "./transformers/v1-to-v2";

export class V1toV2Migration extends StorageMigration {
	from = 1;
	to = 2;

	async run(): Promise<void> {
		const projectsAdapter = new IndexedDBAdapter<unknown>(
			"video-editor-projects",
			"projects",
			1,
		);
		const projects = await projectsAdapter.getAll();

		for (const project of projects) {
			if (typeof project !== "object" || project === null) {
				continue;
			}

			const projectId = getProjectId({
				project: project as Record<string, unknown>,
			});
			if (!projectId) {
				continue;
			}

			const loadMediaAsset = createMediaAssetLoader({ projectId });

			const result = await transformProjectV1ToV2({
				project: project as Record<string, unknown>,
				options: { loadMediaAsset },
			});

			if (result.skipped) {
				continue;
			}

			await projectsAdapter.set(projectId, result.project);

			await cleanupLegacyTimelineDBs({ projectId, project: result.project });
		}
	}
}

function createMediaAssetLoader({
	projectId,
}: {
	projectId: string;
}): TransformV1ToV2Options["loadMediaAsset"] {
	return async ({ mediaId }: { mediaId: string }) => {
		const mediaMetadataAdapter = new IndexedDBAdapter<MediaAssetData>(
			`video-editor-media-${projectId}`,
			"media-metadata",
			1,
		);

		return mediaMetadataAdapter.get(mediaId);
	};
}

async function cleanupLegacyTimelineDBs({
	projectId,
	project,
}: {
	projectId: string;
	project: Record<string, unknown>;
}): Promise<void> {
	const scenes = project.scenes;
	if (!Array.isArray(scenes)) {
		return;
	}

	const dbNamesToDelete: string[] = [];

	for (const scene of scenes) {
		if (typeof scene !== "object" || scene === null) {
			continue;
		}

		const sceneId = scene.id;
		if (typeof sceneId === "string") {
			const sceneDbName = `video-editor-timelines-${projectId}-${sceneId}`;
			dbNamesToDelete.push(sceneDbName);
		}
	}

	const projectDbName = `video-editor-timelines-${projectId}`;
	dbNamesToDelete.push(projectDbName);

	for (const dbName of dbNamesToDelete) {
		try {
			await deleteDatabase({ dbName });
		} catch {
			// ignore errors, DB might not exist or already deleted
		}
	}
}