feat: add total duration and progress to series

This commit is contained in:
Varun Bajaj 2026-02-16 00:52:01 -05:00
parent b01facc034
commit 00f9940712
3 changed files with 48 additions and 1 deletions

View file

@ -44,6 +44,14 @@
<div class="w-6 h-6 rounded-full bg-black/30 flex items-center justify-center ml-3">
<span class="font-mono">{{ $formatNumber(numShowing) }}</span>
</div>
<div v-if="selectedSeries.totalDuration" class="flex items-center ml-4 text-sm opacity-70 pt-0.5 hidden sm:flex">
<template v-if="selectedSeries.progress && selectedSeries.progress.totalDurationListened">
<span>{{ $elapsedPretty(selectedSeries.progress.totalDurationListened).replace(' hr', 'h').replace(' min', 'm') }}</span>
<span class="mx-1.5">/</span>
</template>
<span>{{ $elapsedPretty(selectedSeries.totalDuration).replace(' hr', 'h').replace(' min', 'm') }}</span>
</div>
<div class="grow" />
<!-- RSS feed -->

View file

@ -789,10 +789,15 @@ class LibraryController {
const seriesJson = series.toOldJSON()
if (include.includes('progress')) {
const libraryItemsFinished = libraryItemsInSeries.filter((li) => !!req.user.getMediaProgress(li.media.id)?.isFinished)
const totalListened = libraryItemsInSeries.reduce((acc, li) => {
const p = req.user.getMediaProgress(li.media.id)
return acc + (p?.isFinished ? (li.media.duration || 0) : (p?.currentTime || 0))
}, 0)
seriesJson.progress = {
libraryItemIds: libraryItemsInSeries.map((li) => li.id),
libraryItemIdsFinished: libraryItemsFinished.map((li) => li.id),
isFinished: libraryItemsFinished.length >= libraryItemsInSeries.length
isFinished: libraryItemsFinished.length >= libraryItemsInSeries.length,
totalDurationListened: totalListened
}
}
@ -801,6 +806,9 @@ class LibraryController {
seriesJson.rssFeed = feedObj?.toOldJSONMinified() || null
}
// populate total duration (same unit as libraryItem.duration)
seriesJson.totalDuration = await Database.seriesModel.getTotalDurationById(series.id)
res.json(seriesJson)
}

View file

@ -65,6 +65,37 @@ class Series extends Model {
series.books = await series.getBooksExpandedWithLibraryItem()
return series
}
/**
*
* @param {string} seriesId
* @returns {Promise<number>} total duration (same unit as libraryItem.duration)
*/
static async getTotalDurationById(seriesId) {
const { book: bookModel, libraryItem: libraryItemModel, bookSeries: bookSeriesModel } = this.sequelize.models
// Sum durations on libraryItems for books that belong to the series.
// This relies on Sequelize associations existing between:
// libraryItem -> book, book -> bookSeries (or bookSeries as a through model).
const total = await libraryItemModel.sum('duration', {
where: { mediaType: 'book' },
include: [
{
model: bookModel,
attributes: [],
include: [
{
model: bookSeriesModel,
attributes: [],
where: { seriesId }
}
]
}
]
})
return Number(total) || 0
}
/**
*