diff --git a/migrations/20260123051908_serie_effective_date.js b/migrations/20260123051908_serie_effective_date.js new file mode 100644 index 00000000..7708136f --- /dev/null +++ b/migrations/20260123051908_serie_effective_date.js @@ -0,0 +1,14 @@ +exports.up = async (knex) => { + await knex.raw(` + /* allow scenes without dates to be mixed inbetween scenes with dates */ + ALTER TABLE series + ADD COLUMN effective_date timestamptz + GENERATED ALWAYS AS (COALESCE(date, created_at)) STORED; + `); +}; + +exports.down = async (knex) => { + await knex.schema.alterTable('series', (table) => { + table.dropColumn('effective_date'); + }); +}; diff --git a/seeds/02_sites.js b/seeds/02_sites.js index dfe71077..e20d0613 100755 --- a/seeds/02_sites.js +++ b/seeds/02_sites.js @@ -10389,6 +10389,29 @@ const sites = [ endpoint: 'K9_0ysd-cpgwtSaZ8_nPT', }, }, + { + name: 'Lucid Flix', + slug: 'lucidflix', + url: 'https://lucidflix.com', + independent: true, + parent: 'radical', + parameters: { + endpoint: 'QAE38FkosM6l24CzC5eIG', + videos: 'episodes', + forceDeep: true, // has serie data + }, + }, + { + name: 'Tough Love X', + slug: 'toughlovex', + url: 'https://tour.toughlovex.com', + independent: true, + parent: 'radical', + parameters: { + endpoint: 'OvRVNkuj_-wDFLBYcSm_M', + siteAsSerie: true, + }, + }, // REALITY KINGS { name: 'Look At Her Now', diff --git a/seeds/06_affiliates.js b/seeds/06_affiliates.js index 241a62fe..8716f256 100755 --- a/seeds/06_affiliates.js +++ b/seeds/06_affiliates.js @@ -221,6 +221,92 @@ const affiliates = [ scene: false, }, }, + // radical + { + id: 'radical', + network: 'radical', + url: 'http://radicalcash.com/track/MzAwMDA5NzkuNy42LjYuMC4wLjAuMC4w', + comment: 'webmaster referral', + }, + { + id: 'lucidflix', + channel: 'lucidflix', + url: 'https://register.lucidflix.com/track/MzAwMDA5NzkuMy4xNDcuMzMzLjAuMC4wLjAuMA', + comment: 'rev share', + }, + { + id: 'bjraw', + channel: 'bjraw', + url: 'https://register.bjraw.com/track/MzAwMDA5NzkuMy45Ny4yNTguMC4wLjAuMC4w', + comment: 'rev share', + }, + { + id: 'gotfilled', + channel: 'gotfilled', + url: 'https://register.gotfilled.com/track/MzAwMDA5NzkuMy4xMDEuMjY0LjAuMC4wLjAuMA', + comment: 'rev share', + }, + { + id: 'inserted', + channel: 'inserted', + url: 'https://register.inserted.com/track/MzAwMDA5NzkuMy4xMDIuMjY1LjAuMC4wLjAuMA', + comment: 'rev share', + }, + { + id: 'purgatoryx', + channel: 'purgatoryx', + url: 'https://register.purgatoryx.com/track/MzAwMDA5NzkuMy44OC4yNDUuMC4wLjAuMC4w', + comment: 'rev share', + }, + { + id: 'toughlovex', + channel: 'toughlovex', + url: 'https://register.join-toughlovex.com/track/MzAwMDA5NzkuMy43Ni4xOTcuMC4wLjAuMC4w', + comment: 'rev share', + }, + // radical > topwebmodels + { + id: 'topwebmodels', + network: 'topwebmodels', + url: 'https://register.topwebmodels.com/track/MzAwMDA5NzkuMy41NS4xNjguMC4wLjAuMC4w', + comment: 'rev share', + }, + { + id: 'deepthroatsirens', + channel: 'deepthroatsirens', + url: 'https://register.deepthroatsirens.com/track/MzAwMDA5NzkuMy42MS4xNzQuMC4wLjAuMC4w', + comment: 'rev share', + }, + { + id: 'biggulpgirls', + channel: 'biggulpgirls', + url: 'https://register.biggulpgirls.com/track/MzAwMDA5NzkuMy42Mi4xNzUuMC4wLjAuMC4w', + comment: 'rev share', + }, + { + id: 'cougarseason', + channel: 'cougarseason', + url: 'https://register.cougarseason.com/track/MzAwMDA5NzkuMy41Ny4xNzAuMC4wLjAuMC4w', + comment: 'rev share', + }, + { + id: 'facialsforever', + channel: 'facialsforever', + url: 'https://register.facialsforever.com/track/MzAwMDA5NzkuMy41OC4xNzEuMC4wLjAuMC4w', + comment: 'rev share', + }, + { + id: 'poundedpetite', + channel: 'poundedpetite', + url: 'https://register.poundedpetite.com/track/MzAwMDA5NzkuMy41OS4xNzIuMC4wLjAuMC4w', + comment: 'rev share', + }, + { + id: 'shesbrandnew', + channel: 'shesbrandnew', + url: 'https://register.shesbrandnew.com/track/MzAwMDA5NzkuMy42MC4xNzMuMC4wLjAuMC4w', + comment: 'rev share', + }, // etc { id: 'kink', diff --git a/src/scrapers/actors.js b/src/scrapers/actors.js index e3844d06..6220a934 100644 --- a/src/scrapers/actors.js +++ b/src/scrapers/actors.js @@ -140,6 +140,7 @@ module.exports = { inserted: radical, purgatoryx: radical, topwebmodels: radical, + lucidflix: radical, // hush / hussiepass eyeontheguy: hush, hushpass: hush, diff --git a/src/scrapers/radical.js b/src/scrapers/radical.js index 740b066b..be2ba179 100755 --- a/src/scrapers/radical.js +++ b/src/scrapers/radical.js @@ -78,11 +78,27 @@ function scrapeScene(data, channel, parameters) { release.tags = data.tags; - release.channel = slugify(data.site, ''); release.qualities = Object.values(data.videos || []).map((video) => video.height); - release.photoCount = Number(data.photos_duration) || null; + if (data.serie) { + release.channel = slugify(data.site, ''); + + release.serie = { + entryId: data.serie.id, + title: data.serie.title, + date: unprint.extractDate(data.serie.publish_date, 'YYYY/MM/DD HH:mm:ss'), + poster: data.serie.thumb, + }; + } else if (parameters.siteAsSerie && data.site) { + release.serie = { + entryId: data.site_domain?.split('.')[0] || slugify(data.site, ''), + title: data.site, + }; + } else { + release.channel = slugify(data.site, ''); + } + return release; } @@ -147,7 +163,7 @@ async function fetchUpcoming(channel, _page, { parameters }) { } async function fetchScene(url, channel, baseScene, { parameters }) { - if (baseScene.entryId) { + if (baseScene.entryId && !parameters.forceDeep) { // identical data return baseScene; } @@ -157,7 +173,10 @@ async function fetchScene(url, channel, baseScene, { parameters }) { const res = await http.get(`${channel.url}/_next/data/${endpoint}/${parameters.videos || 'videos'}/${slug}.json?slug=${slug}`); if (res.ok && res.body.pageProps?.content) { - return scrapeScene(res.body.pageProps.content, channel, parameters); + return scrapeScene({ + ...res.body.pageProps.content, + serie: res.body.pageProps.playlist?.data[0], + }, channel, parameters); } return res.status; diff --git a/tests/profiles.js b/tests/profiles.js index 19de1f3e..d25361ae 100644 --- a/tests/profiles.js +++ b/tests/profiles.js @@ -137,6 +137,7 @@ const actors = [ { entity: 'inserted', name: 'Anissa Kate', fields: ['avatar', 'description', 'gender', 'dateOfBirth', 'birthPlace', 'measurements', 'height', 'weight', 'eyes', 'hairColor'] }, { entity: 'topwebmodels', name: 'Lexi Belle', fields: ['avatar', 'gender', 'dateOfBirth', 'birthPlace', 'measurements', 'height', 'weight', 'eyes', 'hairColor'] }, { entity: 'purgatoryx', name: 'Kenzie Reeves', fields: ['avatar', 'description', 'gender', 'dateOfBirth', 'birthPlace', 'measurements', 'height', 'weight', 'eyes', 'hairColor'] }, + { entity: 'lucidflix', name: 'Ava Amira', fields: ['avatar', 'description', 'gender'] }, // wankz { entity: 'wankzvr', name: 'Melody Marks', fields: ['avatar', 'gender', 'description', 'birthPlace', 'height', 'measurements', 'age'] }, { entity: 'milfvr', name: 'Ember Snow', fields: ['avatar', 'gender', 'description', 'measurements', 'birthPlace', 'height', 'age'] },