Refactored Hush / Hussie Pass with unprint.

This commit is contained in:
DebaucheryLibrarian
2026-02-24 04:46:12 +01:00
parent 7286846308
commit d6be985c4b
2 changed files with 188 additions and 174 deletions

View File

@@ -5558,6 +5558,9 @@ const sites = [
url: 'https://seehimfuck.com', url: 'https://seehimfuck.com',
tags: ['male-focus'], tags: ['male-focus'],
parent: 'hussiepass', parent: 'hussiepass',
parameters: {
latest: 'https://seehimfuck.com',
},
}, },
{ {
slug: 'interracialpovs', slug: 'interracialpovs',
@@ -5573,8 +5576,8 @@ const sites = [
tags: ['pov'], tags: ['pov'],
parent: 'hussiepass', parent: 'hussiepass',
parameters: { parameters: {
latest: 'https://www.povpornstars.com/tour/categories/movies_%d_d.html', latest: 'https://www.povpornstars.com/tour/categories/movies_{page}_d.html',
profile: 'https://www.povpornstars.com/tour/models/%s.html', profile: 'https://www.povpornstars.com/tour/models/{actor}.html',
}, },
}, },
// HUSH PASS // HUSH PASS
@@ -5584,7 +5587,7 @@ const sites = [
url: 'https://shotherfirst.com', url: 'https://shotherfirst.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/shot-her-first_%d_d.html', latest: 'https://hushpass.com/t1/categories/shot-her-first_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5595,7 +5598,7 @@ const sites = [
url: 'https://whitezilla.com', url: 'https://whitezilla.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/whitezilla_%d_d.html', latest: 'https://hushpass.com/t1/categories/whitezilla_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5606,7 +5609,7 @@ const sites = [
url: 'https://frathousefuckfest.com', url: 'https://frathousefuckfest.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/frat-house-fuck-fest_%d_d.html', latest: 'https://hushpass.com/t1/categories/frat-house-fuck-fest_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5617,7 +5620,7 @@ const sites = [
url: 'https://freakyfirsttimers.com', url: 'https://freakyfirsttimers.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/freaky-first-timers_%d_d.html', latest: 'https://hushpass.com/t1/categories/freaky-first-timers_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5628,7 +5631,7 @@ const sites = [
url: 'https://milfinvaders.com', url: 'https://milfinvaders.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/milf-invaders_%d_d.html', latest: 'https://hushpass.com/t1/categories/milf-invaders_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5639,7 +5642,7 @@ const sites = [
url: 'https://housewivesneedcash.com', url: 'https://housewivesneedcash.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/housewives-need-cash_%d_d.html', latest: 'https://hushpass.com/t1/categories/housewives-need-cash_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5650,7 +5653,7 @@ const sites = [
url: 'https://bubblebuttbonanza.com', url: 'https://bubblebuttbonanza.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/bubble-butt-bonanza_%d_d.html', latest: 'https://hushpass.com/t1/categories/bubble-butt-bonanza_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5661,7 +5664,7 @@ const sites = [
url: 'https://suburbansexparty.com', url: 'https://suburbansexparty.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/suburban-sex-party_%d_d.html', latest: 'https://hushpass.com/t1/categories/suburban-sex-party_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5672,7 +5675,7 @@ const sites = [
url: 'https://buttnakedinthestreets.com', url: 'https://buttnakedinthestreets.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/ButtNakedInStreets_%d_d.html', latest: 'https://hushpass.com/t1/categories/ButtNakedInStreets_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
match: 'Butt Naked In Streets', match: 'Butt Naked In Streets',
t1: true, t1: true,
@@ -5684,7 +5687,7 @@ const sites = [
url: 'https://muffbumperpatrol.com', url: 'https://muffbumperpatrol.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/muff-bumper-patrol_%d_d.html', latest: 'https://hushpass.com/t1/categories/muff-bumper-patrol_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5695,7 +5698,7 @@ const sites = [
url: 'https://biggathananigga.com', url: 'https://biggathananigga.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/bigga-than-a-nigga_%d_d.html', latest: 'https://hushpass.com/t1/categories/bigga-than-a-nigga_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5706,7 +5709,7 @@ const sites = [
url: 'https://bachelorpartyfuckfest.com', url: 'https://bachelorpartyfuckfest.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/bachelor-party-fuck-fest_%d_d.html', latest: 'https://hushpass.com/t1/categories/bachelor-party-fuck-fest_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5717,7 +5720,7 @@ const sites = [
url: 'https://teencumdumpsters.com', url: 'https://teencumdumpsters.com',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/teen-cum-dumpsters_%d_d.html', latest: 'https://hushpass.com/t1/categories/teen-cum-dumpsters_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5727,7 +5730,7 @@ const sites = [
name: 'POV Hunnies', name: 'POV Hunnies',
parent: 'hushpass', parent: 'hushpass',
parameters: { parameters: {
latest: 'https://hushpass.com/t1/categories/POVHunnies_%d_d.html', latest: 'https://hushpass.com/t1/categories/POVHunnies_{page}_d.html',
media: 'https://hushpass.com', media: 'https://hushpass.com',
t1: true, t1: true,
}, },
@@ -5865,7 +5868,7 @@ const sites = [
tags: ['interracial'], tags: ['interracial'],
parent: 'interracialpass', parent: 'interracialpass',
parameters: { parameters: {
latest: 'https://www.interracialpass.com/t1/categories/2-big-to-be-true_%d_d.html', latest: 'https://www.interracialpass.com/t1/categories/2-big-to-be-true_{page}_d.html',
media: 'https://www.interracialpass.com', media: 'https://www.interracialpass.com',
t1: true, t1: true,
}, },
@@ -5877,7 +5880,7 @@ const sites = [
tags: ['interracial'], tags: ['interracial'],
parent: 'interracialpass', parent: 'interracialpass',
parameters: { parameters: {
latest: 'https://www.interracialpass.com/t1/categories/abominable-black-man_%d_d.html', latest: 'https://www.interracialpass.com/t1/categories/abominable-black-man_{page}_d.html',
media: 'https://www.interracialpass.com', media: 'https://www.interracialpass.com',
t1: true, t1: true,
}, },
@@ -5889,7 +5892,7 @@ const sites = [
parent: 'interracialpass', parent: 'interracialpass',
hasLogo: false, hasLogo: false,
parameters: { parameters: {
latest: 'https://www.interracialpass.com/t1/categories/BootyAnnihilation_%d_d.html', latest: 'https://www.interracialpass.com/t1/categories/BootyAnnihilation_{page}_d.html',
media: 'https://www.interracialpass.com', media: 'https://www.interracialpass.com',
t1: true, t1: true,
}, },
@@ -5901,7 +5904,7 @@ const sites = [
tags: ['interracial'], tags: ['interracial'],
parent: 'interracialpass', parent: 'interracialpass',
parameters: { parameters: {
latest: 'https://www.interracialpass.com/t1/categories/daddys-worst-nightmare_%d_d.html', latest: 'https://www.interracialpass.com/t1/categories/daddys-worst-nightmare_{page}_d.html',
media: 'https://www.interracialpass.com', media: 'https://www.interracialpass.com',
t1: true, t1: true,
}, },
@@ -5913,7 +5916,7 @@ const sites = [
tags: ['interracial'], tags: ['interracial'],
parent: 'interracialpass', parent: 'interracialpass',
parameters: { parameters: {
latest: 'https://www.interracialpass.com/t1/categories/monster-cock-fuck-fest_%d_d.html', latest: 'https://www.interracialpass.com/t1/categories/monster-cock-fuck-fest_{page}_d.html',
media: 'https://www.interracialpass.com', media: 'https://www.interracialpass.com',
t1: true, t1: true,
}, },
@@ -5925,7 +5928,7 @@ const sites = [
tags: ['interracial'], tags: ['interracial'],
parent: 'interracialpass', parent: 'interracialpass',
parameters: { parameters: {
latest: 'https://www.interracialpass.com/t1/categories/my-daughters-fucking-a-black-dude_%d_d.html', latest: 'https://www.interracialpass.com/t1/categories/my-daughters-fucking-a-black-dude_{page}_d.html',
media: 'https://www.interracialpass.com', media: 'https://www.interracialpass.com',
t1: true, t1: true,
}, },
@@ -5937,7 +5940,7 @@ const sites = [
tags: ['interracial'], tags: ['interracial'],
parent: 'interracialpass', parent: 'interracialpass',
parameters: { parameters: {
latest: 'https://www.interracialpass.com/t1/categories/my-moms-fucking-blackzilla_%d_d.html', latest: 'https://www.interracialpass.com/t1/categories/my-moms-fucking-blackzilla_{page}_d.html',
media: 'https://www.interracialpass.com', media: 'https://www.interracialpass.com',
t1: true, t1: true,
}, },
@@ -5949,7 +5952,7 @@ const sites = [
tags: ['interracial'], tags: ['interracial'],
parent: 'interracialpass', parent: 'interracialpass',
parameters: { parameters: {
latest: 'https://www.interracialpass.com/t1/categories/my-wifes-first-monster-cock_%d_d.html', latest: 'https://www.interracialpass.com/t1/categories/my-wifes-first-monster-cock_{page}_d.html',
media: 'https://www.interracialpass.com', media: 'https://www.interracialpass.com',
match: 'My Wifes First Monster Cock', match: 'My Wifes First Monster Cock',
t1: true, t1: true,

View File

@@ -1,20 +1,21 @@
'use strict'; 'use strict';
const util = require('util'); const unprint = require('unprint');
const format = require('template-format');
const qu = require('../utils/q');
const slugify = require('../utils/slugify'); const slugify = require('../utils/slugify');
const { feetInchesToCm, inchesToCm } = require('../utils/convert'); const tryUrls = require('../utils/try-urls');
const { convert } = require('../utils/convert');
function deriveEntryId(release) { function deriveEntryId(release) {
if (release.date && release.url) { if (release.date && release.url) {
const slug = new URL(release.url).pathname.match(/\/trailers\/(.*).html/)[1]; const slug = new URL(release.url).pathname.match(/\/trailers\/(.*).html/)[1];
return `${slugify(qu.formatDate(release.date, 'YYYY-MM-DD'))}-${slugify(slug)}`; return `${slugify(unprint.formatDate(release.date, 'YYYY-MM-DD'))}-${slugify(slug)}`;
} }
if (release.date && release.title) { if (release.date && release.title) {
return `${slugify(qu.formatDate(release.date, 'YYYY-MM-DD'))}-${slugify(release.title)}`; return `${slugify(unprint.formatDate(release.date, 'YYYY-MM-DD'))}-${slugify(release.title)}`;
} }
return null; return null;
@@ -22,7 +23,7 @@ function deriveEntryId(release) {
function extractPoster(posterPath, site, baseRelease) { function extractPoster(posterPath, site, baseRelease) {
if (posterPath && !/400.jpg/.test(posterPath)) { if (posterPath && !/400.jpg/.test(posterPath)) {
const poster = `${site.parameters?.media || site.url}${posterPath}`; const poster = unprint.prefixUrl(posterPath, site.parameters?.media || site.url);
const posterSources = [ const posterSources = [
poster, poster,
// upscaled // upscaled
@@ -40,38 +41,38 @@ function extractPoster(posterPath, site, baseRelease) {
return [baseRelease?.poster || null, []]; return [baseRelease?.poster || null, []];
} }
function getImageWithFallbacks(q, selector, site, el) { function getImageWithFallbacks(query, selector, site, el) {
const sources = el const sources = el
? [ ? [
q(el, selector, 'src0_3x'), unprint.query.attribute(el, selector, 'src0_3x'),
q(el, selector, 'src0_2x'), unprint.query.attribute(el, selector, 'src0_2x'),
q(el, selector, 'src0_1x'), unprint.query.attribute(el, selector, 'src0_1x'),
] ]
: [ : [
q(selector, 'src0_3x'), query.attribute(selector, 'src0_3x'),
q(selector, 'src0_2x'), query.attribute(selector, 'src0_2x'),
q(selector, 'src0_1x'), query.attribute(selector, 'src0_1x'),
]; ];
return sources.filter(Boolean).map((src) => `${site.parameters?.media || site.url}${src}`); return sources.filter(Boolean).map((src) => unprint.prefixUrl(src, site.parameters?.media || site.url));
} }
function scrapeAll(scenes, channel) { function scrapeAll(scenes, channel) {
return scenes.map(({ query }) => { return scenes.map(({ query }) => {
const release = {}; const release = {};
release.title = query.q('h4 a', true); release.title = query.content('h4 a');
release.url = query.url('a'); release.url = query.url('a');
release.date = query.date('.date', 'YYYY-MM-DD'); release.date = query.date('.date', 'YYYY-MM-DD');
release.duration = query.duration('.time'); release.duration = query.duration('.time');
const count = query.number('a img', null, 'cnt'); const count = query.number('a img', { attribute: 'cnt' });
[release.poster, ...release.photos] = Array.from({ length: count }, (value, index) => [ [release.poster, ...release.photos] = Array.from({ length: count }, (_value, index) => [
query.img('a img', `src${index}_3x`, { origin: channel.url }), query.img('a img', { attribute: `src${index}_3x`, origin: channel.url }),
query.img('a img', `src${index}_2x`, { origin: channel.url }), query.img('a img', { attribute: `src${index}_2x`, origin: channel.url }),
query.img('a img', `src${index}_1x`, { origin: channel.url }), query.img('a img', { attribute: `src${index}_1x`, origin: channel.url }),
]); ]);
release.stars = query.count('img[src*="star_full"]') + (query.count('img[src*="star_half"]') * 0.5); release.stars = query.count('img[src*="star_full"]') + (query.count('img[src*="star_half"]') * 0.5);
@@ -85,18 +86,18 @@ function scrapeAllT1(scenes, site, accNetworkReleases) {
return scenes.map(({ query }) => { return scenes.map(({ query }) => {
const release = {}; const release = {};
release.title = query.q('h4 a', 'title') || query.q('h4 a', true); release.title = query.attribute('h4 a', 'title') || query.content('h4 a');
release.url = query.url('h4 a'); release.url = query.url('h4 a');
release.date = query.date('.more-info-div', 'MMM D, YYYY'); release.date = query.date('.more-info-div', 'MMM D, YYYY');
release.duration = query.dur('.more-info-div'); release.duration = query.duration('.more-info-div');
if (/bts|behind the scenes/i.test(release.title)) release.tags = ['behind the scenes']; if (/bts|behind the scenes/i.test(release.title)) release.tags = ['behind the scenes'];
const posterPath = query.q('.img-div img', 'src0_1x') || query.img('img.video_placeholder'); const posterPath = query.attribute('.img-div img', 'src0_1x') || query.img('img.video_placeholder');
if (posterPath) { if (posterPath) {
const poster = /^http/.test(posterPath) ? posterPath : `${site.parameters?.media || site.url}${posterPath}`; const poster = unprint.prefixUrl(posterPath, site.parameters?.media || site.url);
release.poster = [ release.poster = [
poster.replace('-1x', '-3x'), poster.replace('-1x', '-3x'),
@@ -117,37 +118,37 @@ function scrapeAllT1(scenes, site, accNetworkReleases) {
}).filter(Boolean); }).filter(Boolean);
} }
async function fetchLatest(site, page = 1, include, { uniqueReleases = [], duplicateReleases = [] }) { async function fetchLatest(site, page = 1, _include, { uniqueReleases = [], duplicateReleases = [] }) {
const url = (site.parameters?.latest && util.format(site.parameters.latest, page)) const url = (site.parameters?.latest && format(site.parameters.latest, { page }))
|| (site.parameters?.t1 && `${site.url}/t1/categories/movies_${page}_d.html`) || (site.parameters?.t1 && `${site.url}/t1/categories/movies_${page}_d.html`)
|| `${site.url}/categories/movies_${page}_d.html`; || `${site.url}/categories/movies_${page}_d.html`;
const res = await qu.getAll(url, '.modelfeature, .item-video, .updateItem'); const res = await unprint.get(url, { selectAll: '.modelfeature, .item-video, .updateItem' });
if (!res.ok) { if (!res.ok) {
return res.status; return res.status;
} }
if (site.parameters?.t1) { if (site.parameters?.t1) {
return scrapeAllT1(res.items, site, [...uniqueReleases, ...duplicateReleases]); return scrapeAllT1(res.context, site, [...uniqueReleases, ...duplicateReleases]);
} }
return scrapeAll(res.items, site, uniqueReleases); return scrapeAll(res.context, site, uniqueReleases);
} }
function scrapeScene({ html, query }, channel, url) { function scrapeScene({ html, query }, channel, url) {
const release = { url }; // url used for entry ID const release = { url }; // url used for entry ID
release.title = query.cnt('.videoDetails h3'); release.title = query.content('.videoDetails h3');
release.description = query.cnt('.videoDetails p'); release.description = query.content('.videoDetails p');
release.date = query.date('.videoInfo p', ['MM/DD/YYYY', 'YYYY-MM-DD']); release.date = query.date('.videoInfo p', ['MM/DD/YYYY', 'YYYY-MM-DD']);
release.duration = Number(query.cnt('.videoInfo p:nth-of-type(2)')?.match(/(\d+) min/i)?.[1]) * 60; release.duration = Number(query.content('.videoInfo p:nth-of-type(2)')?.match(/(\d+) min/i)?.[1]) * 60;
release.actors = query.cnts('.update_models a'); release.actors = query.contents('.update_models a');
const posterPath = html.match(/poster="([\w-/.]+)"/)?.[1]; const posterPath = html.match(/poster="([\w-/.]+)"/)?.[1];
const poster = qu.prefixUrl(posterPath, channel.url) || query.img('.update_thumb', 'src0_1x', { origin: channel.url }); // latter used when trailer requires signup const poster = unprint.prefixUrl(posterPath, channel.url) || query.img('.update_thumb', 'src0_1x', { origin: channel.url }); // latter used when trailer requires signup
[release.poster, ...release.photos] = [poster, ...query.imgs('.item-thumb img', 'src0_1x', { origin: channel.url })] [release.poster, ...release.photos] = [poster, ...query.imgs('.item-thumb img', 'src0_1x', { origin: channel.url })]
.map((src) => src && [ .map((src) => src && [
@@ -159,10 +160,10 @@ function scrapeScene({ html, query }, channel, url) {
const trailerPath = html.match(/\/trailers?\/.*.mp4/); const trailerPath = html.match(/\/trailers?\/.*.mp4/);
if (trailerPath) { if (trailerPath) {
release.trailer = qu.prefixUrl(trailerPath, channel.parameters?.media || channel.url); release.trailer = unprint.prefixUrl(trailerPath, channel.parameters?.media || channel.url);
} }
release.tags = query.cnts('.featuring a[href*="categories/"]'); release.tags = query.contents('.featuring a[href*="categories/"]');
release.stars = query.count('.stars img[src*="star_full"]') + (query.count('.stars img[src*="star_half"]') * 0.5); release.stars = query.count('.stars img[src*="star_full"]') + (query.count('.stars img[src*="star_half"]') * 0.5);
release.entryId = deriveEntryId(release); release.entryId = deriveEntryId(release);
@@ -173,29 +174,33 @@ function scrapeScene({ html, query }, channel, url) {
function scrapeSceneT1({ html, query }, site, url, baseRelease) { function scrapeSceneT1({ html, query }, site, url, baseRelease) {
const release = { url }; const release = { url };
release.title = query.q('.trailer-section-head .section-title', true); release.title = query.content('.trailer-section-head .section-title');
release.description = query.text('.row .update-info-block'); release.description = query.text('.row .update-info-block');
release.date = query.date('.update-info-row', 'MMM D, YYYY', /\w+ \d{1,2}, \d{4}/); release.date = query.date('.update-info-row', 'MMM D, YYYY', /\w+ \d{1,2}, \d{4}/);
release.duration = query.dur('.update-info-row:nth-child(2)'); release.duration = query.duration('.update-info-row:nth-child(2)');
release.actors = query.all('.models-list-thumbs a').map((el) => ({ release.actors = query.all('.models-list-thumbs a').map((el) => ({
name: query.q(el, 'span', true), name: unprint.query.content(el, 'span'),
avatar: getImageWithFallbacks(query.q, 'img', site, el), avatar: getImageWithFallbacks(query, 'img', site, el),
})); }));
release.tags = query.all('.tags a', true); release.tags = query.contents('.tags a');
// const posterPath = html.match(/poster="(.*\.jpg)/)?.[1]; // const posterPath = html.match(/poster="(.*\.jpg)/)?.[1];
const posterPath = query.q('.player-thumb img', 'src0_1x'); const posterPath = query.img('.player-thumb img', { attribute: 'src0_1x' });
const trailer = html.match(/<video.*src="(.*\.mp4)/)?.[1];
[release.poster, release.photos] = extractPoster(posterPath, site, baseRelease); [release.poster, release.photos] = extractPoster(posterPath, site, baseRelease);
const trailer = html.match(/<video.*src="(.*\.mp4)/)?.[1]; if (trailer) {
if (trailer && /^http/.test(trailer)) release.trailer = { src: trailer, referer: url }; release.trailer = {
else if (trailer) release.trailer = { src: `${site.parameters?.media || site.url}${trailer}`, referer: url }; src: unprint.prefixUrl(trailer, site.parameters?.media || site.url),
referer: url,
};
}
const stars = query.q('.update-rating', true).match(/\d.\d/)?.[0]; release.stars = query.number('.update-rating');
if (stars) release.stars = Number(stars);
if (site.type === 'network') { if (site.type === 'network') {
const channelRegExp = new RegExp(site.children.map((channel) => channel.parameters?.match || channel.name).join('|'), 'i'); const channelRegExp = new RegExp(site.children.map((channel) => channel.parameters?.match || channel.name).join('|'), 'i');
@@ -206,16 +211,99 @@ function scrapeSceneT1({ html, query }, site, url, baseRelease) {
} }
} }
// release.entryId = q('.player-thumb img', 'id')?.match(/set-target-(\d+)/)[1];
release.entryId = deriveEntryId(release); release.entryId = deriveEntryId(release);
return release; return release;
} }
function scrapeProfileT1({ el, query }, site) { async function fetchScene(url, site, baseRelease) {
const profile = {}; const res = await unprint.get(url);
const bio = query.all('.detail-div + .detail-div p, .detail-div p', true).reduce((acc, info) => { if (!res.ok) {
return res.status;
}
if (site.parameters?.t1) {
return scrapeSceneT1(res.context, site, url, baseRelease);
}
return scrapeScene(res.context, site, url, baseRelease);
}
async function fetchActorScenes({ query }, channel, accScenes = []) {
const scenes = scrapeAll(unprint.initAll(query.all('.item-video')), channel);
const nextPage = query.url('.next a');
if (nextPage) {
const res = await unprint.get(nextPage);
if (res.ok) {
return fetchActorScenes(res.context, channel, scenes.concat(accScenes));
}
}
return accScenes.concat(scenes);
}
async function scrapeProfile({ query }, url, channel, options) {
const profile = { url };
const bio = query.all('.stats li').reduce((acc, bioEl) => {
const key = unprint.query.content(bioEl, 'strong');
const value = unprint.query.url(bioEl, null) || unprint.query.text(bioEl);
return {
...acc,
[slugify(key, '_')]: value,
};
}, {});
if (bio.date_of_birth) profile.dateOfBirth = unprint.extractDate(bio.date_of_birth, 'MMMM D, YYYY');
if (bio.birthplace) profile.birthPlace = bio.birthplace;
if (bio.fun_fact) profile.description = bio.fun_fact;
if (bio.ethnicity) profile.ethnicity = bio.ethnicity;
if (bio.height) profile.height = Number(bio.height.match(/^\d{2,3}/)?.[0]);
if (bio.weight) profile.weight = Number(bio.weight.match(/^\d{2,3}/)?.[0]);
if (bio.shoe_size) profile.foot = Number(bio.shoe_size);
profile.measurements = bio.measurements;
if (bio.penis_length) profile.penisLength = Number(bio.penis_length.match(/(\d+)\s*cm/i)?.[1] || convert(bio.penis_length.match(/(\d+\.?\d+)\s*in/i)?.[1], 'cm')) || null;
if (bio.penis_girth) profile.penisGirth = Number(bio.penis_girth.match(/(\d+)\s*cm/i)?.[1] || convert(bio.penis_girth.match(/(\d+\.?\d+)\s*in/i)?.[1], 'cm')) || null;
if (bio.circumcised && /yes/i.test(bio.circumcised)) profile.isCircumcised = true;
if (bio.circumcised && /no/i.test(bio.circumcised)) profile.isCircumcised = false;
if (bio.natural_breasts && /yes/i.test(bio.natural_breasts)) profile.naturalBoobs = true;
if (bio.natural_breasts && /no/i.test(bio.natural_breasts)) profile.naturalBoobs = false;
if (bio.tattoos && /(yes)|(some)|(many)/i.test(bio.tattoos)) profile.hasTattoos = true;
if (bio.tattoos && /no/i.test(bio.tattoos)) profile.hasTattoos = false;
if (bio.piercings && /(yes)|(some)|(many)/i.test(bio.piercings)) profile.hasPiercings = true;
if (bio.piercings && /no/i.test(bio.piercings)) profile.hasPiercings = false;
if (bio.aliases) profile.aliases = bio.aliases.split(',').map((alias) => alias.trim());
profile.socials = [bio.onlyfans, bio.twitter, bio.instagram, bio.domain].filter(Boolean);
profile.avatar = [
query.img('.profile-pic img', { attribute: 'src0_3x', origin: channel.url }),
query.img('.profile-pic img', { attribute: 'src0_2x', origin: channel.url }),
query.img('.profile-pic img', { attribute: 'src0_1x', origin: channel.url }),
];
if (options.includeActorScenes) {
profile.releases = await fetchActorScenes({ query }, channel);
}
return profile;
}
function scrapeProfileT1({ query }, url, site) {
const profile = { url };
const bio = query.contents('.detail-div + .detail-div p, .detail-div p').reduce((acc, info) => {
const [key, value] = info.split(':'); const [key, value] = info.split(':');
if (!value) return acc; if (!value) return acc;
@@ -233,125 +321,48 @@ function scrapeProfileT1({ el, query }, site) {
const heightMetric = bio.height?.match(/(\d{3})(\b|c)/); const heightMetric = bio.height?.match(/(\d{3})(\b|c)/);
const heightImperial = bio.height?.match(/\d{1}(\.\d)?/g); const heightImperial = bio.height?.match(/\d{1}(\.\d)?/g);
if (heightMetric) profile.height = Number(heightMetric[1]);
if (heightImperial) profile.height = feetInchesToCm(Number(heightImperial[0]), Number(heightImperial[1]));
profile.avatar = getImageWithFallbacks(query.q, '.img-div img', site); if (heightMetric) {
profile.height = Number(heightMetric[1]);
}
const qReleases = qu.initAll(el, '.item-video'); if (heightImperial) {
profile.height = convert(`${heightImperial[0]}' ${heightImperial[1]}"`, 'cm');
}
profile.avatar = getImageWithFallbacks(query, '.img-div img', site);
const qReleases = unprint.initAll(query.all('.item-video'));
profile.releases = scrapeAllT1(qReleases, site); profile.releases = scrapeAllT1(qReleases, site);
return profile; return profile;
} }
async function fetchScene(url, site, baseRelease) {
const res = await qu.get(url);
if (!res.ok) {
return res.status;
}
if (site.parameters?.t1) {
return scrapeSceneT1(res.item, site, url, baseRelease);
}
return scrapeScene(res.item, site, url, baseRelease);
}
async function fetchActorScenes({ query, el }, channel, accScenes = []) {
const scenes = scrapeAll(qu.initAll(el, '.item-video'), channel);
const nextPage = query.url('.next a');
if (nextPage) {
const res = await qu.get(nextPage);
if (res.ok) {
return fetchActorScenes(res.item, channel, scenes.concat(accScenes));
}
}
return accScenes.concat(scenes);
}
async function scrapeProfile({ query, el }, channel, options) {
const profile = {};
const bio = query.all('.stats li').reduce((acc, bioEl) => {
const key = query.cnt(bioEl, 'strong');
const value = query.url(bioEl) || query.text(bioEl);
return {
...acc,
[slugify(key, '_')]: value,
};
}, {});
if (bio.date_of_birth) profile.dateOfBirth = qu.extractDate(bio.date_of_birth, 'MMMM D, YYYY');
if (bio.birthplace) profile.birthPlace = bio.birthplace;
if (bio.fun_fact) profile.description = bio.fun_fact;
if (bio.ethnicity) profile.ethnicity = bio.ethnicity;
if (bio.height) profile.height = Number(bio.height.match(/^\d{2,3}/)?.[0]);
if (bio.weight) profile.weight = Number(bio.weight.match(/^\d{2,3}/)?.[0]);
if (bio.shoe_size) profile.foot = Number(bio.shoe_size);
profile.measurements = bio.measurements;
if (bio.penis_length) profile.penisLength = Number(bio.penis_length.match(/(\d+)\s*cm/i)?.[1] || inchesToCm(bio.penis_length.match(/(\d+\.?\d+)\s*in/i)?.[1])) || null;
if (bio.penis_girth) profile.penisGirth = Number(bio.penis_girth.match(/(\d+)\s*cm/i)?.[1] || inchesToCm(bio.penis_girth.match(/(\d+\.?\d+)\s*in/i)?.[1])) || null;
if (bio.circumcised && /yes/i.test(bio.circumcised)) profile.isCircumcised = true;
if (bio.circumcised && /no/i.test(bio.circumcised)) profile.isCircumcised = false;
if (bio.natural_breasts && /yes/i.test(bio.natural_breasts)) profile.naturalBoobs = true;
if (bio.natural_breasts && /no/i.test(bio.natural_breasts)) profile.naturalBoobs = false;
if (bio.tattoos && /(yes)|(some)|(many)/i.test(bio.tattoos)) profile.hasTattoos = true;
if (bio.tattoos && /no/i.test(bio.tattoos)) profile.hasTattoos = false;
if (bio.piercings && /(yes)|(some)|(many)/i.test(bio.piercings)) profile.hasPiercings = true;
if (bio.piercings && /no/i.test(bio.piercings)) profile.hasPiercings = false;
if (bio.aliases) profile.aliases = bio.aliases.split(',').map((alias) => alias.trim());
profile.socials = [bio.onlyfans, bio.twitter, bio.instagram, bio.domain].filter(Boolean);
profile.avatar = [
query.img('.profile-pic img', 'src0_3x', { origin: channel.url }),
query.img('.profile-pic img', 'src0_2x', { origin: channel.url }),
query.img('.profile-pic img', 'src0_1x', { origin: channel.url }),
];
if (options.includeActorScenes) {
profile.releases = await fetchActorScenes({ query, el }, channel);
}
return profile;
}
async function fetchProfile({ name: actorName }, { channel }, options) { async function fetchProfile({ name: actorName }, { channel }, options) {
const actorSlugA = slugify(actorName, ''); const actorSlugA = slugify(actorName, '', { lower: false });
const actorSlugB = slugify(actorName); const actorSlugB = slugify(actorName);
const t1 = channel.parameters?.t1 ? 't1/' : ''; const t1 = channel.parameters?.t1 ? 't1/' : '';
// follow redirects because a lot of profiles redirect from lowercase to uppercase or vice versa const { res, url } = channel.parameters?.profile
const res1 = channel.parameters?.profile ? await tryUrls([
? await qu.get(util.format(channel.parameters.profile, actorSlugA)) format(channel.parameters.profile, { actor: actorSlugA }),
: await qu.get(`${channel.url}/${t1}models/${actorSlugA}.html`, null, null, { followRedirects: true }); format(channel.parameters.profile, { actor: actorSlugB }),
], { followRedirects: false })
const res = (res1.ok && res1) : await tryUrls([
|| (channel.parameters?.profile && await qu.get(util.format(channel.parameters.profile, actorSlugB))) `${channel.url}/${t1}models/${actorSlugA}.html`,
|| await qu.get(`${channel.url}/${t1}models/${actorSlugB}.html`, null, null, { followRedirects: true }); `${channel.url}/${t1}models/${actorSlugB}.html`,
], { followRedirects: false });
if (!res.ok) { if (!res.ok) {
return res.status; return res.status;
} }
if (channel.parameters?.t1) { if (channel.parameters?.t1) {
return scrapeProfileT1(res.item, channel); return scrapeProfileT1(res.context, url, channel);
} }
return scrapeProfile(res.item, channel, options); return scrapeProfile(res.context, url, channel, options);
} }
module.exports = { module.exports = {