request.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. 'use strict';
  2. import request = require('request');
  3. import cheerio = require('cheerio');
  4. import log = require('./log');
  5. var cloudscraper = require('cloudscraper');
  6. var isAuthenticated = false;
  7. var isPremium = false;
  8. var defaultHeaders: request.Headers = {
  9. 'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
  10. 'Connection': 'keep-alive'
  11. };
  12. /**
  13. * Performs a GET request for the resource.
  14. */
  15. export function get(config: IConfig, options: request.Options, done: (err: Error, result?: string) => void) {
  16. authenticate(config, err => {
  17. if (err) return done(err);
  18. cloudscraper.request(modify(options, 'GET'), (err: Error, response: any, body: any) => {
  19. if (err) return done(err);
  20. done(null, typeof body === 'string' ? body : String(body));
  21. });
  22. });
  23. }
  24. /**
  25. * Performs a POST request for the resource.
  26. */
  27. export function post(config: IConfig, options: request.Options, done: (err: Error, result?: string) => void) {
  28. authenticate(config, err => {
  29. if (err) return done(err);
  30. cloudscraper.request(modify(options, 'POST'), (err: Error, response: any, body: any) => {
  31. if (err) return done(err);
  32. done(null, typeof body === 'string' ? body : String(body));
  33. });
  34. });
  35. }
  36. /**
  37. * Authenticates using the configured pass and user.
  38. */
  39. function authenticate(config: IConfig, done: (err: Error) => void) {
  40. if (isAuthenticated || !config.pass || !config.user) return done(null);
  41. /* Bypass the login page and send a login request directly */
  42. var options =
  43. {
  44. headers: defaultHeaders,
  45. jar: true,
  46. gzip: false,
  47. method: 'GET',
  48. url: 'https://www.crunchyroll.com/login'
  49. };
  50. // request(options, (err: Error, rep: string, body: string) =>
  51. cloudscraper.request(options, (err: Error, rep: string, body: string) =>
  52. {
  53. if (err) return done(err);
  54. var $ = cheerio.load(body);
  55. /* Get the token from the login page */
  56. var token = $('input[name="login_form[_token]"]').attr('value');
  57. if (token === '') return done(new Error('Can`t find token!'));
  58. var options =
  59. {
  60. headers: defaultHeaders,
  61. form:
  62. {
  63. 'login_form[redirect_url]': '/',
  64. 'login_form[name]': config.user,
  65. 'login_form[password]': config.pass,
  66. 'login_form[_token]': token
  67. },
  68. jar: true,
  69. gzip: false,
  70. method: 'POST',
  71. url: 'https://www.crunchyroll.com/login'
  72. };
  73. // request.post(options, (err: Error, rep: string, body: string) =>
  74. cloudscraper.request(options, (err: Error, rep: string, body: string) =>
  75. {
  76. if (err) return done(err);
  77. /* The page return with a meta based redirection, as we wan't to check that everything is fine, reload
  78. * the main page. A bit convoluted, but more sure.
  79. */
  80. var options =
  81. {
  82. headers: defaultHeaders,
  83. jar: true,
  84. url: 'http://www.crunchyroll.com/',
  85. method: 'GET'
  86. };
  87. cloudscraper.request(options, (err: Error, rep: string, body: string) =>
  88. {
  89. if (err) return done(err);
  90. var $ = cheerio.load(body);
  91. /* Check if auth worked */
  92. var regexps = /ga\(\'set\', \'dimension[5-8]\', \'([^']*)\'\);/g;
  93. var dims = regexps.exec($('script').text());
  94. for (var i = 1; i < 5; i++)
  95. {
  96. if ((dims[i] !== undefined) && (dims[i] !== '') && (dims[i] !== 'not-registered')) { isAuthenticated = true; }
  97. if ((dims[i] === 'premium') || (dims[i] === 'premiumplus')) { isPremium = true; }
  98. }
  99. if (isAuthenticated === false)
  100. {
  101. var error = $('ul.message, li.error').text();
  102. return done(new Error('Authentication failed: ' + error));
  103. }
  104. if (isPremium === false) { log.warn('Do not use this app without a premium account.'); }
  105. else { log.info('You have a premium account! Good!'); }
  106. done(null);
  107. });
  108. });
  109. });
  110. }
  111. /**
  112. * Modifies the options to use the authenticated cookie jar.
  113. */
  114. function modify(options: string|request.Options, reqMethod: string): request.Options {
  115. if (typeof options !== 'string') {
  116. options.jar = true;
  117. options.headers = defaultHeaders;
  118. options.method = reqMethod;
  119. return options;
  120. }
  121. return { jar: true, headers: defaultHeaders, url: options.toString(), method: reqMethod};
  122. }