mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-16 19:08:42 +01:00
Clean up ApiRouter adding MiscController, move upload and scan to api endpoints
This commit is contained in:
parent
69fcb103e4
commit
57399bb79e
@ -155,7 +155,7 @@ export default {
|
|||||||
},
|
},
|
||||||
settingsUpdated(settings) {},
|
settingsUpdated(settings) {},
|
||||||
scan() {
|
scan() {
|
||||||
this.$root.socket.emit('scan', this.$store.state.libraries.currentLibraryId)
|
this.$store.dispatch('libraries/requestLibraryScan', { libraryId: this.$store.state.libraries.currentLibraryId })
|
||||||
},
|
},
|
||||||
libraryItemAdded(libraryItem) {
|
libraryItemAdded(libraryItem) {
|
||||||
console.log('libraryItem added', libraryItem)
|
console.log('libraryItem added', libraryItem)
|
||||||
|
@ -563,7 +563,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
scan() {
|
scan() {
|
||||||
this.$root.socket.emit('scan', this.currentLibraryId)
|
this.$store.dispatch('libraries/requestLibraryScan', { libraryId: this.currentLibraryId })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -419,23 +419,28 @@ export default {
|
|||||||
toast.error(`Failed to mark as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`)
|
toast.error(`Failed to mark as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
itemScanComplete(result) {
|
|
||||||
this.rescanning = false
|
|
||||||
var toast = this.$toast || this.$nuxt.$toast
|
|
||||||
if (!result) {
|
|
||||||
toast.error(`Re-Scan Failed for "${this.title}"`)
|
|
||||||
} else if (result === 'UPDATED') {
|
|
||||||
toast.success(`Re-Scan complete audiobook was updated`)
|
|
||||||
} else if (result === 'UPTODATE') {
|
|
||||||
toast.success(`Re-Scan complete audiobook was up to date`)
|
|
||||||
} else if (result === 'REMOVED') {
|
|
||||||
toast.error(`Re-Scan complete audiobook was removed`)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rescan() {
|
rescan() {
|
||||||
this.rescanning = true
|
this.rescanning = true
|
||||||
this._socket.once('item_scan_complete', this.itemScanComplete)
|
this.$axios
|
||||||
this._socket.emit('scan_item', this.libraryItemId)
|
.$get(`/api/items/${this.libraryItemId}/scan`)
|
||||||
|
.then((data) => {
|
||||||
|
this.rescanning = false
|
||||||
|
var result = data.result
|
||||||
|
if (!result) {
|
||||||
|
this.$toast.error(`Re-Scan Failed for "${this.title}"`)
|
||||||
|
} else if (result === 'UPDATED') {
|
||||||
|
this.$toast.success(`Re-Scan complete item was updated`)
|
||||||
|
} else if (result === 'UPTODATE') {
|
||||||
|
this.$toast.success(`Re-Scan complete item was up to date`)
|
||||||
|
} else if (result === 'REMOVED') {
|
||||||
|
this.$toast.error(`Re-Scan complete item was removed`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to scan library item', error)
|
||||||
|
this.$toast.error('Failed to scan library item')
|
||||||
|
this.rescanning = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
showEditModalTracks() {
|
showEditModalTracks() {
|
||||||
// More menu func
|
// More menu func
|
||||||
|
@ -117,8 +117,13 @@ export default {
|
|||||||
this.quickMatching = false
|
this.quickMatching = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
libraryScanComplete(result) {
|
rescan() {
|
||||||
|
this.rescanning = true
|
||||||
|
this.$axios
|
||||||
|
.$get(`/api/items/${this.libraryItemId}/scan`)
|
||||||
|
.then((data) => {
|
||||||
this.rescanning = false
|
this.rescanning = false
|
||||||
|
var result = data.result
|
||||||
if (!result) {
|
if (!result) {
|
||||||
this.$toast.error(`Re-Scan Failed for "${this.title}"`)
|
this.$toast.error(`Re-Scan Failed for "${this.title}"`)
|
||||||
} else if (result === 'UPDATED') {
|
} else if (result === 'UPDATED') {
|
||||||
@ -128,11 +133,12 @@ export default {
|
|||||||
} else if (result === 'REMOVED') {
|
} else if (result === 'REMOVED') {
|
||||||
this.$toast.error(`Re-Scan complete item was removed`)
|
this.$toast.error(`Re-Scan complete item was removed`)
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
rescan() {
|
.catch((error) => {
|
||||||
this.rescanning = true
|
console.error('Failed to scan library item', error)
|
||||||
this.$root.socket.once('item_scan_complete', this.libraryScanComplete)
|
this.$toast.error('Failed to scan library item')
|
||||||
this.$root.socket.emit('scan_item', this.libraryItemId)
|
this.rescanning = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
saveMetadataComplete(result) {
|
saveMetadataComplete(result) {
|
||||||
this.savingMetadata = false
|
this.savingMetadata = false
|
||||||
|
@ -76,10 +76,10 @@ export default {
|
|||||||
this.$emit('edit', this.library)
|
this.$emit('edit', this.library)
|
||||||
},
|
},
|
||||||
scan() {
|
scan() {
|
||||||
this.$root.socket.emit('scan', this.library.id)
|
this.$store.dispatch('libraries/requestLibraryScan', { libraryId: this.library.id })
|
||||||
},
|
},
|
||||||
forceScan() {
|
forceScan() {
|
||||||
this.$root.socket.emit('scan', this.library.id, { forceRescan: true })
|
this.$store.dispatch('libraries/requestLibraryScan', { libraryId: this.library.id, force: 1 })
|
||||||
},
|
},
|
||||||
deleteClick() {
|
deleteClick() {
|
||||||
if (this.isMain) return
|
if (this.isMain) return
|
||||||
|
@ -232,7 +232,7 @@ export default {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return this.$axios
|
return this.$axios
|
||||||
.$post('/upload', form)
|
.$post('/api/upload', form)
|
||||||
.then(() => true)
|
.then(() => true)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Failed', error)
|
console.error('Failed', error)
|
||||||
|
@ -46,7 +46,7 @@ export const actions = {
|
|||||||
var updatePayload = {
|
var updatePayload = {
|
||||||
...payload
|
...payload
|
||||||
}
|
}
|
||||||
return this.$axios.$patch('/api/serverSettings', updatePayload).then((result) => {
|
return this.$axios.$patch('/api/settings', updatePayload).then((result) => {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
commit('setServerSettings', result.serverSettings)
|
commit('setServerSettings', result.serverSettings)
|
||||||
return true
|
return true
|
||||||
|
@ -33,6 +33,14 @@ export const getters = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
|
requestLibraryScan({ state, commit }, { libraryId, force }) {
|
||||||
|
this.$axios.$get(`/api/libraries/${libraryId}/scan`, { params: { force } }).then(() => {
|
||||||
|
this.$toast.success('Library scan started')
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('Failed to start scan', error)
|
||||||
|
this.$toast.error('Failed to start scan')
|
||||||
|
})
|
||||||
|
},
|
||||||
loadFolders({ state, commit }) {
|
loadFolders({ state, commit }) {
|
||||||
if (state.folders.length) {
|
if (state.folders.length) {
|
||||||
var lastCheck = Date.now() - state.folderLastUpdate
|
var lastCheck = Date.now() - state.folderLastUpdate
|
||||||
|
@ -9,8 +9,6 @@ const rateLimit = require('express-rate-limit')
|
|||||||
const { version } = require('../package.json')
|
const { version } = require('../package.json')
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
const { ScanResult } = require('./utils/constants')
|
|
||||||
const filePerms = require('./utils/filePerms')
|
|
||||||
const dbMigration = require('./utils/dbMigration')
|
const dbMigration = require('./utils/dbMigration')
|
||||||
const Logger = require('./Logger')
|
const Logger = require('./Logger')
|
||||||
|
|
||||||
@ -160,12 +158,11 @@ class Server {
|
|||||||
const distPath = Path.join(global.appRoot, '/client/dist')
|
const distPath = Path.join(global.appRoot, '/client/dist')
|
||||||
app.use(express.static(distPath))
|
app.use(express.static(distPath))
|
||||||
|
|
||||||
|
// TODO: Are these necessary?
|
||||||
// Metadata folder static path
|
// Metadata folder static path
|
||||||
app.use('/metadata', this.authMiddleware.bind(this), express.static(global.MetadataPath))
|
// app.use('/metadata', this.authMiddleware.bind(this), express.static(global.MetadataPath))
|
||||||
|
|
||||||
// Downloads folder static path
|
// Downloads folder static path
|
||||||
app.use('/downloads', this.authMiddleware.bind(this), express.static(this.downloadManager.downloadDirPath))
|
// app.use('/downloads', this.authMiddleware.bind(this), express.static(this.downloadManager.downloadDirPath))
|
||||||
|
|
||||||
// Static folder
|
// Static folder
|
||||||
app.use(express.static(Path.join(global.appRoot, 'static')))
|
app.use(express.static(Path.join(global.appRoot, 'static')))
|
||||||
|
|
||||||
@ -197,7 +194,6 @@ class Server {
|
|||||||
app.get('/collection/:id', (req, res) => res.sendFile(Path.join(distPath, 'index.html')))
|
app.get('/collection/:id', (req, res) => res.sendFile(Path.join(distPath, 'index.html')))
|
||||||
|
|
||||||
app.post('/login', this.getLoginRateLimiter(), (req, res) => this.auth.login(req, res))
|
app.post('/login', this.getLoginRateLimiter(), (req, res) => this.auth.login(req, res))
|
||||||
app.post('/upload', this.authMiddleware.bind(this), this.handleUpload.bind(this))
|
|
||||||
app.post('/logout', this.authMiddleware.bind(this), this.logout.bind(this))
|
app.post('/logout', this.authMiddleware.bind(this), this.logout.bind(this))
|
||||||
app.get('/ping', (req, res) => {
|
app.get('/ping', (req, res) => {
|
||||||
Logger.info('Recieved ping')
|
Logger.info('Recieved ping')
|
||||||
@ -230,9 +226,7 @@ class Server {
|
|||||||
// with the goal of the web socket connection being a nice-to-have not need-to-have
|
// with the goal of the web socket connection being a nice-to-have not need-to-have
|
||||||
|
|
||||||
// Scanning
|
// Scanning
|
||||||
socket.on('scan', this.scan.bind(this))
|
|
||||||
socket.on('cancel_scan', this.cancelScan.bind(this))
|
socket.on('cancel_scan', this.cancelScan.bind(this))
|
||||||
socket.on('scan_item', (libraryItemId) => this.scanLibraryItem(socket, libraryItemId))
|
|
||||||
socket.on('save_metadata', (libraryItemId) => this.saveMetadata(socket, libraryItemId))
|
socket.on('save_metadata', (libraryItemId) => this.saveMetadata(socket, libraryItemId))
|
||||||
|
|
||||||
// Downloading
|
// Downloading
|
||||||
@ -273,24 +267,6 @@ class Server {
|
|||||||
await this.scanner.scanFilesChanged(fileUpdates)
|
await this.scanner.scanFilesChanged(fileUpdates)
|
||||||
}
|
}
|
||||||
|
|
||||||
async scan(libraryId, options = {}) {
|
|
||||||
Logger.info('[Server] Starting Scan')
|
|
||||||
await this.scanner.scan(libraryId, options)
|
|
||||||
// await this.scanner.scan(libraryId)
|
|
||||||
Logger.info('[Server] Scan complete')
|
|
||||||
}
|
|
||||||
|
|
||||||
async scanLibraryItem(socket, libraryItemId) {
|
|
||||||
var result = await this.scanner.scanLibraryItemById(libraryItemId)
|
|
||||||
var scanResultName = ''
|
|
||||||
for (const key in ScanResult) {
|
|
||||||
if (ScanResult[key] === result) {
|
|
||||||
scanResultName = key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
socket.emit('item_scan_complete', scanResultName)
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelScan(id) {
|
cancelScan(id) {
|
||||||
Logger.debug('[Server] Cancel scan', id)
|
Logger.debug('[Server] Cancel scan', id)
|
||||||
this.scanner.setCancelLibraryScan(id)
|
this.scanner.setCancelLibraryScan(id)
|
||||||
@ -351,70 +327,6 @@ class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleUpload(req, res) {
|
|
||||||
if (!req.user.canUpload) {
|
|
||||||
Logger.warn('User attempted to upload without permission', req.user)
|
|
||||||
return res.sendStatus(403)
|
|
||||||
}
|
|
||||||
var files = Object.values(req.files)
|
|
||||||
var title = req.body.title
|
|
||||||
var author = req.body.author
|
|
||||||
var series = req.body.series
|
|
||||||
var libraryId = req.body.library
|
|
||||||
var folderId = req.body.folder
|
|
||||||
|
|
||||||
var library = this.db.libraries.find(lib => lib.id === libraryId)
|
|
||||||
if (!library) {
|
|
||||||
return res.status(500).send(`Library not found with id ${libraryId}`)
|
|
||||||
}
|
|
||||||
var folder = library.folders.find(fold => fold.id === folderId)
|
|
||||||
if (!folder) {
|
|
||||||
return res.status(500).send(`Folder not found with id ${folderId} in library ${library.name}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!files.length || !title) {
|
|
||||||
return res.status(500).send(`Invalid post data`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// For setting permissions recursively
|
|
||||||
var firstDirPath = Path.join(folder.fullPath, author)
|
|
||||||
|
|
||||||
var outputDirectory = ''
|
|
||||||
if (series && author) {
|
|
||||||
outputDirectory = Path.join(folder.fullPath, author, series, title)
|
|
||||||
} else if (author) {
|
|
||||||
outputDirectory = Path.join(folder.fullPath, author, title)
|
|
||||||
} else {
|
|
||||||
outputDirectory = Path.join(folder.fullPath, title)
|
|
||||||
}
|
|
||||||
|
|
||||||
var exists = await fs.pathExists(outputDirectory)
|
|
||||||
if (exists) {
|
|
||||||
Logger.error(`[Server] Upload directory "${outputDirectory}" already exists`)
|
|
||||||
return res.status(500).send(`Directory "${outputDirectory}" already exists`)
|
|
||||||
}
|
|
||||||
|
|
||||||
await fs.ensureDir(outputDirectory)
|
|
||||||
|
|
||||||
Logger.info(`Uploading ${files.length} files to`, outputDirectory)
|
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
var file = files[i]
|
|
||||||
|
|
||||||
var path = Path.join(outputDirectory, file.name)
|
|
||||||
await file.mv(path).then(() => {
|
|
||||||
return true
|
|
||||||
}).catch((error) => {
|
|
||||||
Logger.error('Failed to move file', path, error)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await filePerms.setDefault(firstDirPath)
|
|
||||||
|
|
||||||
res.sendStatus(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
// First time login rate limit is hit
|
// First time login rate limit is hit
|
||||||
loginLimitReached(req, res, options) {
|
loginLimitReached(req, res, options) {
|
||||||
Logger.error(`[Server] Login rate limit (${options.max}) was hit for ip ${req.ip}`)
|
Logger.error(`[Server] Login rate limit (${options.max}) was hit for ip ${req.ip}`)
|
||||||
|
@ -487,6 +487,20 @@ class LibraryController {
|
|||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET: api/scan (Root)
|
||||||
|
async scan(req, res) {
|
||||||
|
if (!req.user.isRoot) {
|
||||||
|
Logger.error(`[LibraryController] Non-root user attempted to scan library`, req.user)
|
||||||
|
return res.sendStatus(403)
|
||||||
|
}
|
||||||
|
var options = {
|
||||||
|
forceRescan: req.query.force == 1
|
||||||
|
}
|
||||||
|
res.sendStatus(200)
|
||||||
|
await this.scanner.scan(req.library, options)
|
||||||
|
Logger.info('[LibraryController] Scan complete')
|
||||||
|
}
|
||||||
|
|
||||||
middleware(req, res, next) {
|
middleware(req, res, next) {
|
||||||
var librariesAccessible = req.user.librariesAccessible || []
|
var librariesAccessible = req.user.librariesAccessible || []
|
||||||
if (librariesAccessible && librariesAccessible.length && !librariesAccessible.includes(req.params.id)) {
|
if (librariesAccessible && librariesAccessible.length && !librariesAccessible.includes(req.params.id)) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const { reqSupportsWebp } = require('../utils/index')
|
const { reqSupportsWebp } = require('../utils/index')
|
||||||
|
const { ScanResult } = require('../utils/constants')
|
||||||
|
|
||||||
class LibraryItemController {
|
class LibraryItemController {
|
||||||
constructor() { }
|
constructor() { }
|
||||||
@ -239,6 +240,18 @@ class LibraryItemController {
|
|||||||
else res.sendStatus(500)
|
else res.sendStatus(500)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET: api/items/:id/scan (Root)
|
||||||
|
async scan(req, res) {
|
||||||
|
if (!req.user.isRoot) {
|
||||||
|
Logger.error(`[LibraryItemController] Non-root user attempted to scan library item`, req.user)
|
||||||
|
return res.sendStatus(403)
|
||||||
|
}
|
||||||
|
var result = await this.scanner.scanLibraryItemById(req.libraryItem.id)
|
||||||
|
res.json({
|
||||||
|
result: Object.keys(ScanResult).find(key => ScanResult[key] == result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
middleware(req, res, next) {
|
middleware(req, res, next) {
|
||||||
var item = this.db.libraryItems.find(li => li.id === req.params.id)
|
var item = this.db.libraryItems.find(li => li.id === req.params.id)
|
||||||
|
198
server/controllers/MiscController.js
Normal file
198
server/controllers/MiscController.js
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
const Path = require('path')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
const axios = require('axios')
|
||||||
|
|
||||||
|
const Logger = require('../Logger')
|
||||||
|
const { parsePodcastRssFeedXml } = require('../utils/podcastUtils')
|
||||||
|
const { isObject } = require('../utils/index')
|
||||||
|
|
||||||
|
//
|
||||||
|
// This is a controller for routes that don't have a home yet :(
|
||||||
|
//
|
||||||
|
class MiscController {
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
// POST: api/upload
|
||||||
|
async handleUpload(req, res) {
|
||||||
|
if (!req.user.canUpload) {
|
||||||
|
Logger.warn('User attempted to upload without permission', req.user)
|
||||||
|
return res.sendStatus(403)
|
||||||
|
}
|
||||||
|
var files = Object.values(req.files)
|
||||||
|
var title = req.body.title
|
||||||
|
var author = req.body.author
|
||||||
|
var series = req.body.series
|
||||||
|
var libraryId = req.body.library
|
||||||
|
var folderId = req.body.folder
|
||||||
|
|
||||||
|
var library = this.db.libraries.find(lib => lib.id === libraryId)
|
||||||
|
if (!library) {
|
||||||
|
return res.status(500).send(`Library not found with id ${libraryId}`)
|
||||||
|
}
|
||||||
|
var folder = library.folders.find(fold => fold.id === folderId)
|
||||||
|
if (!folder) {
|
||||||
|
return res.status(500).send(`Folder not found with id ${folderId} in library ${library.name}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!files.length || !title) {
|
||||||
|
return res.status(500).send(`Invalid post data`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For setting permissions recursively
|
||||||
|
var firstDirPath = Path.join(folder.fullPath, author)
|
||||||
|
|
||||||
|
var outputDirectory = ''
|
||||||
|
if (series && author) {
|
||||||
|
outputDirectory = Path.join(folder.fullPath, author, series, title)
|
||||||
|
} else if (author) {
|
||||||
|
outputDirectory = Path.join(folder.fullPath, author, title)
|
||||||
|
} else {
|
||||||
|
outputDirectory = Path.join(folder.fullPath, title)
|
||||||
|
}
|
||||||
|
|
||||||
|
var exists = await fs.pathExists(outputDirectory)
|
||||||
|
if (exists) {
|
||||||
|
Logger.error(`[Server] Upload directory "${outputDirectory}" already exists`)
|
||||||
|
return res.status(500).send(`Directory "${outputDirectory}" already exists`)
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.ensureDir(outputDirectory)
|
||||||
|
|
||||||
|
Logger.info(`Uploading ${files.length} files to`, outputDirectory)
|
||||||
|
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
var file = files[i]
|
||||||
|
|
||||||
|
var path = Path.join(outputDirectory, file.name)
|
||||||
|
await file.mv(path).then(() => {
|
||||||
|
return true
|
||||||
|
}).catch((error) => {
|
||||||
|
Logger.error('Failed to move file', path, error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await filePerms.setDefault(firstDirPath)
|
||||||
|
|
||||||
|
res.sendStatus(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET: api/download/:id
|
||||||
|
async download(req, res) {
|
||||||
|
if (!req.user.canDownload) {
|
||||||
|
Logger.error('User attempting to download without permission', req.user)
|
||||||
|
return res.sendStatus(403)
|
||||||
|
}
|
||||||
|
var downloadId = req.params.id
|
||||||
|
Logger.info('Download Request', downloadId)
|
||||||
|
var download = this.downloadManager.getDownload(downloadId)
|
||||||
|
if (!download) {
|
||||||
|
Logger.error('Download request not found', downloadId)
|
||||||
|
return res.sendStatus(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': download.mimeType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.download(download.fullPath, download.filename, options, (err) => {
|
||||||
|
if (err) {
|
||||||
|
Logger.error('Download Error', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PATCH: api/settings (Root)
|
||||||
|
async updateServerSettings(req, res) {
|
||||||
|
if (!req.user.isRoot) {
|
||||||
|
Logger.error('User other than root attempting to update server settings', req.user)
|
||||||
|
return res.sendStatus(403)
|
||||||
|
}
|
||||||
|
var settingsUpdate = req.body
|
||||||
|
if (!settingsUpdate || !isObject(settingsUpdate)) {
|
||||||
|
return res.status(500).send('Invalid settings update object')
|
||||||
|
}
|
||||||
|
|
||||||
|
var madeUpdates = this.db.serverSettings.update(settingsUpdate)
|
||||||
|
if (madeUpdates) {
|
||||||
|
// If backup schedule is updated - update backup manager
|
||||||
|
if (settingsUpdate.backupSchedule !== undefined) {
|
||||||
|
this.backupManager.updateCronSchedule()
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.db.updateServerSettings()
|
||||||
|
}
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
serverSettings: this.db.serverSettings
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST: api/purgecache (Root)
|
||||||
|
async purgeCache(req, res) {
|
||||||
|
if (!req.user.isRoot) {
|
||||||
|
return res.sendStatus(403)
|
||||||
|
}
|
||||||
|
Logger.info(`[ApiRouter] Purging all cache`)
|
||||||
|
await this.cacheManager.purgeAll()
|
||||||
|
res.sendStatus(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
getPodcastFeed(req, res) {
|
||||||
|
var url = req.body.rssFeed
|
||||||
|
if (!url) {
|
||||||
|
return res.status(400).send('Bad request')
|
||||||
|
}
|
||||||
|
|
||||||
|
axios.get(url).then(async (data) => {
|
||||||
|
if (!data || !data.data) {
|
||||||
|
Logger.error('Invalid podcast feed request response')
|
||||||
|
return res.status(500).send('Bad response from feed request')
|
||||||
|
}
|
||||||
|
var podcast = await parsePodcastRssFeedXml(data.data)
|
||||||
|
if (!podcast) {
|
||||||
|
return res.status(500).send('Invalid podcast RSS feed')
|
||||||
|
}
|
||||||
|
res.json(podcast)
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('Failed', error)
|
||||||
|
res.status(500).send(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async findBooks(req, res) {
|
||||||
|
var provider = req.query.provider || 'google'
|
||||||
|
var title = req.query.title || ''
|
||||||
|
var author = req.query.author || ''
|
||||||
|
var results = await this.bookFinder.search(provider, title, author)
|
||||||
|
res.json(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
async findCovers(req, res) {
|
||||||
|
var query = req.query
|
||||||
|
var result = await this.bookFinder.findCovers(query.provider, query.title, query.author || null)
|
||||||
|
res.json(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
async findPodcasts(req, res) {
|
||||||
|
var term = req.query.term
|
||||||
|
var results = await this.podcastFinder.search(term)
|
||||||
|
res.json(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAuthor(req, res) {
|
||||||
|
var query = req.query.q
|
||||||
|
var author = await this.authorFinder.findAuthorByName(query)
|
||||||
|
res.json(author)
|
||||||
|
}
|
||||||
|
|
||||||
|
authorize(req, res) {
|
||||||
|
if (!req.user) {
|
||||||
|
Logger.error('Invalid user in authorize')
|
||||||
|
return res.sendStatus(401)
|
||||||
|
}
|
||||||
|
res.json({ user: req.user })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = new MiscController()
|
@ -2,11 +2,7 @@ const express = require('express')
|
|||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
const fs = require('fs-extra')
|
const fs = require('fs-extra')
|
||||||
const date = require('date-and-time')
|
const date = require('date-and-time')
|
||||||
const axios = require('axios')
|
|
||||||
|
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const { isObject } = require('../utils/index')
|
|
||||||
const { parsePodcastRssFeedXml } = require('../utils/podcastUtils')
|
|
||||||
|
|
||||||
const LibraryController = require('../controllers/LibraryController')
|
const LibraryController = require('../controllers/LibraryController')
|
||||||
const UserController = require('../controllers/UserController')
|
const UserController = require('../controllers/UserController')
|
||||||
@ -18,6 +14,7 @@ const SeriesController = require('../controllers/SeriesController')
|
|||||||
const AuthorController = require('../controllers/AuthorController')
|
const AuthorController = require('../controllers/AuthorController')
|
||||||
const MediaEntityController = require('../controllers/MediaEntityController')
|
const MediaEntityController = require('../controllers/MediaEntityController')
|
||||||
const SessionController = require('../controllers/SessionController')
|
const SessionController = require('../controllers/SessionController')
|
||||||
|
const MiscController = require('../controllers/MiscController')
|
||||||
|
|
||||||
const BookFinder = require('../finders/BookFinder')
|
const BookFinder = require('../finders/BookFinder')
|
||||||
const AuthorFinder = require('../finders/AuthorFinder')
|
const AuthorFinder = require('../finders/AuthorFinder')
|
||||||
@ -69,6 +66,7 @@ class ApiRouter {
|
|||||||
this.router.get('/libraries/:id/stats', LibraryController.middleware.bind(this), LibraryController.stats.bind(this))
|
this.router.get('/libraries/:id/stats', LibraryController.middleware.bind(this), LibraryController.stats.bind(this))
|
||||||
this.router.get('/libraries/:id/authors', LibraryController.middleware.bind(this), LibraryController.getAuthors.bind(this))
|
this.router.get('/libraries/:id/authors', LibraryController.middleware.bind(this), LibraryController.getAuthors.bind(this))
|
||||||
this.router.post('/libraries/:id/matchbooks', LibraryController.middleware.bind(this), LibraryController.matchBooks.bind(this))
|
this.router.post('/libraries/:id/matchbooks', LibraryController.middleware.bind(this), LibraryController.matchBooks.bind(this))
|
||||||
|
this.router.get('/libraries/:id/scan', LibraryController.middleware.bind(this), LibraryController.scan.bind(this)) // Root only
|
||||||
|
|
||||||
this.router.post('/libraries/order', LibraryController.reorder.bind(this))
|
this.router.post('/libraries/order', LibraryController.reorder.bind(this))
|
||||||
|
|
||||||
@ -95,6 +93,7 @@ class ApiRouter {
|
|||||||
this.router.delete('/items/:id/cover', LibraryItemController.middleware.bind(this), LibraryItemController.removeCover.bind(this))
|
this.router.delete('/items/:id/cover', LibraryItemController.middleware.bind(this), LibraryItemController.removeCover.bind(this))
|
||||||
this.router.post('/items/:id/match', LibraryItemController.middleware.bind(this), LibraryItemController.match.bind(this))
|
this.router.post('/items/:id/match', LibraryItemController.middleware.bind(this), LibraryItemController.match.bind(this))
|
||||||
this.router.post('/items/:id/play', LibraryItemController.middleware.bind(this), LibraryItemController.startPlaybackSession.bind(this))
|
this.router.post('/items/:id/play', LibraryItemController.middleware.bind(this), LibraryItemController.startPlaybackSession.bind(this))
|
||||||
|
this.router.get('/items/:id/scan', LibraryItemController.middleware.bind(this), LibraryItemController.scan.bind(this)) // Root only
|
||||||
|
|
||||||
this.router.post('/items/batch/delete', LibraryItemController.batchDelete.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/update', LibraryItemController.batchUpdate.bind(this))
|
||||||
@ -146,14 +145,6 @@ class ApiRouter {
|
|||||||
this.router.delete('/backup/:id', BackupController.delete.bind(this))
|
this.router.delete('/backup/:id', BackupController.delete.bind(this))
|
||||||
this.router.post('/backup/upload', BackupController.upload.bind(this))
|
this.router.post('/backup/upload', BackupController.upload.bind(this))
|
||||||
|
|
||||||
//
|
|
||||||
// Search Routes
|
|
||||||
//
|
|
||||||
this.router.get('/search/covers', this.findCovers.bind(this))
|
|
||||||
this.router.get('/search/books', this.findBooks.bind(this))
|
|
||||||
this.router.get('/search/podcast', this.findPodcasts.bind(this))
|
|
||||||
this.router.get('/search/authors', this.findAuthor.bind(this))
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// File System Routes
|
// File System Routes
|
||||||
//
|
//
|
||||||
@ -183,102 +174,19 @@ class ApiRouter {
|
|||||||
//
|
//
|
||||||
// Misc Routes
|
// Misc Routes
|
||||||
//
|
//
|
||||||
this.router.patch('/serverSettings', this.updateServerSettings.bind(this))
|
this.router.post('/upload', MiscController.handleUpload.bind(this))
|
||||||
|
this.router.get('/download/:id', MiscController.download.bind(this))
|
||||||
this.router.post('/authorize', this.authorize.bind(this))
|
this.router.patch('/settings', MiscController.updateServerSettings.bind(this)) // Root only
|
||||||
|
this.router.post('/purgecache', MiscController.purgeCache.bind(this)) // Root only
|
||||||
this.router.get('/download/:id', this.download.bind(this))
|
this.router.post('/getPodcastFeed', MiscController.getPodcastFeed.bind(this))
|
||||||
|
this.router.post('/authorize', MiscController.authorize.bind(this))
|
||||||
this.router.post('/purgecache', this.purgeCache.bind(this))
|
this.router.get('/search/covers', MiscController.findCovers.bind(this))
|
||||||
|
this.router.get('/search/books', MiscController.findBooks.bind(this))
|
||||||
|
this.router.get('/search/podcast', MiscController.findPodcasts.bind(this))
|
||||||
|
this.router.get('/search/authors', MiscController.findAuthor.bind(this))
|
||||||
|
|
||||||
// OLD
|
// OLD
|
||||||
// this.router.post('/syncUserAudiobookData', this.syncUserAudiobookData.bind(this))
|
// this.router.post('/syncUserAudiobookData', this.syncUserAudiobookData.bind(this))
|
||||||
|
|
||||||
this.router.post('/getPodcastFeed', this.getPodcastFeed.bind(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
async findBooks(req, res) {
|
|
||||||
var provider = req.query.provider || 'google'
|
|
||||||
var title = req.query.title || ''
|
|
||||||
var author = req.query.author || ''
|
|
||||||
var results = await this.bookFinder.search(provider, title, author)
|
|
||||||
res.json(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
async findCovers(req, res) {
|
|
||||||
var query = req.query
|
|
||||||
var result = await this.bookFinder.findCovers(query.provider, query.title, query.author || null)
|
|
||||||
res.json(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
async findPodcasts(req, res) {
|
|
||||||
var term = req.query.term
|
|
||||||
var results = await this.podcastFinder.search(term)
|
|
||||||
res.json(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
async findAuthor(req, res) {
|
|
||||||
var query = req.query.q
|
|
||||||
var author = await this.authorFinder.findAuthorByName(query)
|
|
||||||
res.json(author)
|
|
||||||
}
|
|
||||||
|
|
||||||
authorize(req, res) {
|
|
||||||
if (!req.user) {
|
|
||||||
Logger.error('Invalid user in authorize')
|
|
||||||
return res.sendStatus(401)
|
|
||||||
}
|
|
||||||
res.json({ user: req.user })
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateServerSettings(req, res) {
|
|
||||||
if (!req.user.isRoot) {
|
|
||||||
Logger.error('User other than root attempting to update server settings', req.user)
|
|
||||||
return res.sendStatus(403)
|
|
||||||
}
|
|
||||||
var settingsUpdate = req.body
|
|
||||||
if (!settingsUpdate || !isObject(settingsUpdate)) {
|
|
||||||
return res.status(500).send('Invalid settings update object')
|
|
||||||
}
|
|
||||||
|
|
||||||
var madeUpdates = this.db.serverSettings.update(settingsUpdate)
|
|
||||||
if (madeUpdates) {
|
|
||||||
// If backup schedule is updated - update backup manager
|
|
||||||
if (settingsUpdate.backupSchedule !== undefined) {
|
|
||||||
this.backupManager.updateCronSchedule()
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.db.updateServerSettings()
|
|
||||||
}
|
|
||||||
return res.json({
|
|
||||||
success: true,
|
|
||||||
serverSettings: this.db.serverSettings
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async download(req, res) {
|
|
||||||
if (!req.user.canDownload) {
|
|
||||||
Logger.error('User attempting to download without permission', req.user)
|
|
||||||
return res.sendStatus(403)
|
|
||||||
}
|
|
||||||
var downloadId = req.params.id
|
|
||||||
Logger.info('Download Request', downloadId)
|
|
||||||
var download = this.downloadManager.getDownload(downloadId)
|
|
||||||
if (!download) {
|
|
||||||
Logger.error('Download request not found', downloadId)
|
|
||||||
return res.sendStatus(404)
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': download.mimeType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.download(download.fullPath, download.filename, options, (err) => {
|
|
||||||
if (err) {
|
|
||||||
Logger.error('Download Error', err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDirectories(dir, relpath, excludedDirs, level = 0) {
|
async getDirectories(dir, relpath, excludedDirs, level = 0) {
|
||||||
@ -450,15 +358,6 @@ class ApiRouter {
|
|||||||
return listeningStats
|
return listeningStats
|
||||||
}
|
}
|
||||||
|
|
||||||
async purgeCache(req, res) {
|
|
||||||
if (!req.user.isRoot) {
|
|
||||||
return res.sendStatus(403)
|
|
||||||
}
|
|
||||||
Logger.info(`[ApiRouter] Purging all cache`)
|
|
||||||
await this.cacheManager.purgeAll()
|
|
||||||
res.sendStatus(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
async createAuthorsAndSeriesForItemUpdate(mediaPayload) {
|
async createAuthorsAndSeriesForItemUpdate(mediaPayload) {
|
||||||
if (mediaPayload.metadata) {
|
if (mediaPayload.metadata) {
|
||||||
var mediaMetadata = mediaPayload.metadata
|
var mediaMetadata = mediaPayload.metadata
|
||||||
@ -512,27 +411,5 @@ class ApiRouter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPodcastFeed(req, res) {
|
|
||||||
var url = req.body.rssFeed
|
|
||||||
if (!url) {
|
|
||||||
return res.status(400).send('Bad request')
|
|
||||||
}
|
|
||||||
|
|
||||||
axios.get(url).then(async (data) => {
|
|
||||||
if (!data || !data.data) {
|
|
||||||
Logger.error('Invalid podcast feed request response')
|
|
||||||
return res.status(500).send('Bad response from feed request')
|
|
||||||
}
|
|
||||||
var podcast = await parsePodcastRssFeedXml(data.data)
|
|
||||||
if (!podcast) {
|
|
||||||
return res.status(500).send('Invalid podcast RSS feed')
|
|
||||||
}
|
|
||||||
res.json(podcast)
|
|
||||||
}).catch((error) => {
|
|
||||||
console.error('Failed', error)
|
|
||||||
res.status(500).send(error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
module.exports = ApiRouter
|
module.exports = ApiRouter
|
@ -109,17 +109,13 @@ class Scanner {
|
|||||||
return ScanResult.UPTODATE
|
return ScanResult.UPTODATE
|
||||||
}
|
}
|
||||||
|
|
||||||
async scan(libraryId, options = {}) {
|
async scan(library, options = {}) {
|
||||||
if (this.isLibraryScanning(libraryId)) {
|
if (this.isLibraryScanning(library.id)) {
|
||||||
Logger.error(`[Scanner] Already scanning ${libraryId}`)
|
Logger.error(`[Scanner] Already scanning ${library.id}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var library = this.db.libraries.find(lib => lib.id === libraryId)
|
if (!library.folders.length) {
|
||||||
if (!library) {
|
|
||||||
Logger.error(`[Scanner] Library not found for scan ${libraryId}`)
|
|
||||||
return
|
|
||||||
} else if (!library.folders.length) {
|
|
||||||
Logger.warn(`[Scanner] Library has no folders to scan "${library.name}"`)
|
Logger.warn(`[Scanner] Library has no folders to scan "${library.name}"`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user