mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-15 02:18:27 +01:00
Add: api endpoint for starting streams for android auto support
This commit is contained in:
parent
2451861e0e
commit
663d02e9fe
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "1.6.14",
|
"version": "1.6.15",
|
||||||
"description": "Audiobook manager and player",
|
"description": "Audiobook manager and player",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "1.6.14",
|
"version": "1.6.15",
|
||||||
"description": "Self-hosted audiobook server for managing and playing audiobooks",
|
"description": "Self-hosted audiobook server for managing and playing audiobooks",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -57,6 +57,7 @@ class ApiController {
|
|||||||
this.router.post('/audiobook/:id/cover', this.uploadAudiobookCover.bind(this))
|
this.router.post('/audiobook/:id/cover', this.uploadAudiobookCover.bind(this))
|
||||||
this.router.patch('/audiobook/:id/coverfile', this.updateAudiobookCoverFromFile.bind(this))
|
this.router.patch('/audiobook/:id/coverfile', this.updateAudiobookCoverFromFile.bind(this))
|
||||||
this.router.get('/audiobook/:id/match', this.matchAudiobookBook.bind(this))
|
this.router.get('/audiobook/:id/match', this.matchAudiobookBook.bind(this))
|
||||||
|
this.router.get('/audiobook/:id/stream', this.openAudiobookStream.bind(this))
|
||||||
this.router.patch('/audiobook/:id', this.updateAudiobook.bind(this))
|
this.router.patch('/audiobook/:id', this.updateAudiobook.bind(this))
|
||||||
|
|
||||||
this.router.patch('/match/:id', this.match.bind(this))
|
this.router.patch('/match/:id', this.match.bind(this))
|
||||||
@ -541,6 +542,13 @@ class ApiController {
|
|||||||
res.json(results)
|
res.json(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async openAudiobookStream(req, res) {
|
||||||
|
var audiobook = this.db.audiobooks.find(a => a.id === req.params.id)
|
||||||
|
if (!audiobook) return res.sendStatus(404)
|
||||||
|
|
||||||
|
this.streamManager.openStreamApiRequest(res, req.user, audiobook)
|
||||||
|
}
|
||||||
|
|
||||||
async updateAudiobook(req, res) {
|
async updateAudiobook(req, res) {
|
||||||
if (!req.user.canUpdate) {
|
if (!req.user.canUpdate) {
|
||||||
Logger.warn('User attempted to update without permission', req.user)
|
Logger.warn('User attempted to update without permission', req.user)
|
||||||
|
@ -100,8 +100,24 @@ class StreamManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async openStreamApiRequest(res, user, audiobook) {
|
||||||
|
Logger.info(`[StreamManager] User "${user.username}" open stream request for "${audiobook.title}"`)
|
||||||
|
var client = {
|
||||||
|
user
|
||||||
|
}
|
||||||
|
var stream = await this.openStream(client, audiobook)
|
||||||
|
this.db.updateUserStream(client.user.id, stream.id)
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
audiobookId: audiobook.id,
|
||||||
|
startTime: stream.startTime,
|
||||||
|
streamId: stream.id,
|
||||||
|
streamUrl: stream.clientPlaylistUri
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async openStreamSocketRequest(socket, audiobookId) {
|
async openStreamSocketRequest(socket, audiobookId) {
|
||||||
Logger.info('Open Stream Request', socket.id, audiobookId)
|
Logger.info('[StreamManager] Open Stream Request', socket.id, audiobookId)
|
||||||
var audiobook = this.audiobooks.find(ab => ab.id === audiobookId)
|
var audiobook = this.audiobooks.find(ab => ab.id === audiobookId)
|
||||||
var client = socket.sheepClient
|
var client = socket.sheepClient
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class Stream extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get socket() {
|
get socket() {
|
||||||
return this.client.socket
|
return this.client ? this.client.socket || null : null
|
||||||
}
|
}
|
||||||
|
|
||||||
get audiobookId() {
|
get audiobookId() {
|
||||||
@ -89,15 +89,15 @@ class Stream extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get clientUser() {
|
get clientUser() {
|
||||||
return this.client.user || {}
|
return this.client ? this.client.user || {} : null
|
||||||
}
|
}
|
||||||
|
|
||||||
get clientUserAudiobooks() {
|
get clientUserAudiobooks() {
|
||||||
return this.clientUser.audiobooks || {}
|
return this.client ? this.clientUser.audiobooks || {} : null
|
||||||
}
|
}
|
||||||
|
|
||||||
get clientUserAudiobookData() {
|
get clientUserAudiobookData() {
|
||||||
return this.clientUserAudiobooks[this.audiobookId]
|
return this.client ? this.clientUserAudiobooks[this.audiobookId] : null
|
||||||
}
|
}
|
||||||
|
|
||||||
get clientPlaylistUri() {
|
get clientPlaylistUri() {
|
||||||
@ -189,9 +189,11 @@ class Stream extends EventEmitter {
|
|||||||
|
|
||||||
if (this.segmentsCreated.size > 6 && !this.isClientInitialized) {
|
if (this.segmentsCreated.size > 6 && !this.isClientInitialized) {
|
||||||
this.isClientInitialized = true
|
this.isClientInitialized = true
|
||||||
|
if (this.socket) {
|
||||||
Logger.info(`[STREAM] ${this.id} notifying client that stream is ready`)
|
Logger.info(`[STREAM] ${this.id} notifying client that stream is ready`)
|
||||||
this.socket.emit('stream_open', this.toJSON())
|
this.socket.emit('stream_open', this.toJSON())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var chunks = []
|
var chunks = []
|
||||||
var current_chunk = []
|
var current_chunk = []
|
||||||
@ -221,14 +223,16 @@ class Stream extends EventEmitter {
|
|||||||
|
|
||||||
var perc = (this.segmentsCreated.size * 100 / this.numSegments).toFixed(2) + '%'
|
var perc = (this.segmentsCreated.size * 100 / this.numSegments).toFixed(2) + '%'
|
||||||
Logger.info('[STREAM-CHECK] Check Files', this.segmentsCreated.size, 'of', this.numSegments, perc, `Furthest Segment: ${this.furthestSegmentCreated}`)
|
Logger.info('[STREAM-CHECK] Check Files', this.segmentsCreated.size, 'of', this.numSegments, perc, `Furthest Segment: ${this.furthestSegmentCreated}`)
|
||||||
Logger.debug('[STREAM-CHECK] Chunks', chunks.join(', '))
|
// Logger.debug('[STREAM-CHECK] Chunks', chunks.join(', '))
|
||||||
|
|
||||||
|
if (this.socket) {
|
||||||
this.socket.emit('stream_progress', {
|
this.socket.emit('stream_progress', {
|
||||||
stream: this.id,
|
stream: this.id,
|
||||||
percent: perc,
|
percent: perc,
|
||||||
chunks,
|
chunks,
|
||||||
numSegments: this.numSegments
|
numSegments: this.numSegments
|
||||||
})
|
})
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error('Failed checking files', error)
|
Logger.error('Failed checking files', error)
|
||||||
}
|
}
|
||||||
@ -236,15 +240,19 @@ class Stream extends EventEmitter {
|
|||||||
|
|
||||||
startLoop() {
|
startLoop() {
|
||||||
// Logger.info(`[Stream] ${this.audiobookTitle} (${this.id}) Start Loop`)
|
// Logger.info(`[Stream] ${this.audiobookTitle} (${this.id}) Start Loop`)
|
||||||
|
if (this.socket) {
|
||||||
this.socket.emit('stream_progress', { stream: this.id, chunks: [], numSegments: 0, percent: '0%' })
|
this.socket.emit('stream_progress', { stream: this.id, chunks: [], numSegments: 0, percent: '0%' })
|
||||||
|
}
|
||||||
|
|
||||||
clearInterval(this.loop)
|
clearInterval(this.loop)
|
||||||
var intervalId = setInterval(() => {
|
var intervalId = setInterval(() => {
|
||||||
if (!this.isTranscodeComplete) {
|
if (!this.isTranscodeComplete) {
|
||||||
this.checkFiles()
|
this.checkFiles()
|
||||||
} else {
|
} else {
|
||||||
|
if (this.socket) {
|
||||||
Logger.info(`[Stream] ${this.audiobookTitle} sending stream_ready`)
|
Logger.info(`[Stream] ${this.audiobookTitle} sending stream_ready`)
|
||||||
this.socket.emit('stream_ready')
|
this.socket.emit('stream_ready')
|
||||||
|
}
|
||||||
clearInterval(intervalId)
|
clearInterval(intervalId)
|
||||||
}
|
}
|
||||||
}, 2000)
|
}, 2000)
|
||||||
@ -342,9 +350,11 @@ class Stream extends EventEmitter {
|
|||||||
// For very small fast load
|
// For very small fast load
|
||||||
if (!this.isClientInitialized) {
|
if (!this.isClientInitialized) {
|
||||||
this.isClientInitialized = true
|
this.isClientInitialized = true
|
||||||
|
if (this.socket) {
|
||||||
Logger.info(`[STREAM] ${this.id} notifying client that stream is ready`)
|
Logger.info(`[STREAM] ${this.id} notifying client that stream is ready`)
|
||||||
this.socket.emit('stream_open', this.toJSON())
|
this.socket.emit('stream_open', this.toJSON())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.isTranscodeComplete = true
|
this.isTranscodeComplete = true
|
||||||
this.ffmpeg = null
|
this.ffmpeg = null
|
||||||
clearInterval(this.loop)
|
clearInterval(this.loop)
|
||||||
@ -367,7 +377,9 @@ class Stream extends EventEmitter {
|
|||||||
Logger.error('Failed to delete session data', err)
|
Logger.error('Failed to delete session data', err)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.client.socket.emit('stream_closed', this.id)
|
if (this.socket) {
|
||||||
|
this.socket.emit('stream_closed', this.id)
|
||||||
|
}
|
||||||
|
|
||||||
this.emit('closed')
|
this.emit('closed')
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user