'use strict'; const unprint = require('unprint'); const slugify = require('../utils/slugify'); const { stripQuery } = require('../utils/url'); async function getTrailer(data) { const mediaId = data.medias?.find((media) => media.type === 'free')?.media_id; if (!mediaId) { return null; } const res = await unprint.get(`https://pornbox.com/media/${mediaId}/stream`); if (res.ok) { return res.data.qualities?.map((video) => ({ src: video.src, type: video.type, quality: parseInt(video.size, 10), })); } return null; } async function scrapeScene(data, channel, include) { const release = {}; const entityUrl = new URL(channel.url).origin; release.title = data.scene_name || data.custom_name; release.entryId = data.id; release.url = `${entityUrl}/watch/${data.id}/${slugify(release.title, '_')}`; release.date = new Date(data.release_date || data.publish_date); release.duration = unprint.extractDuration(data.runtime); release.actors = [...data.models, ...(data.male_models || [])].map((model) => ({ name: model.model_name, gender: model.sex, entryId: model.model_id, url: `${entityUrl}/model/${model.model_id}/${slugify(model.model_name, '_')}`, })); release.tags = data.niches.map((niche) => niche.niche); release.poster = [ data.player_poster, data.thumbnail.mosaic, data.thumbnail.list, data.thumbnail.large, // squared data.thumbnail.small, ]; release.photos = data.gallery?.map((photo) => [ stripQuery(photo.large_thumbnail), photo.large_thumbnail, photo.small_thumbnail, ]); release.caps = data.screenshots?.map((screenshot) => [ screenshot.xga_size, screenshot.small_thumbnail, ]); release.teaser = data.video_preview; if (include?.includeTrailers) { release.trailer = await getTrailer(data); release.qualities = release.trailer?.map((src) => src.quality); } if (data.has_4k_full_video && !release.qualities) { release.qualities = [2160]; } release.studio = slugify(data.studio, ''); return release; } function scrapeAll(scenes, channel) { return Promise.all(scenes.map(async (data) => scrapeScene(data, channel))); } async function fetchLatest(channel, page = 1) { if (!channel.parameters?.studioId) { return null; } const url = `https://pornbox.com/studio/${channel.parameters.studioId}/?sort=latest&skip=${page}`; // sort=recent is 'sold recently', not latest chronologically const res = await unprint.get(url); if (res.ok && res.data?.contents) { return scrapeAll(res.data.contents, channel); } return res.status; } async function fetchScene(url, entity, baseRelease, include) { const entryId = new URL(url).pathname.match(/\/watch(?:-page)?\/(\d+)/)[1]; const res = await unprint.get(`https://pornbox.com/contents/${entryId}`); if (res.ok) { return scrapeScene(res.data, entity, include); } return res.status; } function scrapeProfile(data) { const profile = {}; profile.age = data.age; profile.gender = data.sex; profile.birthCountry = data.nationality?.[0]?.name; profile.nationality = data.nationality?.[0]?.nationality; profile.aliases = data.stage_name; profile.avatar = [ stripQuery(data.headshot), data.headshot, ]; return profile; } async function getActorUrl(actor, entity) { if (actor.url) { return actor.url; } const res = await unprint.get(`https://pornbox.com/autocomplete/${actor.name}`); if (res.ok && Array.isArray(res.data)) { const model = res.data.find((item) => item.type === 'model' && item.name === actor.name); if (model) { return `${entity.url}/model/${model.id}/${slugify(model.name, '_')}`; } } return null; } async function fetchProfile(actor, entity) { const url = await getActorUrl(actor, entity); if (!url) { return null; } const actorId = new URL(url).pathname.match(/\/model\/(\d+)/)[1]; const res = await unprint.get(`https://pornbox.com/model/info/${actorId}`); if (res.ok) { return scrapeProfile(res.data, entity); } return res.status; } module.exports = { fetchLatest, fetchProfile, fetchScene, };