mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-12-20 19:06:06 +01:00
Update get User API endpoint to load media progress from db
This commit is contained in:
parent
1ebe8a6f4c
commit
361732a463
@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
<div v-if="imageFailed" class="absolute top-0 left-0 right-0 bottom-0 w-full h-full bg-red-100" :style="{ padding: placeholderCoverPadding + 'rem' }">
|
<div v-if="imageFailed" class="absolute top-0 left-0 right-0 bottom-0 w-full h-full bg-red-100" :style="{ padding: placeholderCoverPadding + 'rem' }">
|
||||||
<div class="w-full h-full border-2 border-error flex flex-col items-center justify-center">
|
<div class="w-full h-full border-2 border-error flex flex-col items-center justify-center">
|
||||||
<img src="/Logo.png" class="mb-2" :style="{ height: 64 * sizeMultiplier + 'px' }" />
|
<img v-if="width > 100" src="/Logo.png" class="mb-2" :style="{ height: 40 * sizeMultiplier + 'px' }" />
|
||||||
<p class="text-center text-error" :style="{ fontSize: sizeMultiplier + 'rem' }">Invalid Cover</p>
|
<p class="text-center text-error" :style="{ fontSize: invalidCoverFontSize + 'rem' }">Invalid Cover</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -58,6 +58,9 @@ export default {
|
|||||||
sizeMultiplier() {
|
sizeMultiplier() {
|
||||||
return this.width / 120
|
return this.width / 120
|
||||||
},
|
},
|
||||||
|
invalidCoverFontSize() {
|
||||||
|
return Math.max(this.sizeMultiplier * 0.8, 0.5)
|
||||||
|
},
|
||||||
placeholderCoverPadding() {
|
placeholderCoverPadding() {
|
||||||
return 0.8 * this.sizeMultiplier
|
return 0.8 * this.sizeMultiplier
|
||||||
},
|
},
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
<div class="py-2">
|
<div class="py-2">
|
||||||
<h1 class="text-lg mb-2 text-white text-opacity-90 px-2 sm:px-0">{{ $strings.HeaderSavedMediaProgress }}</h1>
|
<h1 class="text-lg mb-2 text-white text-opacity-90 px-2 sm:px-0">{{ $strings.HeaderSavedMediaProgress }}</h1>
|
||||||
|
|
||||||
<table v-if="mediaProgressWithMedia.length" class="userAudiobooksTable">
|
<table v-if="mediaProgress.length" class="userAudiobooksTable">
|
||||||
<tr class="bg-primary bg-opacity-40">
|
<tr class="bg-primary bg-opacity-40">
|
||||||
<th class="w-16 text-left">{{ $strings.LabelItem }}</th>
|
<th class="w-16 text-left">{{ $strings.LabelItem }}</th>
|
||||||
<th class="text-left"></th>
|
<th class="text-left"></th>
|
||||||
@ -55,19 +55,14 @@
|
|||||||
<th class="w-40 hidden sm:table-cell">{{ $strings.LabelStartedAt }}</th>
|
<th class="w-40 hidden sm:table-cell">{{ $strings.LabelStartedAt }}</th>
|
||||||
<th class="w-40 hidden sm:table-cell">{{ $strings.LabelLastUpdate }}</th>
|
<th class="w-40 hidden sm:table-cell">{{ $strings.LabelLastUpdate }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-for="item in mediaProgressWithMedia" :key="item.id" :class="!item.isFinished ? '' : 'isFinished'">
|
<tr v-for="item in mediaProgress" :key="item.id" :class="!item.isFinished ? '' : 'isFinished'">
|
||||||
<td>
|
<td>
|
||||||
<covers-book-cover :width="50" :library-item="item" :book-cover-aspect-ratio="bookCoverAspectRatio" />
|
<covers-preview-cover v-if="item.coverPath" :width="50" :src="$store.getters['globals/getLibraryItemCoverSrcById'](item.libraryItemId, item.mediaUpdatedAt)" :book-cover-aspect-ratio="bookCoverAspectRatio" :show-resolution="false" />
|
||||||
|
<div v-else class="bg-primary flex items-center justify-center text-center text-xs text-gray-400 p-1" :style="{ width: '50px', height: 50 * bookCoverAspectRatio + 'px' }">No Cover</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<template v-if="item.media && item.media.metadata && item.episode">
|
<p>{{ item.displayTitle || 'Unknown' }}</p>
|
||||||
<p>{{ item.episode.title || 'Unknown' }}</p>
|
<p v-if="item.displaySubtitle" class="text-white text-opacity-50 text-sm font-sans">{{ item.displaySubtitle }}</p>
|
||||||
<p class="text-white text-opacity-50 text-sm font-sans">{{ item.media.metadata.title }}</p>
|
|
||||||
</template>
|
|
||||||
<template v-else-if="item.media && item.media.metadata">
|
|
||||||
<p>{{ item.media.metadata.title || 'Unknown' }}</p>
|
|
||||||
<p v-if="item.media.metadata.authorName" class="text-white text-opacity-50 text-sm font-sans">by {{ item.media.metadata.authorName }}</p>
|
|
||||||
</template>
|
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<p class="text-sm">{{ Math.floor(item.progress * 100) }}%</p>
|
<p class="text-sm">{{ Math.floor(item.progress * 100) }}%</p>
|
||||||
@ -124,9 +119,6 @@ export default {
|
|||||||
mediaProgress() {
|
mediaProgress() {
|
||||||
return this.user.mediaProgress.sort((a, b) => b.lastUpdate - a.lastUpdate)
|
return this.user.mediaProgress.sort((a, b) => b.lastUpdate - a.lastUpdate)
|
||||||
},
|
},
|
||||||
mediaProgressWithMedia() {
|
|
||||||
return this.mediaProgress.filter((mp) => mp.media)
|
|
||||||
},
|
|
||||||
totalListeningTime() {
|
totalListeningTime() {
|
||||||
return this.listeningStats.totalTime || 0
|
return this.listeningStats.totalTime || 0
|
||||||
},
|
},
|
||||||
|
@ -32,13 +32,60 @@ class UserController {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET: /api/users/:id
|
||||||
|
* Get a single user toJSONForBrowser
|
||||||
|
* Media progress items include: `displayTitle`, `displaySubtitle` (for podcasts), `coverPath` and `mediaUpdatedAt`
|
||||||
|
*
|
||||||
|
* @param {import("express").Request} req
|
||||||
|
* @param {import("express").Response} res
|
||||||
|
*/
|
||||||
async findOne(req, res) {
|
async findOne(req, res) {
|
||||||
if (!req.user.isAdminOrUp) {
|
if (!req.user.isAdminOrUp) {
|
||||||
Logger.error('User other than admin attempting to get user', req.user)
|
Logger.error('User other than admin attempting to get user', req.user)
|
||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(this.userJsonWithItemProgressDetails(req.reqUser, !req.user.isRoot))
|
// Get user media progress with associated mediaItem
|
||||||
|
const mediaProgresses = await Database.models.mediaProgress.findAll({
|
||||||
|
where: {
|
||||||
|
userId: req.reqUser.id
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: Database.models.book,
|
||||||
|
attributes: ['id', 'title', 'coverPath', 'updatedAt']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: Database.models.podcastEpisode,
|
||||||
|
attributes: ['id', 'title'],
|
||||||
|
include: {
|
||||||
|
model: Database.models.podcast,
|
||||||
|
attributes: ['id', 'title', 'coverPath', 'updatedAt']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const oldMediaProgresses = mediaProgresses.map(mp => {
|
||||||
|
const oldMediaProgress = mp.getOldMediaProgress()
|
||||||
|
oldMediaProgress.displayTitle = mp.mediaItem?.title
|
||||||
|
if (mp.mediaItem?.podcast) {
|
||||||
|
oldMediaProgress.displaySubtitle = mp.mediaItem.podcast?.title
|
||||||
|
oldMediaProgress.coverPath = mp.mediaItem.podcast?.coverPath
|
||||||
|
oldMediaProgress.mediaUpdatedAt = mp.mediaItem.podcast?.updatedAt
|
||||||
|
} else if (mp.mediaItem) {
|
||||||
|
oldMediaProgress.coverPath = mp.mediaItem.coverPath
|
||||||
|
oldMediaProgress.mediaUpdatedAt = mp.mediaItem.updatedAt
|
||||||
|
}
|
||||||
|
return oldMediaProgress
|
||||||
|
})
|
||||||
|
|
||||||
|
const userJson = req.reqUser.toJSONForBrowser(!req.user.isRoot)
|
||||||
|
|
||||||
|
userJson.mediaProgress = oldMediaProgresses
|
||||||
|
|
||||||
|
res.json(userJson)
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(req, res) {
|
async create(req, res) {
|
||||||
|
@ -352,34 +352,6 @@ class ApiRouter {
|
|||||||
//
|
//
|
||||||
// Helper Methods
|
// Helper Methods
|
||||||
//
|
//
|
||||||
userJsonWithItemProgressDetails(user, hideRootToken = false) {
|
|
||||||
const json = user.toJSONForBrowser(hideRootToken)
|
|
||||||
|
|
||||||
json.mediaProgress = json.mediaProgress.map(lip => {
|
|
||||||
const libraryItem = Database.libraryItems.find(li => li.id === lip.libraryItemId)
|
|
||||||
if (!libraryItem) {
|
|
||||||
Logger.warn('[ApiRouter] Library item not found for users progress ' + lip.libraryItemId)
|
|
||||||
lip.media = null
|
|
||||||
} else {
|
|
||||||
if (lip.episodeId) {
|
|
||||||
const episode = libraryItem.mediaType === 'podcast' ? libraryItem.media.getEpisode(lip.episodeId) : null
|
|
||||||
if (!episode) {
|
|
||||||
Logger.warn(`[ApiRouter] Episode ${lip.episodeId} not found for user media progress, podcast: ${libraryItem.media.metadata.title}`)
|
|
||||||
lip.media = null
|
|
||||||
} else {
|
|
||||||
lip.media = libraryItem.media.toJSONExpanded()
|
|
||||||
lip.episode = episode.toJSON()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lip.media = libraryItem.media.toJSONExpanded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lip
|
|
||||||
}).filter(lip => !!lip)
|
|
||||||
|
|
||||||
return json
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove library item and associated entities
|
* Remove library item and associated entities
|
||||||
* @param {string} mediaType
|
* @param {string} mediaType
|
||||||
|
Loading…
Reference in New Issue
Block a user