Added Model Media API layout, renamed AsiaM.
This commit is contained in:
parent
e91ff659e9
commit
5a210451e0
|
|
@ -94,7 +94,7 @@
|
|||
"tunnel": "0.0.6",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"undici": "^5.28.1",
|
||||
"unprint": "^0.18.25",
|
||||
"unprint": "^0.18.26",
|
||||
"url-pattern": "^1.0.3",
|
||||
"v-tooltip": "^2.1.3",
|
||||
"video.js": "^8.6.1",
|
||||
|
|
@ -20380,9 +20380,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/unprint": {
|
||||
"version": "0.18.25",
|
||||
"resolved": "https://registry.npmjs.org/unprint/-/unprint-0.18.25.tgz",
|
||||
"integrity": "sha512-eGUq818vUOHOqJ1niie/2SH3nu3zf6yPWQrlPgpJJOi8QqxS1XLHVStwU7Ql7VdBPXjS1BbWoHbX+JQptKGQAQ==",
|
||||
"version": "0.18.26",
|
||||
"resolved": "https://registry.npmjs.org/unprint/-/unprint-0.18.26.tgz",
|
||||
"integrity": "sha512-E9DAwwBtwZmg+2A6y7Ili94XGCkjuyl5saVA+G5oEy6lnFvum58aubOtfQO1Qasiy/xVeBZbwk5QZ/VPzGIchw==",
|
||||
"dependencies": {
|
||||
"bottleneck": "^2.19.5",
|
||||
"cookie": "^1.1.1",
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@
|
|||
"tunnel": "0.0.6",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"undici": "^5.28.1",
|
||||
"unprint": "^0.18.25",
|
||||
"unprint": "^0.18.26",
|
||||
"url-pattern": "^1.0.3",
|
||||
"v-tooltip": "^2.1.3",
|
||||
"video.js": "^8.6.1",
|
||||
|
|
|
|||
|
|
@ -7908,11 +7908,18 @@ const sites = [
|
|||
parent: 'modelmedia',
|
||||
},
|
||||
{
|
||||
slug: 'asiam',
|
||||
name: 'AsiaM',
|
||||
slug: 'modelmediaasia',
|
||||
rename: 'asiam',
|
||||
name: 'Model Media Asia',
|
||||
url: 'https://www.modelmediaasia.com',
|
||||
independent: true,
|
||||
tags: ['asian'],
|
||||
parent: 'modelmedia',
|
||||
parameters: {
|
||||
layout: 'api',
|
||||
basePath: '/en-US',
|
||||
api: 'https://model-api.bvncmsldo.com/api/v2',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'jerkaoke',
|
||||
|
|
|
|||
|
|
@ -171,9 +171,6 @@ module.exports = {
|
|||
aziani,
|
||||
'2poles1hole': aziani,
|
||||
creampiled: aziani,
|
||||
// woodman
|
||||
pierrewoodman,
|
||||
wakeupnfuck: pierrewoodman,
|
||||
// naughty america
|
||||
naughtyamerica,
|
||||
tonightsgirlfriend: naughtyamerica,
|
||||
|
|
@ -200,21 +197,24 @@ module.exports = {
|
|||
swappz: teamskeet,
|
||||
freeuse: teamskeet,
|
||||
familystrokes: teamskeet,
|
||||
// model media
|
||||
jerkaoke: modelmedia,
|
||||
modelmediaasia: modelmedia,
|
||||
// delphine: modelmedia,
|
||||
// etc
|
||||
'18vr': badoink,
|
||||
theflourishxxx: theflourish,
|
||||
pierrewoodman,
|
||||
exploitedx, // only from known URL that will specify site
|
||||
fullpornnetwork,
|
||||
adultempire,
|
||||
allherluv: missax,
|
||||
americanpornstar,
|
||||
angelogodshackoriginal,
|
||||
asiam: modelmedia,
|
||||
babevr: badoink,
|
||||
badoinkvr: badoink,
|
||||
bamvisions,
|
||||
bang,
|
||||
// delphine: modelmedia,
|
||||
meidenvanholland: bluedonkeymedia, // Vurig Vlaanderen uses same database
|
||||
boobpedia,
|
||||
bradmontana,
|
||||
|
|
@ -225,7 +225,6 @@ module.exports = {
|
|||
hitzefrei,
|
||||
hookuphotshot,
|
||||
inthecrack,
|
||||
jerkaoke: modelmedia,
|
||||
karups,
|
||||
boyfun: karups,
|
||||
kellymadison,
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ function scrapeProfile(html, actorName) {
|
|||
|
||||
if (bio.dateOfBirth) profile.birthdate = moment.utc(bio.dateOfBirth, 'YYYY-MM-DD').toDate();
|
||||
|
||||
if (profile.placeOfBirth && bio.country) profile.birthPlace = `${bio.placeOfBirth}, ${bio.country}`;
|
||||
if (bio.placeOfBirth && bio.country) profile.birthPlace = `${bio.placeOfBirth}, ${bio.country}`;
|
||||
else if (bio.country) profile.birthPlace = bio.country;
|
||||
|
||||
profile.eyes = bio.eyeColor;
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ function scrapeProfile(data) {
|
|||
|
||||
profile.dateOfBirth = unprint.extractDate(bio.birthdate, 'YYYY-MM-DD');
|
||||
profile.age = bio.age;
|
||||
profile.placeOfBirth = bio.born;
|
||||
profile.birthPlace = bio.born;
|
||||
|
||||
profile.measurements = bio.measurements;
|
||||
profile.height = convert(bio.height, 'cm');
|
||||
|
|
|
|||
|
|
@ -2,6 +2,71 @@
|
|||
|
||||
const unprint = require('unprint');
|
||||
|
||||
const slugify = require('../utils/slugify');
|
||||
|
||||
function scrapeSceneApi(scene, channel, parameters) {
|
||||
const release = {};
|
||||
|
||||
release.entryId = scene.id;
|
||||
release.shootId = scene.serial_number;
|
||||
|
||||
release.url = `${channel.origin}${parameters.basePath || ''}/videos/${release.shootId}`;
|
||||
|
||||
release.title = scene.title;
|
||||
release.altTitles = [scene.title_cn].filter(Boolean);
|
||||
|
||||
release.description = scene.description;
|
||||
release.altDescriptions = [scene.description_cn].filter(Boolean);
|
||||
|
||||
release.date = new Date(scene.published_at);
|
||||
release.duration = scene.duration;
|
||||
|
||||
release.actors = scene.models?.map((model) => ({
|
||||
name: model.name,
|
||||
alias: [model.name_cn].filter(Boolean),
|
||||
gender: model.gender,
|
||||
entryId: model.id,
|
||||
avatar: Array.from(new Set([
|
||||
model.avatar,
|
||||
model.avatar?.replace('_compressed', ''), // this is often a wider image, not just uncompressed
|
||||
])).filter(Boolean),
|
||||
}));
|
||||
|
||||
release.tags = scene.tags?.map((tag) => tag.name);
|
||||
|
||||
release.poster = scene.cover;
|
||||
release.trailer = scene.preview_video;
|
||||
|
||||
return release;
|
||||
}
|
||||
|
||||
async function fetchLatestApi(channel, page, { parameters }) {
|
||||
const res = await unprint.get(`${parameters.api}/videos?page=${page}&pageSize=12&sort=published_at`);
|
||||
|
||||
if (res.ok && res.data?.status) {
|
||||
return res.data.data.list.map((scene) => scrapeSceneApi(scene, channel, parameters));
|
||||
}
|
||||
|
||||
return res.status;
|
||||
}
|
||||
|
||||
async function fetchSceneApi(url, channel, _baseRelease, { parameters }) {
|
||||
// shallow data missing actors and tags
|
||||
const shootId = new URL(url).pathname.match(/\/videos\/([\w-]+)/)?.[1];
|
||||
|
||||
if (!shootId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const res = await unprint.get(`${parameters.api}/videos/${shootId}`);
|
||||
|
||||
if (res.ok) {
|
||||
return scrapeSceneApi(res.data.data, channel, parameters);
|
||||
}
|
||||
|
||||
return res.status;
|
||||
}
|
||||
|
||||
function scrapeAll(scenes) {
|
||||
return scenes.map(({ query }) => {
|
||||
const release = {};
|
||||
|
|
@ -10,7 +75,7 @@ function scrapeAll(scenes) {
|
|||
|
||||
const url = query.url(null);
|
||||
|
||||
if (url) {
|
||||
if (url && !url.includes('/plans')) {
|
||||
const { origin, pathname, searchParams } = new URL(url);
|
||||
|
||||
release.url = `${origin}${pathname}`;
|
||||
|
|
@ -63,6 +128,78 @@ function scrapeAll(scenes) {
|
|||
});
|
||||
}
|
||||
|
||||
function scrapeProfileApi(model, channel, parameters) {
|
||||
const profile = {};
|
||||
|
||||
profile.entryId = model.id;
|
||||
profile.url = `${channel.origin}${parameters.basePath || ''}/models/${model.id}`;
|
||||
|
||||
profile.description = model.description || null;
|
||||
|
||||
profile.gender = model.gender;
|
||||
profile.alias = [model.name_cn].filter(Boolean);
|
||||
|
||||
if (!model.birth_day?.includes('0001')) {
|
||||
profile.dateOfBirth = unprint.extractDate(model.birth_day, 'YYYY-MM-DD');
|
||||
}
|
||||
|
||||
profile.birthPlace = model.birth_place || null;
|
||||
|
||||
profile.height = model.height_cm || null;
|
||||
profile.weight = model.weight_kg || null;
|
||||
|
||||
profile.bust = model.measurements_chest;
|
||||
profile.waist = model.measurements_waist;
|
||||
profile.hip = model.measurements_hips;
|
||||
|
||||
profile.avatar = Array.from(new Set([
|
||||
model.avatar,
|
||||
model.avatar?.replace('_compressed', ''), // this is often a wider image, not just uncompressed
|
||||
])).filter(Boolean);
|
||||
|
||||
profile.socials = model.socialmedia;
|
||||
|
||||
profile.scenes = model.videos.map((scene) => scrapeSceneApi(scene, channel, parameters));
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
async function getModelId(actor, parameters) {
|
||||
if (actor.url) {
|
||||
const modelId = new URL(actor.url).pathname.match(/\/models\/\d+/)?.[1];
|
||||
|
||||
if (modelId) {
|
||||
return Number(modelId);
|
||||
}
|
||||
}
|
||||
|
||||
const res = await unprint.get(`${parameters.api}/search?keyword=${slugify(actor.name, '+')}`);
|
||||
|
||||
if (res.ok) {
|
||||
const model = res.data.data?.models?.find((modelResult) => slugify(modelResult.name) === actor.slug);
|
||||
|
||||
if (model) {
|
||||
return model.id;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function fetchProfileApi(actor, { entity, parameters }) {
|
||||
const modelId = await getModelId(actor, parameters);
|
||||
|
||||
if (modelId) {
|
||||
const res = await unprint.get(`${parameters.api}/models/${modelId}`);
|
||||
|
||||
if (res.ok && res.data.data) {
|
||||
return scrapeProfileApi(res.data.data, entity, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function scrapeProfile({ query }) {
|
||||
const profile = {};
|
||||
const avatar = query.img('div[class*="prof-pic"] > img');
|
||||
|
|
@ -88,7 +225,7 @@ function scrapeProfile({ query }) {
|
|||
return profile;
|
||||
}
|
||||
|
||||
async function getCookie(channel) {
|
||||
async function getCookie(channel, _parameters) {
|
||||
const tokenRes = await unprint.get(channel.url);
|
||||
|
||||
if (!tokenRes.ok) {
|
||||
|
|
@ -116,8 +253,8 @@ async function getCookie(channel) {
|
|||
return cookie;
|
||||
}
|
||||
|
||||
async function fetchLatest(channel, page) {
|
||||
const cookie = await getCookie(channel);
|
||||
async function fetchLatest(channel, page, context) {
|
||||
const cookie = await getCookie(channel, context.parameters);
|
||||
|
||||
const res = await unprint.get(`${channel.url}/videos?sort=published_at&page=${page}`, {
|
||||
selectAll: '.row a[video-id]',
|
||||
|
|
@ -136,7 +273,7 @@ async function fetchLatest(channel, page) {
|
|||
// deep pages are paywalled
|
||||
|
||||
async function searchProfile(actor, context, cookie) {
|
||||
const searchRes = await unprint.get(`${context.channel.url}/livesearch?keyword=${actor.name}`, {
|
||||
const searchRes = await unprint.get(`${context.channel.url}${context.parameters.searchPath || '/livesearch'}?${context.parameters.searchParameter || 'keyword'}=${actor.name}`, {
|
||||
headers: {
|
||||
cookie,
|
||||
},
|
||||
|
|
@ -150,7 +287,7 @@ async function searchProfile(actor, context, cookie) {
|
|||
}
|
||||
|
||||
async function fetchProfile(actor, context) {
|
||||
const cookie = await getCookie(context.entity);
|
||||
const cookie = await getCookie(context.entity, context.parameters);
|
||||
const actorUrl = actor.url || await searchProfile(actor, context, cookie);
|
||||
|
||||
if (!actorUrl) {
|
||||
|
|
@ -173,4 +310,9 @@ async function fetchProfile(actor, context) {
|
|||
module.exports = {
|
||||
fetchLatest,
|
||||
fetchProfile,
|
||||
api: {
|
||||
fetchLatest: fetchLatestApi,
|
||||
fetchScene: fetchSceneApi,
|
||||
fetchProfile: fetchProfileApi,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ async function findModel(actor, entity) {
|
|||
const modelEl = resModels.context.query.all('.content-grid-item').find((el) => slugify(unprint.query.content(el, 'a.title')) === slugify(actor.name));
|
||||
|
||||
if (modelEl) {
|
||||
const modelUrl = `${origin}${unprint.query.url(modelEl, 'a.title')}`;
|
||||
const modelUrl = unprint.query.url(modelEl, 'a.title', { origin: entity.origin });
|
||||
const modelAvatar = unprint.query.sourceSet(modelEl, 'a picture img', 'data-srcset');
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ async function scrapeProfile({ query }, url, include) {
|
|||
`);
|
||||
|
||||
profile.nationality = bio.nationality;
|
||||
profile.placeOfBirth = bio.birth_place;
|
||||
profile.birthPlace = bio.birth_place;
|
||||
profile.age = unprint.extractNumber(bio.age);
|
||||
|
||||
profile.dateOfBirth = unprint.extractDate(bio.birth_date, 'MMM D, YYYY');
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ module.exports = {
|
|||
amateureuro: porndoe,
|
||||
amnesiac,
|
||||
angelogodshackoriginal,
|
||||
asiam: modelmedia,
|
||||
modelmediaasia: modelmedia,
|
||||
assylum,
|
||||
aziani,
|
||||
badoink,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ async function curateReleaseEntry(release, batchId, existingRelease, type = 'sce
|
|||
date_precision: release.datePrecision,
|
||||
slug,
|
||||
description: decode(release.description),
|
||||
alt_descriptions: release.altDescriptions?.map((description) => decode(description)),
|
||||
comment: release.comment,
|
||||
attributes: release.attributes,
|
||||
photo_count: Number(release.photoCount) || null,
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ const actors = [
|
|||
{ entity: 'nfbusty', name: 'Ella Reese', fields: ['avatar', 'age', 'residencePlace', 'height', 'measurements', 'photos'] },
|
||||
{ entity: 'nubilefilms', name: 'Jade Kimiko', fields: ['avatar', 'age', 'residencePlace', 'height', 'measurements', 'photos'] },
|
||||
{ entity: 'thatsitcomshow', name: 'Casey Calvert', fields: ['avatar', 'age', 'residencePlace', 'height', 'measurements', 'photos'] },
|
||||
{ entity: 'brattysis', name: 'Scarlett Alexis', fields: ['avatar', 'age', 'height', 'measurements', 'description', 'residencePlace', 'photos'] },
|
||||
// porndoe
|
||||
{ entity: 'vipsexvault', name: 'Amirah Adara', fields: ['avatar', 'nationality', 'placeOfBirth', 'age', 'naturalBoobs', 'hairColor', 'description'] },
|
||||
{ entity: 'amateureuro', name: 'Luna Oara', fields: ['avatar', 'nationality', 'placeOfBirth', 'age', 'naturalBoobs', 'description'] },
|
||||
|
|
@ -211,6 +212,9 @@ const actors = [
|
|||
{ entity: 'exploitedx', name: 'Megan Marx', url: 'https://excogigirls.com/models/megan-marx.html', fields: ['avatar', 'description', 'age', 'height', 'measurements'] },
|
||||
{ entity: 'exploitedx', name: 'Sophie Hunt', url: 'https://www.backroomcastingcouch.com/models/Sophie-Hunt.html', fields: ['avatar', 'age'] },
|
||||
{ entity: 'exploitedx', name: 'Lao Latina', url: 'https://hotmilfsfuck.com/models/Lao-Latina.html', fields: ['avatar', 'description', 'age', 'height', 'measurements'] },
|
||||
// model media
|
||||
{ entity: 'jerkaoke', name: 'Harley Haze', fields: ['avatar', 'description', 'height', 'weight', 'banner', 'photos'] },
|
||||
{ entity: 'modelmediaasia', name: 'Li WeiWei', fields: ['avatar', 'entryId', 'gender', 'alias', 'height', 'weight', 'bust', 'waist', 'hip', 'socials'] },
|
||||
// etc.
|
||||
{ entity: 'analvids', name: 'Veronica Leal', fields: ['avatar', 'gender', 'birthCountry', 'nationality', 'age', 'aliases', 'nationality'] },
|
||||
{ entity: 'bangbros', name: 'Kira Perez', fields: ['avatar', 'gender', 'ethnicity', 'hairColor'] },
|
||||
|
|
@ -227,6 +231,7 @@ const actors = [
|
|||
{ entity: 'porncz', name: 'Kama Oxi', fields: ['avatar', 'gender', 'birthCountry', 'ethnicity', 'age', 'hairColor', 'cup', 'naturalBoobs', 'hasTattoos'] },
|
||||
{ entity: 'score', name: 'Vanessa Blue', fields: ['avatar', 'gender', 'placeOfResidence', 'ethnicity', 'height', 'weight', 'measurements', 'hairColor', 'dateOfBirth'] },
|
||||
{ entity: 'pierrewoodman', name: 'Abby Lee Brazil', fields: ['avatar', 'nationality'] },
|
||||
{ entity: 'wakeupnfuck', name: 'Abby Lee Brazil', fields: ['avatar', 'nationality'] },
|
||||
{ entity: 'dorcelclub', name: 'Clea Gaultier', fields: ['avatar'] },
|
||||
{ entity: 'hitzefrei', name: 'Jolee Love', fields: ['avatar', 'dateOfBirth', 'birthPlace', 'measurements', 'height', 'weight', 'eyes', 'hair', 'description'] },
|
||||
{ entity: 'mariskax', name: 'Honey Demon', fields: ['avatar', 'gender', 'dateOfBirth', 'placeOfBirth', 'measurements', 'height', 'weight', 'hairColor', 'eyes'] },
|
||||
|
|
|
|||
Loading…
Reference in New Issue