1
0

indexedCache.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. (() => {
  2. const INDEXEDCACHE_STORAGE_NAME = "databank";
  3. const INDEXEDCACHE_DATABASE_NAME = "media";
  4. const INDEXEDCACHE_DBVERSION_COLUMN = "dbVersion";
  5. IDBTransaction.READ_WRITE = "readwrite";
  6. IDBTransaction.READ = "readonly";
  7. class IndexedCache
  8. {
  9. #version = parseInt(localStorage?.getItem(INDEXEDCACHE_DBVERSION_COLUMN)) || 0;
  10. #openReq = null;
  11. #db = null;
  12. #mediaStore = null;
  13. constructor()
  14. {
  15. this.#openReq = new Promise((ok, ko) => {
  16. const DBOpenRequest = window.indexedDB.open(INDEXEDCACHE_STORAGE_NAME, 1);
  17. DBOpenRequest.onerror = evt => {
  18. console.error("Failed to open db: ", evt);
  19. this.onDbReady();
  20. };
  21. DBOpenRequest.onupgradeneeded = evt => {
  22. let mediaStore = evt.target.result.createObjectStore(INDEXEDCACHE_DATABASE_NAME, {
  23. autoIncrement: true,
  24. });
  25. };
  26. DBOpenRequest.onsuccess = evt => {
  27. this.onDbReady(evt.target.result);
  28. };
  29. });
  30. MediaStorage.Instance.addEventListener("doneLoading", async evt => {
  31. await this.pushMedias(MediaStorage.Instance.medias, MediaStorage.Instance.getDbVersion());
  32. let removed = await this.removeObsolete();
  33. removed.length && MediaStorage.Instance.removeMedia(removed);
  34. });
  35. }
  36. onDbReady(db, mediaStore) {
  37. this.#openReq = null;
  38. this.#db = db;
  39. this.#mediaStore = mediaStore;
  40. }
  41. #reduceMedia(media) {
  42. let mediaRawData = {
  43. date: media.getDateTs(),
  44. md5sum: media.md5sum,
  45. fixedSum: media.fixedSum,
  46. path: media.path,
  47. fileName: media.fileName,
  48. meta: media.meta,
  49. fixedTags: Array.from(media.fixedTags),
  50. tags: Array.from(media.tags),
  51. version: media.version,
  52. writeAccess: media.writeAccess
  53. };
  54. for (let i in mediaRawData.meta) {
  55. if (mediaRawData.meta[i].value instanceof Date)
  56. mediaRawData.meta[i].value = mediaRawData.meta[i].value.getTime();
  57. }
  58. return JSON.stringify(mediaRawData);
  59. }
  60. async listMedias() {
  61. await this.#openReq;
  62. if (!this.#db)
  63. return { medias: [], version: 0 };
  64. let data = [];
  65. try {
  66. await new Promise(ok => {
  67. let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ);
  68. transaction.oncomplete = ok;
  69. transaction.onerror = ok;
  70. let storage = transaction.objectStore(INDEXEDCACHE_DATABASE_NAME);
  71. let req = storage.getAll();
  72. req.onsuccess = () => {
  73. data = req.result.map(x => JSON.parse(x));
  74. }
  75. });
  76. }
  77. catch (err) {
  78. console.error(err);
  79. return { medias: [], version: 0 };
  80. }
  81. return { medias: data, version: data.length ? this.#version : 0 };
  82. }
  83. allKeyCount() {
  84. return new Promise(async ok => {
  85. let count = 0;
  86. let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ);
  87. transaction.oncomplete = () => ok(count);
  88. transaction.onerror = () => ok(count);
  89. count = (await (new Promise(okKey => {
  90. let req = transaction.objectStore(INDEXEDCACHE_DATABASE_NAME).getAllKeys();
  91. req.onsuccess = evt => {
  92. okKey(req?.result || []);
  93. }
  94. }))).length;
  95. });
  96. }
  97. #getObsoleteKeys() {
  98. return new Promise((ok, ko) => {
  99. $.get(`/api/media/sumlist`, async srvKeys => {
  100. let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ);
  101. let obsoleteIndexes = [];
  102. transaction.oncomplete = () => ok(obsoleteIndexes);
  103. transaction.onerror = () => ok(obsoleteIndexes);
  104. obsoleteIndexes = (await (new Promise((okLocalKey) => {
  105. let req = transaction.objectStore(INDEXEDCACHE_DATABASE_NAME).getAllKeys();
  106. req.onsuccess = evt => {
  107. okLocalKey(req?.result || []);
  108. }
  109. }))).filter(i => srvKeys.data.indexOf(i) === -1);
  110. });
  111. });
  112. }
  113. async removeItems(md5Sums) {
  114. await this.#openReq;
  115. if (!this.#db)
  116. return;
  117. for (let i of md5Sums)
  118. await new Promise((ok, ko) => {
  119. let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ_WRITE);
  120. transaction.oncomplete = ok;
  121. transaction.onerror = ko;
  122. transaction.objectStore(INDEXEDCACHE_DATABASE_NAME).delete(i);
  123. });
  124. }
  125. async removeObsolete() {
  126. await this.#openReq;
  127. if (!this.#db)
  128. return;
  129. let obsoleteIndexes = await this.#getObsoleteKeys();
  130. await this.removeItems(obsoleteIndexes);
  131. return obsoleteIndexes;
  132. }
  133. async pushMedias(mediaArr, version) {
  134. await this.#openReq;
  135. if (!this.#db)
  136. return;
  137. await new Promise(ok => {
  138. let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ_WRITE);
  139. transaction.oncomplete = ok;
  140. transaction.onerror = ok;
  141. let storage = transaction.objectStore(INDEXEDCACHE_DATABASE_NAME);
  142. mediaArr.forEach(x => storage.put(this.#reduceMedia(x), x.fixedSum));
  143. });
  144. localStorage?.setItem(INDEXEDCACHE_DBVERSION_COLUMN, version);
  145. this.#version = version;
  146. console.log("Done updating db");
  147. }
  148. async removeAll() {
  149. await this.#openReq;
  150. if (!this.#db)
  151. return;
  152. await new Promise(ok => {
  153. let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ_WRITE);
  154. transaction.oncomplete = ok;
  155. transaction.onerror = ok;
  156. let storage = transaction.objectStore(INDEXEDCACHE_DATABASE_NAME);
  157. storage.clear();
  158. });
  159. }
  160. async clean() {
  161. await this.#openReq;
  162. if (!this.#db)
  163. return;
  164. this.#db.transaction(INDEXEDCACHE_DATABASE_NAME, IDBTransaction.READ_WRITE);
  165. localStorage?.removeItem(INDEXEDCACHE_DBVERSION_COLUMN);
  166. this.#version = 0;
  167. }
  168. }
  169. window.indexedData = new IndexedCache();
  170. })();