#!/usr/bin/ruby

# Date: 17 June 2014
# License: GPLv3
# Website: http://github.com/trizen

func usage(code=0) {
    var name = File(__FILE__).basename

    <<"USAGE".say;
# Convert any multimedia file to MP3 by executing multiple
# instances of `ffmpeg` in parallel, in a controllable way.

Usage:
      #{name} [options] -f [files]

Options:
       -t int         : use this many threads (default: 2)
       -o str         : output directory (default: .)
       -f f1 f2 [...] : input files
USAGE

    Sys.exit(code);
}

func executeCmd(cmd, arg) {
    Sys.run(cmd, arg...);
}

func wait_th(forks) {
    say "** Running threads: #{forks.len}";
    say "** FFmpeg exit-code: #{forks.pop_rand.wait}";
}

func main() {
    const extensionRe = %r/\.\w{1,5}\z/;
    const outputFormat = "mp3";

    const ffmpegCmd = "ffmpeg";
    var ffmpegArg = ["-y", "-vn", "-ac", "2", "-ab", "192K", "-ar", "48000", "-f", outputFormat];

    var files = [];
    var maxThreads = 2;
    var outputDir = Dir.cwd;

    # Command-line flags
    frequire('Getopt::Long').GetOptionsFromArray(ARGV,
        'output-dir|o=s' => func(_, val) { outputDir  = Dir(val) },
        'threads|t=i'    => func(_, val) { maxThreads = Num(val) },
        'files|f=s{1,}'  => func(_, val) { files.push(File(val)) },
        'help|h'         => func         { usage()               },
    );

    if (files.len == 0) {
        Sys.warn("\n[!] No input file has been provided!\n");
        usage(2);
    }

    if (!outputDir.exists) {
        outputDir.create || die "Can't create dir `#{outputDir}': #{$!}";
    }

    var forks = [];
    var counter = 0;
    files.each { |file|

        if (!file.exists) {
            Sys.warn("File `#{file}' does not exists! Skipping file...\n");
            next;
        }

        if (!file.is_file) {
            Sys.warn("File `#{file}' is not a plain file! Skipping it...\n");
            next;
        }

        # Basename only
        var outputFile = file.basename;

        # Remove the format suffix
        outputFile.sub!(extensionRe);

        outputFile = File(outputDir, outputFile + '.' + outputFormat);
        printf("[%2d] %s -> %s\n", counter++, file, outputFile);

        var args = ['-i', file.to_s, ffmpegArg..., outputFile];
        forks.append({executeCmd(ffmpegCmd, args)}.fork);

        if (forks.len >= maxThreads) {
            wait_th(forks);
        }
    }

    while (forks.len > 0) {
        wait_th(forks);
    }

    if (counter > 0) {
        say "** All done!";
    }
}

main();