fix: missing media files when renaming + saving the project

This commit is contained in:
Xiaohan-Tian
2026-04-10 12:14:20 -07:00
parent 6080accd3a
commit 83f4bfe47f
7 changed files with 167 additions and 52 deletions
+15 -2
View File
@@ -7,13 +7,14 @@ import { OPFS_CONSTANTS } from '../../constants/coreConstants';
export class KGAudioFileStorage {
/**
* Store an audio file in the project's media/ directory.
* Creates the project and media directories if they don't exist yet.
*/
public static async storeAudioFile(
projectName: string,
fileId: string,
file: File
): Promise<void> {
const mediaDir = await KGAudioFileStorage.getMediaDir(projectName);
const mediaDir = await KGAudioFileStorage.getOrCreateMediaDir(projectName);
const fileHandle = await mediaDir.getFileHandle(fileId, { create: true });
const writable = await fileHandle.createWritable();
await writable.write(await file.arrayBuffer());
@@ -61,7 +62,8 @@ export class KGAudioFileStorage {
}
/**
* Get the media directory handle for a project.
* Get the media directory handle for an existing project (read/delete path).
* Does NOT create directories — fails if project folder doesn't exist.
*/
private static async getMediaDir(projectName: string): Promise<FileSystemDirectoryHandle> {
const root = await navigator.storage.getDirectory();
@@ -69,4 +71,15 @@ export class KGAudioFileStorage {
const projectDir = await projectsDir.getDirectoryHandle(projectName);
return projectDir.getDirectoryHandle(OPFS_CONSTANTS.MEDIA_DIR, { create: true });
}
/**
* Get or create the media directory handle for a project (write path).
* Creates project and media directories if they don't exist.
*/
private static async getOrCreateMediaDir(projectName: string): Promise<FileSystemDirectoryHandle> {
const root = await navigator.storage.getDirectory();
const projectsDir = await root.getDirectoryHandle(OPFS_CONSTANTS.ROOT_DIR, { create: true });
const projectDir = await projectsDir.getDirectoryHandle(projectName, { create: true });
return projectDir.getDirectoryHandle(OPFS_CONSTANTS.MEDIA_DIR, { create: true });
}
}
+57
View File
@@ -206,10 +206,31 @@ export class KGProjectStorage {
project.setName(newName);
await this.save(newName, project, false);
// Copy media files from old to new location
await this.copyMediaFiles(oldName, newName);
// Delete old location
await this.delete(oldName);
}
/**
* Save a project under a new name, migrating media files from the old folder.
* Used when the user renames the project and saves. Handles the case where the
* old folder doesn't exist yet (new project never saved).
*/
public async saveWithRename(oldName: string, newName: string, data: KGProject): Promise<void> {
this.ensureInitialized();
// Save project JSON to the new folder
await this.save(newName, data, false);
// Migrate media files only if the old folder exists
if (await this.exists(oldName)) {
await this.copyMediaFiles(oldName, newName);
await this.delete(oldName);
}
}
/**
* Export a project folder as a zip Blob (.kgstudio bundle).
* Includes project.json, meta.json, and all files in media/.
@@ -374,6 +395,42 @@ export class KGProjectStorage {
return candidate;
}
// --- Media migration ---
/**
* Copy all files from projects/<fromName>/media/ to projects/<toName>/media/.
* If the source media directory doesn't exist, returns without error.
*/
private async copyMediaFiles(fromName: string, toName: string): Promise<void> {
try {
const fromDir = await this.projectsDirHandle!.getDirectoryHandle(fromName);
let fromMedia: FileSystemDirectoryHandle;
try {
fromMedia = await fromDir.getDirectoryHandle(OPFS_CONSTANTS.MEDIA_DIR);
} catch {
// No media directory in source — nothing to copy
return;
}
const toDir = await this.projectsDirHandle!.getDirectoryHandle(toName);
const toMedia = await toDir.getDirectoryHandle(OPFS_CONSTANTS.MEDIA_DIR, { create: true });
for await (const entry of fromMedia.values()) {
if (entry.kind === 'file') {
const fileHandle = entry as FileSystemFileHandle;
const file = await fileHandle.getFile();
const newHandle = await toMedia.getFileHandle(entry.name, { create: true });
const writable = await newHandle.createWritable();
await writable.write(await file.arrayBuffer());
await writable.close();
}
}
} catch (error) {
console.error(`Error copying media files from "${fromName}" to "${toName}":`, error);
throw error;
}
}
// --- File I/O helpers ---
private async writeFile(