traxxx/src/scrapers/kellymadison.js

146 lines
4.8 KiB
JavaScript
Raw Normal View History

'use strict';
const bhttp = require('bhttp');
const { JSDOM } = require('jsdom');
const moment = require('moment');
const siteMapByKey = {
PF: 'pornfidelity',
TF: 'teenfidelity',
KM: 'kellymadison',
};
const siteMapBySlug = Object.entries(siteMapByKey).reduce((acc, [key, value]) => ({ ...acc, [value]: key }), {});
function extractTextNode(parentEl) {
return Array.from(parentEl).reduce((acc, el) => (el.nodeType === 3 ? `${acc}${el.textContent.trim()}` : acc), '');
}
function scrapeLatest(html, site) {
const { document } = new JSDOM(html).window;
return Array.from(document.querySelectorAll('.episode'), (scene) => {
const release = { site };
release.shootId = scene.querySelector('.card-meta .text-right').textContent.trim();
const siteId = release.shootId.match(/\w{2}/)[0];
const siteSlug = siteMapByKey[siteId];
if (site.slug !== siteSlug) {
// using generic network overview, scene is not from the site we want
return null;
}
const durationEl = scene.querySelector('.content a');
[release.entryId] = durationEl.href.match(/\d+$/);
release.url = `${site.url}/episodes/${release.entryId}`;
release.title = scene.querySelector('h5 a').textContent.trim();
const dateEl = scene.querySelector('.card-meta .text-left').childNodes;
const dateString = extractTextNode(dateEl);
release.date = moment.utc(dateString, ['MMM D', 'MMM D, YYYY']).toDate();
release.actors = Array.from(scene.querySelectorAll('.models a'), el => el.textContent);
const durationString = durationEl.textContent.match(/\d+ min/);
if (durationString) release.duration = Number(durationString[0].match(/\d+/)[0]) * 60;
release.poster = scene.querySelector('.card-img-top').src;
release.trailer = {
src: scene.querySelector('video').src,
};
return release;
}).filter(scene => scene);
}
function scrapeScene(html, url, site, shallowRelease) {
const { document } = new JSDOM(html).window;
const release = { url, site };
const titleEl = document.querySelector('.card-header.row h4').childNodes;
const titleString = extractTextNode(titleEl);
if (!shallowRelease) [release.entryId] = url.match(/\d+/);
release.title = titleString
.replace('Trailer: ', '')
.replace(/- \w+ #\d+$/, '')
.trim();
release.channel = titleString.match(/\w+ #\d+$/)[0].match(/\w+/)[0].toLowerCase();
const episode = titleString.match(/#\d+$/)[0];
const siteKey = siteMapBySlug[release.channel];
release.shootId = `${siteKey} ${episode}`;
release.description = document.querySelector('p.card-text').textContent.trim();
const dateEl = document.querySelector('.card-body h4.card-title:nth-child(3)').childNodes;
const dateString = extractTextNode(dateEl);
release.date = moment.utc(dateString, 'YYYY-MM-DD').toDate();
release.actors = Array.from(document.querySelectorAll('.card-body h4.card-title:nth-child(4) a'), el => el.textContent);
const durationRaw = document.querySelector('.card-body h4.card-title:nth-child(1)').textContent;
const durationString = durationRaw.match(/\d+:\d+/)[0];
release.duration = moment.duration(`00:${durationString}`).asSeconds();
const trailerStart = document.body.innerHTML.indexOf('player.updateSrc');
const trailerString = document.body.innerHTML.slice(trailerStart, document.body.innerHTML.indexOf(');', trailerStart));
const trailers = trailerString.match(/https:\/\/.*.mp4/g);
const resolutions = trailerString.match(/res: '\d+'/g).map((res) => {
const resolution = Number(res.match(/\d+/)[0]);
return resolution === 4000 ? 2160 : resolution; // 4k is not 4000 pixels high
});
release.trailer = trailers.map((trailer, index) => ({
src: trailer,
quality: resolutions[index],
}));
release.photos = [document.body.innerHTML
.match(/poster: .*\.jpg/)[0]
.match(/https:\/\/.*\.jpg/)[0]];
return release;
}
async function fetchLatest(site, page = 1) {
const url = `https://kellymadison.com/episodes/search?page=${page}`; // TLS issues with teenfidelity.com, same overview on all sites
const res = await bhttp.get(url, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
});
if (res.statusCode === 200 && res.body.status === 'success') {
return scrapeLatest(res.body.html, site);
}
return null;
}
async function fetchScene(url, site, shallowRelease) {
const { pathname } = new URL(url);
const res = await bhttp.get(`https://www.kellymadison.com${pathname}`, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
});
return scrapeScene(res.body.toString(), url, site, shallowRelease);
}
module.exports = {
fetchLatest,
fetchScene,
};