mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Update:RSS feed API routes
This commit is contained in:
		
							parent
							
								
									775dedc338
								
							
						
					
					
						commit
						e803dcd325
					
				| @ -6,13 +6,13 @@ | |||||||
|       </div> |       </div> | ||||||
|     </template> |     </template> | ||||||
|     <div ref="wrapper" class="px-8 py-6 w-full text-sm rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden"> |     <div ref="wrapper" class="px-8 py-6 w-full text-sm rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden"> | ||||||
|       <div v-if="currentFeedUrl" class="w-full"> |       <div v-if="currentFeed" class="w-full"> | ||||||
|         <p class="text-lg font-semibold mb-4">{{ $strings.HeaderRSSFeedIsOpen }}</p> |         <p class="text-lg font-semibold mb-4">{{ $strings.HeaderRSSFeedIsOpen }}</p> | ||||||
| 
 | 
 | ||||||
|         <div class="w-full relative"> |         <div class="w-full relative"> | ||||||
|           <ui-text-input v-model="currentFeedUrl" readonly /> |           <ui-text-input v-model="currentFeed.feedUrl" readonly /> | ||||||
| 
 | 
 | ||||||
|           <span class="material-icons absolute right-2 bottom-2 p-0.5 text-base transition-transform duration-100 text-gray-300 hover:text-white transform hover:scale-125 cursor-pointer" @click="copyToClipboard(currentFeedUrl)">content_copy</span> |           <span class="material-icons absolute right-2 bottom-2 p-0.5 text-base transition-transform duration-100 text-gray-300 hover:text-white transform hover:scale-125 cursor-pointer" @click="copyToClipboard(currentFeed.feedUrl)">content_copy</span> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <div v-else class="w-full"> |       <div v-else class="w-full"> | ||||||
| @ -28,7 +28,7 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div v-show="userIsAdminOrUp" class="flex items-center pt-6"> |       <div v-show="userIsAdminOrUp" class="flex items-center pt-6"> | ||||||
|         <div class="flex-grow" /> |         <div class="flex-grow" /> | ||||||
|         <ui-btn v-if="currentFeedUrl" color="error" small @click="closeFeed">{{ $strings.ButtonCloseFeed }}</ui-btn> |         <ui-btn v-if="currentFeed" color="error" small @click="closeFeed">{{ $strings.ButtonCloseFeed }}</ui-btn> | ||||||
|         <ui-btn v-else color="success" small @click="openFeed">{{ $strings.ButtonOpenFeed }}</ui-btn> |         <ui-btn v-else color="success" small @click="openFeed">{{ $strings.ButtonOpenFeed }}</ui-btn> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
| @ -43,13 +43,16 @@ export default { | |||||||
|       type: Object, |       type: Object, | ||||||
|       default: () => null |       default: () => null | ||||||
|     }, |     }, | ||||||
|     feedUrl: String |     feed: { | ||||||
|  |       type: Object, | ||||||
|  |       default: () => null | ||||||
|  |     } | ||||||
|   }, |   }, | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       processing: false, |       processing: false, | ||||||
|       newFeedSlug: null, |       newFeedSlug: null, | ||||||
|       currentFeedUrl: null |       currentFeed: null | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   watch: { |   watch: { | ||||||
| @ -106,7 +109,7 @@ export default { | |||||||
|         return |         return | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       var sanitized = this.$sanitizeSlug(this.newFeedSlug) |       const sanitized = this.$sanitizeSlug(this.newFeedSlug) | ||||||
|       if (this.newFeedSlug !== sanitized) { |       if (this.newFeedSlug !== sanitized) { | ||||||
|         this.newFeedSlug = sanitized |         this.newFeedSlug = sanitized | ||||||
|         this.$toast.warning('Slug had to be modified - Run again') |         this.$toast.warning('Slug had to be modified - Run again') | ||||||
| @ -121,19 +124,15 @@ export default { | |||||||
| 
 | 
 | ||||||
|       console.log('Payload', payload) |       console.log('Payload', payload) | ||||||
|       this.$axios |       this.$axios | ||||||
|         .$post(`/api/items/${this.libraryItemId}/open-feed`, payload) |         .$post(`/api/feeds/item/${this.libraryItemId}/open`, payload) | ||||||
|         .then((data) => { |         .then((data) => { | ||||||
|           if (data.success) { |           console.log('Opened RSS Feed', data) | ||||||
|             console.log('Opened RSS Feed', data) |           this.currentFeed = data.feed | ||||||
|             this.currentFeedUrl = data.feedUrl |  | ||||||
|           } else { |  | ||||||
|             const errorMsg = data.error || 'Unknown error' |  | ||||||
|             this.$toast.error(errorMsg) |  | ||||||
|           } |  | ||||||
|         }) |         }) | ||||||
|         .catch((error) => { |         .catch((error) => { | ||||||
|           console.error('Failed to open RSS Feed', error) |           console.error('Failed to open RSS Feed', error) | ||||||
|           this.$toast.error() |           const errorMsg = error.response ? error.response.data : null | ||||||
|  |           this.$toast.error(errorMsg || 'Failed to open RSS Feed') | ||||||
|         }) |         }) | ||||||
|     }, |     }, | ||||||
|     copyToClipboard(str) { |     copyToClipboard(str) { | ||||||
| @ -142,22 +141,23 @@ export default { | |||||||
|     closeFeed() { |     closeFeed() { | ||||||
|       this.processing = true |       this.processing = true | ||||||
|       this.$axios |       this.$axios | ||||||
|         .$post(`/api/items/${this.libraryItem.id}/close-feed`) |         .$post(`/api/feeds/${this.currentFeed.id}/close`) | ||||||
|         .then(() => { |         .then(() => { | ||||||
|           this.$toast.success(this.$strings.ToastRSSFeedCloseSuccess) |           this.$toast.success(this.$strings.ToastRSSFeedCloseSuccess) | ||||||
|           this.show = false |           this.show = false | ||||||
|           this.processing = false |  | ||||||
|         }) |         }) | ||||||
|         .catch((error) => { |         .catch((error) => { | ||||||
|           console.error('Failed to close RSS feed', error) |           console.error('Failed to close RSS feed', error) | ||||||
|           this.processing = false |  | ||||||
|           this.$toast.error(this.$strings.ToastRSSFeedCloseFailed) |           this.$toast.error(this.$strings.ToastRSSFeedCloseFailed) | ||||||
|         }) |         }) | ||||||
|  |         .finally(() => { | ||||||
|  |           this.processing = false | ||||||
|  |         }) | ||||||
|     }, |     }, | ||||||
|     init() { |     init() { | ||||||
|       if (!this.libraryItem) return |       if (!this.libraryItem) return | ||||||
|       this.newFeedSlug = this.libraryItem.id |       this.newFeedSlug = this.libraryItem.id | ||||||
|       this.currentFeedUrl = this.feedUrl |       this.currentFeed = this.feed | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   mounted() {} |   mounted() {} | ||||||
|  | |||||||
| @ -174,7 +174,7 @@ | |||||||
| 
 | 
 | ||||||
|             <!-- RSS feed --> |             <!-- RSS feed --> | ||||||
|             <ui-tooltip v-if="showRssFeedBtn" :text="$strings.LabelOpenRSSFeed" direction="top"> |             <ui-tooltip v-if="showRssFeedBtn" :text="$strings.LabelOpenRSSFeed" direction="top"> | ||||||
|               <ui-icon-btn icon="rss_feed" class="mx-0.5" :bg-color="rssFeedUrl ? 'success' : 'primary'" outlined @click="clickRSSFeed" /> |               <ui-icon-btn icon="rss_feed" class="mx-0.5" :bg-color="rssFeed ? 'success' : 'primary'" outlined @click="clickRSSFeed" /> | ||||||
|             </ui-tooltip> |             </ui-tooltip> | ||||||
|           </div> |           </div> | ||||||
| 
 | 
 | ||||||
| @ -200,7 +200,7 @@ | |||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <modals-podcast-episode-feed v-model="showPodcastEpisodeFeed" :library-item="libraryItem" :episodes="podcastFeedEpisodes" /> |     <modals-podcast-episode-feed v-model="showPodcastEpisodeFeed" :library-item="libraryItem" :episodes="podcastFeedEpisodes" /> | ||||||
|     <modals-rssfeed-view-modal v-model="showRssFeedModal" :library-item="libraryItem" :feed-url="rssFeedUrl" /> |     <modals-rssfeed-view-modal v-model="showRssFeedModal" :library-item="libraryItem" :feed="rssFeed" /> | ||||||
|     <modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :library-item-id="libraryItemId" hide-create @select="selectBookmark" /> |     <modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :library-item-id="libraryItemId" hide-create @select="selectBookmark" /> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| @ -223,7 +223,7 @@ export default { | |||||||
|     } |     } | ||||||
|     return { |     return { | ||||||
|       libraryItem: item, |       libraryItem: item, | ||||||
|       rssFeedUrl: item.rssFeedUrl || null |       rssFeed: item.rssFeed || null | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   data() { |   data() { | ||||||
| @ -432,10 +432,10 @@ export default { | |||||||
|       return this.$store.getters['user/getUserCanDownload'] |       return this.$store.getters['user/getUserCanDownload'] | ||||||
|     }, |     }, | ||||||
|     showRssFeedBtn() { |     showRssFeedBtn() { | ||||||
|       if (!this.rssFeedUrl && !this.podcastEpisodes.length && !this.tracks.length) return false // Cannot open RSS feed with no episodes/tracks |       if (!this.rssFeed && !this.podcastEpisodes.length && !this.tracks.length) return false // Cannot open RSS feed with no episodes/tracks | ||||||
| 
 | 
 | ||||||
|       // If rss feed is open then show feed url to users otherwise just show to admins |       // If rss feed is open then show feed url to users otherwise just show to admins | ||||||
|       return this.userIsAdminOrUp || this.rssFeedUrl |       return this.userIsAdminOrUp || this.rssFeed | ||||||
|     }, |     }, | ||||||
|     showQueueBtn() { |     showQueueBtn() { | ||||||
|       if (!this.isBook) return false |       if (!this.isBook) return false | ||||||
| @ -655,13 +655,13 @@ export default { | |||||||
|     rssFeedOpen(data) { |     rssFeedOpen(data) { | ||||||
|       if (data.entityId === this.libraryItemId) { |       if (data.entityId === this.libraryItemId) { | ||||||
|         console.log('RSS Feed Opened', data) |         console.log('RSS Feed Opened', data) | ||||||
|         this.rssFeedUrl = data.feedUrl |         this.rssFeed = data | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     rssFeedClosed(data) { |     rssFeedClosed(data) { | ||||||
|       if (data.entityId === this.libraryItemId) { |       if (data.entityId === this.libraryItemId) { | ||||||
|         console.log('RSS Feed Closed', data) |         console.log('RSS Feed Closed', data) | ||||||
|         this.rssFeedUrl = null |         this.rssFeed = null | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     queueBtnClick() { |     queueBtnClick() { | ||||||
|  | |||||||
| @ -21,8 +21,8 @@ class LibraryItemController { | |||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (includeEntities.includes('rssfeed')) { |       if (includeEntities.includes('rssfeed')) { | ||||||
|         var feedData = this.rssFeedManager.findFeedForItem(item.id) |         const feedData = this.rssFeedManager.findFeedForItem(item.id) | ||||||
|         item.rssFeedUrl = feedData ? feedData.feedUrl : null |         item.rssFeed = feedData ? feedData.toJSONMinified() : null | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (item.mediaType == 'book') { |       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) { |   async toneScan(req, res) { | ||||||
|     if (!req.libraryItem.media.audioFiles.length) { |     if (!req.libraryItem.media.audioFiles.length) { | ||||||
|       return res.sendStatus(404) |       return res.sendStatus(404) | ||||||
| @ -481,7 +449,7 @@ class LibraryItemController { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   middleware(req, res, next) { |   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) |     if (!item || !item.media) return res.sendStatus(404) | ||||||
| 
 | 
 | ||||||
|     // Check user can access this library item
 |     // Check user can access this library item
 | ||||||
|  | |||||||
							
								
								
									
										68
									
								
								server/controllers/RSSFeedController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								server/controllers/RSSFeedController.js
									
									
									
									
									
										Normal 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() | ||||||
| @ -18,10 +18,10 @@ class RssFeedManager { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async init() { |   async init() { | ||||||
|     var feedObjects = await this.db.getAllEntities('feed') |     const feedObjects = await this.db.getAllEntities('feed') | ||||||
|     if (feedObjects && feedObjects.length) { |     if (feedObjects && feedObjects.length) { | ||||||
|       feedObjects.forEach((feedObj) => { |       feedObjects.forEach((feedObj) => { | ||||||
|         var feed = new Feed(feedObj) |         const feed = new Feed(feedObj) | ||||||
|         this.feeds[feed.id] = feed |         this.feeds[feed.id] = feed | ||||||
|         Logger.info(`[RssFeedManager] Opened rss feed ${feed.feedUrl}`) |         Logger.info(`[RssFeedManager] Opened rss feed ${feed.feedUrl}`) | ||||||
|       }) |       }) | ||||||
| @ -32,8 +32,12 @@ class RssFeedManager { | |||||||
|     return Object.values(this.feeds).find(feed => feed.entityId === libraryItemId) |     return Object.values(this.feeds).find(feed => feed.entityId === libraryItemId) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   findFeed(feedId) { | ||||||
|  |     return this.feeds[feedId] || null | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   async getFeed(req, res) { |   async getFeed(req, res) { | ||||||
|     var feed = this.feeds[req.params.id] |     const feed = this.feeds[req.params.id] | ||||||
|     if (!feed) { |     if (!feed) { | ||||||
|       Logger.debug(`[RssFeedManager] Feed not found ${req.params.id}`) |       Logger.debug(`[RssFeedManager] Feed not found ${req.params.id}`) | ||||||
|       res.sendStatus(404) |       res.sendStatus(404) | ||||||
| @ -49,19 +53,19 @@ class RssFeedManager { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     var xml = feed.buildXml() |     const xml = feed.buildXml() | ||||||
|     res.set('Content-Type', 'text/xml') |     res.set('Content-Type', 'text/xml') | ||||||
|     res.send(xml) |     res.send(xml) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getFeedItem(req, res) { |   getFeedItem(req, res) { | ||||||
|     var feed = this.feeds[req.params.id] |     const feed = this.feeds[req.params.id] | ||||||
|     if (!feed) { |     if (!feed) { | ||||||
|       Logger.debug(`[RssFeedManager] Feed not found ${req.params.id}`) |       Logger.debug(`[RssFeedManager] Feed not found ${req.params.id}`) | ||||||
|       res.sendStatus(404) |       res.sendStatus(404) | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
|     var episodePath = feed.getEpisodePath(req.params.episodeId) |     const episodePath = feed.getEpisodePath(req.params.episodeId) | ||||||
|     if (!episodePath) { |     if (!episodePath) { | ||||||
|       Logger.error(`[RssFeedManager] Feed episode not found ${req.params.episodeId}`) |       Logger.error(`[RssFeedManager] Feed episode not found ${req.params.episodeId}`) | ||||||
|       res.sendStatus(404) |       res.sendStatus(404) | ||||||
| @ -71,7 +75,7 @@ class RssFeedManager { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getFeedCover(req, res) { |   getFeedCover(req, res) { | ||||||
|     var feed = this.feeds[req.params.id] |     const feed = this.feeds[req.params.id] | ||||||
|     if (!feed) { |     if (!feed) { | ||||||
|       Logger.debug(`[RssFeedManager] Feed not found ${req.params.id}`) |       Logger.debug(`[RssFeedManager] Feed not found ${req.params.id}`) | ||||||
|       res.sendStatus(404) |       res.sendStatus(404) | ||||||
| @ -85,7 +89,7 @@ class RssFeedManager { | |||||||
| 
 | 
 | ||||||
|     const extname = Path.extname(feed.coverPath).toLowerCase().slice(1) |     const extname = Path.extname(feed.coverPath).toLowerCase().slice(1) | ||||||
|     res.type(`image/${extname}`) |     res.type(`image/${extname}`) | ||||||
|     var readStream = fs.createReadStream(feed.coverPath) |     const readStream = fs.createReadStream(feed.coverPath) | ||||||
|     readStream.pipe(res) |     readStream.pipe(res) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -93,32 +97,25 @@ class RssFeedManager { | |||||||
|     const serverAddress = options.serverAddress |     const serverAddress = options.serverAddress | ||||||
|     const slug = options.slug |     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() |     const feed = new Feed() | ||||||
|     feed.setFromItem(user.id, slug, libraryItem, serverAddress) |     feed.setFromItem(user.id, slug, libraryItem, serverAddress) | ||||||
|     this.feeds[feed.id] = feed |     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) |     await this.db.insertEntity('feed', feed) | ||||||
|     SocketAuthority.emitter('rss_feed_open', feed.toJSONMinified()) |     SocketAuthority.emitter('rss_feed_open', feed.toJSONMinified()) | ||||||
|     return feed |     return feed | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   closeFeedForItem(libraryItemId) { |   closeFeedForItem(libraryItemId) { | ||||||
|     var feed = this.findFeedForItem(libraryItemId) |     const feed = this.findFeedForItem(libraryItemId) | ||||||
|     if (!feed) return |     if (!feed) return | ||||||
|     return this.closeRssFeed(feed.id) |     return this.closeRssFeed(feed.id) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async closeRssFeed(id) { |   async closeRssFeed(id) { | ||||||
|     if (!this.feeds[id]) return |     if (!this.feeds[id]) return | ||||||
|     var feed = this.feeds[id] |     const feed = this.feeds[id] | ||||||
|     await this.db.removeEntity('feed', id) |     await this.db.removeEntity('feed', id) | ||||||
|     SocketAuthority.emitter('rss_feed_closed', feed.toJSONMinified()) |     SocketAuthority.emitter('rss_feed_closed', feed.toJSONMinified()) | ||||||
|     delete this.feeds[id] |     delete this.feeds[id] | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ const NotificationController = require('../controllers/NotificationController') | |||||||
| const SearchController = require('../controllers/SearchController') | const SearchController = require('../controllers/SearchController') | ||||||
| const CacheController = require('../controllers/CacheController') | const CacheController = require('../controllers/CacheController') | ||||||
| const ToolsController = require('../controllers/ToolsController') | const ToolsController = require('../controllers/ToolsController') | ||||||
|  | const RSSFeedController = require('../controllers/RSSFeedController') | ||||||
| const MiscController = require('../controllers/MiscController') | const MiscController = require('../controllers/MiscController') | ||||||
| 
 | 
 | ||||||
| const BookFinder = require('../finders/BookFinder') | 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/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/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/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/:id/tone-scan/:index?', LibraryItemController.middleware.bind(this), LibraryItemController.toneScan.bind(this)) | ||||||
| 
 | 
 | ||||||
|     this.router.post('/items/batch/delete', LibraryItemController.batchDelete.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)) |     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.get('/notifications', NotificationController.middleware.bind(this), NotificationController.get.bind(this)) | ||||||
|     this.router.patch('/notifications', NotificationController.middleware.bind(this), NotificationController.update.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)) |     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/purge', CacheController.purgeCache.bind(this)) | ||||||
|     this.router.post('/cache/items/purge', CacheController.purgeItemsCache.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.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.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)) |     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
 |     // Misc Routes
 | ||||||
|     //
 |     //
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user