UNPKG

theelitesubs

Version:

A simple wrapper to download subtitles from the yifysubtitles website.

137 lines (120 loc) 3.72 kB
const fs = require('fs'); const path = require('path'); const got = require('got'); const pMap = require('p-map'); const streamz = require('streamz'); const unzipper = require('unzipper').Parse; const srt2vtt = require('srt-to-vtt'); const cheerio = require('cheerio'); const langsFormat = require('./langs'); // Down const apiUri = 'http://api.yifysubtitles.com/subs'; const uri = 'https://www.yifysubtitles.org/movie-imdb'; const downloadUri = 'https://yifysubtitles.org'; const langK = Object.keys(langsFormat); const langV = langK.map(i => langsFormat[i]); const formatLangLong = lang => langV[langK.indexOf(lang)]; const formatLangShort = lang => langK[langV.indexOf(lang)]; // Since yifysubtitle api is not working anymore we scrape the site instead const scrape = imdbId => { return got(`${uri}/${imdbId}`) .then(res => cheerio.load(res.body)) .then($ => { return $('tbody tr') .map((i, el) => { const $el = $(el); return { rating: $el.find('.rating-cell').text(), language: $el .find('.flag-cell .sub-lang') .text() .toLowerCase(), url: $el .find('td a') .attr('href') .replace('subtitles/', 'subtitle/') + '.zip' }; }) .get(); }) .catch(e => console.error(e.message)); }; const langFilter = (subs, langs) => { const data = langs.reduce((acc, l) => { const lang = subs .filter(s => s.language === l) .sort((a, b) => b.rating - a.rating); if (lang.length > 0) { acc[l] = lang[0]; } return acc; }, {}); return data; }; const downloadFormat = format => (lang, url, link) => { let writed = ''; let fullPath = ''; return got .stream(downloadUri + url) .pipe(unzipper()) .pipe( streamz(entry => { const parsedPath = path.parse(entry.path); // Add Language to subtitle name and deete spaces const escapedLang = lang.replace('/', '-'); entry.path = entry.path .replace(/\s+/g, '.') .replace(parsedPath.ext, `_${escapedLang}_${parsedPath.ext}`); if (parsedPath.dir === '' && parsedPath.ext === '.srt') { writed = format === 'srt' ? entry.path : entry.path.replace('srt', 'vtt'); fullPath = path.join(link, writed); return format === 'srt' ? entry.pipe(fs.createWriteStream(fullPath)) : entry.pipe(srt2vtt()).pipe(fs.createWriteStream(fullPath)); } entry.autodrain(); }) ) .promise() .then(() => ({ lang, langShort: formatLangShort(lang), path: fullPath, fileName: writed })); }; const downloads = (res, opts) => { const download = downloadFormat(opts.format); const {concurrency, path} = opts; return pMap( Object.keys(res), lang => download(lang, res[lang].url, path), concurrency ); }; const runConditional = (imdbId, opts, res) => { return Promise.resolve( langFilter(res, opts.langs.map(formatLangLong)) ).then(res => downloads(res, opts)); }; const yifysubtitles = (imdbId, opts) => { opts = Object.assign( { path: __dirname, langs: ['en'], concurrency: Infinity, format: 'vtt' }, opts ); if (opts.langs.constructor !== Array) { throw new TypeError('Expected `langs` to be an array'); } else if (opts.langs.some(lang => langK.indexOf(lang) === -1)) { throw new TypeError(`Expected \`langs\` members to be in ${langK}`); } return scrape(imdbId).then(res => (res.length > 0) ? runConditional(imdbId, opts, res) : [] ); }; module.exports = yifysubtitles;