mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-02-01 00: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()
|
let searchParams = new URLSearchParams()
|
||||||
if (this.page === 'series-books') {
|
if (this.page === 'series-books') {
|
||||||
searchParams.set('filter', `series.${this.$encode(this.seriesId)}`)
|
searchParams.set('filter', `series.${this.$encode(this.seriesId)}`)
|
||||||
searchParams.set('sort', 'book.volumeNumber')
|
|
||||||
searchParams.set('desc', 0)
|
|
||||||
} else {
|
} else {
|
||||||
if (this.filterBy && this.filterBy !== 'all') {
|
if (this.filterBy && this.filterBy !== 'all') {
|
||||||
searchParams.set('filter', this.filterBy)
|
searchParams.set('filter', this.filterBy)
|
||||||
|
@ -35,8 +35,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- No progress shown for collapsed series in library -->
|
<!-- No progress shown for collapsed series in library and podcasts -->
|
||||||
<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>
|
<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 -->
|
<!-- 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">
|
<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>
|
</ui-tooltip>
|
||||||
|
|
||||||
<!-- Volume number -->
|
<!-- 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` }">
|
<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' }">#{{ volumeNumber }}</p>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -100,7 +105,7 @@ export default {
|
|||||||
default: 192
|
default: 192
|
||||||
},
|
},
|
||||||
bookCoverAspectRatio: Number,
|
bookCoverAspectRatio: Number,
|
||||||
showVolumeNumber: Boolean,
|
showSequence: Boolean,
|
||||||
bookshelfView: Number,
|
bookshelfView: Number,
|
||||||
bookMount: {
|
bookMount: {
|
||||||
// Book can be passed as prop or set with setEntity()
|
// Book can be passed as prop or set with setEntity()
|
||||||
@ -162,8 +167,12 @@ export default {
|
|||||||
return this._libraryItem.id
|
return this._libraryItem.id
|
||||||
},
|
},
|
||||||
series() {
|
series() {
|
||||||
|
// Only included when filtering by series or collapse series
|
||||||
return this.mediaMetadata.series
|
return this.mediaMetadata.series
|
||||||
},
|
},
|
||||||
|
seriesSequence() {
|
||||||
|
return this.series ? this.series.sequence : null
|
||||||
|
},
|
||||||
libraryId() {
|
libraryId() {
|
||||||
return this._libraryItem.libraryId
|
return this._libraryItem.libraryId
|
||||||
},
|
},
|
||||||
@ -174,12 +183,20 @@ export default {
|
|||||||
if (this.media.tracks) return this.media.tracks.length
|
if (this.media.tracks) return this.media.tracks.length
|
||||||
return this.media.numTracks || 0 // toJSONMinified
|
return this.media.numTracks || 0 // toJSONMinified
|
||||||
},
|
},
|
||||||
|
numEpisodes() {
|
||||||
|
if (!this.isPodcast) return 0
|
||||||
|
return this.media.numEpisodes || 0
|
||||||
|
},
|
||||||
processingBatch() {
|
processingBatch() {
|
||||||
return this.store.state.processingBatch
|
return this.store.state.processingBatch
|
||||||
},
|
},
|
||||||
|
collapsedSeries() {
|
||||||
|
// Only added to item object when collapseSeries is enabled
|
||||||
|
return this._libraryItem.collapsedSeries
|
||||||
|
},
|
||||||
booksInSeries() {
|
booksInSeries() {
|
||||||
// Only added to item object when collapseSeries is enabled
|
// Only added to item object when collapseSeries is enabled
|
||||||
return this._libraryItem.booksInSeries
|
return this.collapsedSeries ? this.collapsedSeries.numBooks : 0
|
||||||
},
|
},
|
||||||
hasCover() {
|
hasCover() {
|
||||||
return !!this.media.coverPath
|
return !!this.media.coverPath
|
||||||
@ -204,9 +221,6 @@ export default {
|
|||||||
authorLF() {
|
authorLF() {
|
||||||
return this.mediaMetadata.authorNameLF
|
return this.mediaMetadata.authorNameLF
|
||||||
},
|
},
|
||||||
volumeNumber() {
|
|
||||||
return this.mediaMetadata.volumeNumber || null
|
|
||||||
},
|
|
||||||
displayTitle() {
|
displayTitle() {
|
||||||
if (this.orderBy === 'media.metadata.title' && this.sortingIgnorePrefix) {
|
if (this.orderBy === 'media.metadata.title' && this.sortingIgnorePrefix) {
|
||||||
return this.mediaMetadata.titleIgnorePrefix
|
return this.mediaMetadata.titleIgnorePrefix
|
||||||
@ -392,7 +406,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
var router = this.$router || this.$nuxt.$router
|
var router = this.$router || this.$nuxt.$router
|
||||||
if (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}`)
|
else router.push(`/item/${this.libraryItemId}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ export default {
|
|||||||
bookCoverAspectRatio: this.bookCoverAspectRatio,
|
bookCoverAspectRatio: this.bookCoverAspectRatio,
|
||||||
bookshelfView: this.bookshelfView
|
bookshelfView: this.bookshelfView
|
||||||
}
|
}
|
||||||
if (this.entityName === 'series-books') props.showVolumeNumber = true
|
if (this.entityName === 'series-books') props.showSequence = true
|
||||||
if (this.entityName === 'books') {
|
if (this.entityName === 'books') {
|
||||||
props.filterBy = this.filterBy
|
props.filterBy = this.filterBy
|
||||||
props.orderBy = this.orderBy
|
props.orderBy = this.orderBy
|
||||||
|
@ -152,7 +152,11 @@ class LibraryController {
|
|||||||
collapseseries: req.query.collapseseries === '1'
|
collapseseries: req.query.collapseseries === '1'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var filterSeries = null
|
||||||
if (payload.filterBy) {
|
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)
|
libraryItems = libraryHelpers.getFilteredLibraryItems(libraryItems, payload.filterBy, req.user)
|
||||||
payload.total = libraryItems.length
|
payload.total = libraryItems.length
|
||||||
}
|
}
|
||||||
@ -180,7 +184,21 @@ class LibraryController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Potentially implement collapse series again
|
// 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) {
|
if (payload.limit) {
|
||||||
var startIndex = payload.page * payload.limit
|
var startIndex = payload.page * payload.limit
|
||||||
|
@ -57,7 +57,7 @@ class Podcast {
|
|||||||
metadata: this.metadata.toJSON(),
|
metadata: this.metadata.toJSON(),
|
||||||
coverPath: this.coverPath,
|
coverPath: this.coverPath,
|
||||||
tags: [...this.tags],
|
tags: [...this.tags],
|
||||||
episodes: this.episodes.map(e => e.toJSON()),
|
numEpisodes: this.episodes.length,
|
||||||
autoDownloadEpisodes: this.autoDownloadEpisodes,
|
autoDownloadEpisodes: this.autoDownloadEpisodes,
|
||||||
lastEpisodeCheck: this.lastEpisodeCheck,
|
lastEpisodeCheck: this.lastEpisodeCheck,
|
||||||
size: this.size
|
size: this.size
|
||||||
|
@ -151,6 +151,12 @@ class BookMetadata {
|
|||||||
hasNarrator(narratorName) {
|
hasNarrator(narratorName) {
|
||||||
return this.narrators.includes(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) {
|
getSeriesSequence(seriesId) {
|
||||||
var series = this.series.find(se => se.id == seriesId)
|
var series = this.series.find(se => se.id == seriesId)
|
||||||
if (!series) return null
|
if (!series) return null
|
||||||
|
@ -262,4 +262,33 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
return totalSize
|
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