mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-02-01 00:18:14 +01:00
Fix:Podcast episodes store RSS feed guid so they can be matched if the RSS feed changes the episode URL #2207
This commit is contained in:
parent
48a590df4a
commit
0d5792405f
@ -16,11 +16,11 @@
|
|||||||
v-for="(episode, index) in episodesList"
|
v-for="(episode, index) in episodesList"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="relative"
|
class="relative"
|
||||||
:class="itemEpisodeMap[episode.cleanUrl] ? 'bg-primary bg-opacity-40' : selectedEpisodes[episode.cleanUrl] ? 'cursor-pointer bg-success bg-opacity-10' : index % 2 == 0 ? 'cursor-pointer bg-primary bg-opacity-25 hover:bg-opacity-40' : 'cursor-pointer bg-primary bg-opacity-5 hover:bg-opacity-25'"
|
:class="getIsEpisodeDownloaded(episode) ? 'bg-primary bg-opacity-40' : selectedEpisodes[episode.cleanUrl] ? 'cursor-pointer bg-success bg-opacity-10' : index % 2 == 0 ? 'cursor-pointer bg-primary bg-opacity-25 hover:bg-opacity-40' : 'cursor-pointer bg-primary bg-opacity-5 hover:bg-opacity-25'"
|
||||||
@click="toggleSelectEpisode(episode)"
|
@click="toggleSelectEpisode(episode)"
|
||||||
>
|
>
|
||||||
<div class="absolute top-0 left-0 h-full flex items-center p-2">
|
<div class="absolute top-0 left-0 h-full flex items-center p-2">
|
||||||
<span v-if="itemEpisodeMap[episode.cleanUrl]" class="material-icons text-success text-xl">download_done</span>
|
<span v-if="getIsEpisodeDownloaded(episode)" class="material-icons text-success text-xl">download_done</span>
|
||||||
<ui-checkbox v-else v-model="selectedEpisodes[episode.cleanUrl]" small checkbox-bg="primary" border-color="gray-600" />
|
<ui-checkbox v-else v-model="selectedEpisodes[episode.cleanUrl]" small checkbox-bg="primary" border-color="gray-600" />
|
||||||
</div>
|
</div>
|
||||||
<div class="px-8 py-2">
|
<div class="px-8 py-2">
|
||||||
@ -93,7 +93,7 @@ export default {
|
|||||||
return this.libraryItem.media.metadata.title || 'Unknown'
|
return this.libraryItem.media.metadata.title || 'Unknown'
|
||||||
},
|
},
|
||||||
allDownloaded() {
|
allDownloaded() {
|
||||||
return !this.episodesCleaned.some((episode) => !this.itemEpisodeMap[episode.cleanUrl])
|
return !this.episodesCleaned.some((episode) => this.getIsEpisodeDownloaded(episode))
|
||||||
},
|
},
|
||||||
episodesSelected() {
|
episodesSelected() {
|
||||||
return Object.keys(this.selectedEpisodes).filter((key) => !!this.selectedEpisodes[key])
|
return Object.keys(this.selectedEpisodes).filter((key) => !!this.selectedEpisodes[key])
|
||||||
@ -104,18 +104,7 @@ export default {
|
|||||||
return this.$getString('LabelDownloadNEpisodes', [this.episodesSelected.length])
|
return this.$getString('LabelDownloadNEpisodes', [this.episodesSelected.length])
|
||||||
},
|
},
|
||||||
itemEpisodes() {
|
itemEpisodes() {
|
||||||
if (!this.libraryItem) return []
|
return this.libraryItem?.media.episodes || []
|
||||||
return this.libraryItem.media.episodes || []
|
|
||||||
},
|
|
||||||
itemEpisodeMap() {
|
|
||||||
const map = {}
|
|
||||||
this.itemEpisodes.forEach((item) => {
|
|
||||||
if (item.enclosure) {
|
|
||||||
const cleanUrl = this.getCleanEpisodeUrl(item.enclosure.url)
|
|
||||||
map[cleanUrl] = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return map
|
|
||||||
},
|
},
|
||||||
episodesList() {
|
episodesList() {
|
||||||
return this.episodesCleaned.filter((episode) => {
|
return this.episodesCleaned.filter((episode) => {
|
||||||
@ -127,12 +116,23 @@ export default {
|
|||||||
if (this.episodesList.length === this.episodesCleaned.length) {
|
if (this.episodesList.length === this.episodesCleaned.length) {
|
||||||
return this.$strings.LabelSelectAllEpisodes
|
return this.$strings.LabelSelectAllEpisodes
|
||||||
}
|
}
|
||||||
const episodesNotDownloaded = this.episodesList.filter((ep) => !this.itemEpisodeMap[ep.cleanUrl]).length
|
const episodesNotDownloaded = this.episodesList.filter((ep) => !this.getIsEpisodeDownloaded(ep)).length
|
||||||
return this.$getString('LabelSelectEpisodesShowing', [episodesNotDownloaded])
|
return this.$getString('LabelSelectEpisodesShowing', [episodesNotDownloaded])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getIsEpisodeDownloaded(episode) {
|
||||||
|
return this.itemEpisodes.some((downloadedEpisode) => {
|
||||||
|
if (episode.guid && downloadedEpisode.guid === episode.guid) return true
|
||||||
|
if (!downloadedEpisode.enclosure?.url) return false
|
||||||
|
return this.getCleanEpisodeUrl(downloadedEpisode.enclosure.url) === episode.cleanUrl
|
||||||
|
})
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
|
* UPDATE: As of v2.4.5 guid is used for matching existing downloaded episodes if it is found on the RSS feed.
|
||||||
|
* Fallback to checking the clean url
|
||||||
|
* @see https://github.com/advplyr/audiobookshelf/issues/2207
|
||||||
|
*
|
||||||
* RSS feed episode url is used for matching with existing downloaded episodes.
|
* RSS feed episode url is used for matching with existing downloaded episodes.
|
||||||
* Some RSS feeds include timestamps in the episode url (e.g. patreon) that can change on requests.
|
* Some RSS feeds include timestamps in the episode url (e.g. patreon) that can change on requests.
|
||||||
* These need to be removed in order to detect the same episode each time the feed is pulled.
|
* These need to be removed in order to detect the same episode each time the feed is pulled.
|
||||||
@ -169,13 +169,13 @@ export default {
|
|||||||
},
|
},
|
||||||
toggleSelectAll(val) {
|
toggleSelectAll(val) {
|
||||||
for (const episode of this.episodesList) {
|
for (const episode of this.episodesList) {
|
||||||
if (this.itemEpisodeMap[episode.cleanUrl]) this.selectedEpisodes[episode.cleanUrl] = false
|
if (this.getIsEpisodeDownloaded(episode)) this.selectedEpisodes[episode.cleanUrl] = false
|
||||||
else this.$set(this.selectedEpisodes, episode.cleanUrl, val)
|
else this.$set(this.selectedEpisodes, episode.cleanUrl, val)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
checkSetIsSelectedAll() {
|
checkSetIsSelectedAll() {
|
||||||
for (const episode of this.episodesList) {
|
for (const episode of this.episodesList) {
|
||||||
if (!this.itemEpisodeMap[episode.cleanUrl] && !this.selectedEpisodes[episode.cleanUrl]) {
|
if (!this.getIsEpisodeDownloaded(episode) && !this.selectedEpisodes[episode.cleanUrl]) {
|
||||||
this.selectAll = false
|
this.selectAll = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -183,7 +183,7 @@ export default {
|
|||||||
this.selectAll = true
|
this.selectAll = true
|
||||||
},
|
},
|
||||||
toggleSelectEpisode(episode) {
|
toggleSelectEpisode(episode) {
|
||||||
if (this.itemEpisodeMap[episode.cleanUrl]) return
|
if (this.getIsEpisodeDownloaded(episode)) return
|
||||||
this.$set(this.selectedEpisodes, episode.cleanUrl, !this.selectedEpisodes[episode.cleanUrl])
|
this.$set(this.selectedEpisodes, episode.cleanUrl, !this.selectedEpisodes[episode.cleanUrl])
|
||||||
this.checkSetIsSelectedAll()
|
this.checkSetIsSelectedAll()
|
||||||
},
|
},
|
||||||
|
@ -184,10 +184,9 @@ class PodcastController {
|
|||||||
Logger.error(`[PodcastController] Non-admin user attempted to download episodes`, req.user)
|
Logger.error(`[PodcastController] Non-admin user attempted to download episodes`, req.user)
|
||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
}
|
}
|
||||||
var libraryItem = req.libraryItem
|
const libraryItem = req.libraryItem
|
||||||
|
const episodes = req.body
|
||||||
var episodes = req.body
|
if (!episodes?.length) {
|
||||||
if (!episodes || !episodes.length) {
|
|
||||||
return res.sendStatus(400)
|
return res.sendStatus(400)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ class PodcastManager {
|
|||||||
})
|
})
|
||||||
// TODO: Should we check for open playback sessions for this episode?
|
// TODO: Should we check for open playback sessions for this episode?
|
||||||
// TODO: remove all user progress for this episode
|
// TODO: remove all user progress for this episode
|
||||||
if (oldestEpisode && oldestEpisode.audioFile) {
|
if (oldestEpisode?.audioFile) {
|
||||||
Logger.info(`[PodcastManager] Deleting oldest episode "${oldestEpisode.title}"`)
|
Logger.info(`[PodcastManager] Deleting oldest episode "${oldestEpisode.title}"`)
|
||||||
const successfullyDeleted = await removeFile(oldestEpisode.audioFile.metadata.path)
|
const successfullyDeleted = await removeFile(oldestEpisode.audioFile.metadata.path)
|
||||||
if (successfullyDeleted) {
|
if (successfullyDeleted) {
|
||||||
@ -246,7 +246,7 @@ class PodcastManager {
|
|||||||
Logger.debug(`[PodcastManager] runEpisodeCheck: "${libraryItem.media.metadata.title}" checking for episodes after ${new Date(dateToCheckForEpisodesAfter)}`)
|
Logger.debug(`[PodcastManager] runEpisodeCheck: "${libraryItem.media.metadata.title}" checking for episodes after ${new Date(dateToCheckForEpisodesAfter)}`)
|
||||||
|
|
||||||
var newEpisodes = await this.checkPodcastForNewEpisodes(libraryItem, dateToCheckForEpisodesAfter, libraryItem.media.maxNewEpisodesToDownload)
|
var newEpisodes = await this.checkPodcastForNewEpisodes(libraryItem, dateToCheckForEpisodesAfter, libraryItem.media.maxNewEpisodesToDownload)
|
||||||
Logger.debug(`[PodcastManager] runEpisodeCheck: ${newEpisodes ? newEpisodes.length : 'N/A'} episodes found`)
|
Logger.debug(`[PodcastManager] runEpisodeCheck: ${newEpisodes?.length || 'N/A'} episodes found`)
|
||||||
|
|
||||||
if (!newEpisodes) { // Failed
|
if (!newEpisodes) { // Failed
|
||||||
// Allow up to MaxFailedEpisodeChecks failed attempts before disabling auto download
|
// Allow up to MaxFailedEpisodeChecks failed attempts before disabling auto download
|
||||||
@ -280,14 +280,14 @@ class PodcastManager {
|
|||||||
Logger.error(`[PodcastManager] checkPodcastForNewEpisodes no feed url for ${podcastLibraryItem.media.metadata.title} (ID: ${podcastLibraryItem.id})`)
|
Logger.error(`[PodcastManager] checkPodcastForNewEpisodes no feed url for ${podcastLibraryItem.media.metadata.title} (ID: ${podcastLibraryItem.id})`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
var feed = await getPodcastFeed(podcastLibraryItem.media.metadata.feedUrl)
|
const feed = await getPodcastFeed(podcastLibraryItem.media.metadata.feedUrl)
|
||||||
if (!feed || !feed.episodes) {
|
if (!feed?.episodes) {
|
||||||
Logger.error(`[PodcastManager] checkPodcastForNewEpisodes invalid feed payload for ${podcastLibraryItem.media.metadata.title} (ID: ${podcastLibraryItem.id})`, feed)
|
Logger.error(`[PodcastManager] checkPodcastForNewEpisodes invalid feed payload for ${podcastLibraryItem.media.metadata.title} (ID: ${podcastLibraryItem.id})`, feed)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter new and not already has
|
// Filter new and not already has
|
||||||
var newEpisodes = feed.episodes.filter(ep => ep.publishedAt > dateToCheckForEpisodesAfter && !podcastLibraryItem.media.checkHasEpisodeByFeedUrl(ep.enclosure.url))
|
let newEpisodes = feed.episodes.filter(ep => ep.publishedAt > dateToCheckForEpisodesAfter && !podcastLibraryItem.media.checkHasEpisodeByFeedUrl(ep.enclosure.url))
|
||||||
|
|
||||||
if (maxNewEpisodes > 0) {
|
if (maxNewEpisodes > 0) {
|
||||||
newEpisodes = newEpisodes.slice(0, maxNewEpisodes)
|
newEpisodes = newEpisodes.slice(0, maxNewEpisodes)
|
||||||
|
@ -79,6 +79,7 @@ class PodcastEpisode extends Model {
|
|||||||
subtitle: this.subtitle,
|
subtitle: this.subtitle,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
enclosure,
|
enclosure,
|
||||||
|
guid: this.extraData?.guid || null,
|
||||||
pubDate: this.pubDate,
|
pubDate: this.pubDate,
|
||||||
chapters: this.chapters,
|
chapters: this.chapters,
|
||||||
audioFile: this.audioFile,
|
audioFile: this.audioFile,
|
||||||
@ -98,6 +99,9 @@ class PodcastEpisode extends Model {
|
|||||||
if (oldEpisode.oldEpisodeId) {
|
if (oldEpisode.oldEpisodeId) {
|
||||||
extraData.oldEpisodeId = oldEpisode.oldEpisodeId
|
extraData.oldEpisodeId = oldEpisode.oldEpisodeId
|
||||||
}
|
}
|
||||||
|
if (oldEpisode.guid) {
|
||||||
|
extraData.guid = oldEpisode.guid
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
id: oldEpisode.id,
|
id: oldEpisode.id,
|
||||||
index: oldEpisode.index,
|
index: oldEpisode.index,
|
||||||
|
@ -20,6 +20,7 @@ class PodcastEpisode {
|
|||||||
this.subtitle = null
|
this.subtitle = null
|
||||||
this.description = null
|
this.description = null
|
||||||
this.enclosure = null
|
this.enclosure = null
|
||||||
|
this.guid = null
|
||||||
this.pubDate = null
|
this.pubDate = null
|
||||||
this.chapters = []
|
this.chapters = []
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ class PodcastEpisode {
|
|||||||
this.subtitle = episode.subtitle
|
this.subtitle = episode.subtitle
|
||||||
this.description = episode.description
|
this.description = episode.description
|
||||||
this.enclosure = episode.enclosure ? { ...episode.enclosure } : null
|
this.enclosure = episode.enclosure ? { ...episode.enclosure } : null
|
||||||
|
this.guid = episode.guid || null
|
||||||
this.pubDate = episode.pubDate
|
this.pubDate = episode.pubDate
|
||||||
this.chapters = episode.chapters?.map(ch => ({ ...ch })) || []
|
this.chapters = episode.chapters?.map(ch => ({ ...ch })) || []
|
||||||
this.audioFile = new AudioFile(episode.audioFile)
|
this.audioFile = new AudioFile(episode.audioFile)
|
||||||
@ -70,6 +72,7 @@ class PodcastEpisode {
|
|||||||
subtitle: this.subtitle,
|
subtitle: this.subtitle,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
enclosure: this.enclosure ? { ...this.enclosure } : null,
|
enclosure: this.enclosure ? { ...this.enclosure } : null,
|
||||||
|
guid: this.guid,
|
||||||
pubDate: this.pubDate,
|
pubDate: this.pubDate,
|
||||||
chapters: this.chapters.map(ch => ({ ...ch })),
|
chapters: this.chapters.map(ch => ({ ...ch })),
|
||||||
audioFile: this.audioFile.toJSON(),
|
audioFile: this.audioFile.toJSON(),
|
||||||
@ -93,6 +96,7 @@ class PodcastEpisode {
|
|||||||
subtitle: this.subtitle,
|
subtitle: this.subtitle,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
enclosure: this.enclosure ? { ...this.enclosure } : null,
|
enclosure: this.enclosure ? { ...this.enclosure } : null,
|
||||||
|
guid: this.guid,
|
||||||
pubDate: this.pubDate,
|
pubDate: this.pubDate,
|
||||||
chapters: this.chapters.map(ch => ({ ...ch })),
|
chapters: this.chapters.map(ch => ({ ...ch })),
|
||||||
audioFile: this.audioFile.toJSON(),
|
audioFile: this.audioFile.toJSON(),
|
||||||
@ -133,6 +137,7 @@ class PodcastEpisode {
|
|||||||
this.pubDate = data.pubDate || ''
|
this.pubDate = data.pubDate || ''
|
||||||
this.description = data.description || ''
|
this.description = data.description || ''
|
||||||
this.enclosure = data.enclosure ? { ...data.enclosure } : null
|
this.enclosure = data.enclosure ? { ...data.enclosure } : null
|
||||||
|
this.guid = data.guid || null
|
||||||
this.season = data.season || ''
|
this.season = data.season || ''
|
||||||
this.episode = data.episode || ''
|
this.episode = data.episode || ''
|
||||||
this.episodeType = data.episodeType || 'full'
|
this.episodeType = data.episodeType || 'full'
|
||||||
|
@ -4,7 +4,7 @@ const { xmlToJSON, levenshteinDistance } = require('./index')
|
|||||||
const htmlSanitizer = require('../utils/htmlSanitizer')
|
const htmlSanitizer = require('../utils/htmlSanitizer')
|
||||||
|
|
||||||
function extractFirstArrayItem(json, key) {
|
function extractFirstArrayItem(json, key) {
|
||||||
if (!json[key] || !json[key].length) return null
|
if (!json[key]?.length) return null
|
||||||
return json[key][0]
|
return json[key][0]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,13 +110,24 @@ function extractEpisodeData(item) {
|
|||||||
const pubDate = extractFirstArrayItem(item, 'pubDate')
|
const pubDate = extractFirstArrayItem(item, 'pubDate')
|
||||||
if (typeof pubDate === 'string') {
|
if (typeof pubDate === 'string') {
|
||||||
episode.pubDate = pubDate
|
episode.pubDate = pubDate
|
||||||
} else if (pubDate && typeof pubDate._ === 'string') {
|
} else if (typeof pubDate?._ === 'string') {
|
||||||
episode.pubDate = pubDate._
|
episode.pubDate = pubDate._
|
||||||
} else {
|
} else {
|
||||||
Logger.error(`[podcastUtils] Invalid pubDate ${item['pubDate']} for ${episode.enclosure.url}`)
|
Logger.error(`[podcastUtils] Invalid pubDate ${item['pubDate']} for ${episode.enclosure.url}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item['guid']) {
|
||||||
|
const guidItem = extractFirstArrayItem(item, 'guid')
|
||||||
|
if (typeof guidItem === 'string') {
|
||||||
|
episode.guid = guidItem
|
||||||
|
} else if (typeof guidItem?._ === 'string') {
|
||||||
|
episode.guid = guidItem._
|
||||||
|
} else {
|
||||||
|
Logger.error(`[podcastUtils] Invalid guid ${item['guid']} for ${episode.enclosure.url}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const arrayFields = ['title', 'itunes:episodeType', 'itunes:season', 'itunes:episode', 'itunes:author', 'itunes:duration', 'itunes:explicit', 'itunes:subtitle']
|
const arrayFields = ['title', 'itunes:episodeType', 'itunes:season', 'itunes:episode', 'itunes:author', 'itunes:duration', 'itunes:explicit', 'itunes:subtitle']
|
||||||
arrayFields.forEach((key) => {
|
arrayFields.forEach((key) => {
|
||||||
const cleanKey = key.split(':').pop()
|
const cleanKey = key.split(':').pop()
|
||||||
@ -142,6 +153,7 @@ function cleanEpisodeData(data) {
|
|||||||
explicit: data.explicit || '',
|
explicit: data.explicit || '',
|
||||||
publishedAt,
|
publishedAt,
|
||||||
enclosure: data.enclosure,
|
enclosure: data.enclosure,
|
||||||
|
guid: data.guid || null,
|
||||||
chaptersUrl: data.chaptersUrl || null,
|
chaptersUrl: data.chaptersUrl || null,
|
||||||
chaptersType: data.chaptersType || null
|
chaptersType: data.chaptersType || null
|
||||||
}
|
}
|
||||||
@ -159,16 +171,16 @@ function extractPodcastEpisodes(items) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cleanPodcastJson(rssJson, excludeEpisodeMetadata) {
|
function cleanPodcastJson(rssJson, excludeEpisodeMetadata) {
|
||||||
if (!rssJson.channel || !rssJson.channel.length) {
|
if (!rssJson.channel?.length) {
|
||||||
Logger.error(`[podcastUtil] Invalid podcast no channel object`)
|
Logger.error(`[podcastUtil] Invalid podcast no channel object`)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
var channel = rssJson.channel[0]
|
const channel = rssJson.channel[0]
|
||||||
if (!channel.item || !channel.item.length) {
|
if (!channel.item?.length) {
|
||||||
Logger.error(`[podcastUtil] Invalid podcast no episodes`)
|
Logger.error(`[podcastUtil] Invalid podcast no episodes`)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
var podcast = {
|
const podcast = {
|
||||||
metadata: extractPodcastMetadata(channel)
|
metadata: extractPodcastMetadata(channel)
|
||||||
}
|
}
|
||||||
if (!excludeEpisodeMetadata) {
|
if (!excludeEpisodeMetadata) {
|
||||||
@ -181,8 +193,8 @@ function cleanPodcastJson(rssJson, excludeEpisodeMetadata) {
|
|||||||
|
|
||||||
module.exports.parsePodcastRssFeedXml = async (xml, excludeEpisodeMetadata = false, includeRaw = false) => {
|
module.exports.parsePodcastRssFeedXml = async (xml, excludeEpisodeMetadata = false, includeRaw = false) => {
|
||||||
if (!xml) return null
|
if (!xml) return null
|
||||||
var json = await xmlToJSON(xml)
|
const json = await xmlToJSON(xml)
|
||||||
if (!json || !json.rss) {
|
if (!json?.rss) {
|
||||||
Logger.error('[podcastUtils] Invalid XML or RSS feed')
|
Logger.error('[podcastUtils] Invalid XML or RSS feed')
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -215,12 +227,12 @@ module.exports.getPodcastFeed = (feedUrl, excludeEpisodeMetadata = false) => {
|
|||||||
data.data = data.data.toString()
|
data.data = data.data.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data || !data.data) {
|
if (!data?.data) {
|
||||||
Logger.error(`[podcastUtils] getPodcastFeed: Invalid podcast feed request response (${feedUrl})`)
|
Logger.error(`[podcastUtils] getPodcastFeed: Invalid podcast feed request response (${feedUrl})`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
Logger.debug(`[podcastUtils] getPodcastFeed for "${feedUrl}" success - parsing xml`)
|
Logger.debug(`[podcastUtils] getPodcastFeed for "${feedUrl}" success - parsing xml`)
|
||||||
var payload = await this.parsePodcastRssFeedXml(data.data, excludeEpisodeMetadata)
|
const payload = await this.parsePodcastRssFeedXml(data.data, excludeEpisodeMetadata)
|
||||||
if (!payload) {
|
if (!payload) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -246,7 +258,7 @@ module.exports.findMatchingEpisodes = async (feedUrl, searchTitle) => {
|
|||||||
|
|
||||||
module.exports.findMatchingEpisodesInFeed = (feed, searchTitle) => {
|
module.exports.findMatchingEpisodesInFeed = (feed, searchTitle) => {
|
||||||
searchTitle = searchTitle.toLowerCase().trim()
|
searchTitle = searchTitle.toLowerCase().trim()
|
||||||
if (!feed || !feed.episodes) {
|
if (!feed?.episodes) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user