UNPKG

rar-to-zip

Version:

Converts rar (cbr) to zip (cbz), batch mode and CLI

343 lines (266 loc) 10.7 kB
(function () { 'use strict'; var fs = require('fs'); var util = require('util'); var path = require('path'); var child_process = require('child_process'); var exec = child_process.exec; var os = require('os'); var program = require('commander'); var glob = require("glob"); var tmp = require('tmp'); var log = require('npmlog'); var mkdirp = require('mkdirp'); module.exports = rar2Zip.convert = rar2Zip; rar2Zip.convert = function convert(rarFile, options, callback) { var tmpobj = null; var tmpDir = options.tmpDir; var zipFileName = null; /** * Removes true if OS is Mac OS X or Linux. * * @api private */ function isMacOrLinux() { return os.type() === 'Linux'|| os.type() === 'Darwin'; } // isMacOrLinux /** * Removes files and folders in a directory recursively. * * @api private */ function _cleanTmpDirectory() { // remove tmp directory: if (tmpobj) { tmpobj.removeCallback(); } else { _rmdirRecursiveSync(tmpDir); } if (!options.silent) log.info('r2z', 'Tmp directory deleted: %s', tmpDir); } // _cleanTmpDirectory /** * Creates a clean tmp directory. * If a given directory exists, it will be cleaned. * * @api private */ function _createTmpDirectory (){ if (!tmpDir) { tmpobj = tmp.dirSync(); tmpDir = tmpobj.name; } else { if (_fileExistsSync(tmpDir)) { _rmdirRecursiveSync(tmpDir); } mkdirp.sync(tmpDir, {}); } if (!options.silent) log.info('r2z', 'Tmp directory created: %s', tmpDir); } // _createTmpDirectory /** * Checks if a given directory or file exists. * * @param {String} p * @return {boolean} * @api private */ function _fileExistsSync(p) { try { return fs.statSync(p); } catch(e){ return false; } } // _fileExistsSync /** * Removes files and folders in a directory recursively. * The directory will be removed itself too. * * @param {String} root * @api private */ function _rmdirRecursiveSync(root) { var dirs = [root]; do { var dir = dirs.pop(); var deferred = false; var files = fs.readdirSync(dir); for (var i = 0, length = files.length; i < length; i++) { var file = path.join(dir, files[i]); var stat = fs.lstatSync(file); if (stat.isDirectory()) { if (!deferred) { deferred = true; dirs.push(dir); } dirs.push(file); } else { fs.unlinkSync(file); } } if (!deferred) { fs.rmdirSync(dir); } } while (dirs.length !== 0); } // _rmdirRecursiveSync function getFileName(s, escape){ if (path.sep === '/') { s = s.replace(/\\/g, '/'); } else { s = s.replace(/\//g, '\\'); } if (escape) s = s.replace(/ /g, '\\ '); return s; } /** * Main function * * @api private */ function _start() { if (!rarFile) rarFile = options.rarFile; if (!rarFile) return log.error('r2z',"Error: rarFile or options.rarFile missing."); var callb = callback ? callback : function(err, newfile){ if (err) return log.error('r2z', 'Error:', err); if (!options.silent) log.info('r2z', 'Created', newfile); }; if (!options.beforeCleanup) options.beforeCleanup = function(sourceFile, targetFile, tempDir){}; if (options.unrarEscapeSpaceInFileName === undefined) options.unrarEscapeSpaceInFileName = false; if (options.zipEscapeSpaceInFileName === undefined) options.zipEscapeSpaceInFileName = isMacOrLinux(); var skipIfZipAlreadyExists = options.skipIfZipAlreadyExists; if (skipIfZipAlreadyExists === undefined) skipIfZipAlreadyExists = true; var execUnrarTemplate = options.execUnrarTemplate ? options.execUnrarTemplate : "unrar x \"%s\" \"%s\""; var execZipTemplate = options.execZipTemplate ? options.execZipTemplate : "zip -r -0 %s %s "; var zipSuffix = options.zipSuffix ? options.zipSuffix : 'zip'; zipFileName = path.basename(rarFile, path.extname(rarFile)) + '.' + zipSuffix; zipFileName = path.join(path.dirname(rarFile), zipFileName); var tempZip = path.join(path.dirname(rarFile), 'o.zip'); if (_fileExistsSync(zipFileName)) { if (skipIfZipAlreadyExists) { return callb('Abort: Target file already exists.', zipFileName); } try { fs.unlinkSync(zipFileName); } catch(e) { callb('Error: Cannot delete ' + zipFileName, null); } } _createTmpDirectory(); var execUnrarCommand = util.format(execUnrarTemplate, getFileName(rarFile, options.unrarEscapeSpaceInFileName), getFileName(tmpDir, options.unrarEscapeSpaceInFileName)); var execZipCommand = util.format(execZipTemplate, getFileName(tempZip, options.zipEscapeSpaceInFileName), getFileName(path.join(tmpDir, '*'), options.zipEscapeSpaceInFileName)); if (!options.silent) { log.info('r2z', 'execUnrarCommand', execUnrarCommand); log.info('r2z', 'execZipCommand', execZipCommand); } // Unrar source dir: exec(execUnrarCommand, function (err, stdout, stderr) { if (err) { _cleanTmpDirectory(); return callb(err, null); } if (stdout.length > 0 && stdout.match(/.*not RAR archive.*/g)) { _cleanTmpDirectory(); return callb('Unsupported RAR.', null); } if (!options.silent) log.info('r2z', stdout); // Zip the directory tmpDir: exec(execZipCommand, function (err, stdout, stderr) { if (err) { log.error('r2z', err); _cleanTmpDirectory(); return callb('Error ' + err, null); } if (!options.silent) log.info('r2z', stdout); try { fs.renameSync(tempZip, zipFileName); options.beforeCleanup(rarFile, zipFileName, tmpDir); _cleanTmpDirectory(); if (!options.silent) log.info('r2z', 'Done', zipFileName); callb(null, zipFileName); } catch(e) { _cleanTmpDirectory(); if (!options.silent) log.error('r2z', 'Error: ' + e, zipFileName); callb("Could not move " + tempZip + " to " + zipFileName + '\n' + e, null); } }); }); } _start(); }; rar2Zip.convertByGlob = function convertByGlob(pattern, options, callback) { if (!options.silent) log.info('r2z', 'rar2Zip.convertByGlob...'); if (!pattern) options.filePattern; if (!pattern) return log.error('r2z',"Error: pattern or options.filePattern missing."); var callb = callback ? callback : function(err, newfile){ if (err) return log.error('r2z', 'Error:', err); if (!options.silent) log.info('r2z', 'Created', newfile); }; glob(pattern, {}, function (err, files) { function _nextTodo() { if (todoIdx < todos.length) { todos[todoIdx++](function(err, newfile){ if (!err) newfiles.push(newfile); _nextTodo(); }); } else { callb(null, newfiles); } } function _createTodo(rarFile) { return function (cb) { if (!options.silent) log.info('r2z'," File: " + rarFile); rar2Zip.convert(rarFile, options, cb); }; } if (err) return callback(err, "Error: Cannot read " + pattern); if (!options.silent) log.info('r2z',' Files found: ' + files.length); var newfiles = []; var todos = []; var todoIdx = 0; for (var i = 0; i < files.length; i++) { todos.push(_createTodo(files[i])); } _nextTodo(); }); }; rar2Zip(); function rar2Zip(){ console.log('r a r 2 Z i p ( )'); console.log(process.argv); // check, if there is a command call like: node node_modules/rar-to-zip/index.js -g -p /Volumes/2TB/comics/**/*.*r -s cbr program .version('0.0.1') .option('-p, --filePattern [filePattern]', 'Specify glob pattern [**/*.rar]', '**/*.rar') .option('-g, --glob', 'Set glob mode') .option('-f, --rarFile [rarFile]', 'Specify rarFile [abc.rar]', null) .option('-s, --zipSuffix [zip]', 'Specify the file suffix of the target file [zip]', 'zip') .option('-t, --tmpDir [tmp]', 'Specify the temp directory [tmp]', null) .option('-x, --skip', 'Skip convert if target file already exists') .option('-u, --unrarEscapeSpaceInFileName', 'Set escaping space in file name for unrar exec command') .option('-z, --zipEscapeSpaceInFileName', 'Set escaping space in file name for zip exec command') .option('-U, --execUnrarTemplate [execUnrarTemplate]', 'Specify glob pattern [unrar x "%s" "%s"]', 'unrar x "%s" "%s"') .option('-Z, --execZipTemplate [execZipTemplate]', 'Specify glob pattern [zip -r -0 %s %s]', 'zip -r -0 %s %s') .option('-s, --silent', 'Set silent mode') .parse(process.argv); var options = { silent: program.silent, skipIfZipAlreadyExists: program.skip, zipSuffix: program.zipSuffix, tmpDir: program.tmpDir, execUnrarTemplate: program.execUnrarTemplate, execZipTemplate: program.execZipTemplate, }; if (program.unrarEscapeSpaceInFileName !== undefined) options.unrarEscapeSpaceInFileName= program.unrarEscapeSpaceInFileName; if (program.zipEscapeSpaceInFileName !== undefined) options.zipEscapeSpaceInFileName= program.zipEscapeSpaceInFileName; console.log('options', options); if (program.glob) { rar2Zip.convertByGlob(program.filePattern, options, null); } else if (program.rarFile) { rar2Zip.convert(program.rarFile, options, null); } else { // no CLI mode } } })();