Add authentication.

This commit is contained in:
Roel van Uden 2015-01-24 14:34:59 +01:00
parent 17b5c8414c
commit fd9ef59145
5 changed files with 90 additions and 13 deletions

View File

@ -40,12 +40,20 @@ prior to using this application.
* Add batch-mode to queue a bunch of series. * Add batch-mode to queue a bunch of series.
* Add CLI interface with all the options. * Add CLI interface with all the options.
* Support scheduled merging; if it fails now, the video is probably being watched. * 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 ### 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. * 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 ## Work In Progress
Open an issue or e-mail me directly. I'd be happy to answer your questions. Open an issue or e-mail me directly. I'd be happy to answer your questions.

View File

@ -91,6 +91,9 @@ function _parse(args) {
.option('-m, --merge', 'Disables merging subtitles and videos.') .option('-m, --merge', 'Disables merging subtitles and videos.')
// Filters // Filters
.option('-e, --episode <i>', 'The episode filter.') .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 // Settings
.option('-f, --format <s>', 'The subtitle format. (Default: ass)') .option('-f, --format <s>', 'The subtitle format. (Default: ass)')
.option('-o, --output <s>', 'The output path.') .option('-o, --output <s>', 'The output path.')

View File

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

63
src/request.js Normal file
View File

@ -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;
}

View File

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