mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-12-20 19:06:06 +01:00
Add:Quick match option
This commit is contained in:
parent
de32698ea5
commit
088969e1fe
@ -72,6 +72,10 @@
|
||||
<ui-btn v-if="isRootUser" :loading="savingMetadata" color="bg" type="button" class="h-full" small @click.stop.prevent="saveMetadata">Save Metadata</ui-btn>
|
||||
</ui-tooltip>
|
||||
|
||||
<ui-tooltip :disabled="!!quickMatching" :text="`(Root User Only) Populate empty book details & cover with first book result from '${libraryProvider}'. Does not overwrite details.`" direction="bottom" class="mr-4">
|
||||
<ui-btn v-if="isRootUser" :loading="quickMatching" color="bg" type="button" class="h-full" small @click.stop.prevent="quickMatch">Quick Match</ui-btn>
|
||||
</ui-tooltip>
|
||||
|
||||
<ui-tooltip :disabled="!!libraryScan" text="(Root User Only) Rescan audiobook including metadata" direction="bottom" class="mr-4">
|
||||
<ui-btn v-if="isRootUser" :loading="rescanning" :disabled="!!libraryScan" color="bg" type="button" class="h-full" small @click.stop.prevent="rescan">Re-Scan</ui-btn>
|
||||
</ui-tooltip>
|
||||
@ -113,7 +117,8 @@ export default {
|
||||
resettingProgress: false,
|
||||
isScrollable: false,
|
||||
savingMetadata: false,
|
||||
rescanning: false
|
||||
rescanning: false,
|
||||
quickMatching: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -163,12 +168,41 @@ export default {
|
||||
libraryId() {
|
||||
return this.audiobook ? this.audiobook.libraryId : null
|
||||
},
|
||||
libraryProvider() {
|
||||
return this.$store.getters['libraries/getLibraryProvider'](this.libraryId) || 'google'
|
||||
},
|
||||
libraryScan() {
|
||||
if (!this.libraryId) return null
|
||||
return this.$store.getters['scanners/getLibraryScan'](this.libraryId)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
quickMatch() {
|
||||
this.quickMatching = true
|
||||
var matchOptions = {
|
||||
provider: this.libraryProvider,
|
||||
title: details.title,
|
||||
author: details.author !== this.book.author ? details.author : null
|
||||
}
|
||||
this.$axios
|
||||
.$post(`/api/books/${this.audiobookId}/match`, matchOptions)
|
||||
.then((res) => {
|
||||
this.quickMatching = false
|
||||
if (res.warning) {
|
||||
this.$toast.warning(res.warning)
|
||||
} else if (res.updated) {
|
||||
this.$toast.success('Audiobook details updated')
|
||||
} else {
|
||||
this.$toast.info('No updates were made')
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
var errMsg = error.response ? error.response.data || '' : ''
|
||||
console.error('Failed to match', error)
|
||||
this.$toast.error(errMsg || 'Failed to match')
|
||||
this.quickMatching = false
|
||||
})
|
||||
},
|
||||
audiobookScanComplete(result) {
|
||||
this.rescanning = false
|
||||
if (!result) {
|
||||
|
@ -69,7 +69,7 @@ export default {
|
||||
var newfolderpaths = this.folderPaths.join(',')
|
||||
var origfolderpaths = this.library.folders.map((f) => f.fullPath).join(',')
|
||||
|
||||
return newfolderpaths === origfolderpaths && this.name === this.library.name
|
||||
return newfolderpaths === origfolderpaths && this.name === this.library.name && this.provider === this.library.provider
|
||||
},
|
||||
providers() {
|
||||
return this.$store.state.scanners.providers
|
||||
|
@ -20,6 +20,11 @@ export const getters = {
|
||||
},
|
||||
getSortedLibraries: state => () => {
|
||||
return state.libraries.map(lib => ({ ...lib })).sort((a, b) => a.displayOrder - b.displayOrder)
|
||||
},
|
||||
getLibraryProvider: state => libraryId => {
|
||||
var library = state.libraries.find(l => l.id === libraryId)
|
||||
if (!library) return null
|
||||
return library.provider
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,7 @@ class ApiController {
|
||||
this.router.post('/books/:id/cover', BookController.uploadCover.bind(this))
|
||||
this.router.get('/books/:id/cover', BookController.getCover.bind(this))
|
||||
this.router.patch('/books/:id/coverfile', BookController.updateCoverFromFile.bind(this))
|
||||
this.router.post('/books/:id/match', BookController.match.bind(this))
|
||||
|
||||
// TEMP: Support old syntax for mobile app
|
||||
this.router.get('/audiobooks', BookController.findAll.bind(this)) // Old route should pass library id
|
||||
|
@ -18,9 +18,6 @@ class BookController {
|
||||
}
|
||||
|
||||
findOne(req, res) {
|
||||
if (!req.user) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
var audiobook = this.db.audiobooks.find(a => a.id === req.params.id)
|
||||
if (!audiobook) return res.sendStatus(404)
|
||||
|
||||
@ -48,8 +45,8 @@ class BookController {
|
||||
var hasUpdates = audiobook.update(req.body)
|
||||
if (hasUpdates) {
|
||||
await this.db.updateAudiobook(audiobook)
|
||||
this.emitter('audiobook_updated', audiobook.toJSONExpanded())
|
||||
}
|
||||
this.emitter('audiobook_updated', audiobook.toJSONExpanded())
|
||||
res.json(audiobook.toJSON())
|
||||
}
|
||||
|
||||
@ -259,5 +256,71 @@ class BookController {
|
||||
}
|
||||
return this.cacheManager.handleCoverCache(res, audiobook, options)
|
||||
}
|
||||
|
||||
// POST api/books/:id/match
|
||||
async match(req, res) {
|
||||
if (!req.user.canUpdate) {
|
||||
Logger.warn('User attempted to match without permission', req.user)
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
var audiobook = this.db.audiobooks.find(a => a.id === req.params.id)
|
||||
if (!audiobook || !audiobook.book.cover) return res.sendStatus(404)
|
||||
|
||||
// Check user can access this audiobooks library
|
||||
if (!req.user.checkCanAccessLibrary(audiobook.libraryId)) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
|
||||
var options = req.body || {}
|
||||
var provider = options.provider || 'google'
|
||||
var searchTitle = options.title || audiobook.book._title
|
||||
var searchAuthor = options.author || audiobook.book._author
|
||||
|
||||
var results = await this.bookFinder.search(provider, searchTitle, searchAuthor)
|
||||
if (!results.length) {
|
||||
return res.json({
|
||||
warning: `No ${provider} match found`
|
||||
})
|
||||
}
|
||||
var matchData = results[0]
|
||||
|
||||
// Update cover if not set OR overrideCover flag
|
||||
var hasUpdated = false
|
||||
if (matchData.cover && (!audiobook.book.cover || options.overrideCover)) {
|
||||
Logger.debug(`[BookController] Updating cover "${matchData.cover}"`)
|
||||
var coverResult = await this.coverController.downloadCoverFromUrl(audiobook, matchData.cover)
|
||||
if (!coverResult || coverResult.error || !coverResult.cover) {
|
||||
Logger.warn(`[BookController] Match cover "${matchData.cover}" failed to use: ${coverResult ? coverResult.error : 'Unknown Error'}`)
|
||||
} else {
|
||||
hasUpdated = true
|
||||
}
|
||||
}
|
||||
|
||||
// Update book details if not set OR overrideDetails flag
|
||||
const detailKeysToUpdate = ['title', 'subtitle', 'author', 'narrator', 'publisher', 'publishYear', 'series', 'volumeNumber', 'asin', 'isbn']
|
||||
const updatePayload = {}
|
||||
for (const key in matchData) {
|
||||
if (matchData[key] && detailKeysToUpdate.includes(key) && (!audiobook.book[key] || options.overrideDetails)) {
|
||||
updatePayload[key] = matchData[key]
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(updatePayload).length) {
|
||||
Logger.debug('[BookController] Updating details', updatePayload)
|
||||
if (audiobook.update({ book: updatePayload })) {
|
||||
hasUpdated = true
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUpdated) {
|
||||
await this.db.updateEntity('audiobook', audiobook)
|
||||
this.emitter('audiobook_updated', audiobook.toJSONExpanded())
|
||||
}
|
||||
|
||||
res.json({
|
||||
updated: hasUpdated,
|
||||
audiobook: audiobook.toJSONExpanded()
|
||||
})
|
||||
}
|
||||
}
|
||||
module.exports = new BookController()
|
@ -78,6 +78,10 @@ class Library {
|
||||
this.name = payload.name
|
||||
hasUpdates = true
|
||||
}
|
||||
if (payload.provider && payload.provider !== this.provider) {
|
||||
this.provider = payload.provider
|
||||
hasUpdates = true
|
||||
}
|
||||
if (!isNaN(payload.displayOrder) && payload.displayOrder !== this.displayOrder) {
|
||||
this.displayOrder = Number(payload.displayOrder)
|
||||
hasUpdates = true
|
||||
|
Loading…
Reference in New Issue
Block a user