mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Add collapse series, add filter by series include sequence and sort, show number of episodes on podcast card
This commit is contained in:
		
							parent
							
								
									2a386ca2a9
								
							
						
					
					
						commit
						174dac8fd4
					
				| @ -378,8 +378,6 @@ export default { | ||||
|       let searchParams = new URLSearchParams() | ||||
|       if (this.page === 'series-books') { | ||||
|         searchParams.set('filter', `series.${this.$encode(this.seriesId)}`) | ||||
|         searchParams.set('sort', 'book.volumeNumber') | ||||
|         searchParams.set('desc', 0) | ||||
|       } else { | ||||
|         if (this.filterBy && this.filterBy !== 'all') { | ||||
|           searchParams.set('filter', this.filterBy) | ||||
|  | ||||
| @ -35,8 +35,8 @@ | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- No progress shown for collapsed series in library --> | ||||
|     <div v-if="!booksInSeries" class="absolute bottom-0 left-0 h-1 shadow-sm max-w-full z-10 rounded-b" :class="itemIsFinished ? 'bg-success' : 'bg-yellow-400'" :style="{ width: width * userProgressPercent + 'px' }"></div> | ||||
|     <!-- No progress shown for collapsed series in library and podcasts --> | ||||
|     <div v-if="!booksInSeries && !isPodcast" class="absolute bottom-0 left-0 h-1 shadow-sm max-w-full z-10 rounded-b" :class="itemIsFinished ? 'bg-success' : 'bg-yellow-400'" :style="{ width: width * userProgressPercent + 'px' }"></div> | ||||
| 
 | ||||
|     <!-- Overlay is not shown if collapsing series in library --> | ||||
|     <div v-show="!booksInSeries && libraryItem && (isHovering || isSelectionMode || isMoreMenuOpen)" class="w-full h-full absolute top-0 left-0 z-10 bg-black rounded hidden md:block" :class="overlayWrapperClasslist"> | ||||
| @ -78,8 +78,13 @@ | ||||
|     </ui-tooltip> | ||||
| 
 | ||||
|     <!-- Volume number --> | ||||
|     <div v-if="volumeNumber && showVolumeNumber && !isHovering && !isSelectionMode" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-10" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }"> | ||||
|       <p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }">#{{ volumeNumber }}</p> | ||||
|     <div v-if="seriesSequence && showSequence && !isHovering && !isSelectionMode" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-10" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }"> | ||||
|       <p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }">#{{ seriesSequence }}</p> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- Podcast Num Episodes --> | ||||
|     <div v-if="numEpisodes && !isHovering && !isSelectionMode" class="absolute rounded-full bg-black bg-opacity-90 box-shadow-md z-10 flex items-center justify-center" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', width: 1.25 * sizeMultiplier + 'rem', height: 1.25 * sizeMultiplier + 'rem' }"> | ||||
|       <p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }">{{ numEpisodes }}</p> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| @ -100,7 +105,7 @@ export default { | ||||
|       default: 192 | ||||
|     }, | ||||
|     bookCoverAspectRatio: Number, | ||||
|     showVolumeNumber: Boolean, | ||||
|     showSequence: Boolean, | ||||
|     bookshelfView: Number, | ||||
|     bookMount: { | ||||
|       // Book can be passed as prop or set with setEntity() | ||||
| @ -162,8 +167,12 @@ export default { | ||||
|       return this._libraryItem.id | ||||
|     }, | ||||
|     series() { | ||||
|       // Only included when filtering by series or collapse series | ||||
|       return this.mediaMetadata.series | ||||
|     }, | ||||
|     seriesSequence() { | ||||
|       return this.series ? this.series.sequence : null | ||||
|     }, | ||||
|     libraryId() { | ||||
|       return this._libraryItem.libraryId | ||||
|     }, | ||||
| @ -174,12 +183,20 @@ export default { | ||||
|       if (this.media.tracks) return this.media.tracks.length | ||||
|       return this.media.numTracks || 0 // toJSONMinified | ||||
|     }, | ||||
|     numEpisodes() { | ||||
|       if (!this.isPodcast) return 0 | ||||
|       return this.media.numEpisodes || 0 | ||||
|     }, | ||||
|     processingBatch() { | ||||
|       return this.store.state.processingBatch | ||||
|     }, | ||||
|     collapsedSeries() { | ||||
|       // Only added to item object when collapseSeries is enabled | ||||
|       return this._libraryItem.collapsedSeries | ||||
|     }, | ||||
|     booksInSeries() { | ||||
|       // Only added to item object when collapseSeries is enabled | ||||
|       return this._libraryItem.booksInSeries | ||||
|       return this.collapsedSeries ? this.collapsedSeries.numBooks : 0 | ||||
|     }, | ||||
|     hasCover() { | ||||
|       return !!this.media.coverPath | ||||
| @ -204,9 +221,6 @@ export default { | ||||
|     authorLF() { | ||||
|       return this.mediaMetadata.authorNameLF | ||||
|     }, | ||||
|     volumeNumber() { | ||||
|       return this.mediaMetadata.volumeNumber || null | ||||
|     }, | ||||
|     displayTitle() { | ||||
|       if (this.orderBy === 'media.metadata.title' && this.sortingIgnorePrefix) { | ||||
|         return this.mediaMetadata.titleIgnorePrefix | ||||
| @ -392,7 +406,7 @@ export default { | ||||
|       } else { | ||||
|         var router = this.$router || this.$nuxt.$router | ||||
|         if (router) { | ||||
|           if (this.booksInSeries) router.push(`/library/${this.libraryId}/series/${this.$encode(this.series)}`) | ||||
|           if (this.collapsedSeries) router.push(`/library/${this.libraryId}/series/${this.collapsedSeries.id}`) | ||||
|           else router.push(`/item/${this.libraryItemId}`) | ||||
|         } | ||||
|       } | ||||
|  | ||||
| @ -54,7 +54,7 @@ export default { | ||||
|         bookCoverAspectRatio: this.bookCoverAspectRatio, | ||||
|         bookshelfView: this.bookshelfView | ||||
|       } | ||||
|       if (this.entityName === 'series-books') props.showVolumeNumber = true | ||||
|       if (this.entityName === 'series-books') props.showSequence = true | ||||
|       if (this.entityName === 'books') { | ||||
|         props.filterBy = this.filterBy | ||||
|         props.orderBy = this.orderBy | ||||
|  | ||||
| @ -152,7 +152,11 @@ class LibraryController { | ||||
|       collapseseries: req.query.collapseseries === '1' | ||||
|     } | ||||
| 
 | ||||
|     var filterSeries = null | ||||
|     if (payload.filterBy) { | ||||
|       // If filtering by series, will include seriesName and seriesSequence on media metadata
 | ||||
|       filterSeries = (payload.mediaType == 'book' && payload.filterBy.startsWith('series.')) ? libraryHelpers.decode(payload.filterBy.replace('series.', '')) : null | ||||
| 
 | ||||
|       libraryItems = libraryHelpers.getFilteredLibraryItems(libraryItems, payload.filterBy, req.user) | ||||
|       payload.total = libraryItems.length | ||||
|     } | ||||
| @ -180,7 +184,21 @@ class LibraryController { | ||||
|     } | ||||
| 
 | ||||
|     // TODO: Potentially implement collapse series again
 | ||||
|     libraryItems = libraryItems.map(ab => payload.minified ? ab.toJSONMinified() : ab.toJSON()) | ||||
|     if (payload.collapseseries) { | ||||
|       libraryItems = libraryHelpers.collapseBookSeries(libraryItems) | ||||
|       payload.total = libraryItems.length | ||||
|     } else if (filterSeries) { | ||||
|       // Book media when filtering series will include series object on media metadata
 | ||||
|       libraryItems = libraryItems.map(li => { | ||||
|         var series = li.media.metadata.getSeries(filterSeries) | ||||
|         var liJson = payload.minified ? li.toJSONMinified() : li.toJSON() | ||||
|         liJson.media.metadata.series = series | ||||
|         return liJson | ||||
|       }) | ||||
|       libraryItems = naturalSort(libraryItems).asc(li => li.media.metadata.series.sequence) | ||||
|     } else { | ||||
|       libraryItems = libraryItems.map(li => payload.minified ? li.toJSONMinified() : li.toJSON()) | ||||
|     } | ||||
| 
 | ||||
|     if (payload.limit) { | ||||
|       var startIndex = payload.page * payload.limit | ||||
|  | ||||
| @ -57,7 +57,7 @@ class Podcast { | ||||
|       metadata: this.metadata.toJSON(), | ||||
|       coverPath: this.coverPath, | ||||
|       tags: [...this.tags], | ||||
|       episodes: this.episodes.map(e => e.toJSON()), | ||||
|       numEpisodes: this.episodes.length, | ||||
|       autoDownloadEpisodes: this.autoDownloadEpisodes, | ||||
|       lastEpisodeCheck: this.lastEpisodeCheck, | ||||
|       size: this.size | ||||
|  | ||||
| @ -151,6 +151,12 @@ class BookMetadata { | ||||
|   hasNarrator(narratorName) { | ||||
|     return this.narrators.includes(narratorName) | ||||
|   } | ||||
|   getSeries(seriesId) { | ||||
|     return this.series.find(se => se.id == seriesId) | ||||
|   } | ||||
|   getFirstSeries() { | ||||
|     return this.series.length ? this.series[0] : null | ||||
|   } | ||||
|   getSeriesSequence(seriesId) { | ||||
|     var series = this.series.find(se => se.id == seriesId) | ||||
|     if (!series) return null | ||||
|  | ||||
| @ -262,4 +262,33 @@ module.exports = { | ||||
|     }) | ||||
|     return totalSize | ||||
|   }, | ||||
| 
 | ||||
|   collapseBookSeries(libraryItems) { | ||||
|     var seriesObjects = this.getSeriesFromBooks(libraryItems, true) | ||||
|     var seriesToUse = {} | ||||
|     var libraryItemIdsToHide = [] | ||||
|     seriesObjects.forEach((series) => { | ||||
|       series.firstBook = series.books.find(b => !seriesToUse[b.id]) // Find first book not already used
 | ||||
|       if (series.firstBook) { | ||||
|         seriesToUse[series.firstBook.id] = series | ||||
|         libraryItemIdsToHide = libraryItemIdsToHide.concat(series.books.filter(b => !seriesToUse[b.id]).map(b => b.id)) | ||||
|       } | ||||
|     }) | ||||
| 
 | ||||
|     return libraryItems.map((li) => { | ||||
|       if (li.mediaType != 'book') return | ||||
|       var libraryItemJson = li.toJSONMinified() | ||||
|       if (libraryItemIdsToHide.includes(li.id)) { | ||||
|         return null | ||||
|       } | ||||
|       if (seriesToUse[li.id]) { | ||||
|         libraryItemJson.collapsedSeries = { | ||||
|           id: seriesToUse[li.id].id, | ||||
|           name: seriesToUse[li.id].name, | ||||
|           numBooks: seriesToUse[li.id].books.length | ||||
|         } | ||||
|       } | ||||
|       return libraryItemJson | ||||
|     }).filter(li => li) | ||||
|   } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user