diff --git a/assets/components/actors/actor.vue b/assets/components/actors/actor.vue index df57f91b..199716bf 100644 --- a/assets/components/actors/actor.vue +++ b/assets/components/actors/actor.vue @@ -249,7 +249,7 @@ {{ description.text }} diff --git a/assets/components/entities/entity.vue b/assets/components/entities/entity.vue index 3aef60be..85bbdb63 100644 --- a/assets/components/entities/entity.vue +++ b/assets/components/entities/entity.vue @@ -160,6 +160,7 @@ export default { data() { return { entity: null, + pageTitle: null, totalCount: null, limit: Number(this.$route.query.limit) || 20, expanded: false, diff --git a/public/img/logos/hookuphotshot/favicon.png b/public/img/logos/hookuphotshot/favicon.png new file mode 100644 index 00000000..121e510e Binary files /dev/null and b/public/img/logos/hookuphotshot/favicon.png differ diff --git a/public/img/logos/hookuphotshot/hookuphotshot.png b/public/img/logos/hookuphotshot/hookuphotshot.png new file mode 100644 index 00000000..629faf71 Binary files /dev/null and b/public/img/logos/hookuphotshot/hookuphotshot.png differ diff --git a/public/img/logos/hookuphotshot/lazy/hookuphotshot.png b/public/img/logos/hookuphotshot/lazy/hookuphotshot.png new file mode 100644 index 00000000..5e14f298 Binary files /dev/null and b/public/img/logos/hookuphotshot/lazy/hookuphotshot.png differ diff --git a/public/img/logos/hookuphotshot/lazy/network.png b/public/img/logos/hookuphotshot/lazy/network.png new file mode 100644 index 00000000..5e14f298 Binary files /dev/null and b/public/img/logos/hookuphotshot/lazy/network.png differ diff --git a/public/img/logos/hookuphotshot/network.png b/public/img/logos/hookuphotshot/network.png new file mode 100644 index 00000000..b96fdc98 Binary files /dev/null and b/public/img/logos/hookuphotshot/network.png differ diff --git a/public/img/logos/hookuphotshot/thumbs/hookuphotshot.png b/public/img/logos/hookuphotshot/thumbs/hookuphotshot.png new file mode 100644 index 00000000..52d5afea Binary files /dev/null and b/public/img/logos/hookuphotshot/thumbs/hookuphotshot.png differ diff --git a/public/img/logos/hookuphotshot/thumbs/network.png b/public/img/logos/hookuphotshot/thumbs/network.png new file mode 100644 index 00000000..52d5afea Binary files /dev/null and b/public/img/logos/hookuphotshot/thumbs/network.png differ diff --git a/public/img/tags/fake-boobs/9.jpeg b/public/img/tags/fake-boobs/9.jpeg new file mode 100644 index 00000000..95db7c97 Binary files /dev/null and b/public/img/tags/fake-boobs/9.jpeg differ diff --git a/public/img/tags/fake-boobs/lazy/9.jpeg b/public/img/tags/fake-boobs/lazy/9.jpeg new file mode 100644 index 00000000..5fb6afe8 Binary files /dev/null and b/public/img/tags/fake-boobs/lazy/9.jpeg differ diff --git a/public/img/tags/fake-boobs/thumbs/9.jpeg b/public/img/tags/fake-boobs/thumbs/9.jpeg new file mode 100644 index 00000000..ff206627 Binary files /dev/null and b/public/img/tags/fake-boobs/thumbs/9.jpeg differ diff --git a/seeds/00_tags.js b/seeds/00_tags.js index cfc25f98..fae9ee69 100644 --- a/seeds/00_tags.js +++ b/seeds/00_tags.js @@ -184,7 +184,7 @@ const tags = [ }, { name: 'behind the scenes', - slug: 'behind-the-scenes', + slug: 'bts', priority: 6, }, { @@ -1053,8 +1053,8 @@ const aliases = [ for: 'titty-fuck', }, { - name: 'bts', - for: 'behind-the-scenes', + name: 'behind the scenes', + for: 'bts', secondary: true, }, { @@ -1718,14 +1718,18 @@ const aliases = [ for: 'transsexual', secondary: true, }, - { - name: 'trimmed pussy', - for: 'trimmed', - }, { name: 'ts', for: 'transsexual', }, + { + name: 'ts cock', + for: 'transsexual', + }, + { + name: 'trimmed pussy', + for: 'trimmed', + }, { name: 'vr', for: 'virtual reality', diff --git a/seeds/02_sites.js b/seeds/02_sites.js index 48cfca74..7a524a99 100644 --- a/seeds/02_sites.js +++ b/seeds/02_sites.js @@ -2557,6 +2557,12 @@ const sites = [ siteId: 1, }, }, + // HOOKUP HOTSHOT + { + slug: 'hookuphotshot', + name: 'Hookup Hotshot', + url: 'https://www.hookuphotshot.com', + }, // HUSSIE PASS { slug: 'hussiepass', diff --git a/seeds/04_media.js b/seeds/04_media.js index 391c2e73..025518ac 100644 --- a/seeds/04_media.js +++ b/seeds/04_media.js @@ -595,7 +595,7 @@ const tagPosters = [ ['atm', 2, 'Jureka Del Mar in "Stretched Out" for Her Limit'], ['atogm', 0, 'Alysa Gap and Logan in "Anal Buffet 4" for Evil Angel'], ['bdsm', 0, 'Dani Daniels in "The Traning of Dani Daniels, Day 2" for The Training of O at Kink'], - ['behind-the-scenes', 0, 'Janice Griffith in "Day With A Pornstar: Janice" for Brazzers'], + ['bts', 0, 'Janice Griffith in "Day With A Pornstar: Janice" for Brazzers'], ['blonde', 1, 'Marsha May in "Once You Go Black 7" for Jules Jordan'], ['blowbang', 0, 'Lacy Lennon in "Lacy Lennon\'s First Blowbang" for HardX'], ['blowjob', 0, 'Adriana Chechik in "The Dinner Party" for Real Wife Stories (Brazzers)'], @@ -676,7 +676,7 @@ const tagPhotos = [ ['anal', 3, 'Dakota Skye for Brazzers'], // ['anal', 1, 'Veronica Leal and Tina Kay in "Agents On Anal Mission" for Asshole Fever'], // ['anal', 0, 'Veronica Leal'], - ['behind-the-scenes', 1, 'Madison Ivy in "Day With A Pornstar" for Brazzers'], + ['bts', 1, 'Madison Ivy in "Day With A Pornstar" for Brazzers'], ['blonde', 2, 'Isabelle Deltore for Her Limit'], ['blowbang', 'poster', 'Marsha May in "Feeding Frenzy 12" for Jules Jordan'], // ['bukkake', 'poster', 'Mia Malkova in "Facialized 2" for HardX'], @@ -708,6 +708,7 @@ const tagPhotos = [ ['facial', 'poster', 'Jynx Maze'], ['facefucking', 2, 'Jynx Maze for Throated'], ['facefucking', 3, 'Adriana Chechik in "Performing Magic Butt Tricks With Jules Jordan. What Will Disappear In Her Ass?" for Jules Jordan'], + ['fake-boobs', 9, 'Putri Cinta for StasyQ'], ['fake-boobs', 8, 'Amber Alena for Score'], ['fake-boobs', 1, 'Lela Star in "Thick" for Jules Jordan'], // ['fake-boobs', 6, 'Cathy Heaven in "Heavenly Ass" for Big Wett Butts'], diff --git a/src/scrapers/hookuphotshot.js b/src/scrapers/hookuphotshot.js new file mode 100644 index 00000000..7c1e503a --- /dev/null +++ b/src/scrapers/hookuphotshot.js @@ -0,0 +1,103 @@ +'use strict'; + +const qu = require('../utils/q'); +const slugify = require('../utils/slugify'); + +function scrapeAll(scenes) { + return scenes.map(({ query }) => { + const release = {}; + + release.url = query.url('.date-title a'); + + const avatarEl = query.el('.girl-thumb-container img'); + release.actors = query.all('.date-starring a').map((actorEl) => { + const name = query.cnt(actorEl); + + return { + name, + gender: 'female', + url: query.url(actorEl, null), + ...(new RegExp(name).test(avatarEl.alt) && { + avatar: [ + avatarEl.src.replace(/-\d+x\d+/, ''), + avatarEl.src, + ].map(src => ({ src, queueMethod: '1s' })), + }), + }; + }).concat({ + name: 'Bryan Gozzling', + gender: 'male', + }); + + release.duration = query.dur('.date-facts'); + release.stars = query.number('[data-rating]', null, 'data-rating'); + + const photoCount = query.number('input[id*=count]', null, 'value'); + const photoPath = query.url('input[id*=baseurl]', 'value'); + + release.poster = { + src: query.img('.date-img-swap'), + queueMethod: '1s', + }; + + release.photos = [...Array(photoCount)].map((value, index) => ({ + src: `${photoPath}/${String(index + 1).padStart(2, '0')}.jpg`, + queueMethod: '1s', + })); + + // dates appear to be manually curated + const fullTitle = query.cnt('.date-title a'); + const [monthName, date, title] = fullTitle.match(/(\w+)\.? (\d+)\s*-?\s*(.*)/)?.slice(1) || []; + const [year, month] = release.poster.src.match(/uploads\/(\d+)\/(\d+)/)?.slice(1) || []; + + release.title = title.replace(/behind the\.\.\./i, 'Behind the Scenes'); + release.date = qu.extractDate(`${year}-${monthName || month}-${date}`, ['YYYY-MM-DD', 'YYYY-MMM-DD', 'YYYY-MMMM-DD']); + + // release.entryId = new URL(release.url).pathname.split('/')[2]; + release.entryId = `${release.date.getFullYear()}-${release.date.getMonth() + 1}-${release.date.getDate()}-${slugify(release.actors[0].name)}`; + + release.tags = ['rough', ...release.title.match(/behind the scenes|anal/gi) || []]; + + return release; + }); +} + +function scrapeProfile({ query }) { + const profile = {}; + + profile.gender = 'female'; + + profile.description = query.cnts('.girl-about p:not(.bio-facts)').join(' '); + profile.avatar = query.img('.girl-pic'); + + // no deep scraping available, and not all scene details available here + + return profile; +} + +async function fetchLatest(channel, page = 1) { + const url = `${channel.url}/the-dates/page/${page}`; + const res = await qu.getAll(url, '#et-projects li'); + + if (res.ok) { + return scrapeAll(res.items, channel); + } + + return res.status; +} + +async function fetchProfile({ name: actorName }, entity, include) { + const url = `${entity.url}/girls/${slugify(actorName)}`; + const res = await qu.get(url); + + if (res.ok) { + return scrapeProfile(res.item, actorName, entity, include); + } + + return res.status; +} + +module.exports = { + fetchLatest, + fetchProfile, +}; diff --git a/src/scrapers/scrapers.js b/src/scrapers/scrapers.js index 86368d89..a143e39c 100644 --- a/src/scrapers/scrapers.js +++ b/src/scrapers/scrapers.js @@ -23,6 +23,7 @@ const fcuk = require('./fcuk'); const fullpornnetwork = require('./fullpornnetwork'); const girlsway = require('./girlsway'); const hitzefrei = require('./hitzefrei'); +const hookuphotshot = require('./hookuphotshot'); const hush = require('./hush'); const iconmale = require('./iconmale'); const insex = require('./insex'); @@ -104,6 +105,7 @@ module.exports = { girlsway, girlgirl: julesjordan, hitzefrei, + hookuphotshot, hussiepass: hush, hushpass: hush, insex, @@ -191,6 +193,7 @@ module.exports = { hergape: fullpornnetwork, hitzefrei, homemadeanalwhores: fullpornnetwork, + hookuphotshot, hotcrazymess: nubiles, hushpass: hush, hussiepass: hush, diff --git a/src/updates.js b/src/updates.js index 9127c5d6..d34773c5 100644 --- a/src/updates.js +++ b/src/updates.js @@ -183,7 +183,7 @@ async function scrapeChannel(channelEntity, accNetworkReleases) { if (!scraper) { logger.warn(`No scraper found for '${channelEntity.name}' (${channelEntity.parent?.name})`); - return []; + return emptyReleases; } try { @@ -196,7 +196,7 @@ async function scrapeChannel(channelEntity, accNetworkReleases) { } catch (error) { logger.error(`Failed to scrape releases from ${channelEntity.name} using ${scraper.slug}: ${error.message}`); - return []; + return emptyReleases; } }