From dbb62069ef2c985e55afd9a4be68a3c4bceafbce Mon Sep 17 00:00:00 2001
From: Nick Thomson <nick-github@undergrid.org.uk>
Date: Fri, 23 Sep 2022 17:51:34 +0100
Subject: [PATCH] Implementation of batch quick match API and related options
 dialog

---
 .../modals/BatchQuickMatchModel.vue           | 89 +++++++++++++++----
 client/layouts/default.vue                    |  1 +
 server/controllers/LibraryItemController.js   | 25 ++++++
 server/routers/ApiRouter.js                   |  1 +
 4 files changed, 99 insertions(+), 17 deletions(-)

diff --git a/client/components/modals/BatchQuickMatchModel.vue b/client/components/modals/BatchQuickMatchModel.vue
index d02c39f4..c424baaa 100644
--- a/client/components/modals/BatchQuickMatchModel.vue
+++ b/client/components/modals/BatchQuickMatchModel.vue
@@ -10,7 +10,39 @@
       <div v-if="show" class="w-full h-full">
         <div class="py-4 px-4">
           <h1 class="text-2xl">Quick Match {{ selectedBookIds.length }} Books</h1>
-        </div>
+		</div>
+		
+		<div class="w-full overflow-y-auto overflow-x-hidden max-h-96">
+          <div class="flex px-8 items-center py-2">
+		    <p class="pr-4">Provider</p>
+			<ui-dropdown v-model="options.provider" :items="providers" small />
+	      </div>
+          <div class="flex px-8 items-end py-2">
+            <ui-toggle-switch v-model="options.overrideCover"/>
+            <ui-tooltip :text="tooltips.updateCovers">
+              <p class="pl-4">
+			    Update Covers
+                <span class="material-icons icon-text text-sm">info_outlined</span>
+              </p>
+            </ui-tooltip>
+          </div>
+          <div class="flex px-8 items-end py-2">
+            <ui-toggle-switch v-model="options.overrideDetails"/>
+            <ui-tooltip :text="tooltips.updateDetails">
+              <p class="pl-4">
+                Update Details
+                <span class="material-icons icon-text text-sm">info_outlined</span>
+              </p>
+            </ui-tooltip>
+          </div>
+          <div class="mt-4 py-4 border-b border-white border-opacity-10 text-white text-opacity-80" :class="isScrollable ? 'box-shadow-md-up' : 'border-t border-white border-opacity-5'">
+            <div class="flex items-center px-4">
+              <ui-btn type="button" @click="show = false">Cancel</ui-btn>
+              <div class="flex-grow" />
+              <ui-btn color="success" @click="doBatchQuickMatch">Continue</ui-btn>
+            </div>
+          </div>
+		</div>
       </div>
     </div>
   </modals-modal>
@@ -20,7 +52,16 @@
 export default {
   data() {
     return {
-      processing: false
+      processing: false,
+	  options: {
+		provider: 'google',
+		overrideDetails: true,
+		overrideCover: true
+	  },
+	  tooltips: {
+		  updateCovers: 'Update the selected book covers when a match is located.',
+		  updateDetails: 'Update the selected book details when a match is located.'
+	  }
     }
   },
   computed: {
@@ -45,26 +86,40 @@ export default {
     },
     currentLibraryId() {
       return this.$store.state.libraries.currentLibraryId
+    },
+    providers() {
+      if (this.isPodcast) return this.$store.state.scanners.podcastProviders
+      return this.$store.state.scanners.providers
     }
   },
   methods: {
+	  doBatchQuickMatch() {
+		  if (!this.selectedBookIds.length) return
+		  if (this.processing) return
+		  
+		  this.processing = true
+		  this.$store.commit('setProcessingBatch', true)
+		  this.$axios
+            .$post(`/api/items/batch/quickmatch`, {
+			  options: this.options,
+              libraryItemIds: this.selectedBookIds
+            })
+            .then(() => {
+              this.$toast.success('Batch quick match success!')
+              this.processing = false
+              this.$store.commit('setProcessingBatch', false)
+			  this.show = false
+            })
+            .catch((error) => {
+              this.$toast.error('Batch quick match failed')
+              console.error('Failed to batch quick match', error)
+              this.processing = false
+              this.$store.commit('setProcessingBatch', false)
+			  this.show = false
+            })
+	  }
   },
   mounted() {}
 }
 </script>
 
-<style>
-.list-complete-item {
-  transition: all 0.8s ease;
-}
-
-.list-complete-enter-from,
-.list-complete-leave-to {
-  opacity: 0;
-  transform: translateY(30px);
-}
-
-.list-complete-leave-active {
-  position: absolute;
-}
-</style>
\ No newline at end of file
diff --git a/client/layouts/default.vue b/client/layouts/default.vue
index 3e4202f2..89be096e 100644
--- a/client/layouts/default.vue
+++ b/client/layouts/default.vue
@@ -15,6 +15,7 @@
     <modals-podcast-edit-episode />
     <modals-podcast-view-episode />
     <modals-authors-edit-modal />
+	<modals-batch-quick-match-model />
     <prompt-confirm />
     <readers-reader />
   </div>
diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js
index 328e75a5..9eba9cc6 100644
--- a/server/controllers/LibraryItemController.js
+++ b/server/controllers/LibraryItemController.js
@@ -305,6 +305,31 @@ class LibraryItemController {
     res.json(libraryItems)
   }
 
+  // POST: api/items/batch/quickmatch
+  async batchQuickMatch(req, res) {
+	var itemsUpdated = 0
+
+	var matchData = req.body
+	var options = matchData.options || {}
+	var items = matchData.libraryItemIds
+    if (!items || !items.length) {
+      return res.sendStatus(500)
+    }
+	
+    for (let i = 0; i < items.length; i++) {
+		var libraryItem = this.db.libraryItems.find(_li => _li.id === items[i])
+		var matchResult = await this.scanner.quickMatchLibraryItem(libraryItem, options)
+		if (matchResult.updated) {
+			itemsUpdated++
+		}
+	}
+	
+	res.json({
+	  success: itemsUpdated > 0,
+	  updates: itemsUpdated
+    })
+  }
+
   // DELETE: api/items/all
   async deleteAll(req, res) {
     if (!req.user.isAdminOrUp) {
diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js
index edb293df..27b8233c 100644
--- a/server/routers/ApiRouter.js
+++ b/server/routers/ApiRouter.js
@@ -101,6 +101,7 @@ class ApiRouter {
     this.router.post('/items/batch/delete', LibraryItemController.batchDelete.bind(this))
     this.router.post('/items/batch/update', LibraryItemController.batchUpdate.bind(this))
     this.router.post('/items/batch/get', LibraryItemController.batchGet.bind(this))
+	this.router.post('/items/batch/quickmatch', LibraryItemController.batchQuickMatch.bind(this))
 
     //
     // User Routes