(() => { const INDEXEDCACHE_STORAGE_NAME = "databank"; const INDEXEDCACHE_DATABASE_NAME = "media"; const INDEXEDCACHE_DBVERSION_COLUMN = "dbVersion"; IDBTransaction.READ_WRITE = "readwrite"; IDBTransaction.READ = "readonly"; class IndexedCache { #version = parseInt(localStorage?.getItem(INDEXEDCACHE_DBVERSION_COLUMN)) || 0; #openReq = null; #db = null; #mediaStore = null; constructor() { this.#openReq = new Promise((ok, ko) => { const DBOpenRequest = window.indexedDB.open(INDEXEDCACHE_STORAGE_NAME, 1); DBOpenRequest.onerror = evt => { console.error("Failed to open db: ", evt); this.onDbReady(); }; DBOpenRequest.onupgradeneeded = evt => { let mediaStore = evt.target.result.createObjectStore(INDEXEDCACHE_DATABASE_NAME, { autoIncrement: true, }); }; DBOpenRequest.onsuccess = evt => { this.onDbReady(evt.target.result); }; }); MediaStorage.Instance.addEventListener("doneLoading", async evt => { await this.pushMedias(MediaStorage.Instance.medias, MediaStorage.Instance.getDbVersion()); let removed = await this.removeObsolete(); removed.length && MediaStorage.Instance.removeMedia(removed); }); } onDbReady(db, mediaStore) { this.#openReq = null; this.#db = db; this.#mediaStore = mediaStore; } #reduceMedia(media) { let mediaRawData = { date: media.getDateTs(), md5sum: media.md5sum, fixedSum: media.fixedSum, path: media.path, fileName: media.fileName, meta: media.meta, fixedTags: Array.from(media.fixedTags), tags: Array.from(media.tags), version: media.version, writeAccess: media.writeAccess }; for (let i in mediaRawData.meta) { if (mediaRawData.meta[i].value instanceof Date) mediaRawData.meta[i].value = mediaRawData.meta[i].value.getTime(); } return JSON.stringify(mediaRawData); } async listMedias() { await this.#openReq; if (!this.#db) return { medias: [], version: 0 }; let data = []; try { await new Promise(ok => { let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ); transaction.oncomplete = ok; transaction.onerror = ok; let storage = transaction.objectStore(INDEXEDCACHE_DATABASE_NAME); let req = storage.getAll(); req.onsuccess = () => { data = req.result.map(x => JSON.parse(x)); } }); } catch (err) { console.error(err); return { medias: [], version: 0 }; } return { medias: data, version: data.length ? this.#version : 0 }; } allKeyCount() { return new Promise(async ok => { let count = 0; let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ); transaction.oncomplete = () => ok(count); transaction.onerror = () => ok(count); count = (await (new Promise(okKey => { let req = transaction.objectStore(INDEXEDCACHE_DATABASE_NAME).getAllKeys(); req.onsuccess = evt => { okKey(req?.result || []); } }))).length; }); } #getObsoleteKeys() { return new Promise((ok, ko) => { $.get(`/api/media/sumlist`, async srvKeys => { let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ); let obsoleteIndexes = []; transaction.oncomplete = () => ok(obsoleteIndexes); transaction.onerror = () => ok(obsoleteIndexes); obsoleteIndexes = (await (new Promise((okLocalKey) => { let req = transaction.objectStore(INDEXEDCACHE_DATABASE_NAME).getAllKeys(); req.onsuccess = evt => { okLocalKey(req?.result || []); } }))).filter(i => srvKeys.data.indexOf(i) === -1); }); }); } async removeItems(md5Sums) { await this.#openReq; if (!this.#db) return; for (let i of md5Sums) await new Promise((ok, ko) => { let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ_WRITE); transaction.oncomplete = ok; transaction.onerror = ko; transaction.objectStore(INDEXEDCACHE_DATABASE_NAME).delete(i); }); } async removeObsolete() { await this.#openReq; if (!this.#db) return; let obsoleteIndexes = await this.#getObsoleteKeys(); await this.removeItems(obsoleteIndexes); return obsoleteIndexes; } async pushMedias(mediaArr, version) { await this.#openReq; if (!this.#db) return; await new Promise(ok => { let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ_WRITE); transaction.oncomplete = ok; transaction.onerror = ok; let storage = transaction.objectStore(INDEXEDCACHE_DATABASE_NAME); mediaArr.forEach(x => storage.put(this.#reduceMedia(x), x.fixedSum)); }); localStorage?.setItem(INDEXEDCACHE_DBVERSION_COLUMN, version); this.#version = version; console.log("Done updating db"); } async removeAll() { await this.#openReq; if (!this.#db) return; await new Promise(ok => { let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ_WRITE); transaction.oncomplete = ok; transaction.onerror = ok; let storage = transaction.objectStore(INDEXEDCACHE_DATABASE_NAME); storage.clear(); }); } async clean() { await this.#openReq; if (!this.#db) return; this.#db.transaction(INDEXEDCACHE_DATABASE_NAME, IDBTransaction.READ_WRITE); localStorage?.removeItem(INDEXEDCACHE_DBVERSION_COLUMN); this.#version = 0; } } window.indexedData = new IndexedCache(); })();