Browse Source

Add authentication.

Roel van Uden 9 years ago
parent
commit
fd9ef59145
5 changed files with 90 additions and 13 deletions
  1. 9 1
      README.md
  2. 3 0
      src/batch.js
  3. 9 7
      src/episode.js
  4. 63 0
      src/request.js
  5. 6 5
      src/series.js

+ 9 - 1
README.md

@@ -40,12 +40,20 @@ prior to using this application.
 * Add batch-mode to queue a bunch of series.
 * Add CLI interface with all the options.
 * Support scheduled merging; if it fails now, the video is probably being watched.
+* Add authentication to the entire stack to support premium content.
 
 ### Pending Implementation
 
-* Add authentication to the entire stack to support premium content.
+* Binary runner for `npm`
+* Windows examples with a .bat for ease of use.
+* Documentation.
+* Publish to `npm` with a fixed package.json.
 * Enjoy beautiful anime series from disk when internet is down.
 
+## Configuration
+
+Set defaults in https://www.crunchyroll.com/acct/?action=video. We'll use that.
+
 ## Work In Progress
 
 Open an issue or e-mail me directly. I'd be happy to answer your questions.

+ 3 - 0
src/batch.js

@@ -91,6 +91,9 @@ function _parse(args) {
     .option('-m, --merge', 'Disables merging subtitles and videos.')
     // Filters
     .option('-e, --episode <i>', 'The episode filter.')
+    // Authentication
+    .option('-p, --pass <s>', 'The password.')
+    .option('-u, --user <s>', 'The e-mail address or username.')
     // Settings
     .option('-f, --format <s>', 'The subtitle format. (Default: ass)')
     .option('-o, --output <s>', 'The output path.')

+ 9 - 7
src/episode.js

@@ -2,7 +2,7 @@
 var cheerio = require('cheerio');
 var fs = require('fs');
 var mkdirp = require('mkdirp');
-var request = require('request');
+var request = require('./request');
 var path = require('path');
 var subtitle = require('./subtitle');
 var video = require('./video');
@@ -15,9 +15,9 @@ var xml2js = require('xml2js');
  * @param {function(Error)} done
  */
 module.exports = function (config, address, done) {
-  _page(address, function(err, page) {
+  _page(config, address, function(err, page) {
     if (err) return done(err);
-    _player(address, page.id, function(err, player) {
+    _player(config, address, page.id, function(err, player) {
       if (err) return done(err);
       _download(config, page, player, done);
     });
@@ -87,13 +87,14 @@ function _name(config, page, series, tag) {
 /**
  * Requests the page data and scrapes the id, episode, series and swf.
  * @private
+ * @param {Object} config
  * @param {string} address
  * @param {function(Error, Object=)} done
  */
-function _page(address, done) {
+function _page(config, address, done) {
   var id = parseInt((address.match(/[0-9]+$/) || [0])[0], 10);
   if (!id) return done(new Error('Invalid address.'));
-  request.get(address, function(err, res, body) {
+  request.get(config, address, function(err, res, body) {
     if (err) return done(err);
     var $ = cheerio.load(body);
     var swf = /^([^?]+)/.exec($('link[rel=video_src]').attr('href'));
@@ -124,14 +125,15 @@ function _prefix(value, length) {
 /**
  * Requests the player data and scrapes the subtitle and video data.
  * @private
+ * @param {Object} config
  * @param {string} address
  * @param {number} id
  * @param {function(Error, Object=)} done
  */
-function _player(address, id, done) {
+function _player(config, address, id, done) {
   var url = address.match(/^(https?:\/\/[^\/]+)/);
   if (!url) return done(new Error('Invalid address.'));
-  request.post({
+  request.post(config, {
     form: {current_page: address},
     url: url[1] + '/xml/?req=RpcApiVideoPlayer_GetStandardConfig&media_id=' + id
   }, function(err, res, xml) {

+ 63 - 0
src/request.js

@@ -0,0 +1,63 @@
+'use strict';
+var isAuthenticated = false;
+var request = require('request');
+
+/**
+ * Performs a GET request for the resource.
+ * @param {Object} config
+ * @param {(string|Object)} options
+ * @param {function(Error, Object, string)} done
+ */
+module.exports.get = function(config, options, done) {
+  _authenticate(config, function(err) {
+    if (err) return done(err);
+    request.get(_modify(options), done);
+  });
+};
+
+/**
+* Performs a POST request for the resource.
+* @param {Object} config
+* @param {(string|Object)} options
+* @param {function(Error, Object, string)} done
+*/
+module.exports.post = function(config, options, done) {
+  _authenticate(config, function(err) {
+    if (err) return done(err);
+    request.post(_modify(options), done);
+  });
+};
+
+/**
+ * Authenticates using the configured pass and user.
+ * @param {Object} config
+ * @param {function(Error)} done
+ */
+function _authenticate(config, done) {
+  if (isAuthenticated || !config.pass || !config.user) return done();
+  request.post({
+    form: {
+      formname: 'RpcApiUser_Login',
+      fail_url: 'https://www.crunchyroll.com/login',
+      name: config.user,
+      password: config.pass
+    },
+    jar: true,
+    url: 'https://www.crunchyroll.com/?a=formhandler'
+  }, function(err) {
+    if (err) return done(err);
+    isAuthenticated = true;
+    done();
+  });
+}
+
+/**
+ * Modifies the options to use the authenticated cookie jar.
+ * @param {(string|Object)} options
+ * @returns {Object}
+ */
+function _modify(options) {
+  if (typeof options === 'string') options = {url: options};
+  options.jar = true;
+  return options;
+}

+ 6 - 5
src/series.js

@@ -3,7 +3,7 @@ var cheerio = require('cheerio');
 var episode = require('./episode');
 var persistent = '.crpersistent';
 var fs = require('fs');
-var request = require('request');
+var request = require('./request');
 var path = require('path');
 var url = require('url');
 
@@ -13,11 +13,11 @@ var url = require('url');
  * @param {string} address
  * @param {function(Error)} done
  */
-module.exports = function (config, address, done) {
+module.exports = function(config, address, done) {
   var persistentPath = path.join(config.output || process.cwd(), persistent);
   fs.readFile(persistentPath, 'utf8', function(err, data) {
     var cache = config.cache ? {} : JSON.parse(data || '{}');
-    _page(address, function(err, page) {
+    _page(config, address, function(err, page) {
       if (err) return done(err);
       var i = 0;
       (function next() {
@@ -62,11 +62,12 @@ function _download(cache, config, baseAddress, data, done) {
 /**
  * Requests the page data and scrapes the episodes and series.
  * @private
+ * @param {Object} config
  * @param {string} address
  * @param {function(Error, Object=)} done
  */
-function _page(address, done) {
-  request.get(address, function(err, res, body) {
+function _page(config, address, done) {
+  request.get(config, address, function(err, res, body) {
     if (err) return done(err);
     var $ = cheerio.load(body);
     var title = $('.season-dropdown').text() || $('span[itemprop=name]').text();