audiobookshelf/server/managers/CronManager.js
2022-08-19 18:41:58 -05:00

177 lines
6.7 KiB
JavaScript

const cron = require('../libs/nodeCron')
const Logger = require('../Logger')
class CronManager {
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() {
for (const library of this.db.libraries) {
if (library.settings.autoScanCronExpression) {
this.startCronForLibrary(library)
}
}
}
startCronForLibrary(library) {
Logger.debug(`[CronManager] Init library scan cron for ${library.name} on schedule ${library.settings.autoScanCronExpression}`)
const libScanCron = cron.schedule(library.settings.autoScanCronExpression, () => {
Logger.debug(`[CronManager] Library scan cron executing for ${library.name}`)
this.scanner.scan(library)
})
this.libraryScanCrons.push({
libraryId: library.id,
expression: library.settings.autoScanCronExpression,
task: libScanCron
})
}
removeCronForLibrary(library) {
Logger.debug(`[CronManager] Removing library scan cron for ${library.name}`)
this.libraryScanCrons = this.libraryScanCrons.filter(lsc => lsc.libraryId !== library.id)
}
updateLibraryScanCron(library) {
const expression = library.settings.autoScanCronExpression
const existingCron = this.libraryScanCrons.find(lsc => lsc.libraryId === library.id)
if (!expression && existingCron) {
if (existingCron.task.stop) existingCron.task.stop()
this.removeCronForLibrary(library)
} else if (!existingCron && expression) {
this.startCronForLibrary(library)
} else if (existingCron && existingCron.expression !== expression) {
if (existingCron.task.stop) existingCron.task.stop()
this.removeCronForLibrary(library)
this.startCronForLibrary(library)
}
}
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