Compare commits

..

No commits in common. "c17e44e9f98ef6a9765568ee66341cc374aca4cc" and "4d20dae0798772af8fe08f86a5c5dc9373d8cf45" have entirely different histories.

4 changed files with 48 additions and 99 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "traxxx", "name": "traxxx",
"version": "1.229.3", "version": "1.229.2",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "traxxx", "name": "traxxx",
"version": "1.229.3", "version": "1.229.2",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@casl/ability": "^5.2.2", "@casl/ability": "^5.2.2",

View File

@ -1,6 +1,6 @@
{ {
"name": "traxxx", "name": "traxxx",
"version": "1.229.3", "version": "1.229.2",
"description": "All the latest porn releases in one place", "description": "All the latest porn releases in one place",
"main": "src/app.js", "main": "src/app.js",
"scripts": { "scripts": {

View File

@ -62,6 +62,8 @@ function scrapeAll(scenes, channel) {
function scrapeProfile(actor, entity) { function scrapeProfile(actor, entity) {
const profile = {}; const profile = {};
console.log(actor);
if (actor.bio.about && !/\band\b/.test(actor.bio.about)) { if (actor.bio.about && !/\band\b/.test(actor.bio.about)) {
const bio = actor.bio.about.split(/\n/).filter(Boolean).reduce((acc, item) => { const bio = actor.bio.about.split(/\n/).filter(Boolean).reduce((acc, item) => {
const [key, value] = item.match(/(.+): (.+)/).slice(1); const [key, value] = item.match(/(.+): (.+)/).slice(1);

View File

@ -1,12 +1,14 @@
'use strict'; 'use strict';
/* eslint-disable newline-per-chained-call */ /* eslint-disable newline-per-chained-call */
const Promise = require('bluebird');
const moment = require('moment'); const moment = require('moment');
const unprint = require('unprint'); const unprint = require('unprint');
const argv = require('../argv'); const argv = require('../argv');
const qu = require('../utils/qu'); const qu = require('../utils/qu');
const http = require('../utils/http'); const http = require('../utils/http');
const slugify = require('../utils/slugify');
const genderMap = { const genderMap = {
F: 'female', F: 'female',
@ -15,15 +17,10 @@ const genderMap = {
}; };
function getAvatarFallbacks(avatar) { function getAvatarFallbacks(avatar) {
if (!avatar) {
return null;
}
return avatar return avatar
.sort((imageA, imageB) => imageB.height - imageA.height) .sort((imageA, imageB) => imageB.height - imageA.height)
.map((image) => [image.highdpi?.['3x'], image.highdpi?.triple, image.highdpi?.['2x'], image.highdpi?.double, image.src]) .map((image) => [image.highdpi?.['3x'], image.highdpi?.['2x'], image.src])
.flat() .flat();
.filter(Boolean);
} }
function curateSources(sources, type = 'image/jpeg') { function curateSources(sources, type = 'image/jpeg') {
@ -55,9 +52,9 @@ function scrapeAll(scenes, channel) {
release.title = data.title; release.title = data.title;
release.date = qu.extractDate(data.releaseDate); release.date = qu.extractDate(data.releaseDate);
release.actors = (data.modelsSlugged || data.models)?.map((model) => ({ release.actors = data.modelsSlugged.map((model) => ({
name: model.name, name: model.name,
url: model.slugged && `${channel.url}/models/${model.slugged}`, url: `${channel.url}/models/${model.slugged}`,
})); }));
release.poster = curateSources(data.images.listing); release.poster = curateSources(data.images.listing);
@ -303,18 +300,6 @@ const videoFields = `
} }
`; `;
const imageFragment = `
fragment ImageInfo on Image {
src
width
height
highdpi {
double
triple
}
}
`;
function getSlug(release) { function getSlug(release) {
if (release.slug) { if (release.slug) {
return release.slug; return release.slug;
@ -403,12 +388,26 @@ async function fetchScene(url, channel, baseRelease, options) {
return res.status; return res.status;
} }
async function scrapeProfile(data, channel) { async function fetchActorReleases(pages, model, origin) {
const releasesPerPage = await Promise.map(pages, async (page) => {
const url = `${origin}/api${model.targetUrl}?page=${page}`;
const res = await http.get(url);
if (res.status === 200) {
return scrapeAll(res.body.data.videos.videos, null, origin);
}
return [];
}, { concurrency: 3 });
return releasesPerPage.flat();
}
async function scrapeProfile(data, origin, withReleases) {
const model = data.model; const model = data.model;
const profile = {}; const profile = {};
// most details seemingly unavailable in graphql profile.birthdate = new Date(model.dateOfBirth);
if (profile.dateOfBirth) profile.birthdate = new Date(model.dateOfBirth);
profile.gender = genderMap[model.sex]; profile.gender = genderMap[model.sex];
profile.hair = model.hairColour; profile.hair = model.hairColour;
@ -424,8 +423,15 @@ async function scrapeProfile(data, channel) {
profile.poster = getAvatarFallbacks(model.images.profile); profile.poster = getAvatarFallbacks(model.images.profile);
profile.banner = getAvatarFallbacks(model.images.poster); profile.banner = getAvatarFallbacks(model.images.poster);
if (model.videos) { const releases = scrapeAll(data.videos.videos, null, origin);
profile.scenes = scrapeAll(model.videos.edges.map((edge) => edge.node), channel);
if (withReleases) {
const pageCount = Math.ceil(data.videos.count / 6);
const otherReleases = await fetchActorReleases((Array.from({ length: pageCount - 1 }, (value, index) => index + 2)), model, origin);
profile.releases = [...releases, ...otherReleases];
} else {
profile.releases = releases;
} }
return profile; return profile;
@ -536,80 +542,21 @@ async function fetchUpcoming(channel) {
return res.status; return res.status;
} }
async function fetchProfile(actor, { channel }) { async function fetchProfile({ name: actorName }, { site }, include) {
const res = await http.post(`${channel.url}/graphql`, { const origin = site.url;
operationName: 'searchModels', const actorSlug = slugify(actorName);
variables: { const url = `${origin}/api/${actorSlug}`;
slug: actor.slug, const res = await http.get(url);
site: channel.slug.toUpperCase(),
},
query: `
query searchModels(
$slug: String!
$site: Site!
) {
model: findOneModel(input: { slug: $slug, site: $site }) {
name
biography
images {
listing {
...ImageInfo
}
profile {
...ImageInfo
}
poster {
...ImageInfo
}
}
videos {
edges {
node {
videoId
title
slug
releaseDate
runLength
site
rating
models {
name
}
carousel {
main {
src
}
}
previews {
listing {
src
}
}
images {
poster {
...ImageInfo
}
}
}
}
}
}
}
${imageFragment}
`,
}, {
headers: {
referer: channel.url,
origin: channel.url,
},
});
if (res.ok) { if (res.ok) {
return scrapeProfile(res.body.data, channel); if (res.body.data) {
return scrapeProfile(res.body.data, origin, include.scenes);
}
return null;
} }
return null; return res.status;
} }
module.exports = { module.exports = {