Merge pull request #2920 from rasmuslos/master

Add item sessions endpoint
This commit is contained in:
advplyr 2024-05-23 16:39:40 -05:00 committed by GitHub
commit d99a77837b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 69 additions and 22 deletions

View File

@ -6,7 +6,7 @@ const { toNumber } = require('../utils/index')
const userStats = require('../utils/queries/userStats') const userStats = require('../utils/queries/userStats')
class MeController { class MeController {
constructor() { } constructor() {}
getCurrentUser(req, res) { getCurrentUser(req, res) {
res.json(req.user.toJSONForBrowser()) res.json(req.user.toJSONForBrowser())
@ -33,6 +33,43 @@ class MeController {
res.json(payload) res.json(payload)
} }
/**
* GET: /api/me/item/listening-sessions/:libraryItemId/:episodeId
*
* @this import('../routers/ApiRouter')
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async getItemListeningSessions(req, res) {
const libraryItem = await Database.libraryItemModel.findByPk(req.params.libraryItemId)
const episode = await Database.podcastEpisodeModel.findByPk(req.params.episodeId)
if (!libraryItem || (libraryItem.mediaType === 'podcast' && !episode)) {
Logger.error(`[MeController] Media item not found for library item id "${req.params.libraryItemId}"`)
return res.sendStatus(404)
}
const mediaItemId = episode?.id || libraryItem.mediaId
let listeningSessions = await this.getUserItemListeningSessionsHelper(req.user.id, mediaItemId)
const itemsPerPage = toNumber(req.query.itemsPerPage, 10) || 10
const page = toNumber(req.query.page, 0)
const start = page * itemsPerPage
const sessions = listeningSessions.slice(start, start + itemsPerPage)
const payload = {
total: listeningSessions.length,
numPages: Math.ceil(listeningSessions.length / itemsPerPage),
page,
itemsPerPage,
sessions
}
res.json(payload)
}
// GET: api/me/listening-stats // GET: api/me/listening-stats
async getListeningStats(req, res) { async getListeningStats(req, res) {
const listeningStats = await this.getUserListeningStatsHelpers(req.user.id) const listeningStats = await this.getUserListeningStatsHelpers(req.user.id)
@ -80,7 +117,7 @@ class MeController {
if (!libraryItem) { if (!libraryItem) {
return res.status(404).send('Item not found') return res.status(404).send('Item not found')
} }
if (!libraryItem.media.episodes.find(ep => ep.id === episodeId)) { if (!libraryItem.media.episodes.find((ep) => ep.id === episodeId)) {
Logger.error(`[MeController] removeEpisode episode ${episodeId} not found for item ${libraryItem.id}`) Logger.error(`[MeController] removeEpisode episode ${episodeId} not found for item ${libraryItem.id}`)
return res.status(404).send('Episode not found') return res.status(404).send('Episode not found')
} }
@ -123,7 +160,7 @@ class MeController {
// POST: api/me/item/:id/bookmark // POST: api/me/item/:id/bookmark
async createBookmark(req, res) { async createBookmark(req, res) {
if (!await Database.libraryItemModel.checkExistsById(req.params.id)) return res.sendStatus(404) if (!(await Database.libraryItemModel.checkExistsById(req.params.id))) return res.sendStatus(404)
const { time, title } = req.body const { time, title } = req.body
const bookmark = req.user.createBookmark(req.params.id, time, title) const bookmark = req.user.createBookmark(req.params.id, time, title)
@ -134,7 +171,7 @@ class MeController {
// PATCH: api/me/item/:id/bookmark // PATCH: api/me/item/:id/bookmark
async updateBookmark(req, res) { async updateBookmark(req, res) {
if (!await Database.libraryItemModel.checkExistsById(req.params.id)) return res.sendStatus(404) if (!(await Database.libraryItemModel.checkExistsById(req.params.id))) return res.sendStatus(404)
const { time, title } = req.body const { time, title } = req.body
if (!req.user.findBookmark(req.params.id, time)) { if (!req.user.findBookmark(req.params.id, time)) {
@ -152,7 +189,7 @@ class MeController {
// DELETE: api/me/item/:id/bookmark/:time // DELETE: api/me/item/:id/bookmark/:time
async removeBookmark(req, res) { async removeBookmark(req, res) {
if (!await Database.libraryItemModel.checkExistsById(req.params.id)) return res.sendStatus(404) if (!(await Database.libraryItemModel.checkExistsById(req.params.id))) return res.sendStatus(404)
const time = Number(req.params.time) const time = Number(req.params.time)
if (isNaN(time)) return res.sendStatus(500) if (isNaN(time)) return res.sendStatus(500)
@ -254,11 +291,10 @@ class MeController {
// TODO: More efficient to do this in a single query // TODO: More efficient to do this in a single query
for (const mediaProgress of req.user.mediaProgress) { for (const mediaProgress of req.user.mediaProgress) {
if (!mediaProgress.isFinished && (mediaProgress.progress > 0 || mediaProgress.ebookProgress > 0)) { if (!mediaProgress.isFinished && (mediaProgress.progress > 0 || mediaProgress.ebookProgress > 0)) {
const libraryItem = await Database.libraryItemModel.getOldById(mediaProgress.libraryItemId) const libraryItem = await Database.libraryItemModel.getOldById(mediaProgress.libraryItemId)
if (libraryItem) { if (libraryItem) {
if (mediaProgress.episodeId && libraryItem.mediaType === 'podcast') { if (mediaProgress.episodeId && libraryItem.mediaType === 'podcast') {
const episode = libraryItem.media.episodes.find(ep => ep.id === mediaProgress.episodeId) const episode = libraryItem.media.episodes.find((ep) => ep.id === mediaProgress.episodeId)
if (episode) { if (episode) {
const libraryItemWithEpisode = { const libraryItemWithEpisode = {
...libraryItem.toJSONMinified(), ...libraryItem.toJSONMinified(),
@ -277,7 +313,9 @@ class MeController {
} }
} }
itemsInProgress = sort(itemsInProgress).desc(li => li.progressLastUpdate).slice(0, limit) itemsInProgress = sort(itemsInProgress)
.desc((li) => li.progressLastUpdate)
.slice(0, limit)
res.json({ res.json({
libraryItems: itemsInProgress libraryItems: itemsInProgress
}) })
@ -317,19 +355,22 @@ class MeController {
// GET: api/me/progress/:id/remove-from-continue-listening // GET: api/me/progress/:id/remove-from-continue-listening
async removeItemFromContinueListening(req, res) { async removeItemFromContinueListening(req, res) {
const mediaProgress = req.user.mediaProgress.find(mp => mp.id === req.params.id) const mediaProgress = req.user.mediaProgress.find((mp) => mp.id === req.params.id)
if (!mediaProgress) { if (!mediaProgress) {
return res.sendStatus(404) return res.sendStatus(404)
} }
const hasUpdated = req.user.removeProgressFromContinueListening(req.params.id) const hasUpdated = req.user.removeProgressFromContinueListening(req.params.id)
if (hasUpdated) { if (hasUpdated) {
await Database.mediaProgressModel.update({ await Database.mediaProgressModel.update(
{
hideFromContinueListening: true hideFromContinueListening: true
}, { },
{
where: { where: {
id: mediaProgress.id id: mediaProgress.id
} }
}) }
)
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser()) SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
} }
res.json(req.user.toJSONForBrowser()) res.json(req.user.toJSONForBrowser())

View File

@ -166,6 +166,7 @@ class ApiRouter {
// //
this.router.get('/me', MeController.getCurrentUser.bind(this)) this.router.get('/me', MeController.getCurrentUser.bind(this))
this.router.get('/me/listening-sessions', MeController.getListeningSessions.bind(this)) this.router.get('/me/listening-sessions', MeController.getListeningSessions.bind(this))
this.router.get('/me/item/listening-sessions/:libraryItemId/:episodeId?', MeController.getItemListeningSessions.bind(this))
this.router.get('/me/listening-stats', MeController.getListeningStats.bind(this)) this.router.get('/me/listening-stats', MeController.getListeningStats.bind(this))
this.router.get('/me/progress/:id/remove-from-continue-listening', MeController.removeItemFromContinueListening.bind(this)) this.router.get('/me/progress/:id/remove-from-continue-listening', MeController.removeItemFromContinueListening.bind(this))
this.router.get('/me/progress/:id/:episodeId?', MeController.getMediaProgress.bind(this)) this.router.get('/me/progress/:id/:episodeId?', MeController.getMediaProgress.bind(this))
@ -474,6 +475,11 @@ class ApiRouter {
return userSessions.sort((a, b) => b.updatedAt - a.updatedAt) return userSessions.sort((a, b) => b.updatedAt - a.updatedAt)
} }
async getUserItemListeningSessionsHelper(userId, mediaItemId) {
const userSessions = await Database.getPlaybackSessions({ userId, mediaItemId })
return userSessions.sort((a, b) => b.updatedAt - a.updatedAt)
}
async getUserListeningStatsHelpers(userId) { async getUserListeningStatsHelpers(userId) {
const today = date.format(new Date(), 'YYYY-MM-DD') const today = date.format(new Date(), 'YYYY-MM-DD')