JDownloader Community - Appwork GmbH
 

Reply
 
Thread Tools Display Modes
  #1  
Old 28.11.2024, 16:01
FBD's Avatar
FBD FBD is offline
Fibre Channel User
 
Join Date: Nov 2018
Location: https://web.libera.chat/#jDownloader
Posts: 117
Post Convert downloaded audio files to mp3 format

This script converts downloaded audio files to mp3 format and removes the original file.

This script requires the "A download stopped" trigger to be selected.

Be default AAC M4A and OGG audio files are converted, add more file extensions if necessary.

Spoiler:
Code:
// Convert audio files to mp3 format
// Trigger : A download stopped

if (link.finished) {
  var input = link.downloadPath;
  // add or remove filetypes here
  var output = input.replace(/(aac|m4a|ogg)$/, "mp3");

  if (input != output) {
      try {
          var ffmpeg = callAPI("config", "get", "org.jdownloader.controlling.ffmpeg.FFmpegSetup", null, "binarypath");
          var bitrate = callSync(ffmpeg, "-i", input).match(/bitrate: (\d+) kb/)[1];

          callAsync(function(error) {
              !error && getPath(input).delete();
          }, ffmpeg, "-y", "-i", input, "-b:a", bitrate + "k", output);
      } catch (e) {};
  }
}


Direct download: raw.githubusercontent.com/FarBeyondDriven/JDscripts/refs/heads/main/ConvertToMP3.js
__________________
irc.libera.chat #jDownloader web.libera.chat/#jDownloader
Reply With Quote
  #2  
Old 07.01.2025, 12:37
pspzockerscene's Avatar
pspzockerscene pspzockerscene is offline
Community Manager
 
Join Date: Mar 2009
Location: Deutschland
Posts: 74,768
Default

Extended version I created for a ticket user:

Main differences:
- special logging capabilities
- exception-test boolean
- all "settings" (boolean switches) are at the beginning of the code
- uses "early return" principle
Code:
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.

    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");
        }

        // 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 && getPath(destFile).exists()) {
            deleteFile(sourceFile, 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();
__________________
JD Supporter, Plugin Dev. & Community Manager

Erste Schritte & Tutorials || JDownloader 2 Setup Download
Spoiler:

A users' JD crashes and the first thing to ask is:
Quote:
Originally Posted by Jiaz View Post
Do you have Nero installed?
Reply With Quote
  #3  
Old 19.02.2025, 21:53
FBD's Avatar
FBD FBD is offline
Fibre Channel User
 
Join Date: Nov 2018
Location: https://web.libera.chat/#jDownloader
Posts: 117
Smile

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();
__________________
irc.libera.chat #jDownloader web.libera.chat/#jDownloader

Last edited by FBD; 19.02.2025 at 22:21.
Reply With Quote
  #4  
Old 20.02.2025, 09:45
pspzockerscene's Avatar
pspzockerscene pspzockerscene is offline
Community Manager
 
Join Date: Mar 2009
Location: Deutschland
Posts: 74,768
Default

Nice!

Small idea:

To better cover the case with the "not yet existing image", you could another boolean aka "expectThumbnail" and if that is true, go through a wait loop up to X seconds to wait for the presence of the image.
This may make this script more reliable.
__________________
JD Supporter, Plugin Dev. & Community Manager

Erste Schritte & Tutorials || JDownloader 2 Setup Download
Spoiler:

A users' JD crashes and the first thing to ask is:
Quote:
Originally Posted by Jiaz View Post
Do you have Nero installed?
Reply With Quote
  #5  
Old 20.02.2025, 11:01
mgpai mgpai is offline
Script Master
 
Join Date: Sep 2013
Posts: 1,692
Default

Quote:
Originally Posted by pspzockerscene View Post
To better cover the case with the "not yet existing image", you could another boolean aka "expectThumbnail" and if that is true, go through a wait loop up to X seconds to wait for the presence of the image.
This may make this script more reliable.
"Package finished" is more suitable in such cases. All related files (if any) would have been downloaded. Just loop through the files and convert, embed subttiles or images as required.
Reply With Quote
  #6  
Old 20.02.2025, 12:31
pspzockerscene's Avatar
pspzockerscene pspzockerscene is offline
Community Manager
 
Join Date: Mar 2009
Location: Deutschland
Posts: 74,768
Default

Ah yeah you are absolutely right!
__________________
JD Supporter, Plugin Dev. & Community Manager

Erste Schritte & Tutorials || JDownloader 2 Setup Download
Spoiler:

A users' JD crashes and the first thing to ask is:
Quote:
Originally Posted by Jiaz View Post
Do you have Nero installed?
Reply With Quote
  #7  
Old 20.02.2025, 16:35
FBD's Avatar
FBD FBD is offline
Fibre Channel User
 
Join Date: Nov 2018
Location: https://web.libera.chat/#jDownloader
Posts: 117
Default

Quote:
Originally Posted by mgpai View Post
"Package finished" is more suitable in such cases. All related files (if any) would have been downloaded. Just loop through the files and convert, embed subttiles or images as required.
Hm, that would only work if JDownloader is not configured to immediately remove finished files, right? If the finished files are removed immediately, then the "Package finished" trigger would only get the single file that finished last. And not files that finished previously because they are already removed. Or does that work differently?
__________________
irc.libera.chat #jDownloader web.libera.chat/#jDownloader
Reply With Quote
  #8  
Old 20.02.2025, 17:34
mgpai mgpai is offline
Script Master
 
Join Date: Sep 2013
Posts: 1,692
Default

Quote:
Originally Posted by FBD View Post
Hm, that would only work if JDownloader is not configured to immediately remove finished files, right? If the finished files are removed immediately, then the "Package finished" trigger would only get the single file that finished last. And not files that finished previously because they are already removed. Or does that work differently?
Yes. Same as with your script. Subs/images can be embeded only if the setting is 'never'. You can always use the same script to remove the links automatically.

Code:
function getBestImage(link) {
    var ytId = link.getProperty("YT_ID");

    link.package.downloadLinks.some(function(item) {
        if (item.getProperty("YT_ID") == ytId) {
            var data = JSON.parse(item.getProperty("YT_VARIANT"));

            if (data.id == "IMAGE_MAX") {
                imgPath = item.downloadPath;
                return true;
            }
        }
    })

    return imgPath;
}
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

All times are GMT +2. The time now is 20:12.
Provided By AppWork GmbH | Privacy | Imprint
Parts of the Design are used from Kirsch designed by Andrew & Austin
Powered by vBulletin® Version 3.8.10 Beta 1
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.