mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-25 15:52:26 -04:00 
			
		
		
		
	Add:Search for narrators #1495
This commit is contained in:
		
							parent
							
								
									33f20d54cc
								
							
						
					
					
						commit
						a5627a1b52
					
				| @ -28,6 +28,9 @@ | ||||
|         <widgets-authors-slider v-else-if="shelf.type === 'authors'" :key="index + '.'" :items="shelf.entities" :height="192 * sizeMultiplier" class="bookshelf-row pl-8 my-6"> | ||||
|           <p class="font-semibold text-gray-100" :style="{ fontSize: sizeMultiplier + 'rem' }">{{ $strings[shelf.labelStringKey] }}</p> | ||||
|         </widgets-authors-slider> | ||||
|         <widgets-narrators-slider v-else-if="shelf.type === 'narrators'" :key="index + '.'" :items="shelf.entities" :height="100 * sizeMultiplier" class="bookshelf-row pl-8 my-6"> | ||||
|           <p class="font-semibold text-gray-100" :style="{ fontSize: sizeMultiplier + 'rem' }">{{ $strings[shelf.labelStringKey] }}</p> | ||||
|         </widgets-narrators-slider> | ||||
|       </template> | ||||
|     </div> | ||||
|     <!-- Regular bookshelf view --> | ||||
| @ -185,8 +188,8 @@ export default { | ||||
|       this.shelves = categories | ||||
|     }, | ||||
|     async setShelvesFromSearch() { | ||||
|       var shelves = [] | ||||
|       if (this.results.books && this.results.books.length) { | ||||
|       const shelves = [] | ||||
|       if (this.results.books?.length) { | ||||
|         shelves.push({ | ||||
|           id: 'books', | ||||
|           label: 'Books', | ||||
| @ -196,7 +199,7 @@ export default { | ||||
|         }) | ||||
|       } | ||||
| 
 | ||||
|       if (this.results.podcasts && this.results.podcasts.length) { | ||||
|       if (this.results.podcasts?.length) { | ||||
|         shelves.push({ | ||||
|           id: 'podcasts', | ||||
|           label: 'Podcasts', | ||||
| @ -206,7 +209,7 @@ export default { | ||||
|         }) | ||||
|       } | ||||
| 
 | ||||
|       if (this.results.series && this.results.series.length) { | ||||
|       if (this.results.series?.length) { | ||||
|         shelves.push({ | ||||
|           id: 'series', | ||||
|           label: 'Series', | ||||
| @ -221,7 +224,7 @@ export default { | ||||
|           }) | ||||
|         }) | ||||
|       } | ||||
|       if (this.results.tags && this.results.tags.length) { | ||||
|       if (this.results.tags?.length) { | ||||
|         shelves.push({ | ||||
|           id: 'tags', | ||||
|           label: 'Tags', | ||||
| @ -236,7 +239,7 @@ export default { | ||||
|           }) | ||||
|         }) | ||||
|       } | ||||
|       if (this.results.authors && this.results.authors.length) { | ||||
|       if (this.results.authors?.length) { | ||||
|         shelves.push({ | ||||
|           id: 'authors', | ||||
|           label: 'Authors', | ||||
| @ -250,6 +253,20 @@ export default { | ||||
|           }) | ||||
|         }) | ||||
|       } | ||||
|       if (this.results.narrators?.length) { | ||||
|         shelves.push({ | ||||
|           id: 'narrators', | ||||
|           label: 'Narrators', | ||||
|           labelStringKey: 'LabelNarrators', | ||||
|           type: 'narrators', | ||||
|           entities: this.results.narrators.map((n) => { | ||||
|             return { | ||||
|               ...n, | ||||
|               type: 'narrator' | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       } | ||||
|       this.shelves = shelves | ||||
|     }, | ||||
|     scan() { | ||||
|  | ||||
| @ -41,6 +41,11 @@ | ||||
|             <cards-author-card :key="entity.id" :width="bookCoverWidth / 1.25" :height="bookCoverWidth" :author="entity" :size-multiplier="sizeMultiplier" @hook:updated="updatedBookCard" class="pb-6 mx-2" @edit="editAuthor" /> | ||||
|           </template> | ||||
|         </div> | ||||
|         <div v-if="shelf.type === 'narrators'" class="flex items-center"> | ||||
|           <template v-for="entity in shelf.entities"> | ||||
|             <cards-narrator-card :key="entity.name" :width="150" :height="100" :narrator="entity" :size-multiplier="sizeMultiplier" @hook:updated="updatedBookCard" class="pb-6 mx-2" /> | ||||
|           </template> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
| @ -88,6 +93,7 @@ export default { | ||||
|       return this.bookCoverWidth * this.bookCoverAspectRatio | ||||
|     }, | ||||
|     shelfHeight() { | ||||
|       if (this.shelf.type === 'narrators') return 148 | ||||
|       return this.bookCoverHeight + 48 | ||||
|     }, | ||||
|     paddingLeft() { | ||||
|  | ||||
| @ -10,7 +10,7 @@ | ||||
|       <p v-if="matchKey !== 'authors'" class="text-xs text-gray-200 truncate">by {{ authorName }}</p> | ||||
|       <p v-else class="truncate text-xs text-gray-200" v-html="matchHtml" /> | ||||
| 
 | ||||
|       <div v-if="matchKey === 'series' || matchKey === 'tags' || matchKey === 'isbn' || matchKey === 'asin' || matchKey === 'episode'" class="m-0 p-0 truncate text-xs" v-html="matchHtml" /> | ||||
|       <div v-if="matchKey === 'series' || matchKey === 'tags' || matchKey === 'isbn' || matchKey === 'asin' || matchKey === 'episode' || matchKey === 'narrators'" class="m-0 p-0 truncate text-xs" v-html="matchHtml" /> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| @ -67,12 +67,13 @@ export default { | ||||
|       //        but with removing commas periods etc this is no longer plausible | ||||
|       const html = this.matchText | ||||
| 
 | ||||
|       if (this.matchKey === 'episode') return `<p class="truncate">Episode: ${html}</p>` | ||||
|       if (this.matchKey === 'tags') return `<p class="truncate">Tags: ${html}</p>` | ||||
|       if (this.matchKey === 'episode') return `<p class="truncate">${this.$strings.LabelEpisode}: ${html}</p>` | ||||
|       if (this.matchKey === 'tags') return `<p class="truncate">${this.$strings.LabelTags}: ${html}</p>` | ||||
|       if (this.matchKey === 'authors') return `by ${html}` | ||||
|       if (this.matchKey === 'isbn') return `<p class="truncate">ISBN: ${html}</p>` | ||||
|       if (this.matchKey === 'asin') return `<p class="truncate">ASIN: ${html}</p>` | ||||
|       if (this.matchKey === 'series') return `<p class="truncate">Series: ${html}</p>` | ||||
|       if (this.matchKey === 'series') return `<p class="truncate">${this.$strings.LabelSeries}: ${html}</p>` | ||||
|       if (this.matchKey === 'narrators') return `<p class="truncate">${this.$strings.LabelNarrator}: ${html}</p>` | ||||
|       return `${html}` | ||||
|     } | ||||
|   }, | ||||
|  | ||||
							
								
								
									
										50
									
								
								client/components/cards/NarratorCard.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								client/components/cards/NarratorCard.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| <template> | ||||
|   <nuxt-link :to="`/library/${currentLibraryId}/bookshelf?filter=narrators.${$encode(narrator.name)}`"> | ||||
|     <div :style="{ width: width + 'px', height: height + 'px' }" class="bg-primary box-shadow-book rounded-md relative overflow-hidden"> | ||||
|       <div class="absolute inset-0 w-full h-full flex items-center justify-center pointer-events-none opacity-20"> | ||||
|         <span class="material-icons-outlined text-8xl">record_voice_over</span> | ||||
|       </div> | ||||
| 
 | ||||
|       <!-- Narrator name & num books overlay --> | ||||
|       <div class="absolute bottom-0 left-0 w-full py-1 bg-black bg-opacity-60 px-2"> | ||||
|         <p class="text-center font-semibold truncate" :style="{ fontSize: sizeMultiplier * 0.75 + 'rem' }">{{ name }}</p> | ||||
|         <p class="text-center text-gray-200" :style="{ fontSize: sizeMultiplier * 0.65 + 'rem' }">{{ numBooks }} Book{{ numBooks === 1 ? '' : 's' }}</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </nuxt-link> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|   props: { | ||||
|     narrator: { | ||||
|       type: Object, | ||||
|       default: () => {} | ||||
|     }, | ||||
|     width: Number, | ||||
|     height: Number, | ||||
|     sizeMultiplier: { | ||||
|       type: Number, | ||||
|       default: 1 | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return {} | ||||
|   }, | ||||
|   computed: { | ||||
|     name() { | ||||
|       return this.narrator?.name || '' | ||||
|     }, | ||||
|     numBooks() { | ||||
|       return this.narrator?.books?.length || 0 | ||||
|     }, | ||||
|     userCanUpdate() { | ||||
|       return this.$store.getters['user/getUserCanUpdate'] | ||||
|     }, | ||||
|     currentLibraryId() { | ||||
|       return this.$store.state.libraries.currentLibraryId | ||||
|     } | ||||
|   }, | ||||
|   methods: {} | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										34
									
								
								client/components/cards/NarratorSearchCard.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								client/components/cards/NarratorSearchCard.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| <template> | ||||
|   <div class="flex h-full px-1 overflow-hidden"> | ||||
|     <div class="w-10 h-10 flex items-center justify-center"> | ||||
|       <span class="material-icons text-2xl text-gray-200">record_voice_over</span> | ||||
|     </div> | ||||
|     <div class="flex-grow px-2 narratorSearchCardContent h-full"> | ||||
|       <p class="truncate text-sm">{{ narrator }}</p> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|   props: { | ||||
|     narrator: String | ||||
|   }, | ||||
|   data() { | ||||
|     return {} | ||||
|   }, | ||||
|   computed: {}, | ||||
|   methods: {}, | ||||
|   mounted() {} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| .narratorSearchCardContent { | ||||
|   width: calc(100% - 40px); | ||||
|   height: 40px; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   justify-content: center; | ||||
| } | ||||
| </style> | ||||
| @ -63,6 +63,15 @@ | ||||
|               </nuxt-link> | ||||
|             </li> | ||||
|           </template> | ||||
| 
 | ||||
|           <p v-if="narratorResults.length" class="uppercase text-xs text-gray-400 mb-1 mt-3 px-1 font-semibold">{{ $strings.LabelNarrators }}</p> | ||||
|           <template v-for="narrator in narratorResults"> | ||||
|             <li :key="narrator.name" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption"> | ||||
|               <nuxt-link :to="`/library/${currentLibraryId}/bookshelf?filter=narrators.${$encode(narrator.name)}`"> | ||||
|                 <cards-narrator-search-card :narrator="narrator.name" /> | ||||
|               </nuxt-link> | ||||
|             </li> | ||||
|           </template> | ||||
|         </template> | ||||
|       </ul> | ||||
|     </div> | ||||
| @ -84,6 +93,7 @@ export default { | ||||
|       authorResults: [], | ||||
|       seriesResults: [], | ||||
|       tagResults: [], | ||||
|       narratorResults: [], | ||||
|       searchTimeout: null, | ||||
|       lastSearch: null | ||||
|     } | ||||
| @ -114,6 +124,7 @@ export default { | ||||
|       this.authorResults = [] | ||||
|       this.seriesResults = [] | ||||
|       this.tagResults = [] | ||||
|       this.narratorResults = [] | ||||
|       this.showMenu = false | ||||
|       this.isFetching = false | ||||
|       this.isTyping = false | ||||
| @ -142,7 +153,7 @@ export default { | ||||
|       } | ||||
|       this.isFetching = true | ||||
| 
 | ||||
|       var searchResults = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/search?q=${value}&limit=3`).catch((error) => { | ||||
|       const searchResults = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/search?q=${value}&limit=3`).catch((error) => { | ||||
|         console.error('Search error', error) | ||||
|         return [] | ||||
|       }) | ||||
| @ -155,6 +166,7 @@ export default { | ||||
|       this.authorResults = searchResults.authors || [] | ||||
|       this.seriesResults = searchResults.series || [] | ||||
|       this.tagResults = searchResults.tags || [] | ||||
|       this.narratorResults = searchResults.narrators || [] | ||||
| 
 | ||||
|       this.isFetching = false | ||||
|       if (!this.showMenu) { | ||||
|  | ||||
							
								
								
									
										100
									
								
								client/components/widgets/NarratorsSlider.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								client/components/widgets/NarratorsSlider.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | ||||
| <template> | ||||
|   <div class="w-full"> | ||||
|     <div class="flex items-center py-3"> | ||||
|       <slot /> | ||||
|       <div class="flex-grow" /> | ||||
|       <button v-if="isScrollable" class="w-8 h-8 mx-1 flex items-center justify-center rounded-full" :class="canScrollLeft ? 'hover:bg-white hover:bg-opacity-5 text-gray-300 hover:text-white' : 'text-white text-opacity-40 cursor-text'" @click="scrollLeft"> | ||||
|         <span class="material-icons text-2xl">chevron_left</span> | ||||
|       </button> | ||||
|       <button v-if="isScrollable" class="w-8 h-8 mx-1 flex items-center justify-center rounded-full" :class="canScrollRight ? 'hover:bg-white hover:bg-opacity-5 text-gray-300 hover:text-white' : 'text-white text-opacity-40 cursor-text'" @click="scrollRight"> | ||||
|         <span class="material-icons text-2xl">chevron_right</span> | ||||
|       </button> | ||||
|     </div> | ||||
|     <div ref="slider" class="w-full overflow-y-hidden overflow-x-auto no-scroll -mx-2" style="scroll-behavior: smooth" @scroll="scrolled"> | ||||
|       <div class="flex" :style="{ height: height + 'px' }"> | ||||
|         <template v-for="item in items"> | ||||
|           <cards-narrator-card :key="item.name" :ref="`slider-item-${item.name}`" :narrator="item" :height="cardHeight" :width="cardWidth" class="relative mx-2" @hook:updated="setScrollVars" /> | ||||
|         </template> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|   props: { | ||||
|     items: { | ||||
|       type: Array, | ||||
|       default: () => [] | ||||
|     }, | ||||
|     height: { | ||||
|       type: Number, | ||||
|       default: 192 | ||||
|     }, | ||||
|     bookshelfView: { | ||||
|       type: Number, | ||||
|       default: 1 | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       isScrollable: false, | ||||
|       canScrollLeft: false, | ||||
|       canScrollRight: false, | ||||
|       clientWidth: 0 | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     cardHeight() { | ||||
|       return this.height | ||||
|     }, | ||||
|     cardWidth() { | ||||
|       return this.cardHeight * 1.5 | ||||
|     }, | ||||
|     booksPerPage() { | ||||
|       return Math.floor(this.clientWidth / (this.cardWidth + 16)) | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     scrolled() { | ||||
|       this.setScrollVars() | ||||
|     }, | ||||
|     scrollRight() { | ||||
|       if (!this.canScrollRight) return | ||||
|       const slider = this.$refs.slider | ||||
|       if (!slider) return | ||||
|       const scrollAmount = this.booksPerPage * this.cardWidth | ||||
|       const maxScrollLeft = slider.scrollWidth - slider.clientWidth | ||||
| 
 | ||||
|       const newScrollLeft = Math.min(maxScrollLeft, slider.scrollLeft + scrollAmount) | ||||
|       slider.scrollLeft = newScrollLeft | ||||
|     }, | ||||
|     scrollLeft() { | ||||
|       if (!this.canScrollLeft) return | ||||
|       const slider = this.$refs.slider | ||||
|       if (!slider) return | ||||
| 
 | ||||
|       const scrollAmount = this.booksPerPage * this.cardWidth | ||||
| 
 | ||||
|       const newScrollLeft = Math.max(0, slider.scrollLeft - scrollAmount) | ||||
|       slider.scrollLeft = newScrollLeft | ||||
|     }, | ||||
|     setScrollVars() { | ||||
|       const slider = this.$refs.slider | ||||
|       if (!slider) return | ||||
|       const { scrollLeft, scrollWidth, clientWidth } = slider | ||||
|       const scrollPercent = (scrollLeft + clientWidth) / scrollWidth | ||||
| 
 | ||||
|       this.clientWidth = clientWidth | ||||
|       this.isScrollable = scrollWidth > clientWidth | ||||
|       this.canScrollRight = scrollPercent < 1 | ||||
|       this.canScrollLeft = scrollLeft > 0 | ||||
|     } | ||||
|   }, | ||||
|   updated() { | ||||
|     this.setScrollVars() | ||||
|   }, | ||||
|   mounted() {}, | ||||
|   beforeDestroy() {} | ||||
| } | ||||
| </script> | ||||
| @ -11,27 +11,27 @@ | ||||
| <script> | ||||
| export default { | ||||
|   async asyncData({ store, params, redirect, query, app }) { | ||||
|     var libraryId = params.library | ||||
|     var library = await store.dispatch('libraries/fetch', libraryId) | ||||
|     const libraryId = params.library | ||||
|     const library = await store.dispatch('libraries/fetch', libraryId) | ||||
|     if (!library) { | ||||
|       return redirect('/oops?message=Library not found') | ||||
|     } | ||||
|     var query = query.q | ||||
|     var results = await app.$axios.$get(`/api/libraries/${libraryId}/search?q=${query}`).catch((error) => { | ||||
|     let results = await app.$axios.$get(`/api/libraries/${libraryId}/search?q=${query.q}`).catch((error) => { | ||||
|       console.error('Failed to search library', error) | ||||
|       return null | ||||
|     }) | ||||
|     results = { | ||||
|       podcasts: results && results.podcast ? results.podcast : null, | ||||
|       books: results && results.book ? results.book : null, | ||||
|       authors: results && results.authors.length ? results.authors : null, | ||||
|       series: results && results.series.length ? results.series : null, | ||||
|       tags: results && results.tags.length ? results.tags : null | ||||
|       podcasts: results?.podcast || [], | ||||
|       books: results?.book || [], | ||||
|       authors: results?.authors || [], | ||||
|       series: results?.series || [], | ||||
|       tags: results?.tags || [], | ||||
|       narrators: results?.narrators || [] | ||||
|     } | ||||
|     return { | ||||
|       libraryId, | ||||
|       results, | ||||
|       query | ||||
|       query: query.q | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
| @ -55,16 +55,17 @@ export default { | ||||
|   }, | ||||
|   methods: { | ||||
|     async search() { | ||||
|       var results = await this.$axios.$get(`/api/libraries/${this.libraryId}/search?q=${this.query}`).catch((error) => { | ||||
|       const results = await this.$axios.$get(`/api/libraries/${this.libraryId}/search?q=${this.query}`).catch((error) => { | ||||
|         console.error('Failed to search library', error) | ||||
|         return null | ||||
|       }) | ||||
|       this.results = { | ||||
|         podcasts: results && results.podcast ? results.podcast : null, | ||||
|         books: results && results.book ? results.book : null, | ||||
|         authors: results && results.authors.length ? results.authors : null, | ||||
|         series: results && results.series.length ? results.series : null, | ||||
|         tags: results && results.tags.length ? results.tags : null | ||||
|         podcasts: results?.podcast || [], | ||||
|         books: results?.book || [], | ||||
|         authors: results?.authors || [], | ||||
|         series: results?.series || [], | ||||
|         tags: results?.tags || [], | ||||
|         narrators: results?.narrators || [] | ||||
|       } | ||||
|       this.$nextTick(() => { | ||||
|         if (this.$refs.bookshelf) { | ||||
|  | ||||
| @ -596,6 +596,7 @@ class LibraryController { | ||||
| 
 | ||||
|     const itemMatches = [] | ||||
|     const authorMatches = {} | ||||
|     const narratorMatches = {} | ||||
|     const seriesMatches = {} | ||||
|     const tagMatches = {} | ||||
| 
 | ||||
| @ -608,7 +609,7 @@ class LibraryController { | ||||
|           matchText: queryResult.matchText | ||||
|         }) | ||||
|       } | ||||
|       if (queryResult.series && queryResult.series.length) { | ||||
|       if (queryResult.series?.length) { | ||||
|         queryResult.series.forEach((se) => { | ||||
|           if (!seriesMatches[se.id]) { | ||||
|             const _series = this.db.series.find(_se => _se.id === se.id) | ||||
| @ -618,7 +619,7 @@ class LibraryController { | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|       if (queryResult.authors && queryResult.authors.length) { | ||||
|       if (queryResult.authors?.length) { | ||||
|         queryResult.authors.forEach((au) => { | ||||
|           if (!authorMatches[au.id]) { | ||||
|             const _author = this.db.authors.find(_au => _au.id === au.id) | ||||
| @ -631,7 +632,7 @@ class LibraryController { | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|       if (queryResult.tags && queryResult.tags.length) { | ||||
|       if (queryResult.tags?.length) { | ||||
|         queryResult.tags.forEach((tag) => { | ||||
|           if (!tagMatches[tag]) { | ||||
|             tagMatches[tag] = { name: tag, books: [li.toJSON()] } | ||||
| @ -640,13 +641,23 @@ class LibraryController { | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|       if (queryResult.narrators?.length) { | ||||
|         queryResult.narrators.forEach((narrator) => { | ||||
|           if (!narratorMatches[narrator]) { | ||||
|             narratorMatches[narrator] = { name: narrator, books: [li.toJSON()] } | ||||
|           } else { | ||||
|             narratorMatches[narrator].books.push(li.toJSON()) | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     }) | ||||
|     const itemKey = req.library.mediaType | ||||
|     const results = { | ||||
|       [itemKey]: itemMatches.slice(0, maxResults), | ||||
|       tags: Object.values(tagMatches).slice(0, maxResults), | ||||
|       authors: Object.values(authorMatches).slice(0, maxResults), | ||||
|       series: Object.values(seriesMatches).slice(0, maxResults) | ||||
|       series: Object.values(seriesMatches).slice(0, maxResults), | ||||
|       narrators: Object.values(narratorMatches).slice(0, maxResults) | ||||
|     } | ||||
|     res.json(results) | ||||
|   } | ||||
|  | ||||
| @ -322,6 +322,7 @@ class Book { | ||||
|       tags: this.tags.filter(t => cleanStringForSearch(t).includes(query)), | ||||
|       series: this.metadata.searchSeries(query), | ||||
|       authors: this.metadata.searchAuthors(query), | ||||
|       narrators: this.metadata.searchNarrators(query), | ||||
|       matchKey: null, | ||||
|       matchText: null | ||||
|     } | ||||
| @ -336,10 +337,12 @@ class Book { | ||||
|       } else if (payload.series.length) { | ||||
|         payload.matchKey = 'series' | ||||
|         payload.matchText = this.metadata.seriesName | ||||
|       } | ||||
|       else if (payload.tags.length) { | ||||
|       } else if (payload.tags.length) { | ||||
|         payload.matchKey = 'tags' | ||||
|         payload.matchText = this.tags.join(', ') | ||||
|       } else if (payload.narrators.length) { | ||||
|         payload.matchKey = 'narrators' | ||||
|         payload.matchText = this.metadata.narratorName | ||||
|       } | ||||
|     } | ||||
|     return payload | ||||
|  | ||||
| @ -381,6 +381,9 @@ class BookMetadata { | ||||
|   searchAuthors(query) { | ||||
|     return this.authors.filter(au => cleanStringForSearch(au.name).includes(query)) | ||||
|   } | ||||
|   searchNarrators(query) { | ||||
|     return this.narrators.filter(n => cleanStringForSearch(n).includes(query)) | ||||
|   } | ||||
|   searchQuery(query) { // Returns key if match is found
 | ||||
|     const keysToCheck = ['title', 'asin', 'isbn'] | ||||
|     for (const key of keysToCheck) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user