Fixed countries seed file. Updated MOFOS scraper. Improved Reality Kings scraper. Limiting photos for XEmpire scraper.

This commit is contained in:
ThePendulum 2019-11-27 04:58:38 +01:00
parent de36ed97e4
commit d113123778
61 changed files with 2182 additions and 2005 deletions

View File

@ -6,20 +6,16 @@
<FilterBar :fetch-releases="fetchReleases" /> <FilterBar :fetch-releases="fetchReleases" />
<div class="header"> <div class="header">
<h2 class="title">{{ actor.name }}</h2> <img
</div> v-if="actor.avatars && actor.avatars.length > 0"
:src="`/media/${actor.avatars[0].path}`"
class="avatar"
>
<ul class="bio sidebar nolist">
<h2 class="title">{{ actor.name }}</h2>
<div class="content-inner">
<div class="avatars">
<img
v-for="avatar in actor.avatars"
:key="`avatar-${avatar.id}`"
:src="`/media/${avatar.path}`"
class="avatar"
>
</div>
<ul class="bio">
<li v-if="actor.aliases.length"> <li v-if="actor.aliases.length">
<dfn class="bio-heading">Also known as</dfn> <dfn class="bio-heading">Also known as</dfn>
<span>{{ actor.aliases.join(', ') }}</span> <span>{{ actor.aliases.join(', ') }}</span>
@ -78,12 +74,31 @@
<li v-if="actor.social && actor.social.length > 0"> <li v-if="actor.social && actor.social.length > 0">
<dfn class="bio-heading">Social</dfn> <dfn class="bio-heading">Social</dfn>
{{ actor.social.join(',') }} <a
v-for="social in actor.social"
:key="`social-${social.id}`"
:href="social.url"
target="_blank"
rel="noopener noreferrer"
class="social"
>{{ social.platform || social.url }}</a>
</li> </li>
<li class="description">{{ actor.description }}</li>
<div class="photos">
<img
v-for="avatar in actor.avatars.slice(1)"
:key="`avatar-${avatar.id}`"
:src="`/media/${avatar.path}`"
class="photo"
>
</div>
</ul> </ul>
<span class="description">{{ actor.description }}</span> </div>
<div class="content-inner">
<Releases <Releases
:releases="releases" :releases="releases"
:context="actor.name" :context="actor.name"
@ -170,13 +185,18 @@ export default {
padding: 1rem; padding: 1rem;
} }
.avatars {
padding: 1rem;
}
.avatar { .avatar {
height: 20rem; height: 20rem;
margin: 0 1rem 0 0; margin: 0 1rem 0 0;
display: block;
}
.photo {
height: 10rem;
}
.social {
display: block;
} }
.flag { .flag {
@ -184,4 +204,8 @@ export default {
border: solid 1px $shadow-weak; border: solid 1px $shadow-weak;
margin: 0 .25rem 0 0; margin: 0 .25rem 0 0;
} }
.releases {
flex-grow: 1;
}
</style> </style>

View File

@ -226,6 +226,20 @@ exports.up = knex => Promise.resolve()
table.datetime('created_at') table.datetime('created_at')
.defaultTo(knex.fn.now()); .defaultTo(knex.fn.now());
})) }))
.then(() => knex.schema.createTable('social', (table) => {
table.increments('id', 16);
table.string('url');
table.string('platform');
table.string('domain');
table.integer('target_id', 16);
table.unique(['url', 'domain', 'target_id']);
table.datetime('created_at')
.defaultTo(knex.fn.now());
}))
.then(() => knex.schema.createTable('actors_associated', (table) => { .then(() => knex.schema.createTable('actors_associated', (table) => {
table.increments('id', 16); table.increments('id', 16);
@ -281,6 +295,7 @@ exports.down = knex => Promise.resolve()
.then(() => knex.schema.dropTable('tags')) .then(() => knex.schema.dropTable('tags'))
.then(() => knex.schema.dropTable('tags_groups')) .then(() => knex.schema.dropTable('tags_groups'))
.then(() => knex.schema.dropTable('media')) .then(() => knex.schema.dropTable('media'))
.then(() => knex.schema.dropTable('social'))
.then(() => knex.schema.dropTable('actors')) .then(() => knex.schema.dropTable('actors'))
.then(() => knex.schema.dropTable('releases')) .then(() => knex.schema.dropTable('releases'))
.then(() => knex.schema.dropTable('sites')) .then(() => knex.schema.dropTable('sites'))

View File

@ -599,18 +599,25 @@
.description[data-v-677a8360] { .description[data-v-677a8360] {
padding: 1rem; padding: 1rem;
} }
.avatars[data-v-677a8360] {
padding: 1rem;
}
.avatar[data-v-677a8360] { .avatar[data-v-677a8360] {
height: 20rem; height: 20rem;
margin: 0 1rem 0 0; margin: 0 1rem 0 0;
display: block;
}
.photo[data-v-677a8360] {
height: 10rem;
}
.social[data-v-677a8360] {
display: block;
} }
.flag[data-v-677a8360] { .flag[data-v-677a8360] {
height: 1rem; height: 1rem;
border: solid 1px rgba(0, 0, 0, 0.2); border: solid 1px rgba(0, 0, 0, 0.2);
margin: 0 .25rem 0 0; margin: 0 .25rem 0 0;
} }
.releases[data-v-677a8360] {
flex-grow: 1;
}
/* $primary: #ff886c; */ /* $primary: #ff886c; */
.header[data-v-80991bcc] { .header[data-v-80991bcc] {

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -2,120 +2,120 @@
const upsert = require('../src/utils/upsert'); const upsert = require('../src/utils/upsert');
const networks = [ const networks = [
{ {
slug: '21sextury', slug: '21sextury',
name: '21Sextury', name: '21Sextury',
url: 'https://www.21sextury.com', url: 'https://www.21sextury.com',
description: 'Watch all the latest scenes and porn video updates on 21Sextury.com, the best European porn site with the hottest pornstars from all over the world! Watch porn videos from the large network here.', description: 'Watch all the latest scenes and porn video updates on 21Sextury.com, the best European porn site with the hottest pornstars from all over the world! Watch porn videos from the large network here.',
}, },
{ {
slug: 'bangbros', slug: 'bangbros',
name: 'Bang Bros', name: 'Bang Bros',
url: 'https://bangbros.com', url: 'https://bangbros.com',
description: 'Here at Bang Bros, we only film the best highest quality porn with the sexiest Amateur girls and the top pornstars. Updated daily on Bangbros.com.', description: 'Here at Bang Bros, we only film the best highest quality porn with the sexiest Amateur girls and the top pornstars. Updated daily on Bangbros.com.',
}, },
{ {
slug: 'blowpass', slug: 'blowpass',
name: 'Blowpass', name: 'Blowpass',
url: 'https://www.blowpass.com', url: 'https://www.blowpass.com',
description: 'Welcome to Blowpass.com, your ultimate source for deepthroat porn, MILF and teen blowjob videos, big cumshots and any and everything oral!', description: 'Welcome to Blowpass.com, your ultimate source for deepthroat porn, MILF and teen blowjob videos, big cumshots and any and everything oral!',
}, },
{ {
slug: 'brazzers', slug: 'brazzers',
name: 'Brazzers', name: 'Brazzers',
url: 'https://www.brazzers.com', url: 'https://www.brazzers.com',
description: 'Brazzers homepage is updated daily with official HD porn scenes. Our hottest videos and sex series are filled with big tits, sexy milf, top pornstars and special events.', description: 'Brazzers homepage is updated daily with official HD porn scenes. Our hottest videos and sex series are filled with big tits, sexy milf, top pornstars and special events.',
}, },
{ {
slug: 'ddfnetwork', slug: 'ddfnetwork',
name: 'DDF Network', name: 'DDF Network',
url: 'https://ddfnetwork.com', url: 'https://ddfnetwork.com',
description: 'European porn videos hub with exclusive VR, 4K and full HD XXX videos and hot sex photos of Europes finest porn star babes.', description: 'European porn videos hub with exclusive VR, 4K and full HD XXX videos and hot sex photos of Europes finest porn star babes.',
}, },
{ {
slug: 'dogfartnetwork', slug: 'dogfartnetwork',
name: 'Dogfart Network', name: 'Dogfart Network',
url: 'https://dogfartnetwork.com', url: 'https://dogfartnetwork.com',
description: 'The world famous Dogfart Interracial series. Online since 1996, we have the largest collection of Interracial videos, pictures and content on the web.', description: 'The world famous Dogfart Interracial series. Online since 1996, we have the largest collection of Interracial videos, pictures and content on the web.',
parameters: JSON.stringify({ photoLimit: 25 }), parameters: JSON.stringify({ photoLimit: 25 }),
}, },
{ {
slug: 'evilangel', slug: 'evilangel',
name: 'Evil Angel', name: 'Evil Angel',
url: 'https://evilangel.com', url: 'https://evilangel.com',
description: 'Welcome to the award winning Evil Angel website, home to the most popular pornstars of today, yesterday and tomorrow in their most extreme and hardcore porn scenes to date. We feature almost 30 years of rough sex videos and hardcore anal porn like you\'ve never seen before, and have won countless AVN and XBiz awards including \'Best Site\' and \'Best Studio\'.', description: 'Welcome to the award winning Evil Angel website, home to the most popular pornstars of today, yesterday and tomorrow in their most extreme and hardcore porn scenes to date. We feature almost 30 years of rough sex videos and hardcore anal porn like you\'ve never seen before, and have won countless AVN and XBiz awards including \'Best Site\' and \'Best Studio\'.',
}, },
{ {
slug: 'julesjordan', slug: 'julesjordan',
name: 'Jules Jordan', name: 'Jules Jordan',
url: 'https://www.julesjordan.com', url: 'https://www.julesjordan.com',
}, },
{ {
slug: 'kink', slug: 'kink',
name: 'Kink', name: 'Kink',
url: 'https://www.kink.com', url: 'https://www.kink.com',
description: 'Authentic Bondage & Real BDSM Porn Videos. Demystifying and celebrating alternative sexuality by providing the most authentic kinky videos. Experience the other side of porn.', description: 'Authentic Bondage & Real BDSM Porn Videos. Demystifying and celebrating alternative sexuality by providing the most authentic kinky videos. Experience the other side of porn.',
}, },
{ {
slug: 'legalporno', slug: 'legalporno',
name: 'LegalPorno', name: 'LegalPorno',
url: 'https://www.legalporno.com', url: 'https://www.legalporno.com',
description: 'The Best HD Porn For You!', description: 'The Best HD Porn For You!',
}, },
{ {
slug: 'mikeadriano', slug: 'mikeadriano',
name: 'Mike Adriano', name: 'Mike Adriano',
url: null, url: null,
description: null, description: null,
}, },
{ {
slug: 'mofos', slug: 'mofos',
name: 'MOFOS', name: 'MOFOS',
url: 'https://www.mofos.com', url: 'https://www.mofos.com',
description: 'Check out the Official Mofos Network of best amateur pornsites. Girlfriend voyeur - college girls - first anal & more. Bonus Milf sites for wifey lovers.', description: 'Check out the Official Mofos Network of best amateur pornsites. Girlfriend voyeur - college girls - first anal & more. Bonus Milf sites for wifey lovers.',
}, },
{ {
slug: 'naughtyamerica', slug: 'naughtyamerica',
name: 'Naughty America', name: 'Naughty America',
url: 'https://www.naughtyamerica.com', url: 'https://www.naughtyamerica.com',
description: 'The best porn movies daily at Naughty America! Experience the most seductive porn stars in stunning virtual reality, 4K and HD porn videos!', description: 'The best porn movies daily at Naughty America! Experience the most seductive porn stars in stunning virtual reality, 4K and HD porn videos!',
}, },
{ {
slug: 'pervcity', slug: 'pervcity',
name: 'Perv City', name: 'Perv City',
url: 'https://www.pervcity.com', url: 'https://www.pervcity.com',
description: '', description: '',
}, },
{ {
slug: 'pornpros', slug: 'pornpros',
name: 'Porn Pros', name: 'Porn Pros',
url: 'https://pornpros.com', url: 'https://pornpros.com',
description: 'Watch the best HD exclusive movies and videos on Porn Pros. All the hottest new Pornstar and amateur girls in High Definition updated daily.', description: 'Watch the best HD exclusive movies and videos on Porn Pros. All the hottest new Pornstar and amateur girls in High Definition updated daily.',
}, },
{ {
slug: 'private', slug: 'private',
name: 'Private', name: 'Private',
url: 'https://www.private.com', url: 'https://www.private.com',
description: 'Private is the best source for adult movies and videos. Featuring the most popular hardcore adult stars in hundreds of porn movies, Private.com delivers...', description: 'Private is the best source for adult movies and videos. Featuring the most popular hardcore adult stars in hundreds of porn movies, Private.com delivers...',
}, },
{ {
slug: 'realitykings', slug: 'realitykings',
name: 'Reality Kings', name: 'Reality Kings',
url: 'https://www.realitykings.com', url: 'https://www.realitykings.com',
description: 'Home of HD reality porn featuring the nicest tits and ass online! The hottest curvy girls in real amateur sex stories are only on REALITYkings.com', description: 'Home of HD reality porn featuring the nicest tits and ass online! The hottest curvy girls in real amateur sex stories are only on REALITYkings.com',
}, },
{ {
slug: 'vixen', slug: 'vixen',
name: 'Vixen', name: 'Vixen',
url: 'https://www.vixen.com', url: 'https://www.vixen.com',
description: 'Vixen.com features the worlds finest cinematic adult films with 4K quality and high-end erotic photography.', description: 'Vixen.com features the worlds finest cinematic adult films with 4K quality and high-end erotic photography.',
}, },
{ {
slug: 'xempire', slug: 'xempire',
name: 'XEmpire', name: 'XEmpire',
url: 'https://www.xempire.com', url: 'https://www.xempire.com',
description: 'XEmpire.com brings you today\'s top pornstars in beautifully shot, HD sex scenes across 4 unique porn sites of gonzo porn, interracial, lesbian & erotica!', description: 'XEmpire.com brings you today\'s top pornstars in beautifully shot, HD sex scenes across 4 unique porn sites of gonzo porn, interracial, lesbian & erotica!',
}, },
]; ];
exports.seed = knex => Promise.resolve() exports.seed = knex => Promise.resolve()

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,3 @@
'use strict';
const upsert = require('../src/utils/upsert'); const upsert = require('../src/utils/upsert');
const groups = [ const groups = [
@ -61,7 +59,7 @@ function getTags(groupsMap) {
name: '69', name: '69',
slug: '69', slug: '69',
alias_for: null, alias_for: null,
group_id: groupsMap['position'], group_id: groupsMap.position,
}, },
{ {
name: 'airtight', name: 'airtight',
@ -69,7 +67,7 @@ function getTags(groupsMap) {
alias_for: null, alias_for: null,
description: 'A cock in every penetrable hole (of a woman); one in the mouth, one in the vagina, and one in the asshole.', description: 'A cock in every penetrable hole (of a woman); one in the mouth, one in the vagina, and one in the asshole.',
priority: 9, priority: 9,
group_id: groupsMap['penetration'], group_id: groupsMap.penetration,
}, },
{ {
name: 'amateur', name: 'amateur',
@ -80,13 +78,13 @@ function getTags(groupsMap) {
name: 'american', name: 'american',
slug: 'american', slug: 'american',
alias_for: null, alias_for: null,
group_id: groupsMap['ethnicity'], group_id: groupsMap.ethnicity,
}, },
{ {
name: 'anal creampie', name: 'anal creampie',
slug: 'anal-creampie', slug: 'anal-creampie',
alias_for: null, alias_for: null,
description: 'Ejaculating into the asshole.' description: 'Ejaculating into the asshole.',
}, },
{ {
name: 'anal', name: 'anal',
@ -120,13 +118,13 @@ function getTags(groupsMap) {
name: 'asian', name: 'asian',
slug: 'asian', slug: 'asian',
alias_for: null, alias_for: null,
group_id: groupsMap['ethnicity'], group_id: groupsMap.ethnicity,
}, },
{ {
name: 'athletic', name: 'athletic',
slug: 'athletic', slug: 'athletic',
alias_for: null, alias_for: null,
group_id: groupsMap['body'], group_id: groupsMap.body,
}, },
{ {
name: 'ass to mouth', name: 'ass to mouth',
@ -148,13 +146,13 @@ function getTags(groupsMap) {
name: 'ballerina', name: 'ballerina',
slug: 'ballerina', slug: 'ballerina',
alias_for: null, alias_for: null,
group_id: groupsMap['roleplay'], group_id: groupsMap.roleplay,
}, },
{ {
name: 'bathroom', name: 'bathroom',
slug: 'bathroom', slug: 'bathroom',
alias_for: null, alias_for: null,
group_id: groupsMap['location'], group_id: groupsMap.location,
}, },
{ {
name: 'BDSM', name: 'BDSM',
@ -165,25 +163,25 @@ function getTags(groupsMap) {
name: 'BBC', name: 'BBC',
slug: 'bbc', slug: 'bbc',
alias_for: null, alias_for: null,
group_id: groupsMap['body'], group_id: groupsMap.body,
}, },
{ {
name: 'big cock', name: 'big cock',
slug: 'big-cock', slug: 'big-cock',
alias_for: null, alias_for: null,
group_id: groupsMap['body'], group_id: groupsMap.body,
}, },
{ {
name: 'big butt', name: 'big butt',
slug: 'big-butt', slug: 'big-butt',
alias_for: null, alias_for: null,
group_id: groupsMap['body'], group_id: groupsMap.body,
}, },
{ {
name: 'big boobs', name: 'big boobs',
slug: 'big-boobs', slug: 'big-boobs',
alias_for: null, alias_for: null,
group_id: groupsMap['body'], group_id: groupsMap.body,
}, },
{ {
name: 'bisexual', name: 'bisexual',
@ -194,13 +192,13 @@ function getTags(groupsMap) {
name: 'black hair', name: 'black hair',
slug: 'black-hair', slug: 'black-hair',
alias_for: null, alias_for: null,
group_id: groupsMap['hair'], group_id: groupsMap.hair,
}, },
{ {
name: 'blonde', name: 'blonde',
slug: 'blonde', slug: 'blonde',
alias_for: null, alias_for: null,
group_id: groupsMap['hair'], group_id: groupsMap.hair,
}, },
{ {
name: 'blowjob', name: 'blowjob',
@ -211,7 +209,7 @@ function getTags(groupsMap) {
name: 'blowbang', name: 'blowbang',
slug: 'blowbang', slug: 'blowbang',
alias_for: null, alias_for: null,
group_id: groupsMap['group'], group_id: groupsMap.group,
}, },
{ {
name: 'bondage', name: 'bondage',
@ -222,7 +220,7 @@ function getTags(groupsMap) {
name: 'brunette', name: 'brunette',
slug: 'brunette', slug: 'brunette',
alias_for: null, alias_for: null,
group_id: groupsMap['hair'], group_id: groupsMap.hair,
}, },
{ {
name: 'bukkake', name: 'bukkake',
@ -233,7 +231,7 @@ function getTags(groupsMap) {
name: 'cheerleader', name: 'cheerleader',
slug: 'cheerleader', slug: 'cheerleader',
alias_for: null, alias_for: null,
group_id: groupsMap['roleplay'], group_id: groupsMap.roleplay,
}, },
{ {
name: 'choking', name: 'choking',
@ -325,13 +323,13 @@ function getTags(groupsMap) {
name: 'dress', name: 'dress',
slug: 'dress', slug: 'dress',
alias_for: null, alias_for: null,
group_id: groupsMap['clothing'], group_id: groupsMap.clothing,
}, },
{ {
name: 'ebony', name: 'ebony',
slug: 'ebony', slug: 'ebony',
alias_for: null, alias_for: null,
group_id: groupsMap['ethnicity'], group_id: groupsMap.ethnicity,
}, },
{ {
name: 'electric shock', name: 'electric shock',
@ -347,19 +345,19 @@ function getTags(groupsMap) {
name: 'European', name: 'European',
slug: 'european', slug: 'european',
alias_for: null, alias_for: null,
group_id: groupsMap['ethnicity'], group_id: groupsMap.ethnicity,
}, },
{ {
name: 'facefuck', name: 'facefuck',
slug: 'facefuck', slug: 'facefuck',
alias_for: null, alias_for: null,
group_id: groupsMap['position'], group_id: groupsMap.position,
}, },
{ {
name: 'facesitting', name: 'facesitting',
slug: 'facesitting', slug: 'facesitting',
alias_for: null, alias_for: null,
group_id: groupsMap['position'], group_id: groupsMap.position,
}, },
{ {
name: 'facial', name: 'facial',
@ -390,7 +388,7 @@ function getTags(groupsMap) {
name: 'FMF threesome', name: 'FMF threesome',
slug: 'fmf', slug: 'fmf',
alias_for: null, alias_for: null,
group_id: groupsMap['group'], group_id: groupsMap.group,
}, },
{ {
name: 'gag', name: 'gag',
@ -402,7 +400,7 @@ function getTags(groupsMap) {
slug: 'gangbang', slug: 'gangbang',
alias_for: null, alias_for: null,
priority: 9, priority: 9,
group_id: groupsMap['group'], group_id: groupsMap.group,
}, },
{ {
name: 'gapes', name: 'gapes',
@ -424,7 +422,7 @@ function getTags(groupsMap) {
name: 'hairy', name: 'hairy',
slug: 'hairy', slug: 'hairy',
alias_for: null, alias_for: null,
group_id: groupsMap['body'], group_id: groupsMap.body,
}, },
{ {
name: 'hardcore', name: 'hardcore',
@ -435,13 +433,13 @@ function getTags(groupsMap) {
name: 'high heels', name: 'high heels',
slug: 'high-heels', slug: 'high-heels',
alias_for: null, alias_for: null,
group_id: groupsMap['clothing'], group_id: groupsMap.clothing,
}, },
{ {
name: 'hungarian', name: 'hungarian',
slug: 'hungarian', slug: 'hungarian',
alias_for: null, alias_for: null,
group_id: groupsMap['ethnicity'], group_id: groupsMap.ethnicity,
}, },
{ {
name: 'humiliation', name: 'humiliation',
@ -493,13 +491,13 @@ function getTags(groupsMap) {
name: 'lingerie', name: 'lingerie',
slug: 'lingerie', slug: 'lingerie',
alias_for: null, alias_for: null,
group_id: groupsMap['clothing'], group_id: groupsMap.clothing,
}, },
{ {
name: 'maid', name: 'maid',
slug: 'maid', slug: 'maid',
alias_for: null, alias_for: null,
group_id: groupsMap['roleplay'], group_id: groupsMap.roleplay,
}, },
{ {
name: 'masturbation', name: 'masturbation',
@ -510,31 +508,31 @@ function getTags(groupsMap) {
name: 'MILF', name: 'MILF',
slug: 'milf', slug: 'milf',
alias_for: null, alias_for: null,
group_id: groupsMap['age'], group_id: groupsMap.age,
}, },
{ {
name: 'MFM threesome', name: 'MFM threesome',
slug: 'mfm', slug: 'mfm',
alias_for: null, alias_for: null,
group_id: groupsMap['group'], group_id: groupsMap.group,
}, },
{ {
name: 'miniskirt', name: 'miniskirt',
slug: 'miniskirt', slug: 'miniskirt',
alias_for: null, alias_for: null,
group_id: groupsMap['clothing'], group_id: groupsMap.clothing,
}, },
{ {
name: 'missionary', name: 'missionary',
slug: 'missionary', slug: 'missionary',
alias_for: null, alias_for: null,
group_id: groupsMap['position'], group_id: groupsMap.position,
}, },
{ {
name: 'natural boobs', name: 'natural boobs',
slug: 'natural-boobs', slug: 'natural-boobs',
alias_for: null, alias_for: null,
group_id: groupsMap['body'], group_id: groupsMap.body,
}, },
{ {
name: 'nipple clamps', name: 'nipple clamps',
@ -550,13 +548,13 @@ function getTags(groupsMap) {
name: 'orgy', name: 'orgy',
slug: 'orgy', slug: 'orgy',
alias_for: null, alias_for: null,
group_id: groupsMap['group'], group_id: groupsMap.group,
}, },
{ {
name: 'outdoors', name: 'outdoors',
slug: 'outdoors', slug: 'outdoors',
alias_for: null, alias_for: null,
group_id: groupsMap['location'], group_id: groupsMap.location,
}, },
{ {
name: 'outie pussy', name: 'outie pussy',
@ -597,7 +595,7 @@ function getTags(groupsMap) {
name: 'redhead', name: 'redhead',
slug: 'redhead', slug: 'redhead',
alias_for: null, alias_for: null,
group_id: groupsMap['hair'], group_id: groupsMap.hair,
}, },
{ {
name: 'reverse cowgirl', name: 'reverse cowgirl',
@ -618,7 +616,7 @@ function getTags(groupsMap) {
name: 'russian', name: 'russian',
slug: 'russian', slug: 'russian',
alias_for: null, alias_for: null,
group_id: groupsMap['ethnicity'], group_id: groupsMap.ethnicity,
}, },
{ {
name: 'saliva', name: 'saliva',
@ -629,7 +627,7 @@ function getTags(groupsMap) {
name: 'schoolgirl', name: 'schoolgirl',
slug: 'schoolgirl', slug: 'schoolgirl',
alias_for: null, alias_for: null,
group_id: groupsMap['roleplay'], group_id: groupsMap.roleplay,
}, },
{ {
name: 'shaved', name: 'shaved',
@ -640,19 +638,19 @@ function getTags(groupsMap) {
name: 'shoes on', name: 'shoes on',
slug: 'shoes-on', slug: 'shoes-on',
alias_for: null, alias_for: null,
group_id: groupsMap['clothing'], group_id: groupsMap.clothing,
}, },
{ {
name: 'short hair', name: 'short hair',
slug: 'short-hair', slug: 'short-hair',
alias_for: null, alias_for: null,
group_id: groupsMap['hair'], group_id: groupsMap.hair,
}, },
{ {
name: 'skirt', name: 'skirt',
slug: 'skirt', slug: 'skirt',
alias_for: null, alias_for: null,
group_id: groupsMap['clothing'], group_id: groupsMap.clothing,
}, },
{ {
name: 'slapping', name: 'slapping',
@ -668,7 +666,7 @@ function getTags(groupsMap) {
name: 'socks', name: 'socks',
slug: 'socks', slug: 'socks',
alias_for: null, alias_for: null,
group_id: groupsMap['clothing'], group_id: groupsMap.clothing,
}, },
{ {
name: 'spanking', name: 'spanking',
@ -679,7 +677,7 @@ function getTags(groupsMap) {
name: 'spooning', name: 'spooning',
slug: 'spooning', slug: 'spooning',
alias_for: null, alias_for: null,
group_id: groupsMap['position'], group_id: groupsMap.position,
}, },
{ {
name: 'strapon', name: 'strapon',
@ -715,7 +713,7 @@ function getTags(groupsMap) {
name: 'stockings', name: 'stockings',
slug: 'stockings', slug: 'stockings',
alias_for: null, alias_for: null,
group_id: groupsMap['clothing'], group_id: groupsMap.clothing,
}, },
{ {
name: 'strap-on dildo', name: 'strap-on dildo',
@ -736,19 +734,19 @@ function getTags(groupsMap) {
name: 'tattoo', name: 'tattoo',
slug: 'tattoo', slug: 'tattoo',
alias_for: null, alias_for: null,
group_id: groupsMap['body'], group_id: groupsMap.body,
}, },
{ {
name: 'threesome', name: 'threesome',
slug: 'threesome', slug: 'threesome',
alias_for: null, alias_for: null,
group_id: groupsMap['group'], group_id: groupsMap.group,
}, },
{ {
name: 'teen', name: 'teen',
slug: 'teen', slug: 'teen',
alias_for: null, alias_for: null,
group_id: groupsMap['age'], group_id: groupsMap.age,
}, },
{ {
name: 'titty fuck', name: 'titty fuck',
@ -800,7 +798,7 @@ function getTags(groupsMap) {
name: 'white', name: 'white',
slug: 'white', slug: 'white',
alias_for: null, alias_for: null,
group_id: groupsMap['ethnicity'], group_id: groupsMap.ethnicity,
}, },
{ {
name: 'wife', name: 'wife',
@ -811,7 +809,7 @@ function getTags(groupsMap) {
name: 'office', name: 'office',
slug: 'office', slug: 'office',
alias_for: null, alias_for: null,
group_id: groupsMap['location'], group_id: groupsMap.location,
}, },
]; ];
} }
@ -820,23 +818,23 @@ function getTagAliases(tagsMap) {
return [ return [
{ {
name: '2-on-1', name: '2-on-1',
alias_for: tagsMap['threesome'], alias_for: tagsMap.threesome,
}, },
{ {
name: '2 on 1', name: '2 on 1',
alias_for: tagsMap['threesome'], alias_for: tagsMap.threesome,
}, },
{ {
name: '3+ on 1', name: '3+ on 1',
alias_for: tagsMap['gangbang'], alias_for: tagsMap.gangbang,
}, },
{ {
name: 'anal sex', name: 'anal sex',
alias_for: tagsMap['anal'], alias_for: tagsMap.anal,
}, },
{ {
name: 'anal gape', name: 'anal gape',
alias_for: tagsMap['gapes'], alias_for: tagsMap.gapes,
}, },
{ {
name: 'anilingus', name: 'anilingus',
@ -844,7 +842,7 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'asians', name: 'asians',
alias_for: tagsMap['asian'], alias_for: tagsMap.asian,
}, },
{ {
name: 'anal fingering', name: 'anal fingering',
@ -856,7 +854,7 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'ass fucking', name: 'ass fucking',
alias_for: tagsMap['anal'], alias_for: tagsMap.anal,
}, },
{ {
name: 'atm', name: 'atm',
@ -864,11 +862,11 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'bald pussy', name: 'bald pussy',
alias_for: tagsMap['shaved'], alias_for: tagsMap.shaved,
}, },
{ {
name: 'ball gag', name: 'ball gag',
alias_for: tagsMap['gag'], alias_for: tagsMap.gag,
}, },
{ {
name: 'boob fucking', name: 'boob fucking',
@ -876,19 +874,19 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'mfm', name: 'mfm',
alias_for: tagsMap['mfm'], alias_for: tagsMap.mfm,
}, },
{ {
name: 'fmf', name: 'fmf',
alias_for: tagsMap['fmf'], alias_for: tagsMap.fmf,
}, },
{ {
name: 'ffm', name: 'ffm',
alias_for: tagsMap['fmf'], alias_for: tagsMap.fmf,
}, },
{ {
name: 'bgb', name: 'bgb',
alias_for: tagsMap['mfm'], alias_for: tagsMap.mfm,
}, },
{ {
name: 'big ass', name: 'big ass',
@ -896,11 +894,11 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'big black cock', name: 'big black cock',
alias_for: tagsMap['bbc'], alias_for: tagsMap.bbc,
}, },
{ {
name: 'big black cocks', name: 'big black cocks',
alias_for: tagsMap['bbc'], alias_for: tagsMap.bbc,
}, },
{ {
name: 'big cocks', name: 'big cocks',
@ -924,39 +922,47 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'bi', name: 'bi',
alias_for: tagsMap['bisexual'], alias_for: tagsMap.bisexual,
}, },
{ {
name: 'black', name: 'black',
alias_for: tagsMap['ebony'], alias_for: tagsMap.ebony,
}, },
{ {
name: 'blonde hair', name: 'blonde hair',
alias_for: tagsMap['blonde'], alias_for: tagsMap.blonde,
}, },
{ {
name: 'blondes', name: 'blondes',
alias_for: tagsMap['blonde'], alias_for: tagsMap.blonde,
}, },
{ {
name: 'blow job', name: 'blow job',
alias_for: tagsMap['blowjob'], alias_for: tagsMap.blowjob,
}, },
{ {
name: 'blowjobs', name: 'blowjobs',
alias_for: tagsMap['blowjob'], alias_for: tagsMap.blowjob,
}, },
{ {
name: 'blowjob pov', name: 'blowjob pov',
alias_for: tagsMap['blowjob'], alias_for: tagsMap.blowjob,
}, },
{ {
name: 'blowjob (double)', name: 'blowjob (double)',
alias_for: tagsMap['double-blowjob'], alias_for: tagsMap['double-blowjob'],
}, },
{
name: 'blowjob - double',
alias_for: tagsMap['double-blowjob'],
},
{ {
name: 'blowjob (pov)', name: 'blowjob (pov)',
alias_for: tagsMap['blowjob'], alias_for: tagsMap.blowjob,
},
{
name: 'blowjob - pov',
alias_for: tagsMap.blowjob,
}, },
{ {
name: 'boob job', name: 'boob job',
@ -968,11 +974,11 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'brown hair', name: 'brown hair',
alias_for: tagsMap['brunette'], alias_for: tagsMap.brunette,
}, },
{ {
name: 'brunettes', name: 'brunettes',
alias_for: tagsMap['brunette'], alias_for: tagsMap.brunette,
}, },
{ {
name: 'buttplug', name: 'buttplug',
@ -992,7 +998,7 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'cheer leader', name: 'cheer leader',
alias_for: tagsMap['cheerleader'], alias_for: tagsMap.cheerleader,
}, },
{ {
name: 'clover clamps', name: 'clover clamps',
@ -1000,11 +1006,11 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'couples fantasies', name: 'couples fantasies',
alias_for: tagsMap['couples'], alias_for: tagsMap.couples,
}, },
{ {
name: 'creampies', name: 'creampies',
alias_for: tagsMap['creampie'], alias_for: tagsMap.creampie,
}, },
{ {
name: 'crop', // a type of whip, not [sic] short for corporal name: 'crop', // a type of whip, not [sic] short for corporal
@ -1028,11 +1034,11 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'cum swallowing', name: 'cum swallowing',
alias_for: tagsMap['swallowing'], alias_for: tagsMap.swallowing,
}, },
{ {
name: 'cum shot', name: 'cum shot',
alias_for: tagsMap['cumshot'], alias_for: tagsMap.cumshot,
}, },
{ {
name: 'cunnilingus', name: 'cunnilingus',
@ -1044,15 +1050,15 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'deep throat', name: 'deep throat',
alias_for: tagsMap['deepthroat'], alias_for: tagsMap.deepthroat,
}, },
{ {
name: 'deepthroating', name: 'deepthroating',
alias_for: tagsMap['deepthroat'], alias_for: tagsMap.deepthroat,
}, },
{ {
name: 'dildo', name: 'dildo',
alias_for: tagsMap['toys'], alias_for: tagsMap.toys,
}, },
{ {
name: 'doggystyle', name: 'doggystyle',
@ -1072,15 +1078,15 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'dom', name: 'dom',
alias_for: tagsMap['bdsm'], alias_for: tagsMap.bdsm,
}, },
{ {
name: 'domination', name: 'domination',
alias_for: tagsMap['bdsm'], alias_for: tagsMap.bdsm,
}, },
{ {
name: 'dominatrix', name: 'dominatrix',
alias_for: tagsMap['femdom'], alias_for: tagsMap.femdom,
}, },
{ {
name: 'dp', name: 'dp',
@ -1090,6 +1096,10 @@ function getTagAliases(tagsMap) {
name: 'double penetration (dp)', name: 'double penetration (dp)',
alias_for: tagsMap['double-penetration'], alias_for: tagsMap['double-penetration'],
}, },
{
name: 'double penetration - dp',
alias_for: tagsMap['double-penetration'],
},
{ {
name: 'dap', name: 'dap',
alias_for: tagsMap['double-anal'], alias_for: tagsMap['double-anal'],
@ -1132,7 +1142,7 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'drool', name: 'drool',
alias_for: tagsMap['saliva'], alias_for: tagsMap.saliva,
}, },
{ {
name: 'enhanced', name: 'enhanced',
@ -1144,27 +1154,27 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'facefucking', name: 'facefucking',
alias_for: tagsMap['facefuck'], alias_for: tagsMap.facefuck,
}, },
{ {
name: 'face fuck', name: 'face fuck',
alias_for: tagsMap['facefuck'], alias_for: tagsMap.facefuck,
}, },
{ {
name: 'face fucking', name: 'face fucking',
alias_for: tagsMap['facefuck'], alias_for: tagsMap.facefuck,
}, },
{ {
name: 'face sitting', name: 'face sitting',
alias_for: tagsMap['facesitting'], alias_for: tagsMap.facesitting,
}, },
{ {
name: 'facial cumshot', name: 'facial cumshot',
alias_for: tagsMap['facial'], alias_for: tagsMap.facial,
}, },
{ {
name: 'facials', name: 'facials',
alias_for: tagsMap['facial'], alias_for: tagsMap.facial,
}, },
{ {
name: 'fake boobs', name: 'fake boobs',
@ -1180,27 +1190,27 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'foot fetish', name: 'foot fetish',
alias_for: tagsMap['feet'], alias_for: tagsMap.feet,
}, },
{ {
name: 'french kissing', name: 'french kissing',
alias_for: tagsMap['kissing'], alias_for: tagsMap.kissing,
}, },
{ {
name: 'gape', name: 'gape',
alias_for: tagsMap['gapes'], alias_for: tagsMap.gapes,
}, },
{ {
name: 'gaping', name: 'gaping',
alias_for: tagsMap['gapes'], alias_for: tagsMap.gapes,
}, },
{ {
name: 'gapes (gaping asshole)', name: 'gapes (gaping asshole)',
alias_for: tagsMap['gapes'], alias_for: tagsMap.gapes,
}, },
{ {
name: 'group sex', name: 'group sex',
alias_for: tagsMap['orgy'], alias_for: tagsMap.orgy,
}, },
{ {
name: 'flagellation', name: 'flagellation',
@ -1212,7 +1222,7 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'huge toys', name: 'huge toys',
alias_for: tagsMap['toys'], alias_for: tagsMap.toys,
}, },
{ {
name: 'innie', name: 'innie',
@ -1224,23 +1234,23 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'lezdom', name: 'lezdom',
alias_for: tagsMap['lesbian'], alias_for: tagsMap.lesbian,
}, },
{ {
name: 'mini-skirt', name: 'mini-skirt',
alias_for: tagsMap['miniskirt'], alias_for: tagsMap.miniskirt,
}, },
{ {
name: 'mmf', name: 'mmf',
alias_for: tagsMap['mfm'], alias_for: tagsMap.mfm,
}, },
{ {
name: 'mff', name: 'mff',
alias_for: tagsMap['fmf'], alias_for: tagsMap.fmf,
}, },
{ {
name: 'mature & milf', name: 'mature & milf',
alias_for: tagsMap['milf'], alias_for: tagsMap.milf,
}, },
{ {
name: 'natural', name: 'natural',
@ -1252,7 +1262,7 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'oral', name: 'oral',
alias_for: tagsMap['blowjob'], alias_for: tagsMap.blowjob,
}, },
{ {
name: 'outie', name: 'outie',
@ -1260,11 +1270,11 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'piercing', name: 'piercing',
alias_for: tagsMap['piercings'], alias_for: tagsMap.piercings,
}, },
{ {
name: 'pierced', name: 'pierced',
alias_for: tagsMap['piercings'], alias_for: tagsMap.piercings,
}, },
{ {
name: 'prolapse', name: 'prolapse',
@ -1284,11 +1294,11 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'red hair', name: 'red hair',
alias_for: tagsMap['redhead'], alias_for: tagsMap.redhead,
}, },
{ {
name: 'red head', name: 'red head',
alias_for: tagsMap['redhead'], alias_for: tagsMap.redhead,
}, },
{ {
name: 'rimming', name: 'rimming',
@ -1300,39 +1310,39 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'role play', name: 'role play',
alias_for: tagsMap['roleplay'], alias_for: tagsMap.roleplay,
}, },
{ {
name: 'rope bondage', name: 'rope bondage',
alias_for: tagsMap['bondage'], alias_for: tagsMap.bondage,
}, },
{ {
name: 'rough sex', name: 'rough sex',
alias_for: tagsMap['rough'], alias_for: tagsMap.rough,
}, },
{ {
name: 'school girl', name: 'school girl',
alias_for: tagsMap['schoolgirl'], alias_for: tagsMap.schoolgirl,
}, },
{ {
name: 'sadomasochism', name: 'sadomasochism',
alias_for: tagsMap['bdsm'], alias_for: tagsMap.bdsm,
}, },
{ {
name: 'sadism', name: 'sadism',
alias_for: tagsMap['bdsm'], alias_for: tagsMap.bdsm,
}, },
{ {
name: 'scissoring', name: 'scissoring',
alias_for: tagsMap['lesbian'], alias_for: tagsMap.lesbian,
}, },
{ {
name: 'sex toys', name: 'sex toys',
alias_for: tagsMap['toys'], alias_for: tagsMap.toys,
}, },
{ {
name: 'shaved pussy', name: 'shaved pussy',
alias_for: tagsMap['shaved'], alias_for: tagsMap.shaved,
}, },
{ {
name: 'shoes', name: 'shoes',
@ -1340,7 +1350,7 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'slave', name: 'slave',
alias_for: tagsMap['bdsm'], alias_for: tagsMap.bdsm,
}, },
{ {
name: 'small ass', name: 'small ass',
@ -1352,11 +1362,11 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'spit', name: 'spit',
alias_for: tagsMap['saliva'], alias_for: tagsMap.saliva,
}, },
{ {
name: 'spitroast', name: 'spitroast',
alias_for: tagsMap['mfm'], alias_for: tagsMap.mfm,
}, },
{ {
name: 'standing doggystyle', name: 'standing doggystyle',
@ -1364,7 +1374,7 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'swallow', name: 'swallow',
alias_for: tagsMap['swallowing'], alias_for: tagsMap.swallowing,
}, },
{ {
name: 'strap-on', name: 'strap-on',
@ -1380,19 +1390,19 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'sub', name: 'sub',
alias_for: tagsMap['bdsm'], alias_for: tagsMap.bdsm,
}, },
{ {
name: 'submission', name: 'submission',
alias_for: tagsMap['bdsm'], alias_for: tagsMap.bdsm,
}, },
{ {
name: 'tattoos', name: 'tattoos',
alias_for: tagsMap['tattoo'], alias_for: tagsMap.tattoo,
}, },
{ {
name: 'teens', name: 'teens',
alias_for: tagsMap['teen'], alias_for: tagsMap.teen,
}, },
{ {
name: 'tiny boobs', name: 'tiny boobs',
@ -1412,15 +1422,15 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'trans', name: 'trans',
alias_for: tagsMap['transsexual'], alias_for: tagsMap.transsexual,
}, },
{ {
name: 'trimmed pussy', name: 'trimmed pussy',
alias_for: tagsMap['trimmed'], alias_for: tagsMap.trimmed,
}, },
{ {
name: 'ts', name: 'ts',
alias_for: tagsMap['transsexual'], alias_for: tagsMap.transsexual,
}, },
{ {
name: 'whipping', name: 'whipping',
@ -1428,11 +1438,11 @@ function getTagAliases(tagsMap) {
}, },
{ {
name: 'work', name: 'work',
alias_for: tagsMap['office'], alias_for: tagsMap.office,
}, },
{ {
name: 'workplace', name: 'workplace',
alias_for: tagsMap['office'], alias_for: tagsMap.office,
}, },
{ {
name: 'zapper', name: 'zapper',

File diff suppressed because it is too large Load Diff

View File

@ -9,11 +9,13 @@ const whereOr = require('./utils/where-or');
const { createActorMediaDirectory, storeAvatars } = require('./media'); const { createActorMediaDirectory, storeAvatars } = require('./media');
async function curateActor(actor) { async function curateActor(actor) {
const [aliases, avatars] = await Promise.all([ const [aliases, avatars, social] = await Promise.all([
knex('actors').where({ alias_for: actor.id }), knex('actors').where({ alias_for: actor.id }),
knex('media') knex('media')
.where({ domain: 'actors', target_id: actor.id }) .where({ domain: 'actors', target_id: actor.id })
.orderBy('index'), .orderBy('index'),
knex('social')
.where({ domain: 'actors', target_id: actor.id }),
]); ]);
return { return {
@ -46,6 +48,7 @@ async function curateActor(actor) {
aliases: aliases.map(({ name }) => name), aliases: aliases.map(({ name }) => name),
slug: actor.slug, slug: actor.slug,
avatars, avatars,
social,
}; };
} }
@ -94,6 +97,35 @@ function curateActorEntry(actor, scraped, scrapeSuccess) {
return curatedActor; return curatedActor;
} }
function curateSocialEntry(url, actor) {
const { hostname, origin, pathname } = new URL(url);
const platform = ['twitter', 'instagram', 'snapchat', 'modelhub', 'youtube'].find(platformName => hostname.match(platformName));
return {
url: `${origin}${pathname}`,
platform,
domain: 'actors',
target_id: actor.id,
};
}
function curateSocialEntries(urls, actor) {
if (!urls) {
return [];
}
return urls.reduce((acc, url) => {
const socialEntry = curateSocialEntry(url, actor);
if (acc.some(entry => socialEntry.url === entry.url)) {
// prevent duplicates
return acc;
}
return [...acc, socialEntry];
}, []);
}
async function fetchActors(queryObject) { async function fetchActors(queryObject) {
const releases = await knex('actors') const releases = await knex('actors')
.select( .select(
@ -112,34 +144,30 @@ async function fetchActors(queryObject) {
async function storeActor(actor, scraped = false, scrapeSuccess = false) { async function storeActor(actor, scraped = false, scrapeSuccess = false) {
const curatedActor = curateActorEntry(actor, scraped, scrapeSuccess); const curatedActor = curateActorEntry(actor, scraped, scrapeSuccess);
const actorEntries = await knex('actors') const [actorEntry] = await knex('actors')
.insert(curatedActor) .insert(curatedActor)
.returning('*'); .returning('*');
if (actorEntries.length) { await knex('social').insert(curateSocialEntries(actor.social, actor));
const actorEntry = actorEntries[0];
console.log(`Added new entry for actor '${actor.name}'`); console.log(`Added new entry for actor '${actor.name}'`);
return actorEntry; return actorEntry;
}
console.error(`Unable to save profile for '${actor.name}'`);
return null;
} }
async function updateActor(actor, scraped = false, scrapeSuccess = false) { async function updateActor(actor, scraped = false, scrapeSuccess = false) {
const curatedActor = curateActorEntry(actor, scraped, scrapeSuccess); const curatedActor = curateActorEntry(actor, scraped, scrapeSuccess);
const actorEntries = await knex('actors') const [actorEntry] = await knex('actors')
.where({ id: actor.id }) .where({ id: actor.id })
.update(curatedActor) .update(curatedActor)
.returning('*'); .returning('*');
await knex('social').insert(curateSocialEntries(actor.social, actor));
console.log(`Updated entry for actor '${actor.name}'`); console.log(`Updated entry for actor '${actor.name}'`);
return actorEntries[0]; return actorEntry;
} }
function mergeProfiles(profiles, actor) { function mergeProfiles(profiles, actor) {
@ -177,7 +205,6 @@ function mergeProfiles(profiles, actor) {
}, { }, {
social: [], social: [],
avatars: [], avatars: [],
...actor,
}); });
} }

View File

@ -148,7 +148,7 @@ function scrapeProfile(html, url, actorName) {
}; };
if (bio.Ethnicity) profile.ethnicity = bio.Ethnicity; if (bio.Ethnicity) profile.ethnicity = bio.Ethnicity;
if (bio.Measurements && bio.Measurements.match(/\w+-\d+-\d+/)) [profile.bust, profile.waist, profile.hip] = bio.Measurements.split('-'); if (bio.Measurements && bio.Measurements.match(/\d+[A-Z]+-\d+-\d+/)) [profile.bust, profile.waist, profile.hip] = bio.Measurements.split('-');
if (bio['Date of Birth'] && bio['Date of Birth'] !== 'Unknown') profile.birthdate = moment.utc(bio['Date of Birth'], 'MMMM DD, YYYY').toDate(); if (bio['Date of Birth'] && bio['Date of Birth'] !== 'Unknown') profile.birthdate = moment.utc(bio['Date of Birth'], 'MMMM DD, YYYY').toDate();
if (bio['Birth Location']) profile.birthPlace = bio['Birth Location']; if (bio['Birth Location']) profile.birthPlace = bio['Birth Location'];
if (bio['Pussy Type']) profile.pussy = bio['Pussy Type'].split(',').slice(-1)[0].toLowerCase(); if (bio['Pussy Type']) profile.pussy = bio['Pussy Type'].split(',').slice(-1)[0].toLowerCase();

View File

@ -1,67 +1,84 @@
'use strict'; 'use strict';
const Promise = require('bluebird');
const bhttp = require('bhttp'); const bhttp = require('bhttp');
const cheerio = require('cheerio'); const { CookieJar } = Promise.promisifyAll(require('tough-cookie'));
const moment = require('moment'); const moment = require('moment');
const knex = require('../knex'); const { fetchSites } = require('../sites');
const { cookieToData } = require('../utils/cookies');
const { matchTags } = require('../tags'); const { matchTags } = require('../tags');
function getThumbs(scene) {
if (scene.images.poster) {
return scene.images.poster.map(image => image.xl.url);
}
if (scene.images.card_main_rect) {
return scene.images.card_main_rect
.concat(scene.images.card_secondary_rect || [])
.map(image => image.xl.url.replace('.thumb', ''));
}
return [];
}
/* eslint-disable newline-per-chained-call */ /* eslint-disable newline-per-chained-call */
function scrape(html, site) { async function scrapeLatest(items, site) {
const $ = cheerio.load(html, { normalizeWhitespace: true }); return Promise.all(items.map(async (data) => {
const sceneElements = $('.widget-release-card').toArray(); const { id: entryId, title, description } = data;
const url = `https://www.mofos.com/scene/${entryId}/`;
const date = new Date(data.dateReleased);
const actors = data.actors.map(actor => actor.name);
return sceneElements.map((element) => { const rawTags = data.tags.map(tag => tag.name);
const sceneLinkElement = $(element).find('.title a'); const tags = await matchTags(rawTags);
const title = sceneLinkElement.text().trim(); const [poster, ...photos] = getThumbs(data);
const url = `https://www.mofos.com${sceneLinkElement.attr('href')}`; const trailer = data.videos.mediabook && (data.videos.mediabook.files['720p'] || data.videos.mediabook.files['320p']);
const entryId = url.split('/').slice(-2, -1)[0];
const date = moment.utc($(element).find('.date-added').text(), 'MMM DD, YYYY').toDate();
const actors = $(element).find('.girls-name a').map((actorIndex, actorElement) => $(actorElement).attr('title').replace(/\s+/g, ' ')).toArray();
const stars = Number($(element).find('.rating').text().slice(0, -1).trim()) / 20;
return { return {
url, url,
entryId, entryId,
title, title,
description,
actors, actors,
date, tags,
rating: { poster,
stars, photos,
trailer: {
src: trailer.urls.view,
quality: parseInt(trailer.format, 10),
}, },
date,
site, site,
}; };
}); }));
} }
async function scrapeScene(html, url, site) { async function scrapeScene(data, url, site) {
const $ = cheerio.load(html, { normalizeWhitespace: true }); const { id: entryId, title, description } = data;
const sceneElement = $('.video-info'); const date = new Date(data.dateReleased);
const actors = data.actors.map(actor => actor.name);
const entryId = url.split('/').slice(-2, -1)[0]; const rawTags = data.tags.map(tag => tag.name);
const title = sceneElement.find('.title').text();
const description = sceneElement.find('.desc').text();
const actors = sceneElement.find('.girls-site-box a.model-name').map((actorIndex, actorElement) => $(actorElement).text().trim()).toArray();
const siteElement = sceneElement.find('.site-name'); const [poster, ...photos] = getThumbs(data);
const sitename = siteElement.text().trim(); const trailer = data.videos.mediabook && (data.videos.mediabook.files['720p'] || data.videos.mediabook.files['320p']);
const siteId = sitename.replace(/\s+/g, '').toLowerCase();
const siteUrl = siteElement.attr('href').split('/').slice(0, 4).join('/');
const stars = Number(sceneElement.find('.rating-box .rating').text().slice(0, -1).trim()) / 20; const siteName = data.collections[0].name;
const siteId = data.collections[0].id;
const siteSlug = siteName.replace(/\s+/g, '').toLowerCase();
const siteUrl = `https://www.mofos.com/scenes?site=${siteId}`;
const rawTags = sceneElement.find('.categories a').map((tagIndex, tagElement) => $(tagElement).text().trim()).toArray(); const [[channelSite], tags] = await Promise.all([
site.isFallback
const [channelSite, tags] = await Promise.all([ ? fetchSites({
knex('sites') slug: siteSlug,
.where({ slug: siteId }) name: siteName,
.orWhere({ url: `https://www.mofos.com${siteUrl}` }) url: siteUrl,
.orWhere({ name: sitename }) })
.first(), : [site],
matchTags(rawTags), matchTags(rawTags),
]); ]);
@ -72,35 +89,61 @@ async function scrapeScene(html, url, site) {
description, description,
actors, actors,
tags, tags,
rating: { poster,
stars, photos,
trailer: {
src: trailer.urls.view,
quality: parseInt(trailer.format, 10),
}, },
site: channelSite || site, date,
site: channelSite,
}; };
} }
async function fetchLatest(site, page = 1) { async function fetchLatest(site, page = 1) {
const res = page > 1 const { search } = new URL(site.url);
? await bhttp.get(`${site.url}/all-models/all-categories/alltime/bydate/${page}/`) const siteId = new URLSearchParams(search).get('site');
: await bhttp.get(`${site.url}/all-models/all-categories/alltime/bydate/`); // explicit page 1 redirects to homepage
return scrape(res.body.toString(), site); const cookieJar = new CookieJar();
} const session = bhttp.session({ cookieJar });
async function fetchUpcoming(site) { await session.get(site.url);
const res = await bhttp.get(`${site.url}/all-models/all-categories/upcoming/bydate/`);
return scrape(res.body.toString(), site); const cookieString = await cookieJar.getCookieStringAsync(site.url);
const { instance_token: instanceToken } = cookieToData(cookieString);
const beforeDate = moment().add('1', 'day').format('YYYY-MM-DD');
const limit = 10;
const res = await session.get(`https://site-api.project1service.com/v2/releases?collectionId=${siteId}&dateReleased=<${beforeDate}&limit=${limit}&offset=${limit * (page - 1)}&orderBy=-dateReleased&type=scene`, {
headers: {
Instance: instanceToken,
},
});
return scrapeLatest(res.body.result, site);
} }
async function fetchScene(url, site) { async function fetchScene(url, site) {
const res = await bhttp.get(url); const entryId = url.match(/\d+/)[0];
return scrapeScene(res.body.toString(), url, site); const cookieJar = new CookieJar();
const session = bhttp.session({ cookieJar });
await session.get(url);
const cookieString = await cookieJar.getCookieStringAsync(url);
const { instance_token: instanceToken } = cookieToData(cookieString);
const res = await session.get(`https://site-api.project1service.com/v2/releases/${entryId}`, {
headers: {
Instance: instanceToken,
},
});
return scrapeScene(res.body.result, url, site);
} }
module.exports = { module.exports = {
fetchLatest, fetchLatest,
fetchUpcoming,
fetchScene, fetchScene,
}; };

View File

@ -59,7 +59,7 @@ async function scrapeProfile(html, _url, actorName) {
profile.residenceCountry = residenceCountryEntry ? residenceCountryEntry.alpha2 : null; profile.residenceCountry = residenceCountryEntry ? residenceCountryEntry.alpha2 : null;
} }
if (bio.Measurements && bio.Measurements !== '--') [profile.bust, profile.waist, profile.hip] = bio.Measurements.split('-'); if (bio.Measurements && bio.Measurements !== '--') [profile.bust, profile.waist, profile.hip] = bio.Measurements.split('-').map(measurement => parseInt(measurement, 10) || null);
if (bio['Fake Boobs']) profile.naturalBoobs = bio['Fake Boobs'] === 'No'; if (bio['Fake Boobs']) profile.naturalBoobs = bio['Fake Boobs'] === 'No';
if (bio.Height) profile.height = Number(bio.Height.match(/\(\d+/)[0].slice(1)); if (bio.Height) profile.height = Number(bio.Height.match(/\(\d+/)[0].slice(1));

View File

@ -4,8 +4,10 @@
const Promise = require('bluebird'); const Promise = require('bluebird');
const bhttp = require('bhttp'); const bhttp = require('bhttp');
const { CookieJar } = Promise.promisifyAll(require('tough-cookie')); const { CookieJar } = Promise.promisifyAll(require('tough-cookie'));
const { JSDOM } = require('jsdom'); const moment = require('moment');
const { fetchSites } = require('../sites');
const { cookieToData } = require('../utils/cookies');
const { matchTags } = require('../tags'); const { matchTags } = require('../tags');
function getThumbs(scene) { function getThumbs(scene) {
@ -22,40 +24,18 @@ function getThumbs(scene) {
return []; return [];
} }
async function scrapeLatest(html, site) { async function scrapeLatest(items, site) {
const { document } = new JSDOM(html).window; return Promise.all(items.map(async (data) => {
const { id: entryId, title, description } = data;
const scriptString = document.querySelector('script').textContent;
const prefix = 'window.__JUAN.initialState = {';
const dataStart = scriptString.slice(scriptString.indexOf(prefix) + prefix.length - 1);
const dataString = dataStart.slice(0, dataStart.indexOf('};') + 1);
const data = JSON.parse(dataString);
const actorsMap = data.entities.actors;
const tagsMap = data.entities.tags;
const scenes = Object.values(data.entities.releases);
return Promise.all(scenes.map(async (scene) => {
const {
id: entryId,
title,
description,
} = scene;
const url = `https://www.realitykings.com/scene/${entryId}/`; const url = `https://www.realitykings.com/scene/${entryId}/`;
const date = new Date(scene.dateReleased); const date = new Date(data.dateReleased);
const actors = scene.actors.map(actorId => actorsMap[actorId].name); const actors = data.actors.map(actor => actor.name);
const duration = scene.videos.mediabook && scene.videos.mediabook.length;
const rawTags = scene.tags.map(tagId => tagsMap[tagId].name); const rawTags = data.tags.map(tag => tag.name);
const tags = await matchTags(rawTags); const tags = await matchTags(rawTags);
const [poster, ...photos] = getThumbs(scene); const [poster, ...photos] = getThumbs(data);
const trailer720p = scene.videos.mediabook && scene.videos.mediabook.files['720p'] && scene.videos.mediabook.files['720p'].urls.view; const trailer = data.videos.mediabook && (data.videos.mediabook.files['720p'] || data.videos.mediabook.files['320p']);
const trailer360p = scene.videos.mediabook && scene.videos.mediabook.files['360p'] && scene.videos.mediabook.files['360p'].urls.view;
const { likes, dislikes } = scene.stats;
return { return {
url, url,
@ -63,41 +43,44 @@ async function scrapeLatest(html, site) {
title, title,
description, description,
actors, actors,
date,
tags, tags,
duration,
poster, poster,
photos, photos,
trailer: { trailer: trailer && {
src: trailer720p || trailer360p, src: trailer.urls.view,
quality: trailer720p ? 720 : 360, quality: parseInt(trailer.format, 10),
}, },
rating: { likes, dislikes }, date,
site, site,
}; };
})); }));
} }
async function scrapeScene(data, url, site) { async function scrapeScene(data, url, site) {
const { const { id: entryId, title, description } = data;
id: entryId,
title,
description,
} = data;
const date = new Date(data.dateReleased); const date = new Date(data.dateReleased);
const actors = data.actors.map(actor => actor.name); const actors = data.actors.map(actor => actor.name);
const { likes, dislikes } = data.stats;
const rawTags = data.tags.map(tag => tag.name); const rawTags = data.tags.map(tag => tag.name);
const tags = await matchTags(rawTags);
const [poster, ...photos] = getThumbs(data); const [poster, ...photos] = getThumbs(data);
const trailer = data.videos.mediabook && (data.videos.mediabook.files['720p'] || data.videos.mediabook.files['320p']);
const duration = data.videos.mediabook && data.videos.mediabook.length; const siteName = data.collections[0].name;
const trailer720p = data.videos.mediabook && data.videos.mediabook.files['720p'] && data.videos.mediabook.files['720p'].urls.view; const siteId = data.collections[0].id;
const trailer360p = data.videos.mediabook && data.videos.mediabook.files['360p'] && data.videos.mediabook.files['360p'].urls.view; const siteSlug = siteName.replace(/\s+/g, '').toLowerCase();
const siteUrl = `https://www.realitykings.com/scenes?site=${siteId}`;
const [[channelSite], tags] = await Promise.all([
site.isFallback
? fetchSites({
slug: siteSlug,
name: siteName,
url: siteUrl,
})
: [site],
matchTags(rawTags),
]);
return { return {
url, url,
@ -105,41 +88,56 @@ async function scrapeScene(data, url, site) {
title, title,
description, description,
actors, actors,
date,
duration,
tags, tags,
poster, poster,
photos, photos,
trailer: { trailer: trailer && {
src: trailer720p || trailer360p, src: trailer.urls.view,
quality: trailer720p ? 720 : 360, quality: parseInt(trailer.format, 10),
}, },
rating: { date,
likes, site: channelSite,
dislikes,
},
site,
}; };
} }
async function fetchLatest(site, page = 1) { function getUrl(site) {
const { hostname, search } = new URL(site.url); const { hostname, search } = new URL(site.url);
if (hostname.match(/(www\.)?realitykings\.com/) && search.match(/\?site=\d+/)) { if (hostname.match(/(www\.)?realitykings\.com/) && search.match(/\?site=\d+/)) {
const res = await bhttp.get(`${site.url}&page=${page}`); return site.url;
return scrapeLatest(res.body.toString(), site);
} }
if (site.parameters && site.parameters.siteId) { if (site.parameters && site.parameters.siteId) {
const res = await bhttp.get(`https://www.realitykings.com/scenes?site=${site.parameters.siteId}&page=${page}`); return `https://www.realitykings.com/scenes?site=${site.parameters.siteId}`;
return scrapeLatest(res.body.toString(), site);
} }
throw new Error(`Reality Kings site '${site.name}' (${site.url}) not supported`); throw new Error(`Reality Kings site '${site.name}' (${site.url}) not supported`);
} }
async function fetchLatest(site, page = 1) {
const url = getUrl(site);
const { search } = new URL(url);
const siteId = new URLSearchParams(search).get('site');
const cookieJar = new CookieJar();
const session = bhttp.session({ cookieJar });
await session.get(url);
const cookieString = await cookieJar.getCookieStringAsync(url);
const { instance_token: instanceToken } = cookieToData(cookieString);
const beforeDate = moment().add('1', 'day').format('YYYY-MM-DD');
const limit = 10;
const res = await session.get(`https://site-api.project1service.com/v2/releases?collectionId=${siteId}&dateReleased=<${beforeDate}&limit=${limit}&offset=${limit * (page - 1)}&orderBy=-dateReleased&type=scene`, {
headers: {
Instance: instanceToken,
},
});
return scrapeLatest(res.body.result, site);
}
async function fetchScene(url, site) { async function fetchScene(url, site) {
const entryId = url.match(/\d+/)[0]; const entryId = url.match(/\d+/)[0];
@ -148,8 +146,8 @@ async function fetchScene(url, site) {
await session.get(url); await session.get(url);
const cookies = await cookieJar.getCookieStringAsync(url); const cookieString = await cookieJar.getCookieStringAsync(url);
const instanceToken = cookies.split(';')[0].split('=')[1]; const { instance_token: instanceToken } = cookieToData(cookieString);
const res = await session.get(`https://site-api.project1service.com/v2/releases/${entryId}`, { const res = await session.get(`https://site-api.project1service.com/v2/releases/${entryId}`, {
headers: { headers: {
@ -157,7 +155,7 @@ async function fetchScene(url, site) {
}, },
}); });
return scrapeScene(res.body.result.parent || res.body.result, url, site); return scrapeScene(res.body.result, url, site);
} }
module.exports = { module.exports = {

View File

@ -7,6 +7,7 @@ const moment = require('moment');
const knex = require('../knex'); const knex = require('../knex');
const { matchTags } = require('../tags'); const { matchTags } = require('../tags');
const pluckPhotos = require('../utils/pluck-photos');
const defaultTags = { const defaultTags = {
hardx: [], hardx: [],
@ -36,7 +37,7 @@ function scrapePhotos(html) {
return unlockedPhotos.concat(lockedThumbnails); return unlockedPhotos.concat(lockedThumbnails);
} }
async function getPhotos(albumPath, siteDomain) { async function getPhotos(albumPath, siteDomain, site) {
const albumUrl = `https://${siteDomain}${albumPath}`; const albumUrl = `https://${siteDomain}${albumPath}`;
const html = await fetchPhotos(albumUrl); const html = await fetchPhotos(albumUrl);
@ -54,7 +55,14 @@ async function getPhotos(albumPath, siteDomain) {
concurrency: 2, concurrency: 2,
}); });
return photos.concat(otherPhotos.flat()); const allPhotos = photos.concat(otherPhotos.flat());
const photoLimit = (site.network.parameters && site.network.parameters.photoLimit) || 25;
const photoIndexes = pluckPhotos(allPhotos.length - 1, photoLimit);
const pluckedPhotos = photoIndexes.map(photoIndex => allPhotos[photoIndex]);
return pluckedPhotos;
} }
function scrape(html, site) { function scrape(html, site) {
@ -140,7 +148,7 @@ async function scrapeScene(html, url, site) {
const poster = videoData.picPreview; const poster = videoData.picPreview;
const trailer = `${videoData.playerOptions.host}${videoData.url}`; const trailer = `${videoData.playerOptions.host}${videoData.url}`;
const photos = await getPhotos($('.picturesItem a').attr('href'), siteDomain); const photos = await getPhotos($('.picturesItem a').attr('href'), siteDomain, site);
const rawTags = data.keywords.split(', '); const rawTags = data.keywords.split(', ');

16
src/utils/cookies.js Normal file
View File

@ -0,0 +1,16 @@
'use strict';
function cookieToData(cookieString) {
return cookieString.split('; ').reduce((acc, cookie) => {
const [key, value] = cookie.split('=');
return {
...acc,
[key]: value,
};
}, {});
}
module.exports = {
cookieToData,
};

21
src/utils/mofos.js Normal file
View File

@ -0,0 +1,21 @@
'use strict';
const Promise = require('bluebird');
const bhttp = require('bhttp');
const knex = require('../knex');
async function run() {
const network = await knex('networks').where('slug', 'mofos').first();
const sites = await knex('sites').where('network_id', network.id);
await Promise.map(sites, async (site) => {
const res = await bhttp.get(site.url);
console.log(site.url, res.statusCode);
}, {
concurrency: 5,
});
}
run();

View File

@ -35,7 +35,7 @@ async function upsert(table, items, duplicatesById, identifier = 'id', knex) {
return Promise.all([ return Promise.all([
knex(table).insert(insert), knex(table).insert(insert),
knex.transaction(async trx => Promise.all(update.map(item => trx knex.transaction(async trx => Promise.all(update.map(item => trx
.where({ id: item.id }) .where({ [identifier]: item[identifier] })
.update(item) .update(item)
.into(table)))), .into(table)))),
]); ]);