mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-03-06 16:09:46 +00:00
Merge 1204936bf1 into 1d0b7e383a
This commit is contained in:
commit
972f69f273
3 changed files with 49 additions and 2 deletions
|
|
@ -44,6 +44,14 @@
|
||||||
<div class="w-6 h-6 rounded-full bg-black/30 flex items-center justify-center ml-3">
|
<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>
|
<span class="font-mono">{{ $formatNumber(numShowing) }}</span>
|
||||||
</div>
|
</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" />
|
<div class="grow" />
|
||||||
|
|
||||||
<!-- RSS feed -->
|
<!-- RSS feed -->
|
||||||
|
|
|
||||||
|
|
@ -770,7 +770,7 @@ class LibraryController {
|
||||||
*
|
*
|
||||||
* Optional includes (e.g. `?include=rssfeed,progress`)
|
* Optional includes (e.g. `?include=rssfeed,progress`)
|
||||||
* rssfeed: adds `rssFeed` to series object if a feed is open
|
* rssfeed: adds `rssFeed` to series object if a feed is open
|
||||||
* progress: adds `progress` to series object with { libraryItemIds:Array<llid>, libraryItemIdsFinished:Array<llid>, isFinished:boolean }
|
* progress: adds `progress` to series object with { libraryItemIds:Array<llid>, libraryItemIdsFinished:Array<llid>, isFinished:boolean, totalDurationListened: number }
|
||||||
*
|
*
|
||||||
* @param {LibraryControllerRequest} req
|
* @param {LibraryControllerRequest} req
|
||||||
* @param {Response} res - Series
|
* @param {Response} res - Series
|
||||||
|
|
@ -789,10 +789,15 @@ class LibraryController {
|
||||||
const seriesJson = series.toOldJSON()
|
const seriesJson = series.toOldJSON()
|
||||||
if (include.includes('progress')) {
|
if (include.includes('progress')) {
|
||||||
const libraryItemsFinished = libraryItemsInSeries.filter((li) => !!req.user.getMediaProgress(li.media.id)?.isFinished)
|
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 = {
|
seriesJson.progress = {
|
||||||
libraryItemIds: libraryItemsInSeries.map((li) => li.id),
|
libraryItemIds: libraryItemsInSeries.map((li) => li.id),
|
||||||
libraryItemIdsFinished: libraryItemsFinished.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
|
seriesJson.rssFeed = feedObj?.toOldJSONMinified() || null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// populate total duration (same unit as libraryItem.duration)
|
||||||
|
seriesJson.totalDuration = await Database.seriesModel.getTotalDurationById(series.id)
|
||||||
|
|
||||||
res.json(seriesJson)
|
res.json(seriesJson)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,37 @@ class Series extends Model {
|
||||||
series.books = await series.getBooksExpandedWithLibraryItem()
|
series.books = await series.getBooksExpandedWithLibraryItem()
|
||||||
return series
|
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
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue