67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
import { browser } from '$app/environment';
|
|
import { SvelteSet, SvelteMap } from 'svelte/reactivity';
|
|
import type { StreamSummary } from './types.ts';
|
|
|
|
const STORAGE_KEY = 'cachedStreams';
|
|
|
|
export const cached = new SvelteSet<string>(
|
|
browser ? JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]') : []
|
|
);
|
|
export const downloading = new SvelteMap<string, number>();
|
|
|
|
if (browser) {
|
|
$effect.root(() => {
|
|
$effect(() => {
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify([...cached]));
|
|
});
|
|
});
|
|
}
|
|
|
|
export async function download(stream: StreamSummary) {
|
|
const res = await fetch(`/media/tracks/${stream.filename}`);
|
|
if (!res.ok || !res.body) throw new Error('Download failed');
|
|
|
|
const total = Number(res.headers.get('content-length') || 0);
|
|
const reader = res.body.getReader();
|
|
const chunks: Uint8Array[] = [];
|
|
let received = 0;
|
|
|
|
downloading.set(stream.id, 0);
|
|
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
chunks.push(value);
|
|
received += value.length;
|
|
if (total) downloading.set(stream.id, Math.round((received / total) * 100));
|
|
}
|
|
|
|
const root = await navigator.storage.getDirectory();
|
|
const handle = await root.getFileHandle(stream.id, { create: true });
|
|
const writable = await handle.createWritable();
|
|
await writable.write(new Blob(chunks));
|
|
await writable.close();
|
|
|
|
downloading.delete(stream.id);
|
|
cached.add(stream.id);
|
|
}
|
|
|
|
export async function remove(streamId: string) {
|
|
const root = await navigator.storage.getDirectory();
|
|
await root.removeEntry(streamId);
|
|
cached.delete(streamId);
|
|
}
|
|
|
|
export async function getUrl(streamId: string): Promise<string | null> {
|
|
if (!cached.has(streamId)) return null;
|
|
try {
|
|
const root = await navigator.storage.getDirectory();
|
|
const handle = await root.getFileHandle(streamId);
|
|
const file = await handle.getFile();
|
|
return URL.createObjectURL(file);
|
|
} catch {
|
|
cached.delete(streamId);
|
|
return null;
|
|
}
|
|
}
|