traxxx/src/scrapers/loveherfilms.js

155 lines
4.2 KiB
JavaScript
Executable File

'use strict';
const qu = require('../utils/q');
const slugify = require('../utils/slugify');
const { feetInchesToCm, lbsToKg, femaleFeetUsToEu } = require('../utils/convert');
const { getImageWithFallbacks } = require('./elevatedx');
function composeEntryId(release) {
return `${qu.formatDate(release.date, 'YYYY-MM-DD')}-${release.actors.map((actor) => slugify(actor)).join('-')}`;
}
function scrapeAll(scenes, channel) {
return scenes.map(({ query }) => {
const release = {};
release.url = query.url('a.item-video-overlay, a.item-episode-overlay');
release.title = query.cnt('.item-title');
release.date = query.date('.video-date', 'MMM D, YYYY');
release.duration = query.duration('.video-time');
release.actors = query.contents('.information a[href*="models/"]').map((actor) => actor.match(/[a-z ]+/i)?.[0].trim()).filter(Boolean);
release.poster = getImageWithFallbacks(query.q, 'img.mainThumb', channel);
release.teaser = query.video('img.mainThumb', 'data-vid');
release.entryId = composeEntryId(release);
return release;
});
}
function scrapeScene({ query }, url, channel) {
const release = {};
release.title = query.content('h1.title');
release.description = query.content('p.description');
release.date = query.date('.date', 'MMM D, YYYY');
release.duration = query.duration('.time');
release.actors = query.contents('.featured .model');
const poster = query.img('.video video', 'poster', { origin: channel.url });
if (poster) {
release.poster = [
poster.replace('-4x', '-2x'), // 4x appears to be upscaled beyond original resolution
poster.replace('-4x', '-1x'),
poster,
];
}
release.photos = query.imgs('.photos .thumbs[data-src]');
release.trailer = query.video('.video source');
release.tags = query.contents('.video-tags a[href*="categories/"]');
release.entryId = composeEntryId(release);
return release;
}
function scrapeProfile({ query, el }, url, entity, _include) {
const profile = { url };
const bio = Array.from(Array.from(query.html('.stats script').matchAll(/totalStats\.push\(.*\)/g))).reduce((acc, match) => {
const { query: statQuery } = qu.extract(match[0].slice(match[0].indexOf('`'), match[0].lastIndexOf('`')));
return {
...acc,
[slugify(statQuery.content('span'), '_')]: statQuery.text('p'),
};
}, {});
profile.description = query.cnt('.about p');
profile.birthPlace = bio.place_of_birth;
profile.dateOfBirth = qu.extractDate(bio.date_of_birth, ['MMMM DD, YYYY', 'MM/DD/YYYY']);
profile.ethnicity = bio.ethnicity;
profile.measurements = bio.measurements;
profile.height = feetInchesToCm(bio.height);
profile.weight = lbsToKg(bio.weight);
profile.hairColor = bio.hair_color;
profile.eyes = bio.eye_color;
profile.feet = femaleFeetUsToEu(bio.feet_size);
if (/(natural)|(real)/i.test(bio.boob_type || bio.tits_type)) {
profile.naturalBoobs = true;
}
if (/(enhanced)|(fake)/i.test(bio.boob_type || bio.tits_type)) {
profile.naturalBoobs = false;
}
if (bio.tattoos) {
profile.hasTattoos = !/none/i.test(bio.tattoos);
profile.tattoos = profile.hasTattoos ? bio.tattoos : null;
}
if (bio.piercings) {
profile.hasPiercings = !/none/i.test(bio.piercings);
profile.piercings = profile.hasPiercings ? bio.piercings : null;
}
profile.avatar = query.img('.picture img');
profile.scenes = scrapeAll(qu.initAll(el, '.scene, .latest-scene .item-episode'), entity);
console.log(bio);
console.log(profile);
return profile;
}
async function fetchLatest(channel, page = 1) {
const url = `${channel.url}/tour/categories/movies/${page}/latest/`;
const res = await qu.getAll(url, '.main .item-video');
if (res.ok) {
return scrapeAll(res.items, channel);
}
return res.status;
}
async function fetchProfile({ name: actorName }, entity, include) {
const searchRes = await qu.get(`${entity.url}/tour/search.php?model_name=${actorName}`);
if (!searchRes.ok) {
return searchRes.status;
}
const actorUrl = searchRes.item.query.url(`.item-portrait a[title="${actorName}"]`);
if (!actorUrl) {
return null;
}
const actorRes = await qu.get(actorUrl);
if (actorRes.ok) {
return scrapeProfile(actorRes.item, actorUrl, entity, include);
}
return actorRes.status;
}
module.exports = {
fetchLatest,
fetchProfile,
scrapeScene,
};