Add remaining personalized shelf queries for podcasts

This commit is contained in:
advplyr 2023-08-05 15:28:16 -05:00
parent 09eefae808
commit 91b6c4412d
6 changed files with 219 additions and 73 deletions

View File

@ -170,9 +170,8 @@ export default {
this.loaded = true
},
async fetchCategories() {
const endpoint = this.currentLibraryMediaType === 'book' ? 'personalized2' : 'personalized'
const categories = await this.$axios
.$get(`/api/libraries/${this.currentLibraryId}/${endpoint}?include=rssfeed,numEpisodesIncomplete`)
.$get(`/api/libraries/${this.currentLibraryId}/personalized2?include=rssfeed,numEpisodesIncomplete`)
.then((data) => {
return data
})

View File

@ -133,12 +133,15 @@ export default {
this.rendition.spread(settings.spread || 'auto')
},
prev() {
if (!this.rendition?.manager) return
return this.rendition?.prev()
},
next() {
if (!this.rendition?.manager) return
return this.rendition?.next()
},
goToChapter(href) {
if (!this.rendition?.manager) return
return this.rendition?.display(href)
},
keyUp(e) {

View File

@ -446,27 +446,27 @@ module.exports = (sequelize) => {
* @returns {object[]} array of shelf objects
*/
static async getPersonalizedShelves(library, userId, include, limit) {
const isPodcastLibrary = library.mediaType === 'podcast'
const fullStart = Date.now() // Used for testing load times
const shelves = []
// "Continue Listening" shelf
const itemsInProgressPayload = await libraryFilters.getMediaItemsInProgress(library, userId, include, limit, false)
if (itemsInProgressPayload.items.length) {
shelves.push({
id: 'continue-listening',
label: 'Continue Listening',
labelStringKey: 'LabelContinueListening',
type: isPodcastLibrary ? 'episode' : 'book',
type: library.isPodcast ? 'episode' : 'book',
entities: itemsInProgressPayload.items,
total: itemsInProgressPayload.count
})
}
Logger.debug(`Loaded ${itemsInProgressPayload.items.length} items for "Continue Listening" in ${((Date.now() - fullStart) / 1000).toFixed(2)}s`)
Logger.debug(`Loaded ${itemsInProgressPayload.items.length} of ${itemsInProgressPayload.count} items for "Continue Listening" in ${((Date.now() - fullStart) / 1000).toFixed(2)}s`)
let start = Date.now()
if (library.mediaType === 'book') {
if (library.isBook) {
// "Continue Reading" shelf
const ebooksInProgressPayload = await libraryFilters.getMediaItemsInProgress(library, userId, include, limit, true)
if (ebooksInProgressPayload.items.length) {
shelves.push({
@ -478,9 +478,10 @@ module.exports = (sequelize) => {
total: ebooksInProgressPayload.count
})
}
Logger.debug(`Loaded ${ebooksInProgressPayload.items.length} items for "Continue Reading" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
Logger.debug(`Loaded ${ebooksInProgressPayload.items.length} of ${ebooksInProgressPayload.count} items for "Continue Reading" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
start = Date.now()
// "Continue Series" shelf
const continueSeriesPayload = await libraryFilters.getLibraryItemsContinueSeries(library, userId, include, limit)
if (continueSeriesPayload.libraryItems.length) {
shelves.push({
@ -492,10 +493,25 @@ module.exports = (sequelize) => {
total: continueSeriesPayload.count
})
}
Logger.debug(`Loaded ${continueSeriesPayload.libraryItems.length} items for "Continue Series" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
start = Date.now()
Logger.debug(`Loaded ${continueSeriesPayload.libraryItems.length} of ${continueSeriesPayload.count} items for "Continue Series" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
} else if (library.isPodcast) {
// "Newest Episodes" shelf
const newestEpisodesPayload = await libraryFilters.getNewestPodcastEpisodes(library, userId, limit)
if (newestEpisodesPayload.libraryItems.length) {
shelves.push({
id: 'newest-episodes',
label: 'Newest Episodes',
labelStringKey: 'LabelNewestEpisodes',
type: 'episode',
entities: newestEpisodesPayload.libraryItems,
total: newestEpisodesPayload.count
})
}
Logger.debug(`Loaded ${newestEpisodesPayload.libraryItems.length} of ${newestEpisodesPayload.count} episodes for "Newest Episodes" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
}
start = Date.now()
// "Recently Added" shelf
const mostRecentPayload = await libraryFilters.getLibraryItemsMostRecentlyAdded(library, userId, include, limit)
if (mostRecentPayload.libraryItems.length) {
shelves.push({
@ -507,77 +523,86 @@ module.exports = (sequelize) => {
total: mostRecentPayload.count
})
}
Logger.debug(`Loaded ${mostRecentPayload.libraryItems.length} items for "Recently Added" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
Logger.debug(`Loaded ${mostRecentPayload.libraryItems.length} of ${mostRecentPayload.count} items for "Recently Added" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
start = Date.now()
const seriesMostRecentPayload = await libraryFilters.getSeriesMostRecentlyAdded(library, include, 5)
if (seriesMostRecentPayload.series.length) {
shelves.push({
id: 'recent-series',
label: 'Recent Series',
labelStringKey: 'LabelRecentSeries',
type: 'series',
entities: seriesMostRecentPayload.series,
total: seriesMostRecentPayload.count
})
if (library.isBook) {
start = Date.now()
// "Recent Series" shelf
const seriesMostRecentPayload = await libraryFilters.getSeriesMostRecentlyAdded(library, include, 5)
if (seriesMostRecentPayload.series.length) {
shelves.push({
id: 'recent-series',
label: 'Recent Series',
labelStringKey: 'LabelRecentSeries',
type: 'series',
entities: seriesMostRecentPayload.series,
total: seriesMostRecentPayload.count
})
}
Logger.debug(`Loaded ${seriesMostRecentPayload.series.length} of ${seriesMostRecentPayload.count} series for "Recent Series" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
start = Date.now()
// "Discover" shelf
const discoverLibraryItemsPayload = await libraryFilters.getLibraryItemsToDiscover(library, userId, include, limit)
if (discoverLibraryItemsPayload.libraryItems.length) {
shelves.push({
id: 'discover',
label: 'Discover',
labelStringKey: 'LabelDiscover',
type: library.mediaType,
entities: discoverLibraryItemsPayload.libraryItems,
total: discoverLibraryItemsPayload.count
})
}
Logger.debug(`Loaded ${discoverLibraryItemsPayload.libraryItems.length} of ${discoverLibraryItemsPayload.count} items for "Discover" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
}
Logger.debug(`Loaded ${seriesMostRecentPayload.series.length} items for "Recent Series" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
start = Date.now()
const discoverLibraryItemsPayload = await libraryFilters.getLibraryItemsToDiscover(library, userId, include, limit)
if (discoverLibraryItemsPayload.libraryItems.length) {
shelves.push({
id: 'discover',
label: 'Discover',
labelStringKey: 'LabelDiscover',
type: library.mediaType,
entities: discoverLibraryItemsPayload.libraryItems,
total: discoverLibraryItemsPayload.count
})
}
Logger.debug(`Loaded ${discoverLibraryItemsPayload.libraryItems.length} items for "Discover" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
start = Date.now()
// "Listen Again" shelf
const listenAgainPayload = await libraryFilters.getMediaFinished(library, userId, include, limit, false)
if (listenAgainPayload.items.length) {
shelves.push({
id: 'listen-again',
label: 'Listen Again',
labelStringKey: 'LabelListenAgain',
type: isPodcastLibrary ? 'episode' : 'book',
type: library.isPodcast ? 'episode' : 'book',
entities: listenAgainPayload.items,
total: listenAgainPayload.count
})
}
Logger.debug(`Loaded ${listenAgainPayload.items.length} items for "Listen Again" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
Logger.debug(`Loaded ${listenAgainPayload.items.length} of ${listenAgainPayload.count} items for "Listen Again" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
start = Date.now()
const readAgainPayload = await libraryFilters.getMediaFinished(library, userId, include, limit, true)
if (readAgainPayload.items.length) {
shelves.push({
id: 'read-again',
label: 'Read Again',
labelStringKey: 'LabelReadAgain',
type: 'book',
entities: readAgainPayload.items,
total: readAgainPayload.count
})
}
Logger.debug(`Loaded ${readAgainPayload.items.length} items for "Read Again" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
if (library.isBook) {
start = Date.now()
// "Read Again" shelf
const readAgainPayload = await libraryFilters.getMediaFinished(library, userId, include, limit, true)
if (readAgainPayload.items.length) {
shelves.push({
id: 'read-again',
label: 'Read Again',
labelStringKey: 'LabelReadAgain',
type: 'book',
entities: readAgainPayload.items,
total: readAgainPayload.count
})
}
Logger.debug(`Loaded ${readAgainPayload.items.length} of ${readAgainPayload.count} items for "Read Again" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
start = Date.now()
const newestAuthorsPayload = await libraryFilters.getNewestAuthors(library, limit)
if (newestAuthorsPayload.authors.length) {
shelves.push({
id: 'newest-authors',
label: 'Newest Authors',
labelStringKey: 'LabelNewestAuthors',
type: 'authors',
entities: newestAuthorsPayload.authors,
total: newestAuthorsPayload.count
})
start = Date.now()
// "Newest Authors" shelf
const newestAuthorsPayload = await libraryFilters.getNewestAuthors(library, limit)
if (newestAuthorsPayload.authors.length) {
shelves.push({
id: 'newest-authors',
label: 'Newest Authors',
labelStringKey: 'LabelNewestAuthors',
type: 'authors',
entities: newestAuthorsPayload.authors,
total: newestAuthorsPayload.count
})
}
Logger.debug(`Loaded ${newestAuthorsPayload.authors.length} of ${newestAuthorsPayload.count} authors for "Newest Authors" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
}
Logger.debug(`Loaded ${newestAuthorsPayload.authors.length} authors for "Newest Authors" in ${((Date.now() - start) / 1000).toFixed(2)}s`)
Logger.debug(`Loaded ${shelves.length} personalized shelves in ${((Date.now() - fullStart) / 1000).toFixed(2)}s`)

View File

@ -59,10 +59,14 @@ module.exports = {
count
}
} else {
// TODO: Get episodes in progress
const { libraryItems, count } = await libraryItemsPodcastFilters.getFilteredPodcastEpisodes(library.id, userId, 'progress', 'in-progress', 'progress', true, limit, 0)
return {
count: 0,
items: []
count,
items: libraryItems.map(li => {
const oldLibraryItem = Database.models.libraryItem.getOldLibraryItem(li).toJSONMinified()
oldLibraryItem.recentEpisode = li.recentEpisode
return oldLibraryItem
})
}
}
},
@ -160,10 +164,14 @@ module.exports = {
count
}
} else {
// TODO: Get podcast episodes finished
const { libraryItems, count } = await libraryItemsPodcastFilters.getFilteredPodcastEpisodes(library.id, userId, 'progress', 'finished', 'progress', true, limit, 0)
return {
items: [],
count: 0
count,
items: libraryItems.map(li => {
const oldLibraryItem = Database.models.libraryItem.getOldLibraryItem(li).toJSONMinified()
oldLibraryItem.recentEpisode = li.recentEpisode
return oldLibraryItem
})
}
}
},
@ -299,5 +307,26 @@ module.exports = {
}),
count
}
},
/**
* Get podcast episodes most recently added
* @param {oldLibrary} library
* @param {string} userId
* @param {number} limit
* @returns {object} {libraryItems:oldLibraryItem[], count:number}
*/
async getNewestPodcastEpisodes(library, userId, limit) {
if (library.mediaType !== 'podcast') return { libraryItems: [], count: 0 }
const { libraryItems, count } = await libraryItemsPodcastFilters.getFilteredPodcastEpisodes(library.id, userId, null, null, 'createdAt', true, limit, 0)
return {
count,
libraryItems: libraryItems.map(li => {
const oldLibraryItem = Database.models.libraryItem.getOldLibraryItem(li).toJSONMinified()
oldLibraryItem.recentEpisode = li.recentEpisode
return oldLibraryItem
})
}
}
}

View File

@ -230,7 +230,7 @@ module.exports = {
}
} else if (sortBy === 'sequence') {
const nullDir = sortDesc ? 'DESC NULLS FIRST' : 'ASC NULLS LAST'
return [[Sequelize.literal(`CAST(\`series.bookSeries.sequence\` AS INTEGER) COLLATE NOCASE ${nullDir}`)]]
return [[Sequelize.literal(`CAST(\`series.bookSeries.sequence\` AS INTEGER) ${nullDir}`)]]
} else if (sortBy === 'progress') {
return [[Sequelize.literal('mediaProgresses.updatedAt'), dir]]
}
@ -268,7 +268,7 @@ module.exports = {
}
],
order: [
Sequelize.literal('CAST(`books.bookSeries.sequence` AS INTEGER) COLLATE NOCASE ASC NULLS LAST')
Sequelize.literal('CAST(`books.bookSeries.sequence` AS INTEGER) ASC NULLS LAST')
]
})
const bookSeriesToInclude = []
@ -426,7 +426,7 @@ module.exports = {
})
if (sortBy !== 'sequence') {
// Secondary sort by sequence
sortOrder.push([Sequelize.literal('CAST(`series.bookSeries.sequence` AS INTEGER) COLLATE NOCASE ASC NULLS LAST')])
sortOrder.push([Sequelize.literal('CAST(`series.bookSeries.sequence` AS INTEGER) ASC NULLS LAST')])
}
} else if (filterGroup === 'issues') {
libraryItemWhere[Sequelize.Op.or] = [

View File

@ -148,6 +148,96 @@ module.exports = {
return libraryItem
})
return {
libraryItems,
count
}
},
/**
* Get podcast episodes filtered and sorted
* @param {string} libraryId
* @param {string} userId
* @param {[string]} filterGroup
* @param {[string]} filterValue
* @param {string} sortBy
* @param {string} sortDesc
* @param {number} limit
* @param {number} offset
* @returns {object} {libraryItems:LibraryItem[], count:number}
*/
async getFilteredPodcastEpisodes(libraryId, userId, filterGroup, filterValue, sortBy, sortDesc, limit, offset) {
if (sortBy === 'progress' && filterGroup !== 'progress') {
Logger.warn('Cannot sort podcast episodes by progress without filtering by progress')
sortBy = 'createdAt'
}
const podcastEpisodeIncludes = []
let podcastEpisodeWhere = {}
if (filterGroup === 'progress') {
podcastEpisodeIncludes.push({
model: Database.models.mediaProgress,
where: {
userId
},
attributes: ['id', 'isFinished', 'currentTime', 'updatedAt']
})
if (filterValue === 'in-progress') {
podcastEpisodeWhere = [
{
'$mediaProgresses.isFinished$': false
},
{
'$mediaProgresses.currentTime$': {
[Sequelize.Op.gt]: 0
}
}
]
} else if (filterValue === 'finished') {
podcastEpisodeWhere['$mediaProgresses.isFinished$'] = true
}
}
const podcastEpisodeOrder = []
if (sortBy === 'createdAt') {
podcastEpisodeOrder.push(['createdAt', sortDesc ? 'DESC' : 'ASC'])
} else if (sortBy === 'progress') {
podcastEpisodeOrder.push([Sequelize.literal('mediaProgresses.updatedAt'), sortDesc ? 'DESC' : 'ASC'])
}
const { rows: podcastEpisodes, count } = await Database.models.podcastEpisode.findAndCountAll({
where: podcastEpisodeWhere,
include: [
{
model: Database.models.podcast,
include: [
{
model: Database.models.libraryItem,
where: {
libraryId
}
}
]
},
...podcastEpisodeIncludes
],
distinct: true,
subQuery: false,
order: podcastEpisodeOrder,
limit,
offset
})
const libraryItems = podcastEpisodes.map((ep) => {
const libraryItem = ep.podcast.libraryItem.toJSON()
const podcast = ep.podcast.toJSON()
delete podcast.libraryItem
libraryItem.media = podcast
libraryItem.recentEpisode = ep.getOldPodcastEpisode(libraryItem.id)
return libraryItem
})
return {
libraryItems,
count