#!/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();