const CACHE_NAME = 'wetalk-audio-cache' export async function cacheAudio( url: string, onProgress?: (percent: number) => void, ): Promise { try { const cache = await caches.open(CACHE_NAME) // Check if already cached const existing = await cache.match(url) if (existing) return true const response = await fetch(url) if (!response.ok) return false if (onProgress && response.body) { const contentLength = Number(response.headers.get('content-length') || 0) const reader = response.body.getReader() const chunks: Uint8Array[] = [] let received = 0 while (true) { const { done, value } = await reader.read() if (done) break chunks.push(value) received += value.length if (contentLength > 0) onProgress(Math.round((received / contentLength) * 100)) } const blob = new Blob(chunks, { type: response.headers.get('content-type') || 'audio/mpeg' }) const cachedResponse = new Response(blob, { status: 200, headers: { 'Content-Type': blob.type, 'Content-Length': String(blob.size) }, }) await cache.put(url, cachedResponse) } else { await cache.put(url, response) } return true } catch (e) { console.error('[offline] cache failed:', e) return false } } export async function getCachedAudioUrl(url: string): Promise { try { const cache = await caches.open(CACHE_NAME) const response = await cache.match(url) if (!response) return null const blob = await response.blob() return URL.createObjectURL(blob) } catch { return null } } export async function isAudioCached(url: string): Promise { try { const cache = await caches.open(CACHE_NAME) const response = await cache.match(url) return !!response } catch { return false } } export async function removeCachedAudio(url: string): Promise { try { const cache = await caches.open(CACHE_NAME) await cache.delete(url) } catch { /* ignore */ } } export async function getCacheSize(): Promise { try { const cache = await caches.open(CACHE_NAME) const keys = await cache.keys() let total = 0 for (const req of keys) { const res = await cache.match(req) if (res) { const blob = await res.blob() total += blob.size } } return total } catch { return 0 } }