Merge branch 'advplyr:master' into master

This commit is contained in:
Spenser Bushey 2022-11-24 23:12:55 -08:00 committed by GitHub
commit 8c23704e17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 526 additions and 452 deletions

View File

@ -378,7 +378,7 @@ export default {
}
},
streamReady() {
console.log(`[STREAM-CONTAINER] Stream Ready`)
console.log(`[StreamContainer] Stream Ready`)
if (this.$refs.audioPlayer) {
this.$refs.audioPlayer.setStreamReady()
} else {

View File

@ -64,13 +64,11 @@ export default {
showConfirmApply: false,
selectedBackup: null,
isBackingUp: false,
processing: false
processing: false,
backups: []
}
},
computed: {
backups() {
return this.$store.state.backups || []
},
userToken() {
return this.$store.getters['user/getToken']
}
@ -96,9 +94,8 @@ export default {
this.processing = true
this.$axios
.$delete(`/api/backups/${backup.id}`)
.then((backups) => {
console.log('Backup deleted', backups)
this.$store.commit('setBackups', backups)
.then((data) => {
this.setBackups(data.backups || [])
this.$toast.success(this.$strings.ToastBackupDeleteSuccess)
this.processing = false
})
@ -117,10 +114,10 @@ export default {
this.isBackingUp = true
this.$axios
.$post('/api/backups')
.then((backups) => {
.then((data) => {
this.isBackingUp = false
this.$toast.success(this.$strings.ToastBackupCreateSuccess)
this.$store.commit('setBackups', backups)
this.setBackups(data.backups || [])
})
.catch((error) => {
this.isBackingUp = false
@ -136,9 +133,8 @@ export default {
this.$axios
.$post('/api/backups/upload', form)
.then((result) => {
console.log('Upload backup result', result)
this.$store.commit('setBackups', result)
.then((data) => {
this.setBackups(data.backups || [])
this.$toast.success(this.$strings.ToastBackupUploadSuccess)
this.processing = false
})
@ -148,9 +144,29 @@ export default {
this.$toast.error(errorMessage)
this.processing = false
})
},
setBackups(backups) {
backups.sort((a, b) => b.createdAt - a.createdAt)
this.backups = backups
},
loadBackups() {
this.processing = true
this.$axios
.$get('/api/backups')
.then((data) => {
this.setBackups(data.backups || [])
})
.catch((error) => {
console.error('Failed to load backups', error)
this.$toast.error('Failed to load backups')
})
.finally(() => {
this.processing = false
})
}
},
mounted() {
this.loadBackups()
if (this.$route.query.backup) {
this.$toast.success('Backup applied successfully')
this.$router.replace('/config')

View File

@ -26,11 +26,9 @@
</td>
<td class="text-sm">{{ user.type }}</td>
<td class="hidden lg:table-cell">
<div v-if="usersOnline[user.id] && usersOnline[user.id].session && usersOnline[user.id].session.libraryItem && usersOnline[user.id].session.libraryItem.media">
<p class="truncate text-xs">Listening: {{ usersOnline[user.id].session.libraryItem.media.metadata.title || '' }}</p>
</div>
<div v-else-if="user.mostRecent">
<p class="truncate text-xs">Last: {{ user.mostRecent.metadata.title }}</p>
<div v-if="usersOnline[user.id]">
<p v-if="usersOnline[user.id].session && usersOnline[user.id].session.libraryItem" class="truncate text-xs">Listening: {{ usersOnline[user.id].session.libraryItem.media.metadata.title || '' }}</p>
<p v-else-if="usersOnline[user.id].mostRecent && usersOnline[user.id].mostRecent.media" class="truncate text-xs">Last: {{ usersOnline[user.id].mostRecent.media.metadata.title }}</p>
</div>
</td>
<td class="text-xs font-mono hidden sm:table-cell">
@ -81,7 +79,7 @@ export default {
},
usersOnline() {
var usermap = {}
this.$store.state.users.users.forEach((u) => (usermap[u.id] = { online: true, session: u.session }))
this.$store.state.users.usersOnline.forEach((u) => (usermap[u.id] = u))
return usermap
}
},

View File

@ -132,14 +132,8 @@ export default {
}
})
if (payload.backups && payload.backups.length) {
this.$store.commit('setBackups', payload.backups)
}
if (payload.usersOnline) {
this.$store.commit('users/resetUsers')
payload.usersOnline.forEach((user) => {
this.$store.commit('users/updateUser', user)
})
this.$store.commit('users/setUsersOnline', payload.usersOnline)
}
this.$eventBus.$emit('socket_init')
@ -286,13 +280,13 @@ export default {
}
},
userOnline(user) {
this.$store.commit('users/updateUser', user)
this.$store.commit('users/updateUserOnline', user)
},
userOffline(user) {
this.$store.commit('users/removeUser', user)
this.$store.commit('users/removeUserOnline', user)
},
userStreamUpdate(user) {
this.$store.commit('users/updateUser', user)
this.$store.commit('users/updateUserOnline', user)
},
userMediaProgressUpdate(payload) {
this.$store.commit('user/updateMediaProgress', payload)
@ -333,6 +327,9 @@ export default {
this.$toast.info(toast)
}
},
adminMessageEvt(message) {
this.$toast.info(message)
},
initializeSocket() {
this.socket = this.$nuxtSocket({
name: process.env.NODE_ENV === 'development' ? 'dev' : 'prod',
@ -345,6 +342,7 @@ export default {
this.$root.socket = this.socket
console.log('Socket initialized')
// Pre-defined socket events
this.socket.on('connect', this.connect)
this.socket.on('connect_error', this.connectError)
this.socket.on('disconnect', this.disconnect)
@ -353,6 +351,7 @@ export default {
this.socket.io.on('reconnect_error', this.reconnectError)
this.socket.io.on('reconnect_failed', this.reconnectFailed)
// Event received after authorizing socket
this.socket.on('init', this.init)
// Stream Listeners
@ -403,6 +402,8 @@ export default {
this.socket.on('backup_applied', this.backupApplied)
this.socket.on('batch_quickmatch_complete', this.batchQuickMatchComplete)
this.socket.on('admin_message', this.adminMessageEvt)
},
showUpdateToast(versionData) {
var ignoreVersion = localStorage.getItem('ignoreVersion')

View File

@ -21,7 +21,6 @@ export const state = () => ({
processingBatch: false,
previousPath: '/',
showExperimentalFeatures: false,
backups: [],
bookshelfBookIds: [],
openModal: null,
innerModalOpen: false,
@ -245,9 +244,6 @@ export const mutations = {
state.showExperimentalFeatures = val
localStorage.setItem('experimental', val ? 1 : 0)
},
setBackups(state, val) {
state.backups = val.sort((a, b) => b.createdAt - a.createdAt)
},
setOpenModal(state, val) {
state.openModal = val
},

View File

@ -1,11 +1,11 @@
export const state = () => ({
users: []
usersOnline: []
})
export const getters = {
getIsUserOnline: state => id => {
return state.users.find(u => u.id === id)
return state.usersOnline.find(u => u.id === id)
}
}
@ -14,18 +14,18 @@ export const actions = {
}
export const mutations = {
resetUsers(state) {
state.users = []
setUsersOnline(state, usersOnline) {
state.usersOnline = usersOnline
},
updateUser(state, user) {
var index = state.users.findIndex(u => u.id === user.id)
updateUserOnline(state, user) {
var index = state.usersOnline.findIndex(u => u.id === user.id)
if (index >= 0) {
state.users.splice(index, 1, user)
state.usersOnline.splice(index, 1, user)
} else {
state.users.push(user)
state.usersOnline.push(user)
}
},
removeUser(state, user) {
state.users = state.users.filter(u => u.id !== user.id)
removeUserOnline(state, user) {
state.usersOnline = state.usersOnline.filter(u => u.id !== user.id)
}
}

View File

@ -258,23 +258,6 @@ class Db {
})
}
updateUserStream(userId, streamId) {
return this.usersDb.update((record) => record.id === userId, (user) => {
user.stream = streamId
return user
}).then((results) => {
Logger.debug(`[DB] Updated user ${results.updated}`)
this.users = this.users.map(u => {
if (u.id === userId) {
u.stream = streamId
}
return u
})
}).catch((error) => {
Logger.error(`[DB] Update user Failed ${error}`)
})
}
updateServerSettings() {
global.ServerSettings = this.serverSettings.toJSON()
return this.updateEntity('settings', this.serverSettings)

View File

@ -1,7 +1,6 @@
const Path = require('path')
const express = require('express')
const http = require('http')
const SocketIO = require('socket.io')
const fs = require('./libs/fsExtra')
const fileUpload = require('./libs/expressFileupload')
const rateLimit = require('./libs/expressRateLimit')
@ -13,11 +12,11 @@ const dbMigration = require('./utils/dbMigration')
const filePerms = require('./utils/filePerms')
const Logger = require('./Logger')
// Classes
const Auth = require('./Auth')
const Watcher = require('./Watcher')
const Scanner = require('./scanner/Scanner')
const Db = require('./Db')
const SocketAuthority = require('./SocketAuthority')
const ApiRouter = require('./routers/ApiRouter')
const HlsRouter = require('./routers/HlsRouter')
@ -67,59 +66,30 @@ class Server {
this.auth = new Auth(this.db)
// Managers
this.taskManager = new TaskManager(this.emitter.bind(this))
this.notificationManager = new NotificationManager(this.db, this.emitter.bind(this))
this.backupManager = new BackupManager(this.db, this.emitter.bind(this))
this.taskManager = new TaskManager()
this.notificationManager = new NotificationManager(this.db)
this.backupManager = new BackupManager(this.db)
this.logManager = new LogManager(this.db)
this.cacheManager = new CacheManager()
this.abMergeManager = new AbMergeManager(this.db, this.taskManager, this.clientEmitter.bind(this))
this.playbackSessionManager = new PlaybackSessionManager(this.db, this.emitter.bind(this), this.clientEmitter.bind(this))
this.abMergeManager = new AbMergeManager(this.db, this.taskManager)
this.playbackSessionManager = new PlaybackSessionManager(this.db)
this.coverManager = new CoverManager(this.db, this.cacheManager)
this.podcastManager = new PodcastManager(this.db, this.watcher, this.emitter.bind(this), this.notificationManager)
this.audioMetadataManager = new AudioMetadataMangaer(this.db, this.taskManager, this.emitter.bind(this), this.clientEmitter.bind(this))
this.rssFeedManager = new RssFeedManager(this.db, this.emitter.bind(this))
this.podcastManager = new PodcastManager(this.db, this.watcher, this.notificationManager)
this.audioMetadataManager = new AudioMetadataMangaer(this.db, this.taskManager)
this.rssFeedManager = new RssFeedManager(this.db)
this.scanner = new Scanner(this.db, this.coverManager, this.emitter.bind(this))
this.scanner = new Scanner(this.db, this.coverManager)
this.cronManager = new CronManager(this.db, this.scanner, this.podcastManager)
// Routers
this.apiRouter = new ApiRouter(this.db, this.auth, this.scanner, this.playbackSessionManager, this.abMergeManager, this.coverManager, this.backupManager, this.watcher, this.cacheManager, this.podcastManager, this.audioMetadataManager, this.rssFeedManager, this.cronManager, this.notificationManager, this.taskManager, this.getUsersOnline.bind(this), this.emitter.bind(this), this.clientEmitter.bind(this))
this.hlsRouter = new HlsRouter(this.db, this.auth, this.playbackSessionManager, this.emitter.bind(this))
this.apiRouter = new ApiRouter(this)
this.hlsRouter = new HlsRouter(this.db, this.auth, this.playbackSessionManager)
this.staticRouter = new StaticRouter(this.db)
Logger.logManager = this.logManager
this.server = null
this.io = null
this.clients = {}
}
getUsersOnline() {
return Object.values(this.clients).filter(c => c.user).map(client => {
return client.user.toJSONForPublic(this.playbackSessionManager.sessions, this.db.libraryItems)
})
}
getClientsForUser(userId) {
return Object.values(this.clients).filter(c => c.user && c.user.id === userId)
}
emitter(ev, data) {
// Logger.debug('EMITTER', ev)
this.io.emit(ev, data)
}
clientEmitter(userId, ev, data) {
var clients = this.getClientsForUser(userId)
if (!clients.length) {
return Logger.debug(`[Server] clientEmitter - no clients found for user ${userId}`)
}
clients.forEach((client) => {
if (client.socket) {
client.socket.emit(ev, data)
}
})
}
authMiddleware(req, res, next) {
@ -130,7 +100,7 @@ class Server {
Logger.info('[Server] Init v' + version)
await this.playbackSessionManager.removeOrphanStreams()
var previousVersion = await this.db.checkPreviousVersion() // Returns null if same server version
const previousVersion = await this.db.checkPreviousVersion() // Returns null if same server version
if (previousVersion) {
Logger.debug(`[Server] Upgraded from previous version ${previousVersion}`)
}
@ -197,13 +167,13 @@ class Server {
// EBook static file routes
router.get('/ebook/:library/:folder/*', (req, res) => {
var library = this.db.libraries.find(lib => lib.id === req.params.library)
const library = this.db.libraries.find(lib => lib.id === req.params.library)
if (!library) return res.sendStatus(404)
var folder = library.folders.find(fol => fol.id === req.params.folder)
const folder = library.folders.find(fol => fol.id === req.params.folder)
if (!folder) return res.status(404).send('Folder not found')
var remainingPath = req.params['0']
var fullPath = Path.join(folder.fullPath, remainingPath)
const remainingPath = req.params['0']
const fullPath = Path.join(folder.fullPath, remainingPath)
res.sendFile(fullPath)
})
@ -272,58 +242,8 @@ class Server {
Logger.info(`Listening on http://${this.Host}:${this.Port}`)
})
this.io = new SocketIO.Server(this.server, {
cors: {
origin: '*',
methods: ["GET", "POST"]
}
})
this.io.on('connection', (socket) => {
this.clients[socket.id] = {
id: socket.id,
socket,
connected_at: Date.now()
}
socket.sheepClient = this.clients[socket.id]
Logger.info('[Server] Socket Connected', socket.id)
socket.on('auth', (token) => this.authenticateSocket(socket, token))
// Scanning
socket.on('cancel_scan', this.cancelScan.bind(this))
// Logs
socket.on('set_log_listener', (level) => Logger.addSocketListener(socket, level))
socket.on('remove_log_listener', () => Logger.removeSocketListener(socket.id))
socket.on('fetch_daily_logs', () => this.logManager.socketRequestDailyLogs(socket))
socket.on('ping', () => {
var client = this.clients[socket.id] || {}
var user = client.user || {}
Logger.debug(`[Server] Received ping from socket ${user.username || 'No User'}`)
socket.emit('pong')
})
socket.on('disconnect', (reason) => {
Logger.removeSocketListener(socket.id)
var _client = this.clients[socket.id]
if (!_client) {
Logger.warn(`[Server] Socket ${socket.id} disconnect, no client (Reason: ${reason})`)
} else if (!_client.user) {
Logger.info(`[Server] Unauth socket ${socket.id} disconnected (Reason: ${reason})`)
delete this.clients[socket.id]
} else {
Logger.debug('[Server] User Offline ' + _client.user.username)
this.io.emit('user_offline', _client.user.toJSONForPublic(this.playbackSessionManager.sessions, this.db.libraryItems))
const disconnectTime = Date.now() - _client.connected_at
Logger.info(`[Server] Socket ${socket.id} disconnected from client "${_client.user.username}" after ${disconnectTime}ms (Reason: ${reason})`)
delete this.clients[socket.id]
}
})
})
// Start listening for socket connections
SocketAuthority.initialize(this)
}
async initializeServer(req, res) {
@ -342,22 +262,17 @@ class Server {
await this.scanner.scanFilesChanged(fileUpdates)
}
cancelScan(id) {
Logger.debug('[Server] Cancel scan', id)
this.scanner.setCancelLibraryScan(id)
}
// Remove unused /metadata/items/{id} folders
async purgeMetadata() {
var itemsMetadata = Path.join(global.MetadataPath, 'items')
const itemsMetadata = Path.join(global.MetadataPath, 'items')
if (!(await fs.pathExists(itemsMetadata))) return
var foldersInItemsMetadata = await fs.readdir(itemsMetadata)
const foldersInItemsMetadata = await fs.readdir(itemsMetadata)
var purged = 0
let purged = 0
await Promise.all(foldersInItemsMetadata.map(async foldername => {
var hasMatchingItem = this.db.libraryItems.find(ab => ab.id === foldername)
const hasMatchingItem = this.db.libraryItems.find(ab => ab.id === foldername)
if (!hasMatchingItem) {
var folderPath = Path.join(itemsMetadata, foldername)
const folderPath = Path.join(itemsMetadata, foldername)
Logger.debug(`[Server] Purging unused metadata ${folderPath}`)
await fs.remove(folderPath).then(() => {
@ -376,8 +291,8 @@ class Server {
// Remove user media progress with items that no longer exist & remove seriesHideFrom that no longer exist
async cleanUserData() {
for (let i = 0; i < this.db.users.length; i++) {
var _user = this.db.users[i]
var hasUpdated = false
const _user = this.db.users[i]
let hasUpdated = false
if (_user.mediaProgress.length) {
const lengthBefore = _user.mediaProgress.length
_user.mediaProgress = _user.mediaProgress.filter(mp => {
@ -423,68 +338,14 @@ class Server {
}
logout(req, res) {
var { socketId } = req.body
Logger.info(`[Server] User ${req.user ? req.user.username : 'Unknown'} is logging out with socket ${socketId}`)
// Strip user and client from client and client socket
if (socketId && this.clients[socketId]) {
var client = this.clients[socketId]
var clientSocket = client.socket
Logger.debug(`[Server] Found user client ${clientSocket.id}, Has user: ${!!client.user}, Socket has client: ${!!clientSocket.sheepClient}`)
if (client.user) {
Logger.debug('[Server] User Offline ' + client.user.username)
this.io.emit('user_offline', client.user.toJSONForPublic(null, this.db.libraryItems))
}
delete this.clients[socketId].user
if (clientSocket && clientSocket.sheepClient) delete this.clients[socketId].socket.sheepClient
} else if (socketId) {
Logger.warn(`[Server] No client for socket ${socketId}`)
if (req.body.socketId) {
Logger.info(`[Server] User ${req.user ? req.user.username : 'Unknown'} is logging out with socket ${req.body.socketId}`)
SocketAuthority.logout(req.body.socketId)
}
res.sendStatus(200)
}
async authenticateSocket(socket, token) {
var user = await this.auth.authenticateUser(token)
if (!user) {
Logger.error('Cannot validate socket - invalid token')
return socket.emit('invalid_token')
}
var client = this.clients[socket.id]
if (client.user !== undefined) {
Logger.debug(`[Server] Authenticating socket client already has user`, client.user.username)
}
client.user = user
if (!client.user.toJSONForBrowser) {
Logger.error('Invalid user...', client.user)
return
}
Logger.debug(`[Server] User Online ${client.user.username}`)
this.io.emit('user_online', client.user.toJSONForPublic(this.playbackSessionManager.sessions, this.db.libraryItems))
user.lastSeen = Date.now()
await this.db.updateEntity('user', user)
const initialPayload = {
metadataPath: global.MetadataPath,
configPath: global.ConfigPath,
user: client.user.toJSONForBrowser(),
librariesScanning: this.scanner.librariesScanning,
backups: (this.backupManager.backups || []).map(b => b.toJSON())
}
if (user.type === 'root') {
initialPayload.usersOnline = this.getUsersOnline()
}
client.socket.emit('init', initialPayload)
}
async stop() {
await this.watcher.close()
Logger.info('Watcher Closed')

200
server/SocketAuthority.js Normal file
View File

@ -0,0 +1,200 @@
const SocketIO = require('socket.io')
const Logger = require('./Logger')
class SocketAuthority {
constructor() {
this.Server = null
this.io = null
this.clients = {}
}
// returns an array of User.toJSONForPublic with `connections` for the # of socket connections
// a user can have many socket connections
getUsersOnline() {
const onlineUsersMap = {}
Object.values(this.clients).filter(c => c.user).forEach(client => {
if (onlineUsersMap[client.user.id]) {
onlineUsersMap[client.user.id].connections++
} else {
onlineUsersMap[client.user.id] = {
...client.user.toJSONForPublic(this.Server.playbackSessionManager.sessions, this.Server.db.libraryItems),
connections: 1
}
}
})
return Object.values(onlineUsersMap)
}
getClientsForUser(userId) {
return Object.values(this.clients).filter(c => c.user && c.user.id === userId)
}
// Emits event to all authorized clients
emitter(evt, data) {
for (const socketId in this.clients) {
if (this.clients[socketId].user) {
this.clients[socketId].socket.emit(evt, data)
}
}
}
// Emits event to all clients for a specific user
clientEmitter(userId, evt, data) {
const clients = this.getClientsForUser(userId)
if (!clients.length) {
return Logger.debug(`[Server] clientEmitter - no clients found for user ${userId}`)
}
clients.forEach((client) => {
if (client.socket) {
client.socket.emit(evt, data)
}
})
}
// Emits event to all admin user clients
adminEmitter(evt, data) {
for (const socketId in this.clients) {
if (this.clients[socketId].user && this.clients[socketId].user.isAdminOrUp) {
this.clients[socketId].socket.emit(evt, data)
}
}
}
initialize(Server) {
this.Server = Server
this.io = new SocketIO.Server(this.Server.server, {
cors: {
origin: '*',
methods: ["GET", "POST"]
}
})
this.io.on('connection', (socket) => {
this.clients[socket.id] = {
id: socket.id,
socket,
connected_at: Date.now()
}
socket.sheepClient = this.clients[socket.id]
Logger.info('[Server] Socket Connected', socket.id)
// Required for associating a User with a socket
socket.on('auth', (token) => this.authenticateSocket(socket, token))
// Scanning
socket.on('cancel_scan', this.cancelScan.bind(this))
// Logs
socket.on('set_log_listener', (level) => Logger.addSocketListener(socket, level))
socket.on('remove_log_listener', () => Logger.removeSocketListener(socket.id))
socket.on('fetch_daily_logs', () => this.Server.logManager.socketRequestDailyLogs(socket))
// Sent automatically from socket.io clients
socket.on('disconnect', (reason) => {
Logger.removeSocketListener(socket.id)
const _client = this.clients[socket.id]
if (!_client) {
Logger.warn(`[Server] Socket ${socket.id} disconnect, no client (Reason: ${reason})`)
} else if (!_client.user) {
Logger.info(`[Server] Unauth socket ${socket.id} disconnected (Reason: ${reason})`)
delete this.clients[socket.id]
} else {
Logger.debug('[Server] User Offline ' + _client.user.username)
this.adminEmitter('user_offline', _client.user.toJSONForPublic(this.Server.playbackSessionManager.sessions, this.Server.db.libraryItems))
const disconnectTime = Date.now() - _client.connected_at
Logger.info(`[Server] Socket ${socket.id} disconnected from client "${_client.user.username}" after ${disconnectTime}ms (Reason: ${reason})`)
delete this.clients[socket.id]
}
})
//
// Events for testing
//
socket.on('message_all_users', (payload) => {
// admin user can send a message to all authenticated users
// displays on the web app as a toast
const client = this.clients[socket.id] || {}
if (client.user && client.user.isAdminOrUp) {
this.emitter('admin_message', payload.message || '')
} else {
Logger.error(`[Server] Non-admin user sent the message_all_users event`)
}
})
socket.on('ping', () => {
const client = this.clients[socket.id] || {}
const user = client.user || {}
Logger.debug(`[Server] Received ping from socket ${user.username || 'No User'}`)
socket.emit('pong')
})
})
}
// When setting up a socket connection the user needs to be associated with a socket id
// for this the client will send a 'auth' event that includes the users API token
async authenticateSocket(socket, token) {
const user = await this.Server.auth.authenticateUser(token)
if (!user) {
Logger.error('Cannot validate socket - invalid token')
return socket.emit('invalid_token')
}
const client = this.clients[socket.id]
if (client.user !== undefined) {
Logger.debug(`[Server] Authenticating socket client already has user`, client.user.username)
}
client.user = user
if (!client.user.toJSONForBrowser) {
Logger.error('Invalid user...', client.user)
return
}
Logger.debug(`[Server] User Online ${client.user.username}`)
this.adminEmitter('user_online', client.user.toJSONForPublic(this.Server.playbackSessionManager.sessions, this.Server.db.libraryItems))
// Update user lastSeen
user.lastSeen = Date.now()
await this.Server.db.updateEntity('user', user)
const initialPayload = {
userId: client.user.id,
username: client.user.username,
librariesScanning: this.Server.scanner.librariesScanning
}
if (user.isAdminOrUp) {
initialPayload.usersOnline = this.getUsersOnline()
}
client.socket.emit('init', initialPayload)
}
logout(socketId) {
// Strip user and client from client and client socket
if (socketId && this.clients[socketId]) {
const client = this.clients[socketId]
const clientSocket = client.socket
Logger.debug(`[Server] Found user client ${clientSocket.id}, Has user: ${!!client.user}, Socket has client: ${!!clientSocket.sheepClient}`)
if (client.user) {
Logger.debug('[Server] User Offline ' + client.user.username)
this.adminEmitter('user_offline', client.user.toJSONForPublic(null, this.Server.db.libraryItems))
}
delete this.clients[socketId].user
if (clientSocket && clientSocket.sheepClient) delete this.clients[socketId].socket.sheepClient
} else if (socketId) {
Logger.warn(`[Server] No client for socket ${socketId}`)
}
}
cancelScan(id) {
Logger.debug('[Server] Cancel scan', id)
this.Server.scanner.setCancelLibraryScan(id)
}
}
module.exports = new SocketAuthority()

View File

@ -1,4 +1,6 @@
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const { reqSupportsWebp } = require('../utils/index')
const { createNewSortInstance } = require('../libs/fastSort')
@ -93,18 +95,18 @@ class AuthorController {
})
if (itemsWithAuthor.length) {
await this.db.updateLibraryItems(itemsWithAuthor)
this.emitter('items_updated', itemsWithAuthor.map(li => li.toJSONExpanded()))
SocketAuthority.emitter('items_updated', itemsWithAuthor.map(li => li.toJSONExpanded()))
}
// Remove old author
await this.db.removeEntity('author', req.author.id)
this.emitter('author_removed', req.author.toJSON())
SocketAuthority.emitter('author_removed', req.author.toJSON())
// Send updated num books for merged author
var numBooks = this.db.libraryItems.filter(li => {
return li.media.metadata.hasAuthor && li.media.metadata.hasAuthor(existingAuthor.id)
}).length
this.emitter('author_updated', existingAuthor.toJSONExpanded(numBooks))
SocketAuthority.emitter('author_updated', existingAuthor.toJSONExpanded(numBooks))
res.json({
author: existingAuthor.toJSON(),
@ -121,7 +123,7 @@ class AuthorController {
})
if (itemsWithAuthor.length) {
await this.db.updateLibraryItems(itemsWithAuthor)
this.emitter('items_updated', itemsWithAuthor.map(li => li.toJSONExpanded()))
SocketAuthority.emitter('items_updated', itemsWithAuthor.map(li => li.toJSONExpanded()))
}
}
@ -129,7 +131,7 @@ class AuthorController {
var numBooks = this.db.libraryItems.filter(li => {
return li.media.metadata.hasAuthor && li.media.metadata.hasAuthor(req.author.id)
}).length
this.emitter('author_updated', req.author.toJSONExpanded(numBooks))
SocketAuthority.emitter('author_updated', req.author.toJSONExpanded(numBooks))
}
res.json({
@ -190,7 +192,7 @@ class AuthorController {
var numBooks = this.db.libraryItems.filter(li => {
return li.media.metadata.hasAuthor && li.media.metadata.hasAuthor(req.author.id)
}).length
this.emitter('author_updated', req.author.toJSONExpanded(numBooks))
SocketAuthority.emitter('author_updated', req.author.toJSONExpanded(numBooks))
}
res.json({

View File

@ -3,32 +3,29 @@ const Logger = require('../Logger')
class BackupController {
constructor() { }
async create(req, res) {
if (!req.user.isAdminOrUp) {
Logger.error(`[BackupController] Non-admin user attempting to craete backup`, req.user)
return res.sendStatus(403)
}
getAll(req, res) {
res.json({
backups: this.backupManager.backups.map(b => b.toJSON())
})
}
create(req, res) {
this.backupManager.requestCreateBackup(res)
}
async delete(req, res) {
if (!req.user.isAdminOrUp) {
Logger.error(`[BackupController] Non-admin user attempting to delete backup`, req.user)
return res.sendStatus(403)
}
var backup = this.backupManager.backups.find(b => b.id === req.params.id)
if (!backup) {
return res.sendStatus(404)
}
await this.backupManager.removeBackup(backup)
res.json(this.backupManager.backups.map(b => b.toJSON()))
res.json({
backups: this.backupManager.backups.map(b => b.toJSON())
})
}
async upload(req, res) {
if (!req.user.isAdminOrUp) {
Logger.error(`[BackupController] Non-admin user attempting to upload backup`, req.user)
return res.sendStatus(403)
}
if (!req.files.file) {
Logger.error('[BackupController] Upload backup invalid')
return res.sendStatus(500)
@ -37,10 +34,6 @@ class BackupController {
}
async apply(req, res) {
if (!req.user.isAdminOrUp) {
Logger.error(`[BackupController] Non-admin user attempting to apply backup`, req.user)
return res.sendStatus(403)
}
var backup = this.backupManager.backups.find(b => b.id === req.params.id)
if (!backup) {
return res.sendStatus(404)
@ -48,5 +41,13 @@ class BackupController {
await this.backupManager.requestApplyBackup(backup)
res.sendStatus(200)
}
middleware(req, res, next) {
if (!req.user.isAdminOrUp) {
Logger.error(`[BackupController] Non-admin user attempting to access backups`, req.user)
return res.sendStatus(403)
}
next()
}
}
module.exports = new BackupController()

View File

@ -1,4 +1,6 @@
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const Collection = require('../objects/Collection')
class CollectionController {
@ -13,7 +15,7 @@ class CollectionController {
}
var jsonExpanded = newCollection.toJSONExpanded(this.db.libraryItems)
await this.db.insertEntity('collection', newCollection)
this.emitter('collection_added', jsonExpanded)
SocketAuthority.emitter('collection_added', jsonExpanded)
res.json(jsonExpanded)
}
@ -32,7 +34,7 @@ class CollectionController {
var jsonExpanded = collection.toJSONExpanded(this.db.libraryItems)
if (wasUpdated) {
await this.db.updateEntity('collection', collection)
this.emitter('collection_updated', jsonExpanded)
SocketAuthority.emitter('collection_updated', jsonExpanded)
}
res.json(jsonExpanded)
}
@ -41,7 +43,7 @@ class CollectionController {
const collection = req.collection
var jsonExpanded = collection.toJSONExpanded(this.db.libraryItems)
await this.db.removeEntity('collection', collection.id)
this.emitter('collection_removed', jsonExpanded)
SocketAuthority.emitter('collection_removed', jsonExpanded)
res.sendStatus(200)
}
@ -60,7 +62,7 @@ class CollectionController {
collection.addBook(req.body.id)
var jsonExpanded = collection.toJSONExpanded(this.db.libraryItems)
await this.db.updateEntity('collection', collection)
this.emitter('collection_updated', jsonExpanded)
SocketAuthority.emitter('collection_updated', jsonExpanded)
res.json(jsonExpanded)
}
@ -71,7 +73,7 @@ class CollectionController {
collection.removeBook(req.params.bookId)
var jsonExpanded = collection.toJSONExpanded(this.db.libraryItems)
await this.db.updateEntity('collection', collection)
this.emitter('collection_updated', jsonExpanded)
SocketAuthority.emitter('collection_updated', jsonExpanded)
}
res.json(collection.toJSONExpanded(this.db.libraryItems))
}
@ -92,7 +94,7 @@ class CollectionController {
}
if (hasUpdated) {
await this.db.updateEntity('collection', collection)
this.emitter('collection_updated', collection.toJSONExpanded(this.db.libraryItems))
SocketAuthority.emitter('collection_updated', collection.toJSONExpanded(this.db.libraryItems))
}
res.json(collection.toJSONExpanded(this.db.libraryItems))
}
@ -113,7 +115,7 @@ class CollectionController {
}
if (hasUpdated) {
await this.db.updateEntity('collection', collection)
this.emitter('collection_updated', collection.toJSONExpanded(this.db.libraryItems))
SocketAuthority.emitter('collection_updated', collection.toJSONExpanded(this.db.libraryItems))
}
res.json(collection.toJSONExpanded(this.db.libraryItems))
}

View File

@ -2,6 +2,7 @@ const Path = require('path')
const fs = require('../libs/fsExtra')
const filePerms = require('../utils/filePerms')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const Library = require('../objects/Library')
const libraryHelpers = require('../utils/libraryHelpers')
const { sort, createNewSortInstance } = require('../libs/fastSort')
@ -43,7 +44,7 @@ class LibraryController {
library.setData(newLibraryPayload)
await this.db.insertEntity('library', library)
// TODO: Only emit to users that have access
this.emitter('library_added', library.toJSON())
SocketAuthority.emitter('library_added', library.toJSON())
// Add library watcher
this.watcher.addLibrary(library)
@ -120,7 +121,7 @@ class LibraryController {
}
}
await this.db.updateEntity('library', library)
this.emitter('library_updated', library.toJSON())
SocketAuthority.emitter('library_updated', library.toJSON())
}
return res.json(library.toJSON())
}
@ -147,7 +148,7 @@ class LibraryController {
var libraryJson = library.toJSON()
await this.db.removeEntity('library', library.id)
this.emitter('library_removed', libraryJson)
SocketAuthority.emitter('library_removed', libraryJson)
return res.json(libraryJson)
}

View File

@ -1,4 +1,6 @@
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const { reqSupportsWebp, isNullOrNaN } = require('../utils/index')
const { ScanResult } = require('../utils/constants')
@ -53,7 +55,7 @@ class LibraryItemController {
if (hasUpdates) {
Logger.debug(`[LibraryItemController] Updated now saving`)
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
}
res.json(libraryItem.toJSON())
}
@ -97,7 +99,7 @@ class LibraryItemController {
Logger.debug(`[LibraryItemController] Updated library item media ${libraryItem.media.metadata.title}`)
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
}
res.json({
updated: hasUpdates,
@ -132,7 +134,7 @@ class LibraryItemController {
}
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
res.json({
success: true,
cover: result.cover
@ -152,7 +154,7 @@ class LibraryItemController {
}
if (validationResult.updated) {
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
}
res.json({
success: true,
@ -168,7 +170,7 @@ class LibraryItemController {
libraryItem.updateMediaCover('')
await this.cacheManager.purgeCoverCache(libraryItem.id)
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
}
res.sendStatus(200)
@ -228,7 +230,7 @@ class LibraryItemController {
}
libraryItem.media.updateAudioTracks(orderedFileData)
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
res.json(libraryItem.toJSON())
}
@ -284,7 +286,7 @@ class LibraryItemController {
if (hasUpdates) {
Logger.debug(`[LibraryItemController] Updated library item media ${libraryItem.media.metadata.title}`)
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
itemsUpdated++
}
}
@ -342,7 +344,7 @@ class LibraryItemController {
updates: itemsUpdated,
unmatched: itemsUnmatched
}
this.clientEmitter(req.user.id, 'batch_quickmatch_complete', result)
SocketAuthority.clientEmitter(req.user.id, 'batch_quickmatch_complete', result)
}
// DELETE: api/items/all
@ -410,7 +412,7 @@ class LibraryItemController {
const wasUpdated = req.libraryItem.media.updateChapters(chapters)
if (wasUpdated) {
await this.db.updateLibraryItem(req.libraryItem)
this.emitter('item_updated', req.libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', req.libraryItem.toJSONExpanded())
}
res.json({

View File

@ -1,4 +1,5 @@
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const { sort } = require('../libs/fastSort')
const { isObject, toNumber } = require('../utils/index')
@ -48,7 +49,7 @@ class MeController {
return res.sendStatus(200)
}
await this.db.updateEntity('user', req.user)
this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
res.sendStatus(200)
}
@ -62,7 +63,7 @@ class MeController {
var wasUpdated = req.user.createUpdateMediaProgress(libraryItem, req.body)
if (wasUpdated) {
await this.db.updateEntity('user', req.user)
this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
}
res.sendStatus(200)
}
@ -82,7 +83,7 @@ class MeController {
var wasUpdated = req.user.createUpdateMediaProgress(libraryItem, req.body, episodeId)
if (wasUpdated) {
await this.db.updateEntity('user', req.user)
this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
}
res.sendStatus(200)
}
@ -107,7 +108,7 @@ class MeController {
if (shouldUpdate) {
await this.db.updateEntity('user', req.user)
this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
}
res.sendStatus(200)
@ -120,7 +121,7 @@ class MeController {
const { time, title } = req.body
var bookmark = req.user.createBookmark(libraryItem.id, time, title)
await this.db.updateEntity('user', req.user)
this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
res.json(bookmark)
}
@ -136,7 +137,7 @@ class MeController {
var bookmark = req.user.updateBookmark(libraryItem.id, time, title)
if (!bookmark) return res.sendStatus(500)
await this.db.updateEntity('user', req.user)
this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
res.json(bookmark)
}
@ -153,7 +154,7 @@ class MeController {
}
req.user.removeBookmark(libraryItem.id, time)
await this.db.updateEntity('user', req.user)
this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
res.sendStatus(200)
}
@ -233,7 +234,7 @@ class MeController {
Logger.debug(`[MeController] syncLocalMediaProgress server updates = ${numServerProgressUpdates}, local updates = ${updatedLocalMediaProgress.length}`)
if (numServerProgressUpdates > 0) {
await this.db.updateEntity('user', req.user)
this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
}
res.json({
@ -288,7 +289,7 @@ class MeController {
const hasUpdated = req.user.addSeriesToHideFromContinueListening(req.params.id)
if (hasUpdated) {
await this.db.updateEntity('user', req.user)
this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
}
res.json(req.user.toJSONForBrowser())
}
@ -304,7 +305,7 @@ class MeController {
const hasUpdated = req.user.removeSeriesFromHideFromContinueListening(req.params.id)
if (hasUpdated) {
await this.db.updateEntity('user', req.user)
this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
}
res.json(req.user.toJSONForBrowser())
}
@ -314,7 +315,7 @@ class MeController {
const hasUpdated = req.user.removeProgressFromContinueListening(req.params.id)
if (hasUpdated) {
await this.db.updateEntity('user', req.user)
this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
}
res.json(req.user.toJSONForBrowser())
}

View File

@ -1,11 +1,14 @@
const axios = require('axios')
const fs = require('../libs/fsExtra')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const fs = require('../libs/fsExtra')
const { getPodcastFeed, findMatchingEpisodes } = require('../utils/podcastUtils')
const LibraryItem = require('../objects/LibraryItem')
const { getFileTimestampsWithIno } = require('../utils/fileUtils')
const filePerms = require('../utils/filePerms')
const LibraryItem = require('../objects/LibraryItem')
class PodcastController {
async create(req, res) {
@ -75,7 +78,7 @@ class PodcastController {
}
await this.db.insertLibraryItem(libraryItem)
this.emitter('item_added', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_added', libraryItem.toJSONExpanded())
res.json(libraryItem.toJSONExpanded())
@ -194,7 +197,7 @@ class PodcastController {
var wasUpdated = libraryItem.media.updateEpisode(episodeId, req.body)
if (wasUpdated) {
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
}
res.json(libraryItem.toJSONExpanded())
@ -229,7 +232,7 @@ class PodcastController {
}
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
res.json(libraryItem.toJSON())
}

View File

@ -1,4 +1,5 @@
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
class SeriesController {
constructor() { }
@ -38,7 +39,7 @@ class SeriesController {
const hasUpdated = req.series.update(req.body)
if (hasUpdated) {
await this.db.updateEntity('series', req.series)
this.emitter('series_updated', req.series)
SocketAuthority.emitter('series_updated', req.series)
}
res.json(req.series)
}

View File

@ -1,4 +1,6 @@
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const User = require('../objects/user/User')
const { getId, toNumber } = require('../utils/index')
@ -44,7 +46,7 @@ class UserController {
var newUser = new User(account)
var success = await this.db.insertEntity('user', newUser)
if (success) {
this.clientEmitter(req.user.id, 'user_added', newUser)
SocketAuthority.clientEmitter(req.user.id, 'user_added', newUser)
res.json({
user: newUser.toJSONForBrowser()
})
@ -85,7 +87,7 @@ class UserController {
Logger.info(`[UserController] User ${user.username} was generated a new api token`)
}
await this.db.updateEntity('user', user)
this.clientEmitter(req.user.id, 'user_updated', user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', user.toJSONForBrowser())
}
res.json({
@ -109,7 +111,7 @@ class UserController {
var userJson = user.toJSONForBrowser()
await this.db.removeEntity('user', user.id)
this.clientEmitter(req.user.id, 'user_removed', userJson)
SocketAuthority.clientEmitter(req.user.id, 'user_removed', userJson)
res.json({
success: true
})
@ -170,7 +172,7 @@ class UserController {
if (progressPurged) {
Logger.info(`[UserController] Purged ${progressPurged} media progress for user ${user.username}`)
await this.db.updateEntity('user', user)
this.clientEmitter(req.user.id, 'user_updated', user.toJSONForBrowser())
SocketAuthority.clientEmitter(req.user.id, 'user_updated', user.toJSONForBrowser())
}
res.json(this.userJsonWithItemProgressDetails(user, !req.user.isRoot))
@ -181,10 +183,9 @@ class UserController {
if (!req.user.isAdminOrUp) {
return res.sendStatus(403)
}
const usersOnline = this.getUsersOnline()
res.json({
usersOnline,
usersOnline: SocketAuthority.getUsersOnline(),
openSessions: this.playbackSessionManager.sessions
})
}

View File

@ -10,10 +10,9 @@ const { writeConcatFile } = require('../utils/ffmpegHelpers')
const toneHelpers = require('../utils/toneHelpers')
class AbMergeManager {
constructor(db, taskManager, clientEmitter) {
constructor(db, taskManager) {
this.db = db
this.taskManager = taskManager
this.clientEmitter = clientEmitter
this.itemsCacheDir = Path.join(global.MetadataPath, 'cache/items')
this.downloadDirPath = Path.join(global.MetadataPath, 'downloads')

View File

@ -1,18 +1,20 @@
const Path = require('path')
const fs = require('../libs/fsExtra')
const workerThreads = require('worker_threads')
const SocketAuthority = require('../SocketAuthority')
const Logger = require('../Logger')
const fs = require('../libs/fsExtra')
const filePerms = require('../utils/filePerms')
const { secondsToTimestamp } = require('../utils/index')
const { writeMetadataFile } = require('../utils/ffmpegHelpers')
const toneHelpers = require('../utils/toneHelpers')
class AudioMetadataMangaer {
constructor(db, taskManager, emitter, clientEmitter) {
constructor(db, taskManager) {
this.db = db
this.taskManager = taskManager
this.emitter = emitter
this.clientEmitter = clientEmitter
}
updateMetadataForItem(user, libraryItem, useTone, forceEmbedChapters) {
@ -40,7 +42,7 @@ class AudioMetadataMangaer {
audioFiles: audioFiles.map(af => ({ index: af.index, ino: af.ino, filename: af.metadata.filename }))
}
this.emitter('audio_metadata_started', itemAudioMetadataPayload)
SocketAuthority.emitter('audio_metadata_started', itemAudioMetadataPayload)
// Write chapters file
var toneJsonPath = null
@ -67,7 +69,7 @@ class AudioMetadataMangaer {
itemAudioMetadataPayload.results = results
itemAudioMetadataPayload.elapsed = elapsed
itemAudioMetadataPayload.finishedAt = Date.now()
this.emitter('audio_metadata_finished', itemAudioMetadataPayload)
SocketAuthority.emitter('audio_metadata_finished', itemAudioMetadataPayload)
}
async updateAudioFileMetadataWithTone(libraryItemId, audioFile, toneJsonPath, itemCacheDir) {
@ -77,7 +79,7 @@ class AudioMetadataMangaer {
ino: audioFile.ino,
filename: audioFile.metadata.filename
}
this.emitter('audiofile_metadata_started', resultPayload)
SocketAuthority.emitter('audiofile_metadata_started', resultPayload)
// Backup audio file
try {
@ -98,7 +100,7 @@ class AudioMetadataMangaer {
Logger.info(`[AudioMetadataManager] Successfully tagged audio file "${audioFile.metadata.path}"`)
}
this.emitter('audiofile_metadata_finished', resultPayload)
SocketAuthority.emitter('audiofile_metadata_finished', resultPayload)
return resultPayload
}
@ -115,7 +117,7 @@ class AudioMetadataMangaer {
audioFiles: audioFiles.map(af => ({ index: af.index, ino: af.ino, filename: af.metadata.filename }))
}
this.emitter('audio_metadata_started', itemAudioMetadataPayload)
SocketAuthority.emitter('audio_metadata_started', itemAudioMetadataPayload)
var downloadsPath = Path.join(global.MetadataPath, 'downloads')
var outputDir = Path.join(downloadsPath, libraryItem.id)
@ -143,7 +145,7 @@ class AudioMetadataMangaer {
itemAudioMetadataPayload.results = results
itemAudioMetadataPayload.elapsed = elapsed
itemAudioMetadataPayload.finishedAt = Date.now()
this.emitter('audio_metadata_finished', itemAudioMetadataPayload)
SocketAuthority.emitter('audio_metadata_finished', itemAudioMetadataPayload)
}
updateAudioFileMetadataWithFfmpeg(libraryItemId, audioFile, outputDir, metadataFilePath, coverPath = '') {
@ -154,7 +156,7 @@ class AudioMetadataMangaer {
ino: audioFile.ino,
filename: audioFile.metadata.filename
}
this.emitter('audiofile_metadata_started', resultPayload)
SocketAuthority.emitter('audiofile_metadata_started', resultPayload)
Logger.debug(`[AudioFileMetadataManager] Starting audio file metadata encode for "${audioFile.metadata.filename}"`)
@ -229,19 +231,19 @@ class AudioMetadataMangaer {
Logger.debug(`[AudioFileMetadataManager] Audio file replaced successfully "${inputPath}"`)
resultPayload.success = true
this.emitter('audiofile_metadata_finished', resultPayload)
SocketAuthority.emitter('audiofile_metadata_finished', resultPayload)
resolve(resultPayload)
}).catch((error) => {
Logger.error(`[AudioFileMetadataManager] Audio file failed to move "${inputPath}"`, error)
resultPayload.success = false
this.emitter('audiofile_metadata_finished', resultPayload)
SocketAuthority.emitter('audiofile_metadata_finished', resultPayload)
resolve(resultPayload)
})
} else {
Logger.debug(`[AudioFileMetadataManager] Metadata encode FAILED for "${audioFile.metadata.filename}"`)
resultPayload.success = false
this.emitter('audiofile_metadata_finished', resultPayload)
SocketAuthority.emitter('audiofile_metadata_finished', resultPayload)
resolve(resultPayload)
}
} else if (message.type === 'FFMPEG') {

View File

@ -1,4 +1,6 @@
const Path = require('path')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const cron = require('../libs/nodeCron')
const fs = require('../libs/fsExtra')
@ -8,18 +10,16 @@ const StreamZip = require('../libs/nodeStreamZip')
// Utils
const { getFileSize } = require('../utils/fileUtils')
const filePerms = require('../utils/filePerms')
const Logger = require('../Logger')
const Backup = require('../objects/Backup')
class BackupManager {
constructor(db, emitter) {
constructor(db) {
this.BackupPath = Path.join(global.MetadataPath, 'backups')
this.ItemsMetadataPath = Path.join(global.MetadataPath, 'items')
this.AuthorsMetadataPath = Path.join(global.MetadataPath, 'authors')
this.db = db
this.emitter = emitter
this.scheduleTask = null
@ -106,13 +106,20 @@ class BackupManager {
this.backups.push(backup)
}
return res.json(this.backups.map(b => b.toJSON()))
res.json({
backups: this.backups.map(b => b.toJSON())
})
}
async requestCreateBackup(res) {
var backupSuccess = await this.runBackup()
if (backupSuccess) res.json(this.backups.map(b => b.toJSON()))
else res.sendStatus(500)
if (backupSuccess) {
res.json({
backups: this.backups.map(b => b.toJSON())
})
} else {
res.sendStatus(500)
}
}
async requestApplyBackup(backup) {
@ -123,7 +130,7 @@ class BackupManager {
await zip.extract('metadata-authors/', this.AuthorsMetadataPath)
}
await this.db.reinit()
this.emitter('backup_applied')
SocketAuthority.emitter('backup_applied')
}
async loadBackups() {

View File

@ -1,11 +1,11 @@
const axios = require('axios')
const Logger = require("../Logger")
const SocketAuthority = require('../SocketAuthority')
const { notificationData } = require('../utils/notifications')
class NotificationManager {
constructor(db, emitter) {
constructor(db) {
this.db = db
this.emitter = emitter
this.sendingNotification = false
this.notificationQueue = []
@ -58,7 +58,7 @@ class NotificationManager {
}
await this.db.updateEntity('settings', this.db.notificationSettings)
this.emitter('notifications_updated', this.db.notificationSettings)
SocketAuthority.emitter('notifications_updated', this.db.notificationSettings)
this.notificationFinished()
}

View File

@ -1,22 +1,24 @@
const Path = require('path')
const date = require('../libs/dateAndTime')
const serverVersion = require('../../package.json').version
const { PlayMethod } = require('../utils/constants')
const PlaybackSession = require('../objects/PlaybackSession')
const DeviceInfo = require('../objects/DeviceInfo')
const Stream = require('../objects/Stream')
const Logger = require('../Logger')
const fs = require('../libs/fsExtra')
const SocketAuthority = require('../SocketAuthority')
const date = require('../libs/dateAndTime')
const fs = require('../libs/fsExtra')
const uaParserJs = require('../libs/uaParser')
const requestIp = require('../libs/requestIp')
const { PlayMethod } = require('../utils/constants')
const PlaybackSession = require('../objects/PlaybackSession')
const DeviceInfo = require('../objects/DeviceInfo')
const Stream = require('../objects/Stream')
class PlaybackSessionManager {
constructor(db, emitter, clientEmitter) {
constructor(db) {
this.db = db
this.StreamsPath = Path.join(global.MetadataPath, 'streams')
this.emitter = emitter
this.clientEmitter = clientEmitter
this.sessions = []
this.localSessionLock = {}
@ -98,7 +100,7 @@ class PlaybackSessionManager {
if (wasUpdated) {
await this.db.updateEntity('user', user)
var itemProgress = user.getMediaProgress(session.libraryItemId, session.episodeId)
this.clientEmitter(user.id, 'user_item_progress_updated', {
SocketAuthority.clientEmitter(user.id, 'user_item_progress_updated', {
id: itemProgress.id,
data: itemProgress.toJSON()
})
@ -147,7 +149,7 @@ class PlaybackSessionManager {
newPlaybackSession.playMethod = PlayMethod.DIRECTPLAY
} else {
Logger.debug(`[PlaybackSessionManager] "${user.username}" starting stream session for item "${libraryItem.id}"`)
var stream = new Stream(newPlaybackSession.id, this.StreamsPath, user, libraryItem, episodeId, userStartTime, this.clientEmitter.bind(this))
var stream = new Stream(newPlaybackSession.id, this.StreamsPath, user, libraryItem, episodeId, userStartTime)
await stream.generatePlaylist()
stream.start() // Start transcode
@ -167,7 +169,7 @@ class PlaybackSessionManager {
user.currentSessionId = newPlaybackSession.id
this.sessions.push(newPlaybackSession)
this.emitter('user_stream_update', user.toJSONForPublic(this.sessions, this.db.libraryItems))
SocketAuthority.adminEmitter('user_stream_update', user.toJSONForPublic(this.sessions, this.db.libraryItems))
return newPlaybackSession
}
@ -193,7 +195,7 @@ class PlaybackSessionManager {
await this.db.updateEntity('user', user)
var itemProgress = user.getMediaProgress(session.libraryItemId, session.episodeId)
this.clientEmitter(user.id, 'user_item_progress_updated', {
SocketAuthority.clientEmitter(user.id, 'user_item_progress_updated', {
id: itemProgress.id,
data: itemProgress.toJSON()
})
@ -211,7 +213,7 @@ class PlaybackSessionManager {
await this.saveSession(session)
}
Logger.debug(`[PlaybackSessionManager] closeSession "${session.id}"`)
this.emitter('user_stream_update', user.toJSONForPublic(this.sessions, this.db.libraryItems))
SocketAuthority.adminEmitter('user_stream_update', user.toJSONForPublic(this.sessions, this.db.libraryItems))
return this.removeSession(session.id)
}

View File

@ -1,23 +1,24 @@
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const fs = require('../libs/fsExtra')
const { getPodcastFeed } = require('../utils/podcastUtils')
const Logger = require('../Logger')
const { downloadFile, removeFile } = require('../utils/fileUtils')
const filePerms = require('../utils/filePerms')
const { levenshteinDistance } = require('../utils/index')
const opmlParser = require('../utils/parsers/parseOPML')
const prober = require('../utils/prober')
const LibraryFile = require('../objects/files/LibraryFile')
const PodcastEpisodeDownload = require('../objects/PodcastEpisodeDownload')
const PodcastEpisode = require('../objects/entities/PodcastEpisode')
const AudioFile = require('../objects/files/AudioFile')
class PodcastManager {
constructor(db, watcher, emitter, notificationManager) {
constructor(db, watcher, notificationManager) {
this.db = db
this.watcher = watcher
this.emitter = emitter
this.notificationManager = notificationManager
this.downloadQueue = []
@ -63,11 +64,11 @@ class PodcastManager {
async startPodcastEpisodeDownload(podcastEpisodeDownload) {
if (this.currentDownload) {
this.downloadQueue.push(podcastEpisodeDownload)
this.emitter('episode_download_queued', podcastEpisodeDownload.toJSONForClient())
SocketAuthority.emitter('episode_download_queued', podcastEpisodeDownload.toJSONForClient())
return
}
this.emitter('episode_download_started', podcastEpisodeDownload.toJSONForClient())
SocketAuthority.emitter('episode_download_started', podcastEpisodeDownload.toJSONForClient())
this.currentDownload = podcastEpisodeDownload
// Ignores all added files to this dir
@ -97,7 +98,7 @@ class PodcastManager {
this.currentDownload.setFinished(false)
}
this.emitter('episode_download_finished', this.currentDownload.toJSONForClient())
SocketAuthority.emitter('episode_download_finished', this.currentDownload.toJSONForClient())
this.watcher.removeIgnoreDir(this.currentDownload.libraryItem.path)
this.currentDownload = null
@ -141,7 +142,7 @@ class PodcastManager {
libraryItem.updatedAt = Date.now()
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
if (this.currentDownload.isAutoDownload) { // Notifications only for auto downloaded episodes
this.notificationManager.onPodcastEpisodeDownloaded(libraryItem, podcastEpisode)
@ -230,7 +231,7 @@ class PodcastManager {
libraryItem.media.lastEpisodeCheck = Date.now()
libraryItem.updatedAt = Date.now()
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
return libraryItem.media.autoDownloadEpisodes
}
@ -269,7 +270,7 @@ class PodcastManager {
libraryItem.media.lastEpisodeCheck = Date.now()
libraryItem.updatedAt = Date.now()
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
return newEpisodes
}

View File

@ -1,13 +1,15 @@
const Path = require('path')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const fs = require('../libs/fsExtra')
const Feed = require('../objects/Feed')
const Logger = require('../Logger')
// Not functional at the moment
class RssFeedManager {
constructor(db, emitter) {
constructor(db) {
this.db = db
this.emitter = emitter
this.feeds = {}
}
@ -104,7 +106,7 @@ class RssFeedManager {
Logger.debug(`[RssFeedManager] Opened RSS feed ${feed.feedUrl}`)
await this.db.insertEntity('feed', feed)
this.emitter('rss_feed_open', { id: feed.id, entityType: feed.entityType, entityId: feed.entityId, feedUrl: feed.feedUrl })
SocketAuthority.emitter('rss_feed_open', { id: feed.id, entityType: feed.entityType, entityId: feed.entityId, feedUrl: feed.feedUrl })
return feed
}
@ -118,7 +120,7 @@ class RssFeedManager {
if (!this.feeds[id]) return
var feed = this.feeds[id]
await this.db.removeEntity('feed', id)
this.emitter('rss_feed_closed', { id: feed.id, entityType: feed.entityType, entityId: feed.entityId, feedUrl: feed.feedUrl })
SocketAuthority.emitter('rss_feed_closed', { id: feed.id, entityType: feed.entityType, entityId: feed.entityId, feedUrl: feed.feedUrl })
delete this.feeds[id]
Logger.info(`[RssFeedManager] Closed RSS feed "${feed.feedUrl}"`)
}

View File

@ -1,19 +1,19 @@
class TaskManager {
constructor(emitter) {
this.emitter = emitter
const SocketAuthority = require('../SocketAuthority')
class TaskManager {
constructor() {
this.tasks = []
}
addTask(task) {
this.tasks.push(task)
this.emitter('task_started', task.toJSON())
SocketAuthority.emitter('task_started', task.toJSON())
}
taskFinished(task) {
if (this.tasks.some(t => t.id === task.id)) {
this.tasks = this.tasks.filter(t => t.id !== task.id)
this.emitter('task_finished', task.toJSON())
SocketAuthority.emitter('task_finished', task.toJSON())
}
}
}

View File

@ -1,8 +1,12 @@
const Ffmpeg = require('../libs/fluentFfmpeg')
const EventEmitter = require('events')
const Path = require('path')
const fs = require('../libs/fsExtra')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const fs = require('../libs/fsExtra')
const Ffmpeg = require('../libs/fluentFfmpeg')
const { secondsToTimestamp } = require('../utils/index')
const { writeConcatFile } = require('../utils/ffmpegHelpers')
const { AudioMimeType } = require('../utils/constants')
@ -10,14 +14,13 @@ const hlsPlaylistGenerator = require('../utils/hlsPlaylistGenerator')
const AudioTrack = require('./files/AudioTrack')
class Stream extends EventEmitter {
constructor(sessionId, streamPath, user, libraryItem, episodeId, startTime, clientEmitter, transcodeOptions = {}) {
constructor(sessionId, streamPath, user, libraryItem, episodeId, startTime, transcodeOptions = {}) {
super()
this.id = sessionId
this.user = user
this.libraryItem = libraryItem
this.episodeId = episodeId
this.clientEmitter = clientEmitter
this.transcodeOptions = transcodeOptions
@ -408,7 +411,7 @@ class Stream extends EventEmitter {
}
clientEmit(evtName, data) {
if (this.clientEmitter) this.clientEmitter(this.user.id, evtName, data)
SocketAuthority.clientEmitter(this.user.id, evtName, data)
}
getAudioTrack() {

View File

@ -1,8 +1,11 @@
const express = require('express')
const Path = require('path')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const fs = require('../libs/fsExtra')
const date = require('../libs/dateAndTime')
const Logger = require('../Logger')
const LibraryController = require('../controllers/LibraryController')
const UserController = require('../controllers/UserController')
@ -29,25 +32,22 @@ const Author = require('../objects/entities/Author')
const Series = require('../objects/entities/Series')
class ApiRouter {
constructor(db, auth, scanner, playbackSessionManager, abMergeManager, coverManager, backupManager, watcher, cacheManager, podcastManager, audioMetadataManager, rssFeedManager, cronManager, notificationManager, taskManager, getUsersOnline, emitter, clientEmitter) {
this.db = db
this.auth = auth
this.scanner = scanner
this.playbackSessionManager = playbackSessionManager
this.abMergeManager = abMergeManager
this.backupManager = backupManager
this.coverManager = coverManager
this.watcher = watcher
this.cacheManager = cacheManager
this.podcastManager = podcastManager
this.audioMetadataManager = audioMetadataManager
this.rssFeedManager = rssFeedManager
this.cronManager = cronManager
this.notificationManager = notificationManager
this.taskManager = taskManager
this.getUsersOnline = getUsersOnline
this.emitter = emitter
this.clientEmitter = clientEmitter
constructor(Server) {
this.db = Server.db
this.auth = Server.auth
this.scanner = Server.scanner
this.playbackSessionManager = Server.playbackSessionManager
this.abMergeManager = Server.abMergeManager
this.backupManager = Server.backupManager
this.coverManager = Server.coverManager
this.watcher = Server.watcher
this.cacheManager = Server.cacheManager
this.podcastManager = Server.podcastManager
this.audioMetadataManager = Server.audioMetadataManager
this.rssFeedManager = Server.rssFeedManager
this.cronManager = Server.cronManager
this.notificationManager = Server.notificationManager
this.taskManager = Server.taskManager
this.bookFinder = new BookFinder()
this.authorFinder = new AuthorFinder()
@ -163,10 +163,11 @@ class ApiRouter {
//
// Backup Routes
//
this.router.post('/backups', BackupController.create.bind(this))
this.router.delete('/backups/:id', BackupController.delete.bind(this))
this.router.get('/backups/:id/apply', BackupController.apply.bind(this))
this.router.post('/backups/upload', BackupController.upload.bind(this))
this.router.get('/backups', BackupController.middleware.bind(this), BackupController.getAll.bind(this))
this.router.post('/backups', BackupController.middleware.bind(this), BackupController.create.bind(this))
this.router.delete('/backups/:id', BackupController.middleware.bind(this), BackupController.delete.bind(this))
this.router.get('/backups/:id/apply', BackupController.middleware.bind(this), BackupController.apply.bind(this))
this.router.post('/backups/upload', BackupController.middleware.bind(this), BackupController.upload.bind(this))
//
// File System Routes
@ -261,13 +262,13 @@ class ApiRouter {
async getDirectories(dir, relpath, excludedDirs, level = 0) {
try {
var paths = await fs.readdir(dir)
const paths = await fs.readdir(dir)
var dirs = await Promise.all(paths.map(async dirname => {
var fullPath = Path.join(dir, dirname)
var path = Path.join(relpath, dirname)
let dirs = await Promise.all(paths.map(async dirname => {
const fullPath = Path.join(dir, dirname)
const path = Path.join(relpath, dirname)
var isDir = (await fs.lstat(fullPath)).isDirectory()
const isDir = (await fs.lstat(fullPath)).isDirectory()
if (isDir && !excludedDirs.includes(path) && dirname !== 'node_modules') {
return {
path,
@ -292,13 +293,13 @@ class ApiRouter {
// Helper Methods
//
userJsonWithItemProgressDetails(user, hideRootToken = false) {
var json = user.toJSONForBrowser()
const json = user.toJSONForBrowser()
if (json.type === 'root' && hideRootToken) {
json.token = ''
}
json.mediaProgress = json.mediaProgress.map(lip => {
var libraryItem = this.db.libraryItems.find(li => li.id === lip.libraryItemId)
const libraryItem = this.db.libraryItems.find(li => li.id === lip.libraryItemId)
if (!libraryItem) {
Logger.warn('[ApiRouter] Library item not found for users progress ' + lip.libraryItemId)
lip.media = null
@ -325,34 +326,21 @@ class ApiRouter {
async handleDeleteLibraryItem(libraryItem) {
// Remove libraryItem from users
for (let i = 0; i < this.db.users.length; i++) {
var user = this.db.users[i]
var madeUpdates = user.removeMediaProgressForLibraryItem(libraryItem.id)
if (madeUpdates) {
const user = this.db.users[i]
if (user.removeMediaProgressForLibraryItem(libraryItem.id)) {
await this.db.updateEntity('user', user)
}
}
// remove any streams open for this audiobook
// 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)
// }
// }
// TODO: Remove open sessions for library item
// remove book from collections
var collectionsWithBook = this.db.collections.filter(c => c.books.includes(libraryItem.id))
const collectionsWithBook = this.db.collections.filter(c => c.books.includes(libraryItem.id))
for (let i = 0; i < collectionsWithBook.length; i++) {
var collection = collectionsWithBook[i]
const collection = collectionsWithBook[i]
collection.removeBook(libraryItem.id)
await this.db.updateEntity('collection', collection)
this.clientEmitter(collection.userId, 'collection_updated', collection.toJSONExpanded(this.db.libraryItems))
SocketAuthority.clientEmitter(collection.userId, 'collection_updated', collection.toJSONExpanded(this.db.libraryItems))
}
// purge cover cache
@ -360,34 +348,32 @@ class ApiRouter {
await this.cacheManager.purgeCoverCache(libraryItem.id)
}
var json = libraryItem.toJSONExpanded()
await this.db.removeLibraryItem(libraryItem.id)
this.emitter('item_removed', json)
SocketAuthority.emitter('item_removed', libraryItem.toJSONExpanded())
}
async getUserListeningSessionsHelper(userId) {
var userSessions = await this.db.selectUserSessions(userId)
const userSessions = await this.db.selectUserSessions(userId)
return userSessions.sort((a, b) => b.updatedAt - a.updatedAt)
}
async getAllSessionsWithUserData() {
var sessions = await this.db.getAllSessions()
const sessions = await this.db.getAllSessions()
sessions.sort((a, b) => b.updatedAt - a.updatedAt)
return sessions.map(se => {
var user = this.db.users.find(u => u.id === se.userId)
var _se = {
const user = this.db.users.find(u => u.id === se.userId)
return {
...se,
user: user ? { id: user.id, username: user.username } : null
}
return _se
})
}
async getUserListeningStatsHelpers(userId) {
const today = date.format(new Date(), 'YYYY-MM-DD')
var listeningSessions = await this.getUserListeningSessionsHelper(userId)
var listeningStats = {
const listeningSessions = await this.getUserListeningSessionsHelper(userId)
const listeningStats = {
totalTime: 0,
items: {},
days: {},
@ -396,7 +382,7 @@ class ApiRouter {
recentSessions: listeningSessions.slice(0, 10)
}
listeningSessions.forEach((s) => {
var sessionTimeListening = s.timeListening
let sessionTimeListening = s.timeListening
if (typeof sessionTimeListening == 'string') {
sessionTimeListening = Number(sessionTimeListening)
}
@ -431,15 +417,15 @@ class ApiRouter {
async createAuthorsAndSeriesForItemUpdate(mediaPayload) {
if (mediaPayload.metadata) {
var mediaMetadata = mediaPayload.metadata
const mediaMetadata = mediaPayload.metadata
// Create new authors if in payload
if (mediaMetadata.authors && mediaMetadata.authors.length) {
// TODO: validate authors
var newAuthors = []
const newAuthors = []
for (let i = 0; i < mediaMetadata.authors.length; i++) {
if (mediaMetadata.authors[i].id.startsWith('new')) {
var author = this.db.authors.find(au => au.checkNameEquals(mediaMetadata.authors[i].name))
let author = this.db.authors.find(au => au.checkNameEquals(mediaMetadata.authors[i].name))
if (!author) {
author = new Author()
author.setData(mediaMetadata.authors[i])
@ -453,17 +439,17 @@ class ApiRouter {
}
if (newAuthors.length) {
await this.db.insertEntities('author', newAuthors)
this.emitter('authors_added', newAuthors)
SocketAuthority.emitter('authors_added', newAuthors)
}
}
// Create new series if in payload
if (mediaMetadata.series && mediaMetadata.series.length) {
// TODO: validate series
var newSeries = []
const newSeries = []
for (let i = 0; i < mediaMetadata.series.length; i++) {
if (mediaMetadata.series[i].id.startsWith('new')) {
var seriesItem = this.db.series.find(se => se.checkNameEquals(mediaMetadata.series[i].name))
let seriesItem = this.db.series.find(se => se.checkNameEquals(mediaMetadata.series[i].name))
if (!seriesItem) {
seriesItem = new Series()
seriesItem.setData(mediaMetadata.series[i])
@ -477,7 +463,7 @@ class ApiRouter {
}
if (newSeries.length) {
await this.db.insertEntities('series', newSeries)
this.emitter('authors_added', newSeries)
SocketAuthority.emitter('authors_added', newSeries)
}
}
}

View File

@ -1,14 +1,17 @@
const express = require('express')
const Path = require('path')
const fs = require('../libs/fsExtra')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const fs = require('../libs/fsExtra')
class HlsRouter {
constructor(db, auth, playbackSessionManager, emitter) {
constructor(db, auth, playbackSessionManager) {
this.db = db
this.auth = auth
this.playbackSessionManager = playbackSessionManager
this.emitter = emitter
this.router = express()
this.init()
@ -49,7 +52,7 @@ class HlsRouter {
if (startTimeForReset) {
// HLS.js will restart the stream at the new time
Logger.info(`[HlsRouter] Resetting Stream - notify client @${startTimeForReset}s`)
this.emitter('stream_reset', {
SocketAuthority.emitter('stream_reset', {
startTime: startTimeForReset,
streamId: stream.id
})

View File

@ -1,8 +1,9 @@
const fs = require('../libs/fsExtra')
const Path = require('path')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
// Utils
const Logger = require('../Logger')
const { groupFilesIntoLibraryItemPaths, getLibraryItemFileData, scanFolder } = require('../utils/scandir')
const { comparePaths } = require('../utils/index')
const { getIno } = require('../utils/fileUtils')
@ -20,12 +21,11 @@ const Author = require('../objects/entities/Author')
const Series = require('../objects/entities/Series')
class Scanner {
constructor(db, coverManager, emitter) {
constructor(db, coverManager) {
this.ScanLogPath = Path.posix.join(global.MetadataPath, 'logs', 'scans')
this.db = db
this.coverManager = coverManager
this.emitter = emitter
this.cancelLibraryScan = {}
this.librariesScanning = []
@ -113,7 +113,7 @@ class Scanner {
}
if (hasUpdated) {
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
await this.db.updateLibraryItem(libraryItem)
return ScanResult.UPDATED
}
@ -139,7 +139,7 @@ class Scanner {
libraryScan.verbose = false
this.librariesScanning.push(libraryScan.getScanEmitData)
this.emitter('scan_start', libraryScan.getScanEmitData)
SocketAuthority.emitter('scan_start', libraryScan.getScanEmitData)
Logger.info(`[Scanner] Starting library scan ${libraryScan.id} for ${libraryScan.libraryName}`)
@ -158,11 +158,11 @@ class Scanner {
if (canceled && !libraryScan.totalResults) {
var emitData = libraryScan.getScanEmitData
emitData.results = null
this.emitter('scan_complete', emitData)
SocketAuthority.emitter('scan_complete', emitData)
return
}
this.emitter('scan_complete', libraryScan.getScanEmitData)
SocketAuthority.emitter('scan_complete', libraryScan.getScanEmitData)
if (libraryScan.totalResults) {
libraryScan.saveLog(this.ScanLogPath)
@ -302,7 +302,7 @@ class Scanner {
async updateLibraryItemChunk(itemsToUpdate) {
await this.db.updateLibraryItems(itemsToUpdate)
this.emitter('items_updated', itemsToUpdate.map(li => li.toJSONExpanded()))
SocketAuthority.emitter('items_updated', itemsToUpdate.map(li => li.toJSONExpanded()))
}
async rescanLibraryItemDataChunk(itemDataToRescan, libraryScan) {
@ -320,7 +320,7 @@ class Scanner {
if (itemsUpdated.length) {
libraryScan.resultsUpdated += itemsUpdated.length
await this.db.updateLibraryItems(itemsUpdated)
this.emitter('items_updated', itemsUpdated.map(li => li.toJSONExpanded()))
SocketAuthority.emitter('items_updated', itemsUpdated.map(li => li.toJSONExpanded()))
}
}
@ -337,7 +337,7 @@ class Scanner {
libraryScan.resultsAdded += newLibraryItems.length
await this.db.insertLibraryItems(newLibraryItems)
this.emitter('items_added', newLibraryItems.map(li => li.toJSONExpanded()))
SocketAuthority.emitter('items_added', newLibraryItems.map(li => li.toJSONExpanded()))
}
async rescanLibraryItem(libraryItemCheckData, libraryScan) {
@ -458,7 +458,7 @@ class Scanner {
})
if (newAuthors.length) {
await this.db.insertEntities('author', newAuthors)
this.emitter('authors_added', newAuthors.map(au => au.toJSON()))
SocketAuthority.emitter('authors_added', newAuthors.map(au => au.toJSON()))
}
}
if (libraryItem.media.metadata.series.some(se => se.id.startsWith('new'))) {
@ -479,7 +479,7 @@ class Scanner {
})
if (newSeries.length) {
await this.db.insertEntities('series', newSeries)
this.emitter('series_added', newSeries.map(se => se.toJSON()))
SocketAuthority.emitter('series_added', newSeries.map(se => se.toJSON()))
}
}
}
@ -602,7 +602,7 @@ class Scanner {
Logger.info(`[Scanner] Scanning file update group and library item was deleted "${existingLibraryItem.media.metadata.title}" - marking as missing`)
existingLibraryItem.setMissing()
await this.db.updateLibraryItem(existingLibraryItem)
this.emitter('item_updated', existingLibraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', existingLibraryItem.toJSONExpanded())
itemGroupingResults[itemDir] = ScanResult.REMOVED
continue;
@ -629,7 +629,7 @@ class Scanner {
if (newLibraryItem) {
await this.createNewAuthorsAndSeries(newLibraryItem)
await this.db.insertLibraryItem(newLibraryItem)
this.emitter('item_added', newLibraryItem.toJSONExpanded())
SocketAuthority.emitter('item_added', newLibraryItem.toJSONExpanded())
}
itemGroupingResults[itemDir] = newLibraryItem ? ScanResult.ADDED : ScanResult.NOTHING
}
@ -747,7 +747,7 @@ class Scanner {
}
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
}
return {
@ -846,7 +846,7 @@ class Scanner {
author = new Author()
author.setData({ name: authorName })
await this.db.insertEntity('author', author)
this.emitter('author_added', author)
SocketAuthority.emitter('author_added', author)
}
authorPayload.push(author.toJSONMinimal())
}
@ -864,7 +864,7 @@ class Scanner {
seriesItem = new Series()
seriesItem.setData({ name: seriesMatchItem.series })
await this.db.insertEntity('series', seriesItem)
this.emitter('series_added', seriesItem)
SocketAuthority.emitter('series_added', seriesItem)
}
seriesPayload.push(seriesItem.toJSONMinimal(seriesMatchItem.sequence))
}
@ -955,7 +955,7 @@ class Scanner {
var libraryScan = new LibraryScan()
libraryScan.setData(library, null, 'match')
this.librariesScanning.push(libraryScan.getScanEmitData)
this.emitter('scan_start', libraryScan.getScanEmitData)
SocketAuthority.emitter('scan_start', libraryScan.getScanEmitData)
Logger.info(`[Scanner] matchLibraryItems: Starting library match scan ${libraryScan.id} for ${libraryScan.libraryName}`)
@ -987,14 +987,14 @@ class Scanner {
delete this.cancelLibraryScan[libraryScan.libraryId]
var scanData = libraryScan.getScanEmitData
scanData.results = false
this.emitter('scan_complete', scanData)
SocketAuthority.emitter('scan_complete', scanData)
this.librariesScanning = this.librariesScanning.filter(ls => ls.id !== library.id)
return
}
}
this.librariesScanning = this.librariesScanning.filter(ls => ls.id !== library.id)
this.emitter('scan_complete', libraryScan.getScanEmitData)
SocketAuthority.emitter('scan_complete', libraryScan.getScanEmitData)
}
probeAudioFileWithTone(audioFile) {