able to process audio with different model; new project button

This commit is contained in:
2026-04-11 19:42:30 -06:00
parent b8ec396ebd
commit 0df967507f
3 changed files with 120 additions and 2 deletions

View File

@ -20,6 +20,8 @@ import {
Save,
Scissors,
VolumeX,
FilePlus2,
RefreshCw,
} from 'lucide-react';
const IS_ELECTRON = !!window.electronAPI;
@ -31,12 +33,14 @@ export default function App() {
const {
videoPath,
words,
transcriptionModel,
isTranscribing,
transcriptionProgress,
transcriptionStatus,
loadVideo,
setBackendUrl,
setTranscription,
setTranscriptionModel,
setTranscribing,
backendUrl,
selectedWordIndices,
@ -49,6 +53,7 @@ export default function App() {
const [whisperModel, setWhisperModel] = useState('base');
const [cutMode, setCutMode] = useState(false);
const [muteMode, setMuteMode] = useState(false);
const [showReprocessConfirm, setShowReprocessConfirm] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
useKeyboardShortcuts();
@ -137,6 +142,17 @@ export default function App() {
}
};
const handleNewProject = () => {
const shouldReset = window.confirm('Start a new project? Unsaved changes in the current session will be lost.');
if (!shouldReset) return;
useEditorStore.getState().reset();
setActivePanel(null);
setManualPath('');
setCutMode(false);
setMuteMode(false);
};
const handleManualSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const path = manualPath.trim();
@ -177,6 +193,7 @@ export default function App() {
setTranscribing(true, 20, 'Transcribing audio...');
const data = await window.electronAPI.transcribe(path, whisperModel);
setTranscription(data);
setTranscriptionModel(whisperModel);
} catch (err) {
console.error('Transcription error:', err);
alert(`Transcription failed. Check the console for details.\n\n${err}`);
@ -185,6 +202,22 @@ export default function App() {
}
};
const handleReprocessProject = async () => {
if (!videoPath) return;
if (!window.electronAPI?.transcribe) {
alert('Reprocessing is only available in desktop mode.');
return;
}
setShowReprocessConfirm(true);
};
const confirmReprocessProject = async () => {
if (!videoPath) return;
setShowReprocessConfirm(false);
await transcribeVideo(videoPath);
};
const togglePanel = (panel: Panel) => {
setActivePanel((prev) => (prev === panel ? null : panel));
};
@ -326,8 +359,18 @@ export default function App() {
<span className="text-sm font-medium truncate max-w-[300px]">
{videoPath.split(/[\\/]/).pop()}
</span>
{transcriptionModel && (
<span className="px-2 py-0.5 rounded border border-editor-border bg-editor-surface text-[10px] uppercase tracking-wide text-editor-text-muted">
Model: {transcriptionModel}
</span>
)}
</div>
<div className="flex items-center gap-1">
<div className="flex items-center gap-2">
<ToolbarButton
icon={<FilePlus2 className="w-4 h-4" />}
label="New"
onClick={handleNewProject}
/>
<ToolbarButton
icon={<FolderOpen className="w-4 h-4" />}
label="Open"
@ -367,6 +410,42 @@ export default function App() {
onClick={() => togglePanel('silence')}
disabled={!videoPath}
/>
<div className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-editor-surface border border-editor-border">
<select
value={whisperModel}
onChange={(e) => setWhisperModel(e.target.value)}
className="bg-transparent text-xs text-editor-text focus:outline-none"
title="Transcription model"
>
<optgroup label="Multilingual">
<option value="tiny">tiny</option>
<option value="base">base</option>
<option value="small">small</option>
<option value="medium">medium</option>
<option value="large-v2">large-v2</option>
<option value="large-v3">large-v3</option>
<option value="large-v3-turbo">large-v3-turbo</option>
<option value="distil-large-v3">distil-large-v3</option>
</optgroup>
<optgroup label="English">
<option value="tiny.en">tiny.en</option>
<option value="base.en">base.en</option>
<option value="small.en">small.en</option>
<option value="medium.en">medium.en</option>
<option value="distil-small.en">distil-small.en</option>
<option value="distil-medium.en">distil-medium.en</option>
</optgroup>
</select>
<button
onClick={handleReprocessProject}
disabled={isTranscribing || !videoPath}
title="Reprocess transcript with selected model"
className="flex items-center gap-1 px-2 py-1 rounded text-xs text-editor-text-muted hover:text-editor-text hover:bg-editor-bg disabled:opacity-40 disabled:cursor-not-allowed"
>
<RefreshCw className={`w-3 h-3 ${isTranscribing ? 'animate-spin' : ''}`} />
Reprocess
</button>
</div>
<ToolbarButton
icon={<Sparkles className="w-4 h-4" />}
label="AI"
@ -449,6 +528,37 @@ export default function App() {
)}
</div>
{import.meta.env.DEV && <DevPanel />}
{showReprocessConfirm && (
<div
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/60 px-4"
onClick={() => setShowReprocessConfirm(false)}
>
<div
className="w-full max-w-md rounded-xl border border-editor-border bg-editor-bg p-4 space-y-3"
onClick={(e) => e.stopPropagation()}
>
<h3 className="text-sm font-semibold">Reprocess transcript?</h3>
<p className="text-xs text-editor-text-muted leading-relaxed">
This will reprocess the current file with <span className="text-editor-text font-medium">{whisperModel}</span> and replace the current transcript words and timings.
</p>
<div className="flex items-center justify-end gap-2 pt-1">
<button
onClick={() => setShowReprocessConfirm(false)}
className="px-3 py-1.5 rounded-md text-xs text-editor-text-muted hover:text-editor-text hover:bg-editor-surface"
>
Cancel
</button>
<button
onClick={confirmReprocessProject}
className="px-3 py-1.5 rounded-md text-xs bg-editor-accent hover:bg-editor-accent-hover text-white"
>
Reprocess Now
</button>
</div>
</div>
</div>
)}
</div>
);
}