import type { SongMeta, SongRecord } from '../types.ts'; // Song library in IndexedDB. One object store keyed by song id; the whole // song is small JSON, so records are read and written whole. const DB_NAME = 'midiboot'; const STORE = 'songs'; let dbPromise: Promise | null = null; function openDb(): Promise { if (dbPromise) return dbPromise; dbPromise = new Promise((resolve, reject) => { const req = indexedDB.open(DB_NAME, 1); req.onupgradeneeded = () => { const db = req.result; if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE, { keyPath: 'id' }); }; req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); return dbPromise; } function txDone(tx: IDBTransaction): Promise { return new Promise((resolve, reject) => { tx.oncomplete = () => resolve(); tx.onerror = () => reject(tx.error); tx.onabort = () => reject(tx.error); }); } export async function putSong(rec: SongRecord): Promise { const db = await openDb(); const tx = db.transaction(STORE, 'readwrite'); tx.objectStore(STORE).put(rec); await txDone(tx); } export async function getSong(id: string): Promise { const db = await openDb(); return new Promise((resolve, reject) => { const req = db.transaction(STORE, 'readonly').objectStore(STORE).get(id); req.onsuccess = () => resolve((req.result as SongRecord) ?? null); req.onerror = () => reject(req.error); }); } export async function deleteSong(id: string): Promise { const db = await openDb(); const tx = db.transaction(STORE, 'readwrite'); tx.objectStore(STORE).delete(id); await txDone(tx); } export async function listSongs(): Promise { const db = await openDb(); return new Promise((resolve, reject) => { const req = db.transaction(STORE, 'readonly').objectStore(STORE).getAll(); req.onsuccess = () => { const recs = (req.result as SongRecord[]) ?? []; const metas = recs.map((r) => ({ id: r.id, name: r.name, updatedAt: r.updatedAt, trackCount: r.song.tracks.length, noteCount: r.song.tracks.reduce((s, t) => s + t.notes.length, 0), })); metas.sort((a, b) => b.updatedAt - a.updatedAt); resolve(metas); }; req.onerror = () => reject(req.error); }); }