diff --git a/client/components/modals/rssfeed/ViewModal.vue b/client/components/modals/rssfeed/ViewModal.vue
index 82a7193a..cc8240ac 100644
--- a/client/components/modals/rssfeed/ViewModal.vue
+++ b/client/components/modals/rssfeed/ViewModal.vue
@@ -6,18 +6,27 @@
-
+
Podcast RSS Feed is Open
-
+
- content_copy
+ content_copy
+
+
+
+
Open RSS Feed
+
+
+
+
Feed will be {{ demoFeedUrl }}
-
Close RSS Feed
+
Close RSS Feed
+
Open RSS Feed
@@ -35,7 +44,9 @@ export default {
},
data() {
return {
- processing: false
+ processing: false,
+ newFeedSlug: null,
+ currentFeedUrl: null
}
},
watch: {
@@ -57,6 +68,9 @@ export default {
this.$emit('input', val)
}
},
+ libraryItemId() {
+ return this.libraryItem.id
+ },
media() {
return this.libraryItem.media || {}
},
@@ -68,9 +82,48 @@ export default {
},
userIsAdminOrUp() {
return this.$store.getters['user/getIsAdminOrUp']
+ },
+ demoFeedUrl() {
+ return `${window.origin}/feed/${this.newFeedSlug}`
}
},
methods: {
+ openFeed() {
+ if (!this.newFeedSlug) {
+ this.$toast.error('Must set a feed slug')
+ return
+ }
+
+ var sanitized = this.$sanitizeSlug(this.newFeedSlug)
+ if (this.newFeedSlug !== sanitized) {
+ this.newFeedSlug = sanitized
+ this.$toast.warning('Slug had to be modified - Run again')
+ return
+ }
+
+ const payload = {
+ serverAddress: window.origin,
+ slug: this.newFeedSlug
+ }
+ if (this.$isDev) payload.serverAddress = 'http://localhost:3333'
+
+ console.log('Payload', payload)
+ this.$axios
+ .$post(`/api/podcasts/${this.libraryItemId}/open-feed`, payload)
+ .then((data) => {
+ if (data.success) {
+ console.log('Opened RSS Feed', data)
+ this.currentFeedUrl = data.feedUrl
+ } else {
+ const errorMsg = data.error || 'Unknown error'
+ this.$toast.error(errorMsg)
+ }
+ })
+ .catch((error) => {
+ console.error('Failed to open RSS Feed', error)
+ this.$toast.error()
+ })
+ },
copyToClipboard(str) {
this.$copyToClipboard(str, this)
},
@@ -89,7 +142,11 @@ export default {
this.$toast.error()
})
},
- init() {}
+ init() {
+ if (!this.libraryItem) return
+ this.newFeedSlug = this.libraryItem.id
+ this.currentFeedUrl = this.feedUrl
+ }
},
mounted() {}
}
diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue
index dbca379e..5361be48 100644
--- a/client/pages/item/_id/index.vue
+++ b/client/pages/item/_id/index.vue
@@ -492,33 +492,7 @@ export default {
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
- }
- if (this.$isDev) payload.serverAddress = 'http://localhost:3333'
-
- console.log('Payload', payload)
- this.$axios
- .$post(`/api/podcasts/${this.libraryItemId}/open-feed`, payload)
- .then((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)
- })
+ this.showRssFeedModal = true
},
episodeDownloadQueued(episodeDownload) {
if (episodeDownload.libraryItemId === this.libraryItemId) {
diff --git a/client/plugins/init.client.js b/client/plugins/init.client.js
index fe640a13..71585580 100644
--- a/client/plugins/init.client.js
+++ b/client/plugins/init.client.js
@@ -125,6 +125,31 @@ Vue.prototype.$sanitizeFilename = (input, replacement = '') => {
return sanitized
}
+// SOURCE: https://gist.github.com/spyesx/561b1d65d4afb595f295
+// modified: allowed underscores
+Vue.prototype.$sanitizeSlug = (str) => {
+ if (!str) return ''
+
+ str = str.replace(/^\s+|\s+$/g, '') // trim
+ str = str.toLowerCase()
+
+ // remove accents, swap ñ for n, etc
+ var from = "àáäâèéëêìíïîòóöôùúüûñçěščřžýúůďťň·/,:;"
+ var to = "aaaaeeeeiiiioooouuuuncescrzyuudtn-----"
+
+ for (var i = 0, l = from.length; i < l; i++) {
+ str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i))
+ }
+
+ str = str.replace('.', '-') // replace a dot by a dash
+ .replace(/[^a-z0-9 -_]/g, '') // remove invalid chars
+ .replace(/\s+/g, '-') // collapse whitespace and replace by a dash
+ .replace(/-+/g, '-') // collapse dashes
+ .replace(/\//g, '') // collapse all forward-slashes
+
+ return str
+}
+
Vue.prototype.$copyToClipboard = (str, ctx) => {
return new Promise((resolve) => {
if (!navigator.clipboard) {
diff --git a/server/controllers/PodcastController.js b/server/controllers/PodcastController.js
index ea22ce0e..4c54a262 100644
--- a/server/controllers/PodcastController.js
+++ b/server/controllers/PodcastController.js
@@ -173,6 +173,12 @@ class PodcastController {
}
const feedData = this.rssFeedManager.openPodcastFeed(req.user, req.libraryItem, req.body)
+ if (feedData.error) {
+ return res.json({
+ success: false,
+ error: feedData.error
+ })
+ }
res.json({
success: true,
diff --git a/server/managers/RssFeedManager.js b/server/managers/RssFeedManager.js
index 6250a5e5..454e8a75 100644
--- a/server/managers/RssFeedManager.js
+++ b/server/managers/RssFeedManager.js
@@ -59,23 +59,23 @@ class RssFeedManager {
readStream.pipe(res)
}
- openFeed(userId, feedId, libraryItem, serverAddress) {
+ openFeed(userId, slug, libraryItem, serverAddress) {
const podcast = libraryItem.media
- const feedUrl = `${serverAddress}/feed/${feedId}`
+ const feedUrl = `${serverAddress}/feed/${slug}`
// Removed Podcast npm package and ip package
const feed = new Podcast({
title: podcast.metadata.title,
description: podcast.metadata.description,
feedUrl,
siteUrl: serverAddress,
- imageUrl: podcast.coverPath ? `${serverAddress}/feed/${feedId}/cover` : `${serverAddress}/Logo.png`,
+ imageUrl: podcast.coverPath ? `${serverAddress}/feed/${slug}/cover` : `${serverAddress}/Logo.png`,
author: podcast.metadata.author || 'advplyr',
language: 'en'
})
podcast.episodes.forEach((episode) => {
var contentUrl = episode.audioTrack.contentUrl.replace(/\\/g, '/')
- contentUrl = contentUrl.replace(`/s/item/${libraryItem.id}`, `/feed/${feedId}/item`)
+ contentUrl = contentUrl.replace(`/s/item/${libraryItem.id}`, `/feed/${slug}/item`)
feed.addItem({
title: episode.title,
@@ -92,7 +92,8 @@ class RssFeedManager {
})
const feedData = {
- id: feedId,
+ id: slug,
+ slug,
userId,
libraryItemId: libraryItem.id,
libraryItemPath: libraryItem.path,
@@ -101,14 +102,22 @@ class RssFeedManager {
feedUrl,
feed
}
- this.feeds[feedId] = feedData
+ this.feeds[slug] = feedData
return feedData
}
openPodcastFeed(user, libraryItem, options) {
const serverAddress = options.serverAddress
- const feedId = getId('feed')
- const feedData = this.openFeed(user.id, feedId, libraryItem, serverAddress)
+ const slug = options.slug
+
+ if (this.feeds[slug]) {
+ Logger.error(`[RssFeedManager] Slug already in use`)
+ return {
+ error: `Slug "${slug}" already in use`
+ }
+ }
+
+ const feedData = this.openFeed(user.id, slug, libraryItem, serverAddress)
Logger.debug(`[RssFeedManager] Opened podcast feed ${feedData.feedUrl}`)
this.emitter('rss_feed_open', { libraryItemId: libraryItem.id, feedUrl: feedData.feedUrl })
return feedData