Better tests and more user feedback while doing transcription; WIP.
This commit is contained in:
1 parent
18acead43c
commit
be82ecfe57
19 files changed
+447
-138
No files matched your search
+39
@@ -59,6 +59,11 @@ export function App() {
|
||||
);
|
||||
const nextLiveRequest = useRef(0);
|
||||
|
||||
// File-flow run counter (D-018). Cancel bumps it, so a decode that was in
|
||||
// flight when the user cancelled finds itself stale and never posts its
|
||||
// audio to the (fresh) worker. Decoding itself cannot be aborted.
|
||||
const fileRun = useRef(0);
|
||||
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [decoding, setDecoding] = useState(false);
|
||||
const [loadStarted, setLoadStarted] = useState(false);
|
||||
@@ -213,8 +218,40 @@ export function App() {
|
||||
if (loadStarted) startLoad(modelId, preference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the in-flight file transcription (D-018). Transformers.js has no
|
||||
* abort API for a running `asr()` call — and on the WASM backend the pass
|
||||
* blocks the worker's event loop, so even a "cancel" message would sit
|
||||
* unread until the pass finished. Terminating the worker is the only
|
||||
* reliable preemption; it also aborts an in-flight model download. The next
|
||||
* run spawns a fresh worker and reloads the pipeline — cheap, because the
|
||||
* weights stay in the browser Cache API (D-005).
|
||||
*/
|
||||
function cancelTranscription() {
|
||||
if (!busy) return;
|
||||
fileRun.current++; // any decode still running is now stale
|
||||
workerRef.current?.terminate();
|
||||
workerRef.current = null;
|
||||
// The file flow never has live passes pending, but if anything is in the
|
||||
// map (defensive), unblock it so nothing awaits a dead worker forever.
|
||||
const cancelled = new Error("Transcription cancelled.");
|
||||
for (const pending of pendingLive.current.values()) pending.reject(cancelled);
|
||||
pendingLive.current.clear();
|
||||
setBusy(false);
|
||||
setPartialText("");
|
||||
setResult(null);
|
||||
setFileName(null);
|
||||
setError(null);
|
||||
// The worker (and any pipeline / partial download) is gone — reset the
|
||||
// load state so the next transcription reloads from a clean slate.
|
||||
setLoadStarted(false);
|
||||
setLoadProgress(initialLoadProgress);
|
||||
setActiveDevice(null);
|
||||
}
|
||||
|
||||
async function handleFile(file: File) {
|
||||
if (busy || liveStatus !== "idle") return;
|
||||
const run = ++fileRun.current;
|
||||
setBusy(true);
|
||||
setError(null);
|
||||
setPartialText("");
|
||||
@@ -237,6 +274,7 @@ export function App() {
|
||||
// Decoded entirely on-device via Web Audio (D-012); the audio never
|
||||
// touches the network (Architecture.md §5).
|
||||
const audio = await decodeAudioFile(file);
|
||||
if (fileRun.current !== run) return; // cancelled while decoding (D-018)
|
||||
// Transfer the buffer to the worker — zero copy. Language/task only
|
||||
// apply to multilingual models; `.en` variants take neither.
|
||||
postToWorker(
|
||||
@@ -247,6 +285,7 @@ export function App() {
|
||||
[audio.buffer as ArrayBuffer],
|
||||
);
|
||||
} catch (decodeError) {
|
||||
if (fileRun.current !== run) return; // this run was already cancelled
|
||||
setError(decodeError instanceof Error ? decodeError.message : String(decodeError));
|
||||
setBusy(false);
|
||||
} finally {
|
||||
|
||||
Reference in new issue
Block a user