const cheerio = require('cheerio'); const needle = require('needle'); const moment = require('moment'); const decode = require('magnet-uri'); const { defaultOptionsWithProxy } = require('../../lib/requestHelper'); const baseUrl = 'http://www.rutor.info'; const defaultTimeout = 10000; const Categories = { FOREIGN_FILMS: '1', RUSSIAN_FILMS: '5', SCIENCE_FILMS: '12', FOREIGN_SERIES: '4', RUSSIAN_SERIES: '16', RUSSIAN_TV: '6', RUSSIAN_ANIMATION: '7', ANIME: '10', FOREIGN_RELEASES: '17' }; function torrent(torrentId, config = {}, retries = 2, error = null) { if (!torrentId || retries === 0) { return Promise.reject(error || new Error(`Failed ${torrentId} search`)); } return singleRequest(`${baseUrl}/torrent/${torrentId}`) .then((body) => parseTorrentPage(body, torrentId)) .catch((err) => torrent(torrentId, config, retries - 1, err)); } function browse(config = {}, retries = 2, error = null) { if (retries === 0) { return Promise.reject(error || new Error(`Failed browse request`)); } const page = config.page || 1; const category = config.category; return singleRequest(`${baseUrl}/browse/${page - 1}/${category}/0/0`) .then((body) => parseTableBody(body)) .catch((err) => browse(config, retries - 1, err)); } function files(torrentId) { return singleRequest(`${baseUrl}/descriptions/${torrentId}.files`) .then((body) => parseFiles(body)); } function singleRequest(requestUrl) { const options = { ...defaultOptionsWithProxy(), open_timeout: defaultTimeout, follow: 2 }; return needle('get', requestUrl, options) .then((response) => { const body = response.body; if (!body) { throw new Error(`No body: ${requestUrl} with status ${response.statusCode}`); } else if (body.includes('Access Denied')) { console.log(`Access Denied: ${requestUrl}`); throw new Error(`Access Denied: ${requestUrl}`); } else if (body.includes('502: Bad gateway') || body.includes('403 Forbidden') || body.includes('Origin DNS error')) { throw new Error(`Invalid body contents: ${requestUrl}`); } return body; }); } function parseTableBody(body) { return new Promise((resolve, reject) => { const $ = cheerio.load(body); if (!$) { reject(new Error('Failed loading body')); } const torrents = $('#index').find('tr:not(.backgr)').map((i, elem) => { const row = $(elem).find('td'); const links = $(row[1]).find('a'); const peers = $(row[row.length - 1]); const magnetLink = $(links[1]).attr('href'); return { title: $(links[2]).text(), infoHash: decode(magnetLink).infoHash, magnetLink: magnetLink, torrentLink: $(links[0]).attr('href'), torrentId: $(links[2]).attr('href').match(/torrent\/(\d+)/)[1], seeds: parseInt(peers.find('.green').text()), leaches: parseInt(peers.find('.red').text()), uploadDate: parseRussianDate($(row[0]).text()), size: $(row[row.length - 2]).html().replace(' ', ' '), } }).get(); resolve(torrents); }); } function parseTorrentPage(body, torrentId) { return new Promise((resolve, reject) => { const $ = cheerio.load(body); if (!$) { reject(new Error('Failed loading body')); } const rows = $('#details > tr') const details = $(rows[0]).find('td:nth-of-type(2)'); const magnetLink = $('#download a:nth-of-type(1)').attr('href'); const imdbIdMatch = details.html().match(/imdb\.com\/title\/(tt\d+)/i); const parsedTorrent = { title: $('#all h1').first().text(), torrentId: torrentId, infoHash: decode(magnetLink).infoHash, trackers: Array.from(new Set(decode(magnetLink).tr)).join(','), magnetLink: magnetLink, torrentLink: $('#download a:nth-of-type(2)').attr('href'), seeders: parseInt($(rows[rows.length - 8]).find('td:nth-of-type(2)').first().text(), 10), category: $('tr:contains(\'Категория\') a').first().attr('href').match(/\/([\w-]+)$/)[1], languages: parseLanguages(details.text()), size: parseSize($(rows[rows.length - 4]).find('td:nth-of-type(2)').text()), uploadDate: parseDate($(rows[rows.length - 5]).find('td:nth-of-type(2)').first().text()), imdbId: imdbIdMatch && imdbIdMatch[1] }; resolve(parsedTorrent); }); } function parseFiles(body) { if (!body) { throw new Error("No files in the body"); } return body.split('\n') .map((item) => item.match(/