Even more extended version
V2: If the "Best Quality Image" has been downloaded too (and it finished before the audio file, *and* it has the default filename) have ffmpeg use that image as the "cover" for the mp3 so it will be shown in media players when playing this mp3.
(There's surely a better way to get the filename for the best quality image - or other image to be used for the cover. This works for now. Anyone wants to improve it, feel free to do so - and post the update in here!)
Code:
function run() {
// Converts aac/m4a/ogg/opus files to mp3.
// Trigger: "A Download Stopped"
// Requires ffmpeg/ffprobe. Uses ffmpeg/ffprobe settings if available.
// Does NOT overwrite the destination file (mp3) if it already exists.
// V2: If the "Best Quality Image" is downloaded (and finished before the audio file!)
// then have ffmpeg add the image as cover into the mp3 file
// (Assumes default filename for images in the youtube plugin - if not, customize accordingly)
var testExceptionHandling = false; // Boolean to trigger a test exception
var writeErrorToLogfile = true; // Boolean to determine if errors should be written to a log file
var deleteSourceFile = true; // Set whether the source file should be deleted after conversion.
try {
if (!link.isFinished()) {
throw new Error("Download is not finished yet"); // Throw error if the download is not completed.
}
// Extract file name and extension (as provided)
var fileName = link.name.replace(/(.+)(\..+$)/, "$1");
var fileType = link.name.replace(/(.+)(\..+$)/, "$2");
var sourceFile = link.getDownloadPath();
// Check if the file is an audio file (aac/m4a/ogg/opus)
var isAudioFile = /\.(aac|m4a|ogg|opus)$/i.test(sourceFile);
if (!isAudioFile) {
return; // Exit the function if it is not an audio file.
}
var downloadFolder = package.getDownloadFolder();
var destFile = downloadFolder + "/" + fileName + ".mp3";
// Check if the destination file already exists
if (getPath(destFile).exists()) {
throw new Error("The destination file already exists"); // Throw error if the destination file already exists.
}
// Retrieve ffmpeg and ffprobe paths (unchanged)
var ffmpeg = callAPI("config", "get", "org.jdownloader.controlling.ffmpeg.FFmpegSetup", null, "binarypath");
var ffprobe = callAPI("config", "get", "org.jdownloader.controlling.ffmpeg.FFmpegSetup", null, "binarypathprobe");
// ffprobe call with "-v quiet" (as provided)
var data = JSON.parse(callSync(ffprobe, "-v", "quiet", "-print_format", "json", "-show_streams", "-show_format", sourceFile));
if (!data || !data.streams || data.streams.length === 0) {
throw new Error("No valid data could be retrieved from the source file"); // Throw error if no valid data is found.
}
if (testExceptionHandling) {
throw new Error("Test-Exception wurde manuell ausgelöst"); // Throw test exception if the flag is true
}
// Calculate bitrate (as provided)
var streamsBitrate = data.streams[0].bit_rate ? data.streams[0].bit_rate : 0;
var formatBitrate = data.format.bit_rate ? data.format.bit_rate : 0;
var bitrate = Math.max(streamsBitrate, formatBitrate) / 1000; // Convert bitrate to Kbps
// Throw error if the bitrate is invalid
if (bitrate <= 0) {
throw new Error("The file's bitrate is invalid");
}
// Find a possible cover image - must've been downloaded before the audio file finished!
var baseName = sourceFile.match(/^(.*) /); // everything up to the last space
var coverFile = baseName[1] + " BQ.jpg";
if (getPath(coverFile).exists()) {
// we have a cover file, call ffmpeg this way
callSync(ffmpeg,
"-y", // overwrite existing
"-i", coverFile, // source image source 0
"-i", sourceFile, // source audio source 1
"-c:v", "copy", // video-codec copy (image=video)
"-map", "0:v", // encode video from source 0
"-map", "1:a", // encode audio from source 1
"-id3v2_version", "3", // id3v2 tags
"-b:a", bitrate + "k", // audio bitrate
destFile); // target
} else {
// no cover file found or named differently
// Convert the file with ffmpeg
callSync(ffmpeg, "-y", "-i", sourceFile, "-b:a", bitrate + "k", destFile);
}
// Delete the source file if the conversion was successful
if (deleteSourceFile) {
if (getPath(destFile).exists()) {
deleteFile(sourceFile, false);
}
if (getPath(coverFile).exists()) {
deleteFile(coverFile, false);
}
}
} catch (e) {
// Check if the destination file path was determined
if (writeErrorToLogfile && typeof destFile !== 'undefined') {
// Create a log file and write the exception text into it
var logFilePath = package.getDownloadFolder() + "/" + fileName + ".txt";
var text = e.name + ": " + e.message;
writeFile(logFilePath, text, true);
}
// Re-throw the exception to propagate it further
throw e;
}
}
// Run the function
run();