Skip to content

Instantly share code, notes, and snippets.

@Somrlik
Created March 12, 2020 00:12
Show Gist options
  • Save Somrlik/4cd36ea42e28557ceed69e62fab2ad74 to your computer and use it in GitHub Desktop.
Save Somrlik/4cd36ea42e28557ceed69e62fab2ad74 to your computer and use it in GitHub Desktop.
Convert ebooks using calibri CLI
#!/usr/bin/env node
/*
* Converts e-books using calibri convert-ebook binary to files well readable on ipad/any tablet
*
* Libs required can be installed by running `yarn add glob async yargs`
*
* Requires node >= 10 I guess.
*/
const glob = require('glob');
const async = require('async');
const yargs = require('yargs');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const fs = require('fs');
const path = require('path');
const os = require('os');
const argv = require('yargs')
.version(false)
.usage('$0', 'Converts e-books from --source-dir to the same dir in --outputformat')
.alias('h', 'host')
.string('source-dir')
.describe('source-dir', 'Source directory to process')
.string('out-dir')
.describe('out-dir', 'The destination directory')
.string('output-format')
.default('pdf')
.describe('output-format', 'The output format to get')
.number('j')
.default('j', String(os.cpus().length))
.describe('j', 'Number of parallel jobs')
.help()
.epilog('BTW I hate the yargs docs.')
.argv;
const sourceDir = argv['source-dir'];
const outDir = argv['out-dir'];
const outputExtension = argv['output-format'].toLowerCase();
const nJobs = argv.j;
console.log(`Running with ${nJobs} jobs...`);
function getFilenameWithoutExtension(filename) {
return filename.split('.').slice(0, -1).join('.');
}
async function getAllFilesInDirectory(dir) {
var getDirectories = function (src, callback) {
glob(src + '/**/*', callback);
};
return new Promise(resolve => {
getDirectories(dir, function (err, res) {
if (err) {
console.log('Error', err);
} else {
resolve(res);
}
});
});
}
async function doOne(tuple, resolve) {
const infile = tuple[0], outfile = tuple[1];
exec(`ebook-convert "${infile}" "${outfile}" --input-profile=kindle --output-profile=ipad`)
.then(() => {
console.log(`Finished ${infile} => ${outfile}`);
}).catch(() => {
console.error(`Failed to convert ${infile} => ${outfile}`);
})
.finally(resolve);
}
(async () => {
const jobs = [];
const allFiles = await getAllFilesInDirectory(sourceDir);
if (! fs.existsSync(outDir)) {
fs.mkdirSync(outDir, {recursive: true});
}
for (const idx in allFiles) {
const filename = allFiles[idx];
const filenameWithoutExtension = getFilenameWithoutExtension(filename);
const outDirClean = path.relative(sourceDir, filename);
const outputFilename = `${getFilenameWithoutExtension(outDirClean)}.${outputExtension}`;
if (fs.lstatSync(filename).isDirectory()) {
console.log(`${filename} is a directory, skipping`);
continue;
}
if (fs.existsSync(outputFilename)) {
console.log(`${outputFilename} Already exists, skipping...`);
continue;
}
if (path.extname(filename).toLowerCase() === `.${outputExtension}`) {
console.log(`The input and output files have the same format! ${outputFilename}`);
continue;
}
jobs.push([filename, path.join(outDir, outputFilename)]);
}
console.log(`Processing ${jobs.length} files...`);
const ret = await async.mapLimit(jobs, nJobs, async (one) => {
return new Promise(resolve => {
doOne(one, resolve);
});
}, (err) => {
console.error(err);
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment