Showing all unique descriptions on profile with network logo. Fixed Fame Digital scraper.
|
@ -243,10 +243,33 @@
|
|||
@click="expanded = true"
|
||||
><Icon icon="arrow-down3" /></span>
|
||||
|
||||
<div class="descriptions-container">
|
||||
<div
|
||||
v-if="actor.descriptions && actor.descriptions.length > 0"
|
||||
class="descriptions"
|
||||
>
|
||||
<p
|
||||
v-if="actor.description"
|
||||
v-for="description in actor.descriptions"
|
||||
:key="`description-${description.network.id}`"
|
||||
class="description"
|
||||
>{{ actor.description }}</p>
|
||||
>
|
||||
{{ description.text }}
|
||||
<router-link :to="{ name: 'network', params: { networkSlug: description.network.slug } }">
|
||||
<img
|
||||
v-if="description.site"
|
||||
:src="`/img/logos/${description.network.slug}/${description.site.slug}.png`"
|
||||
class="description-logo"
|
||||
>
|
||||
|
||||
<img
|
||||
v-else
|
||||
:src="`/img/logos/${description.network.slug}/network.png`"
|
||||
class="description-logo"
|
||||
>
|
||||
</router-link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Social
|
||||
v-if="actor.social && actor.social.length > 0"
|
||||
|
@ -540,17 +563,28 @@ export default {
|
|||
font-size: .8rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
.descriptions-container {
|
||||
max-width: 30rem;
|
||||
max-height: 12rem;
|
||||
max-height: 100%;
|
||||
position: relative;
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
box-sizing: border-box;
|
||||
margin: 0 2rem 0 0;
|
||||
line-height: 1.5;
|
||||
text-overflow: ellipsis;
|
||||
font-size: .9rem;
|
||||
overflow: hidden;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 1.5rem;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background: linear-gradient(transparent, 25%, var(--profile) 75%);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.descriptions {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
scrollbar-width: none;
|
||||
|
||||
|
@ -559,6 +593,22 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0;
|
||||
padding: 0 2rem 0 0;
|
||||
line-height: 1.5;
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
.description-logo {
|
||||
display: block;
|
||||
width: 15rem;
|
||||
max-height: 2rem;
|
||||
margin: .5rem 0 1.5rem 0;
|
||||
object-fit: contain;
|
||||
object-position: 0 50%;
|
||||
}
|
||||
|
||||
.actor-content {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
|
|
@ -49,6 +49,7 @@ async function mounted() {
|
|||
'creampie',
|
||||
],
|
||||
oral: [
|
||||
'blowjob',
|
||||
'deepthroat',
|
||||
'facefucking',
|
||||
'double-blowjob',
|
||||
|
@ -87,6 +88,14 @@ async function mounted() {
|
|||
'nurse',
|
||||
'maid',
|
||||
],
|
||||
fetish: [
|
||||
'bdsm',
|
||||
'femdom',
|
||||
],
|
||||
toys: [
|
||||
'double-dildo',
|
||||
'double-dildo-blowjob',
|
||||
],
|
||||
misc: [
|
||||
'gaping',
|
||||
'oil',
|
||||
|
|
|
@ -78,6 +78,17 @@ function initActorActions(store, _router) {
|
|||
}
|
||||
profiles: actorsProfiles {
|
||||
description
|
||||
descriptionHash
|
||||
network {
|
||||
id
|
||||
slug
|
||||
name
|
||||
}
|
||||
site {
|
||||
id
|
||||
slug
|
||||
name
|
||||
}
|
||||
avatar: avatarMedia {
|
||||
id
|
||||
path
|
||||
|
|
|
@ -34,7 +34,19 @@ function curateActor(actor, release, curateActorRelease) {
|
|||
.map(profile => profile.avatar)
|
||||
.filter(avatar => avatar && (!curatedActor.avatar || avatar.hash !== curatedActor.avatar.hash));
|
||||
|
||||
const descriptions = actor.profiles.reduce((acc, profile) => ({
|
||||
...acc,
|
||||
...(profile.description && {
|
||||
[profile.descriptionHash]: {
|
||||
text: profile.description,
|
||||
network: profile.network,
|
||||
site: profile.site,
|
||||
},
|
||||
}),
|
||||
}), {});
|
||||
|
||||
curatedActor.photos = Object.values(photos.reduce((acc, photo) => ({ ...acc, [photo.hash]: photo }), {}));
|
||||
curatedActor.descriptions = Object.values(descriptions);
|
||||
}
|
||||
|
||||
if (release && release.date && curatedActor.birthdate) {
|
||||
|
|
|
@ -349,12 +349,13 @@ exports.up = knex => Promise.resolve()
|
|||
.defaultTo(1);
|
||||
|
||||
table.string('real_name');
|
||||
table.string('gender', 18);
|
||||
|
||||
table.date('date_of_birth');
|
||||
table.date('date_of_death');
|
||||
|
||||
table.string('gender', 18);
|
||||
table.text('description');
|
||||
table.string('description_hash');
|
||||
|
||||
table.string('birth_city');
|
||||
table.string('birth_state');
|
||||
|
|
|
@ -3401,6 +3401,11 @@
|
|||
"domelementtype": "1"
|
||||
}
|
||||
},
|
||||
"dompurify": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.11.tgz",
|
||||
"integrity": "sha512-qVoGPjIW9IqxRij7klDQQ2j6nSe4UNWANBhZNLnsS7ScTtLb+3YdxkRY8brNTpkUiTtcXsCJO+jS0UCDfenLuA=="
|
||||
},
|
||||
"domutils": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
"config": "^3.2.5",
|
||||
"csv-stringify": "^5.3.6",
|
||||
"dayjs": "^1.8.21",
|
||||
"dompurify": "^2.0.11",
|
||||
"ejs": "^3.0.1",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
|
|
Before Width: | Height: | Size: 592 KiB After Width: | Height: | Size: 689 KiB |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 932 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 416 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 868 KiB |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 37 KiB |
|
@ -22,6 +22,7 @@ const tagPosters = [
|
|||
['deepthroat', 0, 'Chanel Grey in "Deepthroating Is Fun" for Throated'],
|
||||
['double-anal', 7, 'Adriana Chechik in "DP Masters 6" for Jules Jordan'],
|
||||
['double-blowjob', 1, 'Veronica Rodriguez and Penny Pax in "Fucking Older Guys 5" for Penthouse'],
|
||||
['double-dildo', 0, 'Kali Roses in "Double Dildo Party" for KaliRoses.com'],
|
||||
['double-dildo-blowjob', 0, 'Adriana Chechik and Vicki Chase in "Anal Savages 1" for Jules Jordan'],
|
||||
['double-penetration', 2, 'Megan Rain in "DP Masters 4" for Jules Jordan'],
|
||||
['double-vaginal', 'poster', 'Riley Reid in "Pizza That Ass" for Reid My Lips'],
|
||||
|
@ -31,6 +32,7 @@ const tagPosters = [
|
|||
['facial', 0, 'Brooklyn Gray in "All About Ass 4" for Evil Angel'],
|
||||
['fake-boobs', 1, 'Lela Star in "Thick" for Jules Jordan'],
|
||||
['family', 0, 'Teanna Trump in "A Family Appear: Part One" for Brazzers'],
|
||||
['femdom', 0, 'Alina Li in "Asian Domination… She Holds Jules Jordan\'s Cock Hostage!" for Jules Jordan'],
|
||||
['gangbang', 5, 'Carter Cruise\'s first gangbang in "Slut Puppies 9" for Jules Jordan'],
|
||||
['gaping', 1, 'Vina Sky in "Vina Sky Does Anal" for HardX'],
|
||||
['interracial', 0, 'Jaye Summers and Prince Yahshua in "Platinum Pussy 3" for Jules Jordan'],
|
||||
|
@ -100,6 +102,7 @@ const tagPhotos = [
|
|||
['dv-tp', 0, 'Luna Rival in LegalPorno SZ1490'],
|
||||
['facial', 1, 'Ella Knox in "Mr Saltys Adult Emporium Adventure 2" for Aziani'],
|
||||
['facial', 'poster', 'Jynx Maze'],
|
||||
['facefucking', 3, 'Adriana Chechik in "Performing Magic Butt Tricks With Jules Jordan. What Will Disappear In Her Ass?" for Jules Jordan'],
|
||||
['facefucking', 1, 'Carrie for Young Throats'],
|
||||
// ['fake-boobs', 0, 'Marsha May in "Once You Go Black 7" for Jules Jordan'],
|
||||
['gangbang', 'poster', 'Kristen Scott in "Interracial Gangbang!" for Jules Jordan'],
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
const config = require('config');
|
||||
const Promise = require('bluebird');
|
||||
const moment = require('moment');
|
||||
const blake2 = require('blake2');
|
||||
const DOMPurify = require('dompurify');
|
||||
const { JSDOM } = require('jsdom');
|
||||
|
||||
const { window } = new JSDOM('');
|
||||
const domPurify = DOMPurify(window);
|
||||
|
||||
// const logger = require('./logger')(__filename);
|
||||
const knex = require('./knex');
|
||||
|
@ -148,6 +154,7 @@ function curateProfileEntry(profile) {
|
|||
gender: profile.gender,
|
||||
ethnicity: profile.ethnicity,
|
||||
description: profile.description,
|
||||
description_hash: profile.descriptionHash,
|
||||
birth_city: profile.placeOfBirth?.city || null,
|
||||
birth_state: profile.placeOfBirth?.state || null,
|
||||
birth_country_alpha2: profile.placeOfBirth?.country || null,
|
||||
|
@ -189,7 +196,14 @@ async function curateProfile(profile) {
|
|||
update: profile.update,
|
||||
};
|
||||
|
||||
curatedProfile.description = profile.description?.trim() || null;
|
||||
curatedProfile.description = domPurify.sanitize(profile.description, { ALLOWED_TAGS: [] }).trim() || null;
|
||||
|
||||
const hasher = curatedProfile.description && blake2
|
||||
.createHash('blake2b')
|
||||
.update(Buffer.from(slugify(curatedProfile.description)));
|
||||
|
||||
curatedProfile.descriptionHash = curatedProfile.description && hasher.digest('hex');
|
||||
|
||||
curatedProfile.nationality = profile.nationality?.trim() || null; // used to derive country when country not available
|
||||
|
||||
curatedProfile.ethnicity = ethnicities[profile.ethnicity?.trim().toLowerCase()] || null;
|
||||
|
|
|
@ -39,9 +39,8 @@ function decodeId(id) {
|
|||
.toString('hex');
|
||||
}
|
||||
|
||||
function scrapeScene(scene, site) {
|
||||
function scrapeScene(scene) {
|
||||
const release = {
|
||||
site,
|
||||
entryId: scene.id,
|
||||
title: scene.name,
|
||||
description: scene.description,
|
||||
|
@ -80,11 +79,11 @@ function scrapeScene(scene, site) {
|
|||
return release;
|
||||
}
|
||||
|
||||
function scrapeAll(scenes, site) {
|
||||
return scenes.map(({ _source: scene }) => scrapeScene(scene, site));
|
||||
function scrapeAll(scenes) {
|
||||
return scenes.map(({ _source: scene }) => scrapeScene(scene));
|
||||
}
|
||||
|
||||
async function fetchActorReleases(actor, site) {
|
||||
async function fetchActorReleases(actor) {
|
||||
const res = await bhttp.post(`https://${clusterId}.us-east-1.aws.found.io/videos/video/_search`, {
|
||||
size: 50,
|
||||
query: {
|
||||
|
@ -138,11 +137,10 @@ async function fetchActorReleases(actor, site) {
|
|||
},
|
||||
});
|
||||
|
||||
return scrapeAll(res.body.hits.hits, site);
|
||||
return scrapeAll(res.body.hits.hits);
|
||||
}
|
||||
|
||||
|
||||
async function scrapeProfile(actor, site, include) {
|
||||
async function scrapeProfile(actor, include) {
|
||||
const profile = {};
|
||||
|
||||
profile.aliases = actor.aliases;
|
||||
|
@ -174,7 +172,7 @@ async function scrapeProfile(actor, site, include) {
|
|||
if (actor.image) profile.avatar = `https://i.bang.com/pornstars/${actor.identifier}.jpg`;
|
||||
|
||||
if (include.releases) {
|
||||
profile.releases = await fetchActorReleases(actor, site);
|
||||
profile.releases = await fetchActorReleases(actor);
|
||||
}
|
||||
|
||||
return profile;
|
||||
|
@ -267,7 +265,7 @@ async function fetchLatest(site, page = 1) {
|
|||
return scrapeAll(res.body.hits.hits, site);
|
||||
}
|
||||
|
||||
async function fetchScene(url, site) {
|
||||
async function fetchScene(url) {
|
||||
const encodedId = new URL(url).pathname.split('/')[2];
|
||||
const entryId = decodeId(encodedId);
|
||||
|
||||
|
@ -277,10 +275,10 @@ async function fetchScene(url, site) {
|
|||
},
|
||||
});
|
||||
|
||||
return scrapeScene(res.body._source, site); // eslint-disable-line no-underscore-dangle
|
||||
return scrapeScene(res.body._source); // eslint-disable-line no-underscore-dangle
|
||||
}
|
||||
|
||||
async function fetchProfile(actorName, actorSlug, site, include) {
|
||||
async function fetchProfile(actorName, context, include) {
|
||||
const res = await post(`https://${clusterId}.us-east-1.aws.found.io/actors/actor/_search`, {
|
||||
size: 5,
|
||||
sort: [{
|
||||
|
@ -315,7 +313,7 @@ async function fetchProfile(actorName, actorSlug, site, include) {
|
|||
const actor = res.body.hits.hits.find(hit => hit._source.name.toLowerCase() === actorName.toLowerCase());
|
||||
|
||||
if (actor) {
|
||||
return scrapeProfile(actor._source, site, include);
|
||||
return scrapeProfile(actor._source, include);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -91,8 +91,8 @@ async function fetchClassicProfile(actorName, { site }) {
|
|||
}
|
||||
|
||||
async function networkFetchProfile(actorName, context, include) {
|
||||
const profile = await ((context.site.parameters.api && fetchApiProfile(actorName, context, include))
|
||||
|| (context.site.parameters.classic && include.scenes && fetchClassicProfile(actorName, context, include)) // classic profiles only have scenes, no bio
|
||||
const profile = await ((context.site.parameters?.api && fetchApiProfile(actorName, context, include))
|
||||
|| (context.site.parameters?.classic && include.scenes && fetchClassicProfile(actorName, context, include)) // classic profiles only have scenes, no bio
|
||||
|| fetchProfile(actorName, context, true, getActorReleasesUrl, include));
|
||||
|
||||
return profile;
|
||||
|
|