'use strict'; const bhttp = require('bhttp'); const cheerio = require('cheerio'); const moment = require('moment'); const knex = require('../knex'); const { matchTags } = require('../tags'); function scrape(html, site) { const $ = cheerio.load(html, { normalizeWhitespace: true }); const scenesElements = $('li[data-itemtype=scene]').toArray(); return scenesElements.map((element) => { const sceneLinkElement = $(element).find('.sceneTitle a'); const url = `${site.url}${sceneLinkElement.attr('href')}`; const title = sceneLinkElement.attr('title'); const entryId = $(element).attr('data-itemid'); const date = moment .utc($(element).find('.sceneDate').text(), 'MM-DD-YYYY') .toDate(); const actors = $(element).find('.sceneActors a') .map((actorIndex, actorElement) => $(actorElement).attr('title')) .toArray(); const [likes, dislikes] = $(element).find('.value') .toArray() .map(value => Number($(value).text())); return { url, entryId, title, actors, director: 'Mason', date, rating: { likes, dislikes, }, site, }; }); } async function scrapeScene(html, url, site) { const $ = cheerio.load(html, { normalizeWhitespace: true }); const json = $('script[type="application/ld+json"]').html(); const data = JSON.parse(json)[0]; const entryId = new URL(url).pathname.split('/').slice(-1)[0]; const title = $('meta[name="twitter:title"]').attr('content'); const date = moment.utc(data.dateCreated, 'YYYY-MM-DD').toDate(); const actors = data.actor .sort(({ gender: genderA }, { gender: genderB }) => { if (genderA === 'female' && genderB === 'male') return -1; if (genderA === 'male' && genderB === 'female') return 1; return 0; }) .map(actor => actor.name); const description = data.description || undefined; const stars = (data.aggregateRating.ratingValue / data.aggregateRating.bestRating) * 5; const duration = moment.duration(data.duration.slice(2).split(':')).asSeconds(); const rawTags = data.keywords.split(', '); const siteDomain = $('meta[name="twitter:domain"]').attr('content'); const siteId = siteDomain && siteDomain.split('.')[0].toLowerCase(); const siteUrl = siteDomain && `https://www.${siteDomain}`; const [channelSite, tags] = await Promise.all([ site.isFallback ? knex('sites') .where({ url: siteUrl }) .orWhere({ id: siteId }) .first() : site, matchTags(rawTags), ]); return { url: channelSite ? `${channelSite.url}/en/video/${new URL(url).pathname.split('/').slice(-2).join('/')}` : url, entryId, title, date, actors, director: 'Mason', description, duration, tags, rating: { stars, }, site: channelSite || site, }; } async function fetchLatest(site, page = 1) { const res = await bhttp.get(`${site.url}/en/videos/AllCategories/0/${page}`); return scrape(res.body.toString(), site); } async function fetchUpcoming(site) { const res = await bhttp.get(`${site.url}/en/videos/AllCategories/0/1/upcoming`); return scrape(res.body.toString(), site); } async function fetchScene(url, site) { const res = await bhttp.get(url); return scrapeScene(res.body.toString(), url, site); } module.exports = { fetchLatest, fetchUpcoming, fetchScene, };