Added Virtual Taboo (including OnlyTarts).
This commit is contained in:
parent
a114211e87
commit
06f9efa492
|
@ -15182,6 +15182,13 @@ const sites = [
|
|||
tags: ['cheating', 'family'],
|
||||
parent: 'nubiles',
|
||||
},
|
||||
{
|
||||
slug: 'realitysis',
|
||||
name: 'Reality Sis',
|
||||
url: 'https://www.realitysis.com',
|
||||
tags: ['family'],
|
||||
parent: 'nubiles',
|
||||
},
|
||||
// PASCALS SUBSLUTS
|
||||
{
|
||||
slug: 'pascalssubsluts',
|
||||
|
@ -20511,30 +20518,31 @@ const sites = [
|
|||
{
|
||||
slug: 'virtualtaboo',
|
||||
name: 'Virtual Taboo',
|
||||
url: 'https://www.virtualtaboo.com',
|
||||
url: 'https://virtualtaboo.com',
|
||||
tags: ['vr'],
|
||||
parent: 'virtualtaboo',
|
||||
parameters: {
|
||||
latest: '/videos',
|
||||
actor: '/pornstars',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'onlytarts',
|
||||
name: 'OnlyTarts',
|
||||
url: 'https://www.onlytarts.com',
|
||||
url: 'https://onlytarts.com',
|
||||
parent: 'virtualtaboo',
|
||||
},
|
||||
{
|
||||
slug: 'oopsfamily',
|
||||
name: 'Oops Family',
|
||||
url: 'https://www.oopsfamily.com',
|
||||
url: 'https://oopsfamily.com',
|
||||
tags: ['family'],
|
||||
parent: 'virtualtaboo',
|
||||
},
|
||||
{
|
||||
slug: 'darkroomvr',
|
||||
name: 'Dark Room VR',
|
||||
url: 'https://www.darkroomvr.com',
|
||||
url: 'https://darkroomvr.com',
|
||||
tags: ['vr'],
|
||||
parent: 'virtualtaboo',
|
||||
},
|
||||
|
|
|
@ -775,7 +775,7 @@ async function scrapeActors(argNames) {
|
|||
const entitySlugs = sources.flat();
|
||||
|
||||
const [entitiesBySlug, existingActorEntries] = await Promise.all([
|
||||
fetchEntitiesBySlug(entitySlugs, { types: ['channel', 'network', 'info'] }),
|
||||
fetchEntitiesBySlug(entitySlugs, { types: ['channel', 'network', 'info'], prefer: argv.prefer }),
|
||||
knex('actors')
|
||||
.select(knex.raw('actors.id, actors.name, actors.slug, actors.entry_id, actors.entity_id, row_to_json(entities) as entity'))
|
||||
.whereIn('actors.slug', baseActors.map((baseActor) => baseActor.slug))
|
||||
|
|
|
@ -353,6 +353,10 @@ const scrapers = {
|
|||
tushyraw: vixen,
|
||||
twistys: aylo,
|
||||
vipsexvault: porndoe,
|
||||
virtualtaboo,
|
||||
darkroomvr: virtualtaboo,
|
||||
onlytarts: virtualtaboo,
|
||||
oopsfamily: virtualtaboo,
|
||||
vixen,
|
||||
vrcosplayx: badoink,
|
||||
wankzvr,
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
'use strict';
|
||||
|
||||
const unprint = require('unprint');
|
||||
|
||||
const slugify = require('../utils/slugify');
|
||||
|
||||
function scrapeAll(scenes) {
|
||||
return scenes.map(({ query }) => {
|
||||
const release = {};
|
||||
|
||||
release.url = query.url('a.image-container, a.video-card__title') || query.url(null);
|
||||
release.entryId = new URL(release.url).pathname.match(/\/videos?\/([\w-]+)/)[1];
|
||||
|
||||
release.title = query.content('.video-card__title');
|
||||
|
||||
release.duration = query.duration('.video-card__quality');
|
||||
|
||||
release.actors = query.exists('.video-card__actors a')
|
||||
? query.all('.video-card__actors a').map((actorEl) => ({
|
||||
name: unprint.query.content(actorEl),
|
||||
url: unprint.query.url(actorEl, null),
|
||||
}))
|
||||
: query.content('.video-card__actors')?.split(',').map((actor) => actor.trim());
|
||||
|
||||
release.poster = query.img('.image-container img');
|
||||
release.teaser = query.video('.video-card__trailer');
|
||||
|
||||
return release;
|
||||
});
|
||||
}
|
||||
|
||||
function getPhotos(query) {
|
||||
const teaserPhotos = query.urls('.video-detail__gallery a[href*="//static"], .gallery-item-container a[href*="//static"]');
|
||||
const galleryMore = query.number('.video-detail__gallery-item--more, .video-detail__gallery-item-more');
|
||||
const galleryUrl = /\/(img_)?\d{3}\.jpg/.test(teaserPhotos[0]) && teaserPhotos[0];
|
||||
|
||||
// no incremental URL found, return original links
|
||||
if (!galleryMore || !galleryUrl) {
|
||||
return teaserPhotos;
|
||||
}
|
||||
|
||||
return Array.from({
|
||||
length: teaserPhotos.length + galleryMore + 1, // + number seems to be off by one
|
||||
}, (_value, index) => galleryUrl.replace(/\d+\.jpg/, `${String(index + 1).padStart(3, '0')}.jpg`));
|
||||
}
|
||||
|
||||
function getTrailer({ query, window }) {
|
||||
if (query.exists('.download-pane__list, .download-list')) {
|
||||
// Dark Room VR
|
||||
return query.all('.download-pane__item-container, .download-list__item-container').map((videoEl) => ({
|
||||
src: unprint.query.url(videoEl, '.download-pane__item, .download-list__item'),
|
||||
quality: unprint.query.number(videoEl, '.download-pane__item, .download-list__item', { match: /\d+×(\d+)/, matchIndex: 1 }),
|
||||
vr: true, // only used on VR sites
|
||||
expectType: {
|
||||
'application/octet-stream': 'video/mp4',
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
try {
|
||||
const trailerData = window.eval('coreSettings')?.sources?.standard?.h264;
|
||||
|
||||
return trailerData
|
||||
.filter((source) => source.quality !== 'auto')
|
||||
.map((source) => ({
|
||||
src: source.fallback, // main url doesn't seem to return plausible video files
|
||||
quality: Number(source.label.match(/\d+\s*x\s*(\d+)/)?.[1]) || null,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
// no data variable
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function scrapeScene({ query, window }, { url }) {
|
||||
const release = {};
|
||||
|
||||
release.entryId = new URL(url).pathname.match(/\/videos?\/([\w-]+)/)[1];
|
||||
|
||||
release.title = query.content('.right-info h1, .video-detail__title');
|
||||
release.description = query.text('.video-detail__description p, .description p');
|
||||
|
||||
release.date = query.date('.video-info__time, .info', 'DD MMMM, YYYY', { match: /\d{1,2} \w+, \d{4}/ });
|
||||
release.duration = query.duration('.video-info__time, .info');
|
||||
|
||||
release.actors = query.all('.video-detail__desktop-sidebar .video-info__text a[href*="/model"], .right-info .info a[href*="/pornstars"]').map((actorEl) => ({
|
||||
name: unprint.query.content(actorEl),
|
||||
url: unprint.query.url(actorEl, null),
|
||||
}));
|
||||
|
||||
release.tags = query.contents('.tag-list a, .tags a');
|
||||
|
||||
// release.poster = query.sourceSet('.image-container img') || query.background('.xp-poster');
|
||||
release.poster = query.img(['meta[property="og:image"]', 'meta[property="twitter:image"'], { attribute: 'content' })
|
||||
|| query.poster('.video-detail__image-container *[poster]');
|
||||
|
||||
release.photos = getPhotos(query);
|
||||
release.trailer = getTrailer({ query, window });
|
||||
|
||||
return release;
|
||||
}
|
||||
|
||||
function scrapeProfile({ query }) {
|
||||
const profile = {};
|
||||
|
||||
const bioKeys = query.contents('.pornstar-detail__params--top strong, .actor-detail__param-name');
|
||||
const bioValues = query.exists('.actor-detail__param-value')
|
||||
? query.contents('.actor-detail__param-value')
|
||||
: query.text('.pornstar-detail__params--top', { join: false })?.map((text) => text.split('•')[0].replace(':', '').trim());
|
||||
|
||||
const bio = Object.fromEntries(bioKeys.map((key, index) => [slugify(key, '_'), bioValues[index]]));
|
||||
const tags = query.contents('.actor-detail__tags a').map((tag) => slugify(tag, '_'));
|
||||
|
||||
profile.description = query.content('.pornstar-detail__description, .actor-detail__description') || null;
|
||||
profile.birthPlace = query.content('.pornstar-detail__info span, .actor-detail__info-value')?.split(',')[0].trim();
|
||||
profile.dateOfBirth = unprint.extractDate(bio.birthday, 'MMM D, YYYY');
|
||||
|
||||
profile.measurements = bio.measurements;
|
||||
profile.height = unprint.extractNumber(bio.height);
|
||||
profile.weight = unprint.extractNumber(bio.weight);
|
||||
|
||||
profile.naturalBoobs = tags.includes('natural_tits') ? true : null; // seemingly no tag for fake tits
|
||||
profile.hasTattoos = tags.includes('no_tattoos') ? false : null;
|
||||
|
||||
profile.avatar = query.img('img.pornstar-detail__picture, .actor-detail__picture img');
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
async function fetchLatest(channel, page = 1, { parameters }) {
|
||||
const url = `${channel.url}${parameters.latest || '/video'}?page=${page}`;
|
||||
const res = await unprint.get(url, { selectAll: '.video-card__item' });
|
||||
|
||||
if (res.ok) {
|
||||
return scrapeAll(res.context, channel);
|
||||
}
|
||||
|
||||
return res.status;
|
||||
}
|
||||
|
||||
async function fetchProfile({ name: actorName }, { entity, parameters }) {
|
||||
const url = `${entity.url}${parameters.actor || '/model'}/${slugify(actorName, '-')}`;
|
||||
const res = await unprint.get(url);
|
||||
|
||||
if (res.ok) {
|
||||
return scrapeProfile(res.context, entity);
|
||||
}
|
||||
|
||||
return res.status;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fetchLatest,
|
||||
fetchProfile,
|
||||
scrapeScene: {
|
||||
scraper: scrapeScene,
|
||||
parser: {
|
||||
runScripts: 'dangerously',
|
||||
},
|
||||
},
|
||||
};
|
Loading…
Reference in New Issue