traxxx/src/scrapers/hookuphotshot.js

113 lines
3.2 KiB
JavaScript
Executable File

'use strict';
const unprint = require('unprint');
function scrapeAll(scenes, _channel) {
return scenes.map(({ query }) => {
const release = {};
release.url = query.url('.item-thumb a, .item-info h4 a');
release.entryId = new URL(release.url).pathname.match(/\/trailers\/(.*)\.html/)[1].toLowerCase();
release.title = query.content('.item-info h4 a, .item-info a[title]');
release.date = query.date('.date', 'YYYY-MM-DD');
release.duration = query.duration('.time');
release.photoCount = query.number('.time');
const photoCount = query.number('.item-thumb img.mainThumb', { attribute: 'cnt' });
if (photoCount) {
[release.poster, ...release.photos] = Array.from({ length: photoCount }, (value, index) => [
query.img('.item-thumb img.mainThumb', { attribute: `src${index}_2x` }),
query.img('.item-thumb img.mainThumb', { attribute: `src${index}_3x` }), // 3x is too big and usually inflated, try 2x first
query.img('.item-thumb img.mainThumb', { attribute: `src${index}_1x` }),
]);
}
return release;
});
}
function scrapeScene({ query, html }, { url, entity, baseRelease }) {
const release = {};
release.entryId = new URL(url).pathname.match(/\/trailers\/(.*)\.html/)[1].toLowerCase();
release.title = query.content('.videoDetails h3');
release.description = query.content('.videoDetails p');
release.date = query.date('.videoInfo', 'MMMM D, YYYY');
release.duration = query.duration('.videoInfo');
release.actors = query.all('.update_models a').map((actorEl) => ({
name: unprint.query.content(actorEl),
url: unprint.query.url(actorEl, null),
}));
release.tags = query.contents('.featuring a[href*="categories/"]');
release.photoCount = query.number('.videoInfo', { match: /(\d+) photos/i, matchIndex: 1 });
release.trailer = unprint.prefixUrl(html.match(/src="(\/trailers\/.*\.mp4)"/)?.[1], entity.url);
const posterUrl = unprint.prefixUrl(html.match(/poster="(\/content\/.*\.jpg)"/)?.[1], entity.url);
const posterFallbacks = [
posterUrl.replace('-1x', '-2x'),
posterUrl.replace('-1x', '-3x'),
posterUrl,
];
// scene page poster does not appear on update page
if (baseRelease?.poster) {
release.photos = [posterFallbacks, ...(baseRelease.photos || [])];
} else {
release.poster = posterFallbacks;
}
return release;
}
function scrapeProfile({ query }) {
const profile = {};
profile.description = query.content('.profile-about') || null;
profile.avatar = [
query.img('.profile-pic img', { attribute: 'src0_1x' }),
// too big, not desirable unless 1x fails
query.img('.profile-pic img', { attribute: 'src0_2x' }),
query.img('.profile-pic img', { attribute: 'src0_3x' }),
];
return profile;
}
async function fetchLatest(channel, page = 1) {
const url = `${channel.url}/categories/movies/${page}/latest/`;
const res = await unprint.get(url, { selectAll: '.items .item-video' });
if (res.ok) {
return scrapeAll(res.context, channel);
}
return res.status;
}
async function fetchProfile(actor, entity) {
const url = actor.url || `${entity.url}/models/${actor.slug}.html`;
const res = await unprint.get(url);
if (res.ok) {
return scrapeProfile(res.context);
}
return res.status;
}
module.exports = {
fetchLatest,
fetchProfile,
scrapeScene,
};