'use strict'; const unprint = require('unprint'); const slugify = require('../utils/slugify'); const tryUrls = require('../utils/try-urls'); function scrapeAll(scenes) { return scenes.map(({ query }) => { const release = {}; const url = query.url('.item-title a'); const { pathname } = new URL(url); release.url = url; release.entryId = pathname.match(/\/trailers\/(.*).html/)[1]; release.title = query.content('.item-title a'); release.date = query.date('.item-date', 'MMMM D, YYYY'); release.duration = query.duration('.item-date', /(\d{2}:)?\d{2}:\d{2}/); release.actors = query.all('.item-models a').map((actorEl) => ({ name: unprint.query.content(actorEl), url: unprint.query.url(actorEl, null), })); release.poster = query.img('.item-video-thumb', { attribute: 'data-videoposter' }) || query.img('img.video_placeholder'); release.teaser = query.video('.item-video-thumb', { attribute: 'data-videosrc' }); release.photoCount = query.number('.item-date', { match: /(\d+) photos/i, matchIndex: 1 }); release.channel = slugify(query.content('.item-sitename a'), ''); return release; }); } async function fetchLatest(channel, page = 1) { const slug = channel.parameters?.slug || new URL(channel.url).pathname.match(/\/series\/([\w-]+)/)[1]; const res = await unprint.get(`https://cherrypimps.com/categories/${slug}_${page}_d.html`, { selectAll: '.item-updates .item-video' }); if (res.ok) { return scrapeAll(res.context, channel); } return res.status; } function scrapeScene({ query }, url) { const release = { url }; const { pathname } = new URL(url); release.entryId = pathname.match(/\/trailers\/(.*).html/)[1]; release.title = query.content('.item-title h1'); release.description = query.content('.update-info-block p'); release.date = query.date('.update-info-row:first-child', 'MMMM D, YYYY'); release.duration = query.duration('.update-info-row:last-child'); release.photoCount = query.number('.update-info-row:last-child', { match: /(\d+) photos/i, matchIndex: 1 }); release.actors = query.all('.models-list-thumbs .model-list-item').map((actorEl) => ({ name: unprint.query.content(actorEl, 'span'), url: unprint.query.url(actorEl, 'a'), avatar: [ unprint.query.img(actorEl, 'img', { attribute: 'src0_3x' }), unprint.query.img(actorEl, 'img', { attribute: 'src0_2x' }), unprint.query.img(actorEl, 'img', { attribute: 'src0_1x' }), ], })); release.tags = query.contents('.update-info-block a[href*="categories/"]'); release.poster = [ query.img('.update_thumb', { attribute: 'src0_3x' }), query.img('.update_thumb', { attribute: 'src0_2x' }), query.img('.update_thumb', { attribute: 'src0_1x' }), // usually only this one available ].filter(Boolean); // faux video trailer player redirects to signup return release; } async function fetchScene(url, site, release) { const res = await unprint.get(url); if (res.ok) { return scrapeScene(res.context, url, site, release); } return res.status; } function scrapeProfile({ query }, url) { const profile = { url }; const bio = Object.fromEntries(query.all('.model-stats li').map((bioEl) => [ slugify(unprint.query.content(bioEl, 'strong'), '_'), unprint.query.text(bioEl), ])); profile.height = Number(bio.height?.match(/\((\d+)\s*cm\)/)?.[1]) || null; profile.weight = Number(bio.weight?.match(/\((\d+)\s*kg\)/)?.[1]) || null; profile.age = parseInt(bio.age, 10) || null; profile.dateOfBirth = unprint.extractDate(bio.date_of_birth, 'MMMM D, YYYY'); profile.birthPlace = bio.birthplace; profile.ethnicity = bio.race; profile.measurements = bio.measurements; profile.hair = bio.hair_color; profile.eyes = bio.eye_color; if (/various|several/i.test(bio.tattoos)) profile.hasTattoos = true; else if (/none/i.test(bio.tattoos)) profile.hasTattoos = false; else if (bio.tattoos) { profile.hasTattoos = true; profile.tattoos = bio.tattoos; } if (/various/i.test(bio.piercings)) profile.hasPiercings = true; else if (/none/i.test(bio.piercings)) profile.hasPiercings = false; else if (bio.piercings) { profile.hasPiercings = true; profile.piercings = bio.piercings; } profile.aliases = bio.aliases?.split(',').map((alias) => alias.trim()); profile.avatar = [ query.img('.model-img img, .model_bio_thumb', { attribute: 'src0_3x' }), query.img('.model-img img, .model_bio_thumb', { attribute: 'src0_2x' }), query.img('.model-img img, .model_bio_thumb', { attribute: 'src0_1x' }), ]; return profile; } async function fetchProfile({ name: actorName, url: actorUrl }, { channel, network }) { const origin = new URL(channel?.url || network.url).origin; const { res, url } = await tryUrls([ actorUrl, `${origin}/models/${slugify(actorName, '')}.html`, `${origin}/models/${slugify(actorName)}.html`, ]); if (res.ok) { return scrapeProfile(res.context, url); } return res.status; } module.exports = { fetchLatest, fetchScene, fetchProfile, };