traxxx/src/scrapers/adultempire.js

211 lines
6.3 KiB
JavaScript
Executable File

'use strict';
const unprint = require('unprint');
const http = require('../utils/http');
const slugify = require('../utils/slugify');
const { feetInchesToCm, lbsToKg } = require('../utils/convert');
function scrapeAll(scenes, channel, _options) {
return scenes.map(({ query }) => {
const release = {};
release.url = query.url('a.scene-title, a.scene-img', { origin: channel.url });
release.entryId = query.attribute('article[data-scene-id]', 'data-scene-id') || new URL(release.url).pathname.match(/^\/(\d+)/)?.[1];
release.title = query.content('.scene-title')?.trim();
release.duration = query.duration('.scene-length');
release.actors = query.content('.scene-performer-names')?.split(/[,&]/).map((actor) => actor.trim());
release.poster = query.sourceSet('.screenshot', 'data-srcset');
const sceneId = query.attribute('article[data-scene-id]', 'data-scene-id');
const masterId = query.attribute('article[data-master-id]', 'data-master-id');
if (sceneId && masterId) {
release.teaser = `https://video.adultempire.com/hls/previewscene/${masterId}/${sceneId}/index-f1-v1.m3u8`;
}
return release;
});
}
const photoRegex = /(\/\w\/\d+\/)\d+/;
async function scrapeRelease({ query, html, element }, { url, entity, baseRelease, parameters }) {
const release = {};
const type = query.exists('.scene-list-header') ? 'movie' : 'scene';
release.entryId = new URL(url).pathname.match(/\/(\d+)/)[1];
const title = query.content('.scene-page .description, .video-page .description');
if (/^scene \d+$/i.test(title)) {
release.sceneIndex = unprint.extractNumber(title);
} else {
release.title = title;
}
release.date = query.date('.release-date:first-child', 'MMM DD, YYYY', /\w{3} \d{2}, \d{4}/);
release.duration = query.duration('.release-date:last-child');
release.actors = query.all('.video-performer').map((el) => {
const avatar = unprint.query.img(el, 'img', 'data-bgsrc');
return {
name: unprint.query.content(el, 'span').trim(),
url: unprint.query.url(el, 'a', { origin: entity.url }),
avatar: [
avatar.replace(/\/actor\/\d+/, '/actor/1600'),
avatar,
],
};
});
release.tags = query.contents('.tags a, .categories a');
release.studio = parameters?.studio === false ? null : slugify(query.content('.studio span:last-child, .studio a'), '');
if (type === 'scene') {
release.director = query.text('.director');
release.duration = query.number('.release-date:last-child') * 60;
release.poster = baseRelease?.poster || query.url('link[rel="image_src"]') || query.meta('property="og:image"');
}
if (type === 'movie') {
release.director = query.content('.director a');
release.covers = [query.sourceSet('.carousel-item .boxcover-image', 'data-srcset')];
release.scenes = scrapeAll(unprint.initAll(element, '#scenes .grid-item'), entity);
}
if (query.exists('.video-title .movie-title')) {
release.movie = {
title: query.content('#viewLargeBoxcover .modal-title a'),
url: query.url('#viewLargeBoxcover .modal-title a', 'href', { origin: entity.url }),
entryId: query.url('#viewLargeBoxcover .modal-title a')?.match(/(\d+)\//)[1],
covers: query.imgs('#viewLargeBoxcover #viewLargeBoxcoverCarousel .carousel-item > img'),
};
}
release.caps = query.imgs('#dv_frames a > img', { attribute: 'data-src' }).map((photo) => [
photo.replace(photoRegex, (match, path) => `${path}1920`),
photo.replace(photoRegex, (match, path) => `${path}1280`),
photo,
]);
const trailerId = html.match(/item: (\d+),/)?.[1];
if (trailerId) {
release.trailer = `https://trailer.adultempire.com/hls/trailer/${trailerId}/master.m3u8`;
}
if (query.exists('.user-actions .btn-4k')) {
release.qualities = [2160];
}
return release;
}
async function scrapeProfile({ query }) {
const profile = {};
const bio = query.contents('#profileModal .well li').reduce((acc, info) => {
const [key, value] = info.split(':');
return {
...acc,
[slugify(key, '_')]: value.trim(),
};
}, {});
const bioText = query.content('#profileModal .well');
profile.description = query.content('#profileModal .modal-body')
.slice(bioText.length)
.replace(/Biography Text ©Adult DVD Empire/i, '')
.trim();
profile.measurements = bio.measurements?.replace(/["\s]+/g, '');
profile.hair = bio.hair;
profile.eyes = bio.eyes;
profile.ethnicity = bio.ethnicity;
profile.height = feetInchesToCm(bio.height);
profile.weight = lbsToKg(bio.weight);
const avatar = query.img('picture img, .performer-image-container img');
if (avatar) {
profile.avatar = [
avatar
.replace('_bust', '_body')
.replace(/\/actor\/\d+\//i, '/actor/1000/'),
avatar,
];
}
return profile;
}
async function fetchLatest(channel, page, options) {
// const res = await qu.getAll(`${channel.url}/watch-newest-clips-and-scenes.html?page=${page}&hybridview=member`, '.item-grid-scene .grid-item');
const res = await unprint.get(options.parameters?.latest
? `${options.parameters.latest}?page=${page}&view=grid`
: `${channel.url}/watch-newest-clips-and-scenes.html?page=${page}&view=grid`, { selectAll: '.item-grid-scene .grid-item' });
if (res.ok) {
return scrapeAll(res.context, channel, options);
}
return res.status;
}
async function fetchProfilePage(actorUrl) {
const res = await unprint.get(actorUrl, {
select: '#content',
rejectUnauthorized: false,
});
if (res.ok) {
return scrapeProfile(res.context);
}
return res.status;
}
async function fetchProfile(baseActor, channel, include) {
if (baseActor.url) {
const profile = await fetchProfilePage(baseActor, channel, include);
if (typeof profile === 'object') {
return profile;
}
}
const searchRes = await http.get(`https://www.adultempire.com/search/SearchAutoComplete_Agg_EmpireDTRank?search_type=Pornstars&rows=9&name_startsWith=${slugify(baseActor.name, '+')}`);
if (searchRes.ok && searchRes.body.Results) {
const actorResult = searchRes.body.Results.find((result) => /performer/i.test(result.BasicResponseGroup?.displaytype) && new RegExp(baseActor.name, 'i').test(result.BasicResponseGroup?.description));
if (actorResult) {
const url = `https://www.adultempire.com/${actorResult.BasicResponseGroup.id}`;
return fetchProfilePage(url);
}
return null;
}
return searchRes.status;
}
module.exports = {
fetchLatest,
// fetchMovies,
fetchProfile,
scrapeScene: scrapeRelease,
scrapeMovie: scrapeRelease,
};