An open-source lightweight Equestrian lossless (if possible) audio codec
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 

163 lines
5.6 KiB

#!/usr/bin/env node
global.path = require('path');
global.fs = require('fs');
global.zlib = require('zlib');
global.os = require('os');
global.child_process = require('child_process');
global.uuid = require('uuid')['v4'];
global.version = "1.1";
global.argv = process.argv;
argv.shift(); argv.shift();
console.log("Equestria Lossless Audio Codec");
console.log(" (c) Copyright, Equestria.dev");
console.log("ELAC Encoder");
console.log(" (c) Copyright, Equestria.dev");
console.log("version " + version);
console.log("");
try {
child_process.execSync("ffmpeg -version");
child_process.execSync("ffprobe -version");
child_process.execSync("ffplay -version");
} catch (e) {
console.error("Unable to start the ELAC Encoder:\n ffmpeg was not found or is incomplete");
process.exit(2);
}
if (argv.length < 1) {
console.error("Unable to start the ELAC Encoder:\n No output file has been specified");
process.exit(2);
}
global.output = argv[0].trim();
global.dir = path.dirname(output);
global.file = path.basename(output);
if (!output.endsWith(".elac")) {
console.warn("warning: specified output (\"" + output + "\") does not contain extension, using \"" + output + ".elac\" instead.");
file = file + ".elac";
output = output + ".elac";
}
if (!fs.existsSync(dir)) {
console.error("Unable to encode \"" + file + "\":\n \"" + dir + "\": no such file or directory");
process.exit(2);
}
if (fs.existsSync(output)) {
console.error("Unable to encode \"" + file + "\":\n \"" + output + "\": file exists");
process.exit(2);
}
if (argv.length < 2) {
console.error("Unable to encode \"" + file + "\":\n No input stream found");
process.exit(2);
}
global.inputs = argv; inputs.shift();
global.valid_inputs = [];
global.out = {
_eqmc: true,
files: []
};
for (let input of inputs) {
if (!fs.existsSync(input)) {
console.warn("warning: specified input (\"" + input + "\") cannot be used: no such file or directory.");
continue;
}
try {
fs.readFileSync(input);
} catch (e) {
console.warn("warning: specified input (\"" + input + "\") cannot be used: file is unreadable.");
continue;
}
let probe = JSON.parse(child_process.execFileSync("ffprobe", [ "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", input]).toString());
let streams = probe.streams.filter(i => i['codec_type'] === "audio");
if (streams.length < 1) {
console.warn("warning: specified input (\"" + input + "\") cannot be used: no audio streams found.");
continue;
}
if (streams.length > 1) {
console.warn("warning: specified input (\"" + input + "\") contains more than one audio stream, only the " + probe.streams[0]["codec_name"] + " stream will be used.");
continue;
}
valid_inputs.push(input);
}
for (let input of valid_inputs) {
let probe = JSON.parse(child_process.execFileSync("ffprobe", [ "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", input]).toString());
let streams = probe.streams.filter(i => i['codec_type'] === "audio");
let fileId = uuid();
let lossless = false;
let outFile = os.tmpdir() + "/elac-encode-temp/" + fileId + ".mp3";
if (streams[0]['codec_name'].includes("pcm") || streams[0]['codec_name'].includes("flac") || streams[0]['codec_name'].includes("alac") || streams[0]['codec_name'].includes("dts")) {
lossless = true;
outFile = os.tmpdir() + "/elac-encode-temp/" + fileId + ".flac";
}
console.log("\n" + input + ":");
console.log(" Duration: " + new Date(probe.format['duration'] * 1000).toISOString().substring(11, 19));
console.log(" Container Type: " + probe.format['format_long_name']);
console.log(" Stream Type: " + streams[0]['codec_long_name']);
console.log(" ELAC Stream ID: " + fileId);
console.log(" Lossless Mode: " + (lossless ? "yes" : "no") + " (" + (lossless ? "ELAC Physical Lossless" : "ELAC Emulated Lossless") + ")");
console.log("");
try {
if (fs.existsSync(os.tmpdir() + "/elac-encode-temp")) {
fs.rmSync(os.tmpdir() + "/elac-encode-temp", { recursive: true });
}
fs.mkdirSync(os.tmpdir() + "/elac-encode-temp", { recursive: true });
} catch (e) {
console.error("Unable to encode \"" + file + "\":\n Cannot create temporary working directory");
process.exit(2);
}
console.log("Encoding \"" + output + ":/" + fileId + "\"...");
try {
child_process.execFileSync("ffmpeg", [ "-v", "quiet", "-i", input, "-map_metadata", "-1", outFile ]);
if (!fs.existsSync(outFile)) {
console.error("Unable to encode \"" + file + "\":\n Successfully encoded file " + fileId + ", but file does not exist");
process.exit(2);
}
} catch (e) {
console.error("Unable to encode \"" + file + "\":\n Failed to encode file " + fileId);
process.exit(2);
}
out.files.push({
_id: fileId,
lossless,
data: zlib.deflateSync(fs.readFileSync(outFile), {
level: -1,
strategy: 0,
memLevel: 9
}).toString("base64"),
tags: probe.format.tags
})
try {
fs.rmSync(os.tmpdir() + "/elac-encode-temp", { recursive: true });
} catch (e) {
console.error("Unable to encode \"" + file + "\":\n Cannot clear temporary working directory");
process.exit(2);
}
}
console.log("Writing to \"" + output + "\"...");
fs.writeFileSync(output, zlib.deflateSync(JSON.stringify(out), {
level: -1,
strategy: 0,
memLevel: 9
}));
console.log("Done!");