mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-12-26 16:48:53 +01:00
Add:Schedule podcast new episode checks
This commit is contained in:
parent
a690dfe671
commit
46668854ad
@ -46,12 +46,14 @@ export default {
|
||||
{
|
||||
id: 'chapters',
|
||||
title: 'Chapters',
|
||||
component: 'modals-item-tabs-chapters'
|
||||
component: 'modals-item-tabs-chapters',
|
||||
mediaType: 'book'
|
||||
},
|
||||
{
|
||||
id: 'episodes',
|
||||
title: 'Episodes',
|
||||
component: 'modals-item-tabs-episodes'
|
||||
component: 'modals-item-tabs-episodes',
|
||||
mediaType: 'podcast'
|
||||
},
|
||||
{
|
||||
id: 'files',
|
||||
@ -66,7 +68,16 @@ export default {
|
||||
{
|
||||
id: 'manage',
|
||||
title: 'Manage',
|
||||
component: 'modals-item-tabs-manage'
|
||||
component: 'modals-item-tabs-manage',
|
||||
mediaType: 'book',
|
||||
admin: true
|
||||
},
|
||||
{
|
||||
id: 'schedule',
|
||||
title: 'Schedule',
|
||||
component: 'modals-item-tabs-schedule',
|
||||
mediaType: 'podcast',
|
||||
admin: true
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -120,13 +131,17 @@ export default {
|
||||
userCanDownload() {
|
||||
return this.$store.getters['user/getUserCanDownload']
|
||||
},
|
||||
userIsAdminOrUp() {
|
||||
return this.$store.getters['user/getIsAdminOrUp']
|
||||
},
|
||||
availableTabs() {
|
||||
if (!this.userCanUpdate && !this.userCanDownload) return []
|
||||
return this.tabs.filter((tab) => {
|
||||
if (tab.experimental && !this.showExperimentalFeatures) return false
|
||||
if (tab.id === 'manage' && (this.isMissing || this.mediaType !== 'book')) return false
|
||||
if (this.mediaType == 'podcast' && tab.id == 'chapters') return false
|
||||
if (this.mediaType == 'book' && tab.id == 'episodes') return false
|
||||
if (tab.mediaType && this.mediaType !== tab.mediaType) return false
|
||||
if (tab.admin && !this.userIsAdminOrUp) return false
|
||||
|
||||
if (tab.id === 'manage' && this.isMissing) return false
|
||||
|
||||
if ((tab.id === 'manage' || tab.id === 'files') && this.userCanDownload) return true
|
||||
if (tab.id !== 'manage' && tab.id !== 'files' && this.userCanUpdate) return true
|
||||
|
116
client/components/modals/item/tabs/Schedule.vue
Normal file
116
client/components/modals/item/tabs/Schedule.vue
Normal file
@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="w-full h-full relative">
|
||||
<div id="scheduleWrapper" class="w-full overflow-y-auto px-2 py-4 md:px-4 md:py-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<p class="text-base md:text-lg font-semibold">Schedule Automatic Episode Downloads</p>
|
||||
<ui-checkbox v-model="enableAutoDownloadEpisodes" label="Enable" small checkbox-bg="bg" label-class="pl-2 text-base" />
|
||||
</div>
|
||||
<widgets-cron-expression-builder ref="cronExpressionBuilder" v-if="enableAutoDownloadEpisodes" v-model="cronExpression" @input="updatedCron" />
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-0 left-0 w-full py-2 md:py-4 bg-bg border-t border-white border-opacity-5">
|
||||
<div class="flex items-center px-2 md:px-4">
|
||||
<div class="flex-grow" />
|
||||
<ui-btn @click="save" :disabled="!isUpdated" :color="isUpdated ? 'success' : 'primary'" class="mx-2">{{ isUpdated ? 'Save' : 'No update necessary' }}</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
processing: Boolean,
|
||||
libraryItem: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
enableAutoDownloadEpisodes: false,
|
||||
cronExpression: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isProcessing: {
|
||||
get() {
|
||||
return this.processing
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:processing', val)
|
||||
}
|
||||
},
|
||||
userIsAdminOrUp() {
|
||||
return this.$store.getters['user/getIsAdminOrUp']
|
||||
},
|
||||
autoDownloadEpisodes() {
|
||||
return !!this.media.autoDownloadEpisodes
|
||||
},
|
||||
autoDownloadSchedule() {
|
||||
return this.media.autoDownloadSchedule
|
||||
},
|
||||
media() {
|
||||
return this.libraryItem ? this.libraryItem.media || {} : {}
|
||||
},
|
||||
libraryItemId() {
|
||||
return this.libraryItem ? this.libraryItem.id : null
|
||||
},
|
||||
isUpdated() {
|
||||
return this.autoDownloadSchedule !== this.cronExpression || this.autoDownloadEpisodes !== this.enableAutoDownloadEpisodes
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.enableAutoDownloadEpisodes = this.autoDownloadEpisodes
|
||||
this.cronExpression = this.autoDownloadSchedule
|
||||
},
|
||||
updatedCron() {
|
||||
console.log('Updated cron', this.cronExpression)
|
||||
},
|
||||
save() {
|
||||
// If custom expression input is focused then unfocus it instead of submitting
|
||||
if (this.$refs.cronExpressionBuilder && this.$refs.cronExpressionBuilder.checkBlurExpressionInput) {
|
||||
if (this.$refs.cronExpressionBuilder.checkBlurExpressionInput()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const updatePayload = {
|
||||
autoDownloadEpisodes: this.enableAutoDownloadEpisodes
|
||||
}
|
||||
if (this.enableAutoDownloadEpisodes) {
|
||||
updatePayload.autoDownloadSchedule = this.cronExpression
|
||||
}
|
||||
this.updateDetails(updatePayload)
|
||||
},
|
||||
async updateDetails(updatePayload) {
|
||||
this.isProcessing = true
|
||||
var updateResult = await this.$axios.$patch(`/api/items/${this.libraryItemId}/media`, updatePayload).catch((error) => {
|
||||
console.error('Failed to update', error)
|
||||
return false
|
||||
})
|
||||
this.isProcessing = false
|
||||
if (updateResult) {
|
||||
if (updateResult.updated) {
|
||||
this.$toast.success('Item details updated')
|
||||
return true
|
||||
} else {
|
||||
this.$toast.info('No updates were necessary')
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#scheduleWrapper {
|
||||
height: calc(100% - 80px);
|
||||
max-height: calc(100% - 80px);
|
||||
}
|
||||
</style>
|
@ -75,7 +75,7 @@ class Server {
|
||||
this.rssFeedManager = new RssFeedManager(this.db, this.emitter.bind(this))
|
||||
|
||||
this.scanner = new Scanner(this.db, this.coverManager, this.emitter.bind(this))
|
||||
this.cronManager = new CronManager(this.db, this.scanner)
|
||||
this.cronManager = new CronManager(this.db, this.scanner, this.podcastManager)
|
||||
|
||||
// Routers
|
||||
this.apiRouter = new ApiRouter(this.db, this.auth, this.scanner, this.playbackSessionManager, this.abMergeManager, this.coverManager, this.backupManager, this.watcher, this.cacheManager, this.podcastManager, this.audioMetadataManager, this.rssFeedManager, this.cronManager, this.emitter.bind(this), this.clientEmitter.bind(this))
|
||||
@ -152,7 +152,6 @@ class Server {
|
||||
await this.backupManager.init()
|
||||
await this.logManager.init()
|
||||
await this.rssFeedManager.init()
|
||||
this.podcastManager.init()
|
||||
this.cronManager.init()
|
||||
|
||||
if (this.db.serverSettings.scannerDisableWatcher) {
|
||||
|
@ -79,12 +79,27 @@ class LibraryItemController {
|
||||
await this.cacheManager.purgeCoverCache(libraryItem.id)
|
||||
}
|
||||
|
||||
// Book specific
|
||||
if (libraryItem.isBook) {
|
||||
await this.createAuthorsAndSeriesForItemUpdate(mediaPayload)
|
||||
}
|
||||
|
||||
// Podcast specific
|
||||
var isPodcastAutoDownloadUpdated = false
|
||||
if (libraryItem.isPodcast) {
|
||||
if (mediaPayload.autoDownloadEpisodes !== undefined && libraryItem.media.autoDownloadEpisodes !== mediaPayload.autoDownloadEpisodes) {
|
||||
isPodcastAutoDownloadUpdated = true
|
||||
} else if (mediaPayload.autoDownloadSchedule !== undefined && libraryItem.media.autoDownloadSchedule !== mediaPayload.autoDownloadSchedule) {
|
||||
isPodcastAutoDownloadUpdated = true
|
||||
}
|
||||
}
|
||||
|
||||
var hasUpdates = libraryItem.media.update(mediaPayload)
|
||||
if (hasUpdates) {
|
||||
if (isPodcastAutoDownloadUpdated) {
|
||||
this.cronManager.checkUpdatePodcastCron(libraryItem)
|
||||
}
|
||||
|
||||
Logger.debug(`[LibraryItemController] Updated library item media ${libraryItem.media.metadata.title}`)
|
||||
await this.db.updateLibraryItem(libraryItem)
|
||||
this.emitter('item_updated', libraryItem.toJSONExpanded())
|
||||
|
@ -2,15 +2,20 @@ const cron = require('../libs/nodeCron')
|
||||
const Logger = require('../Logger')
|
||||
|
||||
class CronManager {
|
||||
constructor(db, scanner) {
|
||||
constructor(db, scanner, podcastManager) {
|
||||
this.db = db
|
||||
this.scanner = scanner
|
||||
this.podcastManager = podcastManager
|
||||
|
||||
this.libraryScanCrons = []
|
||||
this.podcastCrons = []
|
||||
|
||||
this.podcastCronExpressionsExecuting = []
|
||||
}
|
||||
|
||||
init() {
|
||||
this.initLibraryScanCrons()
|
||||
this.initPodcastCrons()
|
||||
}
|
||||
|
||||
initLibraryScanCrons() {
|
||||
@ -57,5 +62,116 @@ class CronManager {
|
||||
}
|
||||
}
|
||||
|
||||
initPodcastCrons() {
|
||||
const cronExpressionMap = {}
|
||||
this.db.libraryItems.forEach((li) => {
|
||||
if (li.mediaType === 'podcast' && li.media.autoDownloadEpisodes) {
|
||||
if (!li.media.autoDownloadSchedule) {
|
||||
Logger.error(`[CronManager] Podcast auto download schedule is not set for ${li.media.metadata.title}`)
|
||||
} else {
|
||||
if (!cronExpressionMap[li.media.autoDownloadSchedule]) {
|
||||
cronExpressionMap[li.media.autoDownloadSchedule] = {
|
||||
expression: li.media.autoDownloadSchedule,
|
||||
libraryItemIds: []
|
||||
}
|
||||
}
|
||||
cronExpressionMap[li.media.autoDownloadSchedule].libraryItemIds.push(li.id)
|
||||
}
|
||||
}
|
||||
})
|
||||
if (!Object.keys(cronExpressionMap).length) return
|
||||
|
||||
Logger.debug(`[CronManager] Found ${Object.keys(cronExpressionMap).length} podcast episode schedules to start`)
|
||||
for (const expression in cronExpressionMap) {
|
||||
this.startPodcastCron(expression, cronExpressionMap[expression].libraryItemIds)
|
||||
}
|
||||
}
|
||||
|
||||
startPodcastCron(expression, libraryItemIds) {
|
||||
try {
|
||||
Logger.debug(`[CronManager] Scheduling podcast episode check cron "${expression}" for ${libraryItemIds.length} item(s)`)
|
||||
const task = cron.schedule(expression, () => {
|
||||
if (this.podcastCronExpressionsExecuting.includes(expression)) {
|
||||
Logger.warn(`[CronManager] Podcast cron "${expression}" is already executing`)
|
||||
} else {
|
||||
this.executePodcastCron(expression, libraryItemIds)
|
||||
}
|
||||
})
|
||||
this.podcastCrons.push({
|
||||
libraryItemIds,
|
||||
expression,
|
||||
task
|
||||
})
|
||||
} catch (error) {
|
||||
Logger.error(`[PodcastManager] Failed to schedule podcast cron ${this.serverSettings.podcastEpisodeSchedule}`, error)
|
||||
}
|
||||
}
|
||||
|
||||
async executePodcastCron(expression, libraryItemIds) {
|
||||
Logger.debug(`[CronManager] Start executing podcast cron ${expression} for ${libraryItemIds.length} item(s)`)
|
||||
const podcastCron = this.podcastCrons.find(cron => cron.expression === expression)
|
||||
if (!podcastCron) {
|
||||
Logger.error(`[CronManager] Podcast cron not found for expression ${expression}`)
|
||||
return
|
||||
}
|
||||
this.podcastCronExpressionsExecuting.push(expression)
|
||||
|
||||
// Get podcast library items to check
|
||||
const libraryItems = []
|
||||
for (const libraryItemId of libraryItemIds) {
|
||||
const libraryItem = this.db.libraryItems.find(li => li.id === libraryItemId)
|
||||
if (!libraryItem) {
|
||||
Logger.error(`[CronManager] Library item ${libraryItemId} not found for episode check cron ${expression}`)
|
||||
podcastCron.libraryItemIds = podcastCron.libraryItemIds.filter(lid => lid !== libraryItemId) // Filter it out
|
||||
} else {
|
||||
libraryItems.push(libraryItem)
|
||||
}
|
||||
}
|
||||
|
||||
// Run episode checks
|
||||
for (const libraryItem of libraryItems) {
|
||||
const keepAutoDownloading = await this.podcastManager.runEpisodeCheck(libraryItem)
|
||||
if (!keepAutoDownloading) { // auto download was disabled
|
||||
podcastCron.libraryItemIds = podcastCron.libraryItemIds.filter(lid => lid !== libraryItemId) // Filter it out
|
||||
}
|
||||
}
|
||||
|
||||
// Stop and remove cron if no more library items
|
||||
if (!podcastCron.libraryItemIds.length) {
|
||||
this.removePodcastEpisodeCron(podcastCron)
|
||||
return
|
||||
}
|
||||
|
||||
Logger.debug(`[CronManager] Finished executing podcast cron ${expression} for ${libraryItems.length} item(s)`)
|
||||
this.podcastCronExpressionsExecuting = this.podcastCronExpressionsExecuting.filter(exp => exp !== expression)
|
||||
}
|
||||
|
||||
removePodcastEpisodeCron(podcastCron) {
|
||||
Logger.info(`[CronManager] Stopping & removing podcast episode cron for ${podcastCron.expression}`)
|
||||
if (podcastCron.task) podcastCron.task.stop()
|
||||
this.podcastCrons = this.podcastCrons.filter(pc => pc.expression !== podcastCron.expression)
|
||||
}
|
||||
|
||||
checkUpdatePodcastCron(libraryItem) {
|
||||
// Remove from old cron by library item id
|
||||
const existingCron = this.podcastCrons.find(pc => pc.libraryItemIds.includes(libraryItem.id))
|
||||
if (existingCron) {
|
||||
existingCron.libraryItemIds = existingCron.libraryItemIds.filter(lid => lid !== libraryItem.id)
|
||||
if (!existingCron.libraryItemIds.length) {
|
||||
this.removePodcastEpisodeCron(existingCron)
|
||||
}
|
||||
}
|
||||
|
||||
// Add to cron or start new cron
|
||||
if (libraryItem.media.autoDownloadEpisodes && libraryItem.media.autoDownloadSchedule) {
|
||||
const cronMatchingExpression = this.podcastCrons.find(pc => pc.expression === libraryItem.media.autoDownloadSchedule)
|
||||
if (cronMatchingExpression) {
|
||||
cronMatchingExpression.libraryItemIds.push(libraryItem.id)
|
||||
Logger.info(`[CronManager] Added podcast "${libraryItem.media.metadata.title}" to auto dl episode cron "${cronMatchingExpression.expression}"`)
|
||||
} else {
|
||||
this.startPodcastCron(libraryItem.media.autoDownloadSchedule, [libraryItem.id])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = CronManager
|
@ -1,5 +1,4 @@
|
||||
const fs = require('../libs/fsExtra')
|
||||
const cron = require('../libs/nodeCron')
|
||||
const axios = require('axios')
|
||||
|
||||
const { parsePodcastRssFeedXml } = require('../utils/podcastUtils')
|
||||
@ -23,22 +22,14 @@ class PodcastManager {
|
||||
this.downloadQueue = []
|
||||
this.currentDownload = null
|
||||
|
||||
this.episodeScheduleTask = null
|
||||
this.failedCheckMap = {},
|
||||
this.MaxFailedEpisodeChecks = 24
|
||||
this.failedCheckMap = {}
|
||||
this.MaxFailedEpisodeChecks = 24
|
||||
}
|
||||
|
||||
get serverSettings() {
|
||||
return this.db.serverSettings || {}
|
||||
}
|
||||
|
||||
init() {
|
||||
var podcastsWithAutoDownload = this.db.libraryItems.some(li => li.mediaType === 'podcast' && li.media.autoDownloadEpisodes)
|
||||
if (podcastsWithAutoDownload) {
|
||||
this.schedulePodcastEpisodeCron()
|
||||
}
|
||||
}
|
||||
|
||||
getEpisodeDownloadsInQueue(libraryItemId) {
|
||||
return this.downloadQueue.filter(d => d.libraryItemId === libraryItemId)
|
||||
}
|
||||
@ -189,73 +180,45 @@ class PodcastManager {
|
||||
return newAudioFile
|
||||
}
|
||||
|
||||
schedulePodcastEpisodeCron() {
|
||||
try {
|
||||
Logger.debug(`[PodcastManager] Scheduled podcast episode check cron "${this.serverSettings.podcastEpisodeSchedule}"`)
|
||||
this.episodeScheduleTask = cron.schedule(this.serverSettings.podcastEpisodeSchedule, () => {
|
||||
Logger.debug(`[PodcastManager] Running cron`)
|
||||
this.checkForNewEpisodes()
|
||||
})
|
||||
} catch (error) {
|
||||
Logger.error(`[PodcastManager] Failed to schedule podcast cron ${this.serverSettings.podcastEpisodeSchedule}`, error)
|
||||
}
|
||||
}
|
||||
// Returns false if auto download episodes was disabled (disabled if reaches max failed checks)
|
||||
async runEpisodeCheck(libraryItem) {
|
||||
const lastEpisodeCheckDate = new Date(libraryItem.media.lastEpisodeCheck || 0)
|
||||
const latestEpisodePublishedAt = libraryItem.media.latestEpisodePublished
|
||||
Logger.info(`[PodcastManager] runEpisodeCheck: "${libraryItem.media.metadata.title}" | Last check: ${lastEpisodeCheckDate} | ${latestEpisodePublishedAt ? `Latest episode pubDate: ${new Date(latestEpisodePublishedAt)}` : 'No latest episode'}`)
|
||||
|
||||
cancelCron() {
|
||||
Logger.debug(`[PodcastManager] Canceled new podcast episode check cron`)
|
||||
if (this.episodeScheduleTask) {
|
||||
this.episodeScheduleTask.destroy()
|
||||
this.episodeScheduleTask = null
|
||||
}
|
||||
}
|
||||
// Use latest episode pubDate if exists OR fallback to using lastEpisodeCheckDate
|
||||
// lastEpisodeCheckDate will be the current time when adding a new podcast
|
||||
const dateToCheckForEpisodesAfter = latestEpisodePublishedAt || lastEpisodeCheckDate
|
||||
Logger.debug(`[PodcastManager] runEpisodeCheck: "${libraryItem.media.metadata.title}" checking for episodes after ${new Date(dateToCheckForEpisodesAfter)}`)
|
||||
|
||||
async checkForNewEpisodes() {
|
||||
var podcastsWithAutoDownload = this.db.libraryItems.filter(li => li.mediaType === 'podcast' && li.media.autoDownloadEpisodes)
|
||||
if (!podcastsWithAutoDownload.length) {
|
||||
Logger.info(`[PodcastManager] checkForNewEpisodes - No podcasts with auto download set`)
|
||||
this.cancelCron()
|
||||
return
|
||||
}
|
||||
Logger.debug(`[PodcastManager] checkForNewEpisodes - Checking ${podcastsWithAutoDownload.length} Podcasts`)
|
||||
var newEpisodes = await this.checkPodcastForNewEpisodes(libraryItem, dateToCheckForEpisodesAfter)
|
||||
Logger.debug(`[PodcastManager] runEpisodeCheck: ${newEpisodes ? newEpisodes.length : 'N/A'} episodes found`)
|
||||
|
||||
for (const libraryItem of podcastsWithAutoDownload) {
|
||||
const lastEpisodeCheckDate = new Date(libraryItem.media.lastEpisodeCheck || 0)
|
||||
const latestEpisodePublishedAt = libraryItem.media.latestEpisodePublished
|
||||
Logger.info(`[PodcastManager] checkForNewEpisodes: "${libraryItem.media.metadata.title}" | Last check: ${lastEpisodeCheckDate} | ${latestEpisodePublishedAt ? `Latest episode pubDate: ${new Date(latestEpisodePublishedAt)}` : 'No latest episode'}`)
|
||||
|
||||
// Use latest episode pubDate if exists OR fallback to using lastEpisodeCheckDate
|
||||
// lastEpisodeCheckDate will be the current time when adding a new podcast
|
||||
const dateToCheckForEpisodesAfter = latestEpisodePublishedAt || lastEpisodeCheckDate
|
||||
Logger.debug(`[PodcastManager] checkForNewEpisodes: "${libraryItem.media.metadata.title}" checking for episodes after ${new Date(dateToCheckForEpisodesAfter)}`)
|
||||
|
||||
var newEpisodes = await this.checkPodcastForNewEpisodes(libraryItem, dateToCheckForEpisodesAfter)
|
||||
Logger.debug(`[PodcastManager] checkForNewEpisodes checked result ${newEpisodes ? newEpisodes.length : 'N/A'}`)
|
||||
|
||||
if (!newEpisodes) { // Failed
|
||||
// Allow up to 3 failed attempts before disabling auto download
|
||||
if (!this.failedCheckMap[libraryItem.id]) this.failedCheckMap[libraryItem.id] = 0
|
||||
this.failedCheckMap[libraryItem.id]++
|
||||
if (this.failedCheckMap[libraryItem.id] >= this.MaxFailedEpisodeChecks) {
|
||||
Logger.error(`[PodcastManager] checkForNewEpisodes ${this.failedCheckMap[libraryItem.id]} failed attempts at checking episodes for "${libraryItem.media.metadata.title}" - disabling auto download`)
|
||||
libraryItem.media.autoDownloadEpisodes = false
|
||||
delete this.failedCheckMap[libraryItem.id]
|
||||
} else {
|
||||
Logger.warn(`[PodcastManager] checkForNewEpisodes ${this.failedCheckMap[libraryItem.id]} failed attempts at checking episodes for "${libraryItem.media.metadata.title}"`)
|
||||
}
|
||||
} else if (newEpisodes.length) {
|
||||
if (!newEpisodes) { // Failed
|
||||
// Allow up to MaxFailedEpisodeChecks failed attempts before disabling auto download
|
||||
if (!this.failedCheckMap[libraryItem.id]) this.failedCheckMap[libraryItem.id] = 0
|
||||
this.failedCheckMap[libraryItem.id]++
|
||||
if (this.failedCheckMap[libraryItem.id] >= this.MaxFailedEpisodeChecks) {
|
||||
Logger.error(`[PodcastManager] runEpisodeCheck ${this.failedCheckMap[libraryItem.id]} failed attempts at checking episodes for "${libraryItem.media.metadata.title}" - disabling auto download`)
|
||||
libraryItem.media.autoDownloadEpisodes = false
|
||||
delete this.failedCheckMap[libraryItem.id]
|
||||
Logger.info(`[PodcastManager] Found ${newEpisodes.length} new episodes for podcast "${libraryItem.media.metadata.title}" - starting download`)
|
||||
this.downloadPodcastEpisodes(libraryItem, newEpisodes, true)
|
||||
} else {
|
||||
delete this.failedCheckMap[libraryItem.id]
|
||||
Logger.debug(`[PodcastManager] No new episodes for "${libraryItem.media.metadata.title}"`)
|
||||
Logger.warn(`[PodcastManager] runEpisodeCheck ${this.failedCheckMap[libraryItem.id]} failed attempts at checking episodes for "${libraryItem.media.metadata.title}"`)
|
||||
}
|
||||
|
||||
libraryItem.media.lastEpisodeCheck = Date.now()
|
||||
libraryItem.updatedAt = Date.now()
|
||||
await this.db.updateLibraryItem(libraryItem)
|
||||
this.emitter('item_updated', libraryItem.toJSONExpanded())
|
||||
} else if (newEpisodes.length) {
|
||||
delete this.failedCheckMap[libraryItem.id]
|
||||
Logger.info(`[PodcastManager] Found ${newEpisodes.length} new episodes for podcast "${libraryItem.media.metadata.title}" - starting download`)
|
||||
this.downloadPodcastEpisodes(libraryItem, newEpisodes, true)
|
||||
} else {
|
||||
delete this.failedCheckMap[libraryItem.id]
|
||||
Logger.debug(`[PodcastManager] No new episodes for "${libraryItem.media.metadata.title}"`)
|
||||
}
|
||||
|
||||
libraryItem.media.lastEpisodeCheck = Date.now()
|
||||
libraryItem.updatedAt = Date.now()
|
||||
await this.db.updateLibraryItem(libraryItem)
|
||||
this.emitter('item_updated', libraryItem.toJSONExpanded())
|
||||
return libraryItem.media.autoDownloadEpisodes
|
||||
}
|
||||
|
||||
async checkPodcastForNewEpisodes(podcastLibraryItem, dateToCheckForEpisodesAfter) {
|
||||
|
@ -18,6 +18,7 @@ class Podcast {
|
||||
this.episodes = []
|
||||
|
||||
this.autoDownloadEpisodes = false
|
||||
this.autoDownloadSchedule = null
|
||||
this.lastEpisodeCheck = 0
|
||||
this.maxEpisodesToKeep = 0
|
||||
|
||||
@ -40,6 +41,7 @@ class Podcast {
|
||||
return podcastEpisode
|
||||
})
|
||||
this.autoDownloadEpisodes = !!podcast.autoDownloadEpisodes
|
||||
this.autoDownloadSchedule = podcast.autoDownloadSchedule || '0 * * * *' // Added in 2.1.3 so default to hourly
|
||||
this.lastEpisodeCheck = podcast.lastEpisodeCheck || 0
|
||||
this.maxEpisodesToKeep = podcast.maxEpisodesToKeep || 0
|
||||
}
|
||||
@ -52,6 +54,7 @@ class Podcast {
|
||||
tags: [...this.tags],
|
||||
episodes: this.episodes.map(e => e.toJSON()),
|
||||
autoDownloadEpisodes: this.autoDownloadEpisodes,
|
||||
autoDownloadSchedule: this.autoDownloadSchedule,
|
||||
lastEpisodeCheck: this.lastEpisodeCheck,
|
||||
maxEpisodesToKeep: this.maxEpisodesToKeep
|
||||
}
|
||||
@ -64,6 +67,7 @@ class Podcast {
|
||||
tags: [...this.tags],
|
||||
numEpisodes: this.episodes.length,
|
||||
autoDownloadEpisodes: this.autoDownloadEpisodes,
|
||||
autoDownloadSchedule: this.autoDownloadSchedule,
|
||||
lastEpisodeCheck: this.lastEpisodeCheck,
|
||||
maxEpisodesToKeep: this.maxEpisodesToKeep,
|
||||
size: this.size
|
||||
@ -78,6 +82,7 @@ class Podcast {
|
||||
tags: [...this.tags],
|
||||
episodes: this.episodes.map(e => e.toJSONExpanded()),
|
||||
autoDownloadEpisodes: this.autoDownloadEpisodes,
|
||||
autoDownloadSchedule: this.autoDownloadSchedule,
|
||||
lastEpisodeCheck: this.lastEpisodeCheck,
|
||||
maxEpisodesToKeep: this.maxEpisodesToKeep,
|
||||
size: this.size
|
||||
@ -165,14 +170,15 @@ class Podcast {
|
||||
return null
|
||||
}
|
||||
|
||||
setData(mediaMetadata) {
|
||||
setData(mediaData) {
|
||||
this.metadata = new PodcastMetadata()
|
||||
if (mediaMetadata.metadata) {
|
||||
this.metadata.setData(mediaMetadata.metadata)
|
||||
if (mediaData.metadata) {
|
||||
this.metadata.setData(mediaData.metadata)
|
||||
}
|
||||
|
||||
this.coverPath = mediaMetadata.coverPath || null
|
||||
this.autoDownloadEpisodes = !!mediaMetadata.autoDownloadEpisodes
|
||||
this.coverPath = mediaData.coverPath || null
|
||||
this.autoDownloadEpisodes = !!mediaData.autoDownloadEpisodes
|
||||
this.autoDownloadSchedule = mediaData.autoDownloadSchedule || global.ServerSettings.podcastEpisodeSchedule
|
||||
this.lastEpisodeCheck = Date.now() // Makes sure new episodes are after this
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user