Browse Source

make login work

Roei Elisha 2 years ago
parent
commit
6ad4cbed0a
5 changed files with 173 additions and 138 deletions
  1. 51 8
      package-lock.json
  2. 4 0
      package.json
  3. 111 128
      src/my_request.ts
  4. 4 1
      tsconfig.json
  5. 3 1
      tslint.json

+ 51 - 8
package-lock.json

@@ -4,11 +4,15 @@
   "lockfileVersion": 1,
   "requires": true,
   "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": {
       "version": "0.12.1",
       "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz",
-      "integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A==",
-      "dev": true
+      "integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A=="
     },
     "@types/cheerio": {
       "version": "0.22.7",
@@ -20,7 +24,6 @@
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
       "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
-      "dev": true,
       "requires": {
         "@types/node": "8.5.2"
       }
@@ -37,14 +40,12 @@
     "@types/node": {
       "version": "8.5.2",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.2.tgz",
-      "integrity": "sha512-KA4GKOpgXnrqEH2eCVhiv2CsxgXGQJgV1X0vsGlh+WCnxbeAE1GT44ZsTU1IN5dEeV/gDupKa7gWo08V5IxWVQ==",
-      "dev": true
+      "integrity": "sha512-KA4GKOpgXnrqEH2eCVhiv2CsxgXGQJgV1X0vsGlh+WCnxbeAE1GT44ZsTU1IN5dEeV/gDupKa7gWo08V5IxWVQ=="
     },
     "@types/request": {
       "version": "2.47.0",
       "resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.0.tgz",
       "integrity": "sha512-/KXM5oev+nNCLIgBjkwbk8VqxmzI56woD4VUxn95O+YeQ8hJzcSmIZ1IN3WexiqBb6srzDo2bdMbsXxgXNkz5Q==",
-      "dev": true,
       "requires": {
         "@types/caseless": "0.12.1",
         "@types/form-data": "2.2.1",
@@ -52,11 +53,19 @@
         "@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": {
       "version": "2.3.2",
       "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.2.tgz",
-      "integrity": "sha512-vOVmaruQG5EatOU/jM6yU2uCp3Lz6mK1P5Ztu4iJjfM4SVHU9XYktPUQtKlIXuahqXHdEyUarMrBEwg5Cwu+bA==",
-      "dev": true
+      "integrity": "sha512-vOVmaruQG5EatOU/jM6yU2uCp3Lz6mK1P5Ztu4iJjfM4SVHU9XYktPUQtKlIXuahqXHdEyUarMrBEwg5Cwu+bA=="
     },
     "@types/xml2js": {
       "version": "0.4.2",
@@ -178,6 +187,11 @@
       "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.27.tgz",
       "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": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@@ -709,6 +723,11 @@
         "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": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
@@ -919,6 +938,25 @@
         "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": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz",
@@ -979,6 +1017,11 @@
       "integrity": "sha1-kQ9dKu17Ugxud3SZwfMuE5/eyxA=",
       "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": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",

+ 4 - 0
package.json

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

+ 111 - 128
src/my_request.ts

@@ -1,39 +1,84 @@
 'use strict';
-import request = require('request');
 import cheerio = require('cheerio');
+import request = require('request');
+import rp = require('request-promise');
+import Promise = require('bluebird');
 import log = require('./log');
+import { RequestPromise } from 'request-promise';
+import { Response } from 'request';
+
+// tslint:disable-next-line:no-var-requires
 const cloudscraper = require('cloudscraper');
 
 let isAuthenticated = 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',
-  //             Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
   '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.
  */
-export function get(config: IConfig, options: string|request.Options, done: (err: Error, result?: string) => void)
-{
-  authenticate(config, err =>
-  {
-    if (err)
-    {
-        return done(err);
+export function get(config: IConfig, options: string|request.Options, done: (err: Error, result?: string) => void) {
+  authenticate(config, (err) => {
+    if (err) {
+      return done(err);
     }
 
-    cloudscraper.request(modify(options, 'GET'), (err: Error, response: any, body: any) =>
-    {
-      if (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));
     });
   });
@@ -42,22 +87,14 @@ export function get(config: IConfig, options: string|request.Options, done: (err
 /**
  * Performs a POST request for the resource.
  */
-export function post(config: IConfig, options: request.Options, done: (err: Error, result?: string) => void)
-{
-  authenticate(config, err =>
-  {
-    if (err)
-    {
+export function post(config: IConfig, options: request.Options, done: (err: Error, result?: string) => void) {
+  authenticate(config, (err) => {
+    if (err) {
         return done(err);
     }
 
-    cloudscraper.request(modify(options, 'POST'), (err: Error, response: any, body: any) =>
-    {
-      if (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));
     });
   });
@@ -66,114 +103,61 @@ export function post(config: IConfig, options: request.Options, done: (err: Erro
 /**
  * Authenticates using the configured pass and user.
  */
-function authenticate(config: IConfig, done: (err: Error) => void)
-{
-  if (isAuthenticated || !config.pass || !config.user)
-  {
+function authenticate(config: IConfig, done: (err: Error) => void) {
+  if (isAuthenticated || !config.pass || !config.user) {
       return done(null);
   }
 
-  /* Bypass the login page and send a login request directly */
-  let options =
-  {
-    headers: defaultHeaders,
-    jar: true,
-    gzip: false,
-    method: 'GET',
-    url: 'https://www.crunchyroll.com/login'
-  };
-
-  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 =
-    {
+  startSession()
+  .then((sessionId: string) => {
+    defaultHeaders.Cookie = `sess_id=${sessionId}; c_locale=enUS`;
+    return login(sessionId, config.user, config.pass);
+  })
+  .then((userData) => {
+    /**
+     * 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.
+     */
+    const 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'
+      url: 'http://www.crunchyroll.com/',
+      method: 'GET',
     };
 
-    cloudscraper.request(options, (err: Error, rep: string, body: string) =>
-    {
-      if (err)
-      {
-          return done(err);
-      }
+    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.
-       */
-      let options =
-      {
-        headers: defaultHeaders,
-        jar: true,
-        url: 'http://www.crunchyroll.com/',
-        method: 'GET'
-      };
-
-      cloudscraper.request(options, (err: Error, rep: string, body: string) =>
-      {
-        if (err)
-        {
-            return done(err);
-        }
+      const $ = cheerio.load(body);
 
-        let $ = cheerio.load(body);
+      /* Check if auth worked */
+      const regexps = /ga\('set', 'dimension[5-8]', '([^']*)'\);/g;
+      const dims = regexps.exec($('script').text());
 
-        /* Check if auth worked */
-        const regexps = /ga\('set', 'dimension[5-8]', '([^']*)'\);/g;
-        const dims = regexps.exec($('script').text());
-
-        for (let i = 1; i < 5; i++)
-        {
-          if ((dims[i] !== undefined) && (dims[i] !== '') && (dims[i] !== 'not-registered'))
-          {
-              isAuthenticated = true;
-          }
-
-          if ((dims[i] === 'premium') || (dims[i] === 'premiumplus'))
-          {
-              isPremium = true;
-          }
+      for (let i = 1; i < 5; i++) {
+        if ((dims[i] !== undefined) && (dims[i] !== '') && (dims[i] !== 'not-registered')) {
+            isAuthenticated = true;
         }
 
-        if (isAuthenticated === false)
-        {
-          const error = $('ul.message, li.error').text();
-          return done(new Error('Authentication failed: ' + error));
+        if ((dims[i] === 'premium') || (dims[i] === 'premiumplus')) {
+            isPremium = true;
         }
+      }
 
-        if (isPremium === false)
-        {
-            log.warn('Do not use this app without a premium account.');
-        }
-        else
-        {
-            log.info('You have a premium account! Good!');
-        }
-        done(null);
-      });
+      if (isAuthenticated === false) {
+        const error = $('ul.message, li.error').text();
+        return done(new Error('Authentication failed: ' + error));
+      }
+
+      if (isPremium === false) {
+          log.warn('Do not use this app without a premium account.');
+      } else {
+          log.info('You have a premium account! Good!');
+      }
+      done(null);
     });
-  });
+  })
+  .catch(done);
 }
 
 /**
@@ -181,12 +165,11 @@ function authenticate(config: IConfig, done: (err: Error) => void)
  */
 function modify(options: string|request.Options, reqMethod: string): request.Options
 {
-  if (typeof options !== 'string')
-  {
+  if (typeof options !== 'string') {
     options.jar = true;
     options.headers = defaultHeaders;
     options.method = reqMethod;
     return options;
   }
   return { jar: true, headers: defaultHeaders, url: options.toString(), method: reqMethod };
-}
+}

+ 4 - 1
tsconfig.json

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

+ 3 - 1
tslint.json

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