mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Merge pull request #4263 from advplyr/new_session_track_endpoint
Add new api endpoint for direct playing audio files using session id
This commit is contained in:
		
						commit
						1afb8840db
					
				@ -1,5 +1,5 @@
 | 
			
		||||
export default class AudioTrack {
 | 
			
		||||
  constructor(track, userToken, routerBasePath) {
 | 
			
		||||
  constructor(track, sessionId, userToken, routerBasePath) {
 | 
			
		||||
    this.index = track.index || 0
 | 
			
		||||
    this.startOffset = track.startOffset || 0 // Total time of all previous tracks
 | 
			
		||||
    this.duration = track.duration || 0
 | 
			
		||||
@ -8,28 +8,29 @@ export default class AudioTrack {
 | 
			
		||||
    this.mimeType = track.mimeType
 | 
			
		||||
    this.metadata = track.metadata || {}
 | 
			
		||||
 | 
			
		||||
    this.userToken = userToken
 | 
			
		||||
    this.sessionId = sessionId
 | 
			
		||||
    this.routerBasePath = routerBasePath || ''
 | 
			
		||||
    if (this.contentUrl?.startsWith('/hls')) {
 | 
			
		||||
      this.sessionTrackUrl = `${this.contentUrl}?token=${userToken}`
 | 
			
		||||
    } else {
 | 
			
		||||
      this.sessionTrackUrl = `/public/session/${sessionId}/track/${this.index}`
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Used for CastPlayer
 | 
			
		||||
   */
 | 
			
		||||
  get fullContentUrl() {
 | 
			
		||||
    if (!this.contentUrl || this.contentUrl.startsWith('http')) return this.contentUrl
 | 
			
		||||
 | 
			
		||||
    if (process.env.NODE_ENV === 'development') {
 | 
			
		||||
      return `${process.env.serverUrl}${this.contentUrl}?token=${this.userToken}`
 | 
			
		||||
      return `${process.env.serverUrl}${this.sessionTrackUrl}`
 | 
			
		||||
    }
 | 
			
		||||
    return `${window.location.origin}${this.routerBasePath}${this.contentUrl}?token=${this.userToken}`
 | 
			
		||||
    return `${window.location.origin}${this.routerBasePath}${this.sessionTrackUrl}`
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Used for LocalPlayer
 | 
			
		||||
   */
 | 
			
		||||
  get relativeContentUrl() {
 | 
			
		||||
    if (!this.contentUrl || this.contentUrl.startsWith('http')) return this.contentUrl
 | 
			
		||||
 | 
			
		||||
    return `${this.routerBasePath}${this.contentUrl}?token=${this.userToken}`
 | 
			
		||||
    return `${this.routerBasePath}${this.sessionTrackUrl}`
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -226,7 +226,7 @@ export default class PlayerHandler {
 | 
			
		||||
 | 
			
		||||
    console.log('[PlayerHandler] Preparing Session', session)
 | 
			
		||||
 | 
			
		||||
    var audioTracks = session.audioTracks.map((at) => new AudioTrack(at, this.userToken, this.ctx.$config.routerBasePath))
 | 
			
		||||
    var audioTracks = session.audioTracks.map((at) => new AudioTrack(at, session.id, this.userToken, this.ctx.$config.routerBasePath))
 | 
			
		||||
 | 
			
		||||
    this.ctx.playerLoading = true
 | 
			
		||||
    this.isHlsTranscode = true
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,9 @@
 | 
			
		||||
const Path = require('path')
 | 
			
		||||
const { Request, Response, NextFunction } = require('express')
 | 
			
		||||
const Logger = require('../Logger')
 | 
			
		||||
const Database = require('../Database')
 | 
			
		||||
const { toNumber, isUUID } = require('../utils/index')
 | 
			
		||||
const { getAudioMimeTypeFromExtname, encodeUriPath } = require('../utils/fileUtils')
 | 
			
		||||
 | 
			
		||||
const ShareManager = require('../managers/ShareManager')
 | 
			
		||||
 | 
			
		||||
@ -266,6 +268,51 @@ class SessionController {
 | 
			
		||||
    this.playbackSessionManager.syncLocalSessionsRequest(req, res)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * GET: /public/session/:id/track/:index
 | 
			
		||||
   * While a session is open, this endpoint can be used to stream the audio track
 | 
			
		||||
   *
 | 
			
		||||
   * @this {import('../routers/PublicRouter')}
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Request} req
 | 
			
		||||
   * @param {Response} res
 | 
			
		||||
   */
 | 
			
		||||
  async getTrack(req, res) {
 | 
			
		||||
    const audioTrackIndex = toNumber(req.params.index, null)
 | 
			
		||||
    if (audioTrackIndex === null) {
 | 
			
		||||
      Logger.error(`[SessionController] Invalid audio track index "${req.params.index}"`)
 | 
			
		||||
      return res.sendStatus(400)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const playbackSession = this.playbackSessionManager.getSession(req.params.id)
 | 
			
		||||
    if (!playbackSession) {
 | 
			
		||||
      Logger.error(`[SessionController] Unable to find playback session with id=${req.params.id}`)
 | 
			
		||||
      return res.sendStatus(404)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const audioTrack = playbackSession.audioTracks.find((t) => t.index === audioTrackIndex)
 | 
			
		||||
    if (!audioTrack) {
 | 
			
		||||
      Logger.error(`[SessionController] Unable to find audio track with index=${audioTrackIndex}`)
 | 
			
		||||
      return res.sendStatus(404)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const user = await Database.userModel.getUserById(playbackSession.userId)
 | 
			
		||||
    Logger.debug(`[SessionController] Serving audio track ${audioTrack.index} for session "${req.params.id}" belonging to user "${user.username}"`)
 | 
			
		||||
 | 
			
		||||
    if (global.XAccel) {
 | 
			
		||||
      const encodedURI = encodeUriPath(global.XAccel + audioTrack.metadata.path)
 | 
			
		||||
      Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
 | 
			
		||||
      return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Express does not set the correct mimetype for m4b files so use our defined mimetypes if available
 | 
			
		||||
    const audioMimeType = getAudioMimeTypeFromExtname(Path.extname(audioTrack.metadata.path))
 | 
			
		||||
    if (audioMimeType) {
 | 
			
		||||
      res.setHeader('Content-Type', audioMimeType)
 | 
			
		||||
    }
 | 
			
		||||
    res.sendFile(audioTrack.metadata.path)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   *
 | 
			
		||||
   * @param {RequestWithUser} req
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,12 @@ class PlaybackSessionManager {
 | 
			
		||||
    this.sessions = []
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get open session by id
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} sessionId
 | 
			
		||||
   * @returns {PlaybackSession}
 | 
			
		||||
   */
 | 
			
		||||
  getSession(sessionId) {
 | 
			
		||||
    return this.sessions.find((s) => s.id === sessionId)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
const express = require('express')
 | 
			
		||||
const ShareController = require('../controllers/ShareController')
 | 
			
		||||
const SessionController = require('../controllers/SessionController')
 | 
			
		||||
 | 
			
		||||
class PublicRouter {
 | 
			
		||||
  constructor(playbackSessionManager) {
 | 
			
		||||
@ -17,6 +18,7 @@ class PublicRouter {
 | 
			
		||||
    this.router.get('/share/:slug/cover', ShareController.getMediaItemShareCoverImage.bind(this))
 | 
			
		||||
    this.router.get('/share/:slug/download', ShareController.downloadMediaItemShare.bind(this))
 | 
			
		||||
    this.router.patch('/share/:slug/progress', ShareController.updateMediaItemShareProgress.bind(this))
 | 
			
		||||
    this.router.get('/session/:id/track/:index', SessionController.getTrack.bind(this))
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
module.exports = PublicRouter
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user