// ============================================================ // WHISPER WORKER V2 — Alternative loading using dynamic import // Loads transformers.js via script tag method instead of importScripts // ============================================================ console.log('[WhisperWorker] V2 Starting initialization'); // Alternative loading method that works with CSP var TRANSFORMERS_URL = 'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2/dist/transformers.min.js'; var _transformers = null; var _pipe = null; var _loadedModel = null; var _loading = false; // Load transformers library using alternative method async function initTransformers() { if (_transformers) return _transformers; if (_loading) { // Wait for current load to complete while (_loading) { await new Promise(resolve => setTimeout(resolve, 100)); } return _transformers; } _loading = true; console.log('[WhisperWorker] V2 Loading transformers via importScripts fallback...'); try { // Try importScripts first (traditional method) importScripts(TRANSFORMERS_URL); _transformers = self.transformers || transformers; console.log('[WhisperWorker] V2 Loaded via importScripts'); } catch (err) { console.error('[WhisperWorker] V2 importScripts failed, trying alternative:', err.message); // Fallback: Notify main thread to use server-side transcription self.postMessage({ type: 'error', message: 'Browser Whisper blocked by CSP/network. Using server transcription instead.' }); _loading = false; throw new Error('CDN blocked - use server transcription'); } if (!_transformers) { _loading = false; throw new Error('Transformers library did not load'); } _transformers.env.allowLocalModels = false; _transformers.env.useBrowserCache = true; _loading = false; console.log('[WhisperWorker] V2 Transformers ready'); return _transformers; } async function loadModel(modelName) { if (_pipe && _loadedModel === modelName) { console.log('[WhisperWorker] V2 Model already loaded:', modelName); return; } console.log('[WhisperWorker] V2 Loading model:', modelName); self.postMessage({ type: 'loading', model: modelName }); try { var T = await initTransformers(); _pipe = await T.pipeline('automatic-speech-recognition', modelName, { progress_callback: function(p) { console.log('[WhisperWorker] V2 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) }); } if (p.status === 'done' || p.status === 'ready') { self.postMessage({ type: 'progress', file: p.file || '', progress: 100 }); } } }); _loadedModel = modelName; console.log('[WhisperWorker] V2 Model loaded successfully'); self.postMessage({ type: 'ready', model: modelName }); } catch (err) { console.error('[WhisperWorker] V2 Load error:', err); self.postMessage({ type: 'error', message: 'Model load failed: ' + err.message + '. Try server transcription instead.' }); throw err; } } self.addEventListener('message', async function(e) { var d = e.data; console.log('[WhisperWorker] V2 Message received:', d.type); try { if (d.type === 'load') { await loadModel(d.model || 'Xenova/whisper-tiny.en'); return; } if (d.type === 'transcribe') { await loadModel(d.model || 'Xenova/whisper-tiny.en'); console.log('[WhisperWorker] V2 Starting transcription...'); var result = await _pipe(d.audio, { language: 'english', task: 'transcribe', chunk_length_s: 30, stride_length_s: 5, return_timestamps: false }); console.log('[WhisperWorker] V2 Transcription complete'); self.postMessage({ type: 'result', text: result.text.trim() }); } } catch (err) { console.error('[WhisperWorker] V2 Error:', err); self.postMessage({ type: 'error', message: err.message || 'Worker error' }); } }); console.log('[WhisperWorker] V2 Worker initialized');