v15.1: CRITICAL FIX - TTS Preview + Browser Whisper actually working now
Some checks failed
Build TWA APK / build-apk (push) Has been cancelled
Build & Push Docker Image / build (push) Has been cancelled

ROOT CAUSES FOUND AND FIXED:
1. TTS Preview not working: voicePreferences.js listening for wrong event
   - Was: 'tab-loaded' (never dispatched)
   - Now: 'tabChanged' (correct event name used by app.js)
   - Added immediate init if page already loaded
   - Added 500ms delay for DOM readiness

2. Browser Whisper CDN blocked: CSP too restrictive
   - Added 'unsafe-eval' to scriptSrc (required by transformers.js)
   - Added cdn.jsdelivr.net to connectSrc (worker importScripts)
   - Added childSrc directive for worker script loading
   - Better error messages in worker

3. Worker loading errors: Now logged with specific reasons
   - importScripts wrapped in try-catch
   - Posts error message to main thread
   - Verifies transformers object exists after load

Testing:
- TTS Preview should now work when clicking Settings tab
- Browser Whisper should load from CDN (or show specific error)
- Console logs will show exact init sequence
This commit is contained in:
ifedan-ed 2026-03-31 16:00:10 +00:00
parent ee3729eb57
commit 88036a45c4
3 changed files with 45 additions and 9 deletions

View file

@ -5,24 +5,37 @@
(function() { (function() {
var _inited = false; var _inited = false;
window.addEventListener('tab-loaded', function(e) { // Listen for tab changes (correct event name is 'tabChanged' not 'tab-loaded')
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'settings' || _inited) return; if (e.detail.tab !== 'settings' || _inited) return;
_inited = true; _inited = true;
console.log('[VoicePrefs] Initializing...'); console.log('[VoicePrefs] Initializing on settings tab load...');
initVoicePreferences(); // Small delay to ensure DOM is ready
setTimeout(function() {
initVoicePreferences();
}, 100);
}); });
// Also init on DOMContentLoaded as backup // Also init on DOMContentLoaded as backup for direct page load
if (document.readyState === 'loading') { if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() { setTimeout(function() {
if (!_inited && document.getElementById('btn-preview-voice')) { if (!_inited && document.getElementById('btn-preview-voice')) {
console.log('[VoicePrefs] Init via DOMContentLoaded'); console.log('[VoicePrefs] Init via DOMContentLoaded (direct settings page load)');
_inited = true; _inited = true;
initVoicePreferences(); initVoicePreferences();
} }
}, 1000); }, 500);
}); });
} else {
// Page already loaded, init immediately if on settings
setTimeout(function() {
if (!_inited && document.getElementById('btn-preview-voice')) {
console.log('[VoicePrefs] Init immediate (page already loaded)');
_inited = true;
initVoicePreferences();
}
}, 500);
} }
function initVoicePreferences() { function initVoicePreferences() {

View file

@ -3,11 +3,29 @@
// Audio never leaves the device. Model cached in IndexedDB. // Audio never leaves the device. Model cached in IndexedDB.
// ============================================================ // ============================================================
importScripts('https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2'); console.log('[WhisperWorker] Starting worker initialization');
console.log('[WhisperWorker] Attempting to load transformers from CDN...');
try {
importScripts('https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2');
console.log('[WhisperWorker] Transformers library loaded successfully');
} catch (err) {
console.error('[WhisperWorker] Failed to load transformers library:', err);
self.postMessage({
type: 'error',
message: 'Failed to load transformers library. Check network/firewall: ' + err.message
});
throw err;
}
var T = self.transformers || transformers; var T = self.transformers || transformers;
if (!T) {
console.error('[WhisperWorker] Transformers object not found after import');
throw new Error('Transformers library did not initialize');
}
T.env.allowLocalModels = false; T.env.allowLocalModels = false;
T.env.useBrowserCache = true; T.env.useBrowserCache = true;
console.log('[WhisperWorker] Transformers configured');
var _pipe = null; var _pipe = null;
var _loadedModel = null; var _loadedModel = null;

View file

@ -21,7 +21,8 @@ app.use(helmet({
defaultSrc: ["'self'"], defaultSrc: ["'self'"],
// 'wasm-unsafe-eval' required for WebAssembly (Whisper in-browser transcription) // 'wasm-unsafe-eval' required for WebAssembly (Whisper in-browser transcription)
// cdn.jsdelivr.net required for @xenova/transformers worker script // cdn.jsdelivr.net required for @xenova/transformers worker script
scriptSrc: ["'self'", "'wasm-unsafe-eval'", 'https://cdn.jsdelivr.net'], // 'unsafe-eval' needed for transformers.js dynamic imports in worker
scriptSrc: ["'self'", "'wasm-unsafe-eval'", "'unsafe-eval'", 'https://cdn.jsdelivr.net'],
scriptSrcAttr: ["'none'"], scriptSrcAttr: ["'none'"],
styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com', 'https://cdnjs.cloudflare.com'], styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com', 'https://cdnjs.cloudflare.com'],
fontSrc: ["'self'", 'https://fonts.gstatic.com', 'https://cdnjs.cloudflare.com'], fontSrc: ["'self'", 'https://fonts.gstatic.com', 'https://cdnjs.cloudflare.com'],
@ -29,8 +30,12 @@ app.use(helmet({
mediaSrc: ["'self'", 'blob:'], mediaSrc: ["'self'", 'blob:'],
connectSrc: ["'self'", 'https://cdnjs.cloudflare.com', 'https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://www.google.com', 'wss://www.google.com', 'https://clinicaltables.nlm.nih.gov', connectSrc: ["'self'", 'https://cdnjs.cloudflare.com', 'https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://www.google.com', 'wss://www.google.com', 'https://clinicaltables.nlm.nih.gov',
// HuggingFace CDN for Whisper model downloads // HuggingFace CDN for Whisper model downloads
'https://huggingface.co', 'https://cdn-lfs.huggingface.co', 'https://cdn-lfs-us-1.huggingface.co', 'https://cdn-lfs-us-2.huggingface.co'], 'https://huggingface.co', 'https://cdn-lfs.huggingface.co', 'https://cdn-lfs-us-1.huggingface.co', 'https://cdn-lfs-us-2.huggingface.co',
// jsdelivr for worker importScripts
'https://cdn.jsdelivr.net'],
workerSrc: ["'self'", 'blob:'], workerSrc: ["'self'", 'blob:'],
// Allow workers to load scripts from jsdelivr
childSrc: ["'self'", 'blob:', 'https://cdn.jsdelivr.net'],
frameSrc: ["'none'"], frameSrc: ["'none'"],
objectSrc: ["'none'"], objectSrc: ["'none'"],
} }