Created
March 12, 2020 00:12
-
-
Save Somrlik/4cd36ea42e28557ceed69e62fab2ad74 to your computer and use it in GitHub Desktop.
Convert ebooks using calibri CLI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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