2023-03-30 01:05:53 +02:00
|
|
|
const axios = require('axios')
|
2022-07-07 00:38:19 +02:00
|
|
|
const Ffmpeg = require('../libs/fluentFfmpeg')
|
2022-07-06 02:53:01 +02:00
|
|
|
const fs = require('../libs/fsExtra')
|
2021-09-30 03:43:36 +02:00
|
|
|
const Path = require('path')
|
|
|
|
const Logger = require('../Logger')
|
2023-01-06 00:45:27 +01:00
|
|
|
const { filePathToPOSIX } = require('./fileUtils')
|
2021-09-04 21:17:26 +02:00
|
|
|
|
|
|
|
function escapeSingleQuotes(path) {
|
|
|
|
// return path.replace(/'/g, '\'\\\'\'')
|
2023-01-06 00:45:27 +01:00
|
|
|
return filePathToPOSIX(path).replace(/ /g, '\\ ').replace(/'/g, '\\\'')
|
2021-09-04 21:17:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns first track start time
|
|
|
|
// startTime is for streams starting an encode part-way through an audiobook
|
|
|
|
async function writeConcatFile(tracks, outputPath, startTime = 0) {
|
|
|
|
var trackToStartWithIndex = 0
|
|
|
|
var firstTrackStartTime = 0
|
|
|
|
|
|
|
|
// Find first track greater than startTime
|
|
|
|
if (startTime > 0) {
|
|
|
|
var currTrackEnd = 0
|
|
|
|
var startingTrack = tracks.find(t => {
|
|
|
|
currTrackEnd += t.duration
|
|
|
|
return startTime < currTrackEnd
|
|
|
|
})
|
|
|
|
if (startingTrack) {
|
|
|
|
firstTrackStartTime = currTrackEnd - startingTrack.duration
|
|
|
|
trackToStartWithIndex = startingTrack.index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var tracksToInclude = tracks.filter(t => t.index >= trackToStartWithIndex)
|
|
|
|
var trackPaths = tracksToInclude.map(t => {
|
2022-03-13 02:59:35 +01:00
|
|
|
var line = 'file ' + escapeSingleQuotes(t.metadata.path) + '\n' + `duration ${t.duration}`
|
2021-09-04 21:17:26 +02:00
|
|
|
return line
|
|
|
|
})
|
|
|
|
var inputstr = trackPaths.join('\n\n')
|
|
|
|
await fs.writeFile(outputPath, inputstr)
|
|
|
|
|
|
|
|
return firstTrackStartTime
|
|
|
|
}
|
2021-09-08 16:15:54 +02:00
|
|
|
module.exports.writeConcatFile = writeConcatFile
|
|
|
|
|
2021-09-30 03:43:36 +02:00
|
|
|
async function extractCoverArt(filepath, outputpath) {
|
|
|
|
var dirname = Path.dirname(outputpath)
|
|
|
|
await fs.ensureDir(dirname)
|
|
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
var ffmpeg = Ffmpeg(filepath)
|
2021-10-01 01:52:32 +02:00
|
|
|
ffmpeg.addOption(['-map 0:v', '-frames:v 1'])
|
2021-09-30 03:43:36 +02:00
|
|
|
ffmpeg.output(outputpath)
|
|
|
|
|
|
|
|
ffmpeg.on('start', (cmd) => {
|
|
|
|
Logger.debug(`[FfmpegHelpers] Extract Cover Cmd: ${cmd}`)
|
|
|
|
})
|
|
|
|
ffmpeg.on('error', (err, stdout, stderr) => {
|
|
|
|
Logger.error(`[FfmpegHelpers] Extract Cover Error ${err}`)
|
|
|
|
resolve(false)
|
|
|
|
})
|
|
|
|
ffmpeg.on('end', () => {
|
|
|
|
Logger.debug(`[FfmpegHelpers] Cover Art Extracted Successfully`)
|
|
|
|
resolve(outputpath)
|
|
|
|
})
|
|
|
|
ffmpeg.run()
|
|
|
|
})
|
|
|
|
}
|
2021-12-13 20:29:31 +01:00
|
|
|
module.exports.extractCoverArt = extractCoverArt
|
|
|
|
|
|
|
|
//This should convert based on the output file extension as well
|
|
|
|
async function resizeImage(filePath, outputPath, width, height) {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
var ffmpeg = Ffmpeg(filePath)
|
|
|
|
ffmpeg.addOption(['-vf', `scale=${width || -1}:${height || -1}`])
|
|
|
|
ffmpeg.addOutput(outputPath)
|
|
|
|
ffmpeg.on('start', (cmd) => {
|
|
|
|
Logger.debug(`[FfmpegHelpers] Resize Image Cmd: ${cmd}`)
|
|
|
|
})
|
|
|
|
ffmpeg.on('error', (err, stdout, stderr) => {
|
2021-12-14 22:19:23 +01:00
|
|
|
Logger.error(`[FfmpegHelpers] Resize Image Error ${err} ${stdout} ${stderr}`)
|
2021-12-13 20:29:31 +01:00
|
|
|
resolve(false)
|
|
|
|
})
|
|
|
|
ffmpeg.on('end', () => {
|
|
|
|
Logger.debug(`[FfmpegHelpers] Image resized Successfully`)
|
|
|
|
resolve(outputPath)
|
|
|
|
})
|
|
|
|
ffmpeg.run()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
module.exports.resizeImage = resizeImage
|
2023-03-30 01:05:53 +02:00
|
|
|
|
|
|
|
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',
|
2023-03-31 01:04:21 +02:00
|
|
|
`subtitle=${podcastEpisodeDownload.podcastEpisode?.subtitle ?? ""}`, // Episode Subtitle
|
2023-03-30 01:05:53 +02:00
|
|
|
'-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()
|
|
|
|
})
|
|
|
|
}
|