mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Updated matching with latest changes, Added override toggle for quickmatch, added asin and isbn to quickmatch query, updated audible provider to use audnexus
This commit is contained in:
		
							parent
							
								
									f2e16017f6
								
							
						
					
					
						commit
						d15264832d
					
				| @ -87,7 +87,7 @@ | ||||
|         <div v-if="selectedMatch.series" class="flex items-center py-2"> | ||||
|           <ui-checkbox v-model="selectedMatchUsage.series" /> | ||||
|           <div class="flex-grow ml-4"> | ||||
|             <ui-text-input-with-label v-model="selectedMatch.series" :disabled="!selectedMatchUsage.series" label="Series" /> | ||||
|             <ui-multi-select-query-input v-if="selectedMatch.series" ref="seriesSelect" v-model="seriesItems" text-key="displayName" label="Series" readonly /> | ||||
|             <p v-if="mediaMetadata.seriesName" class="text-xs ml-1 text-white text-opacity-60">Currently: {{ mediaMetadata.seriesName || '' }}</p> | ||||
|           </div> | ||||
|         </div> | ||||
| @ -95,6 +95,27 @@ | ||||
|           <ui-checkbox v-model="selectedMatchUsage.volumeNumber" /> | ||||
|           <ui-text-input-with-label v-model="selectedMatch.volumeNumber" :disabled="!selectedMatchUsage.volumeNumber" label="Volume Number" class="flex-grow ml-4" /> | ||||
|         </div> | ||||
|         <div v-if="selectedMatch.genres" class="flex items-center py-2"> | ||||
|           <ui-checkbox v-model="selectedMatchUsage.genres" /> | ||||
|           <div class="flex-grow ml-4"> | ||||
|             <ui-text-input-with-label v-model="selectedMatch.genres" :disabled="!selectedMatchUsage.genres" label="Genres" /> | ||||
|             <p v-if="mediaMetadata.genresList" class="text-xs ml-1 text-white text-opacity-60">Currently: {{ mediaMetadata.genresList || '' }}</p> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div v-if="selectedMatch.tags" class="flex items-center py-2"> | ||||
|           <ui-checkbox v-model="selectedMatchUsage.tags" /> | ||||
|           <div class="flex-grow ml-4"> | ||||
|             <ui-text-input-with-label v-model="selectedMatch.tags" :disabled="!selectedMatchUsage.tags" label="Tags" /> | ||||
|             <p v-if="mediaMetadata.tagsList" class="text-xs ml-1 text-white text-opacity-60">Currently: {{ mediaMetadata.tagsList || '' }}</p> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div v-if="selectedMatch.language" class="flex items-center py-2"> | ||||
|           <ui-checkbox v-model="selectedMatchUsage.language" /> | ||||
|           <div class="flex-grow ml-4"> | ||||
|             <ui-text-input-with-label v-model="selectedMatch.language" :disabled="!selectedMatchUsage.language" label="Language" /> | ||||
|             <p v-if="mediaMetadata.language" class="text-xs ml-1 text-white text-opacity-60">Currently: {{ mediaMetadata.language || '' }}</p> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div v-if="selectedMatch.isbn" class="flex items-center py-2"> | ||||
|           <ui-checkbox v-model="selectedMatchUsage.isbn" /> | ||||
|           <div class="flex-grow ml-4"> | ||||
| @ -177,6 +198,10 @@ export default { | ||||
|         publishedYear: true, | ||||
|         series: true, | ||||
|         volumeNumber: true, | ||||
|         genres: true,  | ||||
|         tags: true,  | ||||
|         language: true,  | ||||
|         explicit: true, | ||||
|         asin: true, | ||||
|         isbn: true, | ||||
|         // Podcast specific | ||||
| @ -204,6 +229,19 @@ export default { | ||||
|         this.$emit('update:processing', val) | ||||
|       } | ||||
|     }, | ||||
|     seriesItems: { | ||||
|       get() { | ||||
|         return this.selectedMatch.series.map((se) => { | ||||
|           return { | ||||
|             displayName: se.volumeNumber ? `${se.series} #${se.volumeNumber}` : se.series, | ||||
|             ...se | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|       set(val) { | ||||
|         this.selectedMatch.series = val | ||||
|       } | ||||
|     }, | ||||
|     bookCoverAspectRatio() { | ||||
|       return this.$store.getters['getBookCoverAspectRatio'] | ||||
|     }, | ||||
| @ -294,6 +332,10 @@ export default { | ||||
|         publishedYear: true, | ||||
|         series: true, | ||||
|         volumeNumber: true, | ||||
|         genres: true,  | ||||
|         tags: true,  | ||||
|         language: true,  | ||||
|         explicit: true, | ||||
|         asin: true, | ||||
|         isbn: true, | ||||
|         // Podcast specific | ||||
| @ -324,32 +366,42 @@ export default { | ||||
|     }, | ||||
|     buildMatchUpdatePayload() { | ||||
|       var updatePayload = {} | ||||
|       updatePayload.metadata = {} | ||||
| 
 | ||||
|       var volumeNumber = this.selectedMatchUsage.volumeNumber ? this.selectedMatch.volumeNumber || null : null | ||||
|       for (const key in this.selectedMatchUsage) { | ||||
|         if (this.selectedMatchUsage[key] && this.selectedMatch[key]) { | ||||
|           if (key === 'series') { | ||||
|             var seriesItem = { | ||||
|             if(!Array.isArray(this.selectedMatch[key])) this.selectedMatch[key] = [{ series: this.selectedMatch[key], volumeNumber: volumeNumber }] | ||||
|             var seriesPayload = [] | ||||
|             this.selectedMatch[key].forEach(seriesItem => seriesPayload.push({ | ||||
|               id: `new-${Math.floor(Math.random() * 10000)}`, | ||||
|               name: this.selectedMatch[key], | ||||
|               sequence: volumeNumber | ||||
|             } | ||||
|             updatePayload.series = [seriesItem] | ||||
|               name: seriesItem.series, | ||||
|               sequence: seriesItem.volumeNumber | ||||
|             })) | ||||
|             updatePayload.metadata.series = seriesPayload | ||||
|           } else if (key === 'author' && !this.isPodcast) { | ||||
|             var authorItem = { | ||||
|             if(!Array.isArray(this.selectedMatch[key])) this.selectedMatch[key] = [this.selectedMatch[key]] | ||||
|             var authorPayload = [] | ||||
|             this.selectedMatch[key].forEach(authorName => authorPayload.push({ | ||||
|               id: `new-${Math.floor(Math.random() * 10000)}`, | ||||
|               name: this.selectedMatch[key] | ||||
|             } | ||||
|             updatePayload.authors = [authorItem] | ||||
|               name: authorName | ||||
|             })) | ||||
|             updatePayload.metadata.authors = authorPayload | ||||
|           } else if (key === 'narrator') { | ||||
|             updatePayload.narrators = [this.selectedMatch[key]] | ||||
|             updatePayload.metadata.narrators = [this.selectedMatch[key]] | ||||
|           } else if (key === 'genres') { | ||||
|             updatePayload.metadata.genres = this.selectedMatch[key].split(',') | ||||
|           } else if (key === 'tags') { | ||||
|             updatePayload.tags = this.selectedMatch[key].split(',') | ||||
|           } else if (key === 'itunesId') { | ||||
|             updatePayload.itunesId = Number(this.selectedMatch[key]) | ||||
|           } else if (key !== 'volumeNumber') { | ||||
|             updatePayload[key] = this.selectedMatch[key] | ||||
|             updatePayload.metadata.itunesId = Number(this.selectedMatch[key]) | ||||
|           } else { | ||||
|             updatePayload.metadata[key] = this.selectedMatch[key] | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       console.log(updatePayload) | ||||
|       return updatePayload | ||||
|     }, | ||||
|     async submitMatchUpdate() { | ||||
| @ -361,7 +413,7 @@ export default { | ||||
| 
 | ||||
|       if (updatePayload.cover) { | ||||
|         var coverPayload = { | ||||
|           url: updatePayload.cover | ||||
|           url: updatePayload.metadata.cover | ||||
|         } | ||||
|         var success = await this.$axios.$post(`/api/items/${this.libraryItemId}/cover`, coverPayload).catch((error) => { | ||||
|           console.error('Failed to update', error) | ||||
| @ -373,13 +425,11 @@ export default { | ||||
|           this.$toast.error('Item Cover Failed to Update') | ||||
|         } | ||||
|         console.log('Updated cover') | ||||
|         delete updatePayload.cover | ||||
|         delete updatePayload.metadata.cover | ||||
|       } | ||||
| 
 | ||||
|       if (Object.keys(updatePayload).length) { | ||||
|         var mediaUpdatePayload = { | ||||
|           metadata: updatePayload | ||||
|         } | ||||
|         var mediaUpdatePayload = updatePayload | ||||
|         var updateResult = await this.$axios.$patch(`/api/items/${this.libraryItemId}/media`, mediaUpdatePayload).catch((error) => { | ||||
|           console.error('Failed to update', error) | ||||
|           return false | ||||
|  | ||||
| @ -113,6 +113,16 @@ | ||||
|         </ui-tooltip> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class="flex items-center py-2"> | ||||
|         <ui-toggle-switch v-model="newServerSettings.scannerPreferMatchedMetadata" :disabled="updatingServerSettings" @input="(val) => updateSettingsKey('scannerPreferMatchedMetadata', val)" /> | ||||
|         <ui-tooltip :text="tooltips.scannerPreferMatchedMetadata"> | ||||
|           <p class="pl-4 text-lg"> | ||||
|             Scanner prefer matched metadata | ||||
|             <span class="material-icons icon-text">info_outlined</span> | ||||
|           </p> | ||||
|         </ui-tooltip> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class="flex items-center py-2"> | ||||
|         <ui-toggle-switch v-model="newServerSettings.scannerDisableWatcher" :disabled="updatingServerSettings" @input="(val) => updateSettingsKey('scannerDisableWatcher', val)" /> | ||||
|         <ui-tooltip :text="tooltips.scannerDisableWatcher"> | ||||
| @ -226,6 +236,7 @@ export default { | ||||
|         experimentalFeatures: 'Features in development that could use your feedback and help testing. Click to open github discussion.', | ||||
|         scannerDisableWatcher: 'Disables the automatic adding/updating of items when file changes are detected. *Requires server restart', | ||||
|         scannerPreferOpfMetadata: 'OPF file metadata will be used for book details over folder names', | ||||
|         scannerPreferMatchedMetadata: 'Matched data will overide book details when using Quick Match', | ||||
|         scannerPreferAudioMetadata: 'Audio file ID3 meta tags will be used for book details over folder names', | ||||
|         scannerParseSubtitle: 'Extract subtitles from audiobook folder names.<br>Subtitle must be seperated by " - "<br>i.e. "Book Title - A Subtitle Here" has the subtitle "A Subtitle Here"', | ||||
|         sortingIgnorePrefix: 'i.e. for prefix "the" book title "The Book Title" would sort as "Book Title, The"', | ||||
|  | ||||
| @ -166,14 +166,14 @@ class BookFinder { | ||||
|     return this.iTunesApi.searchAudiobooks(title) | ||||
|   } | ||||
| 
 | ||||
|   async getAudibleResults(title, author) { | ||||
|     var books = await this.audible.search(title, author); | ||||
|   async getAudibleResults(title, author, asin) { | ||||
|     var books = await this.audible.search(title, author, asin); | ||||
|     if (this.verbose) Logger.debug(`Audible Book Search Results: ${books.length || 0}`) | ||||
|     if (!books) return [] | ||||
|     return books | ||||
|   } | ||||
| 
 | ||||
|   async search(provider, title, author, options = {}) { | ||||
|   async search(provider, title, author, isbn, asin, options = {}) { | ||||
|     var books = [] | ||||
|     var maxTitleDistance = !isNaN(options.titleDistance) ? Number(options.titleDistance) : 4 | ||||
|     var maxAuthorDistance = !isNaN(options.authorDistance) ? Number(options.authorDistance) : 4 | ||||
| @ -182,7 +182,7 @@ class BookFinder { | ||||
|     if (provider === 'google') { | ||||
|       return this.getGoogleBooksResults(title, author) | ||||
|     } else if (provider === 'audible') { | ||||
|       return this.getAudibleResults(title, author) | ||||
|       return this.getAudibleResults(title, author, asin) | ||||
|     } else if (provider === 'itunes') { | ||||
|       return this.getiTunesAudiobooksResults(title, author) | ||||
|     } else if (provider === 'libgen') { | ||||
|  | ||||
| @ -48,7 +48,7 @@ class Series { | ||||
|   } | ||||
| 
 | ||||
|   checkNameEquals(name) { | ||||
|     if (!name) return false | ||||
|     if (!name || !this.name) return false | ||||
|     return this.name.toLowerCase() == name.toLowerCase().trim() | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -11,7 +11,8 @@ class ServerSettings { | ||||
|     this.scannerCoverProvider = 'google' | ||||
|     this.scannerPreferAudioMetadata = false | ||||
|     this.scannerPreferOpfMetadata = false | ||||
|     this.scannerDisableWatcher = false | ||||
|     this.scannerPreferMatchedMetadata = false | ||||
|     this.scannerDisableWatcher = false  | ||||
| 
 | ||||
|     // Metadata - choose to store inside users library item folder
 | ||||
|     this.storeCoverWithItem = false | ||||
| @ -62,6 +63,7 @@ class ServerSettings { | ||||
|     this.scannerParseSubtitle = settings.scannerParseSubtitle | ||||
|     this.scannerPreferAudioMetadata = !!settings.scannerPreferAudioMetadata | ||||
|     this.scannerPreferOpfMetadata = !!settings.scannerPreferOpfMetadata | ||||
|     this.scannerPreferMatchedMetadata = !!settings.scannerPreferMatchedMetadata | ||||
|     this.scannerDisableWatcher = !!settings.scannerDisableWatcher | ||||
| 
 | ||||
|     this.storeCoverWithItem = !!settings.storeCoverWithItem | ||||
| @ -107,6 +109,7 @@ class ServerSettings { | ||||
|       scannerParseSubtitle: this.scannerParseSubtitle, | ||||
|       scannerPreferAudioMetadata: this.scannerPreferAudioMetadata, | ||||
|       scannerPreferOpfMetadata: this.scannerPreferOpfMetadata, | ||||
|       scannerPreferMatchedMetadata: this.scannerPreferMatchedMetadata, | ||||
|       scannerDisableWatcher: this.scannerDisableWatcher, | ||||
|       storeCoverWithItem: this.storeCoverWithItem, | ||||
|       storeMetadataWithItem: this.storeMetadataWithItem, | ||||
|  | ||||
| @ -6,83 +6,81 @@ class Audible { | ||||
|     constructor() { } | ||||
| 
 | ||||
|     cleanResult(item) { | ||||
|         var { title, subtitle, asin, authors, narrators, publisher_name, publisher_summary, release_date, series, product_images, publication_name } = item; | ||||
|         var { title, subtitle, asin, authors, narrators, publisherName, summary, releaseDate, image, genres, seriesPrimary, seriesSecondary, language } = item; | ||||
| 
 | ||||
|         var primarySeries = this.getPrimarySeries(series, publication_name); | ||||
|         var series = [] | ||||
|         if(seriesPrimary) series.push(seriesPrimary) | ||||
|         if(seriesSecondary) series.push(seriesSecondary) | ||||
| 
 | ||||
|         var genresFiltered = genres ? genres.filter(g => g.type == "genre") : [] | ||||
|         var tagsFiltered = genres ? genres.filter(g => g.type == "tag") : [] | ||||
| 
 | ||||
|         return { | ||||
|             title, | ||||
|             subtitle: subtitle || null, | ||||
|             author: authors ? authors.map(({ name }) => name).join(', ') : null, | ||||
|             narrator: narrators ? narrators.map(({ name }) => name).join(', ') : null, | ||||
|             publisher: publisher_name, | ||||
|             publishedYear: release_date ? release_date.split('-')[0] : null, | ||||
|             description: publisher_summary ? stripHtml(publisher_summary).result : null, | ||||
|             cover: this.getBestImageLink(product_images), | ||||
|             publisher: publisherName, | ||||
|             publishedYear: releaseDate ? releaseDate.split('-')[0] : null, | ||||
|             description: summary ? stripHtml(summary).result : null, | ||||
|             cover: image, | ||||
|             asin, | ||||
|             series: primarySeries ? primarySeries.title : null, | ||||
|             volumeNumber: primarySeries ? primarySeries.sequence : null | ||||
|             genres: genresFiltered.length > 0 ? genresFiltered.map(({ name }) => name).join(', ') : null, | ||||
|             tags: tagsFiltered.length > 0 ? tagsFiltered.map(({ name }) => name).join(', ') : null, | ||||
|             series: series != [] ? series.map(({name, position}) => ({ series: name, volumeNumber: position })) : null, | ||||
|             language: language ? language.charAt(0).toUpperCase() + language.slice(1) : null | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     getBestImageLink(images) { | ||||
|         if (!images) return null | ||||
|         var keys = Object.keys(images) | ||||
|         if (!keys.length) return null | ||||
|         return images[keys[keys.length - 1]] | ||||
|     } | ||||
| 
 | ||||
|     getPrimarySeries(series, publication_name) { | ||||
|         return (series && series.length > 0) ? series.find((s) => s.title == publication_name) || series[0] : null | ||||
|     } | ||||
| 
 | ||||
|     isProbablyAsin(title) { | ||||
|         return /^[0-9A-Z]{10}$/.test(title) | ||||
|     } | ||||
| 
 | ||||
|     asinSearch(asin) { | ||||
|         var queryObj = { | ||||
|             response_groups: 'rating,series,contributors,product_desc,media,product_extended_attrs', | ||||
|             image_sizes: '500,1024,2000' | ||||
|         }; | ||||
|         var queryString = (new URLSearchParams(queryObj)).toString(); | ||||
|         asin = encodeURIComponent(asin); | ||||
|         var url = `https://api.audible.com/1.0/catalog/products/${asin}?${queryString}` | ||||
|         var url = `https://api.audnex.us/books/${asin}` | ||||
|         Logger.debug(`[Audible] ASIN url: ${url}`) | ||||
|         return axios.get(url).then((res) => { | ||||
|             if (!res || !res.data || !res.data.product || !res.data.product.authors) return [] | ||||
|             return [res.data.product] | ||||
|             if (!res || !res.data || !res.data.asin) return null | ||||
|             return res.data | ||||
|         }).catch(error => { | ||||
|             Logger.error('[Audible] search error', error) | ||||
|             Logger.error('[Audible] ASIN search error', error) | ||||
|             return [] | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     async search(title, author) { | ||||
|         if (this.isProbablyAsin(title)) { | ||||
|             var items = await this.asinSearch(title) | ||||
|             if (items.length > 0) return items.map(item => this.cleanResult(item)) | ||||
|     async search(title, author, asin) {         | ||||
|         var items | ||||
|         if(asin) { | ||||
|             items = [await this.asinSearch(asin)] | ||||
|         } | ||||
|          | ||||
|         if (!items && this.isProbablyAsin(title)) { | ||||
|             items = [await this.asinSearch(title)] | ||||
|         } | ||||
| 
 | ||||
|         var queryObj = { | ||||
|             response_groups: 'rating,series,contributors,product_desc,media,product_extended_attrs', | ||||
|             image_sizes: '500,1024,2000', | ||||
|             num_results: '25', | ||||
|             products_sort_by: 'Relevance', | ||||
|             title: title | ||||
|         }; | ||||
|         if (author) queryObj.author = author | ||||
|         var queryString = (new URLSearchParams(queryObj)).toString(); | ||||
|         var url = `https://api.audible.com/1.0/catalog/products?${queryString}` | ||||
|         Logger.debug(`[Audible] Search url: ${url}`) | ||||
|         var items = await axios.get(url).then((res) => { | ||||
|             if (!res || !res.data || !res.data.products) return [] | ||||
|             return res.data.products | ||||
|         }).catch(error => { | ||||
|             Logger.error('[Audible] search error', error) | ||||
|             return [] | ||||
|         }) | ||||
|         return items.map(item => this.cleanResult(item)) | ||||
|         if(!items) { | ||||
|             var queryObj = { | ||||
|                 response_groups: 'rating,series,contributors,product_desc,media,product_extended_attrs', | ||||
|                 image_sizes: '500,1024,2000', | ||||
|                 num_results: '25', | ||||
|                 products_sort_by: 'Relevance', | ||||
|                 title: title | ||||
|             }; | ||||
|             if (author) queryObj.author = author | ||||
|             var queryString = (new URLSearchParams(queryObj)).toString(); | ||||
|             var url = `https://api.audible.com/1.0/catalog/products?${queryString}` | ||||
|             Logger.debug(`[Audible] Search url: ${url}`) | ||||
|             items = await axios.get(url).then((res) => { | ||||
|                 if (!res || !res.data || !res.data.products) return null | ||||
|                 return Promise.all(res.data.products.map(result => this.asinSearch(result.asin))) | ||||
|             }).catch(error => { | ||||
|                 Logger.error('[Audible] query search error', error) | ||||
|                 return [] | ||||
|             }) | ||||
|         } | ||||
| 
 | ||||
|         return items ? items.map(item => this.cleanResult(item)) : [] | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,7 @@ class ScanOptions { | ||||
|     this.storeCoverWithItem = false | ||||
|     this.preferAudioMetadata = false | ||||
|     this.preferOpfMetadata = false | ||||
|     this.preferMatchedMetadata = false | ||||
| 
 | ||||
|     if (options) { | ||||
|       this.construct(options) | ||||
| @ -32,7 +33,8 @@ class ScanOptions { | ||||
|       findCovers: this.findCovers, | ||||
|       storeCoverWithItem: this.storeCoverWithItem, | ||||
|       preferAudioMetadata: this.preferAudioMetadata, | ||||
|       preferOpfMetadata: this.preferOpfMetadata | ||||
|       preferOpfMetadata: this.preferOpfMetadata, | ||||
|       preferOpfMetadata: this.preferMatchedMetadata | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -44,6 +46,7 @@ class ScanOptions { | ||||
|     this.storeCoverWithItem = serverSettings.storeCoverWithItem | ||||
|     this.preferAudioMetadata = serverSettings.scannerPreferAudioMetadata | ||||
|     this.preferOpfMetadata = serverSettings.scannerPreferOpfMetadata | ||||
|     this.preferOpfMetadata = serverSettings.scannerPreferMatchedMetadata | ||||
|   } | ||||
| } | ||||
| module.exports = ScanOptions | ||||
| @ -632,8 +632,10 @@ class Scanner { | ||||
|     var provider = options.provider || 'google' | ||||
|     var searchTitle = options.title || libraryItem.media.metadata.title | ||||
|     var searchAuthor = options.author || libraryItem.media.metadata.authorName | ||||
|     var searchISBN = options.isbn || libraryItem.media.metadata.isbn | ||||
|     var searchASIN = options.asin || libraryItem.media.metadata.asin | ||||
| 
 | ||||
|     var results = await this.bookFinder.search(provider, searchTitle, searchAuthor) | ||||
|     var results = await this.bookFinder.search(provider, searchTitle, searchAuthor, searchISBN, searchASIN) | ||||
|     if (!results.length) { | ||||
|       return { | ||||
|         warning: `No ${provider} match found` | ||||
| @ -641,6 +643,12 @@ class Scanner { | ||||
|     } | ||||
|     var matchData = results[0] | ||||
| 
 | ||||
|     // Set to override existing metadata if scannerPreferMatchedMetadata setting is true
 | ||||
|     if(this.db.serverSettings.scannerPreferMatchedMetadata) { | ||||
|       options.overrideCover = true | ||||
|       options.overrideDetails = true | ||||
|     } | ||||
| 
 | ||||
|     // Update cover if not set OR overrideCover flag
 | ||||
|     var hasUpdated = false | ||||
|     if (matchData.cover && (!libraryItem.media.coverPath || options.overrideCover)) { | ||||
| @ -654,47 +662,68 @@ class Scanner { | ||||
|     } | ||||
| 
 | ||||
|     // Update media metadata if not set OR overrideDetails flag
 | ||||
|     const detailKeysToUpdate = ['title', 'subtitle', 'description', 'narrator', 'publisher', 'publishedYear', 'asin', 'isbn'] | ||||
|     const detailKeysToUpdate = ['title', 'subtitle', 'description', 'narrator', 'publisher', 'publishedYear', 'genres', 'tags', 'language', 'explicit', 'asin', 'isbn'] | ||||
|     const updatePayload = {} | ||||
|     updatePayload.metadata = {} | ||||
|     for (const key in matchData) { | ||||
|       if (matchData[key] && detailKeysToUpdate.includes(key)) { | ||||
|         if (key === 'narrator') { | ||||
|           if ((!libraryItem.media.metadata.narratorName || options.overrideDetails)) { | ||||
|             updatePayload.narrators = [matchData[key]] | ||||
|             updatePayload.metadata.narrators = matchData[key].split(',') | ||||
|           } | ||||
|         } else if (key === 'genres') { | ||||
|           if ((!libraryItem.media.metadata.genres || options.overrideDetails)) { | ||||
|             updatePayload.metadata[key] = matchData[key].split(',') | ||||
|           } | ||||
|         } else if (key === 'tags') { | ||||
|           if ((!libraryItem.media.tags || options.overrideDetails)) { | ||||
|             updatePayload[key] = matchData[key].split(',') | ||||
|           } | ||||
|         } else if ((!libraryItem.media.metadata[key] || options.overrideDetails)) { | ||||
|           updatePayload[key] = matchData[key] | ||||
|           updatePayload.metadata[key] = matchData[key] | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // Add or set author if not set
 | ||||
|     if (matchData.author && !libraryItem.media.metadata.authorName) { | ||||
|       var author = this.db.authors.find(au => au.checkNameEquals(matchData.author)) | ||||
|       if (!author) { | ||||
|         author = new Author() | ||||
|         author.setData({ name: matchData.author }) | ||||
|         await this.db.insertEntity('author', author) | ||||
|         this.emitter('author_added', author) | ||||
|     if (matchData.author && !libraryItem.media.metadata.authorName || options.overrideDetails) { | ||||
|       if(!Array.isArray(matchData.author)) matchData.author = [matchData.author] | ||||
|       const authorPayload = [] | ||||
|       for (let index = 0; index < matchData.author.length; index++) { | ||||
|         const authorName = matchData.author[index] | ||||
|         var author = this.db.authors.find(au => au.checkNameEquals(authorName)) | ||||
|         if (!author) { | ||||
|           author = new Author() | ||||
|           author.setData({ name: authorName }) | ||||
|           await this.db.insertEntity('author', author) | ||||
|           this.emitter('author_added', author) | ||||
|         } | ||||
|         authorPayload.push(author.toJSONMinimal()) | ||||
|       } | ||||
|       updatePayload.authors = [author.toJSONMinimal()] | ||||
|       updatePayload.metadata.authors = authorPayload | ||||
|     } | ||||
| 
 | ||||
|     // Add or set series if not set
 | ||||
|     if (matchData.series && !libraryItem.media.metadata.seriesName) { | ||||
|       var seriesItem = this.db.series.find(au => au.checkNameEquals(matchData.series)) | ||||
|       if (!seriesItem) { | ||||
|         seriesItem = new Series() | ||||
|         seriesItem.setData({ name: matchData.series }) | ||||
|         await this.db.insertEntity('series', seriesItem) | ||||
|         this.emitter('series_added', seriesItem) | ||||
|     if (matchData.series && !libraryItem.media.metadata.seriesName || options.overrideDetails) { | ||||
|       if(!Array.isArray(matchData.series)) matchData.series = [{ series: matchData.series, volumeNumber: matchData.volumeNumber }] | ||||
|       const seriesPayload = [] | ||||
|       for (let index = 0; index < matchData.series.length; index++) { | ||||
|         const seriesMatchItem = matchData.series[index] | ||||
|         var seriesItem = this.db.series.find(au => au.checkNameEquals(seriesMatchItem.series)) | ||||
|         if (!seriesItem) { | ||||
|           seriesItem = new Series() | ||||
|           seriesItem.setData({ name: seriesMatchItem.series }) | ||||
|           await this.db.insertEntity('series', seriesItem) | ||||
|           this.emitter('series_added', seriesItem) | ||||
|         } | ||||
|         seriesPayload.push(seriesItem.toJSONMinimal(seriesMatchItem.volumeNumber)) | ||||
|       } | ||||
|       updatePayload.series = [seriesItem.toJSONMinimal(matchData.volumeNumber)] | ||||
|       updatePayload.metadata.series = seriesPayload | ||||
|     } | ||||
| 
 | ||||
|     if (Object.keys(updatePayload).length) { | ||||
|       Logger.debug('[Scanner] Updating details', updatePayload) | ||||
|       if (libraryItem.media.update({ metadata: updatePayload })) { | ||||
|       if (libraryItem.media.update(updatePayload)) { | ||||
|         hasUpdated = true | ||||
|       } | ||||
|     } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user