diff --git a/Dockerfile b/Dockerfile index dc8f7fb..8a8dd1e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,10 +13,13 @@ COPY . . RUN mkdir -p /app/data/logs -# Download Browser Whisper models (self-hosted) -# Library (900KB) loads from CDN, models (40MB+) served from our server +# Download Browser Whisper (COMPLETE self-hosting - zero CDN dependencies) +# Library + Models all bundled and served from our server RUN mkdir -p /app/public/models/Xenova/whisper-tiny.en/onnx && \ - cd /app/public/models/Xenova/whisper-tiny.en && \ + cd /app/public/models && \ + echo "Downloading transformers.js library (worker-compatible build)..." && \ + curl -sL -o transformers.min.js https://cdn.jsdelivr.net/npm/@xenova/transformers@2.6.2/dist/transformers.min.js && \ + cd Xenova/whisper-tiny.en && \ echo "Downloading Whisper model files..." && \ curl -sL -o config.json https://huggingface.co/Xenova/whisper-tiny.en/resolve/main/config.json && \ curl -sL -o tokenizer.json https://huggingface.co/Xenova/whisper-tiny.en/resolve/main/tokenizer.json && \ @@ -24,7 +27,7 @@ RUN mkdir -p /app/public/models/Xenova/whisper-tiny.en/onnx && \ curl -sL -o generation_config.json https://huggingface.co/Xenova/whisper-tiny.en/resolve/main/generation_config.json && \ curl -sL -o onnx/encoder_model_quantized.onnx https://huggingface.co/Xenova/whisper-tiny.en/resolve/main/onnx/encoder_model_quantized.onnx && \ curl -sL -o onnx/decoder_model_merged_quantized.onnx https://huggingface.co/Xenova/whisper-tiny.en/resolve/main/onnx/decoder_model_merged_quantized.onnx && \ - echo "Whisper models downloaded successfully (models: 42MB)" + echo "✅ Browser Whisper: 100% self-hosted (library: 760KB, models: 42MB)" EXPOSE 3000 diff --git a/public/js/browserWhisper.js b/public/js/browserWhisper.js index a58515a..0a52b2b 100644 --- a/public/js/browserWhisper.js +++ b/public/js/browserWhisper.js @@ -91,8 +91,7 @@ _loading = true; _modelLoaded = model; - // Use module worker to support ES modules - _worker = new Worker('/js/whisperWorker.js', { type: 'module' }); + _worker = new Worker('/js/whisperWorker.js'); _worker.addEventListener('message', function(e) { var d = e.data; diff --git a/public/js/whisperWorker.js b/public/js/whisperWorker.js index 50e80f1..f68abd7 100644 --- a/public/js/whisperWorker.js +++ b/public/js/whisperWorker.js @@ -1,22 +1,26 @@ // ============================================================ // WHISPER WORKER — runs @xenova/transformers in a Web Worker // Audio never leaves the device. Model cached in IndexedDB. -// Uses ES module worker (type: 'module') // ============================================================ -import { pipeline, env } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2'; - console.log('[WhisperWorker] Starting worker initialization'); +// Load transformers.js from our server (bundled, no external CDN dependency) +importScripts('/models/transformers.min.js'); + +var env = self.transformers.env; +var pipeline = self.transformers.pipeline; + // Configure transformers.js to load models from our local server env.allowLocalModels = false; env.useBrowserCache = true; -env.allowRemoteModels = false; // Force local-only -env.localModelPath = '/models/'; // Our bundled models (40MB+) -console.log('[WhisperWorker] Transformers configured for local models at /models/'); +env.allowRemoteModels = false; +env.localModelPath = '/models/'; -let _pipe = null; -let _loadedModel = null; +console.log('[WhisperWorker] Transformers loaded, models path: /models/'); + +var _pipe = null; +var _loadedModel = null; async function load(modelName) { if (_pipe && _loadedModel === modelName) { @@ -28,7 +32,7 @@ async function load(modelName) { try { _pipe = await pipeline('automatic-speech-recognition', modelName, { - progress_callback: (p) => { + progress_callback: function(p) { console.log('[WhisperWorker] Progress:', p.status, p.file, p.progress); if (p.status === 'downloading' || p.status === 'progress') { self.postMessage({ type: 'progress', file: p.file || '', progress: Math.round(p.progress || 0) }); @@ -47,8 +51,8 @@ async function load(modelName) { } } -self.addEventListener('message', async (e) => { - const d = e.data; +self.addEventListener('message', async function(e) { + var d = e.data; console.log('[WhisperWorker] Message received:', d.type); if (d.type === 'load') { @@ -65,7 +69,7 @@ self.addEventListener('message', async (e) => { try { await load(d.model || 'Xenova/whisper-tiny.en'); console.log('[WhisperWorker] Starting transcription...'); - const result = await _pipe(d.audio, { + var result = await _pipe(d.audio, { language: 'english', task: 'transcribe', chunk_length_s: 30, diff --git a/scripts/download-whisper-models.sh b/scripts/download-whisper-models.sh index 42d55bd..f1b0f0a 100755 --- a/scripts/download-whisper-models.sh +++ b/scripts/download-whisper-models.sh @@ -13,10 +13,10 @@ echo "" # Create directories mkdir -p "$MODELS_DIR/Xenova/whisper-tiny.en/onnx" -# Download transformers.js +# Download transformers.js (worker-compatible build) echo "📦 Downloading transformers.js..." curl -L --progress-bar -o "$MODELS_DIR/transformers.min.js" \ - "https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2/dist/transformers.min.js" + "https://cdn.jsdelivr.net/npm/@xenova/transformers@2.6.2/dist/transformers.min.js" echo "✅ transformers.min.js ($(du -h $MODELS_DIR/transformers.min.js | cut -f1))" echo ""