2019-12-10 04:10:08 +00:00
|
|
|
'use strict';
|
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
const slugify = require('../utils/slugify');
|
|
|
|
const qu = require('../utils/qu');
|
|
|
|
const http = require('../utils/http');
|
2019-12-10 21:35:00 +00:00
|
|
|
const { feetInchesToCm } = require('../utils/convert');
|
|
|
|
|
2019-12-10 04:10:08 +00:00
|
|
|
const siteMapByKey = {
|
2020-05-14 02:26:05 +00:00
|
|
|
PF: 'pornfidelity',
|
|
|
|
TF: 'teenfidelity',
|
|
|
|
KM: 'kellymadison',
|
2020-07-14 02:36:14 +00:00
|
|
|
'5KP': '5kporn',
|
|
|
|
'5KT': '5kteens',
|
2019-12-10 04:10:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const siteMapBySlug = Object.entries(siteMapByKey).reduce((acc, [key, value]) => ({ ...acc, [value]: key }), {});
|
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
function scrapeLatest(scenes, site) {
|
|
|
|
return scenes.map(({ query }) => {
|
2020-05-14 02:26:05 +00:00
|
|
|
const release = { site };
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 02:36:14 +00:00
|
|
|
release.shootId = query.q('.card-meta .text-right, .row .text-right', true);
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 02:36:14 +00:00
|
|
|
const siteId = release.shootId.match(/\d?\w{2}/)[0];
|
2020-05-14 02:26:05 +00:00
|
|
|
const siteSlug = siteMapByKey[siteId];
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-05-14 02:26:05 +00:00
|
|
|
if (site.slug !== siteSlug) {
|
|
|
|
// using generic network overview, scene is not from the site we want
|
|
|
|
return null;
|
|
|
|
}
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 02:36:14 +00:00
|
|
|
const { pathname } = new URL(query.url('h5 a, .ep-title a'));
|
2020-07-14 01:46:31 +00:00
|
|
|
[release.entryId] = pathname.match(/\d+$/);
|
2020-07-14 02:36:14 +00:00
|
|
|
release.url = `${site.url}${pathname}`;
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 02:36:14 +00:00
|
|
|
release.title = query.q('h5 a, .ep-title a', true);
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 02:36:14 +00:00
|
|
|
release.date = query.date('.card-meta .text-left, .row .col-4:first-child', ['MMM D', 'MMM D, YYYY'], /\w+ \d+(, \w+)?/);
|
|
|
|
release.actors = query.all('.models a, .ep-models a', true);
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
release.duration = query.dur('.content a');
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 02:36:14 +00:00
|
|
|
const duration = query.q('.content a, .ep-runtime strong', true).match(/(\d+) min/)[1];
|
2020-07-14 01:46:31 +00:00
|
|
|
if (duration) release.duration = Number(duration) * 60;
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 02:36:14 +00:00
|
|
|
if (query.exists('.episodes-preview')) {
|
|
|
|
[release.poster, ...release.photos] = query.imgs('.episodes-preview img');
|
|
|
|
} else {
|
|
|
|
release.poster = query.img('.card-img-top');
|
|
|
|
release.teaser = {
|
|
|
|
src: query.video('video'),
|
|
|
|
};
|
|
|
|
}
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-05-14 02:26:05 +00:00
|
|
|
return release;
|
|
|
|
}).filter(scene => scene);
|
2019-12-10 04:10:08 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
async function scrapeScene({ query, html }, url, baseRelease) {
|
|
|
|
const { pathname, origin } = new URL(url);
|
|
|
|
const release = {};
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
[release.entryId] = pathname.match(/\d+$/);
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 02:36:14 +00:00
|
|
|
const titleString = query.q('.card-header.row h4, .trailer-starring span', true);
|
2020-07-14 01:46:31 +00:00
|
|
|
const episode = titleString.match(/#\d+$/)[0];
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 02:36:14 +00:00
|
|
|
release.title = query.q('.trailer-title', true) || titleString.match(/Trailer: ([\w\s]+) -/)[1];
|
2020-07-14 01:46:31 +00:00
|
|
|
release.channel = slugify(titleString.match(/([\w\s]+) #\d+$/)[1], '');
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-05-14 02:26:05 +00:00
|
|
|
const siteKey = siteMapBySlug[release.channel];
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-05-14 02:26:05 +00:00
|
|
|
release.shootId = `${siteKey} ${episode}`;
|
2020-07-14 02:36:14 +00:00
|
|
|
release.description = query.q('p.card-text, h5.heavy + p', true);
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
// order not reliable, get keys
|
2020-07-14 02:36:14 +00:00
|
|
|
const detailElsByKey = query.all('.card-body h4.card-title, .video-summary h5').reduce((acc, rowEl) => ({
|
2020-07-14 01:46:31 +00:00
|
|
|
...acc,
|
2020-07-14 02:36:14 +00:00
|
|
|
[slugify(rowEl?.textContent.match(/(\w+):/)?.[1])]: rowEl,
|
2020-07-14 01:46:31 +00:00
|
|
|
}), {});
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
release.date = query.date(detailElsByKey.published, null, 'YYYY-MM-DD');
|
|
|
|
release.duration = query.dur(detailElsByKey.episode);
|
|
|
|
release.actors = query.all(detailElsByKey.starring, 'a', true);
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 02:36:14 +00:00
|
|
|
const posterPrefix = html.indexOf('poster:');
|
|
|
|
const poster = query.img('.trailer-poster') || html.slice(html.indexOf('http', posterPrefix), html.indexOf('.jpg', posterPrefix) + 4);
|
|
|
|
|
|
|
|
if (poster) {
|
|
|
|
if (baseRelease?.poster) {
|
|
|
|
release.photos = [poster, ...(baseRelease.photos || [])];
|
|
|
|
} else {
|
|
|
|
release.poster = poster;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
const token = query.meta('name=_token');
|
|
|
|
const trailerInfoUrl = `${origin}/episodes/trailer/sources/${release.entryId}?type=trailer`;
|
|
|
|
const trailerInfoRes = await http.post(trailerInfoUrl, null, {
|
|
|
|
'X-CSRF-Token': token,
|
|
|
|
'X-Requested-With': 'XMLHttpRequest',
|
2020-05-14 02:26:05 +00:00
|
|
|
});
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
if (trailerInfoRes.ok && trailerInfoRes.body.sources.length > 0) {
|
|
|
|
release.trailer = trailerInfoRes.body.sources.map(trailer => ({
|
|
|
|
src: trailer.src,
|
|
|
|
type: trailer.type,
|
|
|
|
quality: trailer.res.replace(4000, 2160),
|
|
|
|
}));
|
|
|
|
}
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-05-14 02:26:05 +00:00
|
|
|
return release;
|
2019-12-10 04:10:08 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
function scrapeProfile({ query }) {
|
|
|
|
const profile = {};
|
2019-12-10 21:35:00 +00:00
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
const bioKeys = query.all('table.table td:nth-child(1)', true);
|
|
|
|
const bioValues = query.all('table.table td:nth-child(2)', true);
|
|
|
|
const bio = bioKeys.reduce((acc, key, index) => ({ ...acc, [key.slice(0, -1)]: bioValues[index] }), {});
|
2019-12-10 21:35:00 +00:00
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
if (bio.Ethnicity) profile.ethnicity = bio.Ethnicity;
|
2020-05-14 02:26:05 +00:00
|
|
|
if (bio.Measurements) [profile.bust, profile.waist, profile.hip] = bio.Measurements.split('-');
|
|
|
|
if (bio.Birthplace) profile.birthPlace = bio.Birthplace;
|
2019-12-10 21:35:00 +00:00
|
|
|
|
2020-05-14 02:26:05 +00:00
|
|
|
if (bio.Height) {
|
|
|
|
const [feet, inches] = bio.Height.match(/\d+/g);
|
|
|
|
profile.height = feetInchesToCm(feet, inches);
|
|
|
|
}
|
2019-12-10 21:35:00 +00:00
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
profile.avatar = query.img('img[src*="model"]');
|
2019-12-10 21:35:00 +00:00
|
|
|
|
2020-05-14 02:26:05 +00:00
|
|
|
return profile;
|
2019-12-10 21:35:00 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 02:36:14 +00:00
|
|
|
async function fetchLatest(channel, page = 1) {
|
|
|
|
const url = `${channel.url}/episodes/search?page=${page}`; // TLS issues with teenfidelity.com, same overview on all sites
|
2020-07-14 01:46:31 +00:00
|
|
|
const res = await http.get(url, {
|
|
|
|
'X-Requested-With': 'XMLHttpRequest',
|
2020-05-14 02:26:05 +00:00
|
|
|
});
|
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
if (res.ok && res.body.status === 'success') {
|
2020-07-14 02:36:14 +00:00
|
|
|
return scrapeLatest(qu.extractAll(res.body.html, '.episode, .ep'), channel);
|
2020-05-14 02:26:05 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
return res.status;
|
2019-12-10 04:10:08 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
async function fetchScene(url, channel, baseRelease) {
|
2020-07-14 02:36:14 +00:00
|
|
|
const res = await qu.get(url, null, {
|
2020-07-14 01:46:31 +00:00
|
|
|
'X-Requested-With': 'XMLHttpRequest',
|
2020-05-14 02:26:05 +00:00
|
|
|
});
|
2019-12-10 04:10:08 +00:00
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
return res.ok ? scrapeScene(res.item, url, baseRelease) : res.status;
|
2019-12-10 04:10:08 +00:00
|
|
|
}
|
|
|
|
|
2020-07-20 23:44:51 +00:00
|
|
|
async function fetchProfile({ name: actorName }) {
|
2020-07-14 01:46:31 +00:00
|
|
|
const actorSlug = slugify(actorName);
|
|
|
|
const res = await qu.get(`https://www.kellymadison.com/models/${actorSlug}`, null, {
|
|
|
|
'X-Requested-With': 'XMLHttpRequest',
|
2020-05-14 02:26:05 +00:00
|
|
|
});
|
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
if (res.ok) {
|
|
|
|
return scrapeProfile(res.item);
|
2020-05-14 02:26:05 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 01:46:31 +00:00
|
|
|
return res.status;
|
2019-12-10 21:35:00 +00:00
|
|
|
}
|
|
|
|
|
2019-12-10 04:10:08 +00:00
|
|
|
module.exports = {
|
2020-05-14 02:26:05 +00:00
|
|
|
fetchLatest,
|
|
|
|
fetchProfile,
|
|
|
|
fetchScene,
|
2019-12-10 04:10:08 +00:00
|
|
|
};
|