Browse Source

Fixes #23 short polling

isundil 1 năm trước cách đây
mục cha
commit
8f8b290b07
4 tập tin đã thay đổi với 57 bổ sung32 xóa
  1. 11 11
      model/mediaService.js
  2. 10 9
      router/api.js
  3. 33 9
      static/public/js/medias.js
  4. 3 3
      static/public/js/uiMedia.js

+ 11 - 11
model/mediaService.js

@@ -131,7 +131,7 @@ function reduceReqToMediaStruct(acc, i) {
     return acc;
 }
 
-module.exports.fetchMultiple = async function(app, md5sums, accessList) {
+module.exports.fetchMultiple = async function(app, md5sums, accessList, maxVersion) {
     let result = ((await app.databaseHelper.runSql(`
         select mediaFile.path, mediaFile.md5sum, mediaFile.date, mediaFile.fixedSum,
         mediaMeta.key as metaKey, mediaMeta.value as metaValue,
@@ -140,7 +140,7 @@ module.exports.fetchMultiple = async function(app, md5sums, accessList) {
         from mediaFile
         left join mediaMeta on mediaMeta.md5sum=mediaFile.fixedSum
         left join mediaTag on mediaTag.md5sum=mediaFile.fixedSum
-        where mediaFile.fixedSum in (` +md5sums.map(x => '?').join(',')+`)`, md5sums)) || []).reduce(reduceReqToMediaStruct, {}) || null;
+        where mediaFile.version>? and mediaFile.fixedSum in (` +md5sums.map(x => '?').join(',')+`)`, [maxVersion].concat(md5sums))) || []).reduce(reduceReqToMediaStruct, {}) || null;
     accessList = await buildAccessList(app, accessList);
     for (let key in result)
         if (!result[key].HaveAccess(accessList))
@@ -148,8 +148,8 @@ module.exports.fetchMultiple = async function(app, md5sums, accessList) {
     return result;
 }
 
-module.exports.fetchOne = async function(app, md5sum, accessList) {
-    return (await module.exports.fetchMultiple(app, [md5sum], accessList))[md5sum] || null;
+module.exports.fetchOne = async function(app, md5sum, accessList, maxVersion) {
+    return (await module.exports.fetchMultiple(app, [md5sum], accessList, maxVersion))[md5sum] || null;
 }
 
 function fetchAccessSubQuery(args, access) {
@@ -175,11 +175,11 @@ function fetchAccessSubQuery(args, access) {
     return result.join(" or ") || "1=0";
 }
 
-function fetchMediasBuildSubQuery(startTs, count, access) {
+function fetchMediasBuildSubQuery(startTs, count, access, maxVersion) {
     let query = "select distinct fixedSum from mediaFile";
-    let whereClause = ' where 1=1';
+    let whereClause = ` where version>?`;
     let accessWhere = null;
-    let args = [];
+    let args = [maxVersion];
 
     // access filter
     if (access !== undefined) {
@@ -209,8 +209,8 @@ function fetchMediasBuildSubQuery(startTs, count, access) {
     };
 }
 
-module.exports.fetchMedias = async function(app, startTs, count, access) {
-    let subQuery = fetchMediasBuildSubQuery(startTs, count, access);
+module.exports.fetchMedias = async function(app, startTs, count, access, maxVersion) {
+    let subQuery = fetchMediasBuildSubQuery(startTs, count, access, maxVersion);
     let result = ((await app.databaseHelper.runSql(`
         select mediaFile.path, mediaFile.md5sum, mediaFile.date, mediaFile.fixedSum,
         mediaMeta.key as metaKey, mediaMeta.value as metaValue,
@@ -226,13 +226,13 @@ module.exports.fetchMedias = async function(app, startTs, count, access) {
     return result;
 };
 
-module.exports.fetchMediasWithAccess = async function(app, startTs, count, access) {
+module.exports.fetchMediasWithAccess = async function(app, startTs, count, access, maxVersion) {
     let result = [];
     let lastTs = startTs;
 
     access = await buildAccessList(app, access);
     while (result.length < count) {
-        let tmp = await module.exports.fetchMedias(app, lastTs, 25, access);
+        let tmp = await module.exports.fetchMedias(app, lastTs, 25, access, maxVersion);
         if (!tmp.length)
             return result;
         lastTs = tmp[tmp.length-1].date;

+ 10 - 9
router/api.js

@@ -55,12 +55,12 @@ module.exports = { register: app => {
             checksum = req.body['list[]'];
         }
 
-        let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList);
+        let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
         data = Object.keys(data).map(x => data[x]).filter(x => x.ACCESS_TYPE != ACCESS_GRANT.write);
         await Promise.all(data.map(x => x.updateVersionInDb(app.databaseHelper)));
 
         await app.databaseHelper.remove(MediaFileTagModel, { md5sum: data.map(x => x.fixedSum), tag: decodeURIComponent(req.params.tag), fromMeta: 0 });
-        const allMedias = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList);
+        const allMedias = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
         app.routerUtils.jsonResponse(res, Object.keys(allMedias).map(x => allMedias[x]).map(x => MediaToJson(x)));
     });
     app.router.put("/api/media/:id/tag", async (req, res) => {
@@ -76,7 +76,7 @@ module.exports = { register: app => {
             checksum = req.body['list[]'];
         }
 
-        let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList);
+        let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
         data = Object.keys(data)
             .map(x => data[x])
             .filter(x => {
@@ -92,7 +92,7 @@ module.exports = { register: app => {
 
         let tag = data.map(x => new MediaFileTagModel(x.fixedSum, requestedTag, false));
         await app.databaseHelper.insertMultipleSameTable(tag);
-        const allMedias = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList);
+        const allMedias = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
         app.routerUtils.jsonResponse(res, Object.keys(allMedias).map(x => allMedias[x]).map(x => MediaToJson(x)));
     });
     app.router.patch("/api/media/:id/meta/:key", async (req, res) => {
@@ -107,7 +107,7 @@ module.exports = { register: app => {
             checksum = req.body['list[]'];
         }
 
-        let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList);
+        let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
         data = Object.keys(data)
             .map(x => data[x])
             .filter(x => x.ACCESS_TYPE != ACCESS_GRANT.write);
@@ -139,21 +139,22 @@ module.exports = { register: app => {
                 app,
                 isNaN(fromDate) ? 0 : fromDate,
                 isNaN(count) ? 25 : Math.min(350, count),
-                req.sessionObj?.accessList)).map(MediaToJson),
+                req.sessionObj?.accessList,
+                req.body?.version || 0)).map(MediaToJson),
             first: first,
             last: last
         });
     });
     app.router.get("/api/media/:md5sum", async (req, res) => {
         app.routerUtils.onApiRequest(req, res);
-        let data = MediaToJson(await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList));
+        let data = MediaToJson(await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList, 0));
         if (!data)
             return app.routerUtils.onPageNotFound(res);
         app.routerUtils.jsonResponse(res, data);
     });
     app.router.get("/api/media/thumbnail/:md5sum.jpg", async (req, res) => {
         app.routerUtils.onApiRequest(req, res);
-        let data = await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList);
+        let data = await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList, 0);
         if (!data)
             return app.routerUtils.onPageNotFound(res);
         try {
@@ -183,7 +184,7 @@ module.exports = { register: app => {
     });
     app.router.get("/api/media/original/:md5sum", async (req, res) => {
         app.routerUtils.onApiRequest(req, res);
-        let data = await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList);
+        let data = await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList, 0);
         if (!data)
             return app.routerUtils.onPageNotFound(res);
         const fileName = Path.basename(data.path);

+ 33 - 9
static/public/js/medias.js

@@ -8,6 +8,7 @@ class Media {
         this.fileName = data.fileName;
         this.meta = data.meta || {};
         this.fixedTags = [];
+        this.version = data.version;
 
         this.tags = [];
         this.writeAccess = data.accessType === 2;
@@ -67,6 +68,15 @@ function tryLoadMedia(md5sum) {
 
 class MediaStorage extends EventTarget
 {
+    allMeta = {};
+    allMetaTypes = {};
+    allTags = new Set();
+    medias = [];
+    oldest = null;
+    newest = null;
+    dbVersion = 0;
+    loadingVersion = 0;
+
     constructor() {
         super();
         this.#reset();
@@ -79,6 +89,8 @@ class MediaStorage extends EventTarget
         this.medias = [];
         this.oldest = null;
         this.newest = null;
+        this.dbVersion = 0;
+        this.loadingVersion = 0;
     }
 
     rebuildMetaList() {
@@ -88,7 +100,11 @@ class MediaStorage extends EventTarget
         this.downloadMetaList();
     }
 
-    downloadMetaList() {
+    update() {
+        this.downloadMetaList(true);
+    }
+
+    downloadMetaList(isUpdate) {
         if (this.isLoading())
             return;
         this.#isLoading = true;
@@ -96,20 +112,21 @@ class MediaStorage extends EventTarget
         LoadingTasks.push(() => {
             return new Promise(ok => {
                 let chronology = window.chronology.isInitialized() ? "" : "&chronology"
-                let oldest = (this.oldest?.date?.getTime() || 0);
+                let oldest = isUpdate !== true ? (this.oldest?.date?.getTime() || 0) : 0;
                 let oldestArg = oldest ? `&from=${oldest}` : "";
                 let requestCount = 300;
-                $.get(`/api/media/list?count=${requestCount}${chronology}${oldestArg}`, data => {
+                $.get(`/api/media/list?count=${requestCount}${chronology}${oldestArg}&version=${this.dbVersion}`, data => {
                     this.pushAll(data.data.map(i => new Media(i)));
                     if (data.first || data.last)
                         window.chronology.rebuildRange(data.first, data.last);
                     this.#isLoading = false;
                     if ((data.data?.length || 0) < requestCount) {
                         document.getElementById("pch-infiniteScrollLoading").classList.add("hidden");
+                        this.dbVersion = Math.max(this.dbVersion, this.loadingVersion);
                         window.ReloadFilters(MediaStorage.Instance);
                     }
                     else
-                        setTimeout(this.downloadMetaList.bind(this), 25);
+                        setTimeout(this.downloadMetaList.bind(this, isUpdate), 25);
                     ok();
                 });
             });
@@ -152,21 +169,27 @@ class MediaStorage extends EventTarget
     isLoading() { return this.#isLoading; }
 
     pushAll(arr, partialLoad) {
-        let result = [];
         let reorder = false;
+        let newItems = [];
+
         for (let i of arr) {
+            this.loadingVersion = Math.max(this.loadingVersion, i.version);
             if (partialLoad !== true) {
                 this.oldest = !this.oldest || this.oldest.date.getTime() > i.date.getTime() ? i : this.oldest;
                 this.newest = !this.newest || this.newest.date.getTime() < i.date.getTime() ? i : this.newest;
             }
             if (this.medias.length && this.medias[this.medias.length -1].date.getTime() < i.date.getTime())
                 reorder = true;
-            if (this.medias.find(x => x.fixedSum === i.fixedSum))
-                continue;
+            let previous = this.medias.find(x => x.fixedSum === i.fixedSum);
+            if (previous) {
+                this.medias = this.medias.filter(x => x.fixedSum !== i.fixedSum);
+                i.ui = previous.ui;
+            } else {
+                newItems.push(i);
+            }
             this.#pushUnique(i);
-            result.push(i);
         }
-        for (let i of result)
+        for (let i of newItems)
             this.dispatchEvent(new CustomEvent("newMedia", { detail: i }));
         if (reorder) {
             this.medias.sort((a, b) => b.date.getTime() - a.date.getTime());
@@ -313,4 +336,5 @@ class MediaStorage extends EventTarget
 }
 
 MediaStorage.Instance = new MediaStorage();
+setInterval(MediaStorage.Instance.update.bind(MediaStorage.Instance), 60000);
 

+ 3 - 3
static/public/js/uiMedia.js

@@ -61,7 +61,6 @@ $(() => {
             }
             return false;
         }
-        mediaItem.setSelectionCheckboxValue = value => setSelectionCheckboxValue(mediaItem, value);
         let cascadeSetSelectionCheckboxValue = function(value) {
             if (!setSelectionCheckboxValue(mediaItem, value))
                 return;
@@ -113,7 +112,8 @@ $(() => {
         return mediaItem.ui = {
             root: container,
             img: img,
-            checkbox: checkbox
+            checkbox: checkbox,
+            setSelectionCheckboxValue: value => setSelectionCheckboxValue(mediaItem, value)
         };
     }
 
@@ -165,7 +165,7 @@ $(() => {
         targetDisplayedItems = displayItemBatchCount;
         for (let i of selectedThumbnails) {
             let media = MediaStorage.Instance.getMediaLocal(i);
-            media?.setSelectionCheckboxValue(false);
+            media?.ui?.setSelectionCheckboxValue(false);
         }
         lastSelection = null;
         for (let i of MediaStorage.Instance.medias)