mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-11-08 00:54:33 +01:00
Merge branch 'master' of https://github.com/advplyr/audiobookshelf
This commit is contained in:
commit
04cbc49577
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full h-16 bg-primary relative">
|
<div class="w-full h-16 bg-primary relative">
|
||||||
<div id="appbar" class="absolute top-0 bottom-0 left-0 w-full h-full px-2 md:px-6 py-1 z-40">
|
<div id="appbar" class="absolute top-0 bottom-0 left-0 w-full h-full px-2 md:px-6 py-1 z-50">
|
||||||
<div class="flex h-full items-center">
|
<div class="flex h-full items-center">
|
||||||
<img v-if="!showBack" src="/Logo48.png" class="w-10 h-10 md:w-12 md:h-12 mr-4" />
|
<img v-if="!showBack" src="/Logo48.png" class="w-10 h-10 md:w-12 md:h-12 mr-4" />
|
||||||
<a v-if="showBack" @click="back" class="rounded-full h-12 w-12 flex items-center justify-center hover:bg-white hover:bg-opacity-10 mr-4 cursor-pointer">
|
<a v-if="showBack" @click="back" class="rounded-full h-12 w-12 flex items-center justify-center hover:bg-white hover:bg-opacity-10 mr-4 cursor-pointer">
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<p>Series</p>
|
<p>Series</p>
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
</div>
|
</div>
|
||||||
<div id="toolbar" class="absolute top-10 md:top-0 left-0 w-full h-10 md:h-full z-20 flex items-center px-2 md:px-8">
|
<div id="toolbar" class="absolute top-10 md:top-0 left-0 w-full h-10 md:h-full z-30 flex items-center px-2 md:px-8">
|
||||||
<template v-if="page !== 'search' && !isHome">
|
<template v-if="page !== 'search' && !isHome">
|
||||||
<p v-if="!selectedSeries" class="font-book hidden md:block">{{ numShowing }} {{ entityName }}</p>
|
<p v-if="!selectedSeries" class="font-book hidden md:block">{{ numShowing }} {{ entityName }}</p>
|
||||||
<div v-else class="items-center hidden md:flex">
|
<div v-else class="items-center hidden md:flex">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-20 bg-bg h-full relative box-shadow-side z-30" style="min-width: 80px">
|
<div class="w-20 bg-bg h-full relative box-shadow-side z-40" style="min-width: 80px">
|
||||||
<div class="absolute top-0 -right-4 w-4 bg-bg h-10 pointer-events-none" />
|
<div class="absolute top-0 -right-4 w-4 bg-bg h-10 pointer-events-none" />
|
||||||
<nuxt-link :to="`/library/${currentLibraryId}`" class="w-full h-20 flex flex-col items-center justify-center text-white border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="homePage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
|
<nuxt-link :to="`/library/${currentLibraryId}`" class="w-full h-20 flex flex-col items-center justify-center text-white border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="homePage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label">
|
<ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-80 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label">
|
||||||
<template v-for="item in items">
|
<template v-for="item in items">
|
||||||
<li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" :class="item.value === selected ? 'bg-primary bg-opacity-50' : ''" role="option" @click="clickedOption(item.value)">
|
<li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" :class="item.value === selected ? 'bg-primary bg-opacity-50' : ''" role="option" @click="clickedOption(item.value)">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
<p class="text-xl font-book pl-4 hover:underline cursor-pointer" @click.stop="$emit('click', library)">{{ library.name }}</p>
|
<p class="text-xl font-book pl-4 hover:underline cursor-pointer" @click.stop="$emit('click', library)">{{ library.name }}</p>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<ui-btn v-show="isHovering && !libraryScan && canScan" small color="bg" @click.stop="scan">Scan</ui-btn>
|
<ui-btn v-show="isHovering && !libraryScan && canScan" small color="bg" @click.stop="scan">Scan</ui-btn>
|
||||||
|
<ui-btn v-show="isHovering && !libraryScan && canScan" small color="bg" class="ml-2" @click.stop="forceScan">Force Re-Scan</ui-btn>
|
||||||
<span v-show="isHovering && !libraryScan && showEdit && canEdit" class="material-icons text-xl text-gray-300 hover:text-gray-50 ml-4 cursor-pointer" @click.stop="editClick">edit</span>
|
<span v-show="isHovering && !libraryScan && showEdit && canEdit" class="material-icons text-xl text-gray-300 hover:text-gray-50 ml-4 cursor-pointer" @click.stop="editClick">edit</span>
|
||||||
<span v-show="!libraryScan && isHovering && showEdit && canDelete && !isDeleting" class="material-icons text-xl text-gray-300 ml-3" :class="isMain ? 'text-opacity-5 cursor-not-allowed' : 'hover:text-gray-50 cursor-pointer'" @click.stop="deleteClick">delete</span>
|
<span v-show="!libraryScan && isHovering && showEdit && canDelete && !isDeleting" class="material-icons text-xl text-gray-300 ml-3" :class="isMain ? 'text-opacity-5 cursor-not-allowed' : 'hover:text-gray-50 cursor-pointer'" @click.stop="deleteClick">delete</span>
|
||||||
<div v-show="isDeleting" class="text-xl text-gray-300 ml-3 animate-spin">
|
<div v-show="isDeleting" class="text-xl text-gray-300 ml-3 animate-spin">
|
||||||
@ -58,15 +59,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
itemClicked() {
|
|
||||||
// this.$emit('click', this.library)
|
|
||||||
},
|
|
||||||
editClick() {
|
editClick() {
|
||||||
this.$emit('edit', this.library)
|
this.$emit('edit', this.library)
|
||||||
},
|
},
|
||||||
scan() {
|
scan() {
|
||||||
this.$root.socket.emit('scan', this.library.id)
|
this.$root.socket.emit('scan', this.library.id)
|
||||||
},
|
},
|
||||||
|
forceScan() {
|
||||||
|
this.$root.socket.emit('scan', this.library.id, { forceRescan: true })
|
||||||
|
},
|
||||||
deleteClick() {
|
deleteClick() {
|
||||||
if (this.isMain) return
|
if (this.isMain) return
|
||||||
if (confirm(`Are you sure you want to permanently delete library "${this.library.name}"?`)) {
|
if (confirm(`Are you sure you want to permanently delete library "${this.library.name}"?`)) {
|
||||||
|
@ -6,14 +6,16 @@
|
|||||||
<span class="material-icons" style="font-size: 1.4rem">add</span>
|
<span class="material-icons" style="font-size: 1.4rem">add</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<draggable v-model="libraryCopies" v-bind="dragOptions" class="list-group" draggable=".item" tag="div" @start="startDrag" @end="endDrag">
|
<draggable :list="libraryCopies" v-bind="dragOptions" class="list-group" draggable=".item" tag="div" @start="startDrag" @end="endDrag">
|
||||||
<!-- <transition-group type="transition" :name="!drag ? 'flip-list' : null"> -->
|
|
||||||
<template v-for="library in libraryCopies">
|
<template v-for="library in libraryCopies">
|
||||||
<modals-libraries-library-item :key="library.id" :library="library" :selected="currentLibraryId === library.id" :show-edit="true" :dragging="drag" @edit="editLibrary" @click="setLibrary" class="item" />
|
<div :key="library.id" class="item">
|
||||||
|
<modals-libraries-library-item :library="library" :selected="currentLibraryId === library.id" :show-edit="true" :dragging="drag" @edit="editLibrary" @sort="draggableSort" @click="setLibrary" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- </transition-group> -->
|
|
||||||
</draggable>
|
</draggable>
|
||||||
<modals-edit-library-modal v-model="showLibraryModal" :library="selectedLibrary" />
|
<modals-edit-library-modal v-model="showLibraryModal" :library="selectedLibrary" />
|
||||||
|
|
||||||
|
<p class="text-xs mt-4 text-gray-200">*<strong>Force Re-Scan</strong> will scan all files again like a fresh scan. Audio file ID3 tags, OPF files, and text files will be probed/parsed and used for book details.</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -48,6 +50,9 @@ export default {
|
|||||||
},
|
},
|
||||||
libraries() {
|
libraries() {
|
||||||
return this.$store.getters['libraries/getSortedLibraries']()
|
return this.$store.getters['libraries/getSortedLibraries']()
|
||||||
|
},
|
||||||
|
libraryScans() {
|
||||||
|
return this.$store.state.scanners.libraryScans
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -55,10 +60,9 @@ export default {
|
|||||||
this.drag = true
|
this.drag = true
|
||||||
clearTimeout(this.orderTimeout)
|
clearTimeout(this.orderTimeout)
|
||||||
},
|
},
|
||||||
endDrag() {
|
endDrag(e) {
|
||||||
this.drag = false
|
this.drag = false
|
||||||
this.checkOrder()
|
this.checkOrder()
|
||||||
console.log('DRAG END')
|
|
||||||
},
|
},
|
||||||
checkOrder() {
|
checkOrder() {
|
||||||
clearTimeout(this.orderTimeout)
|
clearTimeout(this.orderTimeout)
|
||||||
@ -78,7 +82,7 @@ export default {
|
|||||||
})
|
})
|
||||||
var newOrder = libraryOrderData.map((lib) => lib.id).join(',')
|
var newOrder = libraryOrderData.map((lib) => lib.id).join(',')
|
||||||
if (currOrder !== newOrder) {
|
if (currOrder !== newOrder) {
|
||||||
this.$axios.$patch('/api/libraries/order', libraryOrderData).then((libraries) => {
|
this.$axios.$post('/api/libraries/order', libraryOrderData).then((libraries) => {
|
||||||
if (libraries && libraries.length) {
|
if (libraries && libraries.length) {
|
||||||
this.$toast.success('Library order saved', { timeout: 1500 })
|
this.$toast.success('Library order saved', { timeout: 1500 })
|
||||||
this.$store.commit('libraries/set', libraries)
|
this.$store.commit('libraries/set', libraries)
|
||||||
|
@ -210,7 +210,7 @@ export const mutations = {
|
|||||||
}
|
}
|
||||||
if (audiobook.book.genres && audiobook.book.genres.length) {
|
if (audiobook.book.genres && audiobook.book.genres.length) {
|
||||||
audiobook.book.genres.forEach((genre) => {
|
audiobook.book.genres.forEach((genre) => {
|
||||||
if (tag && !state.filterData.genres.includes(genre)) state.filterData.genres.push(genre)
|
if (genre && !state.filterData.genres.includes(genre)) state.filterData.genres.push(genre)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,8 @@ class ApiController {
|
|||||||
this.router.get('/libraries/:id/search', LibraryController.middleware.bind(this), LibraryController.search.bind(this))
|
this.router.get('/libraries/:id/search', LibraryController.middleware.bind(this), LibraryController.search.bind(this))
|
||||||
this.router.get('/libraries/:id/stats', LibraryController.middleware.bind(this), LibraryController.stats.bind(this))
|
this.router.get('/libraries/:id/stats', LibraryController.middleware.bind(this), LibraryController.stats.bind(this))
|
||||||
this.router.get('/libraries/:id/authors', LibraryController.middleware.bind(this), LibraryController.getAuthors.bind(this))
|
this.router.get('/libraries/:id/authors', LibraryController.middleware.bind(this), LibraryController.getAuthors.bind(this))
|
||||||
this.router.patch('/libraries/order', LibraryController.reorder.bind(this))
|
this.router.post('/libraries/order', LibraryController.reorder.bind(this))
|
||||||
|
|
||||||
|
|
||||||
// TEMP: Support old syntax for mobile app
|
// TEMP: Support old syntax for mobile app
|
||||||
this.router.get('/library/:id/audiobooks', LibraryController.middleware.bind(this), LibraryController.getBooksForLibrary.bind(this))
|
this.router.get('/library/:id/audiobooks', LibraryController.middleware.bind(this), LibraryController.getBooksForLibrary.bind(this))
|
||||||
|
@ -316,9 +316,9 @@ class Server {
|
|||||||
await this.scanner2.scanFilesChanged(fileUpdates)
|
await this.scanner2.scanFilesChanged(fileUpdates)
|
||||||
}
|
}
|
||||||
|
|
||||||
async scan(libraryId) {
|
async scan(libraryId, options = {}) {
|
||||||
Logger.info('[Server] Starting Scan')
|
Logger.info('[Server] Starting Scan')
|
||||||
await this.scanner2.scan(libraryId)
|
await this.scanner2.scan(libraryId, options)
|
||||||
// await this.scanner.scan(libraryId)
|
// await this.scanner.scan(libraryId)
|
||||||
Logger.info('[Server] Scan complete')
|
Logger.info('[Server] Scan complete')
|
||||||
}
|
}
|
||||||
|
@ -96,9 +96,9 @@ class AudioFile {
|
|||||||
this.exclude = !!data.exclude
|
this.exclude = !!data.exclude
|
||||||
this.error = data.error || null
|
this.error = data.error || null
|
||||||
|
|
||||||
this.trackNumFromMeta = data.trackNumFromMeta || null
|
this.trackNumFromMeta = data.trackNumFromMeta
|
||||||
this.trackNumFromFilename = data.trackNumFromFilename || null
|
this.trackNumFromFilename = data.trackNumFromFilename
|
||||||
this.cdNumFromFilename = data.cdNumFromFilename || null
|
this.cdNumFromFilename = data.cdNumFromFilename
|
||||||
|
|
||||||
this.format = data.format
|
this.format = data.format
|
||||||
this.duration = data.duration
|
this.duration = data.duration
|
||||||
@ -130,9 +130,9 @@ class AudioFile {
|
|||||||
this.fullPath = data.fullPath
|
this.fullPath = data.fullPath
|
||||||
this.addedAt = Date.now()
|
this.addedAt = Date.now()
|
||||||
|
|
||||||
this.trackNumFromMeta = data.trackNumFromMeta || null
|
this.trackNumFromMeta = data.trackNumFromMeta
|
||||||
this.trackNumFromFilename = data.trackNumFromFilename || null
|
this.trackNumFromFilename = data.trackNumFromFilename
|
||||||
this.cdNumFromFilename = data.cdNumFromFilename || null
|
this.cdNumFromFilename = data.cdNumFromFilename
|
||||||
|
|
||||||
this.manuallyVerified = !!data.manuallyVerified
|
this.manuallyVerified = !!data.manuallyVerified
|
||||||
this.invalid = !!data.invalid
|
this.invalid = !!data.invalid
|
||||||
@ -302,9 +302,9 @@ class AudioFile {
|
|||||||
hasUpdated = true
|
hasUpdated = true
|
||||||
}
|
}
|
||||||
} else if (this[key] !== newjson[key]) {
|
} else if (this[key] !== newjson[key]) {
|
||||||
|
// console.log(this.filename, 'key', key, 'updated', this[key], newjson[key])
|
||||||
this[key] = newjson[key]
|
this[key] = newjson[key]
|
||||||
hasUpdated = true
|
hasUpdated = true
|
||||||
// console.log('key', key, 'updated', this[key], newjson[key])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hasUpdated
|
return hasUpdated
|
||||||
|
@ -542,10 +542,9 @@ class Audiobook {
|
|||||||
var alreadyHasDescTxt = otherFilenamesAlreadyInBook.includes('desc.txt')
|
var alreadyHasDescTxt = otherFilenamesAlreadyInBook.includes('desc.txt')
|
||||||
var alreadyHasReaderTxt = otherFilenamesAlreadyInBook.includes('reader.txt')
|
var alreadyHasReaderTxt = otherFilenamesAlreadyInBook.includes('reader.txt')
|
||||||
|
|
||||||
|
// Filter out other files no longer in directory
|
||||||
var newOtherFilePaths = newOtherFiles.map(f => f.path)
|
var newOtherFilePaths = newOtherFiles.map(f => f.path)
|
||||||
this.otherFiles = this.otherFiles.filter(f => newOtherFilePaths.includes(f.path))
|
this.otherFiles = this.otherFiles.filter(f => newOtherFilePaths.includes(f.path))
|
||||||
|
|
||||||
// Some files are not there anymore and filtered out
|
|
||||||
if (currOtherFileNum !== this.otherFiles.length) {
|
if (currOtherFileNum !== this.otherFiles.length) {
|
||||||
Logger.debug(`[Audiobook] ${currOtherFileNum - this.otherFiles.length} other files were removed for "${this.title}"`)
|
Logger.debug(`[Audiobook] ${currOtherFileNum - this.otherFiles.length} other files were removed for "${this.title}"`)
|
||||||
hasUpdates = true
|
hasUpdates = true
|
||||||
@ -937,6 +936,8 @@ class Audiobook {
|
|||||||
|
|
||||||
var newAudioFileData = []
|
var newAudioFileData = []
|
||||||
var newOtherFileData = []
|
var newOtherFileData = []
|
||||||
|
var existingAudioFileData = []
|
||||||
|
var existingOtherFileData = []
|
||||||
|
|
||||||
dataFound.audioFiles.forEach((af) => {
|
dataFound.audioFiles.forEach((af) => {
|
||||||
var audioFileFoundCheck = this.checkFileFound(af, true)
|
var audioFileFoundCheck = this.checkFileFound(af, true)
|
||||||
@ -944,6 +945,9 @@ class Audiobook {
|
|||||||
newAudioFileData.push(af)
|
newAudioFileData.push(af)
|
||||||
} else if (audioFileFoundCheck) {
|
} else if (audioFileFoundCheck) {
|
||||||
hasUpdated = true
|
hasUpdated = true
|
||||||
|
existingAudioFileData.push(af)
|
||||||
|
} else {
|
||||||
|
existingAudioFileData.push(af)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -953,6 +957,9 @@ class Audiobook {
|
|||||||
newOtherFileData.push(otherFileData)
|
newOtherFileData.push(otherFileData)
|
||||||
} else if (fileFoundCheck) {
|
} else if (fileFoundCheck) {
|
||||||
hasUpdated = true
|
hasUpdated = true
|
||||||
|
existingOtherFileData.push(otherFileData)
|
||||||
|
} else {
|
||||||
|
existingOtherFileData.push(otherFileData)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1010,7 +1017,9 @@ class Audiobook {
|
|||||||
newAudioFileData,
|
newAudioFileData,
|
||||||
newOtherFileData,
|
newOtherFileData,
|
||||||
audioFilesRemoved,
|
audioFilesRemoved,
|
||||||
otherFilesRemoved
|
otherFilesRemoved,
|
||||||
|
existingAudioFileData, // Existing file data may get re-scanned if forceRescan is set
|
||||||
|
existingOtherFileData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,12 +139,10 @@ class AudioFileScanner {
|
|||||||
}
|
}
|
||||||
if (existingAF) {
|
if (existingAF) {
|
||||||
if (audiobook.updateAudioFile(newAF)) {
|
if (audiobook.updateAudioFile(newAF)) {
|
||||||
// console.log('update dauido file')
|
|
||||||
hasUpdated = true
|
hasUpdated = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
audiobook.addAudioFile(newAF)
|
audiobook.addAudioFile(newAF)
|
||||||
// console.log('added auido file')
|
|
||||||
hasUpdated = true
|
hasUpdated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class AudioProbeData {
|
|||||||
|
|
||||||
setData(data) {
|
setData(data) {
|
||||||
var audioStream = this.getDefaultAudioStream(data.audio_streams)
|
var audioStream = this.getDefaultAudioStream(data.audio_streams)
|
||||||
this.embeddedCoverArt = data.video_stream ? this.getEmbeddedCoverArt(data.video_stream) : false
|
this.embeddedCoverArt = data.video_stream ? this.getEmbeddedCoverArt(data.video_stream) : null
|
||||||
this.format = data.format
|
this.format = data.format
|
||||||
this.duration = data.duration
|
this.duration = data.duration
|
||||||
this.size = data.size
|
this.size = data.size
|
||||||
|
@ -221,7 +221,7 @@ class Scanner {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var checkRes = audiobook.checkScanData(dataFound, version)
|
var checkRes = audiobook.checkScanData(dataFound, version)
|
||||||
if (checkRes.newAudioFileData.length || checkRes.newOtherFileData.length) { // Audiobook has new files
|
if (checkRes.newAudioFileData.length || checkRes.newOtherFileData.length || libraryScan.scanOptions.forceRescan) { // Audiobook has new files
|
||||||
checkRes.audiobook = audiobook
|
checkRes.audiobook = audiobook
|
||||||
checkRes.bookScanData = dataFound
|
checkRes.bookScanData = dataFound
|
||||||
audiobookDataToRescan.push(checkRes)
|
audiobookDataToRescan.push(checkRes)
|
||||||
@ -305,9 +305,11 @@ class Scanner {
|
|||||||
return this.rescanAudiobook(abd, libraryScan)
|
return this.rescanAudiobook(abd, libraryScan)
|
||||||
}))
|
}))
|
||||||
audiobooksUpdated = audiobooksUpdated.filter(ab => ab) // Filter out nulls
|
audiobooksUpdated = audiobooksUpdated.filter(ab => ab) // Filter out nulls
|
||||||
libraryScan.resultsUpdated += audiobooksUpdated.length
|
if (audiobooksUpdated.length) {
|
||||||
await this.db.updateEntities('audiobook', audiobooksUpdated)
|
libraryScan.resultsUpdated += audiobooksUpdated.length
|
||||||
this.emitter('audiobooks_updated', audiobooksUpdated.map(ab => ab.toJSONExpanded()))
|
await this.db.updateEntities('audiobook', audiobooksUpdated)
|
||||||
|
this.emitter('audiobooks_updated', audiobooksUpdated.map(ab => ab.toJSONExpanded()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async scanNewAudiobookDataChunk(newAudiobookDataToScan, libraryScan) {
|
async scanNewAudiobookDataChunk(newAudiobookDataToScan, libraryScan) {
|
||||||
@ -321,24 +323,38 @@ class Scanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async rescanAudiobook(audiobookCheckData, libraryScan) {
|
async rescanAudiobook(audiobookCheckData, libraryScan) {
|
||||||
const { newAudioFileData, newOtherFileData, audiobook, bookScanData } = audiobookCheckData
|
const { newAudioFileData, newOtherFileData, audiobook, bookScanData, updated, existingAudioFileData, existingOtherFileData } = audiobookCheckData
|
||||||
libraryScan.addLog(LogLevel.DEBUG, `Library "${libraryScan.libraryName}" Re-scanning "${audiobook.path}"`)
|
libraryScan.addLog(LogLevel.DEBUG, `Library "${libraryScan.libraryName}" Re-scanning "${audiobook.path}"`)
|
||||||
|
var hasUpdated = updated
|
||||||
|
|
||||||
// Sync other files first to use local images as cover before extracting audio file cover
|
// Sync other files first to use local images as cover before extracting audio file cover
|
||||||
if (newOtherFileData.length) {
|
if (newOtherFileData.length || libraryScan.scanOptions.forceRescan) {
|
||||||
// TODO: Cleanup other file sync
|
// TODO: Cleanup other file sync
|
||||||
var allOtherFiles = newOtherFileData.concat(audiobook._otherFiles)
|
var allOtherFiles = newOtherFileData.concat(existingOtherFileData)
|
||||||
await audiobook.syncOtherFiles(allOtherFiles, this.MetadataPath, libraryScan.preferOpfMetadata)
|
if (await audiobook.syncOtherFiles(allOtherFiles, this.MetadataPath, libraryScan.preferOpfMetadata)) {
|
||||||
|
hasUpdated = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// forceRescan all existing audio files - will probe and update ID3 tag metadata
|
||||||
|
if (libraryScan.scanOptions.forceRescan && existingAudioFileData.length) {
|
||||||
|
if (await AudioFileScanner.scanAudioFiles(existingAudioFileData, bookScanData, audiobook, libraryScan.preferAudioMetadata, libraryScan)) {
|
||||||
|
hasUpdated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Scan new audio files
|
||||||
if (newAudioFileData.length) {
|
if (newAudioFileData.length) {
|
||||||
await AudioFileScanner.scanAudioFiles(newAudioFileData, bookScanData, audiobook, libraryScan.preferAudioMetadata, libraryScan)
|
if (await AudioFileScanner.scanAudioFiles(newAudioFileData, bookScanData, audiobook, libraryScan.preferAudioMetadata, libraryScan)) {
|
||||||
|
hasUpdated = true
|
||||||
// Extract embedded cover art if cover is not already in directory
|
}
|
||||||
|
}
|
||||||
|
// If an audio file has embedded cover art and no cover is set yet, extract & use it
|
||||||
|
if (newAudioFileData.length || libraryScan.scanOptions.forceRescan) {
|
||||||
if (audiobook.hasEmbeddedCoverArt && !audiobook.cover) {
|
if (audiobook.hasEmbeddedCoverArt && !audiobook.cover) {
|
||||||
var outputCoverDirs = this.getCoverDirectory(audiobook)
|
var outputCoverDirs = this.getCoverDirectory(audiobook)
|
||||||
var relativeDir = await audiobook.saveEmbeddedCoverArt(outputCoverDirs.fullPath, outputCoverDirs.relPath)
|
var relativeDir = await audiobook.saveEmbeddedCoverArt(outputCoverDirs.fullPath, outputCoverDirs.relPath)
|
||||||
if (relativeDir) {
|
if (relativeDir) {
|
||||||
|
hasUpdated = true
|
||||||
libraryScan.addLog(LogLevel.DEBUG, `Saved embedded cover art "${relativeDir}"`)
|
libraryScan.addLog(LogLevel.DEBUG, `Saved embedded cover art "${relativeDir}"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -346,17 +362,20 @@ class Scanner {
|
|||||||
|
|
||||||
if (!audiobook.audioFilesToInclude.length && !audiobook.ebooks.length) { // Audiobook is invalid
|
if (!audiobook.audioFilesToInclude.length && !audiobook.ebooks.length) { // Audiobook is invalid
|
||||||
audiobook.setInvalid()
|
audiobook.setInvalid()
|
||||||
|
hasUpdated = true
|
||||||
} else if (audiobook.isInvalid) {
|
} else if (audiobook.isInvalid) {
|
||||||
audiobook.isInvalid = false
|
audiobook.isInvalid = false
|
||||||
|
hasUpdated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan for cover if enabled and has no cover
|
// Scan for cover if enabled and has no cover (and author or title has changed OR has been 7 days since last lookup)
|
||||||
if (audiobook && libraryScan.findCovers && !audiobook.cover && audiobook.book.shouldSearchForCover) {
|
if (audiobook && libraryScan.findCovers && !audiobook.cover && audiobook.book.shouldSearchForCover) {
|
||||||
var updatedCover = await this.searchForCover(audiobook, libraryScan)
|
var updatedCover = await this.searchForCover(audiobook, libraryScan)
|
||||||
audiobook.book.updateLastCoverSearch(updatedCover)
|
audiobook.book.updateLastCoverSearch(updatedCover)
|
||||||
|
hasUpdated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return audiobook
|
return hasUpdated ? audiobook : null
|
||||||
}
|
}
|
||||||
|
|
||||||
async scanNewAudiobook(audiobookData, preferAudioMetadata, preferOpfMetadata, findCovers, libraryScan = null) {
|
async scanNewAudiobook(audiobookData, preferAudioMetadata, preferOpfMetadata, findCovers, libraryScan = null) {
|
||||||
|
Loading…
Reference in New Issue
Block a user