From 66f62d3aa1617c9dee6ae9472acc7df2887f2f4a Mon Sep 17 00:00:00 2001 From: Roel van Uden Date: Sat, 24 Jan 2015 12:29:12 +0100 Subject: [PATCH] Added batch mode and CLI interface. --- README.md | 7 +++-- app.js | 26 ----------------- index.js | 7 +++++ package.json | 1 + src/batch.js | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/episode.js | 50 ++++++++++++++++---------------- src/index.js | 5 ++++ 7 files changed, 118 insertions(+), 55 deletions(-) delete mode 100644 app.js create mode 100644 index.js create mode 100644 src/batch.js create mode 100644 src/index.js diff --git a/README.md b/README.md index e6c25fd..e26f820 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,14 @@ prior to using this application. * Add ASS support. * Add muxing (MP4+ASS=MKV). * Add series API to save an entire series rather than per-episode. +* Add support for incremental saves. +* Add batch-mode to queue a bunch of series. +* Add CLI interface with all the options. ### Pending Implementation -* Add batch-mode to queue a bunch of series. -* Add support for incremental saves; currently just overwriting stuff, bad. +* Support scheduled merging; if it fails now, the video is probably being watched. * Add authentication to the entire stack to support premium content. -* Add CLI interface with all the options. * Enjoy beautiful anime series from disk when internet is down. ## Work In Progress diff --git a/app.js b/app.js deleted file mode 100644 index ada956c..0000000 --- a/app.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; -var config = { - format: 'ass', // defaults to srt - merge: true, // defaults to false - path: 'F:\\Anime', // defaults to process.cwd(), - title: 'Fairy Tail', // defaults to series title. - tag: undefined, // defaults to CrunchyRoll -}; - -/*var episode = require('./src/episode'); -episode( - config, - 'http://www.crunchyroll.com/fairy-tail/episode-1-the-dragon-king-652167', - function(err) { - if (err) return console.log(err); - console.log('All done!'); -});*/ - -var series = require('./src/series'); -series( - config, - 'http://www.crunchyroll.com/fairy-tail', - function(err) { - if (err) return console.log(err.stack || err); - console.log('All done!'); -}); diff --git a/index.js b/index.js new file mode 100644 index 0000000..ccf1c5c --- /dev/null +++ b/index.js @@ -0,0 +1,7 @@ +'use strict'; +var src = require('./src'); + +src.batch(process.argv, function(err) { + if (err) return console.error(err.stack || err); + console.log('Finished.'); +}); diff --git a/package.json b/package.json index c29a70b..02972d3 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "big-integer": "^1.4.1", "cheerio": "^0.18.0", + "commander": "^2.6.0", "mkdirp": "^0.5.0", "request": "^2.51.0", "xml2js": "^0.4.4" diff --git a/src/batch.js b/src/batch.js new file mode 100644 index 0000000..658445b --- /dev/null +++ b/src/batch.js @@ -0,0 +1,77 @@ +'use strict'; +var Command = require('commander').Command; +var fs = require('fs'); +var path = require('path'); +var series = require('./series'); + +/** + * Streams the batch of series to disk. + * @param {Array.} args + * @param {function(Error)} done + */ +module.exports = function(args, done) { + var config = _parse(args); + var batchPath = path.join(config.output || process.cwd(), 'CrunchyRoll.txt'); + _tasks(config, batchPath, function(err, tasks) { + if (err) return done(err); + var i = 0; + (function next() { + if (i >= tasks.length) return done(); + series(tasks[i].config, tasks[i].address, function(err) { + if (err) return done(err); + i += 1; + next(); + }); + })(); + }); +}; + +/** + * Parses the configuration or reads the batch-mode file for tasks. + * @private + * @param {Object} config + * @param {string} batchPath + * @param {function(Error, Object=} done + */ +function _tasks(config, batchPath, done) { + if (config.args.length) { + return done(undefined, config.args.map(function(address) { + return {address: address, config: config}; + })); + } + fs.exists(batchPath, function(exists) { + if (!exists) return done(undefined, []); + fs.readFile(batchPath, 'utf8', function(err, data) { + if (err) return done(err); + var map = []; + data.split(/\r?\n/).forEach(function(line) { + var lineConfig = _parse(process.argv.concat(line.split(' '))); + lineConfig.args.forEach(function(address) { + if (!address) return; + map.push({address: address, config: lineConfig}); + }); + }); + done(undefined, map); + }); + }); +} + +/** + * Parses the arguments and returns a configuration. + * @private + * @param {Array.} args + * @returns {Object} + */ +function _parse(args) { + return new Command().version(require('../package').version) + // Disables + .option('-c, --cache', 'Disables the cache.') + .option('-m, --merge', 'Disables merging subtitles and videos.') + // Settings + .option('-f, --format ', 'The subtitle format. (Default: ass)') + .option('-o, --output ', 'The output path.') + .option('-s, --series ', 'The series override.') + .option('-t, --tag ', 'The subgroup. (Default: CrunchyRoll)') + .option('-v, --volume ', 'The volume.') + .parse(args); +} diff --git a/src/episode.js b/src/episode.js index dcaac95..a430079 100644 --- a/src/episode.js +++ b/src/episode.js @@ -24,21 +24,6 @@ module.exports = function (config, address, done) { }); }; -/** - * Affixes zero-padding to the value. - * @private - * @param {(number|string)} value - * @param {number} length - * @returns {string} - */ -function _affix(value, length) { - if (typeof value !== 'string') value = String(value); - var suffix = value.indexOf('.') !== -1; - var add = length - (suffix ? value.indexOf('.') : value.length); - while ((add -= 1) >= 0) value = '0' + value; - return value; -} - /** * Completes a download and writes the message with an elapsed time. * @param {string} message @@ -47,9 +32,9 @@ function _affix(value, length) { */ function _complete(message, begin, done) { var timeInMs = Date.now() - begin; - var seconds = _affix(Math.floor(timeInMs / 1000) % 60, 2); - var minutes = _affix(Math.floor(timeInMs / 1000 / 60) % 60, 2); - var hours = _affix(Math.floor(timeInMs / 1000 / 60 / 60), 2); + var seconds = _prefix(Math.floor(timeInMs / 1000) % 60, 2); + var minutes = _prefix(Math.floor(timeInMs / 1000 / 60) % 60, 2); + var hours = _prefix(Math.floor(timeInMs / 1000 / 60 / 60), 2); console.log(message + ' (' + hours + ':' + minutes + ':' + seconds + ')'); done(); } @@ -86,17 +71,17 @@ function _download(config, page, player, done) { } /** -* Names the file based on the config, page, series and tag. -* @param {Object} config -* @param {Object} page -* @param {string} series -* @param {string} tag -* @returns {string} -*/ + * Names the file based on the config, page, series and tag. + * @param {Object} config + * @param {Object} page + * @param {string} series + * @param {string} tag + * @returns {string} + */ function _name(config, page, series, tag) { var v = config.volume ? (config.volume < 10 ? '0' : '') + config.volume : ''; var e = (page.episode < 10 ? '0' : '') + page.episode; - return series + ' - ' + (v ? v + 'x' : '') + e + ' [' + tag + ']'; + return series + ' ' + (v ? v + 'x' : '') + e + ' [' + tag + ']'; } /** @@ -123,6 +108,19 @@ function _page(address, done) { }); } +/** + * Prefixes a value. + * @private + * @param {(number|string)} value + * @param {number} length + * @returns {string} + */ +function _prefix(value, length) { + if (typeof value !== 'string') value = String(value); + while (value.length < length) value = '0' + value; + return value; +} + /** * Requests the player data and scrapes the subtitle and video data. * @private diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..b0d833d --- /dev/null +++ b/src/index.js @@ -0,0 +1,5 @@ +module.exports = { + batch: require('./batch'), + episode: require('./episode'), + series: require('./series') +};