traxxx/src/scrapers/pervcity.js

174 lines
4.4 KiB
JavaScript
Executable File

'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,
};