10 Commits
6 changed files with 114 additions and 40 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "traxxx", "name": "traxxx",
"version": "1.256.0", "version": "1.256.5",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "traxxx", "name": "traxxx",
"version": "1.256.0", "version": "1.256.5",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.458.0", "@aws-sdk/client-s3": "^3.458.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "traxxx", "name": "traxxx",
"version": "1.256.0", "version": "1.256.5",
"description": "All the latest porn releases in one place", "description": "All the latest porn releases in one place",
"main": "src/app.js", "main": "src/app.js",
"scripts": { "scripts": {
+1 -1
View File
@@ -172,7 +172,7 @@ const networks = [
{ {
slug: 'familystrokes', slug: 'familystrokes',
name: 'Family Strokes', name: 'Family Strokes',
url: 'https://www.familystrokes.com/', url: 'https://www.familystrokes.com',
parent: 'paperstreetmedia', parent: 'paperstreetmedia',
parameters: { parameters: {
endpoint: 'familybundle', endpoint: 'familybundle',
+87 -11
View File
@@ -8517,6 +8517,33 @@ const sites = [
id: 'hookup-pad', id: 'hookup-pad',
}, },
}, },
{
slug: 'thepickupvan',
name: 'The Pickup Van',
url: 'https://www.mylf.com/series/the-pickup-van',
parent: 'mylf',
parameters: {
id: 'the-pickup-van',
},
},
{
slug: 'motherfucker',
name: 'Motherfucker',
url: 'https://www.mylf.com/series/motherfucker',
parent: 'mylf',
parameters: {
id: 'motherfucker',
},
},
{
slug: 'bluecollarbabes',
name: 'Blue Collar Babes',
url: 'https://www.mylf.com/series/blue-collar-babes',
parent: 'mylf',
parameters: {
id: 'blue-collar-babes',
},
},
/* premium */ /* premium */
{ {
slug: 'analmom', slug: 'analmom',
@@ -8551,12 +8578,9 @@ const sites = [
{ {
slug: 'mylfdom', slug: 'mylfdom',
name: 'MYLF Dom', name: 'MYLF Dom',
url: 'https://www.mylfdom.com', url: 'https://www.mylf.com/series/mylfdom-ts',
tags: ['bdsm', 'milf'], tags: ['bdsm', 'milf'],
parent: 'mylf', parent: 'mylf',
parameters: {
endpoint: 'mylf_dom',
},
}, },
// NAUGHTY AMERICA // NAUGHTY AMERICA
{ {
@@ -12397,13 +12421,6 @@ const sites = [
url: 'https://www.teamskeet.com/series/bad-milfs', url: 'https://www.teamskeet.com/series/bad-milfs',
parent: 'teamskeet', parent: 'teamskeet',
}, },
{
slug: 'blackstepdad',
name: 'Black StepDad',
url: 'https://www.teamskeet.com/series/black-stepdad',
alias: ['bsd'],
parent: 'teamskeet',
},
{ {
slug: 'exxxtrasmall', slug: 'exxxtrasmall',
name: 'Exxxtra Small', name: 'Exxxtra Small',
@@ -12783,6 +12800,44 @@ const sites = [
url: 'https://www.teamskeet.com/series/passport-bros', url: 'https://www.teamskeet.com/series/passport-bros',
parent: 'teamskeet', parent: 'teamskeet',
}, },
{
slug: 'underthetable',
name: 'Under the Table',
url: 'https://www.teamskeet.com/series/under-the-table',
parent: 'teamskeet',
},
{
slug: 'mydirtyuncle',
name: 'My Dirty Uncle',
url: 'https://www.teamskeet.com/series/my-dirty-uncle',
tags: ['family'],
parent: 'teamskeet',
},
{
slug: 'mormongirlz',
name: 'Mormon Girlz',
url: 'https://www.teamskeet.com/series/mormon-girlz',
tags: ['religion'],
parent: 'teamskeet',
},
{
slug: 'breedingmaterial',
name: 'Breeding Material',
url: 'https://www.teamskeet.com/series/breeding-material',
parent: 'teamskeet',
},
{
slug: 'teamskeetsingles',
name: 'TeamSkeet Singles',
url: 'https://www.teamskeet.com/series/teamskeet-singles',
parent: 'teamskeet',
},
{
slug: 'sheseducedme',
name: 'She Seduced Me',
url: 'https://www.teamskeet.com/series/she-seduced-me',
parent: 'teamskeet',
},
// PERVZ // PERVZ
{ {
slug: 'pervzfeatures', slug: 'pervzfeatures',
@@ -12859,6 +12914,12 @@ const sites = [
url: 'https://www.pervz.com/series/pervdriver', url: 'https://www.pervz.com/series/pervdriver',
parent: 'pervz', parent: 'pervz',
}, },
{
slug: 'charmed',
name: 'Charmed',
url: 'https://www.pervz.com/series/charmed',
parent: 'pervz',
},
// SWAPPZ // SWAPPZ
{ {
slug: 'swappzfeatures', slug: 'swappzfeatures',
@@ -12925,6 +12986,13 @@ const sites = [
parent: 'freeuse', parent: 'freeuse',
tags: ['free-use', 'pov'], tags: ['free-use', 'pov'],
}, },
{
slug: 'freeusesingles',
name: 'Freeuse Singles',
url: 'https://www.freeuse.com/series/freeuse-singles',
parent: 'freeuse',
tags: ['free-use'],
},
// FAMILY STROKES // FAMILY STROKES
{ {
slug: 'familystrokes', slug: 'familystrokes',
@@ -13006,6 +13074,14 @@ const sites = [
tags: ['family', 'milf'], tags: ['family', 'milf'],
parent: 'familystrokes', parent: 'familystrokes',
}, },
{
slug: 'blackstepdad',
name: 'Black StepDad',
url: 'https://www.familystrokes.com/series/black-stepdad',
alias: ['bsd'],
tags: ['family'],
parent: 'familystrokes',
},
// SAY UNCLE // SAY UNCLE
{ {
slug: 'blackgodz', slug: 'blackgodz',
+18 -21
View File
@@ -33,7 +33,8 @@ async function fetchTrailerUrl(videoId, entity) {
const token = res.data.token; const token = res.data.token;
if (token) { if (token) {
return `https://customer-bk3o9te23pydwwcb.cloudflarestream.com/${token}/manifest/video.mpd?parentOrigin=${entity.origin}`; // ffmpeg fails to read the DASH manifest over a single connection, HLS works
return `https://customer-bk3o9te23pydwwcb.cloudflarestream.com/${token}/manifest/video.m3u8?parentOrigin=${entity.origin}`;
} }
} }
@@ -46,17 +47,18 @@ async function scrapeScene(scene, channel, _parameters, includeTrailers) {
// release.entryId = scene.id; // legacy // release.entryId = scene.id; // legacy
release.entryId = scene.itemId; release.entryId = scene.itemId;
release.url = `${channel.type === 'network' || channel.parameters?.layout === 'organic' ? channel.url : channel.parent.url}/movies/${release.entryId}`; release.url = `${channel.origin}/movies/${scene.id || release.entryId}`; // numeric item ID URL 404s, id is the slug
release.title = scene.title; release.title = scene.title;
release.description = scene.description; release.description = scene.description;
release.date = unprint.extractDate(scene.publishedDate, 'YYYY-MM-DD'); release.date = unprint.extractDate(scene.publishedDate, 'YYYY-MM-DD');
release.duration = scene.d;
release.actors = scene.models?.map((model) => ({ release.actors = scene.models?.map((model) => ({
name: model.modelName || model.name || model.title, name: model.modelName || model.name || model.title,
avatar: model.img, avatar: model.img,
gender: model.gender, gender: model.gender,
url: `${channel.url}/models/${model.modelId || model.id}`, url: `${channel.origin}/models/${model.modelId || model.id}`,
})); }));
if (scene.img) { if (scene.img) {
@@ -92,7 +94,7 @@ function scrapeAll(scenes, channel, parameters) {
async function fetchLatest(channel, page = 1, { parameters }) { async function fetchLatest(channel, page = 1, { parameters }) {
// url: 'https://www.pervz.com/series/pervz-features', // url: 'https://www.pervz.com/series/pervz-features',
const seriesId = parameters.id || new URL(channel.url).pathname.match(/\/series\/([a-z-]+)/)?.[1]; const seriesId = parameters.id || new URL(channel.url).pathname.match(/\/series\/([\w-]+)/)?.[1];
const url = seriesId const url = seriesId
? `https://tours-store.psmcdn.net/${parameters.endpoint}/_search?q=(site.seo.seoSlug:"${seriesId}" AND type:video)&sort=publishedDate:desc&size=30&from=${(page - 1) * 30}` ? `https://tours-store.psmcdn.net/${parameters.endpoint}/_search?q=(site.seo.seoSlug:"${seriesId}" AND type:video)&sort=publishedDate:desc&size=30&from=${(page - 1) * 30}`
@@ -113,27 +115,22 @@ async function fetchScene(url, channel, baseScene, { parameters, includeTrailers
return baseScene; return baseScene;
} }
const sceneSlug = new URL(url).pathname.match(/\/([\w-]+$)/)[1]; const sceneSlug = new URL(url).pathname.match(/\/([\w-]+)\/?$/)?.[1];
const res = await unprint.get(url, { if (!sceneSlug) {
parser: { return null;
runScripts: 'dangerously', }
},
});
if (res.ok) { // scene page no longer embeds state, look up the scene in the elastic API by its slug instead
const videos = res.context.window.__INITIAL_STATE__?.content?.videosContent; // legacy URLs use the numeric item ID, which 404s on the site but can still be looked up
const isItemId = /^\d+$/.test(sceneSlug);
const res = await unprint.get(`https://tours-store.psmcdn.net/${parameters.endpoint}/_search?q=(${isItemId ? `itemId:${sceneSlug}` : `id:"${sceneSlug}"`} AND type:video)&size=1`);
res.context.window.fetch = () => {}; // suppress fetch missing error if (res.ok && res.data?.hits) {
const scene = res.data.hits.hits.find(({ _source: hit }) => (isItemId ? String(hit.itemId) : hit.id) === sceneSlug)?._source;
if (!videos) { if (scene) {
return null; return scrapeScene(scene, channel, parameters, includeTrailers);
}
const video = videos?.[sceneSlug] || Object.values(videos)[0];
if (video) {
return scrapeScene(video, channel, parameters, includeTrailers);
} }
return null; return null;
+5 -4
View File
@@ -79,13 +79,14 @@ function withRelations(queryBuilder, withMedia) {
} }
async function matchTags(rawTags) { async function matchTags(rawTags) {
const tags = rawTags const tags = Array.from(new Set(rawTags
.map((tag) => tag?.trim().match(/[a-z0-9()-]+/ig)?.join(' ').toLowerCase()) .map((tag) => (typeof tag === 'string' ? tag : null)?.trim().match(/[a-z0-9()-]+/ig)?.join(' ').toLowerCase())
.filter(Boolean); .filter(Boolean)));
// array binding, large batches can easily exceed the bindings limit otherwise
const tagEntries = await knex('tags') const tagEntries = await knex('tags')
.select('tags.id', 'tags.name', 'tags.alias_for', 'tags.implied_tag_ids') .select('tags.id', 'tags.name', 'tags.alias_for', 'tags.implied_tag_ids')
.whereIn(knex.raw('lower(tags.name)'), tags); .whereRaw('lower(tags.name) = ANY(:tags)', { tags });
const tagIdsBySlug = tagEntries const tagIdsBySlug = tagEntries
.reduce((acc, tag) => ({ .reduce((acc, tag) => ({