Added Nubiles network.
|  | @ -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', | ||||||
|  |  | ||||||
| After Width: | Height: | Size: 52 KiB | 
| After Width: | Height: | Size: 6.2 KiB | 
| After Width: | Height: | Size: 5.3 KiB | 
| After Width: | Height: | Size: 18 KiB | 
| After Width: | Height: | Size: 5.5 KiB | 
| After Width: | Height: | Size: 26 KiB | 
| After Width: | Height: | Size: 3.8 KiB | 
| After Width: | Height: | Size: 21 KiB | 
| Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 1.5 KiB | 
| After Width: | Height: | Size: 78 KiB | 
| After Width: | Height: | Size: 71 KiB | 
| After Width: | Height: | Size: 6.0 KiB | 
| After Width: | Height: | Size: 29 KiB | 
| After Width: | Height: | Size: 6.6 KiB | 
| After Width: | Height: | Size: 20 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 12 KiB | 
| After Width: | Height: | Size: 25 KiB | 
| After Width: | Height: | Size: 37 KiB | 
| After Width: | Height: | Size: 18 KiB | 
| After Width: | Height: | Size: 41 KiB | 
| After Width: | Height: | Size: 11 KiB | 
| After Width: | Height: | Size: 35 KiB | 
| After Width: | Height: | Size: 4.9 KiB | 
| After Width: | Height: | Size: 32 KiB | 
| After Width: | Height: | Size: 24 KiB | 
| After Width: | Height: | Size: 11 KiB | 
| After Width: | Height: | Size: 120 KiB | 
| After Width: | Height: | Size: 28 KiB | 
| After Width: | Height: | Size: 7.2 KiB | 
| After Width: | Height: | Size: 10 KiB | 
| Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 13 KiB | 
| After Width: | Height: | Size: 20 KiB | 
| After Width: | Height: | Size: 10 KiB | 
| After Width: | Height: | Size: 14 KiB | 
| After Width: | Height: | Size: 5.6 KiB | 
| After Width: | Height: | Size: 33 KiB | 
| After Width: | Height: | Size: 4.2 KiB | 
| After Width: | Height: | Size: 3.3 KiB | 
| After Width: | Height: | Size: 14 KiB | 
| After Width: | Height: | Size: 21 KiB | 
| After Width: | Height: | Size: 4.1 KiB | 
| After Width: | Height: | Size: 12 KiB | 
| After Width: | Height: | Size: 124 KiB | 
|  | @ -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', | ||||||
|  |  | ||||||
|  | @ -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', | ||||||
|  |  | ||||||
|  | @ -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', | ||||||
|  |  | ||||||
|  | @ -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}], | ||||||
|  |  | ||||||
|  | @ -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, | ||||||
|  | }; | ||||||
|  | @ -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, | ||||||
|  |  | ||||||
|  | @ -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, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -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)); | ||||||
| } | } | ||||||
|  |  | ||||||