OpenCut/apps/web/src/commands/media/remove-media-asset.ts

120 lines
3.1 KiB
TypeScript

import { Command, type CommandResult } from "@/commands/base-command";
import { EditorCore } from "@/core";
import type { MediaAsset } from "@/media/types";
import { buildWaveformSourceKey } from "@/media/waveform-summary";
import { storageService } from "@/services/storage/service";
import { videoCache } from "@/services/video-cache/service";
import { waveformCache } from "@/services/waveform-cache/service";
import { hasMediaId } from "@/timeline/element-utils";
import type { SceneTracks } from "@/timeline";
export class RemoveMediaAssetCommand extends Command {
private savedAssets: MediaAsset[] | null = null;
private savedTracks: SceneTracks | null = null;
private removedAsset: MediaAsset | null = null;
constructor({
projectId,
assetId,
}: {
projectId: string;
assetId: string;
}) {
super();
this.projectId = projectId;
this.assetId = assetId;
}
private projectId: string;
private assetId: string;
execute(): CommandResult | undefined {
const editor = EditorCore.getInstance();
const assets = editor.media.getAssets();
this.savedAssets = [...assets];
this.savedTracks = editor.scenes.getActiveScene().tracks;
this.removedAsset =
assets.find((media) => media.id === this.assetId) ?? null;
if (!this.removedAsset) {
console.error("Media asset not found:", this.assetId);
return;
}
if (this.removedAsset.url) {
URL.revokeObjectURL(this.removedAsset.url);
}
if (this.removedAsset.thumbnailUrl) {
URL.revokeObjectURL(this.removedAsset.thumbnailUrl);
}
videoCache.clearVideo({ mediaId: this.assetId });
waveformCache.clearSource({
sourceKey: buildWaveformSourceKey({
kind: "media",
id: this.assetId,
}),
});
editor.media.setAssets({
assets: assets.filter((media) => media.id !== this.assetId),
});
const elementsToRemove: Array<{ trackId: string; elementId: string }> = [];
for (const track of [
...this.savedTracks.overlay,
this.savedTracks.main,
...this.savedTracks.audio,
]) {
for (const element of track.elements) {
if (hasMediaId(element) && element.mediaId === this.assetId) {
elementsToRemove.push({ trackId: track.id, elementId: element.id });
}
}
}
if (elementsToRemove.length > 0) {
editor.timeline.deleteElements({ elements: elementsToRemove });
}
storageService
.deleteMediaAsset({ projectId: this.projectId, id: this.assetId })
.catch((error) => {
console.error("Failed to delete media item:", error);
});
}
undo(): void {
const editor = EditorCore.getInstance();
if (this.savedAssets && this.removedAsset) {
const restoredAsset: MediaAsset = {
...this.removedAsset,
url: URL.createObjectURL(this.removedAsset.file),
};
editor.media.setAssets({
assets: this.savedAssets.map((a) =>
a.id === this.assetId ? restoredAsset : a,
),
});
storageService
.saveMediaAsset({
projectId: this.projectId,
mediaAsset: restoredAsset,
})
.catch((error) => {
console.error("Failed to restore media item on undo:", error);
});
}
if (this.savedTracks) {
editor.timeline.updateTracks(this.savedTracks);
}
}
}