mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Refactor RssFeedManager to use new model when closing feeds, fix close series feed when series is removed, update RssFeedManager to singleton
This commit is contained in:
		
							parent
							
								
									e50bd93958
								
							
						
					
					
						commit
						4c68ad46f4
					
				| @ -71,7 +71,6 @@ class Server { | ||||
|     this.playbackSessionManager = new PlaybackSessionManager() | ||||
|     this.podcastManager = new PodcastManager() | ||||
|     this.audioMetadataManager = new AudioMetadataMangaer() | ||||
|     this.rssFeedManager = new RssFeedManager() | ||||
|     this.cronManager = new CronManager(this.podcastManager, this.playbackSessionManager) | ||||
|     this.apiCacheManager = new ApiCacheManager() | ||||
|     this.binaryManager = new BinaryManager() | ||||
| @ -137,7 +136,7 @@ class Server { | ||||
| 
 | ||||
|     await ShareManager.init() | ||||
|     await this.backupManager.init() | ||||
|     await this.rssFeedManager.init() | ||||
|     await RssFeedManager.init() | ||||
| 
 | ||||
|     const libraries = await Database.libraryModel.getAllWithFolders() | ||||
|     await this.cronManager.init(libraries) | ||||
| @ -291,14 +290,14 @@ class Server { | ||||
|     // RSS Feed temp route
 | ||||
|     router.get('/feed/:slug', (req, res) => { | ||||
|       Logger.info(`[Server] Requesting rss feed ${req.params.slug}`) | ||||
|       this.rssFeedManager.getFeed(req, res) | ||||
|       RssFeedManager.getFeed(req, res) | ||||
|     }) | ||||
|     router.get('/feed/:slug/cover*', (req, res) => { | ||||
|       this.rssFeedManager.getFeedCover(req, res) | ||||
|       RssFeedManager.getFeedCover(req, res) | ||||
|     }) | ||||
|     router.get('/feed/:slug/item/:episodeId/*', (req, res) => { | ||||
|       Logger.debug(`[Server] Requesting rss feed episode ${req.params.slug}/${req.params.episodeId}`) | ||||
|       this.rssFeedManager.getFeedItem(req, res) | ||||
|       RssFeedManager.getFeedItem(req, res) | ||||
|     }) | ||||
| 
 | ||||
|     // Auth routes
 | ||||
|  | ||||
| @ -4,6 +4,7 @@ const Logger = require('../Logger') | ||||
| const SocketAuthority = require('../SocketAuthority') | ||||
| const Database = require('../Database') | ||||
| 
 | ||||
| const RssFeedManager = require('../managers/RssFeedManager') | ||||
| const Collection = require('../objects/Collection') | ||||
| 
 | ||||
| /** | ||||
| @ -148,6 +149,8 @@ class CollectionController { | ||||
|   /** | ||||
|    * DELETE: /api/collections/:id | ||||
|    * | ||||
|    * @this {import('../routers/ApiRouter')} | ||||
|    * | ||||
|    * @param {RequestWithUser} req | ||||
|    * @param {Response} res | ||||
|    */ | ||||
| @ -155,7 +158,7 @@ class CollectionController { | ||||
|     const jsonExpanded = await req.collection.getOldJsonExpanded() | ||||
| 
 | ||||
|     // Close rss feed - remove from db and emit socket event
 | ||||
|     await this.rssFeedManager.closeFeedForEntityId(req.collection.id) | ||||
|     await RssFeedManager.closeFeedForEntityId(req.collection.id) | ||||
| 
 | ||||
|     await req.collection.destroy() | ||||
| 
 | ||||
|  | ||||
| @ -18,6 +18,8 @@ const LibraryScanner = require('../scanner/LibraryScanner') | ||||
| const Scanner = require('../scanner/Scanner') | ||||
| const Database = require('../Database') | ||||
| const Watcher = require('../Watcher') | ||||
| const RssFeedManager = require('../managers/RssFeedManager') | ||||
| 
 | ||||
| const libraryFilters = require('../utils/queries/libraryFilters') | ||||
| const libraryItemsPodcastFilters = require('../utils/queries/libraryItemsPodcastFilters') | ||||
| const authorFilters = require('../utils/queries/authorFilters') | ||||
| @ -759,7 +761,7 @@ class LibraryController { | ||||
|     } | ||||
| 
 | ||||
|     if (include.includes('rssfeed')) { | ||||
|       const feedObj = await this.rssFeedManager.findFeedForEntityId(seriesJson.id) | ||||
|       const feedObj = await RssFeedManager.findFeedForEntityId(seriesJson.id) | ||||
|       seriesJson.rssFeed = feedObj?.toJSONMinified() || null | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -13,6 +13,8 @@ const { getAudioMimeTypeFromExtname, encodeUriPath } = require('../utils/fileUti | ||||
| const LibraryItemScanner = require('../scanner/LibraryItemScanner') | ||||
| const AudioFileScanner = require('../scanner/AudioFileScanner') | ||||
| const Scanner = require('../scanner/Scanner') | ||||
| 
 | ||||
| const RssFeedManager = require('../managers/RssFeedManager') | ||||
| const CacheManager = require('../managers/CacheManager') | ||||
| const CoverManager = require('../managers/CoverManager') | ||||
| const ShareManager = require('../managers/ShareManager') | ||||
| @ -48,7 +50,7 @@ class LibraryItemController { | ||||
|       } | ||||
| 
 | ||||
|       if (includeEntities.includes('rssfeed')) { | ||||
|         const feedData = await this.rssFeedManager.findFeedForEntityId(item.id) | ||||
|         const feedData = await RssFeedManager.findFeedForEntityId(item.id) | ||||
|         item.rssFeed = feedData?.toJSONMinified() || null | ||||
|       } | ||||
| 
 | ||||
|  | ||||
| @ -1,7 +1,8 @@ | ||||
| const { Request, Response, NextFunction } = require('express') | ||||
| const Logger = require('../Logger') | ||||
| const Database = require('../Database') | ||||
| const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilters') | ||||
| 
 | ||||
| const RssFeedManager = require('../managers/RssFeedManager') | ||||
| 
 | ||||
| /** | ||||
|  * @typedef RequestUserObject | ||||
| @ -22,7 +23,7 @@ class RSSFeedController { | ||||
|    * @param {Response} res | ||||
|    */ | ||||
|   async getAll(req, res) { | ||||
|     const feeds = await this.rssFeedManager.getFeeds() | ||||
|     const feeds = await RssFeedManager.getFeeds() | ||||
|     res.json({ | ||||
|       feeds: feeds.map((f) => f.toJSON()), | ||||
|       minified: feeds.map((f) => f.toJSONMinified()) | ||||
| @ -62,12 +63,12 @@ class RSSFeedController { | ||||
|     } | ||||
| 
 | ||||
|     // Check that this slug is not being used for another feed (slug will also be the Feed id)
 | ||||
|     if (await this.rssFeedManager.findFeedBySlug(reqBody.slug)) { | ||||
|     if (await RssFeedManager.findFeedBySlug(reqBody.slug)) { | ||||
|       Logger.error(`[RSSFeedController] Cannot open RSS feed because slug "${reqBody.slug}" is already in use`) | ||||
|       return res.status(400).send('Slug already in use') | ||||
|     } | ||||
| 
 | ||||
|     const feed = await this.rssFeedManager.openFeedForItem(req.user.id, itemExpanded, reqBody) | ||||
|     const feed = await RssFeedManager.openFeedForItem(req.user.id, itemExpanded, reqBody) | ||||
|     if (!feed) { | ||||
|       Logger.error(`[RSSFeedController] Failed to open RSS feed for item "${itemExpanded.media.title}"`) | ||||
|       return res.status(500).send('Failed to open RSS feed') | ||||
| @ -99,7 +100,7 @@ class RSSFeedController { | ||||
|     } | ||||
| 
 | ||||
|     // Check that this slug is not being used for another feed (slug will also be the Feed id)
 | ||||
|     if (await this.rssFeedManager.findFeedBySlug(reqBody.slug)) { | ||||
|     if (await RssFeedManager.findFeedBySlug(reqBody.slug)) { | ||||
|       Logger.error(`[RSSFeedController] Cannot open RSS feed because slug "${reqBody.slug}" is already in use`) | ||||
|       return res.status(400).send('Slug already in use') | ||||
|     } | ||||
| @ -112,7 +113,7 @@ class RSSFeedController { | ||||
|       return res.status(400).send('Collection has no audio tracks') | ||||
|     } | ||||
| 
 | ||||
|     const feed = await this.rssFeedManager.openFeedForCollection(req.user.id, collection, reqBody) | ||||
|     const feed = await RssFeedManager.openFeedForCollection(req.user.id, collection, reqBody) | ||||
|     if (!feed) { | ||||
|       Logger.error(`[RSSFeedController] Failed to open RSS feed for collection "${collection.name}"`) | ||||
|       return res.status(500).send('Failed to open RSS feed') | ||||
| @ -144,7 +145,7 @@ class RSSFeedController { | ||||
|     } | ||||
| 
 | ||||
|     // Check that this slug is not being used for another feed (slug will also be the Feed id)
 | ||||
|     if (await this.rssFeedManager.findFeedBySlug(reqBody.slug)) { | ||||
|     if (await RssFeedManager.findFeedBySlug(reqBody.slug)) { | ||||
|       Logger.error(`[RSSFeedController] Cannot open RSS feed because slug "${reqBody.slug}" is already in use`) | ||||
|       return res.status(400).send('Slug already in use') | ||||
|     } | ||||
| @ -157,7 +158,7 @@ class RSSFeedController { | ||||
|       return res.status(400).send('Series has no audio tracks') | ||||
|     } | ||||
| 
 | ||||
|     const feed = await this.rssFeedManager.openFeedForSeries(req.user.id, series, req.body) | ||||
|     const feed = await RssFeedManager.openFeedForSeries(req.user.id, series, req.body) | ||||
|     if (!feed) { | ||||
|       Logger.error(`[RSSFeedController] Failed to open RSS feed for series "${series.name}"`) | ||||
|       return res.status(500).send('Failed to open RSS feed') | ||||
| @ -176,8 +177,16 @@ class RSSFeedController { | ||||
|    * @param {RequestWithUser} req | ||||
|    * @param {Response} res | ||||
|    */ | ||||
|   closeRSSFeed(req, res) { | ||||
|     this.rssFeedManager.closeRssFeed(req, res) | ||||
|   async closeRSSFeed(req, res) { | ||||
|     const feed = await Database.feedModel.findByPk(req.params.id) | ||||
|     if (!feed) { | ||||
|       Logger.error(`[RSSFeedController] Cannot close RSS feed because feed "${req.params.id}" does not exist`) | ||||
|       return res.sendStatus(404) | ||||
|     } | ||||
| 
 | ||||
|     await RssFeedManager.handleCloseFeed(feed) | ||||
| 
 | ||||
|     res.sendStatus(200) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|  | ||||
| @ -2,6 +2,9 @@ const { Request, Response, NextFunction } = require('express') | ||||
| const Logger = require('../Logger') | ||||
| const SocketAuthority = require('../SocketAuthority') | ||||
| const Database = require('../Database') | ||||
| 
 | ||||
| const RssFeedManager = require('../managers/RssFeedManager') | ||||
| 
 | ||||
| const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilters') | ||||
| 
 | ||||
| /** | ||||
| @ -51,7 +54,7 @@ class SeriesController { | ||||
|     } | ||||
| 
 | ||||
|     if (include.includes('rssfeed')) { | ||||
|       const feedObj = await this.rssFeedManager.findFeedForEntityId(seriesJson.id) | ||||
|       const feedObj = await RssFeedManager.findFeedForEntityId(seriesJson.id) | ||||
|       seriesJson.rssFeed = feedObj?.toJSONMinified() || null | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,6 @@ const SocketAuthority = require('../SocketAuthority') | ||||
| const Database = require('../Database') | ||||
| 
 | ||||
| const fs = require('../libs/fsExtra') | ||||
| const Feed = require('../objects/Feed') | ||||
| const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilters') | ||||
| 
 | ||||
| class RssFeedManager { | ||||
| @ -69,15 +68,6 @@ class RssFeedManager { | ||||
|     return Database.feedModel.findOneOld({ slug }) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Find open feed for a slug | ||||
|    * @param {string} slug | ||||
|    * @returns {Promise<objects.Feed>} oldFeed | ||||
|    */ | ||||
|   findFeed(id) { | ||||
|     return Database.feedModel.findByPkOld(id) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * GET: /feed/:slug | ||||
|    * | ||||
| @ -303,33 +293,57 @@ class RssFeedManager { | ||||
|     return feedExpanded | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Close Feed and emit Socket event | ||||
|    * | ||||
|    * @param {import('../models/Feed')} feed | ||||
|    * @returns {Promise<boolean>} - true if feed was closed | ||||
|    */ | ||||
|   async handleCloseFeed(feed) { | ||||
|     if (!feed) return | ||||
|     await Database.removeFeed(feed.id) | ||||
|     SocketAuthority.emitter('rss_feed_closed', feed.toJSONMinified()) | ||||
|     Logger.info(`[RssFeedManager] Closed RSS feed "${feed.feedUrl}"`) | ||||
|   } | ||||
| 
 | ||||
|   async closeRssFeed(req, res) { | ||||
|     const feed = await this.findFeed(req.params.id) | ||||
|     if (!feed) { | ||||
|       Logger.error(`[RssFeedManager] RSS feed not found with id "${req.params.id}"`) | ||||
|       return res.sendStatus(404) | ||||
|     } | ||||
|     await this.handleCloseFeed(feed) | ||||
|     res.sendStatus(200) | ||||
|     if (!feed) return false | ||||
|     const wasRemoved = await Database.feedModel.removeById(feed.id) | ||||
|     SocketAuthority.emitter('rss_feed_closed', feed.toOldJSONMinified()) | ||||
|     Logger.info(`[RssFeedManager] Closed RSS feed "${feed.feedURL}"`) | ||||
|     return wasRemoved | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * | ||||
|    * @param {string} entityId | ||||
|    * @returns {Promise<boolean>} - true if feed was closed | ||||
|    */ | ||||
|   async closeFeedForEntityId(entityId) { | ||||
|     const feed = await this.findFeedForEntityId(entityId) | ||||
|     if (!feed) return | ||||
|     const feed = await Database.feedModel.findOne({ | ||||
|       where: { | ||||
|         entityId | ||||
|       } | ||||
|     }) | ||||
|     if (!feed) { | ||||
|       Logger.warn(`[RssFeedManager] closeFeedForEntityId: Feed not found for entity id ${entityId}`) | ||||
|       return false | ||||
|     } | ||||
|     return this.handleCloseFeed(feed) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * | ||||
|    * @param {string[]} entityIds | ||||
|    */ | ||||
|   async closeFeedsForEntityIds(entityIds) { | ||||
|     const feeds = await Database.feedModel.findAll({ | ||||
|       where: { | ||||
|         entityId: entityIds | ||||
|       } | ||||
|     }) | ||||
|     for (const feed of feeds) { | ||||
|       await this.handleCloseFeed(feed) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   async getFeeds() { | ||||
|     const feeds = await Database.models.feed.getOldFeeds() | ||||
|     Logger.info(`[RssFeedManager] Fetched all feeds`) | ||||
|     return feeds | ||||
|   } | ||||
| } | ||||
| module.exports = RssFeedManager | ||||
| module.exports = new RssFeedManager() | ||||
|  | ||||
| @ -124,12 +124,18 @@ class Feed extends Model { | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   static removeById(feedId) { | ||||
|     return this.destroy({ | ||||
|       where: { | ||||
|         id: feedId | ||||
|       } | ||||
|     }) | ||||
|   /** | ||||
|    * @param {string} feedId | ||||
|    * @returns {Promise<boolean>} - true if feed was removed | ||||
|    */ | ||||
|   static async removeById(feedId) { | ||||
|     return ( | ||||
|       (await this.destroy({ | ||||
|         where: { | ||||
|           id: feedId | ||||
|         } | ||||
|       })) > 0 | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
| @ -163,22 +169,6 @@ class Feed extends Model { | ||||
|     return this.getOldFeed(feedExpanded) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Find feed and return oldFeed | ||||
|    * @param {string} id | ||||
|    * @returns {Promise<oldFeed>} oldFeed | ||||
|    */ | ||||
|   static async findByPkOld(id) { | ||||
|     if (!id) return null | ||||
|     const feedExpanded = await this.findByPk(id, { | ||||
|       include: { | ||||
|         model: this.sequelize.models.feedEpisode | ||||
|       } | ||||
|     }) | ||||
|     if (!feedExpanded) return null | ||||
|     return this.getOldFeed(feedExpanded) | ||||
|   } | ||||
| 
 | ||||
|   static async fullCreateFromOld(oldFeed) { | ||||
|     const feedObj = this.getFromOld(oldFeed) | ||||
|     const newFeed = await this.create(feedObj) | ||||
|  | ||||
| @ -10,6 +10,7 @@ const fs = require('../libs/fsExtra') | ||||
| const date = require('../libs/dateAndTime') | ||||
| 
 | ||||
| const CacheManager = require('../managers/CacheManager') | ||||
| const RssFeedManager = require('../managers/RssFeedManager') | ||||
| 
 | ||||
| const LibraryController = require('../controllers/LibraryController') | ||||
| const UserController = require('../controllers/UserController') | ||||
| @ -49,8 +50,6 @@ class ApiRouter { | ||||
|     this.podcastManager = Server.podcastManager | ||||
|     /** @type {import('../managers/AudioMetadataManager')} */ | ||||
|     this.audioMetadataManager = Server.audioMetadataManager | ||||
|     /** @type {import('../managers/RssFeedManager')} */ | ||||
|     this.rssFeedManager = Server.rssFeedManager | ||||
|     /** @type {import('../managers/CronManager')} */ | ||||
|     this.cronManager = Server.cronManager | ||||
|     /** @type {import('../managers/EmailManager')} */ | ||||
| @ -394,7 +393,7 @@ class ApiRouter { | ||||
|     } | ||||
| 
 | ||||
|     // Close rss feed - remove from db and emit socket event
 | ||||
|     await this.rssFeedManager.closeFeedForEntityId(libraryItemId) | ||||
|     await RssFeedManager.closeFeedForEntityId(libraryItemId) | ||||
| 
 | ||||
|     // purge cover cache
 | ||||
|     await CacheManager.purgeCoverCache(libraryItemId) | ||||
| @ -493,7 +492,7 @@ class ApiRouter { | ||||
|    * @param {import('../models/Series')} series | ||||
|    */ | ||||
|   async removeEmptySeries(series) { | ||||
|     await this.rssFeedManager.closeFeedForEntityId(series.id) | ||||
|     await RssFeedManager.closeFeedForEntityId(series.id) | ||||
|     Logger.info(`[ApiRouter] Series "${series.name}" is now empty. Removing series`) | ||||
| 
 | ||||
|     // Remove series from library filter data
 | ||||
|  | ||||
| @ -6,21 +6,24 @@ const { getTitleIgnorePrefix, areEquivalent } = require('../utils/index') | ||||
| const parseNameString = require('../utils/parsers/parseNameString') | ||||
| const parseEbookMetadata = require('../utils/parsers/parseEbookMetadata') | ||||
| const globals = require('../utils/globals') | ||||
| const { readTextFile, filePathToPOSIX, getFileTimestampsWithIno } = require('../utils/fileUtils') | ||||
| 
 | ||||
| const AudioFileScanner = require('./AudioFileScanner') | ||||
| const Database = require('../Database') | ||||
| const { readTextFile, filePathToPOSIX, getFileTimestampsWithIno } = require('../utils/fileUtils') | ||||
| const AudioFile = require('../objects/files/AudioFile') | ||||
| const CoverManager = require('../managers/CoverManager') | ||||
| const LibraryFile = require('../objects/files/LibraryFile') | ||||
| const SocketAuthority = require('../SocketAuthority') | ||||
| const fsExtra = require('../libs/fsExtra') | ||||
| const BookFinder = require('../finders/BookFinder') | ||||
| const fsExtra = require('../libs/fsExtra') | ||||
| const EBookFile = require('../objects/files/EBookFile') | ||||
| const AudioFile = require('../objects/files/AudioFile') | ||||
| const LibraryFile = require('../objects/files/LibraryFile') | ||||
| 
 | ||||
| const RssFeedManager = require('../managers/RssFeedManager') | ||||
| const CoverManager = require('../managers/CoverManager') | ||||
| 
 | ||||
| const LibraryScan = require('./LibraryScan') | ||||
| const OpfFileScanner = require('./OpfFileScanner') | ||||
| const NfoFileScanner = require('./NfoFileScanner') | ||||
| const AbsMetadataFileScanner = require('./AbsMetadataFileScanner') | ||||
| const EBookFile = require('../objects/files/EBookFile') | ||||
| 
 | ||||
| /** | ||||
|  * Metadata for books pulled from files | ||||
| @ -941,6 +944,9 @@ class BookScanner { | ||||
|           id: bookSeriesToRemove | ||||
|         } | ||||
|       }) | ||||
|       // Close any open feeds for series
 | ||||
|       await RssFeedManager.closeFeedsForEntityIds(bookSeriesToRemove) | ||||
| 
 | ||||
|       bookSeriesToRemove.forEach((seriesId) => { | ||||
|         Database.removeSeriesFromFilterData(libraryId, seriesId) | ||||
|         SocketAuthority.emitter('series_removed', { id: seriesId, libraryId }) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user