Showing all unique descriptions on profile with network logo. Fixed Fame Digital scraper.

This commit is contained in:
ThePendulum 2020-05-19 04:46:49 +02:00
parent 9883c3d9c2
commit c0898b84d6
23 changed files with 131 additions and 27 deletions

View File

@ -243,10 +243,33 @@
@click="expanded = true" @click="expanded = true"
><Icon icon="arrow-down3" /></span> ><Icon icon="arrow-down3" /></span>
<div class="descriptions-container">
<div
v-if="actor.descriptions && actor.descriptions.length > 0"
class="descriptions"
>
<p <p
v-if="actor.description" v-for="description in actor.descriptions"
:key="`description-${description.network.id}`"
class="description" 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 <Social
v-if="actor.social && actor.social.length > 0" v-if="actor.social && actor.social.length > 0"
@ -540,17 +563,28 @@ export default {
font-size: .8rem; font-size: .8rem;
} }
.description { .descriptions-container {
max-width: 30rem; max-width: 30rem;
max-height: 12rem; max-height: 100%;
position: relative; position: relative;
display: block; display: block;
flex-grow: 1; flex-grow: 1;
box-sizing: border-box; box-sizing: border-box;
margin: 0 2rem 0 0; overflow: hidden;
line-height: 1.5;
text-overflow: ellipsis; &::after {
font-size: .9rem; 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; overflow: auto;
scrollbar-width: none; 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 { .actor-content {
display: flex; display: flex;
flex-grow: 1; flex-grow: 1;

View File

@ -49,6 +49,7 @@ async function mounted() {
'creampie', 'creampie',
], ],
oral: [ oral: [
'blowjob',
'deepthroat', 'deepthroat',
'facefucking', 'facefucking',
'double-blowjob', 'double-blowjob',
@ -87,6 +88,14 @@ async function mounted() {
'nurse', 'nurse',
'maid', 'maid',
], ],
fetish: [
'bdsm',
'femdom',
],
toys: [
'double-dildo',
'double-dildo-blowjob',
],
misc: [ misc: [
'gaping', 'gaping',
'oil', 'oil',

View File

@ -78,6 +78,17 @@ function initActorActions(store, _router) {
} }
profiles: actorsProfiles { profiles: actorsProfiles {
description description
descriptionHash
network {
id
slug
name
}
site {
id
slug
name
}
avatar: avatarMedia { avatar: avatarMedia {
id id
path path

View File

@ -34,7 +34,19 @@ function curateActor(actor, release, curateActorRelease) {
.map(profile => profile.avatar) .map(profile => profile.avatar)
.filter(avatar => avatar && (!curatedActor.avatar || avatar.hash !== curatedActor.avatar.hash)); .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.photos = Object.values(photos.reduce((acc, photo) => ({ ...acc, [photo.hash]: photo }), {}));
curatedActor.descriptions = Object.values(descriptions);
} }
if (release && release.date && curatedActor.birthdate) { if (release && release.date && curatedActor.birthdate) {

View File

@ -349,12 +349,13 @@ exports.up = knex => Promise.resolve()
.defaultTo(1); .defaultTo(1);
table.string('real_name'); table.string('real_name');
table.string('gender', 18);
table.date('date_of_birth'); table.date('date_of_birth');
table.date('date_of_death'); table.date('date_of_death');
table.string('gender', 18);
table.text('description'); table.text('description');
table.string('description_hash');
table.string('birth_city'); table.string('birth_city');
table.string('birth_state'); table.string('birth_state');

5
package-lock.json generated
View File

@ -3401,6 +3401,11 @@
"domelementtype": "1" "domelementtype": "1"
} }
}, },
"dompurify": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.11.tgz",
"integrity": "sha512-qVoGPjIW9IqxRij7klDQQ2j6nSe4UNWANBhZNLnsS7ScTtLb+3YdxkRY8brNTpkUiTtcXsCJO+jS0UCDfenLuA=="
},
"domutils": { "domutils": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",

View File

@ -89,6 +89,7 @@
"config": "^3.2.5", "config": "^3.2.5",
"csv-stringify": "^5.3.6", "csv-stringify": "^5.3.6",
"dayjs": "^1.8.21", "dayjs": "^1.8.21",
"dompurify": "^2.0.11",
"ejs": "^3.0.1", "ejs": "^3.0.1",
"express": "^4.17.1", "express": "^4.17.1",
"express-promise-router": "^3.0.3", "express-promise-router": "^3.0.3",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 KiB

After

Width:  |  Height:  |  Size: 689 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -22,6 +22,7 @@ const tagPosters = [
['deepthroat', 0, 'Chanel Grey in "Deepthroating Is Fun" for Throated'], ['deepthroat', 0, 'Chanel Grey in "Deepthroating Is Fun" for Throated'],
['double-anal', 7, 'Adriana Chechik in "DP Masters 6" for Jules Jordan'], ['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-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-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-penetration', 2, 'Megan Rain in "DP Masters 4" for Jules Jordan'],
['double-vaginal', 'poster', 'Riley Reid in "Pizza That Ass" for Reid My Lips'], ['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'], ['facial', 0, 'Brooklyn Gray in "All About Ass 4" for Evil Angel'],
['fake-boobs', 1, 'Lela Star in "Thick" for Jules Jordan'], ['fake-boobs', 1, 'Lela Star in "Thick" for Jules Jordan'],
['family', 0, 'Teanna Trump in "A Family Appear: Part One" for Brazzers'], ['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'], ['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'], ['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'], ['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'], ['dv-tp', 0, 'Luna Rival in LegalPorno SZ1490'],
['facial', 1, 'Ella Knox in "Mr Saltys Adult Emporium Adventure 2" for Aziani'], ['facial', 1, 'Ella Knox in "Mr Saltys Adult Emporium Adventure 2" for Aziani'],
['facial', 'poster', 'Jynx Maze'], ['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'], ['facefucking', 1, 'Carrie for Young Throats'],
// ['fake-boobs', 0, 'Marsha May in "Once You Go Black 7" for Jules Jordan'], // ['fake-boobs', 0, 'Marsha May in "Once You Go Black 7" for Jules Jordan'],
['gangbang', 'poster', 'Kristen Scott in "Interracial Gangbang!" for Jules Jordan'], ['gangbang', 'poster', 'Kristen Scott in "Interracial Gangbang!" for Jules Jordan'],

View File

@ -3,6 +3,12 @@
const config = require('config'); const config = require('config');
const Promise = require('bluebird'); const Promise = require('bluebird');
const moment = require('moment'); 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 logger = require('./logger')(__filename);
const knex = require('./knex'); const knex = require('./knex');
@ -148,6 +154,7 @@ function curateProfileEntry(profile) {
gender: profile.gender, gender: profile.gender,
ethnicity: profile.ethnicity, ethnicity: profile.ethnicity,
description: profile.description, description: profile.description,
description_hash: profile.descriptionHash,
birth_city: profile.placeOfBirth?.city || null, birth_city: profile.placeOfBirth?.city || null,
birth_state: profile.placeOfBirth?.state || null, birth_state: profile.placeOfBirth?.state || null,
birth_country_alpha2: profile.placeOfBirth?.country || null, birth_country_alpha2: profile.placeOfBirth?.country || null,
@ -189,7 +196,14 @@ async function curateProfile(profile) {
update: profile.update, 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.nationality = profile.nationality?.trim() || null; // used to derive country when country not available
curatedProfile.ethnicity = ethnicities[profile.ethnicity?.trim().toLowerCase()] || null; curatedProfile.ethnicity = ethnicities[profile.ethnicity?.trim().toLowerCase()] || null;

View File

@ -39,9 +39,8 @@ function decodeId(id) {
.toString('hex'); .toString('hex');
} }
function scrapeScene(scene, site) { function scrapeScene(scene) {
const release = { const release = {
site,
entryId: scene.id, entryId: scene.id,
title: scene.name, title: scene.name,
description: scene.description, description: scene.description,
@ -80,11 +79,11 @@ function scrapeScene(scene, site) {
return release; return release;
} }
function scrapeAll(scenes, site) { function scrapeAll(scenes) {
return scenes.map(({ _source: scene }) => scrapeScene(scene, site)); 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`, { const res = await bhttp.post(`https://${clusterId}.us-east-1.aws.found.io/videos/video/_search`, {
size: 50, size: 50,
query: { 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, include) {
async function scrapeProfile(actor, site, include) {
const profile = {}; const profile = {};
profile.aliases = actor.aliases; 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 (actor.image) profile.avatar = `https://i.bang.com/pornstars/${actor.identifier}.jpg`;
if (include.releases) { if (include.releases) {
profile.releases = await fetchActorReleases(actor, site); profile.releases = await fetchActorReleases(actor);
} }
return profile; return profile;
@ -267,7 +265,7 @@ async function fetchLatest(site, page = 1) {
return scrapeAll(res.body.hits.hits, site); 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 encodedId = new URL(url).pathname.split('/')[2];
const entryId = decodeId(encodedId); 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`, { const res = await post(`https://${clusterId}.us-east-1.aws.found.io/actors/actor/_search`, {
size: 5, size: 5,
sort: [{ 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()); const actor = res.body.hits.hits.find(hit => hit._source.name.toLowerCase() === actorName.toLowerCase());
if (actor) { if (actor) {
return scrapeProfile(actor._source, site, include); return scrapeProfile(actor._source, include);
} }
return null; return null;

View File

@ -91,8 +91,8 @@ async function fetchClassicProfile(actorName, { site }) {
} }
async function networkFetchProfile(actorName, context, include) { async function networkFetchProfile(actorName, context, include) {
const profile = await ((context.site.parameters.api && fetchApiProfile(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 || (context.site.parameters?.classic && include.scenes && fetchClassicProfile(actorName, context, include)) // classic profiles only have scenes, no bio
|| fetchProfile(actorName, context, true, getActorReleasesUrl, include)); || fetchProfile(actorName, context, true, getActorReleasesUrl, include));
return profile; return profile;