mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Add:Experimental generate podcast RSS feed #553
This commit is contained in:
		
							parent
							
								
									8b38dda229
								
							
						
					
					
						commit
						678dceefed
					
				
							
								
								
									
										96
									
								
								client/components/modals/rssfeed/ViewModal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								client/components/modals/rssfeed/ViewModal.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <modals-modal v-model="show" name="rss-feed-modal" :width="600" :height="'unset'" :processing="processing">
 | 
				
			||||||
 | 
					    <template #outer>
 | 
				
			||||||
 | 
					      <div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden">
 | 
				
			||||||
 | 
					        <p class="font-book text-3xl text-white truncate">{{ title }}</p>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </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 class="w-full">
 | 
				
			||||||
 | 
					        <p class="text-lg font-semibold mb-4">Podcast RSS Feed is Open</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="w-full relative">
 | 
				
			||||||
 | 
					          <ui-text-input v-model="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(feedUrl)">content_copy</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div v-show="userIsAdminOrUp" class="flex items-center pt-6">
 | 
				
			||||||
 | 
					        <div class="flex-grow" />
 | 
				
			||||||
 | 
					        <ui-btn color="error" small @click="closeFeed">Close RSS Feed</ui-btn>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </modals-modal>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    value: Boolean,
 | 
				
			||||||
 | 
					    libraryItem: {
 | 
				
			||||||
 | 
					      type: Object,
 | 
				
			||||||
 | 
					      default: () => null
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    feedUrl: String
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      processing: false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  watch: {
 | 
				
			||||||
 | 
					    show: {
 | 
				
			||||||
 | 
					      immediate: true,
 | 
				
			||||||
 | 
					      handler(newVal) {
 | 
				
			||||||
 | 
					        if (newVal) {
 | 
				
			||||||
 | 
					          this.init()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    show: {
 | 
				
			||||||
 | 
					      get() {
 | 
				
			||||||
 | 
					        return this.value
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      set(val) {
 | 
				
			||||||
 | 
					        this.$emit('input', val)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    media() {
 | 
				
			||||||
 | 
					      return this.libraryItem.media || {}
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    mediaMetadata() {
 | 
				
			||||||
 | 
					      return this.media.metadata || {}
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    title() {
 | 
				
			||||||
 | 
					      return this.mediaMetadata.title
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    userIsAdminOrUp() {
 | 
				
			||||||
 | 
					      return this.$store.getters['user/getIsAdminOrUp']
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    copyToClipboard(str) {
 | 
				
			||||||
 | 
					      this.$copyToClipboard(str, this)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    closeFeed() {
 | 
				
			||||||
 | 
					      this.processing = true
 | 
				
			||||||
 | 
					      this.$axios
 | 
				
			||||||
 | 
					        .$post(`/api/podcasts/${this.libraryItem.id}/close-feed`)
 | 
				
			||||||
 | 
					        .then(() => {
 | 
				
			||||||
 | 
					          this.$toast.success('RSS Feed Closed')
 | 
				
			||||||
 | 
					          this.show = false
 | 
				
			||||||
 | 
					          this.processing = false
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch((error) => {
 | 
				
			||||||
 | 
					          console.error('Failed to close RSS feed', error)
 | 
				
			||||||
 | 
					          this.processing = false
 | 
				
			||||||
 | 
					          this.$toast.error()
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    init() {}
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  mounted() {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
@ -158,8 +158,8 @@
 | 
				
			|||||||
            </ui-tooltip>
 | 
					            </ui-tooltip>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Experimental RSS feed open -->
 | 
					            <!-- Experimental RSS feed open -->
 | 
				
			||||||
            <ui-tooltip v-if="isPodcast && showExperimentalFeatures" text="Open RSS Feed" direction="top">
 | 
					            <ui-tooltip v-if="showRssFeedBtn" text="Open RSS Feed" direction="top">
 | 
				
			||||||
              <ui-icon-btn icon="rss_feed" class="mx-0.5" outlined @click="openRSSFeed" />
 | 
					              <ui-icon-btn icon="rss_feed" class="mx-0.5" :bg-color="rssFeedUrl ? 'success' : 'primary'" outlined @click="clickRSSFeed" />
 | 
				
			||||||
            </ui-tooltip>
 | 
					            </ui-tooltip>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -183,6 +183,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" />
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -194,7 +195,7 @@ export default {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Include episode downloads for podcasts
 | 
					    // 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)
 | 
					      console.error('Failed', error)
 | 
				
			||||||
      return false
 | 
					      return false
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
@ -203,7 +204,8 @@ export default {
 | 
				
			|||||||
      return redirect('/')
 | 
					      return redirect('/')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      libraryItem: item
 | 
					      libraryItem: item,
 | 
				
			||||||
 | 
					      rssFeedUrl: item.rssFeedUrl || null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  data() {
 | 
					  data() {
 | 
				
			||||||
@ -214,7 +216,8 @@ export default {
 | 
				
			|||||||
      showPodcastEpisodeFeed: false,
 | 
					      showPodcastEpisodeFeed: false,
 | 
				
			||||||
      podcastFeedEpisodes: [],
 | 
					      podcastFeedEpisodes: [],
 | 
				
			||||||
      episodesDownloading: [],
 | 
					      episodesDownloading: [],
 | 
				
			||||||
      episodeDownloadsQueued: []
 | 
					      episodeDownloadsQueued: [],
 | 
				
			||||||
 | 
					      showRssFeedModal: false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  computed: {
 | 
					  computed: {
 | 
				
			||||||
@ -373,6 +376,11 @@ export default {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    userCanDownload() {
 | 
					    userCanDownload() {
 | 
				
			||||||
      return this.$store.getters['user/getUserCanDownload']
 | 
					      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: {
 | 
					  methods: {
 | 
				
			||||||
@ -483,6 +491,15 @@ export default {
 | 
				
			|||||||
      this.$store.commit('setSelectedLibraryItem', this.libraryItem)
 | 
					      this.$store.commit('setSelectedLibraryItem', this.libraryItem)
 | 
				
			||||||
      this.$store.commit('globals/setShowUserCollectionsModal', true)
 | 
					      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() {
 | 
					    openRSSFeed() {
 | 
				
			||||||
      const payload = {
 | 
					      const payload = {
 | 
				
			||||||
        serverAddress: window.origin
 | 
					        serverAddress: window.origin
 | 
				
			||||||
@ -493,7 +510,11 @@ export default {
 | 
				
			|||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$post(`/api/podcasts/${this.libraryItemId}/open-feed`, payload)
 | 
					        .$post(`/api/podcasts/${this.libraryItemId}/open-feed`, payload)
 | 
				
			||||||
        .then((data) => {
 | 
					        .then((data) => {
 | 
				
			||||||
 | 
					          if (data.success) {
 | 
				
			||||||
            console.log('Opened RSS Feed', data)
 | 
					            console.log('Opened RSS Feed', data)
 | 
				
			||||||
 | 
					            this.rssFeedUrl = data.feedUrl
 | 
				
			||||||
 | 
					            this.showRssFeedModal = true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .catch((error) => {
 | 
					        .catch((error) => {
 | 
				
			||||||
          console.error('Failed to open RSS Feed', 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.episodeDownloadsQueued = this.episodeDownloadsQueued.filter((d) => d.id !== episodeDownload.id)
 | 
				
			||||||
        this.episodesDownloading = this.episodesDownloading.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() {
 | 
					  mounted() {
 | 
				
			||||||
@ -527,12 +560,16 @@ export default {
 | 
				
			|||||||
      this.$store.commit('libraries/setCurrentLibrary', this.libraryId)
 | 
					      this.$store.commit('libraries/setCurrentLibrary', this.libraryId)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.$root.socket.on('item_updated', this.libraryItemUpdated)
 | 
					    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_queued', this.episodeDownloadQueued)
 | 
				
			||||||
    this.$root.socket.on('episode_download_started', this.episodeDownloadStarted)
 | 
					    this.$root.socket.on('episode_download_started', this.episodeDownloadStarted)
 | 
				
			||||||
    this.$root.socket.on('episode_download_finished', this.episodeDownloadFinished)
 | 
					    this.$root.socket.on('episode_download_finished', this.episodeDownloadFinished)
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  beforeDestroy() {
 | 
					  beforeDestroy() {
 | 
				
			||||||
    this.$root.socket.off('item_updated', this.libraryItemUpdated)
 | 
					    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_queued', this.episodeDownloadQueued)
 | 
				
			||||||
    this.$root.socket.off('episode_download_started', this.episodeDownloadStarted)
 | 
					    this.$root.socket.off('episode_download_started', this.episodeDownloadStarted)
 | 
				
			||||||
    this.$root.socket.off('episode_download_finished', this.episodeDownloadFinished)
 | 
					    this.$root.socket.off('episode_download_finished', this.episodeDownloadFinished)
 | 
				
			||||||
 | 
				
			|||||||
@ -75,7 +75,7 @@ class Server {
 | 
				
			|||||||
    this.coverManager = new CoverManager(this.db, this.cacheManager)
 | 
					    this.coverManager = new CoverManager(this.db, this.cacheManager)
 | 
				
			||||||
    this.podcastManager = new PodcastManager(this.db, this.watcher, this.emitter.bind(this))
 | 
					    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.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))
 | 
					    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}`)
 | 
					      Logger.info(`[Server] requesting rss feed ${req.params.id}`)
 | 
				
			||||||
      this.rssFeedManager.getFeed(req, res)
 | 
					      this.rssFeedManager.getFeed(req, res)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					    app.get('/feed/:id/cover', (req, res) => {
 | 
				
			||||||
 | 
					      this.rssFeedManager.getFeedCover(req, res)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    app.get('/feed/:id/item/*', (req, res) => {
 | 
					    app.get('/feed/:id/item/*', (req, res) => {
 | 
				
			||||||
      Logger.info(`[Server] requesting rss feed ${req.params.id}`)
 | 
					      Logger.info(`[Server] requesting rss feed ${req.params.id}`)
 | 
				
			||||||
      this.rssFeedManager.getFeedItem(req, res)
 | 
					      this.rssFeedManager.getFeedItem(req, res)
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,11 @@ class LibraryItemController {
 | 
				
			|||||||
        item.userMediaProgress = req.user.getMediaProgress(item.id, episodeId)
 | 
					        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 (item.mediaType == 'book') {
 | 
				
			||||||
        if (includeEntities.includes('authors')) {
 | 
					        if (includeEntities.includes('authors')) {
 | 
				
			||||||
          item.media.metadata.authors = item.media.metadata.authors.map(au => {
 | 
					          item.media.metadata.authors = item.media.metadata.authors.map(au => {
 | 
				
			||||||
 | 
				
			|||||||
@ -168,7 +168,7 @@ class PodcastController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  async openPodcastFeed(req, res) {
 | 
					  async openPodcastFeed(req, res) {
 | 
				
			||||||
    if (!req.user.isAdminOrUp) {
 | 
					    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)
 | 
					      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) {
 | 
					  async updateEpisode(req, res) {
 | 
				
			||||||
    var libraryItem = req.libraryItem
 | 
					    var libraryItem = req.libraryItem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5,11 +5,16 @@ const Logger = require('../Logger')
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Not functional at the moment
 | 
					// Not functional at the moment
 | 
				
			||||||
class RssFeedManager {
 | 
					class RssFeedManager {
 | 
				
			||||||
  constructor(db) {
 | 
					  constructor(db, emitter) {
 | 
				
			||||||
    this.db = db
 | 
					    this.db = db
 | 
				
			||||||
 | 
					    this.emitter = emitter
 | 
				
			||||||
    this.feeds = {}
 | 
					    this.feeds = {}
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  findFeedForItem(libraryItemId) {
 | 
				
			||||||
 | 
					    return Object.values(this.feeds).find(feed => feed.libraryItemId === libraryItemId)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getFeed(req, res) {
 | 
					  getFeed(req, res) {
 | 
				
			||||||
    var feedData = this.feeds[req.params.id]
 | 
					    var feedData = this.feeds[req.params.id]
 | 
				
			||||||
    if (!feedData) {
 | 
					    if (!feedData) {
 | 
				
			||||||
@ -34,7 +39,26 @@ class RssFeedManager {
 | 
				
			|||||||
    res.sendFile(fullPath)
 | 
					    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 podcast = libraryItem.media
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const feedUrl = `${serverAddress}/feed/${feedId}`
 | 
					    const feedUrl = `${serverAddress}/feed/${feedId}`
 | 
				
			||||||
@ -43,7 +67,8 @@ class RssFeedManager {
 | 
				
			|||||||
      title: podcast.metadata.title,
 | 
					      title: podcast.metadata.title,
 | 
				
			||||||
      description: podcast.metadata.description,
 | 
					      description: podcast.metadata.description,
 | 
				
			||||||
      feedUrl,
 | 
					      feedUrl,
 | 
				
			||||||
      imageUrl: `${serverAddress}/Logo.png`,
 | 
					      siteUrl: serverAddress,
 | 
				
			||||||
 | 
					      imageUrl: podcast.coverPath ? `${serverAddress}/feed/${feedId}/cover` : `${serverAddress}/Logo.png`,
 | 
				
			||||||
      author: podcast.metadata.author || 'advplyr',
 | 
					      author: podcast.metadata.author || 'advplyr',
 | 
				
			||||||
      language: 'en'
 | 
					      language: 'en'
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
@ -59,6 +84,7 @@ class RssFeedManager {
 | 
				
			|||||||
          type: episode.audioTrack.mimeType,
 | 
					          type: episode.audioTrack.mimeType,
 | 
				
			||||||
          size: episode.size
 | 
					          size: episode.size
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        date: episode.pubDate || '',
 | 
				
			||||||
        url: `${serverAddress}${contentUrl}`,
 | 
					        url: `${serverAddress}${contentUrl}`,
 | 
				
			||||||
        author: podcast.metadata.author || 'advplyr'
 | 
					        author: podcast.metadata.author || 'advplyr'
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
@ -66,8 +92,10 @@ class RssFeedManager {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const feedData = {
 | 
					    const feedData = {
 | 
				
			||||||
      id: feedId,
 | 
					      id: feedId,
 | 
				
			||||||
 | 
					      userId,
 | 
				
			||||||
      libraryItemId: libraryItem.id,
 | 
					      libraryItemId: libraryItem.id,
 | 
				
			||||||
      libraryItemPath: libraryItem.path,
 | 
					      libraryItemPath: libraryItem.path,
 | 
				
			||||||
 | 
					      mediaCoverPath: podcast.coverPath,
 | 
				
			||||||
      serverAddress: serverAddress,
 | 
					      serverAddress: serverAddress,
 | 
				
			||||||
      feedUrl,
 | 
					      feedUrl,
 | 
				
			||||||
      feed
 | 
					      feed
 | 
				
			||||||
@ -79,9 +107,24 @@ class RssFeedManager {
 | 
				
			|||||||
  openPodcastFeed(user, libraryItem, options) {
 | 
					  openPodcastFeed(user, libraryItem, options) {
 | 
				
			||||||
    const serverAddress = options.serverAddress
 | 
					    const serverAddress = options.serverAddress
 | 
				
			||||||
    const feedId = getId('feed')
 | 
					    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}`)
 | 
					    Logger.debug(`[RssFeedManager] Opened podcast feed ${feedData.feedUrl}`)
 | 
				
			||||||
 | 
					    this.emitter('rss_feed_open', { libraryItemId: libraryItem.id, feedUrl: feedData.feedUrl })
 | 
				
			||||||
    return feedData
 | 
					    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
 | 
					module.exports = RssFeedManager
 | 
				
			||||||
@ -186,6 +186,7 @@ class ApiRouter {
 | 
				
			|||||||
    this.router.get('/podcasts/:id/clear-queue', PodcastController.middleware.bind(this), PodcastController.clearEpisodeDownloadQueue.bind(this))
 | 
					    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/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/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))
 | 
					    this.router.patch('/podcasts/:id/episode/:episodeId', PodcastController.middleware.bind(this), PodcastController.updateEpisode.bind(this))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user