'use strict'; const bhttp = require('bhttp'); const { post } = require('../utils/http'); const { extractDate } = require('../utils/qu'); const { inchesToCm } = require('../utils/convert'); const slugify = require('../utils/slugify'); const clusterId = '617fb597b659459bafe6472470d9073a'; const authKey = 'YmFuZy1yZWFkOktqVDN0RzJacmQ1TFNRazI='; const genderMap = { M: 'male', F: 'female', }; function getScreenUrl(item, scene) { return `https://i.bang.com/screenshots/${scene.dvd.id}/movie/${scene.order}/${item.screenId}.jpg`; } function encodeId(id) { return Buffer .from(id, 'hex') .toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ','); } function decodeId(id) { const restoredId = id .replace(/-/g, '+') .replace(/_/g, '/') .replace(/,/g, '='); return Buffer .from(restoredId, 'base64') .toString('hex'); } function scrapeScene(scene, site) { const release = { site, entryId: scene.id, title: scene.name, description: scene.description, tags: scene.genres.concat(scene.actions).map(genre => genre.name), duration: scene.duration, }; const slug = slugify(release.title); release.url = `https://www.bang.com/video/${encodeId(release.entryId)}/${slug}`; const date = new Date(scene.releaseDate); release.date = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())); release.actors = scene.actors.map(actor => ({ name: actor.name, gender: genderMap[actor.gender] })); if (scene.is4k) release.tags.push('4k'); if (scene.gay) release.tags.push('gay'); const defaultPoster = scene.screenshots.find(photo => photo.default === true); const photoset = scene.screenshots.filter(photo => photo.default === false); const photos = defaultPoster ? photoset : photoset.slice(1); const poster = defaultPoster || photoset[0]; release.poster = getScreenUrl(poster, scene); release.photos = photos.map(photo => getScreenUrl(photo, scene)); release.trailer = { src: `https://i.bang.com/v/${scene.dvd.id}/${scene.identifier}/preview.mp4`, }; release.channel = scene.series.name .replace(/[! .]/g, '') .replace('&', 'and'); return release; } function scrapeLatest(scenes, site) { return scenes.map(({ _source: scene }) => scrapeScene(scene, site)); } function scrapeProfile(actor) { const profile = {}; profile.aliases = actor.aliases; profile.dateOfBirth = extractDate(actor.birthDate); profile.gender = ({ F: 'female', M: 'male' })[actor.gender]; profile.ethnicity = actor.ethnicity; profile.nationality = actor.nationality; profile.birthPlace = `${actor.birthCity}, ${actor.birthCountry || ''}`; profile.hair = actor.hairColor; profile.eyes = actor.eyeColor; profile.naturalBoobs = actor.naturalBreasts; if (actor.measurements) { const { cupSize, shoulder, chest, waist, height } = actor.measurements; if (height) profile.height = inchesToCm(height); if (cupSize) profile.cup = cupSize; // [SIC] if (shoulder) profile.bust = shoulder; if (chest) profile.waist = chest; if (waist) profile.hip = waist; } if (actor.twitter) profile.social = [`https://www.twitter.com/${actor.twitter}`]; if (actor.image) profile.avatar = `https://i.bang.com/pornstars/${actor.identifier}.jpg`; // TODO: get releases return profile; } async function fetchLatest(site, page = 1) { const res = await bhttp.post(`https://${clusterId}.us-east-1.aws.found.io/videos/video/_search`, { size: 50, from: (page - 1) * 50, query: { bool: { must: [ { match: { status: 'ok', }, }, { range: { releaseDate: { lte: 'now', }, }, }, /* * global fetch { nested: { path: 'studio', query: { bool: { must: [ { match: { 'studio.name': { operator: 'AND', query: 'bang! originals', }, }, }, ], }, }, }, }, */ { nested: { path: 'series', query: { bool: { must: [ { match: { 'series.id': { operator: 'AND', query: site.parameters.siteId, }, }, }, ], }, }, }, }, ], must_not: [ { match: { type: 'trailer', }, }, ], }, }, sort: [ { releaseDate: { order: 'desc', }, }, ], }, { encodeJSON: true, headers: { Authorization: `Basic ${authKey}`, }, }); return scrapeLatest(res.body.hits.hits, site); } async function fetchScene(url, site) { const encodedId = new URL(url).pathname.split('/')[2]; const entryId = decodeId(encodedId); const res = await bhttp.get(`https://${clusterId}.us-east-1.aws.found.io/videos/video/${entryId}`, { headers: { Authorization: `Basic ${authKey}`, }, }); return scrapeScene(res.body._source, site); // eslint-disable-line no-underscore-dangle } async function fetchProfile(actorName) { const res = await post(`https://${clusterId}.us-east-1.aws.found.io/actors/actor/_search`, { size: 5, sort: [{ _score: { order: 'desc', }, }], query: { bool: { must: [ { match: { name: { query: actorName, operator: 'and', }, }, }, { match: { status: 'ok', }, }, ], }, }, }, { Authorization: `Basic ${authKey}`, }, { encodeJSON: true }); if (res.ok) { const actor = res.body.hits.hits.find(hit => hit._source.name === actorName); return scrapeProfile(actor._source); } return res.status; } module.exports = { fetchLatest, fetchProfile, fetchScene, };