diff --git a/assets/components/actors/actor.vue b/assets/components/actors/actor.vue index 717a6997..2913f8e8 100644 --- a/assets/components/actors/actor.vue +++ b/assets/components/actors/actor.vue @@ -352,6 +352,7 @@ export default { .avatar-link { font-size: 0; padding: 0 0 1rem 1rem; + flex-shrink: 0; } .avatar { diff --git a/assets/js/curate.js b/assets/js/curate.js index a1a1bc91..f35c6c15 100644 --- a/assets/js/curate.js +++ b/assets/js/curate.js @@ -27,6 +27,7 @@ function curateRelease(release) { }; if (release.photos) curatedRelease.photos = release.photos.map(({ media }) => media); + if (release.covers) curatedRelease.covers = release.covers.map(({ media }) => media); if (release.trailer) curatedRelease.trailer = release.trailer.media; if (release.teaser) curatedRelease.teaser = release.teaser.media; if (release.actors) curatedRelease.actors = release.actors.map(({ actor }) => curateActor(actor, curatedRelease)); diff --git a/assets/js/fragments.js b/assets/js/fragments.js index 89ddaba8..1a700945 100644 --- a/assets/js/fragments.js +++ b/assets/js/fragments.js @@ -75,6 +75,17 @@ const releasePosterFragment = ` } `; +const releaseCoversFragment = ` + covers: releasesCovers { + media { + index + path + thumbnail + comment + } + } +`; + const releasePhotosFragment = ` photos: releasesPhotos { media { @@ -119,6 +130,7 @@ const releaseFields = ` ${releaseActorsFragment} ${releaseTagsFragment} ${releasePosterFragment} + ${releaseCoversFragment} ${siteFragment} studio { id diff --git a/public/img/logos/insex/aganmedon.png b/public/img/logos/insex/aganmedon.png new file mode 100644 index 00000000..02e591a8 Binary files /dev/null and b/public/img/logos/insex/aganmedon.png differ diff --git a/public/img/logos/insex/favicon.png b/public/img/logos/insex/favicon.png new file mode 100644 index 00000000..e5e86a72 Binary files /dev/null and b/public/img/logos/insex/favicon.png differ diff --git a/public/img/logos/insex/hardtied.png b/public/img/logos/insex/hardtied.png new file mode 100644 index 00000000..37cac984 Binary files /dev/null and b/public/img/logos/insex/hardtied.png differ diff --git a/public/img/logos/insex/infernalrestraints.png b/public/img/logos/insex/infernalrestraints.png new file mode 100644 index 00000000..753bcf05 Binary files /dev/null and b/public/img/logos/insex/infernalrestraints.png differ diff --git a/public/img/logos/insex/network.png b/public/img/logos/insex/network.png new file mode 100644 index 00000000..12da1d09 Binary files /dev/null and b/public/img/logos/insex/network.png differ diff --git a/public/img/logos/insex/paintoy.png b/public/img/logos/insex/paintoy.png new file mode 100644 index 00000000..d2ceb293 Binary files /dev/null and b/public/img/logos/insex/paintoy.png differ diff --git a/public/img/logos/insex/realtimebondage.png b/public/img/logos/insex/realtimebondage.png new file mode 100644 index 00000000..5a15d3a2 Binary files /dev/null and b/public/img/logos/insex/realtimebondage.png differ diff --git a/public/img/logos/insex/sensualpain.png b/public/img/logos/insex/sensualpain.png new file mode 100644 index 00000000..e74da2ce Binary files /dev/null and b/public/img/logos/insex/sensualpain.png differ diff --git a/public/img/logos/insex/sexuallybroken.png b/public/img/logos/insex/sexuallybroken.png new file mode 100644 index 00000000..c3ab15cc Binary files /dev/null and b/public/img/logos/insex/sexuallybroken.png differ diff --git a/public/img/logos/insex/topgrl.png b/public/img/logos/insex/topgrl.png new file mode 100644 index 00000000..be03a22a Binary files /dev/null and b/public/img/logos/insex/topgrl.png differ diff --git a/seeds/00_tags.js b/seeds/00_tags.js index 6303a29b..dbe77788 100644 --- a/seeds/00_tags.js +++ b/seeds/00_tags.js @@ -121,6 +121,11 @@ function getTags(groupsMap) { description: 'Stuffing a toy, such as a dildo or buttplug, into the ass', alias_for: null, }, + { + name: 'animated', + slug: 'animated', + alias_for: null, + }, { name: 'asian', slug: 'asian', diff --git a/seeds/01_networks.js b/seeds/01_networks.js index 68fdc6c0..e3c4eebd 100644 --- a/seeds/01_networks.js +++ b/seeds/01_networks.js @@ -114,6 +114,12 @@ const networks = [ url: 'https://www.girlsway.com', description: 'Girlsway.com has the best lesbian porn videos online! The hottest pornstars & first time lesbians in real girl on girl sex, tribbing, squirting & pussy licking action right HERE!', }, + { + slug: 'insex', + name: 'Insex', + description: 'The original bondage and BDSM transgression.', + url: 'http://www.insex.com', + }, { slug: 'jayrock', name: 'JayRock Productions', diff --git a/seeds/02_sites.js b/seeds/02_sites.js index 973c86a6..a5b1a6a0 100644 --- a/seeds/02_sites.js +++ b/seeds/02_sites.js @@ -1775,6 +1775,63 @@ const sites = [ scene: 'https://www.girlsway.com/en/video', }, }, + // INSEX + { + slug: 'sexuallybroken', + name: 'Sexually Broken', + url: 'http://www.sexuallybroken.com', + tags: ['bdsm'], + network: 'insex', + }, + { + slug: 'infernalrestraints', + name: 'Infernal Restraints', + url: 'http://www.infernalrestraints.com', + tags: ['bdsm'], + network: 'insex', + }, + { + slug: 'hardtied', + name: 'Hardtied', + url: 'http://www.hardtied.com', + tags: ['bdsm'], + network: 'insex', + }, + { + slug: 'realtimebondage', + name: 'Real Time Bondage', + url: 'http://www.realtimebondage.com', + tags: ['bdsm'], + network: 'insex', + }, + { + slug: 'topgrl', + name: 'TopGrl', + url: 'http://www.topgrl.com', + tags: ['bdsm', 'femdom'], + network: 'insex', + }, + { + slug: 'paintoy', + name: 'Paintoy', + url: 'http://www.paintoy.com', + tags: ['bdsm'], + network: 'insex', + }, + { + slug: 'aganmedon', + name: 'Agan Medon', + url: 'http://www.aganmedon.com', + tags: ['bdsm', 'animated'], + network: 'insex', + }, + { + slug: 'sensualpain', + name: 'Sensual Pain', + url: 'http://www.sensualpain.com', + tags: ['bdsm'], + network: 'insex', + }, // JAYS POV { slug: 'jayspov', diff --git a/src/scrapers/ddfnetwork.js b/src/scrapers/ddfnetwork.js index b4eb354e..ca2240d1 100644 --- a/src/scrapers/ddfnetwork.js +++ b/src/scrapers/ddfnetwork.js @@ -2,7 +2,7 @@ const bhttp = require('bhttp'); -const { d, ex, exa, get } = require('../utils/q'); +const { fd, ex, exa, get } = require('../utils/q'); const slugify = require('../utils/slugify'); /* eslint-disable newline-per-chained-call */ @@ -97,7 +97,7 @@ async function scrapeProfile(html, _url, actorName) { }; profile.description = q('.description-box', true); - profile.birthdate = d(bio.birthday, 'MMMM DD, YYYY'); + profile.birthdate = fd(bio.birthday, 'MMMM DD, YYYY'); if (bio.nationality) profile.nationality = bio.nationality; diff --git a/src/scrapers/insex.js b/src/scrapers/insex.js new file mode 100644 index 00000000..371399bc --- /dev/null +++ b/src/scrapers/insex.js @@ -0,0 +1,100 @@ +'use strict'; + +const bhttp = require('bhttp'); +const { get, exa, fd } = require('../utils/q'); + +function scrapeLatest(html, site) { + const scenes = exa(html, 'body > table'); + + return scenes.map(({ q, qd, qi, qu, ql }) => { + // if (q('.articleTitleText')) return scrapeFirstLatest(ctx(el), site); + const release = {}; + + const titleEl = q('.galleryTitleText, .articleTitleText'); + const [title, ...actors] = titleEl.textContent.split('|'); + const date = qd('.articlePostDateText', 'MMM D, YYYY'); + + const url = qu(titleEl, 'a'); + [release.entryId] = url.split('/').slice(-2); + release.url = `${site.url}${url}`; + + if (date) { + release.title = title.trim(); + release.date = date; + } else { + // title should contain date instead + release.title = title.slice(title.indexOf(':') + 1).trim(); + release.date = fd(title.slice(0, title.indexOf(':')), 'MMM D, YYYY'); + } + + release.actors = actors.map(actor => actor.trim()); + + const description = q('.articleCopyText .articleCopyText', true); + if (description) release.description = description; + + const duration = ql('.articleCopyText a:nth-child(2)'); + if (duration) release.duration = duration; + + const poster = qi('a img'); + release.poster = [ + poster.replace('_thumbnail', ''), + poster, + ]; + + return release; + }); +} + +function scrapeScene({ q, qd, ql, qu, qis, qp, qt }, site) { + const release = {}; + + const titleEl = q('.articleTitleText'); + const [title, ...actors] = titleEl.textContent.split('|'); + + const url = qu(titleEl, 'a'); + [release.entryId] = url.split('/').slice(-2); + release.url = `${site.url}${url}`; + + release.title = title.trim(); + release.description = q('.articleCopyText', true); + + release.actors = actors.map(actor => actor.trim()); + release.date = qd('.articlePostDateText', 'MMMM D, YYYY'); + release.duration = ql('.articlePostDateText a:nth-child(2)'); + + const [cover, ...photos] = qis('img[src*="images"]'); + release.covers = [cover]; + release.photos = photos; + + release.poster = qp(); + + const trailer = qt(); + release.trailer = { src: trailer }; + + return release; +} + +async function fetchLatest(site, page = 1) { + const url = `${site.url}/scripts/switch_tour.php?page=${page}`; + const res = await bhttp.get(url, { + type: 'gallery', + page, + }); + + if (res.statusCode === 200) { + return scrapeLatest(res.body.html, site); + } + + return null; +} + +async function fetchScene(url, site) { + const qScene = await get(url); + + return qScene && scrapeScene(qScene, site); +} + +module.exports = { + fetchLatest, + fetchScene, +}; diff --git a/src/scrapers/scrapers.js b/src/scrapers/scrapers.js index 702dab3a..77baf34d 100644 --- a/src/scrapers/scrapers.js +++ b/src/scrapers/scrapers.js @@ -19,6 +19,7 @@ const freeones = require('./freeones'); const freeonesLegacy = require('./freeones_legacy'); const girlsway = require('./girlsway'); const iconmale = require('./iconmale'); +const insex = require('./insex'); const jayrock = require('./jayrock'); const julesjordan = require('./julesjordan'); const kellymadison = require('./kellymadison'); @@ -73,6 +74,7 @@ module.exports = { fakehub, fantasymassage, girlsway, + insex, jayrock, julesjordan, kellymadison, diff --git a/src/scrapers/vivid.js b/src/scrapers/vivid.js index 45397792..0dcfe593 100644 --- a/src/scrapers/vivid.js +++ b/src/scrapers/vivid.js @@ -3,7 +3,7 @@ /* eslint-disable no-unused-vars */ const bhttp = require('bhttp'); -const { get, date } = require('../utils/q'); +const { get, fd } = require('../utils/q'); const { fetchApiLatest, fetchApiUpcoming, fetchScene, fetchApiProfile } = require('./gamma'); const slugify = require('../utils/slugify'); @@ -15,7 +15,7 @@ function scrapeLatestNative(scenes, site) { release.url = `${site.url}${scene.url}`; release.title = scene.name; - release.date = date(scene.release_date, 'YYYY-MM-DD'); + release.date = fd(scene.release_date, 'YYYY-MM-DD'); release.duration = parseInt(scene.runtime, 10) * 60; release.actors = scene.cast?.map(actor => ({ @@ -40,7 +40,7 @@ function scrapeSceneNative({ html, q, qa }, url, _site) { release.description = q('.indie-model-p', true); const dateString = qa('h5').find(el => /Released/.test(el.textContent)).textContent; - release.date = date(dateString, 'MMM DD, YYYY', /\w+ \d{1,2}, \d{4}/); + release.date = fd(dateString, 'MMM DD, YYYY', /\w+ \d{1,2}, \d{4}/); const duration = qa('h5').find(el => /Runtime/.test(el.textContent)).textContent; const [hours, minutes] = duration.match(/\d+/g); @@ -118,7 +118,7 @@ async function fetchSceneWrapper(url, site, release) { return { ...scene, url: `${site.url}${sceneMatch.url}`, - date: date(sceneMatch.release_date, 'YYYY-MM-DD'), + date: fd(sceneMatch.release_date, 'YYYY-MM-DD'), }; } } diff --git a/src/utils/q.js b/src/utils/q.js index efbaffe6..c2d641a0 100644 --- a/src/utils/q.js +++ b/src/utils/q.js @@ -4,6 +4,10 @@ const { JSDOM } = require('jsdom'); const moment = require('moment'); const bhttp = require('bhttp'); +function trim(str) { + return str.trim().replace(/\s+/g, ' '); +} + function prefixProtocol(url, protocol = 'https') { if (protocol && /^\/\//.test(url)) { return `${protocol}:${url}`; @@ -12,7 +16,7 @@ function prefixProtocol(url, protocol = 'https') { return url; } -function q(context, selector, attrArg, trim = true) { +function q(context, selector, attrArg, applyTrim = true) { const attr = attrArg === true ? 'textContent' : attrArg; if (attr) { @@ -20,52 +24,52 @@ function q(context, selector, attrArg, trim = true) { ? context.querySelector(selector)?.[attr] || context.querySelector(selector)?.attributes[attr]?.value : context[attr] || context[attr]?.attributes[attr]?.value; - return trim ? value?.trim() : value; + return applyTrim && value ? trim(value) : value; } return selector ? context.querySelector(selector) : context; } -function qall(context, selector, attrArg, trim = true) { +function qall(context, selector, attrArg, applyTrim = true) { const attr = attrArg === true ? 'textContent' : attrArg; if (attr) { - return Array.from(context.querySelectorAll(selector), el => (trim ? el[attr]?.trim() : el[attr])); + return Array.from(context.querySelectorAll(selector), el => (applyTrim && el[attr] ? trim(el[attr]) : el[attr])); } return Array.from(context.querySelectorAll(selector)); } -function qtext(context, selector, trim = true) { - const el = q(context, selector, null, trim); +function qtext(context, selector, applyTrim = true) { + const el = q(context, selector, null, applyTrim); if (!el) return null; const text = Array.from(el.childNodes) .filter(node => node.nodeName === '#text') - .map(node => (trim ? node.textContent : node.textContent.trim())) + .map(node => (applyTrim ? node.textContent : trim(node.textContent))) .join(' '); - if (trim) return text.trim(); + if (applyTrim) return trim(text); return text; } -function qmeta(context, selector, attrArg = 'content', trim = true) { +function qmeta(context, selector, attrArg = 'content', applyTrim = true) { if (/meta\[.*\]/.test(selector)) { - return q(context, selector, attrArg, trim); + return q(context, selector, attrArg, applyTrim); } - return q(context, `meta[${selector}]`, attrArg, trim); + return q(context, `meta[${selector}]`, attrArg, applyTrim); } -function date(dateString, format, match) { +function formatDate(dateString, format, match) { if (match) { - const dateStamp = dateString.trim().match(match); + const dateStamp = trim(dateString).match(match); if (dateStamp) return moment.utc(dateStamp[0], format).toDate(); return null; } - return moment.utc(dateString.trim(), format).toDate(); + return moment.utc(trim(dateString), format).toDate(); } function qdate(context, selector, format, match, attr = 'textContent') { @@ -73,7 +77,7 @@ function qdate(context, selector, format, match, attr = 'textContent') { if (!dateString) return null; - return date(dateString, format, match); + return formatDate(dateString, format, match); } function qimage(context, selector = 'img', attr = 'src', protocol = 'https') { @@ -226,7 +230,7 @@ async function getAll(url, selector, headers) { } module.exports = { - date, + formatDate, extract, extractAll, init, @@ -235,11 +239,12 @@ module.exports = { getAll, context: init, contextAll: initAll, - d: date, + fd: formatDate, ex: extract, exa: extractAll, ctx: init, ctxa: initAll, geta: getAll, + fdate: formatDate, ...funcs, };