mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-12-15 08:19:37 +00:00
Add:Year in review banner for user stats page #2373
This commit is contained in:
parent
72fa6b8200
commit
0d644fe0c9
7 changed files with 583 additions and 99 deletions
|
|
@ -88,12 +88,53 @@ module.exports = {
|
|||
|
||||
const numAuthorsAdded = await this.getNumAuthorsAddedForYear(year)
|
||||
|
||||
let authorListeningMap = {}
|
||||
let narratorListeningMap = {}
|
||||
let genreListeningMap = {}
|
||||
|
||||
const listeningSessions = await this.getListeningSessionsForYear(year)
|
||||
let totalListeningTime = 0
|
||||
for (const listeningSession of listeningSessions) {
|
||||
totalListeningTime += (listeningSession.timeListening || 0)
|
||||
for (const ls of listeningSessions) {
|
||||
totalListeningTime += (ls.timeListening || 0)
|
||||
|
||||
const authors = ls.mediaMetadata.authors || []
|
||||
authors.forEach((au) => {
|
||||
if (!authorListeningMap[au.name]) authorListeningMap[au.name] = 0
|
||||
authorListeningMap[au.name] += (ls.timeListening || 0)
|
||||
})
|
||||
|
||||
const narrators = ls.mediaMetadata.narrators || []
|
||||
narrators.forEach((narrator) => {
|
||||
if (!narratorListeningMap[narrator]) narratorListeningMap[narrator] = 0
|
||||
narratorListeningMap[narrator] += (ls.timeListening || 0)
|
||||
})
|
||||
|
||||
// Filter out bad genres like "audiobook" and "audio book"
|
||||
const genres = (ls.mediaMetadata.genres || []).filter(g => !g.toLowerCase().includes('audiobook') && !g.toLowerCase().includes('audio book'))
|
||||
genres.forEach((genre) => {
|
||||
if (!genreListeningMap[genre]) genreListeningMap[genre] = 0
|
||||
genreListeningMap[genre] += (ls.timeListening || 0)
|
||||
})
|
||||
}
|
||||
|
||||
let topAuthors = null
|
||||
topAuthors = Object.keys(authorListeningMap).map(authorName => ({
|
||||
name: authorName,
|
||||
time: Math.round(authorListeningMap[authorName])
|
||||
})).sort((a, b) => b.time - a.time).slice(0, 3)
|
||||
|
||||
let topNarrators = null
|
||||
topNarrators = Object.keys(narratorListeningMap).map(narratorName => ({
|
||||
name: narratorName,
|
||||
time: Math.round(narratorListeningMap[narratorName])
|
||||
})).sort((a, b) => b.time - a.time).slice(0, 3)
|
||||
|
||||
let topGenres = null
|
||||
topGenres = Object.keys(genreListeningMap).map(genre => ({
|
||||
genre,
|
||||
time: Math.round(genreListeningMap[genre])
|
||||
})).sort((a, b) => b.time - a.time).slice(0, 3)
|
||||
|
||||
// Stats for total books, size and duration for everything added this year or earlier
|
||||
const [totalStatResultsRow] = await Database.sequelize.query(`SELECT SUM(li.size) AS totalSize, SUM(b.duration) AS totalDuration, COUNT(*) AS totalItems FROM libraryItems li, books b WHERE b.id = li.mediaId AND li.mediaType = 'book' AND li.createdAt < ":nextYear-01-01";`, {
|
||||
replacements: {
|
||||
|
|
@ -112,7 +153,10 @@ module.exports = {
|
|||
totalBooksSize: totalStatResults?.totalSize || 0,
|
||||
totalBooksDuration: totalStatResults?.totalDuration || 0,
|
||||
totalListeningTime,
|
||||
numBooks: totalStatResults?.totalItems || 0
|
||||
numBooks: totalStatResults?.totalItems || 0,
|
||||
topAuthors,
|
||||
topNarrators,
|
||||
topGenres
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,12 +52,14 @@ module.exports = {
|
|||
},
|
||||
include: {
|
||||
model: Database.bookModel,
|
||||
attributes: ['id', 'title', 'coverPath'],
|
||||
include: {
|
||||
model: Database.libraryItemModel,
|
||||
attributes: ['id', 'mediaId', 'mediaType']
|
||||
},
|
||||
required: true
|
||||
}
|
||||
},
|
||||
order: Database.sequelize.random()
|
||||
})
|
||||
return progresses
|
||||
},
|
||||
|
|
@ -69,6 +71,7 @@ module.exports = {
|
|||
async getStatsForYear(user, year) {
|
||||
const userId = user.id
|
||||
const listeningSessions = await this.getUserListeningSessionsForYear(userId, year)
|
||||
const bookProgressesFinished = await this.getBookMediaProgressFinishedForYear(userId, year)
|
||||
|
||||
let totalBookListeningTime = 0
|
||||
let totalPodcastListeningTime = 0
|
||||
|
|
@ -79,11 +82,33 @@ module.exports = {
|
|||
let narratorListeningMap = {}
|
||||
let monthListeningMap = {}
|
||||
let bookListeningMap = {}
|
||||
const booksWithCovers = []
|
||||
|
||||
const booksWithCovers = []
|
||||
const finishedBooksWithCovers = []
|
||||
|
||||
// Get finished book stats
|
||||
const numBooksFinished = bookProgressesFinished.length
|
||||
let longestAudiobookFinished = null
|
||||
for (const mediaProgress of bookProgressesFinished) {
|
||||
// Grab first 5 that have a cover
|
||||
if (mediaProgress.mediaItem?.coverPath && !finishedBooksWithCovers.includes(mediaProgress.mediaItem.libraryItem.id) && finishedBooksWithCovers.length < 5 && await fsExtra.pathExists(mediaProgress.mediaItem.coverPath)) {
|
||||
finishedBooksWithCovers.push(mediaProgress.mediaItem.libraryItem.id)
|
||||
}
|
||||
|
||||
if (mediaProgress.duration && (!longestAudiobookFinished?.duration || mediaProgress.duration > longestAudiobookFinished.duration)) {
|
||||
longestAudiobookFinished = {
|
||||
id: mediaProgress.mediaItem.id,
|
||||
title: mediaProgress.mediaItem.title,
|
||||
duration: Math.round(mediaProgress.duration),
|
||||
finishedAt: mediaProgress.finishedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get listening session stats
|
||||
for (const ls of listeningSessions) {
|
||||
// Grab first 25 that have a cover
|
||||
if (ls.mediaItem?.coverPath && !booksWithCovers.includes(ls.mediaItem.libraryItem.id) && booksWithCovers.length < 25 && await fsExtra.pathExists(ls.mediaItem.coverPath)) {
|
||||
if (ls.mediaItem?.coverPath && !booksWithCovers.includes(ls.mediaItem.libraryItem.id) && !finishedBooksWithCovers.includes(ls.mediaItem.libraryItem.id) && booksWithCovers.length < 25 && await fsExtra.pathExists(ls.mediaItem.coverPath)) {
|
||||
booksWithCovers.push(ls.mediaItem.libraryItem.id)
|
||||
}
|
||||
|
||||
|
|
@ -162,21 +187,6 @@ module.exports = {
|
|||
}
|
||||
}
|
||||
|
||||
const bookProgressesFinished = await this.getBookMediaProgressFinishedForYear(userId, year)
|
||||
|
||||
const numBooksFinished = bookProgressesFinished.length
|
||||
let longestAudiobookFinished = null
|
||||
bookProgressesFinished.forEach((mediaProgress) => {
|
||||
if (mediaProgress.duration && (!longestAudiobookFinished?.duration || mediaProgress.duration > longestAudiobookFinished.duration)) {
|
||||
longestAudiobookFinished = {
|
||||
id: mediaProgress.mediaItem.id,
|
||||
title: mediaProgress.mediaItem.title,
|
||||
duration: Math.round(mediaProgress.duration),
|
||||
finishedAt: mediaProgress.finishedAt
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
totalListeningSessions: listeningSessions.length,
|
||||
totalListeningTime,
|
||||
|
|
@ -189,7 +199,8 @@ module.exports = {
|
|||
numBooksFinished,
|
||||
numBooksListened: Object.keys(bookListeningMap).length,
|
||||
longestAudiobookFinished,
|
||||
booksWithCovers
|
||||
booksWithCovers,
|
||||
finishedBooksWithCovers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue