From 704fbaced853f18e9e543f478fe76f1071e20e17 Mon Sep 17 00:00:00 2001 From: advplyr Date: Wed, 29 Mar 2023 18:05:53 -0500 Subject: [PATCH] Update:Download podcast episodes and embed meta tags #1488 --- server/managers/PodcastManager.js | 14 +++-- server/objects/entities/PodcastEpisode.js | 4 ++ server/utils/ffmpegHelpers.js | 75 +++++++++++++++++++++++ 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js index 593d9754..0cc53f12 100644 --- a/server/managers/PodcastManager.js +++ b/server/managers/PodcastManager.js @@ -4,11 +4,12 @@ const SocketAuthority = require('../SocketAuthority') const fs = require('../libs/fsExtra') const { getPodcastFeed } = require('../utils/podcastUtils') -const { downloadFile, removeFile } = require('../utils/fileUtils') +const { removeFile } = require('../utils/fileUtils') const filePerms = require('../utils/filePerms') const { levenshteinDistance } = require('../utils/index') const opmlParser = require('../utils/parsers/parseOPML') const prober = require('../utils/prober') +const ffmpegHelpers = require('../utils/ffmpegHelpers') const LibraryFile = require('../objects/files/LibraryFile') const PodcastEpisodeDownload = require('../objects/PodcastEpisodeDownload') @@ -93,7 +94,8 @@ class PodcastManager { await filePerms.setDefault(this.currentDownload.libraryItem.path) } - let success = await downloadFile(this.currentDownload.url, this.currentDownload.targetPath).then(() => true).catch((error) => { + // Downloads episode and tags it + let success = await ffmpegHelpers.downloadPodcastEpisode(this.currentDownload).catch((error) => { Logger.error(`[PodcastManager] Podcast Episode download failed`, error) return false }) @@ -126,22 +128,22 @@ class PodcastManager { } async scanAddPodcastEpisodeAudioFile() { - var libraryFile = await this.getLibraryFile(this.currentDownload.targetPath, this.currentDownload.targetRelPath) + const libraryFile = await this.getLibraryFile(this.currentDownload.targetPath, this.currentDownload.targetRelPath) // TODO: Set meta tags on new audio file - var audioFile = await this.probeAudioFile(libraryFile) + const audioFile = await this.probeAudioFile(libraryFile) if (!audioFile) { return false } - var libraryItem = this.db.libraryItems.find(li => li.id === this.currentDownload.libraryItem.id) + const 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 + const podcastEpisode = this.currentDownload.podcastEpisode podcastEpisode.audioFile = audioFile libraryItem.media.addPodcastEpisode(podcastEpisode) if (libraryItem.isInvalid) { diff --git a/server/objects/entities/PodcastEpisode.js b/server/objects/entities/PodcastEpisode.js index f1cfa77e..0ea5e7c6 100644 --- a/server/objects/entities/PodcastEpisode.js +++ b/server/objects/entities/PodcastEpisode.js @@ -106,6 +106,10 @@ class PodcastEpisode { get enclosureUrl() { return this.enclosure ? this.enclosure.url : null } + get pubYear() { + if (!this.publishedAt) return null + return new Date(this.publishedAt).getFullYear() + } setData(data, index = 1) { this.id = getId('ep') diff --git a/server/utils/ffmpegHelpers.js b/server/utils/ffmpegHelpers.js index 72c778d2..e17a94dd 100644 --- a/server/utils/ffmpegHelpers.js +++ b/server/utils/ffmpegHelpers.js @@ -1,3 +1,4 @@ +const axios = require('axios') const Ffmpeg = require('../libs/fluentFfmpeg') const fs = require('../libs/fsExtra') const Path = require('path') @@ -86,3 +87,77 @@ async function resizeImage(filePath, outputPath, width, height) { }) } module.exports.resizeImage = resizeImage + +module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => { + return new Promise(async (resolve) => { + const response = await axios({ + url: podcastEpisodeDownload.url, + method: 'GET', + responseType: 'stream', + timeout: 30000 + }) + + const ffmpeg = Ffmpeg(response.data) + ffmpeg.outputOptions([ + '-c copy', + '-metadata', + `album=${podcastEpisodeDownload.libraryItem?.media.metadata.title ?? ""}`, // Podcast Title + '-metadata', + `album-sort=${podcastEpisodeDownload.libraryItem?.media.metadata.title ?? ""}`, // Podcast Title + '-metadata', + `artist=${podcastEpisodeDownload.libraryItem?.media.metadata.author ?? ""}`, // Podcast Artist + '-metadata', + `artist-sort=${podcastEpisodeDownload.libraryItem?.media.metadata.author ?? ""}`, // Podcast Artist + '-metadata', + `comment=${podcastEpisodeDownload.podcastEpisode?.description ?? ""}`, // Episode Description + '-metadata', + `description=${podcastEpisodeDownload.podcastEpisode?.subtitle ?? ""}`, // Episode Subtitle + '-metadata', + `disc=${podcastEpisodeDownload.podcastEpisode?.season ?? ""}`, // Episode Season + '-metadata', + `genre=${podcastEpisodeDownload.libraryItem?.media.metadata.genres.join(';') ?? ""}`, // Podcast Genres + '-metadata', + `language=${podcastEpisodeDownload.libraryItem?.media.metadata.language ?? ""}`, // Podcast Language + '-metadata', + `MVNM=${podcastEpisodeDownload.libraryItem?.media.metadata.title ?? ""}`, // Podcast Title + '-metadata', + `MVIN=${podcastEpisodeDownload.podcastEpisode?.episode ?? ""}`, // Episode Number + '-metadata', + `track=${podcastEpisodeDownload.podcastEpisode?.episode ?? ""}`, // Episode Number + '-metadata', + `series-part=${podcastEpisodeDownload.podcastEpisode?.episode ?? ""}`, // Episode Number + '-metadata', + `podcast=1`, + '-metadata', + `title=${podcastEpisodeDownload.podcastEpisode?.title ?? ""}`, // Episode Title + '-metadata', + `title-sort=${podcastEpisodeDownload.podcastEpisode?.title ?? ""}`, // Episode Title + '-metadata', + `year=${podcastEpisodeDownload.podcastEpisode?.pubYear ?? ""}`, // Episode Pub Year + '-metadata', + `date=${podcastEpisodeDownload.podcastEpisode?.pubDate ?? ""}`, // Episode PubDate + '-metadata', + `releasedate=${podcastEpisodeDownload.podcastEpisode?.pubDate ?? ""}`, // Episode PubDate + '-metadata', + `itunes-id=${podcastEpisodeDownload.libraryItem?.media.metadata.itunesId ?? ""}`, // Podcast iTunes ID + '-metadata', + `podcast-type=${podcastEpisodeDownload.libraryItem?.media.metadata.type ?? ""}`, // Podcast Type + '-metadata', + `episode-type=${podcastEpisodeDownload.podcastEpisode?.episodeType ?? ""}` // Episode Type + ]) + ffmpeg.addOutput(podcastEpisodeDownload.targetPath) + + ffmpeg.on('start', (cmd) => { + Logger.debug(`[FfmpegHelpers] downloadPodcastEpisode: Cmd: ${cmd}`) + }) + ffmpeg.on('error', (err, stdout, stderr) => { + Logger.error(`[FfmpegHelpers] downloadPodcastEpisode: Error ${err} ${stdout} ${stderr}`) + resolve(false) + }) + ffmpeg.on('end', () => { + Logger.debug(`[FfmpegHelpers] downloadPodcastEpisode: Complete`) + resolve(podcastEpisodeDownload.targetPath) + }) + ffmpeg.run() + }) +} \ No newline at end of file