implemented artwork functionality
This commit is contained in:
parent
e1e6d8edeb
commit
cbe726c4fc
3 changed files with 98 additions and 28 deletions
85
badger-am.js
85
badger-am.js
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
39
lib/audio.js
39
lib/audio.js
|
@ -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;
|
|
@ -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>",
|
||||||
|
|
Loading…
Reference in a new issue