added cut and mute zones
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
import { create } from 'zustand';
|
||||
import { temporal } from 'zundo';
|
||||
import type { Word, Segment, DeletedRange, TranscriptionResult, ProjectFile } from '../types/project';
|
||||
import type { Word, Segment, DeletedRange, CutRange, MuteRange, TranscriptionResult, ProjectFile } from '../types/project';
|
||||
|
||||
interface EditorState {
|
||||
videoPath: string | null;
|
||||
@ -9,6 +9,8 @@ interface EditorState {
|
||||
words: Word[];
|
||||
segments: Segment[];
|
||||
deletedRanges: DeletedRange[];
|
||||
cutRanges: CutRange[];
|
||||
muteRanges: MuteRange[];
|
||||
language: string;
|
||||
|
||||
currentTime: number;
|
||||
@ -41,6 +43,10 @@ interface EditorActions {
|
||||
deleteSelectedWords: () => void;
|
||||
deleteWordRange: (startIndex: number, endIndex: number) => void;
|
||||
restoreRange: (rangeId: string) => void;
|
||||
addCutRange: (start: number, end: number) => void;
|
||||
addMuteRange: (start: number, end: number) => void;
|
||||
removeCutRange: (id: string) => void;
|
||||
removeMuteRange: (id: string) => void;
|
||||
setTranscribing: (active: boolean, progress?: number, status?: string) => void;
|
||||
setExporting: (active: boolean, progress?: number) => void;
|
||||
getKeepSegments: () => Array<{ start: number; end: number }>;
|
||||
@ -56,6 +62,8 @@ const initialState: EditorState = {
|
||||
words: [],
|
||||
segments: [],
|
||||
deletedRanges: [],
|
||||
cutRanges: [],
|
||||
muteRanges: [],
|
||||
language: '',
|
||||
currentTime: 0,
|
||||
duration: 0,
|
||||
@ -82,7 +90,7 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
setExportedAudioPath: (path) => set({ exportedAudioPath: path }),
|
||||
|
||||
saveProject: (): ProjectFile => {
|
||||
const { videoPath, words, segments, deletedRanges, language, exportedAudioPath } = get();
|
||||
const { videoPath, words, segments, deletedRanges, cutRanges, muteRanges, language, exportedAudioPath } = get();
|
||||
if (!videoPath) throw new Error('No video loaded');
|
||||
const now = new Date().toISOString();
|
||||
// Strip globalStartIndex (runtime-only field) before persisting
|
||||
@ -94,6 +102,8 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
words,
|
||||
segments: persistSegments as unknown as Segment[],
|
||||
deletedRanges,
|
||||
cutRanges,
|
||||
muteRanges,
|
||||
language,
|
||||
createdAt: now, // will be overwritten if we track original creation time later
|
||||
modifiedAt: now,
|
||||
@ -174,11 +184,41 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
set({ deletedRanges: deletedRanges.filter((r) => r.id !== rangeId) });
|
||||
},
|
||||
|
||||
addCutRange: (start, end) => {
|
||||
const { cutRanges } = get();
|
||||
const newRange: CutRange = {
|
||||
id: `cut_${nextRangeId++}`,
|
||||
start,
|
||||
end,
|
||||
};
|
||||
set({ cutRanges: [...cutRanges, newRange] });
|
||||
},
|
||||
|
||||
addMuteRange: (start, end) => {
|
||||
const { muteRanges } = get();
|
||||
const newRange: MuteRange = {
|
||||
id: `mute_${nextRangeId++}`,
|
||||
start,
|
||||
end,
|
||||
};
|
||||
set({ muteRanges: [...muteRanges, newRange] });
|
||||
},
|
||||
|
||||
removeCutRange: (id) => {
|
||||
const { cutRanges } = get();
|
||||
set({ cutRanges: cutRanges.filter((r) => r.id !== id) });
|
||||
},
|
||||
|
||||
removeMuteRange: (id) => {
|
||||
const { muteRanges } = get();
|
||||
set({ muteRanges: muteRanges.filter((r) => r.id !== id) });
|
||||
},
|
||||
|
||||
setTranscribing: (active, progress, status) =>
|
||||
set({
|
||||
isTranscribing: active,
|
||||
transcriptionProgress: progress ?? (active ? 0 : 100),
|
||||
transcriptionStatus: status ?? (active ? '' : ''),
|
||||
transcriptionStatus: status ?? (active ? 'Transcribing...' : ''),
|
||||
}),
|
||||
|
||||
setExporting: (active, progress) =>
|
||||
@ -188,7 +228,7 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
}),
|
||||
|
||||
getKeepSegments: () => {
|
||||
const { words, deletedRanges, duration } = get();
|
||||
const { words, deletedRanges, cutRanges, duration } = get();
|
||||
if (words.length === 0) return [{ start: 0, end: duration }];
|
||||
|
||||
const deletedSet = new Set<number>();
|
||||
@ -196,6 +236,16 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
for (const idx of range.wordIndices) deletedSet.add(idx);
|
||||
}
|
||||
|
||||
// Also exclude words that fall within cut ranges
|
||||
for (const cutRange of cutRanges) {
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
const word = words[i];
|
||||
if (word.start >= cutRange.start && word.end <= cutRange.end) {
|
||||
deletedSet.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const segments: Array<{ start: number; end: number }> = [];
|
||||
let segStart: number | null = null;
|
||||
|
||||
@ -249,6 +299,8 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
words: data.words || [],
|
||||
segments: annotatedSegments,
|
||||
deletedRanges: data.deletedRanges || [],
|
||||
cutRanges: data.cutRanges || [],
|
||||
muteRanges: data.muteRanges || [],
|
||||
language: data.language || '',
|
||||
exportedAudioPath: data.exportedAudioPath ?? null,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user