Spaces:
Running
Running
Download worker.js from CyberMax-tools/hushscribe: direct link, hf CLI and curl.
- Browser
- Download file 2.65 kB
-
https://huggingface.co/spaces/CyberMax-tools/hushscribe/resolve/main/worker.js
- Command line
-
hf download hf://spaces/CyberMax-tools/hushscribe/worker.js
-
curl -L -o worker.js https://huggingface.co/spaces/CyberMax-tools/hushscribe/resolve/main/worker.js
2.65 kB
| // Hearlark worker: runs Whisper in the browser with transformers.js (WebGPU when available, else WASM). | |
| // Audio arrives as 16 kHz mono Float32Array; nothing is uploaded anywhere. © 2026 CyberMax. | |
| import { pipeline, WhisperTextStreamer, env } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0'; | |
| env.allowLocalModels = false; | |
| let asr = null; | |
| let loadedKey = null; | |
| async function load(model, device, post) { | |
| const key = `${model}|${device}`; | |
| if (asr && loadedKey === key) return asr; | |
| if (asr) { try { await asr.dispose(); } catch {} asr = null; loadedKey = null; } | |
| const dtype = device === 'webgpu' ? { encoder_model: 'fp32', decoder_model_merged: 'q4' } : 'q8'; | |
| asr = await pipeline('automatic-speech-recognition', model, { | |
| device, | |
| dtype, | |
| progress_callback: (p) => post({ type: 'load', status: p.status, file: p.file, loaded: p.loaded, total: p.total, progress: p.progress }), | |
| }); | |
| loadedKey = key; | |
| return asr; | |
| } | |
| self.onmessage = async ({ data }) => { | |
| if (data?.type !== 'run') return; | |
| const post = (m) => self.postMessage(m); | |
| try { | |
| let device = data.device; | |
| let p; | |
| try { | |
| p = await load(data.model, device, post); | |
| } catch (e) { | |
| if (device !== 'webgpu') throw e; | |
| post({ type: 'info', message: `WebGPU could not start (${e?.message || e}); using the CPU instead.` }); | |
| device = 'wasm'; | |
| p = await load(data.model, device, post); | |
| } | |
| post({ type: 'ready', device }); | |
| const englishOnly = /\.en$/.test(data.model); | |
| const opts = { | |
| chunk_length_s: 30, | |
| stride_length_s: 5, | |
| return_timestamps: true, | |
| ...(englishOnly ? {} : { language: data.language || null, task: data.task === 'translate' ? 'translate' : 'transcribe' }), | |
| }; | |
| let finalized = 0; | |
| let out; | |
| try { | |
| const time_precision = p.processor.feature_extractor.config.chunk_length / p.model.config.max_source_positions; | |
| const streamer = new WhisperTextStreamer(p.tokenizer, { | |
| time_precision, | |
| skip_prompt: true, | |
| callback_function: (text) => post({ type: 'partial', text }), | |
| on_finalize: () => post({ type: 'chunk', done: ++finalized }), | |
| }); | |
| out = await p(data.audio, { ...opts, streamer }); | |
| } catch (e) { | |
| // Streaming is only for live progress; if this transformers.js build rejects it, run without it. | |
| if (!/stream/i.test(String(e?.message || e))) throw e; | |
| out = await p(data.audio, opts); | |
| } | |
| post({ type: 'done', result: { text: out.text, chunks: out.chunks || [] }, device }); | |
| } catch (e) { | |
| post({ type: 'error', message: String(e?.message || e) }); | |
| } | |
| }; | |