diff --git a/client/components/app/Appbar.vue b/client/components/app/Appbar.vue index cc417739..842d559f 100644 --- a/client/components/app/Appbar.vue +++ b/client/components/app/Appbar.vue @@ -49,6 +49,9 @@

{{ numLibraryItemsSelected }} Selected

+ + + @@ -210,7 +213,10 @@ export default { }, setBookshelfTotalEntities(totalEntities) { this.totalEntities = totalEntities - } + }, + batchAutoMatchClick() { + this.$store.commit('globals/setShowBatchQuickMatchModal', true) + }, }, mounted() { this.$eventBus.$on('bookshelf-total-entities', this.setBookshelfTotalEntities) diff --git a/client/components/modals/BatchQuickMatchModel.vue b/client/components/modals/BatchQuickMatchModel.vue new file mode 100644 index 00000000..a80beefa --- /dev/null +++ b/client/components/modals/BatchQuickMatchModel.vue @@ -0,0 +1,141 @@ + + + + diff --git a/client/layouts/default.vue b/client/layouts/default.vue index 3e4202f2..1a23cde7 100644 --- a/client/layouts/default.vue +++ b/client/layouts/default.vue @@ -15,6 +15,7 @@ +
@@ -358,6 +359,18 @@ export default { // Force refresh location.reload() }, + batchQuickMatchComplete(result) { + var success = result.success || false + var toast = 'Batch quick match complete!\n' + result.updates + ' Updated' + if (result.unmatched && (result.unmatched > 0)) { + toast += '\n' + result.unmatched + ' with no matches' + } + if (success) { + this.$toast.success(toast) + } else { + this.$toast.info(toast) + } + }, initializeSocket() { this.socket = this.$nuxtSocket({ name: process.env.NODE_ENV === 'development' ? 'dev' : 'prod', @@ -429,6 +442,8 @@ export default { this.socket.on('rss_feed_closed', this.rssFeedClosed) this.socket.on('backup_applied', this.backupApplied) + + this.socket.on('batch_quickmatch_complete', this.batchQuickMatchComplete) }, showUpdateToast(versionData) { var ignoreVersion = localStorage.getItem('ignoreVersion') diff --git a/client/store/globals.js b/client/store/globals.js index 21a31d5a..9e837f00 100644 --- a/client/store/globals.js +++ b/client/store/globals.js @@ -14,6 +14,7 @@ export const state = () => ({ selectedAuthor: null, isCasting: false, // Actively casting isChromecastInitialized: false, // Script loaded + showBatchQuickMatchModal: false, dateFormats: [ { text: 'MM/DD/YYYY', @@ -108,5 +109,8 @@ export const mutations = { }, setCasting(state, val) { state.isCasting = val + }, + setShowBatchQuickMatchModal(state, val) { + state.showBatchQuickMatchModal = val } } \ No newline at end of file diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js index 328e75a5..acbc6926 100644 --- a/server/controllers/LibraryItemController.js +++ b/server/controllers/LibraryItemController.js @@ -305,6 +305,42 @@ class LibraryItemController { res.json(libraryItems) } + // POST: api/items/batch/quickmatch + async batchQuickMatch(req, res) { + if (!req.user.isAdminOrUp) { + Logger.warn('User other than admin attempted to batch quick match library items', req.user) + return res.sendStatus(403) + } + + var itemsUpdated = 0 + var itemsUnmatched = 0 + + var matchData = req.body + var options = matchData.options || {} + var items = matchData.libraryItemIds + if (!items || !items.length) { + return res.sendStatus(500) + } + res.sendStatus(200) + + 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++ + } else if (matchResult.warning) { + itemsUnmatched++ + } + } + + var result = { + success: itemsUpdated > 0, + updates: itemsUpdated, + unmatched: itemsUnmatched + } + this.clientEmitter(req.user.id, 'batch_quickmatch_complete', result) + } + // 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 9f612cbe..a689db36 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -103,6 +103,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 diff --git a/server/scanner/Scanner.js b/server/scanner/Scanner.js index acfcdd9e..21d95058 100644 --- a/server/scanner/Scanner.js +++ b/server/scanner/Scanner.js @@ -675,9 +675,11 @@ class Scanner { var provider = options.provider || 'google' var searchTitle = options.title || libraryItem.media.metadata.title var searchAuthor = options.author || libraryItem.media.metadata.authorName + var overrideDefaults = options.overrideDefaults || false - // Set to override existing metadata if scannerPreferMatchedMetadata setting is true - if (this.db.serverSettings.scannerPreferMatchedMetadata) { + // Set to override existing metadata if scannerPreferMatchedMetadata setting is true and + // the overrideDefaults option is not set or set to false. + if ((overrideDefaults == false) && (this.db.serverSettings.scannerPreferMatchedMetadata)) { options.overrideCover = true options.overrideDetails = true }