series.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. 'use strict';
  2. var cheerio = require('cheerio');
  3. var episode = require('./episode');
  4. var persistent = '.crpersistent';
  5. var fs = require('fs');
  6. var request = require('request');
  7. var path = require('path');
  8. var url = require('url');
  9. /**
  10. * Streams the series to disk.
  11. * @param {Object} config
  12. * @param {string} address
  13. * @param {function(Error)} done
  14. */
  15. module.exports = function (config, address, done) {
  16. var persistentPath = path.join(config.output || process.cwd(), persistent);
  17. fs.readFile(persistentPath, 'utf8', function(err, data) {
  18. var cache = config.cache ? {} : JSON.parse(data || '{}');
  19. _page(address, function(err, page) {
  20. if (err) return done(err);
  21. var i = 0;
  22. (function next() {
  23. if (i >= page.episodes.length) return done();
  24. _download(cache, config, address, page.episodes[i], function(err) {
  25. if (err) return done(err);
  26. var newCache = JSON.stringify(cache, null, ' ');
  27. fs.writeFile(persistentPath, newCache, function(err) {
  28. if (err) return done(err);
  29. i += 1;
  30. next();
  31. });
  32. });
  33. })();
  34. });
  35. });
  36. };
  37. /**
  38. * Downloads the episode.
  39. * @param {Object.<string, string>} cache
  40. * @param {Object} config
  41. * @param {string} baseAddress
  42. * @param {Object} data
  43. * @param {function(Error)} done
  44. */
  45. function _download(cache, config, baseAddress, data, done) {
  46. if (typeof config.episode === 'number') {
  47. if (config.episode > 0 && data.number < config.episode) return done();
  48. if (config.episode < 0 && -config.episode < data.number) return done();
  49. }
  50. var address = url.resolve(baseAddress, data.address);
  51. if (cache[address]) return done();
  52. episode(config, address, function(err) {
  53. if (err) return done(err);
  54. cache[address] = Date.now();
  55. done();
  56. });
  57. }
  58. /**
  59. * Requests the page data and scrapes the episodes and series.
  60. * @private
  61. * @param {string} address
  62. * @param {function(Error, Object=)} done
  63. */
  64. function _page(address, done) {
  65. request.get(address, function(err, res, body) {
  66. if (err) return done(err);
  67. var $ = cheerio.load(body);
  68. var title = $('.season-dropdown').text() || $('span[itemprop=name]').text();
  69. if (!title) return done(new Error('Invalid page.'));
  70. var episodes = [];
  71. $('.episode').each(function(i, el) {
  72. if ($(el).children('img[src*=coming_soon]').length) return;
  73. var address = $(el).attr('href');
  74. var title = ($(el).children('.series-title').text() || '').trim();
  75. var match = /([0-9]+)$/.exec(title);
  76. if (!address || !match) return;
  77. episodes.push({address: address, number: parseInt(match[0], 10)});
  78. });
  79. done(undefined, {episodes: episodes.reverse(), series: title});
  80. });
  81. }