[CODE] Calling Filebot from JDownloader 2 Event Scripter

Running FileBot from the console, Groovy scripting, shell scripts, etc
Post Reply
caphm
Power User
Posts: 6
Joined: 12 Feb 2014, 13:59

[CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by caphm »

Hey guys,

I have created a script to be used with the Event Scripter extension of JDownloader 2. There are many requests around the web for how to automatically call Filebot when a download is finished in JD2. However I have never come across a satisfying solution, so I created one myself.

It relies on JD2 to extract archives (beaucse of the built in password "cracker") and then runs the AMC script on the extracted files. If it detects nested archives, it will skip processing until the actually relevant files are extracted. After successful renaming, the cleaner script will be called on the base directory of the package, to get rid of empty folders higher up in the hierarchy. It kind of relies on you downloading your stuff into separate folders for each package, but it should work either way.

There are a few options at the top of the script you need to customize and then you should be good to go. Just set up a script to be triggered on the "Archive Extraction Finished" event in JD2 and paste the code:

Code: Select all

var command = "filebot"; // Change this if filebot isn't on your path
var logfile = "C:/log/jdownloader-eventscripter.log"; // File this script logs to. Use forward slashes as path separators!
var filebotLogfile = "C:/log/filebot.log"; // File that filebot will log to. Use forward slashes as path separators!
var downloadBase = "D:\\Downloads"; // Base folder under which your download packages reside. Use escaped backslashes as path separators!
var archiveExtensions = /(\.(zip|rar|7z|par\d+|part\d+|r\d+|t\d+|\d{3}))$/; // Regex to test for nested archives in extracted files

// Parameters for the scripts to run
var params = {
    "rename" : {
        "options" : {
            "-script": "fn:amc",
            "--log-file": filebotLogfile,
            "--action": "move",
            "--conflict": "auto"
        },
        "defs" : {
            "plex": "ZEUS",
            "unsorted": "y",
            "skipExtract": "y",
            "clean": "y",
            "minFileSize": "104857600",
            "excludeList": "C:/Util/amc-input.txt",
            "seriesFormat": "D:/Serien/{n}/{'Season '+s}/{n} - {s00e00} - {t}",
            "movieFormat": "D:/Filme/{n} ({y}) [{vf}]/{n} ({y}) [{vf}]"
        },
        "switches" : [
            "-non-strict"
        ]
    },
    "cleaner" : {
        "options" : {
            "-script": "fn:cleaner",
            "--log-file": filebotLogfile
        },
        "defs" : {
            "root" : "y"
        },
        "switches" : []
    }
}

var logBuf = "";

function log(message) {
    logBuf += new Date().toISOString().slice(0, 19) + " - " + message + "\r\n";
}

function logArray(message, arr) {
    log("\t" + message);

    if (arr == null) {
        log("\t\tnone");
        return;
    }

    for (var i = 0; i < arr.length; i++) {
        log("\t\t" + arr[i]);
    }
}

function logSpacer() {
    log("++++++++++++++++++++++++++++++");
}

function flushLog() {
    // Comment out the next two lines to prevent log from being written to file
    writeFile(logfile, logBuf, true);
    logBuf = "";
}

function quoteIfNecessary(value) {
    return (value != null && value.indexOf("\"") < 0) ? '"' + value + '"' : value;
}

function quoteArrayElements(input) {
    var result = [];

    for (var i = 0; i < input.length; i++) {
        result[result.length] = quoteIfNecessary(input[i]);
    }

    return result;
}

function reduce(map, joinChar) {
    var keyValuePairs = [];

    for (var key in map) {
        keyValuePairs[keyValuePairs.length] = key + joinChar + map[key];
    }

    return keyValuePairs;
}

function mapToArray(map) {
    var array = [];

    for (var key in map) {
        array[array.length] = key;
        array[array.length] = map[key];
    }

    return array;
}

function createArgumentArray(parameters, inputs) {
    var options = mapToArray(parameters["options"]);
    var switches = parameters["switches"];
    var defs = reduce(parameters["defs"], "=");

    return [command].concat(options).concat(switches).concat(inputs).concat(["--def"]).concat(defs);
}

function isArchiveFile(filename) {
    return archiveExtensions.test(filename);
}

function containsNestedArchive(extractedFiles) {
    for (var i = 0; i < extractedFiles.length; i++) {
        if (isArchiveFile(extractedFiles[i])) {
            return true;
        }
    }

    return false;
}

function getPackageRoot(folder) {
    return folder.substring(0, folder.indexOf("\\", downloadBase.length + 1));
}

var archiveFolder = archive.getFolder();
var archiveName = archive.getName();
var archiveType = archive.getArchiveType();
var extractedFiles = archive.getExtractedFiles();
var archiveUID = archiveFolder + "\\" + archiveName;
var packageRoot = getPackageRoot(archiveFolder);

logSpacer();
log("FINISHED EXTRACTION - " + archiveUID);
log("\tType: " + archiveType);
log("\tPackage root: " + packageRoot);
logArray("Extracted files:", extractedFiles);

if (extractedFiles == null || extractedFiles.length == 0) {
    log("SKIPPING - No files extracted.");
} else if (containsNestedArchive(extractedFiles)) {
    log("SKIPPING - Nested archive detected.");
} else {
    var renameInputs = quoteArrayElements(extractedFiles);
    var renameArgs = createArgumentArray(params["rename"], renameInputs);
    log("RUNNING SCRIPT - " + archiveUID);

    callAsync(
        function(exitCode, stdOut, errOut) {
            if (exitCode == 0) {
                log("SUCCESS - " + archiveUID);

                var cleanerArgs = createArgumentArray(params["cleaner"], packageRoot);

                log("CLEANING - " + packageRoot);

                callAsync(
                    function(exitCode, stdOut, errOut) {
                        if(exitCode == 0) {
                            log("ALL CLEAN - " + packageRoot);
                        } else {
                            log("STILL DIRTY - " + packageRoot);
                        }

                        flushLog();
                    },
                    cleanerArgs
                );
            } else {
                log("ERROR - " + archiveUID + " - Code " + exitCode);
            }

            flushLog();
        },
        renameArgs
    );
}

logSpacer();
flushLog();
You can customize the way how Filebot is called by changing the values in params. The produced logfiles can become chunky rather quickly, so be sure to clean them out regularly to prevent wasting disk space. Or go ahead and comment out the part where the logfile is written (I have indicated the relevant lines in the code) to disable logging entirely. For debugging purposes I would not recommend that however.

I hope this script helps some of you people, it certainly worked like a charm for me :) Feel free to distribute and modify upon it and report any bugs you find in this thread.
nytram
Posts: 30
Joined: 12 Feb 2014, 04:34

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by nytram »

Hi

Nice work :-)

Thanks
User avatar
rednoah
The Source
Posts: 22923
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by rednoah »

Looks good. Thanks for the contribution! Might be a good idea to put in on GitHub? :)
:idea: Please read the FAQ and How to Request Help.
traxxus
Posts: 8
Joined: 27 Aug 2015, 08:50

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by traxxus »

Excellent!

But i Need some help: The subtitle files which are coming with the movie, get deleted instead of moved into the movie-folder...

My script:

Code: Select all

var command = "C://Program Files//FileBot//filebot.exe"; // Change this if filebot isn't on your path
var logfile = "C:/jdownloader2-eventscripter.log"; // File this script logs to. Use forward slashes as path separators!
var filebotLogfile = "C:/filebot.log"; // File that filebot will log to. Use forward slashes as path separators!
var downloadBase = "T:\\JDownloader\\entpackt"; // Base folder under which your download packages reside. Use escaped backslashes as path separators!
var archiveExtensions = /(\.(zip|rar|7z|par\d+|part\d+|r\d+|t\d+|\d{3}))$/; // Regex to test for nested archives in extracted files

// Parameters for the scripts to run
var params = {
    "rename": {
        "options": {
            "-script": "fn:amc",
            "--log-file": filebotLogfile,
            "--action": "move",
            "--conflict": "auto",
            "--lang": "de"

        },
        "defs": {
            "unsorted": "y",
            "skipExtract": "y",
            "clean": "y",
            "minFileSize": "104857600",
            "excludeList": "C:/amc-input.txt",
            "seriesFormat": "//NAS/Serien/{n}/{episode.special ? 'Special' : 'Season '+s.pad(2)}/{n}.{episode.special ? 'S00E'+special.pad(2) : s00e00}.{t.replaceAll(/[`´‘’ʻ]/, /'/).replaceAll(/[!?.]+$/).replacePart(', Part $1')}{'.'+lang}",
            "movieFormat": "//NAS/Filme/{n} ({y})/{n} ({y}){' CD'+pi}{'.'+lang}"
        },
        "switches": [
            "-non-strict"
        ]
    },
    "cleaner": {
        "options": {
            "-script": "fn:cleaner",
            "--log-file": filebotLogfile
        },
        "defs": {
            "root": "y"
        },
        "switches": []
    }
}

var logBuf = "";

function log(message) {
    logBuf += new Date().toISOString().slice(0, 19) + " - " + message + "\r\n";
}

function logArray(message, arr) {
    log("\t" + message);

    if (arr == null) {
        log("\t\tnone");
        return;
    }

    for (var i = 0; i < arr.length; i++) {
        log("\t\t" + arr[i]);
    }
}

function logSpacer() {
    log("++++++++++++++++++++++++++++++");
}

function flushLog() {
    // Comment out the next two lines to prevent log from being written to file
    writeFile(logfile, logBuf, true);
    logBuf = "";
}

function quoteIfNecessary(value) {
    return (value != null && value.indexOf("\"") < 0) ? '"' + value + '"' : value;
}

function quoteArrayElements(input) {
    var result = [];

    for (var i = 0; i < input.length; i++) {
        result[result.length] = quoteIfNecessary(input[i]);
    }

    return result;
}

function reduce(map, joinChar) {
    var keyValuePairs = [];

    for (var key in map) {
        keyValuePairs[keyValuePairs.length] = key + joinChar + map[key];
    }

    return keyValuePairs;
}

function mapToArray(map) {
    var array = [];

    for (var key in map) {
        array[array.length] = key;
        array[array.length] = map[key];
    }

    return array;
}

function createArgumentArray(parameters, inputs) {
    var options = mapToArray(parameters["options"]);
    var switches = parameters["switches"];
    var defs = reduce(parameters["defs"], "=");

    return [command].concat(options).concat(switches).concat(inputs).concat(["--def"]).concat(defs);
}

function isArchiveFile(filename) {
    return archiveExtensions.test(filename);
}

function containsNestedArchive(extractedFiles) {
    for (var i = 0; i < extractedFiles.length; i++) {
        if (isArchiveFile(extractedFiles[i])) {
            return true;
        }
    }

    return false;
}

function getPackageRoot(folder) {
    return folder.substring(0, folder.indexOf("\\", downloadBase.length + 1));
}

var archiveFolder = archive.getFolder();
var archiveName = archive.getName();
var archiveType = archive.getArchiveType();
var extractedFiles = archive.getExtractedFiles();
var archiveUID = archiveFolder + "\\" + archiveName;
var packageRoot = getPackageRoot(archiveFolder);

logSpacer();
log("FINISHED EXTRACTION - " + archiveUID);
log("\tType: " + archiveType);
log("\tPackage root: " + packageRoot);
logArray("Extracted files:", extractedFiles);

if (extractedFiles == null || extractedFiles.length == 0) {
    log("SKIPPING - No files extracted.");
} else if (containsNestedArchive(extractedFiles)) {
    log("SKIPPING - Nested archive detected.");
} else {
    var renameInputs = quoteArrayElements(extractedFiles);
    var renameArgs = createArgumentArray(params["rename"], renameInputs);
    log("RUNNING SCRIPT - " + archiveUID);

    callAsync(
        function(exitCode, stdOut, errOut) {
            if (exitCode == 0) {
                log("SUCCESS - " + archiveUID);

                var cleanerArgs = createArgumentArray(params["cleaner"], packageRoot);

                log("CLEANING - " + packageRoot);

                callAsync(
                    function(exitCode, stdOut, errOut) {
                        if (exitCode == 0) {
                            log("ALL CLEAN - " + packageRoot);
                        } else {
                            log("STILL DIRTY - " + packageRoot);
                        }

                        flushLog();
                    },
                    cleanerArgs
                );
            } else {
                log("ERROR - " + archiveUID + " - Code " + exitCode);
            }

            flushLog();
        },
        renameArgs
    );
}

logSpacer();
flushLog();
neutron
Posts: 8
Joined: 23 Aug 2015, 07:43

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by neutron »

Got another problem. Trying to use this on a synology nas (using linux style file paths) and the whole downloadBase folder stuff does not work. Doesn't get the packageroot correct, adding weird paths in front of download base path and so on...
CENGOiSM
Posts: 3
Joined: 19 Sep 2015, 12:57

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by CENGOiSM »

neutron wrote:Got another problem. Trying to use this on a synology nas (using linux style file paths) and the whole downloadBase folder stuff does not work. Doesn't get the packageroot correct, adding weird paths in front of download base path and so on...
I had the same problems with Jdownloader and Filebot, not on a synology but on the zyxel nas.
My solution is this:
EventScripter in JD:

Code: Select all

[{"eventTrigger":"ON_ARCHIVE_EXTRACTED",
"enabled":true,
"name":"FileBot",
"script":"var amcFile = \"/ffp/filebot/jdtofilebot.sh\"; \n var path = archive.getFolder(); callAsync(function() {}, amcFile, path);",
"eventTriggerSettings":{}}]
So on the "archive extracted" event, the sh file "jdtofilebot.sh" is called with the parameter "path", which represents the absolute path to the "packageRoot" folder you're referring to.

jdtofilebot.sh is custom written by me, and looks like this:

Code: Select all

#!/ffp/bin/sh
##FileBot needs these variables
LOG="/ffp/filebot/logs/amc.log"
EXC="/ffp/filebot/amc.txt"
SERIEN="/i-data/md0/TV-Serien/{n}/Staffel {s}/{n} S{s}E{e}"
FILME="/i-data/md0/Filme/{n} ({y})/{n} ({fn.contains('1080') ? '1080p ':''}{fn.contains('720') ? '720p ':''}{fn.contains('3D') || fn.contains('3-D') ? '3D ':''}HD)"
ANIME="/i-data/md0/Anime/{n}/{n} E{absolute}"

##Run FileBot
/ffp/filebot/filebot.sh -script fn:amc --lang de --log-file $LOG --action move "$1" --def "seriesFormat=$SERIEN" "movieFormat=$FILME" "animeFormat=$ANIME" -non-strict --def clean=y --def excludeList=$EXC --def xbmc=10.0.1.5

## Clean empty folders, samples etc after processing
/ffp/filebot/filebot.sh -script fn:cleaner "$1" --def root=y
'--action move "$1"' this $1 will be passed by the eventscripter in jd (the packageroot folder). So jdtofilebot.sh calls then filebot.sh which is shipped with the filebot installation with some custom parameters (eg. formatting, moving, update kodi library, ...). I really dont know if this helps you, but feel free to use it and set it to your personal needs. :D

Be sure to set the right (absolute) paths to the scripts, movies/shows folder, logs, exclude list - and the needed rights (755) and also to run it with the same users, so there is no problems with the privileges, rights etc.
zocidj
Posts: 9
Joined: 08 Oct 2014, 13:00

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by zocidj »

Hy can you help me to make script work for me, step by step. Thanks
Crack00r
Posts: 19
Joined: 26 Nov 2015, 11:00

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by Crack00r »

May anyone can help me?
I have right now this in Jdownloader 2
but I just get a error . . .
net.sourceforge.htmlunit.corejs.javascript.EcmaError: TypeError: Cannot call method "substring" of null (#129)

Code: Select all

var command = "C://Program Files//FileBot//filebot.exe"; // Change this if filebot isn't on your path
var logfile = "C:/jdownloader-eventscripter.log"; // File this script logs to. Use forward slashes as path separators!
var filebotLogfile = "C:/filebot.log"; // File that filebot will log to. Use forward slashes as path separators!
var downloadBase = "D:\Downloads"; // Base folder under which your download packages reside. Use escaped backslashes as path separators!
var archiveExtensions = /(\.(zip|rar|7z|par\d+|part\d+|r\d+|t\d+|\d{3}))$/; // Regex to test for nested archives in extracted files

// Parameters for the scripts to run
var params = {
    "rename": {
        "options": {
            "-script": "fn:amc",
            "--log-file": filebotLogfile,
            "--action": "move",
            "--conflict": "auto"
        },
        "defs": {
            "plex": "crack00r",
            "unsorted": "y",
            "skipExtract": "y",
            "clean": "y",
            "minFileSize": "104857600",
            "excludeList": "C:\amc-input.txt",
            "seriesFormat": "D:\Filme\Filme\Serien\{n}/{'Season '+s}/{n} - {s00e00} - {t}",
            "movieFormat": "D:\Filme\Filme\Filme\{n} ({y}) [{vf}]/{n} ({y}) [{vf}]"
        },
        "switches": [
            "-non-strict"
        ]
    },
    "cleaner": {
        "options": {
            "-script": "fn:cleaner",
            "--log-file": filebotLogfile
        },
        "defs": {
            "root": "y"
        },
        "switches": []
    }
}

var logBuf = "";

function log(message) {
    logBuf += new Date().toISOString().slice(0, 19) + " - " + message + "\r\n";
}

function logArray(message, arr) {
    log("\t" + message);

    if (arr == null) {
        log("\t\tnone");
        return;
    }

    for (var i = 0; i < arr.length; i++) {
        log("\t\t" + arr[i]);
    }
}

function logSpacer() {
    log("++++++++++++++++++++++++++++++");
}

function flushLog() {
    // Comment out the next two lines to prevent log from being written to file
    writeFile(logfile, logBuf, true);
    logBuf = "";
}

function quoteIfNecessary(value) {
    return (value != null && value.indexOf("\"") < 0) ? '"' + value + '"' : value;
}

function quoteArrayElements(input) {
    var result = [];

    for (var i = 0; i < input.length; i++) {
        result[result.length] = quoteIfNecessary(input[i]);
    }

    return result;
}

function reduce(map, joinChar) {
    var keyValuePairs = [];

    for (var key in map) {
        keyValuePairs[keyValuePairs.length] = key + joinChar + map[key];
    }

    return keyValuePairs;
}

function mapToArray(map) {
    var array = [];

    for (var key in map) {
        array[array.length] = key;
        array[array.length] = map[key];
    }

    return array;
}

function createArgumentArray(parameters, inputs) {
    var options = mapToArray(parameters["options"]);
    var switches = parameters["switches"];
    var defs = reduce(parameters["defs"], "=");

    return [command].concat(options).concat(switches).concat(inputs).concat(["--def"]).concat(defs);
}

function isArchiveFile(filename) {
    return archiveExtensions.test(filename);
}

function containsNestedArchive(extractedFiles) {
    for (var i = 0; i < extractedFiles.length; i++) {
        if (isArchiveFile(extractedFiles[i])) {
            return true;
        }
    }

    return false;
}

function getPackageRoot(folder) {
    return folder.substring(0, folder.indexOf("\\", downloadBase.length + 1));
}

var archiveFolder = archive.getFolder();
var archiveName = archive.getName();
var archiveType = archive.getArchiveType();
var extractedFiles = archive.getExtractedFiles();
var archiveUID = archiveFolder + "\\" + archiveName;
var packageRoot = getPackageRoot(archiveFolder);

logSpacer();
log("FINISHED EXTRACTION - " + archiveUID);
log("\tType: " + archiveType);
log("\tPackage root: " + packageRoot);
logArray("Extracted files:", extractedFiles);

if (extractedFiles == null || extractedFiles.length == 0) {
    log("SKIPPING - No files extracted.");
} else if (containsNestedArchive(extractedFiles)) {
    log("SKIPPING - Nested archive detected.");
} else {
    var renameInputs = quoteArrayElements(extractedFiles);
    var renameArgs = createArgumentArray(params["rename"], renameInputs);
    log("RUNNING SCRIPT - " + archiveUID);

    callAsync(
        function(exitCode, stdOut, errOut) {
            if (exitCode == 0) {
                log("SUCCESS - " + archiveUID);

                var cleanerArgs = createArgumentArray(params["cleaner"], packageRoot);

                log("CLEANING - " + packageRoot);

                callAsync(
                    function(exitCode, stdOut, errOut) {
                        if (exitCode == 0) {
                            log("ALL CLEAN - " + packageRoot);
                        } else {
                            log("STILL DIRTY - " + packageRoot);
                        }

                        flushLog();
                    },
                    cleanerArgs
                );
            } else {
                log("ERROR - " + archiveUID + " - Code " + exitCode);
            }

            flushLog();
        },
        renameArgs
    );
}

logSpacer();
flushLog();
mrtec
Posts: 2
Joined: 16 Dec 2015, 19:57

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by mrtec »

I am having the same issue as the previous member.
can anybody help?
( also what to do to change the code to work only when package has finished? )
m4hu
Posts: 10
Joined: 26 Jan 2016, 10:50

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by m4hu »

has anyone tried to set this up on os x ?
ronniehd
Posts: 10
Joined: 29 Dec 2015, 17:01

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by ronniehd »

Crack00r wrote:May anyone can help me?
I have right now this in Jdownloader 2
but I just get a error . . .
net.sourceforge.htmlunit.corejs.javascript.EcmaError: TypeError: Cannot call method "substring" of null (#129)

Code: Select all

var command = "C://Program Files//FileBot//filebot.exe"; // Change this if filebot isn't on your path
var logfile = "C:/jdownloader-eventscripter.log"; // File this script logs to. Use forward slashes as path separators!
var filebotLogfile = "C:/filebot.log"; // File that filebot will log to. Use forward slashes as path separators!
var downloadBase = "D:\Downloads"; // Base folder under which your download packages reside. Use escaped backslashes as path separators!
var archiveExtensions = /(\.(zip|rar|7z|par\d+|part\d+|r\d+|t\d+|\d{3}))$/; // Regex to test for nested archives in extracted files

// Parameters for the scripts to run
var params = {
    "rename": {
        "options": {
            "-script": "fn:amc",
            "--log-file": filebotLogfile,
            "--action": "move",
            "--conflict": "auto"
        },
        "defs": {
            "plex": "crack00r",
            "unsorted": "y",
            "skipExtract": "y",
            "clean": "y",
            "minFileSize": "104857600",
            "excludeList": "C:\amc-input.txt",
            "seriesFormat": "D:\Filme\Filme\Serien\{n}/{'Season '+s}/{n} - {s00e00} - {t}",
            "movieFormat": "D:\Filme\Filme\Filme\{n} ({y}) [{vf}]/{n} ({y}) [{vf}]"
        },
        "switches": [
            "-non-strict"
        ]
    },
    "cleaner": {
        "options": {
            "-script": "fn:cleaner",
            "--log-file": filebotLogfile
        },
        "defs": {
            "root": "y"
        },
        "switches": []
    }
}

var logBuf = "";

function log(message) {
    logBuf += new Date().toISOString().slice(0, 19) + " - " + message + "\r\n";
}

function logArray(message, arr) {
    log("\t" + message);

    if (arr == null) {
        log("\t\tnone");
        return;
    }

    for (var i = 0; i < arr.length; i++) {
        log("\t\t" + arr[i]);
    }
}

function logSpacer() {
    log("++++++++++++++++++++++++++++++");
}

function flushLog() {
    // Comment out the next two lines to prevent log from being written to file
    writeFile(logfile, logBuf, true);
    logBuf = "";
}

function quoteIfNecessary(value) {
    return (value != null && value.indexOf("\"") < 0) ? '"' + value + '"' : value;
}

function quoteArrayElements(input) {
    var result = [];

    for (var i = 0; i < input.length; i++) {
        result[result.length] = quoteIfNecessary(input[i]);
    }

    return result;
}

function reduce(map, joinChar) {
    var keyValuePairs = [];

    for (var key in map) {
        keyValuePairs[keyValuePairs.length] = key + joinChar + map[key];
    }

    return keyValuePairs;
}

function mapToArray(map) {
    var array = [];

    for (var key in map) {
        array[array.length] = key;
        array[array.length] = map[key];
    }

    return array;
}

function createArgumentArray(parameters, inputs) {
    var options = mapToArray(parameters["options"]);
    var switches = parameters["switches"];
    var defs = reduce(parameters["defs"], "=");

    return [command].concat(options).concat(switches).concat(inputs).concat(["--def"]).concat(defs);
}

function isArchiveFile(filename) {
    return archiveExtensions.test(filename);
}

function containsNestedArchive(extractedFiles) {
    for (var i = 0; i < extractedFiles.length; i++) {
        if (isArchiveFile(extractedFiles[i])) {
            return true;
        }
    }

    return false;
}

function getPackageRoot(folder) {
    return folder.substring(0, folder.indexOf("\\", downloadBase.length + 1));
}

var archiveFolder = archive.getFolder();
var archiveName = archive.getName();
var archiveType = archive.getArchiveType();
var extractedFiles = archive.getExtractedFiles();
var archiveUID = archiveFolder + "\\" + archiveName;
var packageRoot = getPackageRoot(archiveFolder);

logSpacer();
log("FINISHED EXTRACTION - " + archiveUID);
log("\tType: " + archiveType);
log("\tPackage root: " + packageRoot);
logArray("Extracted files:", extractedFiles);

if (extractedFiles == null || extractedFiles.length == 0) {
    log("SKIPPING - No files extracted.");
} else if (containsNestedArchive(extractedFiles)) {
    log("SKIPPING - Nested archive detected.");
} else {
    var renameInputs = quoteArrayElements(extractedFiles);
    var renameArgs = createArgumentArray(params["rename"], renameInputs);
    log("RUNNING SCRIPT - " + archiveUID);

    callAsync(
        function(exitCode, stdOut, errOut) {
            if (exitCode == 0) {
                log("SUCCESS - " + archiveUID);

                var cleanerArgs = createArgumentArray(params["cleaner"], packageRoot);

                log("CLEANING - " + packageRoot);

                callAsync(
                    function(exitCode, stdOut, errOut) {
                        if (exitCode == 0) {
                            log("ALL CLEAN - " + packageRoot);
                        } else {
                            log("STILL DIRTY - " + packageRoot);
                        }

                        flushLog();
                    },
                    cleanerArgs
                );
            } else {
                log("ERROR - " + archiveUID + " - Code " + exitCode);
            }

            flushLog();
        },
        renameArgs
    );
}

logSpacer();
flushLog();
SAME HERE, did anyone find a solution?
This thread appears to be dead noone reply, hopefully OP or someone that knows can help with this.

T.I.A
ronniehd
Posts: 10
Joined: 29 Dec 2015, 17:01

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by ronniehd »

Ok i think i figured out and got rid of the error mentioned above and a second error related to permission denied.
I keep testing, once it works properly I'll post the code and setup with an explanation.
FadeAway12
Donor
Posts: 51
Joined: 04 Mar 2016, 10:08

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by FadeAway12 »

Any updates on this? :)
Crack00r
Posts: 19
Joined: 26 Nov 2015, 11:00

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by Crack00r »

I got this running on Windows, without any errors


the only problem is, it didnt delete the folder after move

Code: Select all

var command = "filebot.exe"; // Change this if filebot isn't on your path
var logfile = "D:/jdownloader-eventscripter.log"; // File this script logs to. Use forward slashes as path separators!
var filebotLogfile = "D:/filebot.log"; // File that filebot will log to. Use forward slashes as path separators!
var downloadBase = "D:/\Downloads/\extracted"; // Base folder under which your download packages reside. Use escaped backslashes as path separators!
var archiveExtensions = /(\.(zip|rar|7z|par\d+|part\d+|r\d+|t\d+|\d{3}))$/; // Regex to test for nested archives in extracted files

// Parameters for the scripts to run
var params = {
    "rename": {
        "options": {
            "-script": "fn:amc",
            "--log-file": filebotLogfile,
            "--action": "move",
            "--conflict": "override",
            "--lang": "de"
        },
        "defs": {
            "plex": "crack00r",
            "unsorted": "y",
            "skipExtract": "y",
            "clean": "y",
            "minFileSize": "104857600",
            "excludeList": "D:/amc-input.txt",
            "seriesFormat": "D:/Filme/Filme/Serien/{n}/{'Staffel '+s}/{n} - {s00e00} - {t}",
            "movieFormat": "D:/Filme/Filme/Filme/{n} ({y})"
        },
        "switches": [
            "-non-strict"
        ]
    },
    "cleaner": {
        "options": {
            "-script": "fn:cleaner",
            "--log-file": filebotLogfile
        },
        "defs": {
            "root": "y"
        },
        "switches": []
    }
}

var logBuf = "";

function log(message) {
    logBuf += new Date().toISOString().slice(0, 19) + " - " + message + "\r\n";
}

function logArray(message, arr) {
    log("\t" + message);

    if (arr == null) {
        log("\t\tnone");
        return;
    }

    for (var i = 0; i < arr.length; i++) {
        log("\t\t" + arr[i]);
    }
}

function logSpacer() {
    log("++++++++++++++++++++++++++++++");
}

function flushLog() {
    // Comment out the next two lines to prevent log from being written to file
    writeFile(logfile, logBuf, true);
    logBuf = "";
}

function quoteIfNecessary(value) {
    return (value != null && value.indexOf("\"") < 0) ? '"' + value + '"' : value;
}

function quoteArrayElements(input) {
    var result = [];

    for (var i = 0; i < input.length; i++) {
        result[result.length] = quoteIfNecessary(input[i]);
    }

    return result;
}

function reduce(map, joinChar) {
    var keyValuePairs = [];

    for (var key in map) {
        keyValuePairs[keyValuePairs.length] = key + joinChar + map[key];
    }

    return keyValuePairs;
}

function mapToArray(map) {
    var array = [];

    for (var key in map) {
        array[array.length] = key;
        array[array.length] = map[key];
    }

    return array;
}

function createArgumentArray(parameters, inputs) {
    var options = mapToArray(parameters["options"]);
    var switches = parameters["switches"];
    var defs = reduce(parameters["defs"], "=");

    return [command].concat(options).concat(switches).concat(inputs).concat(["--def"]).concat(defs);
}

function isArchiveFile(filename) {
    return archiveExtensions.test(filename);
}

function containsNestedArchive(extractedFiles) {
    for (var i = 0; i < extractedFiles.length; i++) {
        if (isArchiveFile(extractedFiles[i])) {
            return true;
        }
    }

    return false;
}

function getPackageRoot(folder) {
    return folder.substring(0, folder.indexOf("\\", downloadBase.length + 1));
}

var archiveFolder = archive.getFolder();
var archiveName = archive.getName();
var archiveType = archive.getArchiveType();
var extractedFiles = archive.getExtractedFiles();
var archiveUID = archiveFolder + "\\" + archiveName;
var packageRoot = getPackageRoot(archiveFolder);

logSpacer();
log("FINISHED EXTRACTION - " + archiveUID);
log("\tType: " + archiveType);
log("\tPackage root: " + packageRoot);
logArray("Extracted files:", extractedFiles);

if (extractedFiles == null || extractedFiles.length == 0) {
    log("SKIPPING - No files extracted.");
} else if (containsNestedArchive(extractedFiles)) {
    log("SKIPPING - Nested archive detected.");
} else {
    var renameInputs = quoteArrayElements(extractedFiles);
    var renameArgs = createArgumentArray(params["rename"], renameInputs);
    log("RUNNING SCRIPT - " + archiveUID);

    callAsync(
        function(exitCode, stdOut, errOut) {
            if (exitCode == 0) {
                log("SUCCESS - " + archiveUID);

                var cleanerArgs = createArgumentArray(params["cleaner"], packageRoot);

                log("CLEANING - " + packageRoot);

                callAsync(
                    function(exitCode, stdOut, errOut) {
                        if (exitCode == 0) {
                            log("ALL CLEAN - " + packageRoot);
                        } else {
                            log("STILL DIRTY - " + packageRoot);
                        }

                        flushLog();
                    },
                    cleanerArgs
                );
            } else {
                log("ERROR - " + archiveUID + " - Code " + exitCode);
            }

            flushLog();
        },
        renameArgs
    );
}

logSpacer();
flushLog();
caphm
Power User
Posts: 6
Joined: 12 Feb 2014, 13:59

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by caphm »

It's been a while, I'm sorry for leaving you guys with this still buggy script all alone :(

Anyway, I've been updating the script, fixing a lot of the bugs you mentioned and adding additional features.

Here's what's new:
  • Fixed detection of package root folder. I've tried using package.getDownloadFolder(), but if you have JDwonloader set to create a subfolder for each package, it only returns C:\PathToYourFolder\<jd:packagename>. That's why I reverted to the path matching method. This should work reasonably well on Windows, I've not encountered any problems. For other platforms, especially when using forward slashes as path separators, you will have to change line 143 to reflect this.
  • Added checking for relevant files: Many downloads include additional folders/files for sample, proof, etc. These shall not be considered and will thus not be excluded from processing.
  • Improved checking for nested archives: This is the major pitfall with JDownloaders EventScripter extension. The event is triggered for every successfully extracted archive. If there are nested archives, we don't want to run filebot prematurely. We only want it to run once the innermost archive has been extracted. When there are nested archives, the script recognizes this and skips further processing. The script will be called by JDowloader again once the nested archive has been extracted. This way you can use JDownloader for password cracking. Filebot won't do any decryption on its own. Just be sure to enable deep extract in JDownloader settings.
  • Added checking for relevant archives: Some downloads may contain separate archives for sample, proof and subs. We don't want filebot to run, if the main file has not been extracted yet. This works in conjunction with checking for relevant files. Together, these functions ensure, that filebot is only run once we definitely have the file we're interested in decrypted.
    Depending on the download, this will interfere with subtitles contained in separate archives. These will simply be ignored. Unfortunately, I haven't been able to find a solution yet, that doesn't break proper nested archive recognition.
  • Added tagging of packages for postprocessing: The package path will now be checked if it contains a tag to mark it for postprocessing. I've included it, because I didn't want everything processed by this script. It currently looks for [fb] (case insensitive, including the angle brackets) anywhere in the package path. You can customize this by modifying the regex in line 9.
  • Added tagging for 3D movies: Similarly to the postprocessing tag, you can also tag your packages if they are 3D. Just include [h-sbs] or [h-ou] (both case insensitive, must include angle brackets) anywhere in your package path, depending on the 3D format at hand. The movieFolderFormat and movieFileFormat in lines 16 and 17 use these tags to properly name your 3D movies and move them to a different location. If either h-sbs or h-ou tag is present, the folder where movies are stored will be prefixed with 3D. Also, the proper tag will be added to the new filename, such that your media center (Plex in my case) can properly recognize that it's a 3D movie.
    The naming scheme is specific to Plex's naming best practices. Be sure to customize it to your needs. I prefer having my 3D movies in a separate folder, because Plex doesn't indicate wether a movie has a 3D version while viewing your library.
    The tags can all be combined in one pair of angle brackets by separating them with any character, for example:

    Code: Select all

    [fb,h-sbs]
I've been running this script for quite a while now and it has proven fairly stable. It realiably detects nested archives and other junk files and only calls filebot when there really is a file we actually want to rename. Unfortunately, it doesn't work with subtitles contained in separate nested archives. Subtitles contained directly within the same archive as the main file(s) will be picked up and passed to filebot. You might have to tweak the arguments passed to filebot, to make sure that these will also be moved and not ignored.

I've tried adding Pushbullet support, to send notifications to your smartphone if a download is renamed successfully or if there was an error using alexschneider's amazing pushbullet-js (check it out at https://github.com/alexschneider/pushbullet-js ). Unfortunately, the EMACS interpreter used by JDownloader doesn't support the required Javascript functions, so we'll have to do without.

Hopefully this update will alleviate most of the troubles you guys have been dealing with. Go ahead and try it out:

Code: Select all

var command = "filebot"; // Change this if filebot isn't on your path
var logfile = "C:/log/jdownloader-eventscripter.log"; // File this script logs to. Use forward slashes as path separators!
var filebotLogfile = "C:/log/filebot.log"; // File that filebot will log to. Use forward slashes as path separators!
var downloadBase = "D:\\Downloads"; // Base folder under which your download packages reside. Use escaped backslashes as path separators!
var archiveExtensions = /(\.(zip|rar|7z|par\d+|part\d+|r\d+|t\d+|\d{3}))$/; // Regex to test for nested archives in extracted files
var irrelevantFiles = /(\\subs\\|\\proof\\|\-subs|\-proof)/i; // Regex for matching files which are irrelevant to renaming (subs, proof, etc)

var tags = {
    "filebot" : /\[.*FB.*\]/i,
    "3d" : "(?i)\\[.*(H\\-SBS|H\\-OU).*\\]",
    "hsbs" : "(?i)\\[.*H\\-SBS.*\\]",
    "hou" : "(?i)\\[.*H\\-OU.*\\]"
}

var movieDrive = "D:/Media/"
var movieFolderFormat = "{file =~ /"+tags["3d"]+"/ ? '3D ' : ''}Filme/{n} ({y}) [{vf}]{file =~ /"+tags["3d"]+"/ ? ' [3D]' : ''}{file =~ /"+tags["hsbs"]+"/ ? ' [H-SBS]' : ''}{file =~ /"+tags["hou"]+"/ ? ' [H-OU]' : ''}"
var movieFileFormat = "{n} ({y}) [{vf}]{file =~ /"+tags["3d"]+"/ ? ' [3D]' : ''}{file =~ /"+tags["hsbs"]+"/ ? ' [H-SBS]' : ''}{file =~ /"+tags["hou"]+"/ ? ' [H-OU]' : ''}{' CD'+pi}"

// Parameters for the scripts to run
var params = {
    "rename" : {
        "options" : {
            "-script": "fn:amc",
            "--log-file": filebotLogfile,
            "--action": "move",
            "--conflict": "auto"
        },
        "defs" : {
            "unsorted": "y",
            "skipExtract": "y",
            "clean": "y",
            "minFileSize": "104857600",
            "excludeList": "C:/Util/amc-input.txt",
            "seriesFormat": "D:/Media/Serien/{n}/{'Season '+s}/{n} - {s00e00} - {t}",
            "movieFormat": movieDrive + movieFolderFormat + "/" + movieFileFormat
        },
        "switches" : [
            "-non-strict"
        ]
    },
    "cleaner" : {
        "options" : {
            "-script": "fn:cleaner",
            "--log-file": filebotLogfile
        },
        "defs" : {
            "root" : "y"
        },
        "switches" : []
    }
}

var logBuf = "";

function log(message) {
    logBuf += new Date().toISOString().slice(0, 19) + " - " + message + "\r\n";
}

function logArray(message, arr) {
    log("\t" + message);

    if (arr == null || arr.length == 0) {
        log("\t\tnone");
        return;
    }

    for (var i = 0; i < arr.length; i++) {
        log("\t\t" + arr[i]);
    }
}

function logSpacer() {
    log("++++++++++++++++++++++++++++++");
}

function flushLog() {
    writeFile(logfile, logBuf, true);
    logBuf = "";
}

function quoteIfNecessary(value) {
    return (value != null && value.indexOf("\"") < 0) ? '"' + value + '"' : value;
}

function quoteArrayElements(input) {
    var result = [];

    for (var i = 0; i < input.length; i++) {
        result[result.length] = quoteIfNecessary(input[i]);
    }

    return result;
}

function reduce(map, joinChar) {
    var keyValuePairs = [];

    for (var key in map) {
        keyValuePairs[keyValuePairs.length] = key + joinChar + map[key];
    }

    return keyValuePairs;
}

function mapToArray(map) {
    var array = [];

    for (var key in map) {
        array[array.length] = key;
        array[array.length] = map[key];
    }

    return array;
}

function createArgumentArray(parameters, inputs) {
    var options = mapToArray(parameters["options"]);
    var switches = parameters["switches"];
    var defs = reduce(parameters["defs"], "=");

    return [command].concat(options).concat(switches).concat(inputs).concat(["--def"]).concat(defs);
}

function isArchiveFile(filename) {
    return archiveExtensions.test(filename);
}

function isRelevant(filename) {
    return !irrelevantFiles.test(filename);
}

function containsNestedArchive(extractedFiles) {
    for (var i = 0; i < extractedFiles.length; i++) {
        if (isArchiveFile(extractedFiles[i]) && isRelevant(extractedFiles[i])) {
            return true;
        }
    }

    return false;
}

function getPackageRoot(folder) {
    return folder.substring(0, folder.indexOf("\\", downloadBase.length + 1));
}

function commissionRelevantFiles(files) {
    if(files == null) return [];

    var relevantFiles = [];

    for (var i = 0; i < files.length; i++) {
        if(isRelevant(files[i])) {
            relevantFiles[relevantFiles.length] = files[i];
        }
    }

    return relevantFiles;
}

var links = archive.getDownloadLinks() ? archive.getDownloadLinks() : []
var package = links.length > 0 ? links[0].getPackage() : null
var archiveFolder = archive.getFolder();
var archiveName = archive.getName();
var archiveType = archive.getArchiveType();
var extractedFiles = archive.getExtractedFiles();
var archiveUID = archiveFolder + "\\" + archiveName;
var packageRoot = getPackageRoot(archiveFolder + "\\");
var relevantFiles = commissionRelevantFiles(extractedFiles);

logSpacer();
log("FINISHED EXTRACTION - " + archiveUID);
log("\tType: " + archiveType);
log("\tPackage root: " + packageRoot);
logArray("Extracted files:", extractedFiles);
logArray("Relevant files:", relevantFiles);

if (!tags["filebot"].test(packageRoot)) {
    log("SKIPPING - Package is not tagged for processing.");
} else if (extractedFiles == null || extractedFiles.length == 0) {
    log("SKIPPING - No files extracted.");
} else if (relevantFiles == null || relevantFiles.length == 0) {
    log("SKIPPING - No relevant files encountered.");
} else if (containsNestedArchive(extractedFiles)) {
    log("SKIPPING - Nested archive detected.");
} else if (!isRelevant(archiveUID)) {
    log("SKIPPING - Archive is irrelevant.");
} else {
    var renameInputs = quoteArrayElements(relevantFiles);
    var renameArgs = createArgumentArray(params["rename"], renameInputs);
    log("RUNNING SCRIPT - " + archiveUID);

    callAsync(
        function(exitCode, stdOut, errOut) {
            if (exitCode == 0) {
                log("SUCCESS - " + archiveUID);

                var cleanerArgs = createArgumentArray(params["cleaner"], packageRoot);

                log("CLEANING - " + packageRoot);

                callAsync(
                    function(exitCode, stdOut, errOut) {
                        if(exitCode == 0) {
                            log("ALL CLEAN - " + packageRoot);
                        } else {
                            log("STILL DIRTY - " + packageRoot);
                        }

                        flushLog();
                    },
                    cleanerArgs
                );
            } else {
                log("ERROR - " + archiveUID + " - Code " + exitCode);
            }

            flushLog();
        },
        renameArgs
    );
}

logSpacer();
flushLog();
As a last sidenote, I recommend ommitting the plex option from filebot and enabling Plex's internal folder watcher. By design of Plex's API, filebot is unfortunately limited to issuing a refresh command to all of your libraries. If you have a large photo or music library (like I do), this update takes ages to complete. Depending on the internal numbering of your libraries, your movies or series will only be picked up last, which can take up to over an hour. Plex's folder watcher has become really good recently. I've consistently seen it pick up new files after a couple of seconds, at least on Windows.
Crack00r
Posts: 19
Joined: 26 Nov 2015, 11:00

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by Crack00r »

i just get
SKIPPING - Package is not tagged for processing
on all files
User avatar
rednoah
The Source
Posts: 22923
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by rednoah »

Context? Logs?

SKIPPING - Package is not tagged for processing doesn't sound like something FileBot would say, so it's probably a JDownloader problem that's best discussed in the JDownloader forums:
https://board.jdownloader.org/
:idea: Please read the FAQ and How to Request Help.
caphm
Power User
Posts: 6
Joined: 12 Feb 2014, 13:59

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by caphm »

It's an output of the script. You have to include [fb] somewhere in the filename (including brackets). See second to last bullet of my last post for info.
User avatar
rednoah
The Source
Posts: 22923
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by rednoah »

My bad. I'll leave this thread in the capable hands of caphm then. :lol:
:idea: Please read the FAQ and How to Request Help.
blackpit
Posts: 1
Joined: 07 Nov 2021, 12:47

Re: [CODE] Calling Filebot from JDownloader 2 Event Scripter

Post by blackpit »

Moin zusammen,
ich bekomme es nicht hin das Musik mit gemacht wird. Movies und Serien läuft super aber Mucke??? Synology Jdownloader Event Scripter
Hier mal mein sh Datei

Code: Select all

#!/bin/sh
##FileBot benötigt diese Variablen
##/bin/mkdir /volume1/video/download/testordner
QUELLE="/volume1/video/download/"
export JAVA_OPTS=`free | awk -vm=1024 -vp=0.7 '/Mem:/ {printf "-Xmx%dm", ($2*p)/m; exit}'`    # set -Xmx to 0.7 of physical memory
LOG="/volume1/video/Filebot/amc/amc.log"
EXC="/volume1/video/Filebot/amc/amc.txt"
SERIEN="/volume1/video/Serien/{n}/{'Staffel 0'+s}/{n} - {s00e00} - {t} ({y}) {vf}"
FILME="/volume1/video/Filme/{n.colon(' - ')} - {y}{' CD'+pi}{subt}"
MUSIC="/volume1/video/Music/{n}/{album+'/'}{pi.pad(2)+'. '}{artist} - {t}"
##filebot -script 'fn:amc' /volume1/video/download --output /volume1/video/Music/ --lang en --def 'music=y' 'unsorted=y' 'clean=y' 'deleteAfterExtract=y' 'plex=0.0.0.0' 'pushbullet=vLOrqXdy0whTtBTrPyDxmBHPSfkhbf6n' 'excludeList=.excludes' --log all
##UNSORTIERT="/volume1/video/unsortiert/{file.parentFile.name}/{file.structurePathTail}" ### Wenn Filebot mal etwas nicht zuordnen kann, dann kannst du es in einen seperaten Ordner verschieben lassen, dann bleibt es im Downloadordner übersichtlich.
##FileBot ausführen
/volume1/@appstore/filebot/bin/filebot -script fn:amc --lang de --log-file $LOG --action move "$1" --output "/volume1/video/" --def "seriesFormat=$SERIEN" "movieFormat=$FILME" "musicFormat=$MUSIC" "unsortedFormat=$UNSORTIERT" -non-strict --def unsorted=y "artwork=n" --def clean=y
## Aufräumen, --def root löscht den Unterordner mit
/volume1/@appstore/filebot/bin/filebot -script fn:cleaner "$1" --def root=y
Post Reply