tts-voice-creator-clone-and.../static/sw.js
mARTin-B78 ea50267c30 Add unified Studio casting workflow and fix voice/casting pipeline bugs
Introduces the new Studio section (Source -> Characters -> Voices ->
Perform & Export) that reuses the existing Read Aloud/Library/Script
Rehearsal code via DOM reparenting instead of duplicating it, and rolls up
a long tail of bugs found while producing a real audiobook through it:
umlaut-eating name sanitizers, a voice picker that mispositioned itself and
capped results at 60, PDF pagination silently breaking on trimmed \f
markers, a race letting stale audio keep playing after a new line was
clicked, an alias-overlap bug that could silently redirect a voice/image
save onto the wrong character, voice design failing outright during brief
TTS backend restarts instead of retrying, sparse cast entries defaulting to
English/wrong gender, and a reassigned voice never reaching an already-open
Stage session or invalidating its cached audio. Also adds a persistent
per-line audio cache, audiobook export browsing/download, and an inline
voice-design prompt editor. Full details in CHANGELOG.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 02:03:56 +02:00

45 lines
1.8 KiB
JavaScript

/* TTS Voice Creator — network-first service worker.
Always tries the network first (so live edits/new versions show immediately) and
only falls back to cache when offline. Never touches the API or cross-origin
requests, so it can't serve stale data or break audio/streaming. */
const CACHE = 'tts-vc-v1';
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', (e) => {
e.waitUntil((async () => {
const keys = await caches.keys();
await Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k)));
await self.clients.claim();
})());
});
self.addEventListener('fetch', (e) => {
const req = e.request;
if (req.method !== 'GET') return;
const url = new URL(req.url);
if (url.origin !== self.location.origin) return; // ignore CDN / cross-origin
if (url.pathname.startsWith('/api/')) return; // never cache the API
e.respondWith((async () => {
try {
// cache:'reload' forces this inner fetch to bypass the HTTP cache and go
// to the network every time. Without it, a browser's Ctrl+Shift+R hard
// reload does NOT propagate its cache-bypass into fetch() calls made from
// inside a service worker's fetch handler — so this "network-first" SW
// could still hand back a stale cached response even right after a hard
// reload, which is exactly what made JS edits look like they never landed.
const net = await fetch(req, { cache: 'reload' });
// Cache only successful static shell assets for offline fallback
if (net && net.ok && (url.pathname === '/' || url.pathname.startsWith('/static/'))) {
const c = await caches.open(CACHE);
c.put(req, net.clone());
}
return net;
} catch (err) {
const cached = await caches.match(req);
if (cached) return cached;
throw err;
}
})());
});