'use strict'; const unprint = require('unprint'); const slugify = require('../utils/slugify'); function scrapeAll(scenes, channel) { return scenes.map(({ query }) => { const release = {}; const network = channel.type === 'network' ? channel : channel.parent; release.url = query.url('.thumb__title-link'); release.entryId = new URL(release.url).pathname.match(/\/trailers\/(.+).html/)?.[1].toLowerCase(); release.title = query.content('.thumb__title-link'); release.date = query.date('time', unprint.dateConstants.DATETIME_LOCAL_SECONDS, { attribute: 'datetime' }) || query.date('time', 'MMMM D, YYYY'); release.actors = query.all('.actor__link').map((el) => ({ name: unprint.query.content(el), url: unprint.query.url(el, null), })); release.poster = query.img('.thumb__picture img'); [release.poster, ...release.photos] = Object .entries(query.el('.thumb__image').dataset) .filter(([key]) => /^src/.test(key)) .map(([, value]) => [value.replace('-1x', '-2x'), value].map((path) => unprint.prefixUrl(path, network.url))); release.teaser = query.video('.thumb__video', { origin: network.url }); release.likes = query.number('.thumb__rating-value'); const channelSlug = slugify(query.content('.thumb__detail__site-link'), ''); if (channelSlug) { release.channel = network.children.find((child) => child.slug === channelSlug)?.slug; } return release; }); } function scrapeScene({ query }, context) { const release = {}; release.entryId = new URL(context.url).pathname.match(/\/trailers\/(.+).html/)[1].toLowerCase(); release.title = query.content('.video-title'); release.description = query.content('.video-description-text'); release.date = query.date('.video-info-date', 'MMMM D, YYYY'); release.duration = query.number('.video-info-time') * 60; release.actors = query.all('.video-actor-link').map((el) => ({ name: unprint.query.content(el), url: unprint.query.url(el, null), })); release.tags = query.contents('.video-tag-link'); const poster = query.img('.video-cover__image'); if (!poster.includes('error')) { release.poster = poster; } release.trailer = query.video(); release.likes = query.number('.video-actions-button [data-count]'); release.channel = slugify(query.content('.video-site-link'), ''); return release; } function scrapeProfile({ query }, url, entity) { const profile = {}; const bio = query.all('.model-profile-information-element').reduce((acc, bioEl) => ({ ...acc, [slugify(unprint.query.content(bioEl, '.model-profile-information-key'))]: unprint.query.text(bioEl), }), {}); profile.url = url; profile.description = query.content('.model-profile-about'); profile.hairColor = bio.hair; profile.eyes = bio.eyes; profile.avatar = query.img('.model-profile-image-picture source', { origin: entity.url, attribute: 'srcset' }) || query.img('.model-profile-image-picture img', { origin: entity.url }); profile.scenes = scrapeAll(unprint.initAll(query.all('.video-list .thumb')), entity); return profile; } async function fetchLatest(channel, page = 1) { const url = `${channel.type === 'network' ? channel.url : channel.parent.url}/search.php?site[]=${channel.parameters.siteId}&page=${page}`; const res = await unprint.get(url, { selectAll: '.video-list .thumb' }); if (res.ok) { return scrapeAll(res.context, channel); } return res.status; } async function fetchProfile(actor, entity, include) { const url = actor.url || `${entity.url}/models/${slugify(actor.name)}.html`; const res = await unprint.get(url); if (res.ok) { return scrapeProfile(res.context, url, entity, include); } return res.status; } module.exports = { fetchLatest, fetchProfile, scrapeScene: { scraper: scrapeScene, unprint: true, }, };