Update:RSS feed API routes

This commit is contained in:
advplyr 2022-12-26 16:58:36 -06:00
parent 775dedc338
commit e803dcd325
6 changed files with 123 additions and 85 deletions

View file

@ -21,8 +21,8 @@ class LibraryItemController {
}
if (includeEntities.includes('rssfeed')) {
var feedData = this.rssFeedManager.findFeedForItem(item.id)
item.rssFeedUrl = feedData ? feedData.feedUrl : null
const feedData = this.rssFeedManager.findFeedForItem(item.id)
item.rssFeed = feedData ? feedData.toJSONMinified() : null
}
if (item.mediaType == 'book') {
@ -432,38 +432,6 @@ class LibraryItemController {
})
}
// POST: api/items/:id/open-feed
async openRSSFeed(req, res) {
if (!req.user.isAdminOrUp) {
Logger.error(`[LibraryItemController] Non-admin user attempted to open RSS feed`, req.user.username)
return res.sendStatus(403)
}
const feedData = await this.rssFeedManager.openFeedForItem(req.user, req.libraryItem, req.body)
if (feedData.error) {
return res.json({
success: false,
error: feedData.error
})
}
res.json({
success: true,
feedUrl: feedData.feedUrl
})
}
async closeRSSFeed(req, res) {
if (!req.user.isAdminOrUp) {
Logger.error(`[LibraryItemController] Non-admin user attempted to close RSS feed`, req.user.username)
return res.sendStatus(403)
}
await this.rssFeedManager.closeFeedForItem(req.params.id)
res.sendStatus(200)
}
async toneScan(req, res) {
if (!req.libraryItem.media.audioFiles.length) {
return res.sendStatus(404)
@ -481,7 +449,7 @@ class LibraryItemController {
}
middleware(req, res, next) {
var item = this.db.libraryItems.find(li => li.id === req.params.id)
const 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

View file

@ -0,0 +1,68 @@
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
class RSSFeedController {
constructor() { }
// POST: api/feeds/item/:itemId/open
async openRSSFeedForItem(req, res) {
const options = req.body || {}
const item = this.db.libraryItems.find(li => li.id === req.params.itemId)
if (!item) return res.sendStatus(404)
// Check user can access this library item
if (!req.user.checkCanAccessLibraryItem(item)) {
Logger.error(`[RSSFeedController] User "${req.user.username}" attempted to open an RSS feed for item "${item.media.metadata.title}" that they don\'t have access to`)
return res.sendStatus(403)
}
// Check request body options exist
if (!options.serverAddress || !options.slug) {
Logger.error(`[RSSFeedController] Invalid request body to open RSS feed`)
return res.status(400).send('Invalid request body')
}
// Check item has audio tracks
if (!item.media.numTracks) {
Logger.error(`[RSSFeedController] Cannot open RSS feed for item "${item.media.metadata.title}" because it has no audio tracks`)
return res.status(400).send('Item has no audio tracks')
}
// Check that this slug is not being used for another feed (slug will also be the Feed id)
if (this.rssFeedManager.feeds[options.slug]) {
Logger.error(`[RSSFeedController] Cannot open RSS feed because slug "${options.slug}" is already in use`)
return res.status(400).send('Slug already in use')
}
const feed = await this.rssFeedManager.openFeedForItem(req.user, item, req.body)
res.json({
feed: feed.toJSONMinified()
})
}
// POST: api/feeds/:id/close
async closeRSSFeed(req, res) {
await this.rssFeedManager.closeRssFeed(req.params.id)
res.sendStatus(200)
}
middleware(req, res, next) {
if (!req.user.isAdminOrUp) { // Only admins can manage rss feeds
Logger.error(`[RSSFeedController] Non-admin user attempted to make a request to an RSS feed route`, req.user.username)
return res.sendStatus(403)
}
if (req.params.id) {
const feed = this.rssFeedManager.findFeed(req.params.id)
if (!feed) {
Logger.error(`[RSSFeedController] RSS feed not found with id "${req.params.id}"`)
return res.sendStatus(404)
}
}
next()
}
}
module.exports = new RSSFeedController()

View file

@ -18,10 +18,10 @@ class RssFeedManager {
}
async init() {
var feedObjects = await this.db.getAllEntities('feed')
const feedObjects = await this.db.getAllEntities('feed')
if (feedObjects && feedObjects.length) {
feedObjects.forEach((feedObj) => {
var feed = new Feed(feedObj)
const feed = new Feed(feedObj)
this.feeds[feed.id] = feed
Logger.info(`[RssFeedManager] Opened rss feed ${feed.feedUrl}`)
})
@ -32,8 +32,12 @@ class RssFeedManager {
return Object.values(this.feeds).find(feed => feed.entityId === libraryItemId)
}
findFeed(feedId) {
return this.feeds[feedId] || null
}
async getFeed(req, res) {
var feed = this.feeds[req.params.id]
const feed = this.feeds[req.params.id]
if (!feed) {
Logger.debug(`[RssFeedManager] Feed not found ${req.params.id}`)
res.sendStatus(404)
@ -49,19 +53,19 @@ class RssFeedManager {
}
}
var xml = feed.buildXml()
const xml = feed.buildXml()
res.set('Content-Type', 'text/xml')
res.send(xml)
}
getFeedItem(req, res) {
var feed = this.feeds[req.params.id]
const feed = this.feeds[req.params.id]
if (!feed) {
Logger.debug(`[RssFeedManager] Feed not found ${req.params.id}`)
res.sendStatus(404)
return
}
var episodePath = feed.getEpisodePath(req.params.episodeId)
const episodePath = feed.getEpisodePath(req.params.episodeId)
if (!episodePath) {
Logger.error(`[RssFeedManager] Feed episode not found ${req.params.episodeId}`)
res.sendStatus(404)
@ -71,7 +75,7 @@ class RssFeedManager {
}
getFeedCover(req, res) {
var feed = this.feeds[req.params.id]
const feed = this.feeds[req.params.id]
if (!feed) {
Logger.debug(`[RssFeedManager] Feed not found ${req.params.id}`)
res.sendStatus(404)
@ -85,7 +89,7 @@ class RssFeedManager {
const extname = Path.extname(feed.coverPath).toLowerCase().slice(1)
res.type(`image/${extname}`)
var readStream = fs.createReadStream(feed.coverPath)
const readStream = fs.createReadStream(feed.coverPath)
readStream.pipe(res)
}
@ -93,32 +97,25 @@ class RssFeedManager {
const serverAddress = options.serverAddress
const slug = options.slug
if (this.feeds[slug]) {
Logger.error(`[RssFeedManager] Slug already in use`)
return {
error: `Slug "${slug}" already in use`
}
}
const feed = new Feed()
feed.setFromItem(user.id, slug, libraryItem, serverAddress)
this.feeds[feed.id] = feed
Logger.debug(`[RssFeedManager] Opened RSS feed ${feed.feedUrl}`)
Logger.debug(`[RssFeedManager] Opened RSS feed "${feed.feedUrl}"`)
await this.db.insertEntity('feed', feed)
SocketAuthority.emitter('rss_feed_open', feed.toJSONMinified())
return feed
}
closeFeedForItem(libraryItemId) {
var feed = this.findFeedForItem(libraryItemId)
const feed = this.findFeedForItem(libraryItemId)
if (!feed) return
return this.closeRssFeed(feed.id)
}
async closeRssFeed(id) {
if (!this.feeds[id]) return
var feed = this.feeds[id]
const feed = this.feeds[id]
await this.db.removeEntity('feed', id)
SocketAuthority.emitter('rss_feed_closed', feed.toJSONMinified())
delete this.feeds[id]

View file

@ -23,6 +23,7 @@ const NotificationController = require('../controllers/NotificationController')
const SearchController = require('../controllers/SearchController')
const CacheController = require('../controllers/CacheController')
const ToolsController = require('../controllers/ToolsController')
const RSSFeedController = require('../controllers/RSSFeedController')
const MiscController = require('../controllers/MiscController')
const BookFinder = require('../finders/BookFinder')
@ -104,8 +105,6 @@ class ApiRouter {
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.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))
this.router.post('/items/:id/tone-scan/:index?', LibraryItemController.middleware.bind(this), LibraryItemController.toneScan.bind(this))
this.router.post('/items/batch/delete', LibraryItemController.batchDelete.bind(this))
@ -231,7 +230,7 @@ class ApiRouter {
this.router.delete('/podcasts/:id/episode/:episodeId', PodcastController.middleware.bind(this), PodcastController.removeEpisode.bind(this))
//
// Notification Routes
// Notification Routes (Admin and up)
//
this.router.get('/notifications', NotificationController.middleware.bind(this), NotificationController.get.bind(this))
this.router.patch('/notifications', NotificationController.middleware.bind(this), NotificationController.update.bind(this))
@ -252,18 +251,24 @@ class ApiRouter {
this.router.get('/search/chapters', SearchController.findChapters.bind(this))
//
// Cache Routes
// Cache Routes (Admin and up)
//
this.router.post('/cache/purge', CacheController.purgeCache.bind(this))
this.router.post('/cache/items/purge', CacheController.purgeItemsCache.bind(this))
//
// Tools Routes
// Tools Routes (Admin and up)
//
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))
//
// RSS Feed Routes (Admin and up)
//
this.router.post('/feeds/item/:itemId/open', RSSFeedController.middleware.bind(this), RSSFeedController.openRSSFeedForItem.bind(this))
this.router.post('/feeds/:id/close', RSSFeedController.middleware.bind(this), RSSFeedController.closeRSSFeed.bind(this))
//
// Misc Routes
//