diff options
Diffstat (limited to 'apps/web/src/core/managers/media-manager.ts')
| -rw-r--r-- | apps/web/src/core/managers/media-manager.ts | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/apps/web/src/core/managers/media-manager.ts b/apps/web/src/core/managers/media-manager.ts new file mode 100644 index 00000000..7b9db0d5 --- /dev/null +++ b/apps/web/src/core/managers/media-manager.ts @@ -0,0 +1,162 @@ +import type { EditorCore } from "@/core"; +import type { MediaAsset } from "@/types/assets"; +import { storageService } from "@/services/storage/storage-service"; +import { generateUUID } from "@/utils/id"; +import { videoCache } from "@/services/media/video-cache"; +import { hasMediaId } from "@/lib/timeline/element-utils"; + +export class MediaManager { + private assets: MediaAsset[] = []; + private isLoading = false; + private listeners = new Set<() => void>(); + + constructor(private editor: EditorCore) {} + + async addMediaAsset({ + projectId, + asset, + }: { + projectId: string; + asset: Omit<MediaAsset, "id">; + }): Promise<void> { + const newAsset: MediaAsset = { + ...asset, + id: generateUUID(), + }; + + this.assets = [...this.assets, newAsset]; + this.notify(); + + try { + await storageService.saveMediaAsset({ projectId, mediaAsset: newAsset }); + } catch (error) { + console.error("Failed to save media asset:", error); + this.assets = this.assets.filter((asset) => asset.id !== newAsset.id); + this.notify(); + } + } + + async removeMediaAsset({ + projectId, + id, + }: { + projectId: string; + id: string; + }): Promise<void> { + const asset = this.assets.find((asset) => asset.id === id); + + videoCache.clearVideo({ mediaId: id }); + + if (asset?.url) { + URL.revokeObjectURL(asset.url); + if (asset.thumbnailUrl) { + URL.revokeObjectURL(asset.thumbnailUrl); + } + } + + this.assets = this.assets.filter((asset) => asset.id !== id); + this.notify(); + + const tracks = this.editor.timeline.getTracks(); + const elementsToRemove: Array<{ trackId: string; elementId: string }> = []; + + for (const track of tracks) { + for (const element of track.elements) { + if (hasMediaId(element) && element.mediaId === id) { + elementsToRemove.push({ trackId: track.id, elementId: element.id }); + } + } + } + + if (elementsToRemove.length > 0) { + this.editor.timeline.deleteElements({ elements: elementsToRemove }); + } + + try { + await storageService.deleteMediaAsset({ projectId, id }); + } catch (error) { + console.error("Failed to delete media asset:", error); + } + } + + async loadProjectMedia({ projectId }: { projectId: string }): Promise<void> { + this.isLoading = true; + this.notify(); + + try { + const mediaAssets = await storageService.loadAllMediaAssets({ + projectId, + }); + this.assets = mediaAssets; + this.notify(); + } catch (error) { + console.error("Failed to load media assets:", error); + } finally { + this.isLoading = false; + this.notify(); + } + } + + async clearProjectMedia({ projectId }: { projectId: string }): Promise<void> { + this.assets.forEach((asset) => { + if (asset.url) { + URL.revokeObjectURL(asset.url); + } + if (asset.thumbnailUrl) { + URL.revokeObjectURL(asset.thumbnailUrl); + } + }); + + const mediaIds = this.assets.map((asset) => asset.id); + this.assets = []; + this.notify(); + + try { + await Promise.all( + mediaIds.map((id) => + storageService.deleteMediaAsset({ projectId, id }), + ), + ); + } catch (error) { + console.error("Failed to clear media assets from storage:", error); + } + } + + clearAllAssets(): void { + videoCache.clearAll(); + + this.assets.forEach((asset) => { + if (asset.url) { + URL.revokeObjectURL(asset.url); + } + if (asset.thumbnailUrl) { + URL.revokeObjectURL(asset.thumbnailUrl); + } + }); + + this.assets = []; + this.notify(); + } + + getAssets(): MediaAsset[] { + return this.assets; + } + + setAssets({ assets }: { assets: MediaAsset[] }): void { + this.assets = assets; + this.notify(); + } + + isLoadingMedia(): boolean { + return this.isLoading; + } + + subscribe(listener: () => void): () => void { + this.listeners.add(listener); + return () => this.listeners.delete(listener); + } + + private notify(): void { + this.listeners.forEach((fn) => fn()); + } +} |
