Added Nubiles network.

This commit is contained in:
ThePendulum 2020-02-12 23:00:32 +01:00
parent 2b5b8fb19d
commit cd2ca65903
53 changed files with 377 additions and 87 deletions

View File

@ -42,6 +42,17 @@ module.exports = {
'wicked', 'wicked',
'brazzers', 'brazzers',
'milehighmedia', 'milehighmedia',
[
// Nubiles; use the same scraper, but different results per site
'nubiles',
'nubilesporn',
'deeplush',
'brattysis',
'nfbusty',
'anilos',
'hotcrazymess',
'thatsitcomshow',
],
'21sextury', '21sextury',
'julesjordan', 'julesjordan',
'naughtyamerica', 'naughtyamerica',

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@ -252,6 +252,11 @@ function getTags(groupsMap) {
alias_for: null, alias_for: null,
group_id: groupsMap.finish, group_id: groupsMap.finish,
}, },
{
name: 'casting',
slug: 'casting',
alias_for: null,
},
{ {
name: 'cheerleader', name: 'cheerleader',
slug: 'cheerleader', slug: 'cheerleader',
@ -846,6 +851,12 @@ function getTags(groupsMap) {
alias_for: null, alias_for: null,
group_id: groupsMap.body, group_id: groupsMap.body,
}, },
{
name: 'teacher',
slug: 'teacher',
alias_for: null,
group_id: groupsMap.roleplay,
},
{ {
name: 'threesome', name: 'threesome',
slug: 'threesome', slug: 'threesome',

View File

@ -184,6 +184,12 @@ const networks = [
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: 'nubiles',
name: 'Nubiles',
url: 'https://www.nubiles.com',
description: 'Welcome to the teen megasite that started it all! Browse our massive HD collection of fresh legal hotties at Nubiles.net.',
},
{ {
slug: 'perfectgonzo', slug: 'perfectgonzo',
name: 'Perfect Gonzo', name: 'Perfect Gonzo',

View File

@ -2822,6 +2822,154 @@ const sites = [
url: 'https://www.naughtyamerica.com/site/live-naughty-nurse', url: 'https://www.naughtyamerica.com/site/live-naughty-nurse',
network: 'naughtyamerica', network: 'naughtyamerica',
}, },
// NUBILES
{
slug: 'anilos',
name: 'Anilos',
url: 'https://www.anilos.com',
network: 'nubiles',
},
{
slug: 'brattysis',
name: 'Bratty Sis',
url: 'https://www.brattysis.com',
tags: ['family'],
network: 'nubiles',
},
{
slug: 'deeplush',
name: 'Deep Lush',
url: 'https://www.deeplush.com',
network: 'nubiles',
},
{
slug: 'hotcrazymess',
name: 'Hot Crazy Mess',
url: 'https://www.hotcrazymess.com',
network: 'nubiles',
},
{
slug: 'nfbusty',
name: 'NF Busty',
url: 'https://www.nfbusty.com',
tags: ['big-boobs'],
network: 'nubiles',
},
{
slug: 'nubilefilms',
name: 'Nubile Films',
url: 'https://www.nubilefilms.com',
network: 'nubiles',
},
{
slug: 'nubiles',
name: 'Nubiles',
url: 'https://www.nubiles.net',
network: 'nubiles',
},
{
slug: 'nubilescasting',
name: 'Nubiles Casting',
url: 'https://www.nubiles-casting.com',
tags: ['casting'],
network: 'nubiles',
},
{
slug: 'momsteachsex',
name: 'Moms Teach Sex',
url: 'https://www.momsteachsex.com',
tags: ['family', 'milf'],
network: 'nubiles',
},
{
slug: 'petitehdporn',
name: 'Petite HD Porn',
url: 'https://www.petitehdporn.com',
network: 'nubiles',
},
{
slug: 'driverxxx',
name: 'Driver XXX',
url: 'https://www.driverxxx.com',
network: 'nubiles',
},
{
slug: 'petiteballerinasfucked',
name: 'Petite Ballerinas Fucked',
url: 'https://www.petiteballerinasfucked.com',
network: 'nubiles',
},
{
slug: 'teacherfucksteens',
name: 'Teacher Fucks Teens',
url: 'https://www.teacherfucksteens.com',
tags: ['teacher'],
network: 'nubiles',
},
{
slug: 'stepsiblingscaught',
name: 'Step Siblings Caught',
url: 'https://www.stepsiblingscaught.com',
tags: ['family'],
network: 'nubiles',
},
{
slug: 'princesscum',
name: 'Princess Cum',
url: 'https://www.princesscum.com',
network: 'nubiles',
},
{
slug: 'badteenspunished',
name: 'Bad Teens Punished',
url: 'https://www.badteenspunished.com',
network: 'nubiles',
},
{
slug: 'nubilesunscripted',
name: 'Nubiles Unscripted',
url: 'https://www.nubilesunscripted.com',
network: 'nubiles',
},
{
slug: 'bountyhunterporn',
name: 'Bounty Hunter Porn',
url: 'https://www.bountyhunterporn.com',
network: 'nubiles',
},
{
slug: 'daddyslilangel',
name: 'Daddy\'s Lil Angel',
url: 'https://www.daddyslilangel.com',
tags: ['family', 'anal'],
network: 'nubiles',
},
{
slug: 'myfamilypies',
name: 'My Family Pies',
url: 'https://www.myfamilypies.com',
tags: ['family'],
network: 'nubiles',
},
{
slug: 'nubileset',
name: 'Nubiles Entertainment',
url: 'https://www.nubileset.com',
network: 'nubiles',
},
{
slug: 'detentiongirls',
name: 'Detention Girls',
url: 'https://www.detentiongirls.com',
network: 'nubiles',
},
{
slug: 'thatsitcomshow',
name: 'That Sitcom Show',
url: 'https://www.thatsitcomshow.com',
tags: ['parody'],
network: 'nubiles',
},
// PERFECT GONZO // PERFECT GONZO
{ {
slug: 'allinternal', slug: 'allinternal',

View File

@ -10,6 +10,7 @@
"no-console": 0, "no-console": 0,
"no-underscore-dangle": 0, "no-underscore-dangle": 0,
"indent": "off", "indent": "off",
"prefer-destructuring": "off",
"template-curly-spacing": "off", "template-curly-spacing": "off",
"object-curly-newline": "off", "object-curly-newline": "off",
"max-len": [2, {"code": 300, "tabWidth": 4, "ignoreUrls": true}], "max-len": [2, {"code": 300, "tabWidth": 4, "ignoreUrls": true}],

149
src/scrapers/nubiles.js Normal file
View File

@ -0,0 +1,149 @@
'use strict';
const { get, geta, ctxa } = require('../utils/q');
const slugify = require('../utils/slugify');
const { heightToCm } = require('../utils/convert');
const slugUrlMap = {
nubiles: 'https://www.nubiles.net',
nubilesporn: 'https://www.nubiles-porn.com',
};
async function getPhotos(albumUrl) {
const thumbnails = await geta(albumUrl, '.photo-thumb');
return thumbnails
? thumbnails.map(({ q }) => q('source').srcset)
: [];
}
function scrapeAll(scenes, site, origin) {
return scenes.map(({ q, qa, qu, qd }) => {
const release = {};
release.title = q('.title a', true);
const url = qu('.title a').split('?')[0];
const channelUrl = qu('.site-link');
if (/^http/.test(url)) {
const { pathname } = new URL(url);
release.entryId = pathname.split('/')[3];
if (channelUrl) release.url = `${channelUrl}${pathname}`;
else release.url = url;
} else {
release.entryId = url.split('/')[3];
if (channelUrl) release.url = `${channelUrl}${url}`;
else if (site?.url) release.url = `${site.url}${url}`;
else if (origin) release.url = `${origin}${url}`;
}
release.date = qd('.date', 'MMM D, YYYY');
release.actors = qa('.models a.model', true);
const poster = q('img').dataset.original;
release.poster = [
poster.replace('_640', '_1280'),
poster,
];
release.stars = Number(q('.rating', true));
release.likes = Number(q('.likes', true));
return release;
});
}
async function scrapeScene({ q, qa, qd, qp, qu, qi }, url, site) {
const release = {};
const { origin, pathname } = new URL(url);
release.url = `${origin}${pathname}`;
release.entryId = new URL(url).pathname.split('/')[3];
release.title = q('.content-pane-title h2', true);
release.description = q('.content-pane-column div', true);
release.date = qd('.date', 'MMM D, YYYY');
release.actors = qa('.content-pane-performers .model', true);
release.tags = qa('.categories a', true);
release.poster = qp() || qi('.fake-video-player img');
release.trailer = qa('source').map(source => ({
src: source.src,
quality: Number(source.getAttribute('res')),
}));
release.stars = Number(q('.score', true));
release.likes = Number(q('#likecount', true));
const albumLink = qu('.content-pane-related-links a[href*="gallery"]');
if (albumLink) release.photos = await getPhotos(`${site.url}${albumLink}`);
return release;
}
function scrapeProfile({ q, qa, qi, qu }, _actorName, origin) {
const profile = {};
const keys = qa('.model-profile h5', true);
const values = qa('.model-profile h5 + p', true);
const bio = keys.reduce((acc, key, index) => ({ ...acc, [slugify(key, { delimiter: '_' })]: values[index] }), {});
profile.age = Number(bio.age);
profile.description = q('.model-bio', true);
profile.residencePlace = bio.location;
profile.height = heightToCm(bio.height);
[profile.bust, profile.waist, profile.hip] = bio.figure.split('-').map(v => Number(v) || v);
profile.avatar = qi('.model-profile img');
const releases = qa('.content-grid-item').filter(el => /video\//.test(qu(el, '.img-wrapper a'))); // filter out photos
profile.releases = scrapeAll(ctxa(releases), null, origin);
return profile;
}
async function fetchLatest(site, page = 1) {
const url = `${site.url}/video/gallery/${(page - 1) * 12}`;
const qLatest = await geta(url, '.content-grid-item');
return qLatest && scrapeAll(qLatest, site);
}
async function fetchScene(url, site) {
const qScene = await get(url);
return qScene && scrapeScene(qScene, url, site);
}
async function fetchProfile(actorName, siteSlug) {
const firstLetter = actorName.charAt(0).toLowerCase();
const origin = slugUrlMap[siteSlug] || `https://www.${siteSlug}.com`;
const url = `${origin}/model/alpha/${firstLetter}`;
const { qa } = await get(url);
const modelPath = qa('.content-grid-item a.title').find(el => slugify(el.textContent) === slugify(actorName));
if (modelPath) {
const modelUrl = `${origin}${modelPath}`;
const qModel = await get(modelUrl);
if (qModel) return scrapeProfile(qModel, actorName, origin);
}
return null;
}
module.exports = {
fetchLatest,
fetchScene,
fetchProfile,
};

View File

@ -33,6 +33,7 @@ const mindgeek = require('./mindgeek');
const mofos = require('./mofos'); const mofos = require('./mofos');
const naturals = require('./21naturals'); const naturals = require('./21naturals');
const naughtyamerica = require('./naughtyamerica'); const naughtyamerica = require('./naughtyamerica');
const nubiles = require('./nubiles');
const perfectgonzo = require('./perfectgonzo'); const perfectgonzo = require('./perfectgonzo');
const pervcity = require('./pervcity'); const pervcity = require('./pervcity');
const pornhub = require('./pornhub'); const pornhub = require('./pornhub');
@ -65,13 +66,12 @@ module.exports = {
blowpass, blowpass,
brazzers, brazzers,
ddfnetwork, ddfnetwork,
metrohd,
digitalplayground, digitalplayground,
dogfart, dogfart,
dogfartnetwork: dogfart, dogfartnetwork: dogfart,
famedigital,
evilangel, evilangel,
fakehub, fakehub,
famedigital,
fantasymassage, fantasymassage,
girlsway, girlsway,
insex, insex,
@ -81,16 +81,18 @@ module.exports = {
kink, kink,
legalporno, legalporno,
men, men,
metrohd,
mikeadriano, mikeadriano,
milehighmedia, milehighmedia,
mindgeek, mindgeek,
mofos, mofos,
naughtyamerica,
nubiles,
perfectgonzo, perfectgonzo,
pervcity, pervcity,
pornpros, pornpros,
private: privateNetwork, private: privateNetwork,
puretaboo, puretaboo,
naughtyamerica,
realitykings, realitykings,
score, score,
teamskeet, teamskeet,
@ -102,20 +104,23 @@ module.exports = {
xempire, xempire,
}, },
actors: { actors: {
// ordered by data priority
'21sextury': sextury, '21sextury': sextury,
anilos: nubiles,
babes, babes,
bangbros, bangbros,
blowpass, blowpass,
boobpedia, boobpedia,
brattysis: nubiles,
brazzers, brazzers,
ddfnetwork, ddfnetwork,
deeplush: nubiles,
digitalplayground, digitalplayground,
famedigital,
evilangel, evilangel,
fakehub, fakehub,
famedigital,
freeones, freeones,
freeonesLegacy, freeonesLegacy,
hotcrazymess: nubiles,
iconmale, iconmale,
julesjordan, julesjordan,
kellymadison, kellymadison,
@ -125,9 +130,14 @@ module.exports = {
milehighmedia, milehighmedia,
mofos, mofos,
naughtyamerica, naughtyamerica,
nfbusty: nubiles,
nubilefilms: nubiles,
nubiles,
nubilesporn: nubiles,
pornhub, pornhub,
realitykings, realitykings,
score, score,
thatsitcomshow: nubiles,
transangels, transangels,
twistys, twistys,
wicked, wicked,

View File

@ -1,105 +1,50 @@
'use strict'; 'use strict';
/* eslint-disable newline-per-chained-call */ const { get, geta } = require('../utils/q');
const bhttp = require('bhttp');
const { JSDOM } = require('jsdom');
const moment = require('moment');
const { matchTags } = require('../tags'); function scrapeLatest(scenes, site) {
return scenes.map(({ q, qa, qu, qd }) => {
const release = {};
function scrapeLatest(html, site) { release.title = q('.title a', true);
const { document } = new JSDOM(html).window;
const sceneElements = $('.scenes-latest').toArray();
return sceneElements.map((element) => { const pathname = qu('.title a');
const actors = $(element).find('.actors a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); release.entryId = pathname.split('/')[3];
release.url = `${site.url}${pathname}`;
return { release.date = qd('.date', 'MMM DD, YYYY');
url, release.actors = qa('.models a.model', true);
entryId,
title, release.poster = q('img').dataset.original;
actors,
date, release.stars = Number(q('.rating', true));
rating: { release.likes = Number(q('.likes', true));
likes,
dislikes, console.log(release);
stars, return release;
},
site,
};
}); });
} }
function scrapeUpcoming(html, site) { function scrapeScene(({ q }), _site) {
const { document } = new JSDOM(html).window; const release = {};
const sceneElements = $('.scenes-upcoming').toArray();
return sceneElements.map((element) => { console.log(release);
const actors = $(element).find('.actors a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); return release;
return {
url,
entryId,
title,
actors,
date,
rating: {
likes,
dislikes,
stars,
},
site,
};
});
}
async function scrapeScene(html, url, site) {
const $ = cheerio.load(html, { normalizeWhitespace: true });
const actors = $('.actors a').map((actorIndex, actorElement) => $(actorElement).text()).toArray();
const rawTags = $('.tags a').map((tagIndex, tagElement) => $(tagElement).text()).toArray();
const tags = await matchTags(rawTags);
return {
url,
entryId,
title,
description,
actors,
director,
date,
duration,
tags,
rating: {
likes,
dislikes,
stars,
},
site,
};
} }
async function fetchLatest(site, page = 1) { async function fetchLatest(site, page = 1) {
const res = await bhttp.get(`${site.url}/url`); const url = `${site.url}/${page}`;
const qLatest = await geta(url, '.selector');
return scrapeLatest(res.body.toString(), site); return qLatest && scrapeLatest(qLatest, site);
}
async function fetchUpcoming(site) {
const res = await bhttp.get(`${site.url}/url`);
return scrapeUpcoming(res.body.toString(), site);
} }
async function fetchScene(url, site) { async function fetchScene(url, site) {
const res = await bhttp.get(url); const qScene = await get(url);
return scrapeScene(res.body.toString(), url, site); return qScene && scrapeScene(qScene, site);
} }
module.exports = { module.exports = {
fetchLatest, fetchLatest,
fetchUpcoming,
fetchScene,
}; };

View File

@ -191,6 +191,10 @@ function init(element, window) {
} }
function initAll(context, selector, window) { function initAll(context, selector, window) {
if (Array.isArray(context)) {
return context.map(element => init(element, window));
}
return Array.from(context.querySelectorAll(selector)) return Array.from(context.querySelectorAll(selector))
.map(element => init(element, window)); .map(element => init(element, window));
} }