mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-02 19:11:48 +02:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f7b181b7b | |||
| 1afb8840db | |||
| d9531166b6 | |||
| 336de49d8d | |||
| 45987ffd63 | |||
| 1a1ef9c378 |
@@ -1,5 +1,5 @@
|
|||||||
export default class AudioTrack {
|
export default class AudioTrack {
|
||||||
constructor(track, userToken, routerBasePath) {
|
constructor(track, sessionId, routerBasePath) {
|
||||||
this.index = track.index || 0
|
this.index = track.index || 0
|
||||||
this.startOffset = track.startOffset || 0 // Total time of all previous tracks
|
this.startOffset = track.startOffset || 0 // Total time of all previous tracks
|
||||||
this.duration = track.duration || 0
|
this.duration = track.duration || 0
|
||||||
@@ -8,28 +8,29 @@ export default class AudioTrack {
|
|||||||
this.mimeType = track.mimeType
|
this.mimeType = track.mimeType
|
||||||
this.metadata = track.metadata || {}
|
this.metadata = track.metadata || {}
|
||||||
|
|
||||||
this.userToken = userToken
|
this.sessionId = sessionId
|
||||||
this.routerBasePath = routerBasePath || ''
|
this.routerBasePath = routerBasePath || ''
|
||||||
|
if (this.contentUrl?.startsWith('/hls')) {
|
||||||
|
this.sessionTrackUrl = this.contentUrl
|
||||||
|
} else {
|
||||||
|
this.sessionTrackUrl = `/public/session/${sessionId}/track/${this.index}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for CastPlayer
|
* Used for CastPlayer
|
||||||
*/
|
*/
|
||||||
get fullContentUrl() {
|
get fullContentUrl() {
|
||||||
if (!this.contentUrl || this.contentUrl.startsWith('http')) return this.contentUrl
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
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
|
* Used for LocalPlayer
|
||||||
*/
|
*/
|
||||||
get relativeContentUrl() {
|
get relativeContentUrl() {
|
||||||
if (!this.contentUrl || this.contentUrl.startsWith('http')) return this.contentUrl
|
return `${this.routerBasePath}${this.sessionTrackUrl}`
|
||||||
|
|
||||||
return `${this.routerBasePath}${this.contentUrl}?token=${this.userToken}`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,9 +37,6 @@ export default class PlayerHandler {
|
|||||||
get isPlayingLocalItem() {
|
get isPlayingLocalItem() {
|
||||||
return this.libraryItem && this.player instanceof LocalAudioPlayer
|
return this.libraryItem && this.player instanceof LocalAudioPlayer
|
||||||
}
|
}
|
||||||
get userToken() {
|
|
||||||
return this.ctx.$store.getters['user/getToken']
|
|
||||||
}
|
|
||||||
get playerPlaying() {
|
get playerPlaying() {
|
||||||
return this.playerState === 'PLAYING'
|
return this.playerState === 'PLAYING'
|
||||||
}
|
}
|
||||||
@@ -226,7 +223,7 @@ export default class PlayerHandler {
|
|||||||
|
|
||||||
console.log('[PlayerHandler] Preparing Session', session)
|
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.ctx.$config.routerBasePath))
|
||||||
|
|
||||||
this.ctx.playerLoading = true
|
this.ctx.playerLoading = true
|
||||||
this.isHlsTranscode = true
|
this.isHlsTranscode = true
|
||||||
|
|||||||
+1
-1
@@ -313,7 +313,7 @@ class Server {
|
|||||||
router.use(express.json({ limit: '5mb' }))
|
router.use(express.json({ limit: '5mb' }))
|
||||||
|
|
||||||
router.use('/api', this.auth.ifAuthNeeded(this.authMiddleware.bind(this)), this.apiRouter.router)
|
router.use('/api', this.auth.ifAuthNeeded(this.authMiddleware.bind(this)), this.apiRouter.router)
|
||||||
router.use('/hls', this.authMiddleware.bind(this), this.hlsRouter.router)
|
router.use('/hls', this.hlsRouter.router)
|
||||||
router.use('/public', this.publicRouter.router)
|
router.use('/public', this.publicRouter.router)
|
||||||
|
|
||||||
// Static path to generated nuxt
|
// Static path to generated nuxt
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
const Path = require('path')
|
||||||
const { Request, Response, NextFunction } = require('express')
|
const { Request, Response, NextFunction } = require('express')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const Database = require('../Database')
|
const Database = require('../Database')
|
||||||
const { toNumber, isUUID } = require('../utils/index')
|
const { toNumber, isUUID } = require('../utils/index')
|
||||||
|
const { getAudioMimeTypeFromExtname, encodeUriPath } = require('../utils/fileUtils')
|
||||||
|
|
||||||
const ShareManager = require('../managers/ShareManager')
|
const ShareManager = require('../managers/ShareManager')
|
||||||
|
|
||||||
@@ -266,6 +268,51 @@ class SessionController {
|
|||||||
this.playbackSessionManager.syncLocalSessionsRequest(req, res)
|
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
|
* @param {RequestWithUser} req
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ class PlaybackSessionManager {
|
|||||||
this.sessions = []
|
this.sessions = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get open session by id
|
||||||
|
*
|
||||||
|
* @param {string} sessionId
|
||||||
|
* @returns {PlaybackSession}
|
||||||
|
*/
|
||||||
getSession(sessionId) {
|
getSession(sessionId) {
|
||||||
return this.sessions.find((s) => s.id === sessionId)
|
return this.sessions.find((s) => s.id === sessionId)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
const express = require('express')
|
const express = require('express')
|
||||||
const ShareController = require('../controllers/ShareController')
|
const ShareController = require('../controllers/ShareController')
|
||||||
|
const SessionController = require('../controllers/SessionController')
|
||||||
|
|
||||||
class PublicRouter {
|
class PublicRouter {
|
||||||
constructor(playbackSessionManager) {
|
constructor(playbackSessionManager) {
|
||||||
@@ -17,6 +18,7 @@ class PublicRouter {
|
|||||||
this.router.get('/share/:slug/cover', ShareController.getMediaItemShareCoverImage.bind(this))
|
this.router.get('/share/:slug/cover', ShareController.getMediaItemShareCoverImage.bind(this))
|
||||||
this.router.get('/share/:slug/download', ShareController.downloadMediaItemShare.bind(this))
|
this.router.get('/share/:slug/download', ShareController.downloadMediaItemShare.bind(this))
|
||||||
this.router.patch('/share/:slug/progress', ShareController.updateMediaItemShareProgress.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
|
module.exports = PublicRouter
|
||||||
|
|||||||
@@ -1247,7 +1247,12 @@ module.exports = {
|
|||||||
libraryId
|
libraryId
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return statResults[0]
|
return {
|
||||||
|
totalSize: statResults?.[0]?.totalSize || 0,
|
||||||
|
totalDuration: statResults?.[0]?.totalDuration || 0,
|
||||||
|
numAudioFiles: statResults?.[0]?.numAudioFiles || 0,
|
||||||
|
totalItems: statResults?.[0]?.totalItems || 0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -533,8 +533,10 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
...statResults[0],
|
totalDuration: statResults?.[0]?.totalDuration || 0,
|
||||||
totalSize: sizeResults[0].totalSize || 0
|
numAudioFiles: statResults?.[0]?.numAudioFiles || 0,
|
||||||
|
totalItems: statResults?.[0]?.totalItems || 0,
|
||||||
|
totalSize: sizeResults?.[0]?.totalSize || 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user