New data model start of PlaybackSessionManager to replace StreamManager, remove podcast & ip npm package

This commit is contained in:
advplyr 2022-03-15 19:28:54 -05:00
parent 68b13ae45f
commit 0af6ad63c1
11 changed files with 109 additions and 116 deletions

View file

@ -26,11 +26,11 @@ const Series = require('./objects/entities/Series')
const FileSystemController = require('./controllers/FileSystemController')
class ApiController {
constructor(db, auth, scanner, streamManager, downloadManager, coverController, backupManager, watcher, cacheManager, emitter, clientEmitter) {
constructor(db, auth, scanner, playbackSessionManager, downloadManager, coverController, backupManager, watcher, cacheManager, emitter, clientEmitter) {
this.db = db
this.auth = auth
this.scanner = scanner
this.streamManager = streamManager
this.playbackSessionManager = playbackSessionManager
this.downloadManager = downloadManager
this.backupManager = backupManager
this.coverController = coverController
@ -83,13 +83,15 @@ class ApiController {
this.router.post('/items/:id/cover', LibraryItemController.middleware.bind(this), LibraryItemController.uploadCover.bind(this))
this.router.patch('/items/:id/cover', LibraryItemController.middleware.bind(this), LibraryItemController.updateCover.bind(this))
this.router.delete('/items/:id/cover', LibraryItemController.middleware.bind(this), LibraryItemController.removeCover.bind(this))
this.router.get('/items/:id/stream', LibraryItemController.middleware.bind(this), LibraryItemController.openStream.bind(this))
this.router.post('/items/:id/match', LibraryItemController.middleware.bind(this), LibraryItemController.match.bind(this))
this.router.patch('/items/:id/tracks', LibraryItemController.middleware.bind(this), LibraryItemController.updateTracks.bind(this))
this.router.get('/items/:id/play', LibraryItemController.middleware.bind(this), LibraryItemController.startPlaybackSession.bind(this))
this.router.post('/items/batch/delete', LibraryItemController.batchDelete.bind(this))
this.router.post('/items/batch/update', LibraryItemController.batchUpdate.bind(this))
this.router.post('/items/batch/get', LibraryItemController.batchGet.bind(this))
// Legacy
this.router.get('/items/:id/stream', LibraryItemController.middleware.bind(this), LibraryItemController.openStream.bind(this))
//
// User Routes
@ -331,7 +333,8 @@ class ApiController {
// Sync audiobook stream progress
async syncStream(req, res) {
Logger.debug(`[ApiController] syncStream for ${req.user.username} - ${req.body.streamId}`)
this.streamManager.streamSyncFromApi(req, res)
// this.streamManager.streamSyncFromApi(req, res)
res.sendStatus(500)
}
// Sync local downloaded audiobook progress
@ -381,17 +384,18 @@ class ApiController {
}
// remove any streams open for this audiobook
var streams = this.streamManager.streams.filter(stream => stream.audiobookId === libraryItem.id)
for (let i = 0; i < streams.length; i++) {
var stream = streams[i]
var client = stream.client
await stream.close()
if (client && client.user) {
client.user.stream = null
client.stream = null
this.db.updateUserStream(client.user.id, null)
}
}
// TODO: Change to PlaybackSessionManager to remove open sessions for user
// var streams = this.streamManager.streams.filter(stream => stream.audiobookId === libraryItem.id)
// for (let i = 0; i < streams.length; i++) {
// var stream = streams[i]
// var client = stream.client
// await stream.close()
// if (client && client.user) {
// client.user.stream = null
// client.stream = null
// this.db.updateUserStream(client.user.id, null)
// }
// }
// remove book from collections
var collectionsWithBook = this.db.collections.filter(c => c.books.includes(libraryItem.id))
@ -472,7 +476,7 @@ class ApiController {
async closeStream(req, res) {
const streamId = req.params.id
const userId = req.user.id
this.streamManager.closeStreamApiRequest(userId, streamId)
// this.streamManager.closeStreamApiRequest(userId, streamId)
res.sendStatus(200)
}

View file

@ -4,10 +4,10 @@ const fs = require('fs-extra')
const Logger = require('./Logger')
class HlsController {
constructor(db, auth, streamManager, emitter) {
constructor(db, auth, playbackSessionManager, emitter) {
this.db = db
this.auth = auth
this.streamManager = streamManager
this.streamManager = playbackSessionManager
this.emitter = emitter
this.router = express()

View file

@ -1,8 +1,29 @@
const Path = require('path')
const PlaybackSession = require('./objects/PlaybackSession')
class PlaybackSessionManager {
constructor() {
constructor(db, emitter, clientEmitter) {
this.db = db
this.StreamsPath = Path.join(global.MetadataPath, 'streams')
this.emitter = emitter
this.clientEmitter = clientEmitter
this.sessions = []
}
startSessionRequest(req, res) {
var user = req.user
var libraryItem = req.libraryItem
var options = req.query
const session = this.startSession(user, libraryItem, options)
res.json(session)
}
startSession(user, libraryItem, options) {
// TODO: Determine what play method to use and setup playback session
const newPlaybackSession = new PlaybackSession()
this.sessions.push(newPlaybackSession)
return newPlaybackSession
}
}
module.exports = PlaybackSessionManager

View file

@ -1,6 +1,6 @@
const Podcast = require('podcast')
// const Podcast = require('podcast')
const express = require('express')
const ip = require('ip')
// const ip = require('ip')
const Logger = require('./Logger')
// Not functional at the moment - just an idea
@ -29,29 +29,31 @@ class RssFeeds {
}
openFeed(audiobook) {
var ipAddress = ip.address('public', 'ipv4')
var serverAddress = 'http://' + ipAddress + ':' + this.Port
Logger.info('Open RSS Feed', 'Server address', serverAddress)
// Removed Podcast npm package and ip package
return null
// var ipAddress = ip.address('public', 'ipv4')
// var serverAddress = 'http://' + ipAddress + ':' + this.Port
// Logger.info('Open RSS Feed', 'Server address', serverAddress)
var feedId = (Date.now() + Math.floor(Math.random() * 1000)).toString(36)
const feed = new Podcast({
title: audiobook.title,
description: 'AudioBookshelf RSS Feed',
feed_url: `${serverAddress}/feeds/${feedId}`,
image_url: `${serverAddress}/Logo.png`,
author: 'advplyr',
language: 'en'
})
audiobook.tracks.forEach((track) => {
feed.addItem({
title: `Track ${track.index}`,
description: `AudioBookshelf Audiobook Track #${track.index}`,
url: `${serverAddress}/feeds/${feedId}?track=${track.index}`,
author: 'advplyr'
})
})
this.feeds[feedId] = feed
return feed
// var feedId = (Date.now() + Math.floor(Math.random() * 1000)).toString(36)
// const feed = new Podcast({
// title: audiobook.title,
// description: 'AudioBookshelf RSS Feed',
// feed_url: `${serverAddress}/feeds/${feedId}`,
// image_url: `${serverAddress}/Logo.png`,
// author: 'advplyr',
// language: 'en'
// })
// audiobook.tracks.forEach((track) => {
// feed.addItem({
// title: `Track ${track.index}`,
// description: `AudioBookshelf Audiobook Track #${track.index}`,
// url: `${serverAddress}/feeds/${feedId}?track=${track.index}`,
// author: 'advplyr'
// })
// })
// this.feeds[feedId] = feed
// return feed
}
}
module.exports = RssFeeds

View file

@ -24,7 +24,8 @@ const BackupManager = require('./BackupManager')
const LogManager = require('./LogManager')
const ApiController = require('./ApiController')
const HlsController = require('./HlsController')
const StreamManager = require('./StreamManager')
// const StreamManager = require('./objects/legacy/StreamManager')
const PlaybackSessionManager = require('./PlaybackSessionManager')
const DownloadManager = require('./DownloadManager')
const CoverController = require('./CoverController')
const CacheManager = require('./CacheManager')
@ -58,10 +59,11 @@ class Server {
this.coverController = new CoverController(this.db, this.cacheManager)
this.scanner = new Scanner(this.db, this.coverController, this.emitter.bind(this))
this.streamManager = new StreamManager(this.db, this.emitter.bind(this), this.clientEmitter.bind(this))
this.playbackSessionManager = new PlaybackSessionManager(this.db, this.emitter.bind(this), this.clientEmitter.bind(this))
// this.streamManager = new StreamManager(this.db, this.emitter.bind(this), this.clientEmitter.bind(this))
this.downloadManager = new DownloadManager(this.db)
this.apiController = new ApiController(this.db, this.auth, this.scanner, this.streamManager, this.downloadManager, this.coverController, this.backupManager, this.watcher, this.cacheManager, this.emitter.bind(this), this.clientEmitter.bind(this))
this.hlsController = new HlsController(this.db, this.auth, this.streamManager, this.emitter.bind(this), this.streamManager.StreamsPath)
this.apiController = new ApiController(this.db, this.auth, this.scanner, this.playbackSessionManager, this.downloadManager, this.coverController, this.backupManager, this.watcher, this.cacheManager, this.emitter.bind(this), this.clientEmitter.bind(this))
this.hlsController = new HlsController(this.db, this.auth, this.playbackSessionManager, this.emitter.bind(this))
Logger.logManager = this.logManager
@ -72,8 +74,9 @@ class Server {
}
get usersOnline() {
// TODO: Map open user sessions
return Object.values(this.clients).filter(c => c.user).map(client => {
return client.user.toJSONForPublic(this.streamManager.streams)
return client.user.toJSONForPublic([])
})
}
@ -104,8 +107,9 @@ class Server {
async init() {
Logger.info('[Server] Init v' + version)
await this.streamManager.ensureStreamsDir()
await this.streamManager.removeOrphanStreams()
// TODO: Remove orphan streams from playback session manager
// await this.streamManager.ensureStreamsDir()
// await this.streamManager.removeOrphanStreams()
await this.downloadManager.removeOrphanDownloads()
if (version.localeCompare('1.7.3') < 0) { // Old version data model migration
@ -264,9 +268,9 @@ class Server {
socket.on('save_metadata', (libraryItemId) => this.saveMetadata(socket, libraryItemId))
// Streaming (only still used in the mobile app)
socket.on('open_stream', (audiobookId) => this.streamManager.openStreamSocketRequest(socket, audiobookId))
socket.on('close_stream', () => this.streamManager.closeStreamRequest(socket))
socket.on('stream_sync', (syncData) => this.streamManager.streamSync(socket, syncData))
// socket.on('open_stream', (audiobookId) => this.streamManager.openStreamSocketRequest(socket, audiobookId))
// socket.on('close_stream', () => this.streamManager.closeStreamRequest(socket))
// socket.on('stream_sync', (syncData) => this.streamManager.streamSync(socket, syncData))
// Used to sync when playing local book on mobile, will be moved to API route
socket.on('progress_update', (payload) => this.audiobookProgressUpdate(socket, payload))
@ -299,7 +303,7 @@ class Server {
delete this.clients[socket.id]
} else {
Logger.debug('[Server] User Offline ' + _client.user.username)
this.io.emit('user_offline', _client.user.toJSONForPublic(this.streamManager.streams))
this.io.emit('user_offline', _client.user.toJSONForPublic([]))
const disconnectTime = Date.now() - _client.connected_at
Logger.info(`[Server] Socket ${socket.id} disconnected from client "${_client.user.username}" after ${disconnectTime}ms`)
@ -603,16 +607,16 @@ class Server {
// Check if user has stream open
if (client.user.stream) {
Logger.info('User has stream open already', client.user.stream)
client.stream = this.streamManager.getStream(client.user.stream)
if (!client.stream) {
Logger.error('Invalid user stream id', client.user.stream)
this.streamManager.removeOrphanStreamFiles(client.user.stream)
await this.db.updateUserStream(client.user.id, null)
}
// client.stream = this.streamManager.getStream(client.user.stream)
// if (!client.stream) {
// Logger.error('Invalid user stream id', client.user.stream)
// this.streamManager.removeOrphanStreamFiles(client.user.stream)
// await this.db.updateUserStream(client.user.id, null)
// }
}
Logger.debug(`[Server] User Online ${client.user.username}`)
this.io.emit('user_online', client.user.toJSONForPublic(this.streamManager.streams))
this.io.emit('user_online', client.user.toJSONForPublic([]))
user.lastSeen = Date.now()
await this.db.updateEntity('user', user)

View file

@ -138,7 +138,13 @@ class LibraryItemController {
// GET: api/items/:id/stream
openStream(req, res) {
this.streamManager.openStreamApiRequest(res, req.user, req.libraryItem)
// this.streamManager.openStreamApiRequest(res, req.user, req.libraryItem)
res.sendStatus(500)
}
// GET: api/items/:id/play
startPlaybackSession(req, res) {
res.sendStatus(200)
}
// POST api/items/:id/match

View file

@ -1,8 +1,8 @@
const date = require('date-and-time')
const { getId } = require('../../utils/index')
const { PlayMethod } = require('../../utils/constants')
const BookMetadata = require('../metadata/BookMetadata')
const PodcastMetadata = require('../metadata/PodcastMetadata')
const { getId } = require('../utils/index')
const { PlayMethod } = require('../utils/constants')
const BookMetadata = require('./metadata/BookMetadata')
const PodcastMetadata = require('./metadata/PodcastMetadata')
class PlaybackSession {
constructor(session) {

View file

@ -1,6 +1,6 @@
const Stream = require('./objects/Stream')
const Stream = require('../Stream')
// const StreamTest = require('./test/StreamTest')
const Logger = require('./Logger')
const Logger = require('../../Logger')
const fs = require('fs-extra')
const Path = require('path')

View file

@ -19,7 +19,7 @@ const LibraryFile = require('../objects/files/LibraryFile')
const FileMetadata = require('../objects/metadata/FileMetadata')
const AudioMetaTags = require('../objects/metadata/AudioMetaTags')
const LibraryItemProgress = require('../objects/user/LibraryItemProgress')
const PlaybackSession = require('../objects/user/PlaybackSession')
const PlaybackSession = require('../objects/PlaybackSession')
const { isObject } = require('.')
const User = require('../objects/user/User')