/
index.js
executable file
·192 lines (177 loc) · 6.84 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#!/usr/bin/env node
const pkg = require("./package");
const path = require("path"); // parse/build file paths
const Q = require("q"); // Promise library
const chunk = require("lodash.chunk");
const uniq = require("lodash.uniq");
const Pace = require("pace"); // progress bar
const glob = Q.nfbind(require("glob")); // filepath pattern matching
const mkdirp = Q.nfbind(require("mkdirp")); // mkdir -p
const exec = Q.nfbind(require("child_process").exec); // for running sox commands
const wavRegex = /.wav$/;
const defaultMp3Quality = "192";
const defaultOggQuality = "6";
const defaultChunkSize = 100;
if(require.main === module) { // called directly
const program = require("commander"); // parsing command line arguments
program
.version(pkg.version)
.usage("[options] <input-directory> <output-directory>")
.option("-c, --chunkSize <i>", "The number of files to be converted in parallel. Default: " + defaultChunkSize, parseInt)
.option("-p, --progressBar", "Display progress bar")
.option("-v, --verbose", "Print additional information")
.option("-m, --mp3Only", "Only generate MP3s")
.option("-o, --oggOnly", "Only generate OGGs")
.option("-M, --mp3Quality <n>", "MP3 quality argument provided to Sox. Default: " + defaultMp3Quality)
.option("-O, --oggQuality <n>", "OGG quality argument provided to Sox. Default: " + defaultOggQuality)
.parse(process.argv);
if(program.args.length < 2) {
program.outputHelp();
}
else {
convertDirectory(program.args[0], program.args[1], program)
.catch(function(error) {
console.log("Error: " + error);
});
}
}
else { // required as a module
module.exports = convertDirectory;
}
// Convert all .wav files in `inputDir` into .ogg and .mp3 files and output them to `outputDir`
function convertDirectory(inputDir, outputDir, options) {
var mp3Quality,
oggQuality,
chunkSize,
fileCount,
pace,
started = new Date();
inputDir = resolvePath(inputDir);
outputDir = resolvePath(outputDir);
options = options || {};
mp3Quality = options.mp3Quality || defaultMp3Quality;
oggQuality = options.oggQuality || defaultOggQuality;
chunkSize = options.chunkSize || defaultChunkSize;
mp3Only = options.mp3Only;
oggOnly = options.oggOnly;
progressBar = options.progressBar;
if(typeof chunkSize === "number" && chunkSize <= 0) {
return Q.reject("Chunk size must be a positive integer");
}
if(mp3Only && oggOnly) {
return Q.reject("Cannot use both mp3Only and oggOnly flags at the same time");
}
return Q.resolve()
.then(glob.bind(null, path.join(inputDir, "/**/*.wav")))
.then(function(files) {
return files.filter(function(file) { // filter out all non-wave files
return file.match(wavRegex);
});
})
.then(function(files) {
var outDirectories = uniq(files.map(path.dirname)).map(function(directory) {
return directory.replace(inputDir, outputDir);
});
fileCount = files.length;
if(progressBar) {
pace = Pace({total: fileCount}); // progress bar
pace.op(-1); // can't use op(0) to force an initial render
pace.op();
}
return Q.all(outDirectories.map(function(dir) {
return mkdirp(dir);
})).then(function() {
return files;
});
})
.then(function(files) {
verbose("Converting with chunk size " + chunkSize);
return chunk(files, chunkSize).reduce(function(promise, chunk) {
return promise.then(function() { // after the last chunk has finished
return Q.all(chunk.map(convertFile));
});
}, Q.resolve());
})
.then(function() {
if(!progressBar) {
verbose("Finished converting " + fileCount + " files in " + Math.abs(new Date() - started) + " ms");
}
});
// Convert a file to ogg, mp3, or both depending on mp3Only/oggOnly flags
function convertFile(file) {
var promise;
if(mp3Only) {
promise = buildMP3(file, inputDir, outputDir, mp3Quality);
}
else if(oggOnly) {
promise = buildOGG(file, inputDir, outputDir, oggQuality);
}
else {
promise = Q.all([
buildOGG(file, inputDir, outputDir, oggQuality),
buildMP3(file, inputDir, outputDir, mp3Quality)
]);
}
return promise
.then(function() {
if(progressBar) {
pace.op();
}
});
}
/*
Converts a .wav sound file to a .ogg sound file
Arguments:
inFile - File to be converted
inBase - Base path of the file to be converted
outBase - Base path of the output
quality - Quality argument provided to Sox
*/
function buildOGG(inFile, inBase, outBase, quality) {
var inDir = path.dirname(inFile),
outFile = inFile.replace(new RegExp("^"+inBase), outBase).replace(wavRegex, ".ogg");
verbose("Generating OGG");
verbose(" " + inFile);
verbose(" " + outFile);
return exec([
"sox",
"'"+inFile+"'",
(quality === false || quality === "false") ? "" : "-C"+quality,
"'" + outFile + "'"
].join(" "));
}
/*
Converts a .wav sound file to a .mp3 sound file
Arguments:
inFile - File to be converted
inBase - Base path of the file to be converted
outBase - Base path of the output
quality - Quality argument provided to Sox
*/
function buildMP3(inFile, inBase, outBase, quality) {
var inDir = path.dirname(inFile),
outFile = inFile.replace(new RegExp("^"+inBase), outBase).replace(wavRegex, ".mp3");
verbose("Generating MP3");
verbose(" " + inFile);
verbose(" " + outFile);
return exec([
"sox",
"'" + inFile + "'",
(quality === false || quality === "false") ? "" : "-C"+quality,
"'" + outFile + "'"
].join(" "));
}
// log if in verbose mode
function verbose() {
if(options.verbose) {
return console.log.apply(console, arguments);
}
}
}
// Same as `path.resolve` except this function handles "~" in path strings
function resolvePath(string) {
if(string[0] === '~') {
string = process.env.HOME + string.substr(1)
}
return path.resolve(string);
}