Added RSGH Media sites.

This commit is contained in:
DebaucheryLibrarian
2026-10-04 02:30:57 +02:00
parent d0149e70a7
commit 6643185a81
5 changed files with 219 additions and 1 deletions
+5
View File
@@ -783,6 +783,11 @@ const networks = [
parentSession: false,
},
},
{
slug: 'rsghmedia',
name: 'RSGH Media',
hasLogo: false,
},
{
slug: 'score',
name: 'SCORE',
+53
View File
@@ -11114,6 +11114,59 @@ const sites = [
slug: 'rickysroom',
url: 'https://rickysroom.com',
},
// RSGH MEDIA
{
name: 'Family Therapy XXX',
slug: 'familytherapyxxx',
url: 'https://familytherapyxxx.com',
tags: ['family'],
parent: 'rsghmedia',
independent: true,
},
{
name: 'Mom Comes First',
slug: 'momcomesfirst',
url: 'https://momcomesfirst.com',
tags: ['family'],
parent: 'rsghmedia',
independent: true,
},
{
name: 'Perfect Girlfriend',
slug: 'perfectgirlfriend',
url: 'https://perfectgirlfriend.com',
parent: 'rsghmedia',
independent: true,
},
{
name: 'Anal Therapy XXX',
slug: 'analtherapyxxx',
url: 'https://analtherapyxxx.com',
tags: ['anal'],
parent: 'rsghmedia',
independent: true,
},
{
name: 'Cock Ninja Studios',
slug: 'cockninjastudios',
url: 'https://cockninjastudios.com',
parent: 'rsghmedia',
independent: true,
},
{
name: 'Wife Loves Black',
slug: 'wifelovesblack',
url: 'https://wifelovesblack.com',
parent: 'rsghmedia',
independent: true,
},
{
name: 'Teen Loves Black',
slug: 'teenlovesblack',
url: 'https://teenlovesblack.com',
parent: 'rsghmedia',
independent: true,
},
// SCORE
{
name: '18 Eighteen',
+2
View File
@@ -62,6 +62,7 @@ const pornworld = require('./pornworld');
const privateNetwork = require('./private'); // reserved keyword
const radical = require('./radical');
const rickysroom = require('./rickysroom');
const rsghmedia = require('./rsghmedia');
const sexlikereal = require('./sexlikereal');
const score = require('./score');
const sicflics = require('./sicflics');
@@ -172,6 +173,7 @@ module.exports = {
private: privateNetwork,
radical,
rickysroom,
rsghmedia,
score,
sexlikereal,
sicflics,
+158
View File
@@ -0,0 +1,158 @@
'use strict';
const unprint = require('unprint');
const escapeRegexp = require('escape-string-regexp');
const slugify = require('../utils/slugify');
const capitalize = require('../utils/capitalize');
function getDate(query, release) {
const date = query.date('.published', 'MMMM D, YYYY') || query.date('meta[itemprop="uploadDate"]', 'YYYY-MM-DD', { attribute: 'content' });
if (date) {
return {
date,
datePrecision: 'day',
};
}
const poster = Array.isArray(release.poster)
? release.poster[0]
: release.poster;
if (poster) {
const [, year, month] = new URL(poster).pathname.match(/\/uploads\/(\d{4})\/(\d{2})/) || [];
if (year && month) {
return {
date: new Date(Date.UTC(year, month - 1, 1)),
datePrecision: 'month',
};
}
}
return {};
}
function scrapeAll(scenes) {
return scenes.map(({ query, element }) => {
const release = {};
release.url = query.url('.entry-title a, a.entry-featured-image-url');
release.entryId = new URL(release.url).pathname.match(/\/([\w-]+)/)[1];
const postId = unprint.query.number(element, null, { attribute: 'id' });
if (postId) {
release.attributes = { postId };
}
release.title = query.content('.entry-title a');
// description is cut off
release.tags = query.contents('.post-meta a[href*="/category"], .post-meta a[href*="/tag"]'); // tag only seen on scene page, but just in case
release.poster = Array.from(new Set(query.sourceSet('.entry-featured-image-url img')));
const { date, datePrecision } = getDate(query, release);
release.date = date;
release.datePrecision = datePrecision;
return release;
}).toSorted((sceneA, sceneB) => {
// HTML is laid out in columns that does not align with chronology
if (sceneA.attributes?.postId && sceneB.attributes?.postId) {
return sceneB.attributes.postId - sceneA.attributes.postId;
}
if (sceneA.date && sceneB.date) {
return sceneB.date - sceneA.date;
}
return 0;
});
}
async function fetchLatest(channel, page = 1) {
const url = `${channel.origin}/page/${page}`;
const res = await unprint.get(url, { selectAll: '.et_pb_blog_grid .et_pb_post' });
if (res.ok) {
return scrapeAll(res.context, channel);
}
return res.status;
}
function scrapeScene({ query }, { url }) {
const release = {};
release.entryId = new URL(url).pathname.match(/\/([\w-]+)/)[1];
const postId = query.number('article[id*="post"]', { attribute: 'id' });
if (postId) {
release.attributes = { postId };
}
release.title = query.content('.entry-title');
release.description = query.content('.entry-content p');
release.actors = query.all('.st-post-tags a[href*="/tag"]').map((actorEl) => ({
name: unprint.query.content(actorEl),
url: unprint.query.url(actorEl, null),
}));
release.poster = query.img('meta[property="og:image"], meta[property="twitter:image"]', { attribute: 'content' })
|| query.sourceSet('.et_post_meta_wrapper img');
release.trailer = query.video('.entry-content source');
const { date, datePrecision } = getDate(query, release);
release.date = date;
release.datePrecision = datePrecision;
const tags = query.all('.post-meta a[href*="/category"], .post-meta a[href*="/tag"]');
const starring = query.content('//div[contains(@class, "entry-content")]/p[contains(text(), "Starring")]')
|| query.content('//div[contains(@class, "entry-content")]/p[contains(text(), "***")]')
|| release.title?.split(/\s[-–]\s/).at(-1);
if (tags.length > 0) {
release.tags = tags.map((tagEl) => unprint.query.content(tagEl));
} else {
release.tags = query.content('article[id*="post"]', { attribute: 'class' })
?.match(/(category|tag)-[\w-]+/g)
?.map((tag) => slugify(tag.replace(/(category|tag)-/, ''), ' '))
|| [];
}
if (release.actors.length === 0 && release.tags.length > 0 && starring) {
// actors are always added as tags, sometimes they're clearly divided, sometimes they're mixed in with other tags
// the tail end of the description or title contains a *starring* section against which the tags can be matched
release.actors = release.tags
.map((tag) => {
if (tag === 'alex adams') {
// household male performer that is not usually mentioned in the starring section
return capitalize(tag);
}
// use description text instead of tag name for capitalization
const match = starring.match(new RegExp(`\\b${escapeRegexp(tag)}\\b`, 'i'))?.[0];
if (match) {
return match;
}
return null;
})
.filter(Boolean);
}
return release;
}
module.exports = {
fetchLatest,
scrapeScene,
};
+1 -1
View File
@@ -31,7 +31,7 @@ function scrapeAll(scenes) {
}
async function fetchLatest(channel, page = 1) {
const url = `${channel.url}/${page}`;
const url = `${channel.origin}/${page}`;
const res = await unprint.get(url, { selectAll: '.scene' });
if (res.ok) {