diff --git a/client/components/modals/podcast/NewModal.vue b/client/components/modals/podcast/NewModal.vue index 9d985809..35e247a5 100644 --- a/client/components/modals/podcast/NewModal.vue +++ b/client/components/modals/podcast/NewModal.vue @@ -56,7 +56,6 @@

{{ episode.title }}

{{ episode.subtitle }}

Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}

- @@ -246,17 +245,15 @@ export default { this.$toast.error(errorMsg) }) }, - saveEpisode(episode) { - console.log('Save episode', episode) - }, init() { - this.podcast.title = this._podcastData.title - this.podcast.author = this._podcastData.artistName || '' - this.podcast.description = this._podcastData.description || this.feedMetadata.description || '' + // Prefer using itunes podcast data but not always passed in if manually entering rss feed + this.podcast.title = this._podcastData.title || this.feedMetadata.title || '' + this.podcast.author = this._podcastData.artistName || this.feedMetadata.author || '' + this.podcast.description = this._podcastData.description || this.feedMetadata.descriptionPlain || '' this.podcast.releaseDate = this._podcastData.releaseDate || '' - this.podcast.genres = this._podcastData.genres || [] - this.podcast.feedUrl = this._podcastData.feedUrl - this.podcast.imageUrl = this._podcastData.cover || '' + this.podcast.genres = this._podcastData.genres || this.feedMetadata.categories || [] + this.podcast.feedUrl = this._podcastData.feedUrl || this.feedMetadata.feedUrl || '' + this.podcast.imageUrl = this._podcastData.cover || this.feedMetadata.image || '' this.podcast.itunesPageUrl = this._podcastData.pageUrl || '' this.podcast.itunesId = this._podcastData.id || '' this.podcast.itunesArtistId = this._podcastData.artistId || '' diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue index 1dbe1fe1..07a24a6e 100644 --- a/client/pages/item/_id/index.vue +++ b/client/pages/item/_id/index.vue @@ -342,15 +342,16 @@ export default { return this.$toast.error('Podcast does not have an RSS Feed') } this.fetchingRSSFeed = true - var podcastfeed = await this.$axios.$post(`/api/podcasts/feed`, { rssFeed: this.mediaMetadata.feedUrl }).catch((error) => { + var payload = await this.$axios.$post(`/api/podcasts/feed`, { rssFeed: this.mediaMetadata.feedUrl }).catch((error) => { console.error('Failed to get feed', error) this.$toast.error('Failed to get podcast feed') return null }) this.fetchingRSSFeed = false - if (!podcastfeed) return + if (!payload) return - console.log('Podcast feed', podcastfeed) + console.log('Podcast feed', payload) + const podcastfeed = payload.podcast if (!podcastfeed.episodes || !podcastfeed.episodes.length) { this.$toast.info('No episodes found in RSS feed') return diff --git a/client/pages/library/_library/podcast/search.vue b/client/pages/library/_library/podcast/search.vue index 4d975462..03b25497 100644 --- a/client/pages/library/_library/podcast/search.vue +++ b/client/pages/library/_library/podcast/search.vue @@ -6,9 +6,9 @@
-
- - Search Podcasts + + + Submit
@@ -54,11 +54,10 @@ export default { }, data() { return { - searchTerm: '', + searchInput: '', results: [], termSearched: '', processing: false, - showNewPodcastModal: false, selectedPodcast: null, selectedPodcastFeed: null @@ -70,13 +69,35 @@ export default { } }, methods: { - async submitSearch() { - if (!this.searchTerm) return - console.log('Searching', this.searchTerm) - var term = this.searchTerm + submit() { + if (!this.searchInput) return + + if (this.searchInput.startsWith('http:') || this.searchInput.startsWith('https:')) { + this.termSearched = '' + this.results = [] + this.checkRSSFeed(this.searchInput) + } else { + this.submitSearch(this.searchInput) + } + }, + async checkRSSFeed(rssFeed) { + this.processing = true + var payload = await this.$axios.$post(`/api/podcasts/feed`, { rssFeed }).catch((error) => { + console.error('Failed to get feed', error) + this.$toast.error('Failed to get podcast feed') + return null + }) + this.processing = false + if (!payload) return + + this.selectedPodcastFeed = payload.podcast + this.selectedPodcast = null + this.showNewPodcastModal = true + }, + async submitSearch(term) { this.processing = true this.termSearched = '' - var results = await this.$axios.$get(`/api/search/podcast?term=${encodeURIComponent(this.searchTerm)}`).catch((error) => { + var results = await this.$axios.$get(`/api/search/podcast?term=${encodeURIComponent(term)}`).catch((error) => { console.error('Search request failed', error) return [] }) @@ -92,17 +113,18 @@ export default { return } this.processing = true - var podcastfeed = await this.$axios.$post(`/api/podcasts/feed`, { rssFeed: podcast.feedUrl }).catch((error) => { + var payload = await this.$axios.$post(`/api/podcasts/feed`, { rssFeed: podcast.feedUrl }).catch((error) => { console.error('Failed to get feed', error) this.$toast.error('Failed to get podcast feed') return null }) this.processing = false - if (!podcastfeed) return - this.selectedPodcastFeed = podcastfeed + if (!payload) return + + this.selectedPodcastFeed = payload.podcast this.selectedPodcast = podcast this.showNewPodcastModal = true - console.log('Got podcast feed', podcastfeed) + console.log('Got podcast feed', payload.podcast) } }, mounted() {} diff --git a/server/controllers/PodcastController.js b/server/controllers/PodcastController.js index 88628ae7..b7774a23 100644 --- a/server/controllers/PodcastController.js +++ b/server/controllers/PodcastController.js @@ -94,17 +94,18 @@ class PodcastController { if (!url) { return res.status(400).send('Bad request') } + var includeRaw = req.query.raw == 1 // Include raw json axios.get(url).then(async (data) => { if (!data || !data.data) { Logger.error('Invalid podcast feed request response') return res.status(500).send('Bad response from feed request') } - var podcast = await parsePodcastRssFeedXml(data.data) - if (!podcast) { + var payload = await parsePodcastRssFeedXml(data.data, includeRaw) + if (!payload) { return res.status(500).send('Invalid podcast RSS feed') } - res.json(podcast) + res.json(payload) }).catch((error) => { console.error('Failed', error) res.status(500).send(error) diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js index 616ae658..2ddc9d06 100644 --- a/server/managers/PodcastManager.js +++ b/server/managers/PodcastManager.js @@ -190,11 +190,11 @@ class PodcastManager { Logger.error('Invalid podcast feed request response') return false } - var podcast = await parsePodcastRssFeedXml(data.data) - if (!podcast) { + var payload = await parsePodcastRssFeedXml(data.data) + if (!payload) { return false } - return podcast + return payload.podcast }).catch((error) => { console.error('Failed', error) return false diff --git a/server/utils/podcastUtils.js b/server/utils/podcastUtils.js index e63b0b3c..a0869c81 100644 --- a/server/utils/podcastUtils.js +++ b/server/utils/podcastUtils.js @@ -1,5 +1,6 @@ const Logger = require('../Logger') const { xmlToJSON } = require('./index') +const { stripHtml } = require('string-strip-html') function extractFirstArrayItem(json, key) { if (!json[key] || !json[key].length) return null @@ -39,11 +40,26 @@ function extractCategories(channel) { } function extractPodcastMetadata(channel) { - var arrayFields = ['title', 'language', 'description', 'itunes:explicit', 'itunes:author'] var metadata = { image: extractImage(channel), - categories: extractCategories(channel) + categories: extractCategories(channel), + feedUrl: null, + description: null, + descriptionPlain: null } + + if (channel['itunes:new-feed-url']) { + metadata.feedUrl = extractFirstArrayItem(channel, 'itunes:new-feed-url') + } else if (channel['atom:link'] && channel['atom:link'].length && channel['atom:link'][0]['$']) { + metadata.feedUrl = channel['atom:link'][0]['$'].href || null + } + + if (channel['description']) { + metadata.description = extractFirstArrayItem(channel, 'description') + metadata.descriptionPlain = stripHtml(metadata.description || '').result + } + + var arrayFields = ['title', 'language', 'itunes:explicit', 'itunes:author', 'pubDate', 'link'] arrayFields.forEach((key) => { var cleanKey = key.split(':').pop() metadata[cleanKey] = extractFirstArrayItem(channel, key) @@ -114,12 +130,25 @@ function cleanPodcastJson(rssJson) { return podcast } -module.exports.parsePodcastRssFeedXml = async (xml) => { +module.exports.parsePodcastRssFeedXml = async (xml, includeRaw = false) => { if (!xml) return null var json = await xmlToJSON(xml) if (!json || !json.rss) { Logger.error('[podcastUtils] Invalid XML or RSS feed') return null } - return cleanPodcastJson(json.rss) + + const podcast = cleanPodcastJson(json.rss) + if (!podcast) return null + + if (includeRaw) { + return { + podcast, + rawJson: json + } + } else { + return { + podcast + } + } } \ No newline at end of file