mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-04-07 01:15:44 +02:00
Podcast home page shelves for currently listening episodes, newest episodes. Podcast episode card
This commit is contained in:
parent
84fb7ce8b3
commit
5d12cc3f23
@ -7,6 +7,11 @@
|
|||||||
<cards-lazy-book-card :key="entity.id" :ref="`shelf-book-${entity.id}`" :index="index" :width="bookCoverWidth" :height="bookCoverHeight" :book-cover-aspect-ratio="bookCoverAspectRatio" :book-mount="entity" class="relative mx-2" @hook:updated="updatedBookCard" @select="selectItem" @edit="editBook" />
|
<cards-lazy-book-card :key="entity.id" :ref="`shelf-book-${entity.id}`" :index="index" :width="bookCoverWidth" :height="bookCoverHeight" :book-cover-aspect-ratio="bookCoverAspectRatio" :book-mount="entity" class="relative mx-2" @hook:updated="updatedBookCard" @select="selectItem" @edit="editBook" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="shelf.type === 'episode'" class="flex items-center">
|
||||||
|
<template v-for="(entity, index) in shelf.entities">
|
||||||
|
<cards-lazy-book-card :key="entity.recentEpisode.id" :ref="`shelf-episode-${entity.recentEpisode.id}`" :index="index" :width="bookCoverWidth" :height="bookCoverHeight" :book-cover-aspect-ratio="bookCoverAspectRatio" :book-mount="entity" class="relative mx-2" @hook:updated="updatedBookCard" @select="selectItem" @edit="editEpisode" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
<div v-if="shelf.type === 'series'" class="flex items-center">
|
<div v-if="shelf.type === 'series'" class="flex items-center">
|
||||||
<template v-for="entity in shelf.entities">
|
<template v-for="entity in shelf.entities">
|
||||||
<cards-lazy-series-card :key="entity.name" :series-mount="entity" :height="bookCoverHeight" :width="bookCoverWidth * 2" :book-cover-aspect-ratio="bookCoverAspectRatio" class="relative mx-2" @hook:updated="updatedBookCard" />
|
<cards-lazy-series-card :key="entity.name" :series-mount="entity" :height="bookCoverHeight" :width="bookCoverWidth * 2" :book-cover-aspect-ratio="bookCoverAspectRatio" class="relative mx-2" @hook:updated="updatedBookCard" />
|
||||||
@ -70,11 +75,6 @@ export default {
|
|||||||
selectedAuthor: null
|
selectedAuthor: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
isSelectionMode(newVal) {
|
|
||||||
this.updateSelectionMode(newVal)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
bookCoverHeight() {
|
bookCoverHeight() {
|
||||||
return this.bookCoverWidth * this.bookCoverAspectRatio
|
return this.bookCoverWidth * this.bookCoverAspectRatio
|
||||||
@ -103,9 +103,14 @@ export default {
|
|||||||
this.$store.commit('setBookshelfBookIds', bookIds)
|
this.$store.commit('setBookshelfBookIds', bookIds)
|
||||||
this.$store.commit('showEditModal', audiobook)
|
this.$store.commit('showEditModal', audiobook)
|
||||||
},
|
},
|
||||||
|
editEpisode({ libraryItem, episode }) {
|
||||||
|
this.$store.commit('setSelectedLibraryItem', libraryItem)
|
||||||
|
this.$store.commit('globals/setSelectedEpisode', episode)
|
||||||
|
this.$store.commit('globals/setShowEditPodcastEpisodeModal', true)
|
||||||
|
},
|
||||||
updateSelectionMode(val) {
|
updateSelectionMode(val) {
|
||||||
var selectedLibraryItems = this.$store.state.selectedLibraryItems
|
var selectedLibraryItems = this.$store.state.selectedLibraryItems
|
||||||
if (this.shelf.type === 'book') {
|
if (this.shelf.type === 'book' || this.shelf.type === 'podcast') {
|
||||||
this.shelf.entities.forEach((ent) => {
|
this.shelf.entities.forEach((ent) => {
|
||||||
var component = this.$refs[`shelf-book-${ent.id}`]
|
var component = this.$refs[`shelf-book-${ent.id}`]
|
||||||
if (!component || !component.length) return
|
if (!component || !component.length) return
|
||||||
@ -113,10 +118,24 @@ export default {
|
|||||||
component.setSelectionMode(val)
|
component.setSelectionMode(val)
|
||||||
component.selected = selectedLibraryItems.includes(ent.id)
|
component.selected = selectedLibraryItems.includes(ent.id)
|
||||||
})
|
})
|
||||||
|
} else if (this.shelf.type === 'episode') {
|
||||||
|
this.shelf.entities.forEach((ent) => {
|
||||||
|
var component = this.$refs[`shelf-episode-${ent.recentEpisode.id}`]
|
||||||
|
if (!component || !component.length) return
|
||||||
|
component = component[0]
|
||||||
|
component.setSelectionMode(val)
|
||||||
|
component.selected = selectedLibraryItems.includes(ent.id)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectItem(libraryItem) {
|
selectItem(libraryItem) {
|
||||||
this.$store.commit('toggleLibraryItemSelected', libraryItem.id)
|
this.$store.commit('toggleLibraryItemSelected', libraryItem.id)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$eventBus.$emit('item-selected', libraryItem)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
itemSelectedEvt() {
|
||||||
|
this.updateSelectionMode(this.isSelectionMode)
|
||||||
},
|
},
|
||||||
scrolled() {
|
scrolled() {
|
||||||
clearTimeout(this.scrollTimer)
|
clearTimeout(this.scrollTimer)
|
||||||
@ -160,6 +179,12 @@ export default {
|
|||||||
this.canScrollLeft = false
|
this.canScrollLeft = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$eventBus.$on('item-selected', this.itemSelectedEvt)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.$eventBus.$off('item-selected', this.itemSelectedEvt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -35,8 +35,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- No progress shown for collapsed series in library and podcasts -->
|
<!-- No progress shown for collapsed series in library and podcasts (unless showing podcast episode) -->
|
||||||
<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>
|
<div v-if="!booksInSeries && (!isPodcast || episodeProgress)" 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">
|
||||||
@ -60,7 +60,7 @@
|
|||||||
<span class="material-icons" :class="selected ? 'text-yellow-400' : ''" :style="{ fontSize: 1.25 * sizeMultiplier + 'rem' }">{{ selected ? 'radio_button_checked' : 'radio_button_unchecked' }}</span>
|
<span class="material-icons" :class="selected ? 'text-yellow-400' : ''" :style="{ fontSize: 1.25 * sizeMultiplier + 'rem' }">{{ selected ? 'radio_button_checked' : 'radio_button_unchecked' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ref="moreIcon" v-show="!isSelectionMode" class="hidden md:block absolute cursor-pointer hover:text-yellow-300" :style="{ bottom: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem' }" @click.stop.prevent="clickShowMore">
|
<div ref="moreIcon" v-show="!isSelectionMode && !recentEpisode" class="hidden md:block absolute cursor-pointer hover:text-yellow-300" :style="{ bottom: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem' }" @click.stop.prevent="clickShowMore">
|
||||||
<span class="material-icons" :style="{ fontSize: 1.2 * sizeMultiplier + 'rem' }">more_vert</span>
|
<span class="material-icons" :style="{ fontSize: 1.2 * sizeMultiplier + 'rem' }">more_vert</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -77,13 +77,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
<!-- Volume number -->
|
<!-- Series sequence -->
|
||||||
<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` }">
|
<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>
|
<p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }">#{{ seriesSequence }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Podcast Episode # -->
|
||||||
|
<div v-if="recentEpisodeNumber && !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' }">Episode #{{ recentEpisodeNumber }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Podcast Num Episodes -->
|
<!-- 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' }">
|
<div v-else-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>
|
<p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }">{{ numEpisodes }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -190,6 +195,17 @@ export default {
|
|||||||
processingBatch() {
|
processingBatch() {
|
||||||
return this.store.state.processingBatch
|
return this.store.state.processingBatch
|
||||||
},
|
},
|
||||||
|
recentEpisode() {
|
||||||
|
// Only added to item when getting currently listening podcasts
|
||||||
|
return this._libraryItem.recentEpisode
|
||||||
|
},
|
||||||
|
recentEpisodeNumber() {
|
||||||
|
if (!this.recentEpisode) return null
|
||||||
|
if (this.recentEpisode.episode) {
|
||||||
|
return this.recentEpisode.episode.replace(/^#/, '')
|
||||||
|
}
|
||||||
|
return this.recentEpisode.index
|
||||||
|
},
|
||||||
collapsedSeries() {
|
collapsedSeries() {
|
||||||
// Only added to item object when collapseSeries is enabled
|
// Only added to item object when collapseSeries is enabled
|
||||||
return this._libraryItem.collapsedSeries
|
return this._libraryItem.collapsedSeries
|
||||||
@ -240,7 +256,13 @@ export default {
|
|||||||
if (this.orderBy === 'size') return 'Size: ' + this.$bytesPretty(this._libraryItem.size)
|
if (this.orderBy === 'size') return 'Size: ' + this.$bytesPretty(this._libraryItem.size)
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
|
episodeProgress() {
|
||||||
|
// Only used on home page currently listening podcast shelf
|
||||||
|
if (!this.recentEpisode) return null
|
||||||
|
return this.store.getters['user/getUserMediaProgress'](this.libraryItemId, this.recentEpisode.id)
|
||||||
|
},
|
||||||
userProgress() {
|
userProgress() {
|
||||||
|
if (this.episodeProgress) return this.episodeProgress
|
||||||
return this.store.getters['user/getUserMediaProgress'](this.libraryItemId)
|
return this.store.getters['user/getUserMediaProgress'](this.libraryItemId)
|
||||||
},
|
},
|
||||||
userProgressPercent() {
|
userProgressPercent() {
|
||||||
@ -259,7 +281,7 @@ export default {
|
|||||||
return !this.isSelectionMode && this.showExperimentalFeatures && !this.showPlayButton && this.hasEbook
|
return !this.isSelectionMode && this.showExperimentalFeatures && !this.showPlayButton && this.hasEbook
|
||||||
},
|
},
|
||||||
showPlayButton() {
|
showPlayButton() {
|
||||||
return !this.isSelectionMode && !this.isMissing && !this.isInvalid && this.numTracks && !this.isStreaming
|
return !this.isSelectionMode && !this.isMissing && !this.isInvalid && !this.isStreaming && (this.numTracks || this.recentEpisode)
|
||||||
},
|
},
|
||||||
showSmallEBookIcon() {
|
showSmallEBookIcon() {
|
||||||
return !this.isSelectionMode && this.showExperimentalFeatures && this.hasEbook
|
return !this.isSelectionMode && this.showExperimentalFeatures && this.hasEbook
|
||||||
@ -406,6 +428,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
editClick() {
|
editClick() {
|
||||||
|
if (this.recentEpisode) {
|
||||||
|
return this.$emit('edit', { libraryItem: this.libraryItem, episode: this.recentEpisode })
|
||||||
|
}
|
||||||
this.$emit('edit', this.libraryItem)
|
this.$emit('edit', this.libraryItem)
|
||||||
},
|
},
|
||||||
toggleFinished() {
|
toggleFinished() {
|
||||||
@ -529,7 +554,8 @@ export default {
|
|||||||
play() {
|
play() {
|
||||||
var eventBus = this.$eventBus || this.$nuxt.$eventBus
|
var eventBus = this.$eventBus || this.$nuxt.$eventBus
|
||||||
eventBus.$emit('play-item', {
|
eventBus.$emit('play-item', {
|
||||||
libraryItemId: this.libraryItemId
|
libraryItemId: this.libraryItemId,
|
||||||
|
episodeId: this.recentEpisode ? this.recentEpisode.id : null
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
mouseover() {
|
mouseover() {
|
||||||
|
@ -35,17 +35,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
|
||||||
value: Boolean,
|
|
||||||
libraryItem: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {}
|
|
||||||
},
|
|
||||||
episode: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
processing: false,
|
processing: false,
|
||||||
@ -72,12 +61,18 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
show: {
|
show: {
|
||||||
get() {
|
get() {
|
||||||
return this.value
|
return this.$store.state.globals.showEditPodcastEpisode
|
||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
this.$emit('input', val)
|
this.$store.commit('globals/setShowEditPodcastEpisodeModal', val)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
libraryItem() {
|
||||||
|
return this.$store.state.selectedLibraryItem
|
||||||
|
},
|
||||||
|
episode() {
|
||||||
|
return this.$store.state.globals.selectedEpisode
|
||||||
|
},
|
||||||
episodeId() {
|
episodeId() {
|
||||||
return this.episode ? this.episode.id : null
|
return this.episode ? this.episode.id : null
|
||||||
},
|
},
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</draggable>
|
</draggable>
|
||||||
|
|
||||||
<modals-podcast-edit-episode v-model="showEditEpisodeModal" :library-item="libraryItem" :episode="selectedEpisode" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -40,8 +38,6 @@ export default {
|
|||||||
sortDesc: true,
|
sortDesc: true,
|
||||||
drag: false,
|
drag: false,
|
||||||
episodesCopy: [],
|
episodesCopy: [],
|
||||||
selectedEpisode: null,
|
|
||||||
showEditEpisodeModal: false,
|
|
||||||
orderChanged: false,
|
orderChanged: false,
|
||||||
savingOrder: false
|
savingOrder: false
|
||||||
}
|
}
|
||||||
@ -97,8 +93,9 @@ export default {
|
|||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
editEpisode(episode) {
|
editEpisode(episode) {
|
||||||
this.selectedEpisode = episode
|
this.$store.commit('setSelectedLibraryItem', this.libraryItem)
|
||||||
this.showEditEpisodeModal = true
|
this.$store.commit('globals/setSelectedEpisode', episode)
|
||||||
|
this.$store.commit('globals/setShowEditPodcastEpisodeModal', true)
|
||||||
},
|
},
|
||||||
draggableUpdate() {
|
draggableUpdate() {
|
||||||
this.orderChanged = this.checkHasOrderChanged()
|
this.orderChanged = this.checkHasOrderChanged()
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
<modals-user-collections-modal />
|
<modals-user-collections-modal />
|
||||||
<modals-edit-collection-modal />
|
<modals-edit-collection-modal />
|
||||||
<modals-bookshelf-texture-modal />
|
<modals-bookshelf-texture-modal />
|
||||||
|
<modals-podcast-edit-episode />
|
||||||
<readers-reader />
|
<readers-reader />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -5,6 +5,8 @@ export const state = () => ({
|
|||||||
showBatchUserCollectionModal: false,
|
showBatchUserCollectionModal: false,
|
||||||
showUserCollectionsModal: false,
|
showUserCollectionsModal: false,
|
||||||
showEditCollectionModal: false,
|
showEditCollectionModal: false,
|
||||||
|
showEditPodcastEpisode: false,
|
||||||
|
selectedEpisode: null,
|
||||||
selectedCollection: null,
|
selectedCollection: null,
|
||||||
showBookshelfTextureModal: false,
|
showBookshelfTextureModal: false,
|
||||||
isCasting: false, // Actively casting
|
isCasting: false, // Actively casting
|
||||||
@ -46,10 +48,16 @@ export const mutations = {
|
|||||||
setShowEditCollectionModal(state, val) {
|
setShowEditCollectionModal(state, val) {
|
||||||
state.showEditCollectionModal = val
|
state.showEditCollectionModal = val
|
||||||
},
|
},
|
||||||
|
setShowEditPodcastEpisodeModal(state, val) {
|
||||||
|
state.showEditPodcastEpisode = val
|
||||||
|
},
|
||||||
setEditCollection(state, collection) {
|
setEditCollection(state, collection) {
|
||||||
state.selectedCollection = collection
|
state.selectedCollection = collection
|
||||||
state.showEditCollectionModal = true
|
state.showEditCollectionModal = true
|
||||||
},
|
},
|
||||||
|
setSelectedEpisode(state, episode) {
|
||||||
|
state.selectedEpisode = episode
|
||||||
|
},
|
||||||
setShowBookshelfTextureModal(state, val) {
|
setShowBookshelfTextureModal(state, val) {
|
||||||
state.showBookshelfTextureModal = val
|
state.showBookshelfTextureModal = val
|
||||||
},
|
},
|
||||||
|
@ -300,12 +300,12 @@ class LibraryController {
|
|||||||
var limitPerShelf = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 12
|
var limitPerShelf = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 12
|
||||||
var minified = req.query.minified === '1'
|
var minified = req.query.minified === '1'
|
||||||
|
|
||||||
var itemsWithUserProgress = libraryHelpers.getItemsWithUserProgress(req.user, libraryItems)
|
var itemsWithUserProgress = libraryHelpers.getMediaProgressWithItems(req.user, libraryItems)
|
||||||
var categories = [
|
var categories = [
|
||||||
{
|
{
|
||||||
id: 'continue-listening',
|
id: 'continue-listening',
|
||||||
label: 'Continue Listening',
|
label: 'Continue Listening',
|
||||||
type: req.library.mediaType,
|
type: isPodcastLibrary ? 'episode' : req.library.mediaType,
|
||||||
entities: libraryHelpers.getItemsMostRecentlyListened(itemsWithUserProgress, limitPerShelf, minified)
|
entities: libraryHelpers.getItemsMostRecentlyListened(itemsWithUserProgress, limitPerShelf, minified)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -317,7 +317,7 @@ class LibraryController {
|
|||||||
{
|
{
|
||||||
id: 'listen-again',
|
id: 'listen-again',
|
||||||
label: 'Listen Again',
|
label: 'Listen Again',
|
||||||
type: req.library.mediaType,
|
type: isPodcastLibrary ? 'episode' : req.library.mediaType,
|
||||||
entities: libraryHelpers.getItemsMostRecentlyFinished(itemsWithUserProgress, limitPerShelf, minified)
|
entities: libraryHelpers.getItemsMostRecentlyFinished(itemsWithUserProgress, limitPerShelf, minified)
|
||||||
}
|
}
|
||||||
].filter(cats => { // Remove categories with no items
|
].filter(cats => { // Remove categories with no items
|
||||||
@ -372,57 +372,17 @@ class LibraryController {
|
|||||||
entities: authors
|
entities: authors
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
var episodesRecentlyAdded = libraryHelpers.getEpisodesRecentlyAdded(libraryItems, limitPerShelf, minified)
|
||||||
res.json(categories)
|
if (episodesRecentlyAdded.length) {
|
||||||
}
|
categories.splice(1, 0, {
|
||||||
|
id: 'episodes-recently-added',
|
||||||
// LEGACY
|
label: 'Newest Episodes',
|
||||||
// api/libraries/:id/books/categories
|
type: 'episode',
|
||||||
async getLibraryCategories(req, res) {
|
entities: episodesRecentlyAdded
|
||||||
var library = req.library
|
})
|
||||||
var books = this.db.audiobooks.filter(ab => ab.libraryId === library.id)
|
|
||||||
var limitPerShelf = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 12
|
|
||||||
var minified = req.query.minified === '1'
|
|
||||||
|
|
||||||
var booksWithUserAb = libraryHelpers.getItemsWithUserProgress(req.user, books)
|
|
||||||
var series = libraryHelpers.getSeriesFromBooks(books, minified)
|
|
||||||
var seriesWithUserAb = libraryHelpers.getSeriesWithProgressFromBooks(req.user, books)
|
|
||||||
|
|
||||||
var categories = [
|
|
||||||
{
|
|
||||||
id: 'continue-reading',
|
|
||||||
label: 'Continue Reading',
|
|
||||||
type: 'books',
|
|
||||||
entities: libraryHelpers.getBooksMostRecentlyRead(booksWithUserAb, limitPerShelf, minified)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'continue-series',
|
|
||||||
label: 'Continue Series',
|
|
||||||
type: 'books',
|
|
||||||
entities: libraryHelpers.getBooksNextInSeries(seriesWithUserAb, limitPerShelf, minified)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'recently-added',
|
|
||||||
label: 'Recently Added',
|
|
||||||
type: 'books',
|
|
||||||
entities: libraryHelpers.getBooksMostRecentlyAdded(books, limitPerShelf, minified)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'read-again',
|
|
||||||
label: 'Read Again',
|
|
||||||
type: 'books',
|
|
||||||
entities: libraryHelpers.getBooksMostRecentlyFinished(booksWithUserAb, limitPerShelf, minified)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'recent-series',
|
|
||||||
label: 'Recent Series',
|
|
||||||
type: 'series',
|
|
||||||
entities: libraryHelpers.getSeriesMostRecentlyAdded(series, limitPerShelf)
|
|
||||||
}
|
}
|
||||||
].filter(cats => { // Remove categories with no items
|
}
|
||||||
return cats.entities.length
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json(categories)
|
res.json(categories)
|
||||||
}
|
}
|
||||||
|
@ -257,5 +257,9 @@ class Podcast {
|
|||||||
if (!episode) return 0
|
if (!episode) return 0
|
||||||
return episode.duration
|
return episode.duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEpisode(episodeId) {
|
||||||
|
return this.episodes.find(ep => ep.id == episodeId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
module.exports = Podcast
|
module.exports = Podcast
|
@ -251,6 +251,11 @@ class User {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAllMediaProgressForLibraryItem(libraryItemId) {
|
||||||
|
if (!this.mediaProgress) return []
|
||||||
|
return this.mediaProgress.filter(li => li.libraryItemId === libraryItemId)
|
||||||
|
}
|
||||||
|
|
||||||
createUpdateMediaProgress(libraryItem, updatePayload, episodeId = null) {
|
createUpdateMediaProgress(libraryItem, updatePayload, episodeId = null) {
|
||||||
var itemProgress = this.mediaProgress.find(li => {
|
var itemProgress = this.mediaProgress.find(li => {
|
||||||
if (episodeId && li.episodeId !== episodeId) return false
|
if (episodeId && li.episodeId !== episodeId) return false
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const { sort, createNewSortInstance } = require('fast-sort')
|
const { sort, createNewSortInstance } = require('fast-sort')
|
||||||
|
const Logger = require('../Logger')
|
||||||
const naturalSort = createNewSortInstance({
|
const naturalSort = createNewSortInstance({
|
||||||
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
|
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
|
||||||
})
|
})
|
||||||
@ -172,14 +173,28 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
getItemsWithUserProgress(user, libraryItems) {
|
getMediaProgressWithItems(user, libraryItems) {
|
||||||
return libraryItems.map(li => {
|
var mediaProgress = []
|
||||||
var itemProgress = user.getMediaProgress(li.id)
|
libraryItems.forEach(li => {
|
||||||
return {
|
var itemProgress = user.getAllMediaProgressForLibraryItem(li.id).map(mp => {
|
||||||
userProgress: itemProgress ? itemProgress.toJSON() : null,
|
var episode = null
|
||||||
libraryItem: li
|
if (mp.episodeId) {
|
||||||
}
|
episode = li.media.getEpisode(mp.episodeId)
|
||||||
}).filter(b => !!b.userProgress)
|
if (!episode) {
|
||||||
|
// Episode not found for library item
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
userProgress: mp.toJSON(),
|
||||||
|
libraryItem: li,
|
||||||
|
episode
|
||||||
|
}
|
||||||
|
}).filter(mp => !!mp)
|
||||||
|
|
||||||
|
mediaProgress = mediaProgress.concat(itemProgress)
|
||||||
|
})
|
||||||
|
return mediaProgress
|
||||||
},
|
},
|
||||||
|
|
||||||
getItemsMostRecentlyListened(itemsWithUserProgress, limit, minified = false) {
|
getItemsMostRecentlyListened(itemsWithUserProgress, limit, minified = false) {
|
||||||
@ -187,7 +202,13 @@ module.exports = {
|
|||||||
itemsInProgress.sort((a, b) => {
|
itemsInProgress.sort((a, b) => {
|
||||||
return b.userProgress.lastUpdate - a.userProgress.lastUpdate
|
return b.userProgress.lastUpdate - a.userProgress.lastUpdate
|
||||||
})
|
})
|
||||||
return itemsInProgress.map(b => minified ? b.libraryItem.toJSONMinified() : b.libraryItem.toJSONExpanded()).slice(0, limit)
|
return itemsInProgress.map(b => {
|
||||||
|
var libjson = minified ? b.libraryItem.toJSONMinified() : b.libraryItem.toJSONExpanded()
|
||||||
|
if (b.episode) {
|
||||||
|
libjson.recentEpisode = b.episode
|
||||||
|
}
|
||||||
|
return libjson
|
||||||
|
}).slice(0, limit)
|
||||||
},
|
},
|
||||||
|
|
||||||
getBooksNextInSeries(seriesWithUserAb, limit, minified = false) {
|
getBooksNextInSeries(seriesWithUserAb, limit, minified = false) {
|
||||||
@ -202,17 +223,39 @@ module.exports = {
|
|||||||
return booksNextInSeries.sort((a, b) => { return b.DateLastReadSeries - a.DateLastReadSeries }).map(b => minified ? b.book.toJSONMinified() : b.book.toJSONExpanded()).slice(0, limit)
|
return booksNextInSeries.sort((a, b) => { return b.DateLastReadSeries - a.DateLastReadSeries }).map(b => minified ? b.book.toJSONMinified() : b.book.toJSONExpanded()).slice(0, limit)
|
||||||
},
|
},
|
||||||
|
|
||||||
getItemsMostRecentlyAdded(libraryItems, limit, minified = false) {
|
|
||||||
var itemsSortedByAddedAt = sort(libraryItems).desc(li => li.addedAt)
|
|
||||||
return itemsSortedByAddedAt.map(b => minified ? b.toJSONMinified() : b.toJSONExpanded()).slice(0, limit)
|
|
||||||
},
|
|
||||||
|
|
||||||
getItemsMostRecentlyFinished(itemsWithUserProgress, limit, minified = false) {
|
getItemsMostRecentlyFinished(itemsWithUserProgress, limit, minified = false) {
|
||||||
var itemsFinished = itemsWithUserProgress.filter((data) => data.userProgress && data.userProgress.isFinished)
|
var itemsFinished = itemsWithUserProgress.filter((data) => data.userProgress && data.userProgress.isFinished)
|
||||||
itemsFinished.sort((a, b) => {
|
itemsFinished.sort((a, b) => {
|
||||||
return b.userProgress.finishedAt - a.userProgress.finishedAt
|
return b.userProgress.finishedAt - a.userProgress.finishedAt
|
||||||
})
|
})
|
||||||
return itemsFinished.map(i => minified ? i.libraryItem.toJSONMinified() : i.libraryItem.toJSONExpanded()).slice(0, limit)
|
return itemsFinished.map(i => {
|
||||||
|
var libjson = minified ? i.libraryItem.toJSONMinified() : i.libraryItem.toJSONExpanded()
|
||||||
|
if (i.episode) {
|
||||||
|
libjson.recentEpisode = i.episode
|
||||||
|
}
|
||||||
|
return libjson
|
||||||
|
}).slice(0, limit)
|
||||||
|
},
|
||||||
|
|
||||||
|
getItemsMostRecentlyAdded(libraryItems, limit, minified = false) {
|
||||||
|
var itemsSortedByAddedAt = sort(libraryItems).desc(li => li.addedAt)
|
||||||
|
return itemsSortedByAddedAt.map(b => minified ? b.toJSONMinified() : b.toJSONExpanded()).slice(0, limit)
|
||||||
|
},
|
||||||
|
|
||||||
|
getEpisodesRecentlyAdded(libraryItems, limit, minified = false) {
|
||||||
|
var libraryItemsWithEpisode = []
|
||||||
|
libraryItems.forEach((li) => {
|
||||||
|
if (li.mediaType !== 'podcast' || !li.media.hasMediaEntities) return
|
||||||
|
var libjson = minified ? li.toJSONMinified() : li.toJSONExpanded()
|
||||||
|
var episodes = sort(li.media.episodes || []).desc(ep => ep.addedAt)
|
||||||
|
episodes.forEach((ep) => {
|
||||||
|
var lie = { ...libjson }
|
||||||
|
lie.recentEpisode = ep
|
||||||
|
libraryItemsWithEpisode.push(lie)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
libraryItemsWithEpisode = sort(libraryItemsWithEpisode).desc(lie => lie.recentEpisode.addedAt)
|
||||||
|
return libraryItemsWithEpisode.slice(0, limit)
|
||||||
},
|
},
|
||||||
|
|
||||||
getSeriesMostRecentlyAdded(series, limit) {
|
getSeriesMostRecentlyAdded(series, limit) {
|
||||||
|
Loading…
Reference in New Issue
Block a user