Add:Recommended book home page shelf

This commit is contained in:
advplyr 2023-01-27 17:59:06 -06:00
parent 030c20b12e
commit 49279430fc
10 changed files with 142 additions and 35 deletions

View File

@ -820,7 +820,6 @@ export default {
return null
})
if (!libraryItem) return
console.log('Got library itemn', libraryItem)
this.store.commit('showEReader', libraryItem)
},
selectBtnClick(evt) {

View File

@ -308,6 +308,7 @@
"LabelPublishYear": "Jahr",
"LabelRecentlyAdded": "Kürzlich hinzugefügt",
"LabelRecentSeries": "Aktuelle Serien",
"LabelRecommended": "Recommended",
"LabelRegion": "Region",
"LabelReleaseDate": "Veröffentlichungsdatum",
"LabelRemoveCover": "Lösche Titelbild",
@ -640,4 +641,4 @@
"WeekdayTuesday": "Dienstag",
"WeekdayWed": "Wed",
"WeekdayWednesday": "Mittwoch"
}
}

View File

@ -308,6 +308,7 @@
"LabelPublishYear": "Publish Year",
"LabelRecentlyAdded": "Recently Added",
"LabelRecentSeries": "Recent Series",
"LabelRecommended": "Recommended",
"LabelRegion": "Region",
"LabelReleaseDate": "Release Date",
"LabelRemoveCover": "Remove cover",
@ -640,4 +641,4 @@
"WeekdayTuesday": "Tuesday",
"WeekdayWed": "Wed",
"WeekdayWednesday": "Wednesday"
}
}

View File

@ -308,6 +308,7 @@
"LabelPublishYear": "Publish Year",
"LabelRecentlyAdded": "Recently Added",
"LabelRecentSeries": "Recent Series",
"LabelRecommended": "Recommended",
"LabelRegion": "Region",
"LabelReleaseDate": "Release Date",
"LabelRemoveCover": "Remove cover",
@ -640,4 +641,4 @@
"WeekdayTuesday": "Tuesday",
"WeekdayWed": "Wed",
"WeekdayWednesday": "Wednesday"
}
}

View File

@ -308,6 +308,7 @@
"LabelPublishYear": "Année d'Edition",
"LabelRecentlyAdded": "Derniers Ajouts",
"LabelRecentSeries": "Séries Récentes",
"LabelRecommended": "Recommended",
"LabelRegion": "Région",
"LabelReleaseDate": "Date de Parution",
"LabelRemoveCover": "Supprimer la Couverture",
@ -640,4 +641,4 @@
"WeekdayTuesday": "Mardi",
"WeekdayWed": "Mer",
"WeekdayWednesday": "Mercredi"
}
}

View File

@ -308,6 +308,7 @@
"LabelPublishYear": "Godina izdavanja",
"LabelRecentlyAdded": "Nedavno dodano",
"LabelRecentSeries": "Nedavne serije",
"LabelRecommended": "Recommended",
"LabelRegion": "Regija",
"LabelReleaseDate": "Datum izlaska",
"LabelRemoveCover": "Remove cover",
@ -640,4 +641,4 @@
"WeekdayTuesday": "Utorak",
"WeekdayWed": "Wed",
"WeekdayWednesday": "Srijeda"
}
}

View File

@ -308,6 +308,7 @@
"LabelPublishYear": "Anno Pubblicazione",
"LabelRecentlyAdded": "Aggiunti Recentemente",
"LabelRecentSeries": "Serie Recenti",
"LabelRecommended": "Recommended",
"LabelRegion": "Regione",
"LabelReleaseDate": "Data Release",
"LabelRemoveCover": "Remove cover",
@ -640,4 +641,4 @@
"WeekdayTuesday": "Martedì",
"WeekdayWed": "Mer",
"WeekdayWednesday": "Mercoledì"
}
}

View File

@ -308,6 +308,7 @@
"LabelPublishYear": "Rok publikacji",
"LabelRecentlyAdded": "Niedawno dodany",
"LabelRecentSeries": "Ostatnie serie",
"LabelRecommended": "Recommended",
"LabelRegion": "Region",
"LabelReleaseDate": "Data wydania",
"LabelRemoveCover": "Remove cover",
@ -640,4 +641,4 @@
"WeekdayTuesday": "Wtorek",
"WeekdayWed": "Wed",
"WeekdayWednesday": "Środa"
}
}

View File

@ -308,6 +308,7 @@
"LabelPublishYear": "发布年份",
"LabelRecentlyAdded": "最近添加",
"LabelRecentSeries": "最近添加系列",
"LabelRecommended": "Recommended",
"LabelRegion": "区域",
"LabelReleaseDate": "发布日期",
"LabelRemoveCover": "移除封面",
@ -640,4 +641,4 @@
"WeekdayTuesday": "星期二",
"WeekdayWed": "Wed",
"WeekdayWednesday": "星期三"
}
}

View File

@ -339,6 +339,14 @@ module.exports = {
entities: [],
category: 'continueSeries'
},
{
id: 'episodes-recently-added',
label: 'Newest Episodes',
labelStringKey: 'LabelNewestEpisodes',
type: 'episode',
entities: [],
category: 'newestEpisodes'
},
{
id: 'recently-added',
label: 'Recently Added',
@ -347,14 +355,6 @@ module.exports = {
entities: [],
category: 'newestItems'
},
{
id: 'listen-again',
label: 'Listen Again',
labelStringKey: 'LabelListenAgain',
type: isPodcastLibrary ? 'episode' : mediaType,
entities: [],
category: 'recentlyFinished'
},
{
id: 'recent-series',
label: 'Recent Series',
@ -363,6 +363,22 @@ module.exports = {
entities: [],
category: 'newestSeries'
},
{
id: 'recommended',
label: 'Recommended',
labelStringKey: 'LabelRecommended',
type: mediaType,
entities: [],
category: 'recommended'
},
{
id: 'listen-again',
label: 'Listen Again',
labelStringKey: 'LabelListenAgain',
type: isPodcastLibrary ? 'episode' : mediaType,
entities: [],
category: 'recentlyFinished'
},
{
id: 'newest-authors',
label: 'Newest Authors',
@ -370,22 +386,13 @@ module.exports = {
type: 'authors',
entities: [],
category: 'newestAuthors'
},
{
id: 'episodes-recently-added',
label: 'Newest Episodes',
labelStringKey: 'LabelNewestEpisodes',
type: 'episode',
entities: [],
category: 'newestEpisodes'
}
]
const categories = ['recentlyListened', 'continueSeries', 'newestEpisodes', 'newestItems', 'newestSeries', 'recentlyFinished', 'newestAuthors']
const categoryMap = {}
categories.forEach((cat) => {
categoryMap[cat] = {
category: cat,
shelves.forEach((shelf) => {
categoryMap[shelf.category] = {
category: shelf.category,
biggest: 0,
smallest: 0,
items: []
@ -395,6 +402,12 @@ module.exports = {
const seriesMap = {}
const authorMap = {}
// For use with recommended
const topGenresListened = {}
const topAuthorsListened = {}
const topTagsListened = {}
const notStartedBooks = []
for (const libraryItem of libraryItems) {
if (libraryItem.addedAt > categoryMap.newestItems.smallest) {
@ -494,10 +507,28 @@ module.exports = {
} else if (libraryItem.isBook) {
// Book categories
const mediaProgress = allItemProgress.length ? allItemProgress[0] : null
// Used for recommended. Tally up most listened to authors/genres/tags
if (mediaProgress && (mediaProgress.inProgress || mediaProgress.isFinished)) {
libraryItem.media.metadata.authors.forEach((author) => {
topAuthorsListened[author.id] = (topAuthorsListened[author.id] || 0) + 1
})
libraryItem.media.metadata.genres.forEach((genre) => {
topGenresListened[genre] = (topGenresListened[genre] || 0) + 1
})
libraryItem.media.tags.forEach((tag) => {
topTagsListened[tag] = (topTagsListened[tag] || 0) + 1
})
} else {
// Insert in random position to add randomization to equal weighted items
notStartedBooks.splice(Math.floor(Math.random() * (notStartedBooks.length + 1)), 0, libraryItem)
}
// Newest series
if (libraryItem.media.metadata.series.length) {
for (const librarySeries of libraryItem.media.metadata.series) {
const mediaProgress = allItemProgress.length ? allItemProgress[0] : null
const bookInProgress = mediaProgress && (mediaProgress.inProgress || mediaProgress.isFinished)
const bookActive = mediaProgress && mediaProgress.inProgress && !mediaProgress.isFinished
const libraryItemJson = libraryItem.toJSONMinified()
@ -602,7 +633,6 @@ module.exports = {
}
// Book listening and finished
var mediaProgress = allItemProgress.length ? allItemProgress[0] : null
if (mediaProgress) {
// Handle most recently finished
if (mediaProgress.isFinished) {
@ -612,7 +642,7 @@ module.exports = {
finishedAt: mediaProgress.finishedAt
}
var indexToPut = categoryMap.recentlyFinished.items.findIndex(i => mediaProgress.finishedAt > i.finishedAt)
const indexToPut = categoryMap.recentlyFinished.items.findIndex(i => mediaProgress.finishedAt > i.finishedAt)
if (indexToPut >= 0) {
categoryMap.recentlyFinished.items.splice(indexToPut, 0, libraryItemObj)
} else {
@ -632,7 +662,7 @@ module.exports = {
progressLastUpdate: mediaProgress.lastUpdate
}
var indexToPut = categoryMap.recentlyListened.items.findIndex(i => mediaProgress.lastUpdate > i.progressLastUpdate)
const indexToPut = categoryMap.recentlyListened.items.findIndex(i => mediaProgress.lastUpdate > i.progressLastUpdate)
if (indexToPut >= 0) {
categoryMap.recentlyListened.items.splice(indexToPut, 0, libraryItemObj)
} else { // Should only happen when array is < max
@ -652,9 +682,9 @@ module.exports = {
// For Continue Series - Find next book in series for series that are in progress
for (const seriesId in seriesMap) {
if (seriesMap[seriesId].inProgress && !seriesMap[seriesId].hideFromContinueListening) {
seriesMap[seriesId].books = naturalSort(seriesMap[seriesId].books).asc(li => li.seriesSequence)
seriesMap[seriesId].books = naturalSort(seriesMap[seriesId].books).asc(li => li.seriesSequence)
if (seriesMap[seriesId].inProgress && !seriesMap[seriesId].hideFromContinueListening) {
// take the first book unread with the smallest series sequence
// unless the user is already listening to a book from this series
const hasActiveBook = seriesMap[seriesId].hasActiveBook
@ -683,6 +713,76 @@ module.exports = {
}
}
// For recommended
if (!isPodcastLibrary && notStartedBooks.length) {
const genresCount = Object.values(topGenresListened).reduce((a, b) => a + b, 0)
const authorsCount = Object.values(topAuthorsListened).reduce((a, b) => a + b, 0)
const tagsCount = Object.values(topTagsListened).reduce((a, b) => a + b, 0)
for (const libraryItem of notStartedBooks) {
// dont include books in an unfinished series and books that are not first in an unstarted series
let shouldContinue = !libraryItem.media.metadata.series.length
libraryItem.media.metadata.series.forEach((se) => {
if (seriesMap[se.id]) {
if (seriesMap[se.id].inProgress) {
shouldContinue = false
return
} else if (seriesMap[se.id].books[0].id === libraryItem.id) {
shouldContinue = true
}
}
})
if (!shouldContinue) {
continue;
}
let totalWeight = 0
if (authorsCount > 0) {
libraryItem.media.metadata.authors.forEach((author) => {
if (topAuthorsListened[author.id]) {
totalWeight += topAuthorsListened[author.id] / authorsCount
}
})
}
if (genresCount > 0) {
libraryItem.media.metadata.genres.forEach((genre) => {
if (topGenresListened[genre]) {
totalWeight += topGenresListened[genre] / genresCount
}
})
}
if (tagsCount > 0) {
libraryItem.media.tags.forEach((tag) => {
if (topTagsListened[tag]) {
totalWeight += topTagsListened[tag] / tagsCount
}
})
}
if (!categoryMap.recommended.smallest || totalWeight > categoryMap.recommended.smallest) {
const libraryItemObj = {
...libraryItem.toJSONMinified(),
weight: totalWeight
}
const indexToPut = categoryMap.recommended.items.findIndex(i => totalWeight > i.weight)
if (indexToPut >= 0) {
categoryMap.recommended.items.splice(indexToPut, 0, libraryItemObj)
} else {
categoryMap.recommended.items.push(libraryItemObj)
}
if (categoryMap.recommended.items.length > maxEntitiesPerShelf) {
categoryMap.recommended.items.pop()
categoryMap.recommended.smallest = categoryMap.recommended.items[categoryMap.recommended.items.length - 1].weight
}
}
}
}
// Sort series books by sequence
if (categoryMap.newestSeries.items.length) {
for (const seriesItem of categoryMap.newestSeries.items) {