diff --git a/badger-am.js b/badger-am.js index 3e096c7..1a289c0 100755 --- a/badger-am.js +++ b/badger-am.js @@ -33,13 +33,7 @@ function parseCLI() { .command('convert ') .description('convert .flac to .mp3 files') .option('-b, --bitrate ', 'specify conversion bitrate', 320) - .action(function (input, output, options) { - console.log('--- CONVERT MODE ---'); - console.log('input: ' + input); - console.log('output: ' + output); - console.log('bitrate: ' + commander.options.bitrate); - console.log('conc: ' + commander.concurrency); - }); + .action(convert); // sort mode commander .command('sort ') @@ -50,9 +44,43 @@ function parseCLI() { commander.parse(process.argv); } +// convert files +function convert(input, output, options) { + async.waterfall([ + function (asyncCallback) { + recursive(input, [ignoreFilter], asyncCallback); + }, + function (files, asyncCallback) { + console.log(files.length + ' \'flac\' files found'); + // display progressbar + const bar = util.createProgressBar(files.length); + // handle each file + async.eachLimit(files, commander.concurrency, function (file, eachCallback) { + processFileConvert(output, file, options, function (err) { + bar.tick(); + if (err) { + return eachCallback(err); + } + eachCallback(); + }); + }, function (err) { + if (err) { + return asyncCallback(err); + } + asyncCallback(); + }); + } + ], function (err, result) { + util.exit(err, start); + }); + + function ignoreFilter(file, stats) { + return !stats.isDirectory() && path.extname(file).indexOf('flac') == -1; + } +} + // sort files function sort(input, output, options) { - const concurrency = commander.concurrency; async.waterfall([ function (asyncCallback) { recursive(input, [ignoreFilter], asyncCallback); @@ -62,8 +90,8 @@ function sort(input, output, options) { // display progressbar const bar = util.createProgressBar(files.length); // handle each file - async.eachLimit(files, concurrency, function (file, eachCallback) { - processFileSort(output, file, function (err) { + async.eachLimit(files, commander.concurrency, function (file, eachCallback) { + processFileSort(output, file, options, function (err) { bar.tick(); if (err) { return eachCallback(err); @@ -86,6 +114,26 @@ function sort(input, output, options) { } } +// move file to location defined by its metadata +function processFileConvert(output, sourceFile, options, callback) { + async.waterfall([ + function (asyncCallback) { + audio.extractMetadata(sourceFile, asyncCallback) + }, + function (sourceFile, metadata, asyncCallback) { + util.pathFromMetadata(sourceFile, output, metadata, asyncCallback); + }, + function (targetFile, asyncCallback) { + audio.convert(sourceFile, targetFile, options.bitrate, asyncCallback); + } + ], function (err, results) { + if (err) { + return callback(err); + } + callback(null, results); + }); +} + // move file to location defined by its metadata function processFileSort(output, sourceFile, callback) { async.waterfall([ diff --git a/lib/audio.js b/lib/audio.js index c84a07f..f86e04f 100644 --- a/lib/audio.js +++ b/lib/audio.js @@ -1,6 +1,34 @@ // requirements const fs = require('fs'); +const path = require('path'); +const async = require('async'); const metadata = require('musicmetadata'); +const ffmpeg = require('fluent-ffmpeg'); +const fse = require('fs-extra'); + +// convert file to mp3 at specified bitrate +exports.convert = function convert(input, output, bitrate, callback) { + output = path.join(path.dirname(output), path.basename(output, path.extname(output)) + '.mp3'); + async.series([ + function (asyncCallback) { + fse.mkdirs(path.dirname(output), asyncCallback); + }, + function (asyncCallback) { + ffmpeg(path.normalize(input)).audioCodec('libmp3lame').audioBitrate(bitrate).save(output) + .on('error', function (err) { + return asyncCallback(err); + }) + .on('end', function () { + asyncCallback(); + }); + } + ], function (err, results) { + if (err) { + return callback(err); + } + callback(); + }); +}; // extract metadata for further processing exports.extractMetadata = function extractMetadata(sourceFile, callback) { diff --git a/lib/util.js b/lib/util.js index 0274575..a64e897 100644 --- a/lib/util.js +++ b/lib/util.js @@ -4,17 +4,16 @@ const async = require('async'); const fse = require('fs-extra'); const progress = require('progress'); -// print logo -exports.printLogo = function printLogo() { +function printLogo() { console.log(' _ _ _ __ __ '); console.log(' | |__ __ _ __| |__ _ ___ _ _ /_\\ | \\/ |'); console.log(' | \'_ \\\/ _` \/ _` \/ _` / -_) \'_\/ _ \\| |\\/| |'); console.log(' |_.__\/\\__,_\\__,_\\__, \\___|_|\/_\/ \\_\\_| |_|'); console.log(' |___/ '); -}; +} // create path for target file -exports.pathFromMetadata = function pathFromMetadata(file, output, metadata, callback) { +function pathFromMetadata(file, output, metadata, callback) { // define directory let filePath = path.normalize(output); if (metadata.albumartist && metadata.albumartist.length > 0) { @@ -56,10 +55,10 @@ exports.pathFromMetadata = function pathFromMetadata(file, output, metadata, cal fileName += path.extname(file); // join directory and name callback(null, path.join(filePath, fileName)); -}; +} // create target directory and move the file -exports.moveFile = function moveFile(source, target, callback) { +function moveFile(source, target, callback) { async.series([ function (asyncCallback) { fse.mkdirs(path.dirname(target), asyncCallback); @@ -73,26 +72,26 @@ exports.moveFile = function moveFile(source, target, callback) { } callback(); }); -}; +} // fill a string beginning from the front -exports.frontfill = function frontFill(string, fill, length) { +function frontFill(string, fill, length) { while (string.toString().length < length) { string = fill + string; } return string; -}; +} // create a ascii progressbar -exports.createProgressBar = function createProgressBar(total) { +function createProgressBar(total) { return new progress(':bar | progress: :current/:total (:percent) | elapsed: :elapseds | eta: :etas', { total: total, width: 32 }); -}; +} // shutdown -exports.exit = function exit(err, start) { +function exit(err, start) { if (err) { console.error(err); process.exit(1); @@ -100,4 +99,12 @@ exports.exit = function exit(err, start) { const diff = process.hrtime(start); console.log('exiting after ' + ((diff[0] + (diff[1] / 1000000)) / 1000).toFixed(2) + ' seconds'); process.exit(0); -}; \ No newline at end of file +} + +// api +exports.printLogo = printLogo; +exports.pathFromMetadata = pathFromMetadata; +exports.moveFile = moveFile; +exports.frontFill = frontFill; +exports.createProgressBar = createProgressBar; +exports.exit = exit; \ No newline at end of file diff --git a/package.json b/package.json index ce5d267..a26805e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "badger-am", - "version": "0.2.2", + "version": "0.3.0", "license": "MIT", "description": "audio manager", "author": "Daniel Sommer ",