'use strict'; const unprint = require('unprint'); const slugify = require('../utils/slugify'); const { convert } = require('../utils/convert'); const tryUrls = require('../utils/try-urls'); const channelCodes = { ao: 'analoverdose', bb: 'bangingbeauties', cbj: 'chocolatebjs', oo: 'oraloverdose', uha: 'upherasshole', }; const qualities = { v4k: 2160, vFullHD: 1080, vHD: 720, vSD: 480, }; const channelRegExp = new RegExp(Object.keys(channelCodes).join('|'), 'i'); function scrapeAll(scenes) { return scenes.map(({ query }) => { const release = {}; release.url = query.url('.videoPic a'); release.entryId = query.attribute('.videoPic img', 'id').match(/set-target-(\d+)/)[1]; release.title = query.content('h3 a'); release.description = query.content('.runtime + p'); release.date = query.date('.date', 'MM-DD-YYYY'); release.duration = query.duration('.runtime'); release.actors = query.all('.tour_update_models a').map((actorEl) => ({ name: unprint.query.content(actorEl), url: unprint.query.url(actorEl, null), })); release.poster = query.img('.videoPic img'); return release; }); } function getLatestUrl(channel, page) { if (channel.parameters?.siteId) { return `https://pervcity.com/search.php?site[]=${channel.parameters.siteId}&page=${page}`; } if (channel.parameters?.native) { return `${channel.url}/search.php?site[]=&page=${page}`; } return null; } async function fetchLatest(channel, page = 1) { const url = getLatestUrl(channel, page); if (url) { const res = await unprint.get(url, { selectAll: '.videoBlock' }); if (res.ok) { return scrapeAll(res.context, channel); } return res.status; } return null; } async function fetchUpcoming(channel) { const res = await unprint.get(channel.url, { selectAll: '.upcoming .videoBlock' }); if (res.ok) { return scrapeAll(res.context, channel.parameters?.native ? channel : channel.parent); } return res.status; } function scrapeScene({ query }, channel) { const release = {}; release.entryId = query.attribute('.trailerLeft img', 'id').match(/set-target-(\d+)/)[1]; release.title = query.content('.infoHeader h1'); release.description = query.content('.description'); release.duration = query.duration('.tRuntime'); release.actors = query.all('.infoBox .tour_update_models a').map((actorEl) => ({ name: unprint.query.content(actorEl), url: unprint.query.url(actorEl, null), })); release.tags = query.contents('.tagcats a'); release.qualities = query.imgs('.avaiFormate img').map((src) => qualities[src.match(/\/(\w+)\.png/)[1]]).filter(Boolean); release.poster = query.img('.posterimg'); release.photos = query.imgs('.trailerSnaps img').slice(1); // first photo is poster in lower quality const trailer = query.element('script')?.textContent.match(/\/trailers\/.+\.mp4/)?.[0]; if (trailer) { release.trailer = `${channel.url}${trailer}`; release.channel = channelCodes[release.trailer.match(channelRegExp)?.[0]]; } return release; } async function fetchScene(url, entity) { const res = await unprint.get(url, { select: '.trailerArea' }); if (res.ok) { return scrapeScene(res.context, entity); } return res.status; } function scrapeProfile({ query }, url) { const profile = { url }; const bio = query.all('.moreInfo li, .information li').reduce((acc, el) => ({ ...acc, [slugify(unprint.query.content(el, 'span'), '_')]: unprint.query.text(el), }), {}); profile.description = query.content('.aboutModel p, .modelContent p'); profile.dateOfBirth = unprint.extractDate(bio.date_of_birth, ['MMMM D, YYYY', 'DD-MMM-YY', 'MM-DD-YYYY']); profile.birthPlace = bio.birth_location; profile.ethnicity = bio.ethnicity; profile.height = convert(bio.height, 'cm'); profile.weight = convert(bio.weight, 'lb', 'kg'); profile.eyes = bio.eye_color; profile.hairColor = bio.hair_color; profile.avatar = query.img('.starPic img, .bioBPic img'); profile.releases = scrapeAll(unprint.initAll(query.all('.aboutScenes .videoBlock, .videosArea .videoBlock'))); return profile; } async function fetchProfile({ name: actorName, url: actorUrl }) { const { res, url } = await tryUrls([ actorUrl, `https://pervcity.com/models/${slugify(actorName)}.html`, `https://pervcity.com/models/${slugify(actorName, '')}.html`, ]); if (res.ok) { return scrapeProfile(res.context, url); } return res.status; } module.exports = { fetchLatest, fetchScene, fetchProfile, fetchUpcoming, };