diff --git a/seeds/02_sites.js b/seeds/02_sites.js index 00a60e65..c698ab8c 100755 --- a/seeds/02_sites.js +++ b/seeds/02_sites.js @@ -5558,6 +5558,9 @@ const sites = [ url: 'https://seehimfuck.com', tags: ['male-focus'], parent: 'hussiepass', + parameters: { + latest: 'https://seehimfuck.com', + }, }, { slug: 'interracialpovs', @@ -5573,8 +5576,8 @@ const sites = [ tags: ['pov'], parent: 'hussiepass', parameters: { - latest: 'https://www.povpornstars.com/tour/categories/movies_%d_d.html', - profile: 'https://www.povpornstars.com/tour/models/%s.html', + latest: 'https://www.povpornstars.com/tour/categories/movies_{page}_d.html', + profile: 'https://www.povpornstars.com/tour/models/{actor}.html', }, }, // HUSH PASS @@ -5584,7 +5587,7 @@ const sites = [ url: 'https://shotherfirst.com', parent: 'hushpass', 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', t1: true, }, @@ -5595,7 +5598,7 @@ const sites = [ url: 'https://whitezilla.com', parent: 'hushpass', 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', t1: true, }, @@ -5606,7 +5609,7 @@ const sites = [ url: 'https://frathousefuckfest.com', parent: 'hushpass', 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', t1: true, }, @@ -5617,7 +5620,7 @@ const sites = [ url: 'https://freakyfirsttimers.com', parent: 'hushpass', 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', t1: true, }, @@ -5628,7 +5631,7 @@ const sites = [ url: 'https://milfinvaders.com', parent: 'hushpass', 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', t1: true, }, @@ -5639,7 +5642,7 @@ const sites = [ url: 'https://housewivesneedcash.com', parent: 'hushpass', 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', t1: true, }, @@ -5650,7 +5653,7 @@ const sites = [ url: 'https://bubblebuttbonanza.com', parent: 'hushpass', 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', t1: true, }, @@ -5661,7 +5664,7 @@ const sites = [ url: 'https://suburbansexparty.com', parent: 'hushpass', 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', t1: true, }, @@ -5672,7 +5675,7 @@ const sites = [ url: 'https://buttnakedinthestreets.com', parent: 'hushpass', 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', match: 'Butt Naked In Streets', t1: true, @@ -5684,7 +5687,7 @@ const sites = [ url: 'https://muffbumperpatrol.com', parent: 'hushpass', 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', t1: true, }, @@ -5695,7 +5698,7 @@ const sites = [ url: 'https://biggathananigga.com', parent: 'hushpass', 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', t1: true, }, @@ -5706,7 +5709,7 @@ const sites = [ url: 'https://bachelorpartyfuckfest.com', parent: 'hushpass', 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', t1: true, }, @@ -5717,7 +5720,7 @@ const sites = [ url: 'https://teencumdumpsters.com', parent: 'hushpass', 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', t1: true, }, @@ -5727,7 +5730,7 @@ const sites = [ name: 'POV Hunnies', parent: 'hushpass', 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', t1: true, }, @@ -5865,7 +5868,7 @@ const sites = [ tags: ['interracial'], parent: 'interracialpass', 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', t1: true, }, @@ -5877,7 +5880,7 @@ const sites = [ tags: ['interracial'], parent: 'interracialpass', 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', t1: true, }, @@ -5889,7 +5892,7 @@ const sites = [ parent: 'interracialpass', hasLogo: false, 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', t1: true, }, @@ -5901,7 +5904,7 @@ const sites = [ tags: ['interracial'], parent: 'interracialpass', 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', t1: true, }, @@ -5913,7 +5916,7 @@ const sites = [ tags: ['interracial'], parent: 'interracialpass', 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', t1: true, }, @@ -5925,7 +5928,7 @@ const sites = [ tags: ['interracial'], parent: 'interracialpass', 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', t1: true, }, @@ -5937,7 +5940,7 @@ const sites = [ tags: ['interracial'], parent: 'interracialpass', 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', t1: true, }, @@ -5949,7 +5952,7 @@ const sites = [ tags: ['interracial'], parent: 'interracialpass', 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', match: 'My Wifes First Monster Cock', t1: true, diff --git a/src/scrapers/hush.js b/src/scrapers/hush.js index cd726afc..8ed1b7fb 100755 --- a/src/scrapers/hush.js +++ b/src/scrapers/hush.js @@ -1,20 +1,21 @@ '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 { feetInchesToCm, inchesToCm } = require('../utils/convert'); +const tryUrls = require('../utils/try-urls'); +const { convert } = require('../utils/convert'); function deriveEntryId(release) { if (release.date && release.url) { 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) { - 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; @@ -22,7 +23,7 @@ function deriveEntryId(release) { function extractPoster(posterPath, site, baseRelease) { 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 = [ poster, // upscaled @@ -40,38 +41,38 @@ function extractPoster(posterPath, site, baseRelease) { return [baseRelease?.poster || null, []]; } -function getImageWithFallbacks(q, selector, site, el) { +function getImageWithFallbacks(query, selector, site, el) { const sources = el ? [ - q(el, selector, 'src0_3x'), - q(el, selector, 'src0_2x'), - q(el, selector, 'src0_1x'), + unprint.query.attribute(el, selector, 'src0_3x'), + unprint.query.attribute(el, selector, 'src0_2x'), + unprint.query.attribute(el, selector, 'src0_1x'), ] : [ - q(selector, 'src0_3x'), - q(selector, 'src0_2x'), - q(selector, 'src0_1x'), + query.attribute(selector, 'src0_3x'), + query.attribute(selector, 'src0_2x'), + 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) { return scenes.map(({ query }) => { const release = {}; - release.title = query.q('h4 a', true); + release.title = query.content('h4 a'); release.url = query.url('a'); release.date = query.date('.date', 'YYYY-MM-DD'); 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) => [ - query.img('a img', `src${index}_3x`, { origin: channel.url }), - query.img('a img', `src${index}_2x`, { origin: channel.url }), - query.img('a img', `src${index}_1x`, { origin: channel.url }), + [release.poster, ...release.photos] = Array.from({ length: count }, (_value, index) => [ + query.img('a img', { attribute: `src${index}_3x`, origin: channel.url }), + query.img('a img', { attribute: `src${index}_2x`, 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); @@ -85,18 +86,18 @@ function scrapeAllT1(scenes, site, accNetworkReleases) { return scenes.map(({ query }) => { 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.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']; - 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) { - const poster = /^http/.test(posterPath) ? posterPath : `${site.parameters?.media || site.url}${posterPath}`; + const poster = unprint.prefixUrl(posterPath, site.parameters?.media || site.url); release.poster = [ poster.replace('-1x', '-3x'), @@ -117,37 +118,37 @@ function scrapeAllT1(scenes, site, accNetworkReleases) { }).filter(Boolean); } -async function fetchLatest(site, page = 1, include, { uniqueReleases = [], duplicateReleases = [] }) { - const url = (site.parameters?.latest && util.format(site.parameters.latest, page)) +async function fetchLatest(site, page = 1, _include, { uniqueReleases = [], duplicateReleases = [] }) { + const url = (site.parameters?.latest && format(site.parameters.latest, { page })) || (site.parameters?.t1 && `${site.url}/t1/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) { return res.status; } 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) { const release = { url }; // url used for entry ID - release.title = query.cnt('.videoDetails h3'); - release.description = query.cnt('.videoDetails p'); + release.title = query.content('.videoDetails h3'); + release.description = query.content('.videoDetails p'); 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 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 })] .map((src) => src && [ @@ -159,10 +160,10 @@ function scrapeScene({ html, query }, channel, url) { const trailerPath = html.match(/\/trailers?\/.*.mp4/); 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.entryId = deriveEntryId(release); @@ -173,29 +174,33 @@ function scrapeScene({ html, query }, channel, url) { function scrapeSceneT1({ html, query }, site, url, baseRelease) { 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.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) => ({ - name: query.q(el, 'span', true), - avatar: getImageWithFallbacks(query.q, 'img', site, el), + name: unprint.query.content(el, 'span'), + 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 = query.q('.player-thumb img', 'src0_1x'); + const posterPath = query.img('.player-thumb img', { attribute: 'src0_1x' }); + const trailer = html.match(/ 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); return release; } -function scrapeProfileT1({ el, query }, site) { - const profile = {}; +async function fetchScene(url, site, baseRelease) { + 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(':'); if (!value) return acc; @@ -233,125 +321,48 @@ function scrapeProfileT1({ el, query }, site) { const heightMetric = bio.height?.match(/(\d{3})(\b|c)/); 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); 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) { - const actorSlugA = slugify(actorName, ''); + const actorSlugA = slugify(actorName, '', { lower: false }); const actorSlugB = slugify(actorName); const t1 = channel.parameters?.t1 ? 't1/' : ''; - // follow redirects because a lot of profiles redirect from lowercase to uppercase or vice versa - const res1 = channel.parameters?.profile - ? await qu.get(util.format(channel.parameters.profile, actorSlugA)) - : await qu.get(`${channel.url}/${t1}models/${actorSlugA}.html`, null, null, { followRedirects: true }); - - const res = (res1.ok && res1) - || (channel.parameters?.profile && await qu.get(util.format(channel.parameters.profile, actorSlugB))) - || await qu.get(`${channel.url}/${t1}models/${actorSlugB}.html`, null, null, { followRedirects: true }); + const { res, url } = channel.parameters?.profile + ? await tryUrls([ + format(channel.parameters.profile, { actor: actorSlugA }), + format(channel.parameters.profile, { actor: actorSlugB }), + ], { followRedirects: false }) + : await tryUrls([ + `${channel.url}/${t1}models/${actorSlugA}.html`, + `${channel.url}/${t1}models/${actorSlugB}.html`, + ], { followRedirects: false }); if (!res.ok) { return res.status; } 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 = {