diff --git a/src/lib/db.js b/src/lib/db.js new file mode 100644 index 0000000..9ea591a --- /dev/null +++ b/src/lib/db.js @@ -0,0 +1,83 @@ +const DB_NAME = 'outlier-saver'; +const DB_VERSION = 1; + +let dbPromise = null; + +export function openDb() { + dbPromise ??= new Promise((resolve, reject) => { + const request = indexedDB.open(DB_NAME, DB_VERSION); + request.onupgradeneeded = () => { + const db = request.result; + db.createObjectStore('outliers', { keyPath: 'videoId' }).createIndex('updatedAt', 'updatedAt'); + db.createObjectStore('channels', { keyPath: 'channelId' }); + db.createObjectStore('seen', { keyPath: 'videoId' }); + }; + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }).catch((err) => { + dbPromise = null; + throw err; + }); + return dbPromise; +} + +function promisify(request) { + return new Promise((resolve, reject) => { + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); +} + +async function getMany(storeName, keys) { + const db = await openDb(); + const store = db.transaction(storeName, 'readonly').objectStore(storeName); + const records = await Promise.all(keys.map((key) => promisify(store.get(key)))); + return new Map(records.filter(Boolean).map((record) => [record[store.keyPath], record])); +} + +async function putMany(storeName, records) { + if (records.length === 0) return; + const db = await openDb(); + await new Promise((resolve, reject) => { + const tx = db.transaction(storeName, 'readwrite'); + const store = tx.objectStore(storeName); + for (const record of records) store.put(record); + tx.oncomplete = () => resolve(); + tx.onerror = () => reject(tx.error); + tx.onabort = () => reject(tx.error); + }); +} + +export const getSeen = (videoIds) => getMany('seen', videoIds); +export const putSeen = (records) => putMany('seen', records); +export const getChannels = (channelIds) => getMany('channels', channelIds); +export const putChannels = (records) => putMany('channels', records); +export const getOutliers = (videoIds) => getMany('outliers', videoIds); +export const putOutliers = (records) => putMany('outliers', records); + +export async function listOutliers(limit = Infinity) { + const db = await openDb(); + return new Promise((resolve, reject) => { + const results = []; + const request = db + .transaction('outliers', 'readonly') + .objectStore('outliers') + .index('updatedAt') + .openCursor(null, 'prev'); + request.onsuccess = () => { + const cursor = request.result; + if (!cursor || results.length >= limit) { + resolve(results); + return; + } + results.push(cursor.value); + cursor.continue(); + }; + request.onerror = () => reject(request.error); + }); +} + +export async function countOutliers() { + const db = await openDb(); + return promisify(db.transaction('outliers', 'readonly').objectStore('outliers').count()); +}