Spaces:
Sleeping
Sleeping
| /** | |
| * IndexedDB cache for preset stem PCM data. | |
| * Stores raw int16 bytes + metadata so preset loads are instant after first visit. | |
| * | |
| * Bump CACHE_VERSION whenever preset audio files change on the server — | |
| * old entries will be ignored and re-fetched. | |
| */ | |
| const DB_NAME = 'jam-tracks-presets' | |
| const DB_VERSION = 1 | |
| const STORE_NAME = 'stems' | |
| export const CACHE_VERSION = 1 | |
| function openDB() { | |
| return new Promise((resolve, reject) => { | |
| const req = indexedDB.open(DB_NAME, DB_VERSION) | |
| req.onupgradeneeded = (e) => { | |
| const db = e.target.result | |
| if (!db.objectStoreNames.contains(STORE_NAME)) { | |
| db.createObjectStore(STORE_NAME) | |
| } | |
| } | |
| req.onsuccess = (e) => resolve(e.target.result) | |
| req.onerror = (e) => reject(e.target.error) | |
| }) | |
| } | |
| function dbGet(db, key) { | |
| return new Promise((resolve, reject) => { | |
| const tx = db.transaction(STORE_NAME, 'readonly') | |
| const req = tx.objectStore(STORE_NAME).get(key) | |
| req.onsuccess = (e) => resolve(e.target.result) | |
| req.onerror = (e) => reject(e.target.error) | |
| }) | |
| } | |
| function dbPut(db, key, value) { | |
| return new Promise((resolve, reject) => { | |
| const tx = db.transaction(STORE_NAME, 'readwrite') | |
| const req = tx.objectStore(STORE_NAME).put(value, key) | |
| req.onsuccess = () => resolve() | |
| req.onerror = (e) => reject(e.target.error) | |
| }) | |
| } | |
| function stemKey(presetName, stemName) { | |
| return `v${CACHE_VERSION}:${presetName}:${stemName}` | |
| } | |
| /** Returns true if all stemNames for this preset are cached. */ | |
| export async function isCached(presetName, stemNames) { | |
| try { | |
| const db = await openDB() | |
| const checks = await Promise.all(stemNames.map(s => dbGet(db, stemKey(presetName, s)))) | |
| return checks.every(v => v != null) | |
| } catch { | |
| return false | |
| } | |
| } | |
| /** | |
| * Returns cached data for all stems or null if any stem is missing. | |
| * Shape: { stemName: { bytes: ArrayBuffer, sampleRate, numChannels, numFrames } } | |
| */ | |
| export async function getCachedPreset(presetName, stemNames) { | |
| try { | |
| const db = await openDB() | |
| const result = {} | |
| for (const stemName of stemNames) { | |
| const entry = await dbGet(db, stemKey(presetName, stemName)) | |
| if (!entry) return null | |
| result[stemName] = entry | |
| } | |
| return result | |
| } catch { | |
| return null | |
| } | |
| } | |
| /** Store a single stem's PCM data in IndexedDB. */ | |
| export async function cacheStem(presetName, stemName, data) { | |
| try { | |
| const db = await openDB() | |
| await dbPut(db, stemKey(presetName, stemName), data) | |
| } catch (err) { | |
| console.warn('[presetCache] Failed to cache stem:', err) | |
| } | |
| } | |