my_request.ts 4.4 KB

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