diff --git a/client/components/modals/rssfeed/ViewModal.vue b/client/components/modals/rssfeed/ViewModal.vue
new file mode 100644
index 00000000..82a7193a
--- /dev/null
+++ b/client/components/modals/rssfeed/ViewModal.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
Podcast RSS Feed is Open
+
+
+
+
+ content_copy
+
+
+
+
+
+
+
+
diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue
index b57566a2..dbca379e 100644
--- a/client/pages/item/_id/index.vue
+++ b/client/pages/item/_id/index.vue
@@ -158,8 +158,8 @@
-
-
+
+
@@ -183,6 +183,7 @@
+
@@ -194,7 +195,7 @@ export default {
}
// Include episode downloads for podcasts
- var item = await app.$axios.$get(`/api/items/${params.id}?expanded=1&include=authors,downloads`).catch((error) => {
+ var item = await app.$axios.$get(`/api/items/${params.id}?expanded=1&include=authors,downloads,rssfeed`).catch((error) => {
console.error('Failed', error)
return false
})
@@ -203,7 +204,8 @@ export default {
return redirect('/')
}
return {
- libraryItem: item
+ libraryItem: item,
+ rssFeedUrl: item.rssFeedUrl || null
}
},
data() {
@@ -214,7 +216,8 @@ export default {
showPodcastEpisodeFeed: false,
podcastFeedEpisodes: [],
episodesDownloading: [],
- episodeDownloadsQueued: []
+ episodeDownloadsQueued: [],
+ showRssFeedModal: false
}
},
computed: {
@@ -373,6 +376,11 @@ export default {
},
userCanDownload() {
return this.$store.getters['user/getUserCanDownload']
+ },
+ showRssFeedBtn() {
+ if (!this.showExperimentalFeatures) return false
+ // If rss feed is open then show feed url to users otherwise just show to admins
+ return this.isPodcast && (this.userIsAdminOrUp || this.rssFeedUrl)
}
},
methods: {
@@ -483,6 +491,15 @@ export default {
this.$store.commit('setSelectedLibraryItem', this.libraryItem)
this.$store.commit('globals/setShowUserCollectionsModal', true)
},
+ clickRSSFeed() {
+ if (!this.rssFeedUrl) {
+ if (confirm(`Are you sure you want to open an RSS Feed for this podcast?`)) {
+ this.openRSSFeed()
+ }
+ } else {
+ this.showRssFeedModal = true
+ }
+ },
openRSSFeed() {
const payload = {
serverAddress: window.origin
@@ -493,7 +510,11 @@ export default {
this.$axios
.$post(`/api/podcasts/${this.libraryItemId}/open-feed`, payload)
.then((data) => {
- console.log('Opened RSS Feed', data)
+ if (data.success) {
+ console.log('Opened RSS Feed', data)
+ this.rssFeedUrl = data.feedUrl
+ this.showRssFeedModal = true
+ }
})
.catch((error) => {
console.error('Failed to open RSS Feed', error)
@@ -515,6 +536,18 @@ export default {
this.episodeDownloadsQueued = this.episodeDownloadsQueued.filter((d) => d.id !== episodeDownload.id)
this.episodesDownloading = this.episodesDownloading.filter((d) => d.id !== episodeDownload.id)
}
+ },
+ rssFeedOpen(data) {
+ if (data.libraryItemId === this.libraryItemId) {
+ console.log('RSS Feed Opened', data)
+ this.rssFeedUrl = data.feedUrl
+ }
+ },
+ rssFeedClosed(data) {
+ if (data.libraryItemId === this.libraryItemId) {
+ console.log('RSS Feed Closed', data)
+ this.rssFeedUrl = null
+ }
}
},
mounted() {
@@ -527,12 +560,16 @@ export default {
this.$store.commit('libraries/setCurrentLibrary', this.libraryId)
}
this.$root.socket.on('item_updated', this.libraryItemUpdated)
+ this.$root.socket.on('rss_feed_open', this.rssFeedOpen)
+ this.$root.socket.on('rss_feed_closed', this.rssFeedClosed)
this.$root.socket.on('episode_download_queued', this.episodeDownloadQueued)
this.$root.socket.on('episode_download_started', this.episodeDownloadStarted)
this.$root.socket.on('episode_download_finished', this.episodeDownloadFinished)
},
beforeDestroy() {
this.$root.socket.off('item_updated', this.libraryItemUpdated)
+ this.$root.socket.off('rss_feed_open', this.rssFeedOpen)
+ this.$root.socket.off('rss_feed_closed', this.rssFeedClosed)
this.$root.socket.off('episode_download_queued', this.episodeDownloadQueued)
this.$root.socket.off('episode_download_started', this.episodeDownloadStarted)
this.$root.socket.off('episode_download_finished', this.episodeDownloadFinished)
diff --git a/server/Server.js b/server/Server.js
index b3c9a159..70da7c86 100644
--- a/server/Server.js
+++ b/server/Server.js
@@ -75,7 +75,7 @@ class Server {
this.coverManager = new CoverManager(this.db, this.cacheManager)
this.podcastManager = new PodcastManager(this.db, this.watcher, this.emitter.bind(this))
this.audioMetadataManager = new AudioMetadataMangaer(this.db, this.emitter.bind(this), this.clientEmitter.bind(this))
- this.rssFeedManager = new RssFeedManager(this.db)
+ this.rssFeedManager = new RssFeedManager(this.db, this.emitter.bind(this))
this.scanner = new Scanner(this.db, this.coverManager, this.emitter.bind(this))
@@ -205,6 +205,9 @@ class Server {
Logger.info(`[Server] requesting rss feed ${req.params.id}`)
this.rssFeedManager.getFeed(req, res)
})
+ app.get('/feed/:id/cover', (req, res) => {
+ this.rssFeedManager.getFeedCover(req, res)
+ })
app.get('/feed/:id/item/*', (req, res) => {
Logger.info(`[Server] requesting rss feed ${req.params.id}`)
this.rssFeedManager.getFeedItem(req, res)
diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js
index e89605ba..91928d1c 100644
--- a/server/controllers/LibraryItemController.js
+++ b/server/controllers/LibraryItemController.js
@@ -17,6 +17,11 @@ class LibraryItemController {
item.userMediaProgress = req.user.getMediaProgress(item.id, episodeId)
}
+ if (includeEntities.includes('rssfeed')) {
+ var feedData = this.rssFeedManager.findFeedForItem(item.id)
+ item.rssFeedUrl = feedData ? feedData.feedUrl : null
+ }
+
if (item.mediaType == 'book') {
if (includeEntities.includes('authors')) {
item.media.metadata.authors = item.media.metadata.authors.map(au => {
diff --git a/server/controllers/PodcastController.js b/server/controllers/PodcastController.js
index 88cc0888..ea22ce0e 100644
--- a/server/controllers/PodcastController.js
+++ b/server/controllers/PodcastController.js
@@ -168,7 +168,7 @@ class PodcastController {
async openPodcastFeed(req, res) {
if (!req.user.isAdminOrUp) {
- Logger.error(`[PodcastController] Non-admin user attempted to open podcast feed`, req.user)
+ Logger.error(`[PodcastController] Non-admin user attempted to open podcast feed`, req.user.username)
return res.sendStatus(500)
}
@@ -180,6 +180,17 @@ class PodcastController {
})
}
+ async closePodcastFeed(req, res) {
+ if (!req.user.isAdminOrUp) {
+ Logger.error(`[PodcastController] Non-admin user attempted to close podcast feed`, req.user.username)
+ return res.sendStatus(500)
+ }
+
+ this.rssFeedManager.closePodcastFeedForItem(req.params.id)
+
+ res.sendStatus(200)
+ }
+
async updateEpisode(req, res) {
var libraryItem = req.libraryItem
diff --git a/server/managers/RssFeedManager.js b/server/managers/RssFeedManager.js
index 867ce187..39dc96d2 100644
--- a/server/managers/RssFeedManager.js
+++ b/server/managers/RssFeedManager.js
@@ -5,11 +5,16 @@ const Logger = require('../Logger')
// Not functional at the moment
class RssFeedManager {
- constructor(db) {
+ constructor(db, emitter) {
this.db = db
+ this.emitter = emitter
this.feeds = {}
}
+ findFeedForItem(libraryItemId) {
+ return Object.values(this.feeds).find(feed => feed.libraryItemId === libraryItemId)
+ }
+
getFeed(req, res) {
var feedData = this.feeds[req.params.id]
if (!feedData) {
@@ -34,7 +39,26 @@ class RssFeedManager {
res.sendFile(fullPath)
}
- openFeed(feedId, libraryItem, serverAddress) {
+ getFeedCover(req, res) {
+ var feedData = this.feeds[req.params.id]
+ if (!feedData) {
+ Logger.error(`[RssFeedManager] Feed not found ${req.params.id}`)
+ res.sendStatus(404)
+ return
+ }
+
+ if (!feedData.mediaCoverPath) {
+ res.sendStatus(404)
+ return
+ }
+
+ const extname = Path.extname(feedData.mediaCoverPath).toLowerCase().slice(1)
+ res.type(`image/${extname}`)
+ var readStream = fs.createReadStream(feedData.mediaCoverPath)
+ readStream.pipe(res)
+ }
+
+ openFeed(userId, feedId, libraryItem, serverAddress) {
const podcast = libraryItem.media
const feedUrl = `${serverAddress}/feed/${feedId}`
@@ -43,7 +67,8 @@ class RssFeedManager {
title: podcast.metadata.title,
description: podcast.metadata.description,
feedUrl,
- imageUrl: `${serverAddress}/Logo.png`,
+ siteUrl: serverAddress,
+ imageUrl: podcast.coverPath ? `${serverAddress}/feed/${feedId}/cover` : `${serverAddress}/Logo.png`,
author: podcast.metadata.author || 'advplyr',
language: 'en'
})
@@ -59,6 +84,7 @@ class RssFeedManager {
type: episode.audioTrack.mimeType,
size: episode.size
},
+ date: episode.pubDate || '',
url: `${serverAddress}${contentUrl}`,
author: podcast.metadata.author || 'advplyr'
})
@@ -66,8 +92,10 @@ class RssFeedManager {
const feedData = {
id: feedId,
+ userId,
libraryItemId: libraryItem.id,
libraryItemPath: libraryItem.path,
+ mediaCoverPath: podcast.coverPath,
serverAddress: serverAddress,
feedUrl,
feed
@@ -79,9 +107,24 @@ class RssFeedManager {
openPodcastFeed(user, libraryItem, options) {
const serverAddress = options.serverAddress
const feedId = getId('feed')
- const feedData = this.openFeed(feedId, libraryItem, serverAddress)
+ const feedData = this.openFeed(user.id, feedId, libraryItem, serverAddress)
Logger.debug(`[RssFeedManager] Opened podcast feed ${feedData.feedUrl}`)
+ this.emitter('rss_feed_open', { libraryItemId: libraryItem.id, feedUrl: feedData.feedUrl })
return feedData
}
+
+ closePodcastFeedForItem(libraryItemId) {
+ var feed = this.findFeedForItem(libraryItemId)
+ if (!feed) return
+ this.closeRssFeed(feed.id)
+ }
+
+ closeRssFeed(id) {
+ if (!this.feeds[id]) return
+ var feedData = this.feeds[id]
+ this.emitter('rss_feed_closed', { libraryItemId: feedData.libraryItemId, feedUrl: feedData.feedUrl })
+ delete this.feeds[id]
+ Logger.info(`[RssFeedManager] Closed RSS feed "${feedData.feedUrl}"`)
+ }
}
module.exports = RssFeedManager
\ No newline at end of file
diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js
index 2e280efd..834ca62b 100644
--- a/server/routers/ApiRouter.js
+++ b/server/routers/ApiRouter.js
@@ -186,6 +186,7 @@ class ApiRouter {
this.router.get('/podcasts/:id/clear-queue', PodcastController.middleware.bind(this), PodcastController.clearEpisodeDownloadQueue.bind(this))
this.router.post('/podcasts/:id/download-episodes', PodcastController.middleware.bind(this), PodcastController.downloadEpisodes.bind(this))
this.router.post('/podcasts/:id/open-feed', PodcastController.middleware.bind(this), PodcastController.openPodcastFeed.bind(this))
+ this.router.post('/podcasts/:id/close-feed', PodcastController.middleware.bind(this), PodcastController.closePodcastFeed.bind(this))
this.router.patch('/podcasts/:id/episode/:episodeId', PodcastController.middleware.bind(this), PodcastController.updateEpisode.bind(this))
//