// ── Rehearser library persistence ──────────────────────────────────────────── // SQLite-backed rehearsal records and the one-time IndexedDB migration are // isolated from parsing, casting, and stage playback. Functions stay global // during the compatibility migration because audiobook.js/library.js call them. async function rehDbGetAll() { const r = await fetch('/api/rehearsals'); if (!r.ok) throw new Error('rehDbGetAll failed: ' + r.status); const d = await r.json(); return d.rehearsals || []; } async function rehDbGetById(id) { const r = await fetch('/api/rehearsals/' + id); if (r.status === 404) return undefined; if (!r.ok) throw new Error('rehDbGetById failed: ' + r.status); return r.json(); } async function rehDbAdd(record) { const r = await fetch('/api/rehearsals', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(_rehSanitize(record)) }); if (!r.ok) throw new Error('rehDbAdd failed: ' + r.status); return (await r.json()).id; } async function rehDbPut(record) { if (!record.id) return rehDbAdd(record); const r = await fetch('/api/rehearsals/' + record.id, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(_rehSanitize(record)) }); if (!r.ok) throw new Error('rehDbPut failed: ' + r.status); } async function rehDbDelete(id) { const r = await fetch('/api/rehearsals/' + id, { method: 'DELETE' }); if (!r.ok) throw new Error('rehDbDelete failed: ' + r.status); } function _rehSanitize(rec) { const out = { ...rec }; if (out.clips) out.clips = (out.clips || []).map(c => ({ lineIndex: c.lineIndex, speaker: c.speaker, type: c.type })); return out; } function _rehIdbGetAll() { return new Promise(resolve => { const req = indexedDB.open('reh-library', 1); req.onerror = () => resolve([]); req.onsuccess = e => { const db = e.target.result; if (!db.objectStoreNames.contains('rehearsals')) { db.close(); resolve([]); return; } const all = db.transaction('rehearsals', 'readonly').objectStore('rehearsals').getAll(); all.onsuccess = ev => { db.close(); resolve(ev.target.result || []); }; all.onerror = () => { db.close(); resolve([]); }; }; }); } (async function _rehMigrateIfNeeded() { try { if ((await rehDbGetAll()).length) return; const old = await _rehIdbGetAll(); if (!old.length) return; const r = await fetch('/api/rehearsals/migrate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(old.map(_rehSanitize)) }); if (r.ok) console.log(`[rehearser] migrated ${(await r.json()).imported} records from IndexedDB → SQLite`); } catch (e) { console.warn('[rehearser] migration skipped:', e); } })(); window.rehDbGetById = rehDbGetById;