'use strict'; const unprint = require('unprint'); const slugify = require('../utils/slugify'); function scrapeAll(scenes, _channel) { return scenes.map(({ query, element }) => { const release = {}; release.url = query.url('.card__link'); release.entryId = new URL(release.url).pathname.match(/\/en\/(.*)/)[1]; release.title = query.content('.card__link'); release.duration = query.duration('.card__img_badge.bottom-right'); release.poster = query.img('.card__img img'); release.teaser = unprint.query.dataset(element, null, 'video'); return release; }); } async function fetchLatest(channel, page = 1) { const res = await unprint.get(`${channel.origin}/en/videos?page=${page}`, { selectAll: '.card--item' }); if (res.ok) { return scrapeAll(res.context, channel); } return res.status; } function scrapeScene({ query }, url, channel) { const release = {}; release.entryId = new URL(url).pathname.match(/\/en\/(.*)/)[1]; release.title = query.content('h1.h2'); release.description = query.attribute('meta[property="og:description"]', 'content'); // not usually used, if ever release.date = query.date('meta[property="video:release_date"]', 'YYYY-MM-DD', { attribute: 'content' }); release.duration = query.number('meta[property="video:duration"]', { attribute: 'content' }); release.actors = query.all('.video-info .mini-avatars a').map((actorEl) => ({ name: unprint.query.content(actorEl), url: unprint.query.url(actorEl, null, { origin: channel.origin }), avatar: [ unprint.query.img(actorEl, 'img')?.replace('-video_actor_avatar', '-actor_detail'), unprint.query.img(actorEl, 'img'), ], })); release.tags = query.contents('.video-info a[href*="?category"]').map((tag) => tag.replace('#', '').trim()); release.qualities = query.numbers('.download-dropdown-menu li div', { attribute: 'data-res' }); release.poster = [ query.img('.video-player', { attribute: 'data-poster' }), query.img('meta[property="og:image"]', { attribute: 'content' }), ]; release.trailer = query.all('.video-player source').map((videoEl) => ({ src: unprint.query.video(videoEl, null), quality: unprint.query.number(videoEl, null, { attribute: 'size' }), })); release.photos = query.all('.image .gallery-popup').map((imgEl) => [ unprint.query.img(imgEl, null, { attribute: 'href' }), unprint.query.img(imgEl, 'img'), ]); release.channel = slugify(query.attribute('meta[property="og:site_name"]', 'content'), ''); return release; } async function fetchScene(url, channel) { const res = await unprint.get(url); if (res.ok) { return scrapeScene(res.context, url, channel); } return res.status; } function scrapeProfile({ query }, url, _entity) { const profile = { url }; const bio = Object.fromEntries(query.all('.model-info__item').map((bioEl) => [ slugify(unprint.query.content(bioEl, 'span:first-child'), '_'), unprint.query.content(bioEl, 'span:last-child'), ])); profile.avatar = query.img('.actor-img'); profile.gender = bio.gender; profile.birthCountry = bio.nationality; profile.ethnicity = bio.ethnicity; profile.age = bio.age; profile.hairColor = bio.hair_color; if (!bio.breast_size?.includes('-')) profile.cup = bio.breast_size; // larger than F is defined as F-Z, not too useful if (/natural/i.test(bio.breast_type)) profile.naturalBoobs = true; if (/fake/i.test(bio.breast_type)) profile.naturalBoobs = false; if (/no/i.test(bio.tattoo)) profile.hasTattoos = false; if (/yes/i.test(bio.tattoo)) profile.hasTattoos = true; return profile; } async function fetchProfile(baseActor, entity) { const url = `${new URL(entity.url).origin}/en/pornstars/${baseActor.slug}`; const res = await unprint.get(`${new URL(entity.url).origin}/en/pornstars/${baseActor.slug}`); if (res.ok) { return scrapeProfile(res.context, url, entity); } return res.status; } module.exports = { fetchLatest, fetchScene, fetchProfile, };