| const { Source, RunnerScraper, Hoster } = require("source-scraper-core"); | |
| const { PuppeteerRunner } = require("source-scraper-puppeteer-runner"); | |
| const cheerio = require("cheerio"); | |
| const u = require("url"); | |
| class NinexbuddySource extends Source { | |
| constructor(source) { | |
| super(source); | |
| this.quality = source.quality; | |
| } | |
| } | |
| function parseSourceQuality(raw) { | |
| const ar1 = /^([a-zA-Z\d]+)\s*-\s*(\d+)([a-zA-Z]+)?$/i.exec(raw); | |
| if (ar1 !== null && ar1.length >= 4) | |
| return { | |
| raw, | |
| description: ar1[1], | |
| value: Number(ar1[2]), | |
| unit: ar1[3] | |
| }; | |
| const ar2 = /^(\d+)([a-zA-Z]+)\s*\(?([a-zA-Z\s]+)\)?$/i.exec(raw); | |
| if (ar2 !== null && ar2.length >= 3) | |
| return { | |
| raw, | |
| description: ar2[3], | |
| value: Number(ar2[1]), | |
| unit: ar2[2] | |
| }; | |
| } | |
| function parseHosterName(raw) { | |
| const ar = /^(?:embed\s*-)?\s*([a-zA-Z\d.]+)$/i.exec(raw.trim()); | |
| if (ar === null || ar.length < 2) | |
| return; | |
| return ar[ar.length - 1]; | |
| } | |
| function isDefined(value) { | |
| return typeof value !== 'undefined'; | |
| } | |
| class NinexbuddyScraper extends RunnerScraper { | |
| constructor() { | |
| super(); | |
| this.name = 'ninexbuddy'; | |
| this.domains = []; | |
| this.urlPattern = /.*/i; | |
| this.runner = new PuppeteerRunner(); | |
| this.defaultOptions = { | |
| runnerOptions: { | |
| navigationOptions: { | |
| waitUntil: 'networkidle0' | |
| } | |
| } | |
| }; | |
| } | |
| transformUrl(url) { | |
| return `https://9xbuddy.com/process?url=${url}`; | |
| } | |
| async execWithArgs({ page, url: ninexbuddyUrl }) { | |
| const isHoster = !!(await page.$('.playlist_el').catch(() => false)); | |
| if (isHoster) { | |
| const items = await page.$$('.playlist_el .text-blue'); | |
| const hosters = (await Promise.all(items.map(async (item) => { | |
| const urlPromise = item.getProperty('href').then(async (e) => e.jsonValue()).catch(() => undefined); | |
| const namePromise = item.getProperty('textContent').then(async (e) => e.jsonValue()).catch(() => undefined); | |
| const [url, name] = await Promise.all([urlPromise, namePromise]); | |
| const hosterUrl = u.parse(url, true).query.url; | |
| if (!hosterUrl) | |
| return; | |
| const parsed = name ? parseHosterName(name) : undefined; | |
| if (!parsed) | |
| return; | |
| return new Hoster({ | |
| url: hosterUrl, | |
| name: parsed, | |
| }); | |
| }))).filter(isDefined); | |
| return { | |
| ninexbuddyUrl, | |
| hosters | |
| }; | |
| } else { | |
| const selector = '.tracking-wide.items-center.night-white'; | |
| const items = await page.$$(selector); | |
| const sources = (await Promise.all(items.map(async (item) => { | |
| const formatPromise = item.$eval('.w-24.text-blue', e => e.textContent).catch(() => undefined); | |
| const qualityPromise = item.$eval('.w-1\\/2', e => e.textContent).catch(() => undefined); | |
| const urlPromise = item.$eval('.leading-normal.sm\\:leading-none', e => e.getAttribute('href')).catch(() => undefined); | |
| const [format, quality, url] = await Promise.all([formatPromise, qualityPromise, urlPromise]); | |
| if (!url) | |
| return; | |
| return new NinexbuddySource({ | |
| url, | |
| quality: quality ? parseSourceQuality(quality) : undefined, | |
| type: format || undefined, | |
| codec: undefined | |
| }); | |
| }))).filter(isDefined); | |
| const poster = await page.$eval('img.shadow.border-2.border-grey-lighter.h-48', e => e.src).catch(() => undefined); | |
| return { | |
| ninexbuddyUrl, | |
| title: await page.title(), | |
| poster: poster && !poster.startsWith('data:image') ? poster : undefined, | |
| sources | |
| }; | |
| } | |
| } | |
| } | |
| module.exports = { | |
| NinexbuddySource, | |
| NinexbuddyScraper | |
| } |