make login work

This commit is contained in:
Roei Elisha 2018-05-24 02:22:06 +03:00 committed by Godzil
parent 9e2f5401d0
commit 6ad4cbed0a
5 changed files with 177 additions and 142 deletions

59
package-lock.json generated
View File

@ -4,11 +4,15 @@
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@types/bluebird": {
"version": "3.5.20",
"resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.20.tgz",
"integrity": "sha512-Wk41MVdF+cHBfVXj/ufUHJeO3BlIQr1McbHZANErMykaCWeDSZbH5erGjNBw2/3UlRdSxZbLfSuQTzFmPOYFsA=="
},
"@types/caseless": { "@types/caseless": {
"version": "0.12.1", "version": "0.12.1",
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz",
"integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A==", "integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A=="
"dev": true
}, },
"@types/cheerio": { "@types/cheerio": {
"version": "0.22.7", "version": "0.22.7",
@ -20,7 +24,6 @@
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
"integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
"dev": true,
"requires": { "requires": {
"@types/node": "8.5.2" "@types/node": "8.5.2"
} }
@ -37,14 +40,12 @@
"@types/node": { "@types/node": {
"version": "8.5.2", "version": "8.5.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.2.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.2.tgz",
"integrity": "sha512-KA4GKOpgXnrqEH2eCVhiv2CsxgXGQJgV1X0vsGlh+WCnxbeAE1GT44ZsTU1IN5dEeV/gDupKa7gWo08V5IxWVQ==", "integrity": "sha512-KA4GKOpgXnrqEH2eCVhiv2CsxgXGQJgV1X0vsGlh+WCnxbeAE1GT44ZsTU1IN5dEeV/gDupKa7gWo08V5IxWVQ=="
"dev": true
}, },
"@types/request": { "@types/request": {
"version": "2.47.0", "version": "2.47.0",
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.0.tgz", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.0.tgz",
"integrity": "sha512-/KXM5oev+nNCLIgBjkwbk8VqxmzI56woD4VUxn95O+YeQ8hJzcSmIZ1IN3WexiqBb6srzDo2bdMbsXxgXNkz5Q==", "integrity": "sha512-/KXM5oev+nNCLIgBjkwbk8VqxmzI56woD4VUxn95O+YeQ8hJzcSmIZ1IN3WexiqBb6srzDo2bdMbsXxgXNkz5Q==",
"dev": true,
"requires": { "requires": {
"@types/caseless": "0.12.1", "@types/caseless": "0.12.1",
"@types/form-data": "2.2.1", "@types/form-data": "2.2.1",
@ -52,11 +53,19 @@
"@types/tough-cookie": "2.3.2" "@types/tough-cookie": "2.3.2"
} }
}, },
"@types/request-promise": {
"version": "4.1.41",
"resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.41.tgz",
"integrity": "sha512-qlx6COxSTdSFHY9oX9v2zL1I05hgz5lwqYiXa2SFL2nDxAiG5KK8rnllLBH7k6OqzS3Ck0bWbxlGVdrZhS6oNw==",
"requires": {
"@types/bluebird": "3.5.20",
"@types/request": "2.47.0"
}
},
"@types/tough-cookie": { "@types/tough-cookie": {
"version": "2.3.2", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.2.tgz", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.2.tgz",
"integrity": "sha512-vOVmaruQG5EatOU/jM6yU2uCp3Lz6mK1P5Ztu4iJjfM4SVHU9XYktPUQtKlIXuahqXHdEyUarMrBEwg5Cwu+bA==", "integrity": "sha512-vOVmaruQG5EatOU/jM6yU2uCp3Lz6mK1P5Ztu4iJjfM4SVHU9XYktPUQtKlIXuahqXHdEyUarMrBEwg5Cwu+bA=="
"dev": true
}, },
"@types/xml2js": { "@types/xml2js": {
"version": "0.4.2", "version": "0.4.2",
@ -178,6 +187,11 @@
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.27.tgz", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.27.tgz",
"integrity": "sha512-NzUKMYW4SWme+H5K+mfEmBxEF/V04PhlzoxxXwSnDig78y2t7HLBVotfDBMUhRPRA3WWID3GmJB/OJSWPhVXtg==" "integrity": "sha512-NzUKMYW4SWme+H5K+mfEmBxEF/V04PhlzoxxXwSnDig78y2t7HLBVotfDBMUhRPRA3WWID3GmJB/OJSWPhVXtg=="
}, },
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
},
"boolbase": { "boolbase": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@ -709,6 +723,11 @@
"verror": "1.10.0" "verror": "1.10.0"
} }
}, },
"lodash": {
"version": "4.17.10",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
"lodash.assignin": { "lodash.assignin": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
@ -919,6 +938,25 @@
"uuid": "3.2.1" "uuid": "3.2.1"
} }
}, },
"request-promise": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz",
"integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=",
"requires": {
"bluebird": "3.5.1",
"request-promise-core": "1.1.1",
"stealthy-require": "1.1.1",
"tough-cookie": "2.3.4"
}
},
"request-promise-core": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
"integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
"requires": {
"lodash": "4.17.10"
}
},
"resolve": { "resolve": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz",
@ -979,6 +1017,11 @@
"integrity": "sha1-kQ9dKu17Ugxud3SZwfMuE5/eyxA=", "integrity": "sha1-kQ9dKu17Ugxud3SZwfMuE5/eyxA=",
"dev": true "dev": true
}, },
"stealthy-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
},
"string_decoder": { "string_decoder": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",

View File

@ -22,15 +22,19 @@
}, },
"dependencies": { "dependencies": {
"big-integer": "^1.6.27", "big-integer": "^1.6.27",
"bluebird": "^3.5.1",
"cheerio": "^0.22.0", "cheerio": "^0.22.0",
"cloudscraper": "^1.5.0", "cloudscraper": "^1.5.0",
"commander": "^2.15.1", "commander": "^2.15.1",
"fs-extra": "^5.0.0", "fs-extra": "^5.0.0",
"mkdirp": "^0.5.0", "mkdirp": "^0.5.0",
"request": "^2.85.0", "request": "^2.85.0",
"request-promise": "^4.2.2",
"xml2js": "^0.4.5" "xml2js": "^0.4.5"
}, },
"devDependencies": { "devDependencies": {
"@types/bluebird": "^3.5.20",
"@types/request-promise": "^4.1.41",
"@types/cheerio": "^0.22.7", "@types/cheerio": "^0.22.7",
"@types/mkdirp": "^0.5.2", "@types/mkdirp": "^0.5.2",
"@types/request": "^2.47.0", "@types/request": "^2.47.0",

View File

@ -1,39 +1,84 @@
'use strict'; 'use strict';
import request = require('request');
import cheerio = require('cheerio'); import cheerio = require('cheerio');
import request = require('request');
import rp = require('request-promise');
import Promise = require('bluebird');
import log = require('./log'); import log = require('./log');
import { RequestPromise } from 'request-promise';
import { Response } from 'request';
// tslint:disable-next-line:no-var-requires
const cloudscraper = require('cloudscraper'); const cloudscraper = require('cloudscraper');
let isAuthenticated = false; let isAuthenticated = false;
let isPremium = false; let isPremium = false;
const defaultHeaders: request.Headers = const defaultHeaders: request.Headers = {
{
'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64; x64; rv:58.0) Gecko/20100101 Firefox/58.0', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64; x64; rv:58.0) Gecko/20100101 Firefox/58.0',
// Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
'Connection': 'keep-alive', 'Connection': 'keep-alive',
'Referer': 'https://www.crunchyroll.com/login' 'Referer': 'https://www.crunchyroll.com/login',
}; };
function generateDeviceId(): string {
let id = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 32; i++) {
id += possible.charAt(Math.floor(Math.random() * possible.length));
}
return id;
}
function startSession(): Promise<string> {
return rp({
method: 'GET',
url: 'CR_SESSION_URL',
qs: {
device_id: generateDeviceId(),
device_type: 'CR_DEVICE_TYPE',
access_token: 'CR_SESSION_KEY',
version: 'CR_API_VERSION',
locale: 'CR_LOCALE',
},
json: true,
})
.then((response: any) => {
return response.data.session_id;
});
}
function login(sessionId: string, user: string, pass: string): Promise<any> {
return rp({
method: 'POST',
url: 'CR_LOGIN_URL',
form: {
account: user,
password: pass,
session_id: sessionId,
version: 'CR_API_VERSION',
},
json: true,
})
.then((response) => {
if (response.error) throw new Error('Login failed: ' + response.message);
return response.data;
});
}
// TODO: logout
/** /**
* Performs a GET request for the resource. * Performs a GET request for the resource.
*/ */
export function get(config: IConfig, options: string|request.Options, done: (err: Error, result?: string) => void) export function get(config: IConfig, options: string|request.Options, done: (err: Error, result?: string) => void) {
{ authenticate(config, (err) => {
authenticate(config, err => if (err) {
{
if (err)
{
return done(err);
}
cloudscraper.request(modify(options, 'GET'), (err: Error, response: any, body: any) =>
{
if (err)
{
return done(err); return done(err);
} }
cloudscraper.request(modify(options, 'GET'), (error: Error, response: any, body: any) => {
if (error) return done(error);
done(null, typeof body === 'string' ? body : String(body)); done(null, typeof body === 'string' ? body : String(body));
}); });
}); });
@ -42,22 +87,14 @@ export function get(config: IConfig, options: string|request.Options, done: (err
/** /**
* Performs a POST request for the resource. * Performs a POST request for the resource.
*/ */
export function post(config: IConfig, options: request.Options, done: (err: Error, result?: string) => void) export function post(config: IConfig, options: request.Options, done: (err: Error, result?: string) => void) {
{ authenticate(config, (err) => {
authenticate(config, err => if (err) {
{
if (err)
{
return done(err);
}
cloudscraper.request(modify(options, 'POST'), (err: Error, response: any, body: any) =>
{
if (err)
{
return done(err); return done(err);
} }
cloudscraper.request(modify(options, 'POST'), (error: Error, response: any, body: any) => {
if (error) return done(error);
done(null, typeof body === 'string' ? body : String(body)); done(null, typeof body === 'string' ? body : String(body));
}); });
}); });
@ -66,114 +103,61 @@ export function post(config: IConfig, options: request.Options, done: (err: Erro
/** /**
* Authenticates using the configured pass and user. * Authenticates using the configured pass and user.
*/ */
function authenticate(config: IConfig, done: (err: Error) => void) function authenticate(config: IConfig, done: (err: Error) => void) {
{ if (isAuthenticated || !config.pass || !config.user) {
if (isAuthenticated || !config.pass || !config.user)
{
return done(null); return done(null);
} }
/* Bypass the login page and send a login request directly */ startSession()
let options = .then((sessionId: string) => {
{ defaultHeaders.Cookie = `sess_id=${sessionId}; c_locale=enUS`;
headers: defaultHeaders, return login(sessionId, config.user, config.pass);
jar: true, })
gzip: false, .then((userData) => {
method: 'GET', /**
url: 'https://www.crunchyroll.com/login' * The page return with a meta based redirection, as we wan't to check that everything is fine, reload
};
cloudscraper.request(options, (err: Error, rep: string, body: string) =>
{
if (err) return done(err);
const $ = cheerio.load(body);
/* Get the token from the login page */
const token = $('input[name="login_form[_token]"]').attr('value');
if (token === '')
{
return done(new Error('Can`t find token!'));
}
let options =
{
headers: defaultHeaders,
form:
{
'login_form[name]': config.user,
'login_form[password]': config.pass,
'login_form[redirect_url]': '/',
'login_form[_token]': token
},
jar: true,
gzip: false,
method: 'POST',
url: 'https://www.crunchyroll.com/login'
};
cloudscraper.request(options, (err: Error, rep: string, body: string) =>
{
if (err)
{
return done(err);
}
/* The page return with a meta based redirection, as we wan't to check that everything is fine, reload
* the main page. A bit convoluted, but more sure. * the main page. A bit convoluted, but more sure.
*/ */
let options = const options = {
{
headers: defaultHeaders, headers: defaultHeaders,
jar: true, jar: true,
url: 'http://www.crunchyroll.com/', url: 'http://www.crunchyroll.com/',
method: 'GET' method: 'GET',
}; };
cloudscraper.request(options, (err: Error, rep: string, body: string) => cloudscraper.request(options, (err: Error, rep: string, body: string) => {
{ if (err) return done(err);
if (err)
{
return done(err);
}
let $ = cheerio.load(body); const $ = cheerio.load(body);
/* Check if auth worked */ /* Check if auth worked */
const regexps = /ga\('set', 'dimension[5-8]', '([^']*)'\);/g; const regexps = /ga\('set', 'dimension[5-8]', '([^']*)'\);/g;
const dims = regexps.exec($('script').text()); const dims = regexps.exec($('script').text());
for (let i = 1; i < 5; i++) for (let i = 1; i < 5; i++) {
{ if ((dims[i] !== undefined) && (dims[i] !== '') && (dims[i] !== 'not-registered')) {
if ((dims[i] !== undefined) && (dims[i] !== '') && (dims[i] !== 'not-registered'))
{
isAuthenticated = true; isAuthenticated = true;
} }
if ((dims[i] === 'premium') || (dims[i] === 'premiumplus')) if ((dims[i] === 'premium') || (dims[i] === 'premiumplus')) {
{
isPremium = true; isPremium = true;
} }
} }
if (isAuthenticated === false) if (isAuthenticated === false) {
{
const error = $('ul.message, li.error').text(); const error = $('ul.message, li.error').text();
return done(new Error('Authentication failed: ' + error)); return done(new Error('Authentication failed: ' + error));
} }
if (isPremium === false) if (isPremium === false) {
{
log.warn('Do not use this app without a premium account.'); log.warn('Do not use this app without a premium account.');
} } else {
else
{
log.info('You have a premium account! Good!'); log.info('You have a premium account! Good!');
} }
done(null); done(null);
}); });
}); })
}); .catch(done);
} }
/** /**
@ -181,8 +165,7 @@ function authenticate(config: IConfig, done: (err: Error) => void)
*/ */
function modify(options: string|request.Options, reqMethod: string): request.Options function modify(options: string|request.Options, reqMethod: string): request.Options
{ {
if (typeof options !== 'string') if (typeof options !== 'string') {
{
options.jar = true; options.jar = true;
options.headers = defaultHeaders; options.headers = defaultHeaders;
options.method = reqMethod; options.method = reqMethod;

View File

@ -6,6 +6,9 @@
"removeComments": false, "removeComments": false,
"module": "commonjs", "module": "commonjs",
"outDir": "dist", "outDir": "dist",
"sourceMap": true "sourceMap": true,
"lib": [
"es2015"
]
} }
} }

View File

@ -64,6 +64,8 @@
"check-operator", "check-operator",
"check-separator", "check-separator",
"check-type" "check-type"
] ],
"object-literal-sort-keys": false,
"ordered-imports": false
} }
} }