94 lines
2.4 KiB
TypeScript
94 lines
2.4 KiB
TypeScript
const CACHE_NAME = 'wetalk-audio-cache'
|
|
|
|
export async function cacheAudio(
|
|
url: string,
|
|
onProgress?: (percent: number) => void,
|
|
): Promise<boolean> {
|
|
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<string | null> {
|
|
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<boolean> {
|
|
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<void> {
|
|
try {
|
|
const cache = await caches.open(CACHE_NAME)
|
|
await cache.delete(url)
|
|
} catch { /* ignore */ }
|
|
}
|
|
|
|
export async function getCacheSize(): Promise<number> {
|
|
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
|
|
}
|
|
}
|