mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-12-20 19:06:06 +01:00
Update:API endpoint for syncing multiple local sessions. New API endpoint to get current user. Deprecate /me/sync-local-progress endpoint
This commit is contained in:
parent
debf0f495d
commit
f9e6655359
@ -6,6 +6,10 @@ const { isObject, toNumber } = require('../utils/index')
|
||||
class MeController {
|
||||
constructor() { }
|
||||
|
||||
getCurrentUser(req, res) {
|
||||
res.json(req.user.toJSONForBrowser())
|
||||
}
|
||||
|
||||
// GET: api/me/listening-sessions
|
||||
async getListeningSessions(req, res) {
|
||||
var listeningSessions = await this.getUserListeningSessionsHelper(req.user.id)
|
||||
@ -184,6 +188,7 @@ class MeController {
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Deprecated. Removed from Android. Only used in iOS app now.
|
||||
// POST: api/me/sync-local-progress
|
||||
async syncLocalMediaProgress(req, res) {
|
||||
if (!req.body.localMediaProgress) {
|
||||
|
@ -75,6 +75,11 @@ class SessionController {
|
||||
this.playbackSessionManager.syncLocalSessionRequest(req.user, req.body, res)
|
||||
}
|
||||
|
||||
// POST: api/session/local-all
|
||||
syncLocalSessions(req, res) {
|
||||
this.playbackSessionManager.syncLocalSessionsRequest(req, res)
|
||||
}
|
||||
|
||||
openSessionMiddleware(req, res, next) {
|
||||
var playbackSession = this.playbackSessionManager.getSession(req.params.id)
|
||||
if (!playbackSession) return res.sendStatus(404)
|
||||
|
@ -21,7 +21,6 @@ class PlaybackSessionManager {
|
||||
this.StreamsPath = Path.join(global.MetadataPath, 'streams')
|
||||
|
||||
this.sessions = []
|
||||
this.localSessionLock = {}
|
||||
}
|
||||
|
||||
getSession(sessionId) {
|
||||
@ -61,18 +60,84 @@ class PlaybackSessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
async syncLocalSessionRequest(user, sessionJson, res) {
|
||||
if (this.localSessionLock[sessionJson.id]) {
|
||||
Logger.debug(`[PlaybackSessionManager] syncLocalSessionRequest: Local session is locked and already syncing`)
|
||||
return res.status(500).send('Local session is locked and already syncing')
|
||||
async syncLocalSessionsRequest(req, res) {
|
||||
const user = req.user
|
||||
const sessions = req.body.sessions || []
|
||||
|
||||
const syncResults = []
|
||||
for (const sessionJson of sessions) {
|
||||
Logger.info(`[PlaybackSessionManager] Syncing local session "${sessionJson.displayTitle}" (${sessionJson.id})`)
|
||||
const result = await this.syncLocalSession(user, sessionJson)
|
||||
syncResults.push(result)
|
||||
}
|
||||
|
||||
res.json({
|
||||
results: syncResults
|
||||
})
|
||||
}
|
||||
|
||||
async syncLocalSession(user, sessionJson) {
|
||||
const libraryItem = this.db.getLibraryItem(sessionJson.libraryItemId)
|
||||
if (!libraryItem) {
|
||||
Logger.error(`[PlaybackSessionManager] syncLocalSessionRequest: Library item not found for session "${sessionJson.libraryItemId}"`)
|
||||
return res.status(500).send('Library item not found')
|
||||
const episode = (sessionJson.episodeId && libraryItem && libraryItem.isPodcast) ? libraryItem.media.getEpisode(sessionJson.episodeId) : null
|
||||
if (!libraryItem || (libraryItem.isPodcast && !episode)) {
|
||||
Logger.error(`[PlaybackSessionManager] syncLocalSession: Media item not found for session "${sessionJson.displayTitle}" (${sessionJson.id})`)
|
||||
return {
|
||||
id: sessionJson.id,
|
||||
success: false,
|
||||
error: 'Media item not found'
|
||||
}
|
||||
}
|
||||
|
||||
let session = await this.db.getPlaybackSession(sessionJson.id)
|
||||
if (!session) {
|
||||
// New session from local
|
||||
session = new PlaybackSession(sessionJson)
|
||||
Logger.debug(`[PlaybackSessionManager] Inserting new session for "${session.displayTitle}" (${session.id})`)
|
||||
await this.db.insertEntity('session', session)
|
||||
} else {
|
||||
session.currentTime = sessionJson.currentTime
|
||||
session.timeListening = sessionJson.timeListening
|
||||
session.updatedAt = sessionJson.updatedAt
|
||||
session.date = date.format(new Date(), 'YYYY-MM-DD')
|
||||
session.dayOfWeek = date.format(new Date(), 'dddd')
|
||||
|
||||
Logger.debug(`[PlaybackSessionManager] Updated session for "${session.displayTitle}" (${session.id})`)
|
||||
await this.db.updateEntity('session', session)
|
||||
}
|
||||
|
||||
const result = {
|
||||
id: session.id,
|
||||
success: true,
|
||||
progressSynced: false
|
||||
}
|
||||
|
||||
const userProgressForItem = user.getMediaProgress(session.libraryItemId, session.episodeId)
|
||||
if (userProgressForItem) {
|
||||
if (userProgressForItem.lastUpdate > session.updatedAt) {
|
||||
Logger.debug(`[PlaybackSessionManager] Not updating progress for "${session.displayTitle}" because it has been updated more recently`)
|
||||
} else {
|
||||
Logger.debug(`[PlaybackSessionManager] Updating progress for "${session.displayTitle}" with current time ${session.currentTime} (previously ${userProgressForItem.currentTime})`)
|
||||
result.progressSynced = user.createUpdateMediaProgress(libraryItem, session.mediaProgressObject, session.episodeId)
|
||||
}
|
||||
} else {
|
||||
Logger.debug(`[PlaybackSessionManager] Creating new media progress for media item "${session.displayTitle}"`)
|
||||
result.progressSynced = user.createUpdateMediaProgress(libraryItem, session.mediaProgressObject, session.episodeId)
|
||||
}
|
||||
|
||||
// Update user and emit socket event
|
||||
if (result.progressSynced) {
|
||||
await this.db.updateEntity('user', user)
|
||||
const itemProgress = user.getMediaProgress(session.libraryItemId, session.episodeId)
|
||||
SocketAuthority.clientEmitter(user.id, 'user_item_progress_updated', {
|
||||
id: itemProgress.id,
|
||||
data: itemProgress.toJSON()
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async syncLocalSessionRequest(user, sessionJson, res) {
|
||||
// If server session is open for this same media item then close it
|
||||
const userSessionForThisItem = this.sessions.find(playbackSession => {
|
||||
if (playbackSession.userId !== user.id) return false
|
||||
@ -84,44 +149,14 @@ class PlaybackSessionManager {
|
||||
await this.closeSession(user, userSessionForThisItem, null)
|
||||
}
|
||||
|
||||
this.localSessionLock[sessionJson.id] = true // Lock local session
|
||||
|
||||
let session = await this.db.getPlaybackSession(sessionJson.id)
|
||||
if (!session) {
|
||||
// New session from local
|
||||
session = new PlaybackSession(sessionJson)
|
||||
await this.db.insertEntity('session', session)
|
||||
// Sync
|
||||
const result = await this.syncLocalSession(user, sessionJson)
|
||||
if (result.error) {
|
||||
res.status(500).send(result.error)
|
||||
} else {
|
||||
session.currentTime = sessionJson.currentTime
|
||||
session.timeListening = sessionJson.timeListening
|
||||
session.updatedAt = sessionJson.updatedAt
|
||||
session.date = date.format(new Date(), 'YYYY-MM-DD')
|
||||
session.dayOfWeek = date.format(new Date(), 'dddd')
|
||||
await this.db.updateEntity('session', session)
|
||||
}
|
||||
|
||||
session.currentTime = sessionJson.currentTime
|
||||
|
||||
const itemProgressUpdate = {
|
||||
duration: session.duration,
|
||||
currentTime: session.currentTime,
|
||||
progress: session.progress,
|
||||
lastUpdate: session.updatedAt // Keep media progress update times the same as local
|
||||
}
|
||||
const wasUpdated = user.createUpdateMediaProgress(libraryItem, itemProgressUpdate, session.episodeId)
|
||||
if (wasUpdated) {
|
||||
await this.db.updateEntity('user', user)
|
||||
const itemProgress = user.getMediaProgress(session.libraryItemId, session.episodeId)
|
||||
SocketAuthority.clientEmitter(user.id, 'user_item_progress_updated', {
|
||||
id: itemProgress.id,
|
||||
data: itemProgress.toJSON()
|
||||
})
|
||||
}
|
||||
|
||||
delete this.localSessionLock[sessionJson.id] // Unlock local session
|
||||
|
||||
res.sendStatus(200)
|
||||
}
|
||||
}
|
||||
|
||||
async closeSessionRequest(user, session, syncData, res) {
|
||||
await this.closeSession(user, session, syncData)
|
||||
|
@ -141,6 +141,11 @@ class PlaybackSession {
|
||||
this.updatedAt = session.updatedAt || null
|
||||
}
|
||||
|
||||
get mediaItemId() {
|
||||
if (this.episodeId) return `${this.libraryItemId}-${this.episodeId}`
|
||||
return this.libraryItemId
|
||||
}
|
||||
|
||||
get progress() { // Value between 0 and 1
|
||||
if (!this.duration) return 0
|
||||
return Math.max(0, Math.min(this.currentTime / this.duration, 1))
|
||||
@ -151,6 +156,15 @@ class PlaybackSession {
|
||||
return this.deviceInfo.deviceDescription
|
||||
}
|
||||
|
||||
get mediaProgressObject() {
|
||||
return {
|
||||
duration: this.duration,
|
||||
currentTime: this.currentTime,
|
||||
progress: this.progress,
|
||||
lastUpdate: this.updatedAt
|
||||
}
|
||||
}
|
||||
|
||||
setData(libraryItem, user, mediaPlayer, deviceInfo, startTime, episodeId = null) {
|
||||
this.id = getId('play')
|
||||
this.userId = user.id
|
||||
|
@ -162,6 +162,7 @@ class ApiRouter {
|
||||
//
|
||||
// Current User Routes (Me)
|
||||
//
|
||||
this.router.get('/me', MeController.getCurrentUser.bind(this))
|
||||
this.router.get('/me/listening-sessions', MeController.getListeningSessions.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))
|
||||
@ -174,8 +175,8 @@ class ApiRouter {
|
||||
this.router.patch('/me/item/:id/bookmark', MeController.updateBookmark.bind(this))
|
||||
this.router.delete('/me/item/:id/bookmark/:time', MeController.removeBookmark.bind(this))
|
||||
this.router.patch('/me/password', MeController.updatePassword.bind(this))
|
||||
this.router.patch('/me/settings', MeController.updateSettings.bind(this)) // TODO: Remove after mobile release v0.9.61-beta
|
||||
this.router.post('/me/sync-local-progress', MeController.syncLocalMediaProgress.bind(this))
|
||||
this.router.patch('/me/settings', MeController.updateSettings.bind(this)) // TODO: Deprecated. Remove after mobile release v0.9.61-beta
|
||||
this.router.post('/me/sync-local-progress', MeController.syncLocalMediaProgress.bind(this)) // TODO: Deprecated. Removed from Android. Only used in iOS app now.
|
||||
this.router.get('/me/items-in-progress', MeController.getAllLibraryItemsInProgress.bind(this))
|
||||
this.router.get('/me/series/:id/remove-from-continue-listening', MeController.removeSeriesFromContinueListening.bind(this))
|
||||
this.router.get('/me/series/:id/readd-to-continue-listening', MeController.readdSeriesFromContinueListening.bind(this))
|
||||
@ -215,11 +216,12 @@ class ApiRouter {
|
||||
//
|
||||
this.router.get('/sessions', SessionController.getAllWithUserData.bind(this))
|
||||
this.router.delete('/sessions/:id', SessionController.middleware.bind(this), SessionController.delete.bind(this))
|
||||
this.router.post('/session/local', SessionController.syncLocal.bind(this))
|
||||
this.router.post('/session/local-all', SessionController.syncLocalSessions.bind(this))
|
||||
// TODO: Update these endpoints because they are only for open playback sessions
|
||||
this.router.get('/session/:id', SessionController.openSessionMiddleware.bind(this), SessionController.getOpenSession.bind(this))
|
||||
this.router.post('/session/:id/sync', SessionController.openSessionMiddleware.bind(this), SessionController.sync.bind(this))
|
||||
this.router.post('/session/:id/close', SessionController.openSessionMiddleware.bind(this), SessionController.close.bind(this))
|
||||
this.router.post('/session/local', SessionController.syncLocal.bind(this))
|
||||
|
||||
//
|
||||
// Podcast Routes
|
||||
|
Loading…
Reference in New Issue
Block a user