Files
OpenCut/apps/web/src/lib/commands/timeline/track/add-track.ts
T

119 lines
2.6 KiB
TypeScript

import { Command, type CommandResult } from "@/lib/commands/base-command";
import type { SceneTracks, TrackType } from "@/lib/timeline";
import { generateUUID } from "@/utils/id";
import { EditorCore } from "@/core";
import {
buildEmptyTrack,
getDefaultInsertIndexForTrack,
} from "@/lib/timeline/placement";
export class AddTrackCommand extends Command {
private trackId: string;
private savedState: SceneTracks | null = null;
constructor(
private type: TrackType,
private index?: number,
) {
super();
this.trackId = generateUUID();
}
execute(): CommandResult | undefined {
const editor = EditorCore.getInstance();
this.savedState = editor.scenes.getActiveScene().tracks;
const insertIndex =
this.index ??
getDefaultInsertIndexForTrack({
tracks: this.savedState,
trackType: this.type,
});
const updatedTracks =
this.type === "audio"
? buildAudioTrackState({
tracks: this.savedState,
insertIndex,
trackId: this.trackId,
})
: buildOverlayTrackState({
tracks: this.savedState,
insertIndex,
trackId: this.trackId,
trackType: this.type,
});
editor.timeline.updateTracks(updatedTracks);
return undefined;
}
undo(): void {
if (this.savedState) {
const editor = EditorCore.getInstance();
editor.timeline.updateTracks(this.savedState);
}
}
getTrackId(): string {
return this.trackId;
}
}
function buildAudioTrackState({
tracks,
insertIndex,
trackId,
}: {
tracks: SceneTracks;
insertIndex: number;
trackId: string;
}): SceneTracks {
const audioInsertIndex = Math.max(
0,
insertIndex - tracks.overlay.length - 1,
);
const newTrack = buildEmptyTrack({
id: trackId,
type: "audio",
});
return {
...tracks,
audio: [
...tracks.audio.slice(0, audioInsertIndex),
newTrack,
...tracks.audio.slice(audioInsertIndex),
],
};
}
function buildOverlayTrackState({
tracks,
insertIndex,
trackId,
trackType,
}: {
tracks: SceneTracks;
insertIndex: number;
trackId: string;
trackType: Exclude<TrackType, "audio">;
}): SceneTracks {
const overlayInsertIndex = Math.min(insertIndex, tracks.overlay.length);
const newTrack =
trackType === "video"
? buildEmptyTrack({ id: trackId, type: "video" })
: trackType === "text"
? buildEmptyTrack({ id: trackId, type: "text" })
: trackType === "graphic"
? buildEmptyTrack({ id: trackId, type: "graphic" })
: buildEmptyTrack({ id: trackId, type: "effect" });
return {
...tracks,
overlay: [
...tracks.overlay.slice(0, overlayInsertIndex),
newTrack,
...tracks.overlay.slice(overlayInsertIndex),
],
};
}