traxxx/src/scrapers/nubiles.js

164 lines
4.7 KiB
JavaScript

'use strict';
const qu = require('../utils/qu');
const slugify = require('../utils/slugify');
const { heightToCm } = require('../utils/convert');
const slugUrlMap = {
nubiles: 'https://www.nubiles.net',
nubilesporn: 'https://www.nubiles-porn.com',
};
async function getPhotos(albumUrl) {
const res = await qu.getAll(albumUrl, '.photo-thumb');
return res.ok
? res.items.map(({ query }) => query.q('source').srcset)
: [];
}
function scrapeAll(scenes, site, origin) {
return scenes.map(({ query }) => {
const release = {};
release.title = query.q('.title a', true);
const url = query.url('.title a').split('?')[0];
const channelUrl = query.url('.site-link');
if (/^http/.test(url)) {
const { pathname } = new URL(url);
// release.entryId = pathname.split('/')[3];
if (channelUrl) release.url = `${channelUrl}${pathname}`;
else release.url = url;
} else if (!/\/join/.test(url)) {
// release.entryId = url.split('/')[3];
if (channelUrl) release.url = `${channelUrl}${url}`;
else if (site?.url) release.url = `${site.url}${url}`;
else if (origin) release.url = `${origin}${url}`;
} else {
// release.entryId = qu.q('a img', 'tube_tour_thumb_id');
}
release.date = query.date('.date', 'MMM D, YYYY');
release.actors = query.all('.models a.model', true);
// no reliable entry ID between upcoming and released scenes
release.entryId = `${qu.formatDate(release.date, 'YYYY-MM-DD')}-${slugify(release.title)}`;
const poster = query.sourceSet('img', 'data-srcset')?.[0];
release.poster = poster && [
poster.replace('_640', '_1280'),
poster,
];
release.stars = query.number('.rating');
release.likes = query.number('.likes');
return release;
});
}
async function scrapeScene({ query }, url, site) {
const release = {};
const { origin, pathname } = new URL(url);
release.url = `${origin}${pathname}`;
release.entryId = new URL(url).pathname.split('/')[3];
release.title = query.q('.content-pane-title h2', true);
release.description = query.q('.content-pane-column div', true);
release.date = query.date('.date', 'MMM D, YYYY');
release.actors = query.all('.content-pane-performers .model', true);
release.tags = query.all('.categories a', true);
release.poster = query.poster() || query.img('.fake-video-player img');
release.trailer = query.all('source').map((source) => ({
src: source.src,
quality: Number(source.getAttribute('res')),
}));
release.stars = Number(query.q('.score', true));
release.likes = Number(query.q('#likecount', true));
const albumLink = query.url('.content-pane-related-links a[href*="gallery"]');
if (albumLink) release.photos = await getPhotos(`${site.url}${albumLink}`);
return release;
}
function scrapeProfile({ query }, _actorName, origin) {
const profile = {};
const keys = query.all('.model-profile h5', true);
const values = query.all('.model-profile h5 + p', true);
const bio = keys.reduce((acc, key, index) => ({ ...acc, [slugify(key, '_')]: values[index] }), {});
profile.age = Number(bio.age);
profile.description = query.q('.model-bio', true);
profile.residencePlace = bio.location;
profile.height = heightToCm(bio.height);
[profile.bust, profile.waist, profile.hip] = bio.figure.split('-').map((v) => Number(v) || v);
profile.avatar = query.img('.model-profile img');
const releases = query.all('.content-grid-item').filter((el) => /video\//.test(query.url(el, '.img-wrapper a'))); // filter out photos
profile.releases = scrapeAll(query.initAll(releases), null, origin);
return profile;
}
async function fetchLatest(site, page = 1) {
const url = `${site.url}/video/gallery/${(page - 1) * 12}`;
const res = await qu.getAll(url, '.content-grid-item');
return res.ok ? scrapeAll(res.items, site) : res.status;
}
async function fetchUpcoming(site) {
if (site.parameters?.upcoming) {
const url = `${site.url}/video/upcoming`;
const res = await qu.getAll(url, '.content-grid-item');
return res.ok ? scrapeAll(res.items, site) : res.status;
}
return [];
}
async function fetchProfile({ name: actorName }, { site }) {
const firstLetter = actorName.charAt(0).toLowerCase();
const origin = slugUrlMap[site.slug] || site.url;
const url = `${origin}/model/alpha/${firstLetter}`;
const resModels = await qu.get(url);
if (!resModels.ok) return resModels.status;
const modelPath = resModels.item.qu.all('.content-grid-item a.title').find((el) => slugify(el.textContent) === slugify(actorName));
if (modelPath) {
const modelUrl = `${origin}${modelPath}`;
const resModel = await qu.get(modelUrl);
return resModel.ok ? scrapeProfile(resModel.item, actorName, origin) : resModel.status;
}
return null;
}
module.exports = {
fetchLatest,
fetchUpcoming,
fetchProfile,
scrapeScene,
};