Merge branch 'master' into LocalizedDateFns

This commit is contained in:
Tomazed 2023-01-30 15:22:59 +01:00 committed by GitHub
commit 74d8a09f31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 165 additions and 40 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",

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",

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",

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",

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",

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",

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",

View File

@ -308,6 +308,7 @@
"LabelPublishYear": "发布年份",
"LabelRecentlyAdded": "最近添加",
"LabelRecentSeries": "最近添加系列",
"LabelRecommended": "推荐内容",
"LabelRegion": "区域",
"LabelReleaseDate": "发布日期",
"LabelRemoveCover": "移除封面",

View File

@ -192,7 +192,8 @@ class MeController {
}
const updatedLocalMediaProgress = []
var numServerProgressUpdates = 0
var localMediaProgress = req.body.localMediaProgress || []
const updatedServerMediaProgress = []
const localMediaProgress = req.body.localMediaProgress || []
localMediaProgress.forEach(localProgress => {
if (!localProgress.libraryItemId) {
@ -205,18 +206,22 @@ class MeController {
return
}
var mediaProgress = req.user.getMediaProgress(localProgress.libraryItemId, localProgress.episodeId)
let mediaProgress = req.user.getMediaProgress(localProgress.libraryItemId, localProgress.episodeId)
if (!mediaProgress) {
// New media progress from mobile
Logger.debug(`[MeController] syncLocalMediaProgress local progress is new - creating ${localProgress.id}`)
req.user.createUpdateMediaProgress(libraryItem, localProgress, localProgress.episodeId)
mediaProgress = req.user.getMediaProgress(localProgress.libraryItemId, localProgress.episodeId)
updatedServerMediaProgress.push(mediaProgress)
numServerProgressUpdates++
} else if (mediaProgress.lastUpdate < localProgress.lastUpdate) {
Logger.debug(`[MeController] syncLocalMediaProgress local progress is more recent - updating ${mediaProgress.id}`)
req.user.createUpdateMediaProgress(libraryItem, localProgress, localProgress.episodeId)
mediaProgress = req.user.getMediaProgress(localProgress.libraryItemId, localProgress.episodeId)
updatedServerMediaProgress.push(mediaProgress)
numServerProgressUpdates++
} else if (mediaProgress.lastUpdate > localProgress.lastUpdate) {
var updateTimeDifference = mediaProgress.lastUpdate - localProgress.lastUpdate
const updateTimeDifference = mediaProgress.lastUpdate - localProgress.lastUpdate
Logger.debug(`[MeController] syncLocalMediaProgress server progress is more recent by ${updateTimeDifference}ms - ${mediaProgress.id}`)
for (const key in localProgress) {
@ -240,7 +245,8 @@ class MeController {
res.json({
numServerProgressUpdates,
localProgressUpdates: updatedLocalMediaProgress
localProgressUpdates: updatedLocalMediaProgress, // Array of LocalMediaProgress that were updated from server (server more recent)
serverProgressUpdates: updatedServerMediaProgress // Array of MediaProgress that made updates to server (local more recent)
})
}

View File

@ -81,9 +81,17 @@ class RssFeedManager {
}
// Check if feed needs to be updated
if (feed.entityType === 'item') {
if (feed.entityType === 'libraryItem') {
const libraryItem = this.db.getLibraryItem(feed.entityId)
if (libraryItem && (!feed.entityUpdatedAt || libraryItem.updatedAt > feed.entityUpdatedAt)) {
let mostRecentlyUpdatedAt = libraryItem.updatedAt
if (libraryItem.isPodcast) {
libraryItem.media.episodes.forEach((episode) => {
if (episode.updatedAt > mostRecentlyUpdatedAt) mostRecentlyUpdatedAt = episode.updatedAt
})
}
if (libraryItem && (!feed.entityUpdatedAt || mostRecentlyUpdatedAt > feed.entityUpdatedAt)) {
Logger.debug(`[RssFeedManager] Updating RSS feed for item ${libraryItem.id} "${libraryItem.media.metadata.title}"`)
feed.updateFromItem(libraryItem)
await this.db.updateEntity('feed', feed)

View File

@ -110,13 +110,15 @@ class Feed {
this.episodes = []
if (isPodcast) { // PODCAST EPISODES
media.episodes.forEach((episode) => {
var feedEpisode = new FeedEpisode()
if (episode.updatedAt > this.entityUpdatedAt) this.entityUpdatedAt = episode.updatedAt
const feedEpisode = new FeedEpisode()
feedEpisode.setFromPodcastEpisode(libraryItem, serverAddress, slug, episode, this.meta)
this.episodes.push(feedEpisode)
})
} else { // AUDIOBOOK EPISODES
media.tracks.forEach((audioTrack) => {
var feedEpisode = new FeedEpisode()
const feedEpisode = new FeedEpisode()
feedEpisode.setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, this.meta)
this.episodes.push(feedEpisode)
})
@ -144,13 +146,15 @@ class Feed {
this.episodes = []
if (isPodcast) { // PODCAST EPISODES
media.episodes.forEach((episode) => {
var feedEpisode = new FeedEpisode()
if (episode.updatedAt > this.entityUpdatedAt) this.entityUpdatedAt = episode.updatedAt
const feedEpisode = new FeedEpisode()
feedEpisode.setFromPodcastEpisode(libraryItem, this.serverAddress, this.slug, episode, this.meta)
this.episodes.push(feedEpisode)
})
} else { // AUDIOBOOK EPISODES
media.tracks.forEach((audioTrack) => {
var feedEpisode = new FeedEpisode()
const feedEpisode = new FeedEpisode()
feedEpisode.setFromAudiobookTrack(libraryItem, this.serverAddress, this.slug, audioTrack, this.meta)
this.episodes.push(feedEpisode)
})

View File

@ -314,18 +314,18 @@ class User {
}
createUpdateMediaProgress(libraryItem, updatePayload, episodeId = null) {
var itemProgress = this.mediaProgress.find(li => {
const itemProgress = this.mediaProgress.find(li => {
if (episodeId && li.episodeId !== episodeId) return false
return li.libraryItemId === libraryItem.id
})
if (!itemProgress) {
var newItemProgress = new MediaProgress()
const newItemProgress = new MediaProgress()
newItemProgress.setData(libraryItem.id, updatePayload, episodeId)
this.mediaProgress.push(newItemProgress)
return true
}
var wasUpdated = itemProgress.update(updatePayload)
const wasUpdated = itemProgress.update(updatePayload)
if (updatePayload.lastUpdate) itemProgress.lastUpdate = updatePayload.lastUpdate // For local to keep update times in sync
return wasUpdated

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) {