From e3ba739db523ba8ed6815155dcc177f0b9173986 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 19 Nov 2022 13:28:06 -0600 Subject: [PATCH] Update:Encode & embed metadata API endpoints, separate cache & search endpoints into controllers --- client/pages/audiobook/_id/manage.vue | 6 +- server/controllers/CacheController.js | 26 +++++ server/controllers/LibraryItemController.js | 18 ---- server/controllers/MiscController.js | 103 -------------------- server/controllers/SearchController.js | 44 +++++++++ server/controllers/ToolsController.js | 81 +++++++++++++++ server/managers/AbMergeManager.js | 4 +- server/routers/ApiRouter.js | 37 ++++--- 8 files changed, 183 insertions(+), 136 deletions(-) create mode 100644 server/controllers/CacheController.js create mode 100644 server/controllers/SearchController.js create mode 100644 server/controllers/ToolsController.js diff --git a/client/pages/audiobook/_id/manage.vue b/client/pages/audiobook/_id/manage.vue index 216e6b47..f660da23 100644 --- a/client/pages/audiobook/_id/manage.vue +++ b/client/pages/audiobook/_id/manage.vue @@ -247,7 +247,7 @@ export default { cancelEncodeClick() { this.isCancelingEncode = true this.$axios - .$post(`/api/encode-m4b/${this.libraryItemId}/cancel`) + .$delete(`/api/tools/item/${this.libraryItemId}/encode-m4b`) .then(() => { this.$toast.success('Encode canceled') }) @@ -262,7 +262,7 @@ export default { encodeM4bClick() { this.processing = true this.$axios - .$get(`/api/encode-m4b/${this.libraryItemId}`) + .$post(`/api/tools/item/${this.libraryItemId}/encode-m4b`) .then(() => { console.log('Ab m4b merge started') }) @@ -287,7 +287,7 @@ export default { updateAudioFileMetadata() { this.processing = true this.$axios - .$get(`/api/items/${this.libraryItemId}/audio-metadata?tone=1`) + .$post(`/api/tools/item/${this.libraryItemId}/embed-metadata?tone=1`) .then(() => { console.log('Audio metadata encode started') }) diff --git a/server/controllers/CacheController.js b/server/controllers/CacheController.js new file mode 100644 index 00000000..a489c270 --- /dev/null +++ b/server/controllers/CacheController.js @@ -0,0 +1,26 @@ +const Logger = require('../Logger') + +class CacheController { + constructor() { } + + // POST: api/cache/purge + async purgeCache(req, res) { + if (!req.user.isAdminOrUp) { + return res.sendStatus(403) + } + Logger.info(`[MiscController] Purging all cache`) + await this.cacheManager.purgeAll() + res.sendStatus(200) + } + + // POST: api/cache/items/purge + async purgeItemsCache(req, res) { + if (!req.user.isAdminOrUp) { + return res.sendStatus(403) + } + Logger.info(`[MiscController] Purging items cache`) + await this.cacheManager.purgeItems() + res.sendStatus(200) + } +} +module.exports = new CacheController() \ No newline at end of file diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js index df417dbc..20df9dad 100644 --- a/server/controllers/LibraryItemController.js +++ b/server/controllers/LibraryItemController.js @@ -389,24 +389,6 @@ class LibraryItemController { res.json(this.audioMetadataManager.getToneMetadataObjectForApi(req.libraryItem)) } - // GET: api/items/:id/audio-metadata - async updateAudioFileMetadata(req, res) { - if (!req.user.isAdminOrUp) { - Logger.error(`[LibraryItemController] Non-root user attempted to update audio metadata`, req.user) - return res.sendStatus(403) - } - - if (req.libraryItem.isMissing || !req.libraryItem.hasAudioFiles || !req.libraryItem.isBook) { - Logger.error(`[LibraryItemController] Invalid library item`) - return res.sendStatus(500) - } - - const useTone = req.query.tone === '1' - const forceEmbedChapters = req.query.forceEmbedChapters === '1' - this.audioMetadataManager.updateMetadataForItem(req.user, req.libraryItem, useTone, forceEmbedChapters) - res.sendStatus(200) - } - // POST: api/items/:id/chapters async updateMediaChapters(req, res) { if (!req.user.canUpdate) { diff --git a/server/controllers/MiscController.js b/server/controllers/MiscController.js index cfa1dc44..db060b16 100644 --- a/server/controllers/MiscController.js +++ b/server/controllers/MiscController.js @@ -82,49 +82,6 @@ class MiscController { res.sendStatus(200) } - // GET: api/encode-m4b/:id - async encodeM4b(req, res) { - if (!req.user.isAdminOrUp) { - Logger.error('[MiscController] encodeM4b: Non-admin user attempting to make m4b', req.user) - return res.sendStatus(403) - } - - var libraryItem = this.db.getLibraryItem(req.params.id) - if (!libraryItem || libraryItem.isMissing || libraryItem.isInvalid) { - Logger.error(`[MiscController] encodeM4b: library item not found or invalid ${req.params.id}`) - return res.status(404).send('Audiobook not found') - } - - if (libraryItem.mediaType !== 'book') { - Logger.error(`[MiscController] encodeM4b: Invalid library item ${req.params.id}: not a book`) - return res.status(500).send('Invalid library item: not a book') - } - - if (libraryItem.media.tracks.length <= 0) { - Logger.error(`[MiscController] encodeM4b: Invalid audiobook ${req.params.id}: no audio tracks`) - return res.status(500).send('Invalid audiobook: no audio tracks') - } - - this.abMergeManager.startAudiobookMerge(req.user, libraryItem) - - res.sendStatus(200) - } - - // POST: api/encode-m4b/:id/cancel - async cancelM4bEncode(req, res) { - if (!req.user.isAdminOrUp) { - Logger.error('[MiscController] cancelM4bEncode: Non-admin user attempting to cancel m4b encode', req.user) - return res.sendStatus(403) - } - - const workerTask = this.abMergeManager.getPendingTaskByLibraryItemId(req.params.id) - if (!workerTask) return res.sendStatus(404) - - this.abMergeManager.cancelEncode(workerTask.task) - - res.sendStatus(200) - } - // GET: api/tasks getTasks(req, res) { res.json({ @@ -158,66 +115,6 @@ class MiscController { }) } - // POST: api/cache/purge (admin) - async purgeCache(req, res) { - if (!req.user.isAdminOrUp) { - return res.sendStatus(403) - } - Logger.info(`[MiscController] Purging all cache`) - await this.cacheManager.purgeAll() - res.sendStatus(200) - } - - // POST: api/cache/items/purge - async purgeItemsCache(req, res) { - if (!req.user.isAdminOrUp) { - return res.sendStatus(403) - } - Logger.info(`[MiscController] Purging items cache`) - await this.cacheManager.purgeItems() - res.sendStatus(200) - } - - 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 podcast = query.podcast == 1 - - var result = null - if (podcast) result = await this.podcastFinder.findCovers(query.title) - else 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) - } - - async findChapters(req, res) { - var asin = req.query.asin - var region = (req.query.region || 'us').toLowerCase() - var chapterData = await this.bookFinder.findChapters(asin, region) - if (!chapterData) { - return res.json({ error: 'Chapters not found' }) - } - res.json(chapterData) - } - authorize(req, res) { if (!req.user) { Logger.error('Invalid user in authorize') diff --git a/server/controllers/SearchController.js b/server/controllers/SearchController.js new file mode 100644 index 00000000..9246bed9 --- /dev/null +++ b/server/controllers/SearchController.js @@ -0,0 +1,44 @@ +class SearchController { + constructor() { } + + 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 podcast = query.podcast == 1 + + var result = null + if (podcast) result = await this.podcastFinder.findCovers(query.title) + else 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) + } + + async findChapters(req, res) { + var asin = req.query.asin + var region = (req.query.region || 'us').toLowerCase() + var chapterData = await this.bookFinder.findChapters(asin, region) + if (!chapterData) { + return res.json({ error: 'Chapters not found' }) + } + res.json(chapterData) + } +} +module.exports = new SearchController() \ No newline at end of file diff --git a/server/controllers/ToolsController.js b/server/controllers/ToolsController.js new file mode 100644 index 00000000..fad81c70 --- /dev/null +++ b/server/controllers/ToolsController.js @@ -0,0 +1,81 @@ +const Logger = require('../Logger') + +class ToolsController { + constructor() { } + + + // POST: api/tools/item/:id/encode-m4b + async encodeM4b(req, res) { + if (!req.user.isAdminOrUp) { + Logger.error('[MiscController] encodeM4b: Non-admin user attempting to make m4b', req.user) + return res.sendStatus(403) + } + + if (req.libraryItem.isMissing || req.libraryItem.isInvalid) { + Logger.error(`[MiscController] encodeM4b: library item not found or invalid ${req.params.id}`) + return res.status(404).send('Audiobook not found') + } + + if (req.libraryItem.mediaType !== 'book') { + Logger.error(`[MiscController] encodeM4b: Invalid library item ${req.params.id}: not a book`) + return res.status(500).send('Invalid library item: not a book') + } + + if (req.libraryItem.media.tracks.length <= 0) { + Logger.error(`[MiscController] encodeM4b: Invalid audiobook ${req.params.id}: no audio tracks`) + return res.status(500).send('Invalid audiobook: no audio tracks') + } + + this.abMergeManager.startAudiobookMerge(req.user, req.libraryItem) + + res.sendStatus(200) + } + + // DELETE: api/tools/item/:id/encode-m4b + async cancelM4bEncode(req, res) { + if (!req.user.isAdminOrUp) { + Logger.error('[MiscController] cancelM4bEncode: Non-admin user attempting to cancel m4b encode', req.user) + return res.sendStatus(403) + } + + const workerTask = this.abMergeManager.getPendingTaskByLibraryItemId(req.params.id) + if (!workerTask) return res.sendStatus(404) + + this.abMergeManager.cancelEncode(workerTask.task) + + res.sendStatus(200) + } + + + // POST: api/tools/item/:id/embed-metadata + async embedAudioFileMetadata(req, res) { + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryItemController] Non-root user attempted to update audio metadata`, req.user) + return res.sendStatus(403) + } + + if (req.libraryItem.isMissing || !req.libraryItem.hasAudioFiles || !req.libraryItem.isBook) { + Logger.error(`[LibraryItemController] Invalid library item`) + return res.sendStatus(500) + } + + const useTone = req.query.tone === '1' + const forceEmbedChapters = req.query.forceEmbedChapters === '1' + this.audioMetadataManager.updateMetadataForItem(req.user, req.libraryItem, useTone, forceEmbedChapters) + res.sendStatus(200) + } + + itemMiddleware(req, res, next) { + var item = this.db.libraryItems.find(li => li.id === req.params.id) + if (!item || !item.media) return res.sendStatus(404) + + // Check user can access this library item + if (!req.user.checkCanAccessLibraryItem(item)) { + return res.sendStatus(403) + } + + req.libraryItem = item + next() + } +} +module.exports = new ToolsController() \ No newline at end of file diff --git a/server/managers/AbMergeManager.js b/server/managers/AbMergeManager.js index 72343d50..6fbce262 100644 --- a/server/managers/AbMergeManager.js +++ b/server/managers/AbMergeManager.js @@ -224,14 +224,16 @@ class AbMergeManager { const pendingDl = this.pendingTasks.find(d => d.id === task.id) if (pendingDl) { this.pendingTasks = this.pendingTasks.filter(d => d.id !== task.id) - Logger.warn(`[AbMergeManager] Removing download in progress - stopping worker`) if (pendingDl.worker) { + Logger.warn(`[AbMergeManager] Removing download in progress - stopping worker`) try { pendingDl.worker.postMessage('STOP') return } catch (error) { Logger.error('[AbMergeManager] Error posting stop message to worker', error) } + } else { + Logger.debug(`[AbMergeManager] Removing download in progress - no worker`) } } diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index 3de794ea..d296cf50 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -11,10 +11,14 @@ const MeController = require('../controllers/MeController') const BackupController = require('../controllers/BackupController') const LibraryItemController = require('../controllers/LibraryItemController') const SeriesController = require('../controllers/SeriesController') +const FileSystemController = require('../controllers/FileSystemController') const AuthorController = require('../controllers/AuthorController') const SessionController = require('../controllers/SessionController') const PodcastController = require('../controllers/PodcastController') const NotificationController = require('../controllers/NotificationController') +const SearchController = require('../controllers/SearchController') +const CacheController = require('../controllers/CacheController') +const ToolsController = require('../controllers/ToolsController') const MiscController = require('../controllers/MiscController') const BookFinder = require('../finders/BookFinder') @@ -23,7 +27,6 @@ const PodcastFinder = require('../finders/PodcastFinder') const Author = require('../objects/entities/Author') const Series = require('../objects/entities/Series') -const FileSystemController = require('../controllers/FileSystemController') class ApiRouter { constructor(db, auth, scanner, playbackSessionManager, abMergeManager, coverManager, backupManager, watcher, cacheManager, podcastManager, audioMetadataManager, rssFeedManager, cronManager, notificationManager, taskManager, getUsersOnline, emitter, clientEmitter) { @@ -98,7 +101,6 @@ class ApiRouter { this.router.patch('/items/:id/tracks', LibraryItemController.middleware.bind(this), LibraryItemController.updateTracks.bind(this)) this.router.get('/items/:id/scan', LibraryItemController.middleware.bind(this), LibraryItemController.scan.bind(this)) this.router.get('/items/:id/tone-object', LibraryItemController.middleware.bind(this), LibraryItemController.getToneMetadataObject.bind(this)) - this.router.get('/items/:id/audio-metadata', LibraryItemController.middleware.bind(this), LibraryItemController.updateAudioFileMetadata.bind(this)) this.router.post('/items/:id/chapters', LibraryItemController.middleware.bind(this), LibraryItemController.updateMediaChapters.bind(this)) this.router.post('/items/:id/open-feed', LibraryItemController.middleware.bind(this), LibraryItemController.openRSSFeed.bind(this)) this.router.post('/items/:id/close-feed', LibraryItemController.middleware.bind(this), LibraryItemController.closeRSSFeed.bind(this)) @@ -224,22 +226,35 @@ class ApiRouter { this.router.patch('/notifications/:id', NotificationController.middleware.bind(this), NotificationController.updateNotification.bind(this)) this.router.get('/notifications/:id/test', NotificationController.middleware.bind(this), NotificationController.sendNotificationTest.bind(this)) + // + // Search Routes + // + this.router.get('/search/covers', SearchController.findCovers.bind(this)) + this.router.get('/search/books', SearchController.findBooks.bind(this)) + this.router.get('/search/podcast', SearchController.findPodcasts.bind(this)) + this.router.get('/search/authors', SearchController.findAuthor.bind(this)) + this.router.get('/search/chapters', SearchController.findChapters.bind(this)) + + // + // Cache Routes + // + this.router.post('/cache/purge', CacheController.purgeCache.bind(this)) + this.router.post('/cache/items/purge', CacheController.purgeItemsCache.bind(this)) + + // + // Tools Routes + // + this.router.post('/tools/item/:id/encode-m4b', ToolsController.itemMiddleware.bind(this), ToolsController.encodeM4b.bind(this)) + this.router.delete('/tools/item/:id/encode-m4b', ToolsController.itemMiddleware.bind(this), ToolsController.cancelM4bEncode.bind(this)) + this.router.post('/tools/item/:id/embed-metadata', ToolsController.itemMiddleware.bind(this), ToolsController.embedAudioFileMetadata.bind(this)) + // // Misc Routes // this.router.post('/upload', MiscController.handleUpload.bind(this)) - this.router.get('/encode-m4b/:id', MiscController.encodeM4b.bind(this)) - this.router.post('/encode-m4b/:id/cancel', MiscController.cancelM4bEncode.bind(this)) this.router.get('/tasks', MiscController.getTasks.bind(this)) this.router.patch('/settings', MiscController.updateServerSettings.bind(this)) - this.router.post('/cache/purge', MiscController.purgeCache.bind(this)) - this.router.post('/cache/items/purge', MiscController.purgeItemsCache.bind(this)) this.router.post('/authorize', MiscController.authorize.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)) - this.router.get('/search/chapters', MiscController.findChapters.bind(this)) this.router.get('/tags', MiscController.getAllTags.bind(this)) this.router.post('/validate-cron', MiscController.validateCronExpression.bind(this)) }