mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-01 03:29:03 +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