implemented artwork functionality

This commit is contained in:
Daniel Sommer 2017-03-24 23:36:41 +01:00
parent e1e6d8edeb
commit cbe726c4fc
3 changed files with 98 additions and 28 deletions

View file

@ -28,18 +28,24 @@ function parseCLI() {
.version(app.version) .version(app.version)
.usage('[options] <command>') .usage('[options] <command>')
.option('-c, --concurrency <n>', 'specify concurrency level', os.cpus().length); .option('-c, --concurrency <n>', 'specify concurrency level', os.cpus().length);
// conversion mode // conversion
commander commander
.command('convert <input> <output>') .command('convert <input> <output>')
.description('convert .flac to .mp3 files') .description('convert .flac to .mp3 files')
.option('-b, --bitrate <n>', 'specify conversion bitrate', 320) .option('-b, --bitrate <n>', 'specify conversion bitrate', 320)
.action(convert); .action(convert);
// sort mode // sort
commander commander
.command('sort <input> <output>') .command('sort <input> <output>')
.description('sort audio files by tags') .description('sort audio files by tags')
.option('-f, --format <type>', 'specify audio format (\'flac\', \'mp3\')', 'flac') .option('-f, --format <type>', 'specify audio format (\'flac\', \'mp3\')', 'flac')
.action(sort); .action(sort);
// artwork
commander
.command('artwork <input>')
.description('extract cover artwork')
.option('-f, --format <type>', 'specify audio format (\'flac\', \'mp3\')', 'flac')
.action(artwork);
// parse command line arguments // parse command line arguments
commander.parse(process.argv); commander.parse(process.argv);
} }
@ -79,6 +85,26 @@ function convert(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 (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);
});
}
// sort files // sort files
function sort(input, output, options) { function sort(input, output, options) {
async.waterfall([ async.waterfall([
@ -114,26 +140,6 @@ 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 // move file to location defined by its metadata
function processFileSort(output, sourceFile, callback) { function processFileSort(output, sourceFile, callback) {
async.waterfall([ async.waterfall([
@ -152,4 +158,39 @@ function processFileSort(output, sourceFile, callback) {
} }
callback(null, results); callback(null, results);
}); });
}
// extract artwork
function artwork(input, options) {
async.waterfall([
function (asyncCallback) {
recursive(input, [ignoreFilter], asyncCallback);
},
function (files, asyncCallback) {
console.log(files.length + ' \'' + options.format + '\' files found');
// display progressbar
const bar = util.createProgressBar(files.length);
// handle each file
async.eachLimit(files, commander.concurrency, function (file, eachCallback) {
audio.extractArtwork(file, 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(options.format) == -1;
}
} }

View file

@ -5,9 +5,10 @@ const async = require('async');
const metadata = require('musicmetadata'); const metadata = require('musicmetadata');
const ffmpeg = require('fluent-ffmpeg'); const ffmpeg = require('fluent-ffmpeg');
const fse = require('fs-extra'); const fse = require('fs-extra');
const util = require('./util');
// convert file to mp3 at specified bitrate // convert file to mp3 at specified bitrate
exports.convert = function convert(input, output, bitrate, callback) { function convert(input, output, bitrate, callback) {
output = path.join(path.dirname(output), path.basename(output, path.extname(output)) + '.mp3'); output = path.join(path.dirname(output), path.basename(output, path.extname(output)) + '.mp3');
async.series([ async.series([
function (asyncCallback) { function (asyncCallback) {
@ -28,16 +29,44 @@ exports.convert = function convert(input, output, bitrate, callback) {
} }
callback(); 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 // extract metadata for further processing
exports.extractMetadata = function extractMetadata(sourceFile, callback) { function extractMetadata(sourceFile, callback) {
const stream = fs.createReadStream(sourceFile); const stream = fs.createReadStream(sourceFile);
metadata(stream, function (err, metadata) { metadata(stream, function (err, metadata) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
stream.close(); stream.close();
callback(null, sourceFile, metadata) callback(null, metadata);
}); });
}; }
// api
exports.convert = convert;
exports.extractArtwork = extractArtwork;
exports.extractMetadata = extractMetadata;

View file

@ -1,6 +1,6 @@
{ {
"name": "badger-am", "name": "badger-am",
"version": "0.3.0", "version": "0.4.0",
"license": "MIT", "license": "MIT",
"description": "audio manager", "description": "audio manager",
"author": "Daniel Sommer <daniel.sommer@velvettear.de>", "author": "Daniel Sommer <daniel.sommer@velvettear.de>",