mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-11-03 19:07:00 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			146 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const fs = require('fs-extra')
 | 
						|
const cron = require('node-cron')
 | 
						|
const axios = require('axios')
 | 
						|
 | 
						|
const { parsePodcastRssFeedXml } = require('../utils/podcastUtils')
 | 
						|
const Logger = require('../Logger')
 | 
						|
 | 
						|
const { downloadFile } = require('../utils/fileUtils')
 | 
						|
const prober = require('../utils/prober')
 | 
						|
const LibraryFile = require('../objects/files/LibraryFile')
 | 
						|
const PodcastEpisodeDownload = require('../objects/PodcastEpisodeDownload')
 | 
						|
const PodcastEpisode = require('../objects/entities/PodcastEpisode')
 | 
						|
const AudioFile = require('../objects/files/AudioFile')
 | 
						|
 | 
						|
class PodcastManager {
 | 
						|
  constructor(db, watcher, emitter) {
 | 
						|
    this.db = db
 | 
						|
    this.watcher = watcher
 | 
						|
    this.emitter = emitter
 | 
						|
 | 
						|
    this.downloadQueue = []
 | 
						|
    this.currentDownload = null
 | 
						|
 | 
						|
    this.episodeScheduleTask = null
 | 
						|
  }
 | 
						|
 | 
						|
  init() {
 | 
						|
    var podcastsWithAutoDownload = this.db.libraryItems.find(li => li.mediaType === 'podcast' && li.media.autoDownloadEpisodes)
 | 
						|
    if (podcastsWithAutoDownload.length) {
 | 
						|
      this.schedulePodcastEpisodeCron()
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  async downloadPodcastEpisodes(libraryItem, episodesToDownload) {
 | 
						|
    var index = 1
 | 
						|
    episodesToDownload.forEach((ep) => {
 | 
						|
      var newPe = new PodcastEpisode()
 | 
						|
      newPe.setData(ep, index++)
 | 
						|
      var newPeDl = new PodcastEpisodeDownload()
 | 
						|
      newPeDl.setData(newPe, libraryItem)
 | 
						|
      this.startPodcastEpisodeDownload(newPeDl)
 | 
						|
    })
 | 
						|
  }
 | 
						|
 | 
						|
  async startPodcastEpisodeDownload(podcastEpisodeDownload) {
 | 
						|
    if (this.currentDownload) {
 | 
						|
      this.downloadQueue.push(podcastEpisodeDownload)
 | 
						|
      return
 | 
						|
    }
 | 
						|
    this.currentDownload = podcastEpisodeDownload
 | 
						|
 | 
						|
    // Ignores all added files to this dir
 | 
						|
    this.watcher.addIgnoreDir(this.currentDownload.libraryItem.path)
 | 
						|
 | 
						|
    var success = await downloadFile(this.currentDownload.url, this.currentDownload.targetPath).then(() => true).catch((error) => {
 | 
						|
      Logger.error(`[PodcastManager] Podcast Episode download failed`, error)
 | 
						|
      return false
 | 
						|
    })
 | 
						|
    if (success) {
 | 
						|
      success = await this.scanAddPodcastEpisodeAudioFile()
 | 
						|
      if (!success) {
 | 
						|
        await fs.remove(this.currentDownload.targetPath)
 | 
						|
      } else {
 | 
						|
        Logger.info(`[PodcastManager] Successfully downloaded podcast episode "${this.currentDownload.podcastEpisode.title}"`)
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    this.watcher.removeIgnoreDir(this.currentDownload.libraryItem.path)
 | 
						|
    this.currentDownload = null
 | 
						|
    if (this.downloadQueue.length) {
 | 
						|
      this.startPodcastEpisodeDownload(this.downloadQueue.shift())
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  async scanAddPodcastEpisodeAudioFile() {
 | 
						|
    var libraryFile = await this.getLibraryFile(this.currentDownload.targetPath, this.currentDownload.targetRelPath)
 | 
						|
    var audioFile = await this.probeAudioFile(libraryFile)
 | 
						|
    if (!audioFile) {
 | 
						|
      return false
 | 
						|
    }
 | 
						|
 | 
						|
    var libraryItem = this.db.libraryItems.find(li => li.id === this.currentDownload.libraryItem.id)
 | 
						|
    if (!libraryItem) {
 | 
						|
      Logger.error(`[PodcastManager] Podcast Episode finished but library item was not found ${this.currentDownload.libraryItem.id}`)
 | 
						|
      return false
 | 
						|
    }
 | 
						|
    var podcastEpisode = this.currentDownload.podcastEpisode
 | 
						|
    podcastEpisode.audioFile = audioFile
 | 
						|
    libraryItem.media.addPodcastEpisode(podcastEpisode)
 | 
						|
    libraryItem.updatedAt = Date.now()
 | 
						|
    await this.db.updateLibraryItem(libraryItem)
 | 
						|
    this.emitter('item_updated', libraryItem.toJSONExpanded())
 | 
						|
    return true
 | 
						|
  }
 | 
						|
 | 
						|
  async getLibraryFile(path, relPath) {
 | 
						|
    var newLibFile = new LibraryFile()
 | 
						|
    await newLibFile.setDataFromPath(path, relPath)
 | 
						|
    return newLibFile
 | 
						|
  }
 | 
						|
 | 
						|
  async probeAudioFile(libraryFile) {
 | 
						|
    var path = libraryFile.metadata.path
 | 
						|
    var audioProbeData = await prober.probe(path)
 | 
						|
    if (audioProbeData.error) {
 | 
						|
      Logger.error(`[PodcastManager] Podcast Episode downloaded but failed to probe "${path}"`, audioProbeData.error)
 | 
						|
      return false
 | 
						|
    }
 | 
						|
    var newAudioFile = new AudioFile()
 | 
						|
    newAudioFile.setDataFromProbe(libraryFile, audioProbeData)
 | 
						|
    return newAudioFile
 | 
						|
  }
 | 
						|
 | 
						|
  schedulePodcastEpisodeCron() {
 | 
						|
    try {
 | 
						|
      this.episodeScheduleTask = cron.schedule(this.serverSettings.podcastEpisodeSchedule, this.checkForNewEpisodes.bind(this))
 | 
						|
    } catch (error) {
 | 
						|
      Logger.error(`[PodcastManager] Failed to schedule podcast cron ${this.serverSettings.backupSchedule}`, error)
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  checkForNewEpisodes() {
 | 
						|
    var podcastsWithAutoDownload = this.db.libraryItems.find(li => li.mediaType === 'podcast' && li.media.autoDownloadEpisodes)
 | 
						|
    for (const libraryItem of podcastsWithAutoDownload) {
 | 
						|
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  getPodcastFeed(podcastMedia) {
 | 
						|
    axios.get(podcastMedia.feedUrl).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) {
 | 
						|
        return res.status(500).send('Invalid podcast RSS feed')
 | 
						|
      }
 | 
						|
      res.json(podcast)
 | 
						|
    }).catch((error) => {
 | 
						|
      console.error('Failed', error)
 | 
						|
      res.status(500).send(error)
 | 
						|
    })
 | 
						|
  }
 | 
						|
}
 | 
						|
module.exports = PodcastManager |