From a456865ec0a61d074e4032b42eb6fd8dfd541a37 Mon Sep 17 00:00:00 2001 From: advplyr Date: Wed, 10 Sep 2025 17:10:00 -0500 Subject: [PATCH] Fix issue with episode downloads without streams, fallback to regular dl on ffprobe fail --- server/managers/PodcastManager.js | 39 ++++++++++++++++++------------- server/utils/ffmpegHelpers.js | 10 ++++---- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js index a959ba9a4..bdf6fc764 100644 --- a/server/managers/PodcastManager.js +++ b/server/managers/PodcastManager.js @@ -127,10 +127,20 @@ class PodcastManager { }) let success = !!ffmpegDownloadResponse?.success - // If failed due to ffmpeg error, retry without tagging + if (success) { + // Attempt to ffprobe and add podcast episode audio file + success = await this.scanAddPodcastEpisodeAudioFile() + if (!success) { + Logger.error(`[PodcastManager] Failed to scan and add podcast episode audio file - removing file`) + await fs.remove(this.currentDownload.targetPath) + } + } + + // If failed due to ffmpeg or ffprobe error, retry without tagging // e.g. RSS feed may have incorrect file extension and file type // See https://github.com/advplyr/audiobookshelf/issues/3837 - if (!success && ffmpegDownloadResponse?.isFfmpegError) { + // e.g. Ffmpeg may be download the file without streams causing the ffprobe to fail + if (!success && !ffmpegDownloadResponse?.isRequestError) { Logger.info(`[PodcastManager] Retrying episode download without tagging`) // Download episode only success = await downloadFile(this.currentDownload.url, this.currentDownload.targetPath) @@ -139,23 +149,20 @@ class PodcastManager { Logger.error(`[PodcastManager] Podcast Episode download failed`, error) return false }) + + if (success) { + success = await this.scanAddPodcastEpisodeAudioFile() + if (!success) { + Logger.error(`[PodcastManager] Failed to scan and add podcast episode audio file - removing file`) + await fs.remove(this.currentDownload.targetPath) + } + } } if (success) { - success = await this.scanAddPodcastEpisodeAudioFile() - if (!success) { - await fs.remove(this.currentDownload.targetPath) - this.currentDownload.setFinished(false) - const taskFailedString = { - text: 'Failed', - key: 'MessageTaskFailed' - } - task.setFailed(taskFailedString) - } else { - Logger.info(`[PodcastManager] Successfully downloaded podcast episode "${this.currentDownload.episodeTitle}"`) - this.currentDownload.setFinished(true) - task.setFinished() - } + Logger.info(`[PodcastManager] Successfully downloaded podcast episode "${this.currentDownload.episodeTitle}"`) + this.currentDownload.setFinished(true) + task.setFinished() } else { const taskFailedString = { text: 'Failed', diff --git a/server/utils/ffmpegHelpers.js b/server/utils/ffmpegHelpers.js index 0c4da5f95..80832cc77 100644 --- a/server/utils/ffmpegHelpers.js +++ b/server/utils/ffmpegHelpers.js @@ -99,7 +99,7 @@ module.exports.resizeImage = resizeImage /** * * @param {import('../objects/PodcastEpisodeDownload')} podcastEpisodeDownload - * @returns {Promise<{success: boolean, isFfmpegError?: boolean}>} + * @returns {Promise<{success: boolean, isRequestError?: boolean}>} */ module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => { return new Promise(async (resolve) => { @@ -118,7 +118,7 @@ module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => { method: 'GET', responseType: 'stream', headers: { - 'Accept': '*/*', + Accept: '*/*', 'User-Agent': userAgent }, timeout: global.PodcastDownloadTimeout @@ -139,7 +139,8 @@ module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => { if (!response) { return resolve({ - success: false + success: false, + isRequestError: true }) } @@ -204,8 +205,7 @@ module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => { Logger.error(`Full stderr dump for episode url "${podcastEpisodeDownload.url}": ${stderrLines.join('\n')}`) } resolve({ - success: false, - isFfmpegError: true + success: false }) }) ffmpeg.on('progress', (progress) => {