blastoise-archive/public/trackStorage.js

168 lines
4.6 KiB
JavaScript

// Track Storage Abstraction Layer
// Provides a unified interface for storing/retrieving audio blobs
// Default implementation uses IndexedDB, can be swapped for Electron file API
const TrackStorage = (function() {
const DB_NAME = 'musicroom';
const DB_VERSION = 1;
const STORE_NAME = 'tracks';
let db = null;
let initPromise = null;
// Initialize IndexedDB
function init() {
if (initPromise) return initPromise;
initPromise = new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onerror = () => {
console.warn('TrackStorage: IndexedDB failed to open');
resolve(false);
};
request.onsuccess = () => {
db = request.result;
resolve(true);
};
request.onupgradeneeded = (event) => {
const database = event.target.result;
if (!database.objectStoreNames.contains(STORE_NAME)) {
database.createObjectStore(STORE_NAME, { keyPath: 'filename' });
}
};
});
return initPromise;
}
// Check if a track is cached
async function has(filename) {
await init();
if (!db) return false;
return new Promise((resolve) => {
const transaction = db.transaction(STORE_NAME, 'readonly');
const store = transaction.objectStore(STORE_NAME);
const request = store.getKey(filename);
request.onsuccess = () => resolve(request.result !== undefined);
request.onerror = () => resolve(false);
});
}
// Get a track blob, returns { blob, contentType } or null
async function get(filename) {
await init();
if (!db) return null;
return new Promise((resolve) => {
const transaction = db.transaction(STORE_NAME, 'readonly');
const store = transaction.objectStore(STORE_NAME);
const request = store.get(filename);
request.onsuccess = () => {
const result = request.result;
if (result) {
resolve({ blob: result.blob, contentType: result.contentType });
} else {
resolve(null);
}
};
request.onerror = () => resolve(null);
});
}
// Store a track blob
async function set(filename, blob, contentType) {
await init();
if (!db) return false;
return new Promise((resolve) => {
const transaction = db.transaction(STORE_NAME, 'readwrite');
const store = transaction.objectStore(STORE_NAME);
const request = store.put({ filename, blob, contentType, cachedAt: Date.now() });
request.onsuccess = () => resolve(true);
request.onerror = () => resolve(false);
});
}
// Remove a track from cache
async function remove(filename) {
await init();
if (!db) return false;
return new Promise((resolve) => {
const transaction = db.transaction(STORE_NAME, 'readwrite');
const store = transaction.objectStore(STORE_NAME);
const request = store.delete(filename);
request.onsuccess = () => resolve(true);
request.onerror = () => resolve(false);
});
}
// Clear all cached tracks
async function clear() {
await init();
if (!db) return false;
return new Promise((resolve) => {
const transaction = db.transaction(STORE_NAME, 'readwrite');
const store = transaction.objectStore(STORE_NAME);
const request = store.clear();
request.onsuccess = () => resolve(true);
request.onerror = () => resolve(false);
});
}
// List all cached track filenames
async function list() {
await init();
if (!db) return [];
return new Promise((resolve) => {
const transaction = db.transaction(STORE_NAME, 'readonly');
const store = transaction.objectStore(STORE_NAME);
const request = store.getAllKeys();
request.onsuccess = () => resolve(request.result || []);
request.onerror = () => resolve([]);
});
}
// Get storage stats
async function getStats() {
await init();
if (!db) return { count: 0, totalSize: 0 };
return new Promise((resolve) => {
const transaction = db.transaction(STORE_NAME, 'readonly');
const store = transaction.objectStore(STORE_NAME);
const request = store.getAll();
request.onsuccess = () => {
const tracks = request.result || [];
const totalSize = tracks.reduce((sum, t) => sum + (t.blob?.size || 0), 0);
resolve({ count: tracks.length, totalSize });
};
request.onerror = () => resolve({ count: 0, totalSize: 0 });
});
}
return {
init,
has,
get,
set,
remove,
clear,
list,
getStats
};
})();