Home Manual Reference Source Test Repository

src/providers/movies/yts.js

// Import the neccesary modules.
import asyncq from "async-q";
import req from "request";
import Movie from "../../models/Movie";
import { maxWebRequest, webRequestTimeout } from "../../config/constants";
import Helper from "../helpers/moviehelper";
import Util from "../../util";

/** Class for scraping movies from https://yts.ag/. */
export default class YTS {

  /**
   * Create a yts object for movie content.
   * @param {String} name - The name of the content provider.
   */
  constructor(name) {
    /**
     * The name of the torrent provider.
     * @type {String}
     */
    this.name = name;

    /**
     * The request object with added defaults.
     * @type {Object}
     */
    this._request = req.defaults({
      "headers": {
        "Content-Type": "application/json"
      },
      "baseUrl": "https://yts.ag/api/v2/list_movies.json",
      "timeout": webRequestTimeout * 1000
    });

    /**
     * The helper object for adding movies.
     * @type {Helper}
     */
    this._helper = new Helper(this.name);

    /**
     * The util object with general functions.
     * @type {Util}
     */
    this._util = new Util();
  }

  /**
   * Get the total pages to go through.
   * @param {Boolean} [retry=true] - Retry the request.
   * @returns {Promise} - The maximum pages to go through.
   */
  _getTotalPages(retry = true) {
    const url = "list_movies.json";
    return new Promise((resolve, reject) => {
      this._request(url, (err, res, body) => {
        if (err && retry) {
          return resolve(this._getTotalPages(false));
        } else if (err) {
          return reject(`YTS: ${err} with link: 'list_movies.json'`);
        } else if (!body || res.statusCode >= 400) {
          return reject(`YTS: Could not find data on '${url}'.`);
        } else {
          body = JSON.parse(body);
          const totalPages = Math.ceil(body.data.movie_count / 50);
          return resolve(totalPages);
        }
      });
    });
  }

  /**
   * Format data from movies.
   * @param {Object} data - Data about the movies.
   * @returns {Object} - An object with the imdb id and the torrents.
   */
  _formatPage(data) {
    return asyncq.each(data, movie => {
      if (movie && movie.torrents && movie.imdb_code && movie.language.match(/english/i)) {
        const torrents = {};
        torrents["en"] = {};
        movie.torrents.forEach(torrent => {
          if (torrent.quality !== "3D") {
            torrents["en"][torrent.quality] = {
              url: `magnet:?xt=urn:btih:${torrent.hash}&tr=udp://glotorrents.pw:6969/announce&tr=udp://tracker.opentrackr.org:1337/announce&tr=udp://torrent.gresille.org:80/announce&tr=udp://tracker.openbittorrent.com:80&tr=udp://tracker.coppersurfer.tk:6969&tr=udp://tracker.leechers-paradise.org:6969&tr=udp://p4p.arenabg.ch:1337&tr=udp://tracker.internetwarriors.net:1337`,
              seed: torrent.seeds,
              peer: torrent.peers,
              size: torrent.size_bytes,
              filesize: torrent.size,
              provider: "YTS"
            };
          }
        });

        return {
          imdb_id: movie.imdb_code,
          torrents
        };
      }
    });
  }

  /**
   * Get formatted data from one page.
   * @param {Integer} page - The page to get the data from.
   * @param {Boolean} [retry=true] - Retry the function.
   * @returns {Promise} - Formatted data from one page.
   */
  _getOnePage(page, retry = true) {
    const url = `?limit=50&page=${page + 1}`;
    return new Promise((resolve, reject) => {
      this._request(url, (err, res, body) => {
        if (err && retry) {
          return resolve(this._getOnePage(page, false));
        } else if (err) {
          return reject(`YTS: ${err} with link: '?limit=50&page=${page + 1}'`);
        } else if (!body || res.statusCode >= 400) {
          return reject(`YTS: Could not find data on '${url}'.`);
        } else {
          body = JSON.parse(body);
          return resolve(this._formatPage(body.data.movies));
        }
      });
    });
  }

  /**
   * All the found movies.
   * @returns {Array} - A list of all the found movies.
   */
  async _getMovies() {
    try {
      const totalPages = await this._getTotalPages(); // Change to 'const' for production.
      if (!totalPages) return this._util.onError(`${this.name}: totalPages returned; '${totalPages}'`);
      // totalPages = 3; // For testing purposes only.
      let movies = [];
      return await asyncq.timesSeries(totalPages, async page => {
        try {
          logger.info(`${this.name}: Starting searching YTS on page ${page + 1} out of ${totalPages}`);
          const onePage = await this._getOnePage(page);
          movies = movies.concat(onePage);
        } catch (err) {
          return this._util.onError(err);
        }
      }).then(() => movies);
    } catch (err) {
      return this._util.onError(err);
    }
  }

  /**
   * Returns a list of all the inserted torrents.
   * @returns {Movie[]} - A list of scraped movies.
   */
  async search() {
    try {
      logger.info(`${this.name}: Starting scraping...`);
      const movies = await this._getMovies();
      return await asyncq.eachLimit(movies, maxWebRequest, async ytsMovie => {
        if (ytsMovie && ytsMovie.imdb_id) {
          const newMovie = await this._helper.getTraktInfo(ytsMovie.imdb_id);
          if (newMovie && newMovie._id) {
            delete ytsMovie.imdb_id;
            return await this._helper.addTorrents(newMovie, ytsMovie.torrents);
          }
        }
      });
    } catch (err) {
      return this._util.onError(err);
    }
  }

}