mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-17 00:08:55 +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