// 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'); const util = require('./util'); const cli = require('./cli'); // convert all files in input directory function batchConvert(config, callback) { async.waterfall([ // get files function (waterfallCallback) { util.readDirRecursive(config.input, config.format, waterfallCallback); }, // display info, prompt user and create progressbar function (files, waterfallCallback) { cli.askForConfirmation(files.length + ' files found.\nstart conversion now?', ['yes', 'y'], function (err) { if (err) { return waterfallCallback(err); } waterfallCallback(null, files, cli.createProgressBar(files.length)); }); }, // process each file function (files, bar, waterfallCallback) { async.eachLimit(files, config.concurrency, function (file, eachCallback) { convert(file, config, function (err) { bar.tick(); if (err) { return eachCallback(err); } eachCallback(); }); }, waterfallCallback); } ], callback); } // convert file to mp3 at specified bitrate function convert(source, config, callback) { async.waterfall([ // get metadata function (waterfallCallback) { extractMetadata(source, waterfallCallback); }, // create path from metadata function (metadata, waterfallCallback) { util.getPathByMetadata(source, config.output, metadata, waterfallCallback); }, // create target directory function (target, waterfallCallback) { target = path.join(path.dirname(target), path.basename(target, path.extname(target)) + '.mp3'); fse.mkdirs(path.dirname(target), function (err) { if (err) { return waterfallCallback(err); } waterfallCallback(null, target, config); }); }, // convert file to mp3 function (target, config, waterfallCallback) { ffmpeg(path.normalize(source)).audioCodec('libmp3lame').audioBitrate(config.bitrate).save(target) .on('error', function (err) { return waterfallCallback(err); }) .on('end', function () { waterfallCallback(); }); } ], callback); } // extract cover artwork function extractArtwork(sourceFile, callback) { async.waterfall([ function (asyncCallback) { extractMetadata(sourceFile, asyncCallback); }, function (metadata, asyncCallback) { metadata.picture.forEach(function (picture) { const pic = path.join(path.dirname(sourceFile), path.basename(sourceFile, path.extname(sourceFile)) + '.' + picture.format); const stream = fs.createWriteStream(pic); stream.write(picture.data); stream.end(); asyncCallback(); }); } ], function (err, result) { if (err) { return callback(err); } callback(); }); } // extract metadata for further processing function extractMetadata(sourceFile, callback) { const stream = fs.createReadStream(sourceFile); metadata(stream, function (err, metadata) { if (err) { return callback(err); } stream.close(); callback(null, metadata); }); } // api exports.convert = convert; exports.batchConvert = batchConvert; exports.extractArtwork = extractArtwork; exports.extractMetadata = extractMetadata;