diff --git a/public/img/logos/jayrock/favicon.png b/public/img/logos/jayrock/favicon.png new file mode 100644 index 00000000..180032dc Binary files /dev/null and b/public/img/logos/jayrock/favicon.png differ diff --git a/public/img/logos/jayrock/jayspov.png b/public/img/logos/jayrock/jayspov.png new file mode 100644 index 00000000..95e9a4bd Binary files /dev/null and b/public/img/logos/jayrock/jayspov.png differ diff --git a/public/img/logos/jayrock/misc/jayspov_favicon.png b/public/img/logos/jayrock/misc/jayspov_favicon.png new file mode 100644 index 00000000..ce9c8c45 Binary files /dev/null and b/public/img/logos/jayrock/misc/jayspov_favicon.png differ diff --git a/public/img/logos/jayrock/misc/jayspov_favicon_original.png b/public/img/logos/jayrock/misc/jayspov_favicon_original.png new file mode 100644 index 00000000..15477fdf Binary files /dev/null and b/public/img/logos/jayrock/misc/jayspov_favicon_original.png differ diff --git a/public/img/logos/jayrock/misc/jayspov_site.png b/public/img/logos/jayrock/misc/jayspov_site.png new file mode 100644 index 00000000..ead8db38 Binary files /dev/null and b/public/img/logos/jayrock/misc/jayspov_site.png differ diff --git a/public/img/logos/jayrock/misc/network.png b/public/img/logos/jayrock/misc/network.png new file mode 100644 index 00000000..a0fde261 Binary files /dev/null and b/public/img/logos/jayrock/misc/network.png differ diff --git a/public/img/logos/jayrock/misc/network_dark.png b/public/img/logos/jayrock/misc/network_dark.png new file mode 100644 index 00000000..66e50a6d Binary files /dev/null and b/public/img/logos/jayrock/misc/network_dark.png differ diff --git a/public/img/logos/jayrock/misc/network_original.png b/public/img/logos/jayrock/misc/network_original.png new file mode 100644 index 00000000..c7956571 Binary files /dev/null and b/public/img/logos/jayrock/misc/network_original.png differ diff --git a/public/img/logos/jayrock/misc/network_original_video.png b/public/img/logos/jayrock/misc/network_original_video.png new file mode 100644 index 00000000..18ce295c Binary files /dev/null and b/public/img/logos/jayrock/misc/network_original_video.png differ diff --git a/public/img/logos/jayrock/network.png b/public/img/logos/jayrock/network.png new file mode 100644 index 00000000..7d37c794 Binary files /dev/null and b/public/img/logos/jayrock/network.png differ diff --git a/seeds/00_networks.js b/seeds/00_networks.js index 97aebfbc..16dc4b83 100644 --- a/seeds/00_networks.js +++ b/seeds/00_networks.js @@ -50,6 +50,11 @@ const networks = [ 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\'.', }, + { + slug: 'jayrock', + name: 'JayRock Productions', + url: 'https://www.jayrockcontent.com', + }, { slug: 'julesjordan', name: 'Jules Jordan', diff --git a/seeds/01_sites.js b/seeds/01_sites.js index b803f20c..01ee18f9 100644 --- a/seeds/01_sites.js +++ b/seeds/01_sites.js @@ -1142,6 +1142,13 @@ function getSites(networksMap) { parameters: JSON.stringify({ independent: true }), network_id: networksMap.evilangel, }, + // JAYS POV + { + slug: 'jayspov', + name: 'Jay\'s POV', + url: 'https://www.jayspov.net', + network_id: networksMap.jayrock, + }, // JULES JORDAN { slug: 'julesjordan', diff --git a/src/.eslintrc b/src/.eslintrc index ed306444..68ee9e92 100644 --- a/src/.eslintrc +++ b/src/.eslintrc @@ -8,6 +8,7 @@ "strict": 0, "no-unused-vars": ["error", {"argsIgnorePattern": "^_"}], "no-console": 0, + "no-underscore-dangle": 0, "indent": "off", "template-curly-spacing": "off", "max-len": [2, {"code": 300, "tabWidth": 4, "ignoreUrls": true}], diff --git a/src/scrapers/jayspov.js b/src/scrapers/jayspov.js new file mode 100644 index 00000000..b86667eb --- /dev/null +++ b/src/scrapers/jayspov.js @@ -0,0 +1,104 @@ +'use strict'; + +const Promise = require('bluebird'); +const bhttp = require('bhttp'); + +const slugify = require('../utils/slugify'); + +async function fetchToken() { + const res = await bhttp.get('https://jayspov.net/activity'); + const html = res.body.toString(); + + const time = html.match(/"aet":\d+/)[0].split(':')[1]; + const ah = html.match(/"ah":"[\w-]+"/)[0].split(':')[1].slice(1, -1); + const token = ah.split('').reverse().join(''); + + return { time, token }; +} + +async function fetchActors(entryId, { token, time }) { + const url = `https://jayspov.net/sapi/${token}/${time}/model.getModelContent?_method=model.getModelContent&tz=1&fields[0]=modelId.stageName&fields[1]=_last&fields[2]=modelId.upsellLink&fields[3]=modelId.upsellText&limit=25&transitParameters[contentId]=${entryId}`; + const res = await bhttp.get(url); + + if (res.statusCode === 200 && res.body.status === true) { + return Object.values(res.body.response.collection).map(actor => Object.values(actor.modelId.collection)[0].stageName); + } + + return []; +} + +async function fetchTrailerLocation(entryId) { + const url = `https://jayspov.net/api/download/${entryId}/hd1080/stream`; + const res = await bhttp.get(url, { + followRedirects: false, + }); + + if (res.statusCode === 302) { + return res.headers.location; + } + + return null; +} + +async function scrapeScene(scene, site, tokens) { + const release = { + entryId: scene.id, + title: scene.title, + duration: scene.length, + tokens, // attach tokens to reduce number of requests required for deep fetching + site, + }; + + release.url = `https://jayspov.net/scene/${release.entryId}/${slugify(release.title, true)}`; + release.date = new Date(scene.sites.collection[scene.id].publishDate); + release.poster = scene._resources.primary[0].url; + + if (scene.tags) release.tags = Object.values(scene.tags.collection).map(tag => tag.alias); + if (scene._resources.base) release.photos = scene._resources.base.map(resource => resource.url); + + const [actors, trailer] = await Promise.all([ + fetchActors(release.entryId, tokens), + fetchTrailerLocation(release.entryId), + ]); + + release.actors = actors; + if (trailer) release.trailer = { src: trailer, quality: 1080 }; + + return release; +} + +function scrapeLatest(scenes, site, tokens) { + return Promise.map(scenes, async scene => scrapeScene(scene, site, tokens), { concurrency: 10 }); +} + +async function fetchLatest(site) { + const { time, token } = await fetchToken(); + + const url = `https://jayspov.net/sapi/${token}/${time}/content.load?fields[0]=generatedContentLink&fields[1]=cName&fields[2]=title&fields[3]=_resources.primary.url&fields[4]=sites.publishDate&fields[5]=type&fields[6]=_resources.base.url&fields[7]=_resources.base&fields[8]=length&limit=7&metaFields[resources][thumb]=baseline.sprite.w225i&transitParameters[showOnHome]=true&transitParameters[v1]=OhUOlmasXD&transitParameters[v2]=OhUOlmasXD&transitParameters[preset]=videos`; + const res = await bhttp.get(url); + + if (res.statusCode === 200 && res.body.status) { + return scrapeLatest(res.body.response.collection, site, { time, token }); + } + + return null; +} + +async function fetchScene(url, site, release) { + const { time, token } = release?.tokens || await fetchToken(); // use attached tokens when deep fetching + const { pathname } = new URL(url); + const entryId = pathname.split('/')[2]; + + const res = await bhttp.get(`https://jayspov.net/sapi/${token}/${time}/content.load?_method=content.load&tz=1&filter[id][fields][0]=id&filter[id][values][0]=${entryId}&fields[0]=type&fields[1]=title&fields[2]=sites.publishDate&fields[3]=member&fields[4]=id&fields[5]=tags._last&fields[6]=tags.alias&fields[7]=tags&fields[8]=description&fields[9]=related.relatedContentId.id&fields[10]=related.relatedContentId.title&fields[11]=siteProps.ubs.joinUrl&fields[12]=extender.contentId&fields[13]=vr&fields[14]=backLinkProp.value&fields[15]=backLinkProp.public&limit=1&transitParameters[v1]=ykYa8ALmUD&transitParameters[preset]=scene`); + + if (res.statusCode === 200 && res.body.status) { + return scrapeScene(res.body.response.collection[0], site, { time, token }); + } + + return null; +} + +module.exports = { + fetchLatest, + fetchScene, +}; diff --git a/src/scrapers/scrapers.js b/src/scrapers/scrapers.js index fb7ce692..dab9a150 100644 --- a/src/scrapers/scrapers.js +++ b/src/scrapers/scrapers.js @@ -7,6 +7,7 @@ const bangbros = require('./bangbros'); const blowpass = require('./blowpass'); const dogfart = require('./dogfart'); const evilangel = require('./evilangel'); +const jayspov = require('./jayspov'); const kink = require('./kink'); const mikeadriano = require('./mikeadriano'); const mofos = require('./mofos'); @@ -40,6 +41,7 @@ module.exports = { dogfart, dogfartnetwork: dogfart, evilangel, + jayspov, julesjordan, kellymadison, kink, diff --git a/src/utils/slugify.js b/src/utils/slugify.js index aa5040e6..ad044a8a 100644 --- a/src/utils/slugify.js +++ b/src/utils/slugify.js @@ -1,7 +1,9 @@ 'use strict'; -function slugify(string) { - return string.trim().toLowerCase().match(/\w+/g).join('-'); +function slugify(string, encode = false) { + const slug = string.trim().toLowerCase().match(/\w+/g).join('-'); + + return encode ? encodeURI(slug) : slug; } module.exports = slugify;