Moved all tag photos to public asset directory.
|
@ -37,12 +37,12 @@
|
||||||
v-for="photo in tag.photos"
|
v-for="photo in tag.photos"
|
||||||
:key="`photo-${photo.id}`"
|
:key="`photo-${photo.id}`"
|
||||||
:title="photo.comment"
|
:title="photo.comment"
|
||||||
:href="`/media/${photo.path}`"
|
:href="`/img/${photo.path}`"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/media/${photo.thumbnail}`"
|
:src="`/img/${photo.thumbnail}`"
|
||||||
class="photo"
|
class="photo"
|
||||||
>
|
>
|
||||||
</a>
|
</a>
|
||||||
|
|
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 116 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 154 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 100 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
0
public/img/tags/tattoo_thumb.jpeg → public/img/tags/tattoo/poster_thumb.jpeg
Normal file → Executable file
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
@ -3,7 +3,7 @@ const upsert = require('../src/utils/upsert');
|
||||||
function getMedia(tagsMap) {
|
function getMedia(tagsMap) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
path: 'tags/airtight.jpeg',
|
path: 'tags/airtight/poster.jpeg',
|
||||||
target_id: tagsMap.airtight,
|
target_id: tagsMap.airtight,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: 'Jynx Maze in "Pump My Ass Full of Cum 3" for Jules Jordan',
|
comment: 'Jynx Maze in "Pump My Ass Full of Cum 3" for Jules Jordan',
|
||||||
|
@ -19,41 +19,47 @@ function getMedia(tagsMap) {
|
||||||
comment: 'Chloe Amour in "DP Masters 4" for Jules Jordan',
|
comment: 'Chloe Amour in "DP Masters 4" for Jules Jordan',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/airtight/0.jpeg',
|
path: 'tags/airtight/0/poster.jpeg',
|
||||||
domain: 'tags',
|
domain: 'tags',
|
||||||
target_id: tagsMap.airtight,
|
target_id: tagsMap.airtight,
|
||||||
comment: 'Sheena Shaw in "Ass Worship 14" for Jules Jordan',
|
comment: 'Sheena Shaw in "Ass Worship 14" for Jules Jordan',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/anal.jpeg',
|
path: 'tags/anal/poster.jpeg',
|
||||||
target_id: tagsMap.anal,
|
target_id: tagsMap.anal,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/double-penetration.jpeg',
|
path: 'tags/double-penetration/poster.jpeg',
|
||||||
target_id: tagsMap['double-penetration'],
|
target_id: tagsMap['double-penetration'],
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/double-anal.jpeg',
|
path: 'tags/double-anal/poster.jpeg',
|
||||||
target_id: tagsMap['double-anal'],
|
target_id: tagsMap['double-anal'],
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/double-vaginal.jpeg',
|
path: 'tags/double-vaginal/poster.jpeg',
|
||||||
target_id: tagsMap['double-vaginal'],
|
target_id: tagsMap['double-vaginal'],
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/da-tp.jpeg',
|
path: 'tags/da-tp/poster.jpeg',
|
||||||
target_id: tagsMap['da-tp'],
|
target_id: tagsMap['da-tp'],
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: 'Ninel Mojado aka Mira Cuckold in GIO063 for LegalPorno',
|
comment: 'Ninel Mojado aka Mira Cuckold in GIO063 for LegalPorno',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tags/da-tp/3.jpeg',
|
||||||
|
target_id: tagsMap['da-tp'],
|
||||||
|
role: 'photo',
|
||||||
|
comment: 'Evelina Darling in GIO294',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/da-tp/1.jpeg',
|
path: 'tags/da-tp/1.jpeg',
|
||||||
target_id: tagsMap['da-tp'],
|
target_id: tagsMap['da-tp'],
|
||||||
|
@ -67,19 +73,19 @@ function getMedia(tagsMap) {
|
||||||
comment: 'Angel Smalls in GIO408 for LegalPorno',
|
comment: 'Angel Smalls in GIO408 for LegalPorno',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/dv-tp.jpeg',
|
path: 'tags/dv-tp/poster.jpeg',
|
||||||
target_id: tagsMap['dv-tp'],
|
target_id: tagsMap['dv-tp'],
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: 'Juelz Ventura in "Gangbanged 5" for Elegant Angel',
|
comment: 'Juelz Ventura in "Gangbanged 5" for Elegant Angel',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/tattoo.jpeg',
|
path: 'tags/tattoo/poster.jpeg',
|
||||||
target_id: tagsMap.tattoo,
|
target_id: tagsMap.tattoo,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: 'Kali Roses in "Goes All In For Anal" for Hussie Pass',
|
comment: 'Kali Roses in "Goes All In For Anal" for Hussie Pass',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/triple-anal.jpeg',
|
path: 'tags/triple-anal/poster.jpeg',
|
||||||
target_id: tagsMap['triple-anal'],
|
target_id: tagsMap['triple-anal'],
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: 'Kristy Black in SZ1986 for LegalPorno',
|
comment: 'Kristy Black in SZ1986 for LegalPorno',
|
||||||
|
@ -97,13 +103,13 @@ function getMedia(tagsMap) {
|
||||||
comment: 'Kira Thorn in GIO1018 for LegalPorno',
|
comment: 'Kira Thorn in GIO1018 for LegalPorno',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/blowbang.jpeg',
|
path: 'tags/blowbang/poster.jpeg',
|
||||||
target_id: tagsMap.blowbang,
|
target_id: tagsMap.blowbang,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/gangbang.jpeg',
|
path: 'tags/gangbang/poster.jpeg',
|
||||||
target_id: tagsMap.gangbang,
|
target_id: tagsMap.gangbang,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
|
@ -127,85 +133,85 @@ function getMedia(tagsMap) {
|
||||||
comment: 'Kelsi Monroe in "Brazzers House 2, Day 2" for Brazzers',
|
comment: 'Kelsi Monroe in "Brazzers House 2, Day 2" for Brazzers',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/mff.jpeg',
|
path: 'tags/mff/poster.jpeg',
|
||||||
target_id: tagsMap.mff,
|
target_id: tagsMap.mff,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/mfm.jpeg',
|
path: 'tags/mfm/poster.jpeg',
|
||||||
target_id: tagsMap.mfm,
|
target_id: tagsMap.mfm,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/orgy.jpeg',
|
path: 'tags/orgy/poster.jpeg',
|
||||||
target_id: tagsMap.orgy,
|
target_id: tagsMap.orgy,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/asian.jpeg',
|
path: 'tags/asian/poster.jpeg',
|
||||||
target_id: tagsMap.asian,
|
target_id: tagsMap.asian,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/caucasian.jpeg',
|
path: 'tags/caucasian/poster.jpeg',
|
||||||
target_id: tagsMap.caucasian,
|
target_id: tagsMap.caucasian,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/ebony.jpeg',
|
path: 'tags/ebony/poster.jpeg',
|
||||||
target_id: tagsMap.ebony,
|
target_id: tagsMap.ebony,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/latina.jpeg',
|
path: 'tags/latina/poster.jpeg',
|
||||||
target_id: tagsMap.latina,
|
target_id: tagsMap.latina,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/interracial.jpeg',
|
path: 'tags/interracial/poster.jpeg',
|
||||||
target_id: tagsMap.interracial,
|
target_id: tagsMap.interracial,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/facial.jpeg',
|
path: 'tags/facial/poster.jpeg',
|
||||||
target_id: tagsMap.facial,
|
target_id: tagsMap.facial,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/bukkake.jpeg',
|
path: 'tags/bukkake/poster.jpeg',
|
||||||
target_id: tagsMap.bukkake,
|
target_id: tagsMap.bukkake,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/swallowing.jpeg',
|
path: 'tags/swallowing/poster.jpeg',
|
||||||
target_id: tagsMap.swallowing,
|
target_id: tagsMap.swallowing,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/creampie.jpeg',
|
path: 'tags/creampie/poster.jpeg',
|
||||||
target_id: tagsMap.creampie,
|
target_id: tagsMap.creampie,
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/anal-creampie.jpeg',
|
path: 'tags/anal-creampie/poster.jpeg',
|
||||||
target_id: tagsMap['anal-creampie'],
|
target_id: tagsMap['anal-creampie'],
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/oral-creampie.jpeg',
|
path: 'tags/oral-creampie/poster.jpeg',
|
||||||
target_id: tagsMap['oral-creampie'],
|
target_id: tagsMap['oral-creampie'],
|
||||||
role: 'poster',
|
role: 'poster',
|
||||||
comment: '',
|
comment: '',
|
||||||
|
|
|
@ -332,8 +332,9 @@ async function scrapeActors(actorNames) {
|
||||||
const profile = await scraper.fetchProfile(actorEntry ? actorEntry.name : actorName);
|
const profile = await scraper.fetchProfile(actorEntry ? actorEntry.name : actorName);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
scraper: scraperSlug,
|
|
||||||
...profile,
|
...profile,
|
||||||
|
name: actorName,
|
||||||
|
scraper: scraperSlug,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,135 +1,86 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/* eslint-disable newline-per-chained-call */
|
|
||||||
const bhttp = require('bhttp');
|
const bhttp = require('bhttp');
|
||||||
const { JSDOM } = require('jsdom');
|
const { JSDOM } = require('jsdom');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
async function scrapeProfileFrontpage(html, url, name) {
|
function scrapeProfile(html, actorName) {
|
||||||
const { document } = new JSDOM(html).window;
|
const { document } = new JSDOM(html).window;
|
||||||
const bioEl = document.querySelector('.dashboard-bio-list');
|
const profile = { name: actorName };
|
||||||
|
|
||||||
const bioUrl = `https:${document.querySelector('.seemore a').href}`;
|
const bio = Array.from(document.querySelectorAll('a[href^="/babes"]'), el => decodeURI(el.href)).reduce((acc, item) => {
|
||||||
|
const keyMatch = item.match(/\[\w+\]/);
|
||||||
|
|
||||||
const keys = Array.from(bioEl.querySelectorAll('dt'), el => el.textContent.trim());
|
if (keyMatch) {
|
||||||
const values = Array.from(bioEl.querySelectorAll('dd'), el => el.textContent.trim());
|
const key = keyMatch[0].slice(1, -1);
|
||||||
|
const [, value] = item.split('=');
|
||||||
|
|
||||||
const bio = keys.reduce((acc, key, index) => ({ ...acc, [key]: values[index] }), {});
|
// both hip and waist link to 'waist', assume biggest value is hip
|
||||||
|
if (key === 'waist' && acc.waist) {
|
||||||
|
if (acc.waist > value) {
|
||||||
|
acc.hip = acc.waist;
|
||||||
|
acc.waist = value;
|
||||||
|
|
||||||
const profile = {
|
return acc;
|
||||||
name,
|
}
|
||||||
gender: 'female',
|
|
||||||
};
|
|
||||||
|
|
||||||
const birthdateString = bio['Date of Birth:'];
|
acc.hip = value;
|
||||||
const measurementsString = bio['Measurements:'];
|
|
||||||
|
|
||||||
const birthCityString = bio['Place of Birth:'];
|
return acc;
|
||||||
const birthCity = birthCityString !== undefined && birthCityString !== 'Unknown' && birthCityString !== 'Unknown (add)' && birthCityString;
|
}
|
||||||
|
|
||||||
const birthCountryString = bio['Country of Origin:'];
|
acc[key] = value;
|
||||||
const birthCountry = birthCountryString !== undefined && birthCountryString !== 'Unknown' && birthCountryString !== 'Unknown (add)' && birthCountryString;
|
}
|
||||||
|
|
||||||
const piercingsString = bio['Piercings:'];
|
return acc;
|
||||||
const tattoosString = bio['Tattoos:'];
|
}, {});
|
||||||
|
|
||||||
if (birthdateString && birthdateString !== 'Unknown (add)') profile.birthdate = moment.utc(birthdateString.slice(0, birthdateString.indexOf(' (')), 'MMMM D, YYYY').toDate();
|
if (bio.dateOfBirth) profile.birthdate = moment.utc(bio.dateOfBirth, 'YYYY-MM-DD').toDate();
|
||||||
if (measurementsString) [profile.bust, profile.waist, profile.hip] = measurementsString.split('-').map(measurement => (measurement === '??' ? null : measurement));
|
|
||||||
|
|
||||||
if (bio['Fake Boobs:']) profile.naturalBoobs = bio['Fake Boobs:'] === 'No';
|
if (profile.placeOfBirth || bio.country) profile.birthPlace = `${bio.placeOfBirth}, ${bio.country}`;
|
||||||
profile.birthPlace = `${birthCity || ''}${birthCity ? ', ' : ''}${birthCountry || ''}`;
|
profile.eyes = bio.eyeColor;
|
||||||
|
profile.hair = bio.hairColor;
|
||||||
|
profile.ethnicity = bio.ethnicity;
|
||||||
|
|
||||||
profile.hair = bio['Hair Color:'].toLowerCase();
|
profile.bust = bio.bra;
|
||||||
profile.eyes = bio['Eye Color:'].toLowerCase();
|
if (bio.waist) profile.waist = Number(bio.waist.split(',')[0]);
|
||||||
|
if (bio.hip) profile.hip = Number(bio.hip.split(',')[0]);
|
||||||
|
|
||||||
if (piercingsString) profile.hasPiercings = !!(piercingsString !== 'Unknown (add)' && piercingsString !== 'None');
|
if (bio.height) profile.height = Number(bio.height.split(',')[0]);
|
||||||
if (tattoosString) profile.hasTattoos = !!(tattoosString !== 'Unknown (add)' && tattoosString !== 'None');
|
if (bio.weight) profile.weight = Number(bio.weight.split(',')[0]);
|
||||||
|
|
||||||
if (profile.hasPiercings && piercingsString !== 'various') profile.piercings = piercingsString;
|
profile.social = Array.from(document.querySelectorAll('.profile-meta-item a.social-icons'), el => el.href);
|
||||||
if (profile.hasTattoos && tattoosString !== 'various') profile.tattoos = tattoosString;
|
profile.avatar = document.querySelector('.profile-image-large img').src;
|
||||||
|
|
||||||
profile.social = Array.from(bioEl.querySelectorAll('.dashboard-socialmedia a'), el => el.href);
|
|
||||||
|
|
||||||
return {
|
|
||||||
profile,
|
|
||||||
url: bioUrl,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function scrapeProfileBio(html, frontpageProfile, url, name) {
|
|
||||||
const { document } = new JSDOM(html).window;
|
|
||||||
const bioEl = document.querySelector('#biographyTable');
|
|
||||||
|
|
||||||
const keys = Array.from(bioEl.querySelectorAll('td:nth-child(1)'), el => el.textContent.trim());
|
|
||||||
const values = Array.from(bioEl.querySelectorAll('td:nth-child(2)'), el => el.textContent.trim());
|
|
||||||
|
|
||||||
const bio = keys.reduce((acc, key, index) => ({ ...acc, [key]: values[index] }), {});
|
|
||||||
|
|
||||||
const profile = {
|
|
||||||
...frontpageProfile,
|
|
||||||
name,
|
|
||||||
gender: 'female',
|
|
||||||
};
|
|
||||||
|
|
||||||
const birthdateString = bio['Date of Birth:'];
|
|
||||||
const measurementsString = bio['Measurements:'];
|
|
||||||
|
|
||||||
const birthCityString = bio['Place of Birth:'];
|
|
||||||
const birthCity = birthCityString !== undefined && birthCityString !== 'Unknown' && birthCityString !== 'Unknown (add)' && birthCityString;
|
|
||||||
|
|
||||||
const birthCountryString = bio['Country of Origin:'];
|
|
||||||
const birthCountry = birthCountryString !== undefined && birthCountryString !== 'Unknown' && birthCountryString !== 'Unknown (add)' && birthCountryString;
|
|
||||||
|
|
||||||
const piercingsString = bio['Piercings:'];
|
|
||||||
const tattoosString = bio['Tattoos:'];
|
|
||||||
|
|
||||||
if (birthdateString && birthdateString !== 'Unknown') profile.birthdate = moment.utc(birthdateString.slice(0, birthdateString.indexOf(' (')), 'MMMM D, YYYY').toDate();
|
|
||||||
if (measurementsString) [profile.bust, profile.waist, profile.hip] = measurementsString.split('-').map(measurement => (measurement === '??' ? null : measurement));
|
|
||||||
|
|
||||||
if (bio['Fake boobs']) profile.naturalBoobs = bio['Fake boobs:'] === 'No';
|
|
||||||
profile.ethnicity = bio['Ethnicity:'];
|
|
||||||
|
|
||||||
profile.birthPlace = `${birthCity || ''}${birthCity ? ', ' : ''}${birthCountry || ''}`;
|
|
||||||
|
|
||||||
profile.hair = bio['Hair Color:'].toLowerCase();
|
|
||||||
profile.eyes = bio['Eye Color:'].toLowerCase();
|
|
||||||
profile.height = Number(bio['Height:'].match(/\d+/)[0]);
|
|
||||||
profile.weight = Number(bio['Weight:'].match(/\d+/)[0]);
|
|
||||||
|
|
||||||
if (piercingsString) profile.hasPiercings = !!(piercingsString !== 'Unknown (add)' && piercingsString !== 'None');
|
|
||||||
if (tattoosString) profile.hasTattoos = !!(tattoosString !== 'Unknown (add)' && tattoosString !== 'None');
|
|
||||||
|
|
||||||
if (profile.hasPiercings && piercingsString !== 'various') profile.piercings = piercingsString;
|
|
||||||
if (profile.hasTattoos && tattoosString !== 'various') profile.tattoos = tattoosString;
|
|
||||||
|
|
||||||
profile.social = Array.from(bioEl.querySelectorAll('#socialmedia a'), el => el.href);
|
|
||||||
|
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scrapeSearch(html) {
|
||||||
|
const { document } = new JSDOM(html).window;
|
||||||
|
|
||||||
|
return document.querySelector('a.image-link')?.href || null;
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchProfile(actorName) {
|
async function fetchProfile(actorName) {
|
||||||
const slug = actorName.replace(' ', '_');
|
const actorSlug = actorName.toLowerCase().replace(/\s+/g, '-');
|
||||||
const frontpageUrl = `https://www.freeones.com/html/v_links/${slug}`;
|
|
||||||
|
|
||||||
const resFrontpage = await bhttp.get(frontpageUrl);
|
const res = await bhttp.get(`https://freeones.nl/${actorSlug}/profile`);
|
||||||
|
|
||||||
if (resFrontpage.statusCode === 200) {
|
if (res.statusCode === 200) {
|
||||||
const { url, bio } = await scrapeProfileFrontpage(resFrontpage.body.toString(), frontpageUrl, actorName);
|
return scrapeProfile(res.body.toString(), actorName);
|
||||||
const resBio = await bhttp.get(url);
|
|
||||||
|
|
||||||
return scrapeProfileBio(resBio.body.toString(), bio, url, actorName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// apparently some actors are appended 'Babe' as their surname...
|
const searchRes = await bhttp.get(`https://freeones.nl/babes?q=${actorName}`);
|
||||||
const fallbackSlug = `${slug}_Babe`;
|
const actorPath = scrapeSearch(searchRes.body.toString());
|
||||||
const fallbackUrl = `https://www.freeones.com/html/s_links/${fallbackSlug}`;
|
|
||||||
const resFallback = await bhttp.get(fallbackUrl);
|
|
||||||
|
|
||||||
if (resFallback.statusCode === 200) {
|
if (actorPath) {
|
||||||
const { url, profile } = await scrapeProfileFrontpage(resFallback.body.toString(), fallbackUrl, actorName);
|
const actorRes = await bhttp.get(`https://freeones.nl${actorPath}/profile`);
|
||||||
const resBio = await bhttp.get(url);
|
|
||||||
|
|
||||||
return scrapeProfileBio(resBio.body.toString(), profile, url, actorName);
|
if (actorRes.statusCode === 200) {
|
||||||
|
return scrapeProfile(actorRes.body.toString(), actorName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* eslint-disable newline-per-chained-call */
|
||||||
|
const bhttp = require('bhttp');
|
||||||
|
const { JSDOM } = require('jsdom');
|
||||||
|
const moment = require('moment');
|
||||||
|
|
||||||
|
async function scrapeProfileFrontpage(html, url, name) {
|
||||||
|
const { document } = new JSDOM(html).window;
|
||||||
|
const bioEl = document.querySelector('.dashboard-bio-list');
|
||||||
|
|
||||||
|
const bioUrl = `https:${document.querySelector('.seemore a').href}`;
|
||||||
|
|
||||||
|
const keys = Array.from(bioEl.querySelectorAll('dt'), el => el.textContent.trim());
|
||||||
|
const values = Array.from(bioEl.querySelectorAll('dd'), el => el.textContent.trim());
|
||||||
|
|
||||||
|
const bio = keys.reduce((acc, key, index) => ({ ...acc, [key]: values[index] }), {});
|
||||||
|
|
||||||
|
const profile = {
|
||||||
|
name,
|
||||||
|
gender: 'female',
|
||||||
|
};
|
||||||
|
|
||||||
|
const birthdateString = bio['Date of Birth:'];
|
||||||
|
const measurementsString = bio['Measurements:'];
|
||||||
|
|
||||||
|
const birthCityString = bio['Place of Birth:'];
|
||||||
|
const birthCity = birthCityString !== undefined && birthCityString !== 'Unknown' && birthCityString !== 'Unknown (add)' && birthCityString;
|
||||||
|
|
||||||
|
const birthCountryString = bio['Country of Origin:'];
|
||||||
|
const birthCountry = birthCountryString !== undefined && birthCountryString !== 'Unknown' && birthCountryString !== 'Unknown (add)' && birthCountryString;
|
||||||
|
|
||||||
|
const piercingsString = bio['Piercings:'];
|
||||||
|
const tattoosString = bio['Tattoos:'];
|
||||||
|
|
||||||
|
if (birthdateString && birthdateString !== 'Unknown (add)') profile.birthdate = moment.utc(birthdateString.slice(0, birthdateString.indexOf(' (')), 'MMMM D, YYYY').toDate();
|
||||||
|
if (measurementsString) [profile.bust, profile.waist, profile.hip] = measurementsString.split('-').map(measurement => (measurement === '??' ? null : measurement));
|
||||||
|
|
||||||
|
if (bio['Fake Boobs:']) profile.naturalBoobs = bio['Fake Boobs:'] === 'No';
|
||||||
|
profile.birthPlace = `${birthCity || ''}${birthCity ? ', ' : ''}${birthCountry || ''}`;
|
||||||
|
|
||||||
|
profile.hair = bio['Hair Color:'].toLowerCase();
|
||||||
|
profile.eyes = bio['Eye Color:'].toLowerCase();
|
||||||
|
|
||||||
|
if (piercingsString) profile.hasPiercings = !!(piercingsString !== 'Unknown (add)' && piercingsString !== 'None');
|
||||||
|
if (tattoosString) profile.hasTattoos = !!(tattoosString !== 'Unknown (add)' && tattoosString !== 'None');
|
||||||
|
|
||||||
|
if (profile.hasPiercings && piercingsString !== 'various') profile.piercings = piercingsString;
|
||||||
|
if (profile.hasTattoos && tattoosString !== 'various') profile.tattoos = tattoosString;
|
||||||
|
|
||||||
|
profile.social = Array.from(bioEl.querySelectorAll('.dashboard-socialmedia a'), el => el.href);
|
||||||
|
|
||||||
|
return {
|
||||||
|
profile,
|
||||||
|
url: bioUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function scrapeProfileBio(html, frontpageProfile, url, name) {
|
||||||
|
const { document } = new JSDOM(html).window;
|
||||||
|
const bioEl = document.querySelector('#biographyTable');
|
||||||
|
|
||||||
|
const keys = Array.from(bioEl.querySelectorAll('td:nth-child(1)'), el => el.textContent.trim());
|
||||||
|
const values = Array.from(bioEl.querySelectorAll('td:nth-child(2)'), el => el.textContent.trim());
|
||||||
|
|
||||||
|
const bio = keys.reduce((acc, key, index) => ({ ...acc, [key]: values[index] }), {});
|
||||||
|
|
||||||
|
const profile = {
|
||||||
|
...frontpageProfile,
|
||||||
|
name,
|
||||||
|
gender: 'female',
|
||||||
|
};
|
||||||
|
|
||||||
|
const birthdateString = bio['Date of Birth:'];
|
||||||
|
const measurementsString = bio['Measurements:'];
|
||||||
|
|
||||||
|
const birthCityString = bio['Place of Birth:'];
|
||||||
|
const birthCity = birthCityString !== undefined && birthCityString !== 'Unknown' && birthCityString !== 'Unknown (add)' && birthCityString;
|
||||||
|
|
||||||
|
const birthCountryString = bio['Country of Origin:'];
|
||||||
|
const birthCountry = birthCountryString !== undefined && birthCountryString !== 'Unknown' && birthCountryString !== 'Unknown (add)' && birthCountryString;
|
||||||
|
|
||||||
|
const piercingsString = bio['Piercings:'];
|
||||||
|
const tattoosString = bio['Tattoos:'];
|
||||||
|
|
||||||
|
if (birthdateString && birthdateString !== 'Unknown') profile.birthdate = moment.utc(birthdateString.slice(0, birthdateString.indexOf(' (')), 'MMMM D, YYYY').toDate();
|
||||||
|
if (measurementsString) [profile.bust, profile.waist, profile.hip] = measurementsString.split('-').map(measurement => (measurement === '??' ? null : measurement));
|
||||||
|
|
||||||
|
if (bio['Fake boobs']) profile.naturalBoobs = bio['Fake boobs:'] === 'No';
|
||||||
|
profile.ethnicity = bio['Ethnicity:'];
|
||||||
|
|
||||||
|
profile.birthPlace = `${birthCity || ''}${birthCity ? ', ' : ''}${birthCountry || ''}`;
|
||||||
|
|
||||||
|
profile.hair = bio['Hair Color:'].toLowerCase();
|
||||||
|
profile.eyes = bio['Eye Color:'].toLowerCase();
|
||||||
|
profile.height = Number(bio['Height:'].match(/\d+/)[0]);
|
||||||
|
profile.weight = Number(bio['Weight:'].match(/\d+/)[0]);
|
||||||
|
|
||||||
|
if (piercingsString) profile.hasPiercings = !!(piercingsString !== 'Unknown (add)' && piercingsString !== 'None');
|
||||||
|
if (tattoosString) profile.hasTattoos = !!(tattoosString !== 'Unknown (add)' && tattoosString !== 'None');
|
||||||
|
|
||||||
|
if (profile.hasPiercings && piercingsString !== 'various') profile.piercings = piercingsString;
|
||||||
|
if (profile.hasTattoos && tattoosString !== 'various') profile.tattoos = tattoosString;
|
||||||
|
|
||||||
|
profile.social = Array.from(bioEl.querySelectorAll('#socialmedia a'), el => el.href);
|
||||||
|
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchProfile(actorName) {
|
||||||
|
const slug = actorName.replace(' ', '_');
|
||||||
|
const frontpageUrl = `https://www.freeones.com/html/v_links/${slug}`;
|
||||||
|
|
||||||
|
const resFrontpage = await bhttp.get(frontpageUrl);
|
||||||
|
|
||||||
|
if (resFrontpage.statusCode === 200) {
|
||||||
|
const { url, bio } = await scrapeProfileFrontpage(resFrontpage.body.toString(), frontpageUrl, actorName);
|
||||||
|
const resBio = await bhttp.get(url);
|
||||||
|
|
||||||
|
return scrapeProfileBio(resBio.body.toString(), bio, url, actorName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apparently some actors are appended 'Babe' as their surname...
|
||||||
|
const fallbackSlug = `${slug}_Babe`;
|
||||||
|
const fallbackUrl = `https://www.freeones.com/html/s_links/${fallbackSlug}`;
|
||||||
|
const resFallback = await bhttp.get(fallbackUrl);
|
||||||
|
|
||||||
|
if (resFallback.statusCode === 200) {
|
||||||
|
const { url, profile } = await scrapeProfileFrontpage(resFallback.body.toString(), fallbackUrl, actorName);
|
||||||
|
const resBio = await bhttp.get(url);
|
||||||
|
|
||||||
|
return scrapeProfileBio(resBio.body.toString(), profile, url, actorName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
fetchProfile,
|
||||||
|
};
|
|
@ -28,6 +28,10 @@ function getPoster(posterElement, sceneId) {
|
||||||
const posterRangeData = posterRange ? JSON.parse(posterRange) : null;
|
const posterRangeData = posterRange ? JSON.parse(posterRange) : null;
|
||||||
const posterTimeRange = posterRangeData[Math.floor(Math.random() * posterRangeData.length)];
|
const posterTimeRange = posterRangeData[Math.floor(Math.random() * posterRangeData.length)];
|
||||||
|
|
||||||
|
if (!posterTimeRange) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof posterTimeRange === 'number') {
|
if (typeof posterTimeRange === 'number') {
|
||||||
// poster time is already a single time value
|
// poster time is already a single time value
|
||||||
return `https://legalporno.com/casting/${sceneId}/${posterTimeRange}`;
|
return `https://legalporno.com/casting/${sceneId}/${posterTimeRange}`;
|
||||||
|
|
|
@ -25,6 +25,7 @@ const xempire = require('./xempire');
|
||||||
|
|
||||||
// profiles
|
// profiles
|
||||||
const freeones = require('./freeones');
|
const freeones = require('./freeones');
|
||||||
|
const freeonesLegacy = require('./freeones_legacy');
|
||||||
const pornhub = require('./pornhub');
|
const pornhub = require('./pornhub');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -54,11 +55,12 @@ module.exports = {
|
||||||
// ordered by data priority
|
// ordered by data priority
|
||||||
xempire,
|
xempire,
|
||||||
brazzers,
|
brazzers,
|
||||||
freeones,
|
|
||||||
kellymadison,
|
|
||||||
julesjordan,
|
|
||||||
legalporno,
|
legalporno,
|
||||||
pornhub,
|
pornhub,
|
||||||
|
freeones,
|
||||||
|
freeonesLegacy,
|
||||||
|
kellymadison,
|
||||||
|
julesjordan,
|
||||||
ddfnetwork,
|
ddfnetwork,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|