From 11eb66f834825fdcc384b572bdd091a9348377bd Mon Sep 17 00:00:00 2001 From: Niels Simenon Date: Thu, 14 May 2020 04:26:05 +0200 Subject: [PATCH] Switched to tabs. Adding missing actor entries when scraping actors, with batch ID. --- .editorconfig | 2 +- .eslintrc | 5 +- assets/components/actors/actor.vue | 494 +- assets/components/networks/network.vue | 296 +- assets/components/tile/site.vue | 34 +- assets/js/actors/actions.js | 156 +- assets/js/actors/actors.js | 10 +- assets/js/api.js | 94 +- assets/js/auth/auth.js | 10 +- assets/js/auth/state.js | 4 +- assets/js/config/default.js | 16 +- assets/js/curate.js | 128 +- assets/js/fragments.js | 20 +- assets/js/get-date-range.js | 42 +- assets/js/main.js | 72 +- assets/js/networks/actions.js | 44 +- assets/js/networks/networks.js | 10 +- assets/js/releases/actions.js | 62 +- assets/js/releases/mutations.js | 8 +- assets/js/releases/releases.js | 10 +- assets/js/releases/state.js | 2 +- assets/js/router.js | 260 +- assets/js/sites/actions.js | 66 +- assets/js/sites/sites.js | 10 +- assets/js/store.js | 20 +- assets/js/tags/actions.js | 76 +- assets/js/tags/tags.js | 10 +- assets/js/ui/actions.js | 52 +- assets/js/ui/getters.js | 56 +- assets/js/ui/mutations.js | 20 +- assets/js/ui/observers.js | 34 +- assets/js/ui/state.js | 10 +- assets/js/ui/ui.js | 12 +- config/default.js | 354 +- migrations/20190325001339_releases.js | 1485 +- public/img/logos/gamma/favicon.png | Bin 0 -> 2397 bytes public/img/logos/mindgeek/favicon.png | Bin 2977 -> 2977 bytes public/img/logos/mindgeek/lazy/favicon.png | Bin 0 -> 2521 bytes public/img/logos/mindgeek/lazy/network.png | Bin 4180 -> 4180 bytes public/img/logos/mindgeek/lazy/pornhub.png | Bin 0 -> 3406 bytes .../img/logos/mindgeek/lazy/transangels.png | Bin 4112 -> 4112 bytes .../img/logos/mindgeek/lazy/trueamateurs.png | Bin 4987 -> 4987 bytes public/img/logos/mindgeek/lazy/tube8vip.png | Bin 4496 -> 4496 bytes .../img/logos/mindgeek/misc/porn-hub-gay.png | Bin 0 -> 4110 bytes public/img/logos/mindgeek/network.png | Bin 22841 -> 22841 bytes public/img/logos/mindgeek/pornhub.png | Bin 0 -> 36315 bytes public/img/logos/mindgeek/thumbs/favicon.png | Bin 0 -> 9334 bytes public/img/logos/mindgeek/thumbs/network.png | Bin 15055 -> 15055 bytes public/img/logos/mindgeek/thumbs/pornhub.png | Bin 0 -> 8081 bytes .../img/logos/mindgeek/thumbs/transangels.png | Bin 19885 -> 19885 bytes .../logos/mindgeek/thumbs/trueamateurs.png | Bin 27793 -> 27793 bytes public/img/logos/mindgeek/thumbs/tube8vip.png | Bin 13605 -> 13605 bytes public/img/logos/mindgeek/transangels.png | Bin 37036 -> 37036 bytes public/img/logos/mindgeek/trueamateurs.png | Bin 8035 -> 8035 bytes public/img/logos/mindgeek/tube8vip.png | Bin 5667 -> 5667 bytes public/img/tags/bukkake/0.jpeg | Bin 0 -> 700682 bytes public/img/tags/bukkake/lazy/0.jpeg | Bin 0 -> 8430 bytes public/img/tags/bukkake/thumbs/0.jpeg | Bin 0 -> 36795 bytes seeds/02_sites.js | 12233 ++++++++-------- seeds/04_media.js | 1529 +- src/.eslintrc | 3 +- src/actors-legacy.js | 804 +- src/actors.js | 193 +- src/app.js | 48 +- src/argv.js | 366 +- src/deep.js | 217 +- src/knex.js | 8 +- src/logger.js | 48 +- src/media.js | 866 +- src/networks.js | 98 +- src/releases-legacy.js | 714 +- src/releases.js | 12 +- src/scrape-releases.js | 199 - src/scrape-sites.js | 184 - src/scrapers/21naturals.js | 8 +- src/scrapers/21sextreme.js | 8 +- src/scrapers/21sextury.js | 8 +- src/scrapers/adulttime.js | 34 +- src/scrapers/amateurallure.js | 52 +- src/scrapers/assylum.js | 148 +- src/scrapers/aziani.js | 164 +- src/scrapers/babes.js | 8 +- src/scrapers/bamvisions.js | 180 +- src/scrapers/bang.js | 234 +- src/scrapers/bangbros.js | 184 +- src/scrapers/blowpass.js | 32 +- src/scrapers/boobpedia.js | 102 +- src/scrapers/brazzers.js | 276 +- src/scrapers/burningangel.js | 8 +- src/scrapers/cherrypimps.js | 174 +- src/scrapers/ddfnetwork.js | 230 +- src/scrapers/digitalplayground.js | 8 +- src/scrapers/dogfart.js | 196 +- src/scrapers/evilangel.js | 8 +- src/scrapers/fakehub.js | 8 +- src/scrapers/famedigital.js | 134 +- src/scrapers/fantasymassage.js | 6 +- src/scrapers/freeones.js | 106 +- src/scrapers/freeones_legacy.js | 170 +- src/scrapers/fullpornnetwork.js | 104 +- src/scrapers/gamma.js | 854 +- src/scrapers/girlsway.js | 6 +- src/scrapers/hush.js | 507 +- src/scrapers/iconmale.js | 4 +- src/scrapers/insex.js | 132 +- src/scrapers/jayrock.js | 142 +- src/scrapers/jesseloadsmonsterfacials.js | 100 +- src/scrapers/julesjordan.js | 534 +- src/scrapers/kellymadison.js | 224 +- src/scrapers/kink.js | 168 +- src/scrapers/legalporno.js | 246 +- src/scrapers/men.js | 8 +- src/scrapers/metrohd.js | 8 +- src/scrapers/mikeadriano.js | 328 +- src/scrapers/milehighmedia.js | 8 +- src/scrapers/mindgeek.js | 330 +- src/scrapers/mofos.js | 8 +- src/scrapers/naughtyamerica.js | 186 +- src/scrapers/newsensations.js | 82 +- src/scrapers/nubiles.js | 188 +- src/scrapers/perfectgonzo.js | 168 +- src/scrapers/pervcity.js | 164 +- src/scrapers/pornhub.js | 66 +- src/scrapers/private.js | 246 +- src/scrapers/puretaboo.js | 6 +- src/scrapers/realitykings.js | 50 +- src/scrapers/score.js | 316 +- src/scrapers/scrapers.js | 278 +- src/scrapers/teamskeet.js | 210 +- src/scrapers/transangels.js | 4 +- src/scrapers/twistys.js | 8 +- src/scrapers/vivid.js | 162 +- src/scrapers/vixen.js | 304 +- src/scrapers/vogov.js | 284 +- src/scrapers/whalemember.js | 102 +- src/scrapers/wicked.js | 8 +- src/scrapers/xempire.js | 28 +- src/sites.js | 272 +- src/store-releases.js | 269 +- src/tags-legacy.js | 152 +- src/tags.js | 132 +- src/updates.js | 304 +- src/utils/argv-include.js | 28 +- src/utils/buffer.js | 122 +- src/utils/capitalize.js | 16 +- src/utils/chunk.js | 4 +- src/utils/convert.js | 42 +- src/utils/cookies.js | 16 +- src/utils/escape-html.js | 12 +- src/utils/http.js | 136 +- src/utils/img.js | 8 +- src/utils/list.js | 38 +- src/utils/media.js | 132 +- src/utils/mofos.js | 16 +- src/utils/pick-random.js | 2 +- src/utils/posters.js | 42 +- src/utils/qu.js | 424 +- src/utils/rename.js | 29 - src/utils/resolve-place.js | 30 +- src/utils/scorelogos.js | 38 +- src/utils/shuffle.js | 12 +- src/utils/slugify.js | 34 +- src/utils/stream.js | 58 +- src/utils/timeout.js | 16 +- src/utils/titles.js | 20 +- src/utils/upsert.js | 76 +- src/utils/where-or.js | 30 +- src/web/actors.js | 34 +- src/web/networks.js | 22 +- src/web/plugins/actors.js | 46 +- src/web/plugins/plugins.js | 6 +- src/web/plugins/releases.js | 16 +- src/web/plugins/sites.js | 16 +- src/web/releases.js | 12 +- src/web/server.js | 96 +- src/web/sites.js | 22 +- src/web/tags.js | 48 +- webpack.config.babel.js | 132 +- 178 files changed, 16594 insertions(+), 16929 deletions(-) create mode 100644 public/img/logos/gamma/favicon.png create mode 100644 public/img/logos/mindgeek/lazy/favicon.png create mode 100644 public/img/logos/mindgeek/lazy/pornhub.png create mode 100644 public/img/logos/mindgeek/misc/porn-hub-gay.png create mode 100644 public/img/logos/mindgeek/pornhub.png create mode 100644 public/img/logos/mindgeek/thumbs/favicon.png create mode 100644 public/img/logos/mindgeek/thumbs/pornhub.png create mode 100644 public/img/tags/bukkake/0.jpeg create mode 100644 public/img/tags/bukkake/lazy/0.jpeg create mode 100644 public/img/tags/bukkake/thumbs/0.jpeg delete mode 100644 src/scrape-releases.js delete mode 100644 src/scrape-sites.js delete mode 100644 src/utils/rename.js diff --git a/.editorconfig b/.editorconfig index 70d1e851..2b49a63c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,7 +5,7 @@ root = true [*] end_of_line = lf insert_final_newline = true -indent_style = space +indent_style = tab indent_size = 4 # Matches multiple files with brace expansion notation diff --git a/.eslintrc b/.eslintrc index dd96e40e..4e4ee9f2 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,13 +7,14 @@ "sourceType": "module" }, "rules": { + "indent": ["error", "tab"], + "no-tabs": "off", "no-unused-vars": ["error", {"argsIgnorePattern": "^_"}], "no-console": 0, - "indent": "off", "template-curly-spacing": "off", "max-len": 0, "vue/no-v-html": 0, - "vue/html-indent": ["error", 4], + "vue/html-indent": ["error", "tab"], "vue/multiline-html-element-content-newline": 0, "vue/singleline-html-element-content-newline": 0, "no-param-reassign": ["error", { diff --git a/assets/components/actors/actor.vue b/assets/components/actors/actor.vue index d6bda5c2..1822532e 100644 --- a/assets/components/actors/actor.vue +++ b/assets/components/actors/actor.vue @@ -1,248 +1,248 @@ @@ -426,7 +426,7 @@ export default { } .bio-value { - margin: 0 0 0 2rem; + margin: 0 0 0 2rem; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; diff --git a/assets/components/networks/network.vue b/assets/components/networks/network.vue index 5954eb25..59128739 100644 --- a/assets/components/networks/network.vue +++ b/assets/components/networks/network.vue @@ -1,124 +1,124 @@ diff --git a/assets/components/tile/site.vue b/assets/components/tile/site.vue index 2be37b39..08ff0e8a 100644 --- a/assets/components/tile/site.vue +++ b/assets/components/tile/site.vue @@ -1,25 +1,25 @@ diff --git a/assets/js/actors/actions.js b/assets/js/actors/actions.js index a5db238b..ee2de9c9 100644 --- a/assets/js/actors/actions.js +++ b/assets/js/actors/actions.js @@ -1,59 +1,61 @@ import { graphql, get } from '../api'; import { - releasePosterFragment, - releaseActorsFragment, - releaseTagsFragment, + releasePosterFragment, + releaseActorsFragment, + releaseTagsFragment, } from '../fragments'; import { curateRelease } from '../curate'; import getDateRange from '../get-date-range'; function curateActor(actor) { - if (!actor) { - return null; - } + if (!actor) { + return null; + } - const curatedActor = { - ...actor, - height: actor.heightMetric && { - metric: actor.heightMetric, - imperial: actor.heightImperial, - }, - weight: actor.weightMetric && { - metric: actor.weightMetric, - imperial: actor.weightImperial, - }, - origin: actor.birthCountry && { - city: actor.birthCity, - state: actor.birthState, - country: actor.birthCountry, - }, - residence: actor.residenceCountry && { - city: actor.residenceCity, - state: actor.residenceState, - country: actor.residenceCountry, - }, - }; + const curatedActor = { + ...actor, + height: actor.heightMetric && { + metric: actor.heightMetric, + imperial: actor.heightImperial, + }, + weight: actor.weightMetric && { + metric: actor.weightMetric, + imperial: actor.weightImperial, + }, + origin: actor.birthCountry && { + city: actor.birthCity, + state: actor.birthState, + country: actor.birthCountry, + }, + residence: actor.residenceCountry && { + city: actor.residenceCity, + state: actor.residenceState, + country: actor.residenceCountry, + }, + scrapedAt: new Date(actor.createdAt), + updatedAt: new Date(actor.updatedAt), + }; - if (actor.avatar) { - curatedActor.avatar = actor.avatar.media; - } + if (actor.avatar) { + curatedActor.avatar = actor.avatar.media; + } - if (actor.releases) { - curatedActor.releases = actor.releases.map(release => curateRelease(release.release)); - } + if (actor.releases) { + curatedActor.releases = actor.releases.map(release => curateRelease(release.release)); + } - if (actor.photos) { - curatedActor.photos = actor.photos.map(photo => photo.media); - } + if (actor.photos) { + curatedActor.photos = actor.photos.map(photo => photo.media); + } - return curatedActor; + return curatedActor; } function initActorActions(store, _router) { - async function fetchActorBySlug({ _commit }, { actorSlug, limit = 100, range = 'latest' }) { - const { before, after, orderBy } = getDateRange(range); + async function fetchActorBySlug({ _commit }, { actorSlug, limit = 100, range = 'latest' }) { + const { before, after, orderBy } = getDateRange(range); - const { actors: [actor] } = await graphql(` + const { actors: [actor] } = await graphql(` query Actor( $actorSlug: String! $limit:Int = 1000, @@ -90,6 +92,8 @@ function initActorActions(store, _router) { tattoos piercings description + createdAt + updatedAt network { id name @@ -184,27 +188,27 @@ function initActorActions(store, _router) { } } `, { - actorSlug, - limit, - after, - before, - orderBy: orderBy === 'DATE_DESC' ? 'RELEASE_BY_RELEASE_ID__DATE_DESC' : 'RELEASE_BY_RELEASE_ID__DATE_ASC', - exclude: store.state.ui.filter, - }); + actorSlug, + limit, + after, + before, + orderBy: orderBy === 'DATE_DESC' ? 'RELEASE_BY_RELEASE_ID__DATE_DESC' : 'RELEASE_BY_RELEASE_ID__DATE_ASC', + exclude: store.state.ui.filter, + }); - return curateActor(actor); - } + return curateActor(actor); + } - async function fetchActors({ _commit }, { - limit = 100, - letter, - gender, - }) { - const genderFilter = gender === null - ? 'isNull: true' - : `equalTo: "${gender}"`; + async function fetchActors({ _commit }, { + limit = 100, + letter, + gender, + }) { + const genderFilter = gender === null + ? 'isNull: true' + : `equalTo: "${gender}"`; - const { actors } = await graphql(` + const { actors } = await graphql(` query Actors( $limit: Int, $letter: String! = "", @@ -249,28 +253,28 @@ function initActorActions(store, _router) { } } `, { - limit, - letter, - }); + limit, + letter, + }); - return actors.map(actor => curateActor(actor)); - } + return actors.map(actor => curateActor(actor)); + } - async function fetchActorReleases({ _commit }, actorId) { - const releases = await get(`/actors/${actorId}/releases`, { - filter: store.state.ui.filter, - after: store.getters.after, - before: store.getters.before, - }); + async function fetchActorReleases({ _commit }, actorId) { + const releases = await get(`/actors/${actorId}/releases`, { + filter: store.state.ui.filter, + after: store.getters.after, + before: store.getters.before, + }); - return releases; - } + return releases; + } - return { - fetchActorBySlug, - fetchActors, - fetchActorReleases, - }; + return { + fetchActorBySlug, + fetchActors, + fetchActorReleases, + }; } export default initActorActions; diff --git a/assets/js/actors/actors.js b/assets/js/actors/actors.js index 59be7fdf..8e71e886 100644 --- a/assets/js/actors/actors.js +++ b/assets/js/actors/actors.js @@ -3,11 +3,11 @@ import mutations from './mutations'; import actions from './actions'; function initActorsStore(store, router) { - return { - state, - mutations, - actions: actions(store, router), - }; + return { + state, + mutations, + actions: actions(store, router), + }; } export default initActorsStore; diff --git a/assets/js/api.js b/assets/js/api.js index 5fd6a041..eeb35857 100644 --- a/assets/js/api.js +++ b/assets/js/api.js @@ -1,71 +1,71 @@ import config from 'config'; async function get(endpoint, query = {}) { - const curatedQuery = Object.entries(query).reduce((acc, [key, value]) => (value ? { ...acc, [key]: value } : acc), {}); // remove empty values - const q = new URLSearchParams(curatedQuery).toString(); + const curatedQuery = Object.entries(query).reduce((acc, [key, value]) => (value ? { ...acc, [key]: value } : acc), {}); // remove empty values + const q = new URLSearchParams(curatedQuery).toString(); - const res = await fetch(`${config.api.url}${endpoint}?${q}`, { - method: 'GET', - mode: 'cors', - credentials: 'same-origin', - }); + const res = await fetch(`${config.api.url}${endpoint}?${q}`, { + method: 'GET', + mode: 'cors', + credentials: 'same-origin', + }); - if (res.ok) { - return res.json(); - } + if (res.ok) { + return res.json(); + } - const errorMsg = await res.text(); + const errorMsg = await res.text(); - throw new Error(errorMsg); + throw new Error(errorMsg); } async function post(endpoint, data) { - const res = await fetch(`${config.api.url}${endpoint}`, { - method: 'POST', - mode: 'cors', - headers: { - 'Content-Type': 'application/json', - }, - credentials: 'same-origin', - body: JSON.stringify(data), - }); + const res = await fetch(`${config.api.url}${endpoint}`, { + method: 'POST', + mode: 'cors', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'same-origin', + body: JSON.stringify(data), + }); - if (res.ok) { - return res.json(); - } + if (res.ok) { + return res.json(); + } - const errorMsg = await res.text(); + const errorMsg = await res.text(); - throw new Error(errorMsg); + throw new Error(errorMsg); } async function graphql(query, variables = null) { - const res = await fetch('/graphql', { - method: 'POST', - mode: 'cors', - headers: { - 'Content-Type': 'application/json', - }, - credentials: 'same-origin', - body: JSON.stringify({ - query, - variables, - }), - }); + const res = await fetch('/graphql', { + method: 'POST', + mode: 'cors', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'same-origin', + body: JSON.stringify({ + query, + variables, + }), + }); - if (res.ok) { - const { data } = await res.json(); + if (res.ok) { + const { data } = await res.json(); - return data; - } + return data; + } - const errorMsg = await res.text(); + const errorMsg = await res.text(); - throw new Error(errorMsg); + throw new Error(errorMsg); } export { - get, - post, - graphql, + get, + post, + graphql, }; diff --git a/assets/js/auth/auth.js b/assets/js/auth/auth.js index d3eb5076..cb99d9da 100644 --- a/assets/js/auth/auth.js +++ b/assets/js/auth/auth.js @@ -3,11 +3,11 @@ import mutations from './mutations'; import actions from './actions'; function initAuthStore(store, router) { - return { - state, - mutations, - actions: actions(store, router), - }; + return { + state, + mutations, + actions: actions(store, router), + }; } export default initAuthStore; diff --git a/assets/js/auth/state.js b/assets/js/auth/state.js index 427473f0..b857bfbf 100644 --- a/assets/js/auth/state.js +++ b/assets/js/auth/state.js @@ -1,4 +1,4 @@ export default { - authenticated: false, - user: null, + authenticated: false, + user: null, }; diff --git a/assets/js/config/default.js b/assets/js/config/default.js index 3d86df36..7dc92877 100644 --- a/assets/js/config/default.js +++ b/assets/js/config/default.js @@ -1,10 +1,10 @@ export default { - api: { - url: `${window.location.origin}/api`, - }, - filename: { - pattern: '{site.name} - {title} ({actors.$n.name}, {date} {shootId})', - separator: ', ', - date: 'DD-MM-YYYY', - }, + api: { + url: `${window.location.origin}/api`, + }, + filename: { + pattern: '{site.name} - {title} ({actors.$n.name}, {date} {shootId})', + separator: ', ', + date: 'DD-MM-YYYY', + }, }; diff --git a/assets/js/curate.js b/assets/js/curate.js index 5c31e372..31550a2d 100644 --- a/assets/js/curate.js +++ b/assets/js/curate.js @@ -1,94 +1,94 @@ import dayjs from 'dayjs'; function curateActor(actor, release) { - const curatedActor = { - ...actor, - origin: actor.originCountry && { - country: actor.originCountry, - }, - }; + const curatedActor = { + ...actor, + origin: actor.originCountry && { + country: actor.originCountry, + }, + }; - if (actor.avatar) curatedActor.avatar = actor.avatar.media; + if (actor.avatar) curatedActor.avatar = actor.avatar.media; - if (release && release.date && curatedActor.birthdate) { - curatedActor.ageThen = dayjs(release.date).diff(actor.birthdate, 'year'); - } + if (release && release.date && curatedActor.birthdate) { + curatedActor.ageThen = dayjs(release.date).diff(actor.birthdate, 'year'); + } - return curatedActor; + return curatedActor; } function curateRelease(release) { - const curatedRelease = { - ...release, - actors: [], - poster: release.poster && release.poster.media, - tags: release.tags ? release.tags.map(({ tag }) => tag) : [], - }; + const curatedRelease = { + ...release, + actors: [], + poster: release.poster && release.poster.media, + tags: release.tags ? release.tags.map(({ tag }) => tag) : [], + }; - if (release.site) curatedRelease.network = release.site.network; - if (release.scenes) curatedRelease.scenes = release.scenes.map(({ scene }) => curateRelease(scene)); - if (release.movies) curatedRelease.movies = release.movies.map(({ movie }) => curateRelease(movie)); - 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)); - if (release.movieTags && release.movieTags.length > 0) curatedRelease.tags = release.movieTags.map(({ tag }) => tag); - if (release.movieActors && release.movieActors.length > 0) curatedRelease.actors = release.movieActors.map(({ actor }) => curateActor(actor, curatedRelease)); + if (release.site) curatedRelease.network = release.site.network; + if (release.scenes) curatedRelease.scenes = release.scenes.map(({ scene }) => curateRelease(scene)); + if (release.movies) curatedRelease.movies = release.movies.map(({ movie }) => curateRelease(movie)); + 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)); + if (release.movieTags && release.movieTags.length > 0) curatedRelease.tags = release.movieTags.map(({ tag }) => tag); + if (release.movieActors && release.movieActors.length > 0) curatedRelease.actors = release.movieActors.map(({ actor }) => curateActor(actor, curatedRelease)); - return curatedRelease; + return curatedRelease; } function curateSite(site, network) { - const curatedSite = { - id: site.id, - name: site.name, - slug: site.slug, - url: site.url, - independent: site.independent, - }; + const curatedSite = { + id: site.id, + name: site.name, + slug: site.slug, + url: site.url, + independent: site.independent, + }; - if (site.releases) curatedSite.releases = site.releases.map(release => curateRelease(release)); - if (site.network || network) curatedSite.network = site.network || network; - if (site.tags) curatedSite.tags = site.tags.map(({ tag }) => tag); + if (site.releases) curatedSite.releases = site.releases.map(release => curateRelease(release)); + if (site.network || network) curatedSite.network = site.network || network; + if (site.tags) curatedSite.tags = site.tags.map(({ tag }) => tag); - return curatedSite; + return curatedSite; } function curateNetwork(network, releases) { - const curatedNetwork = { - id: network.id, - name: network.name, - slug: network.slug, - url: network.url, - networks: [], - }; + const curatedNetwork = { + id: network.id, + name: network.name, + slug: network.slug, + url: network.url, + networks: [], + }; - if (network.parent) curatedNetwork.parent = curateNetwork(network.parent); - if (network.sites) curatedNetwork.sites = network.sites.map(site => curateSite(site, curatedNetwork)); - if (network.networks) curatedNetwork.networks = network.networks.map(subNetwork => curateNetwork(subNetwork)); - if (network.studios) curatedNetwork.studios = network.studios; - if (releases) curatedNetwork.releases = releases.map(release => curateRelease(release)); + if (network.parent) curatedNetwork.parent = curateNetwork(network.parent); + if (network.sites) curatedNetwork.sites = network.sites.map(site => curateSite(site, curatedNetwork)); + if (network.networks) curatedNetwork.networks = network.networks.map(subNetwork => curateNetwork(subNetwork)); + if (network.studios) curatedNetwork.studios = network.studios; + if (releases) curatedNetwork.releases = releases.map(release => curateRelease(release)); - return curatedNetwork; + return curatedNetwork; } function curateTag(tag) { - const curatedTag = { - ...tag, - }; + const curatedTag = { + ...tag, + }; - if (tag.releases) curatedTag.releases = tag.releases.map(({ release }) => curateRelease(release)); - if (tag.photos) curatedTag.photos = tag.photos.map(({ media }) => media); - if (tag.poster) curatedTag.poster = tag.poster.media; + if (tag.releases) curatedTag.releases = tag.releases.map(({ release }) => curateRelease(release)); + if (tag.photos) curatedTag.photos = tag.photos.map(({ media }) => media); + if (tag.poster) curatedTag.poster = tag.poster.media; - return curatedTag; + return curatedTag; } export { - curateActor, - curateRelease, - curateSite, - curateNetwork, - curateTag, + curateActor, + curateRelease, + curateSite, + curateNetwork, + curateTag, }; diff --git a/assets/js/fragments.js b/assets/js/fragments.js index 60915fbe..d89c7bdc 100644 --- a/assets/js/fragments.js +++ b/assets/js/fragments.js @@ -278,14 +278,14 @@ const releaseFragment = ` `; export { - releaseActorsFragment, - releaseFields, - releaseTagsFragment, - releasePosterFragment, - releasePhotosFragment, - releaseTrailerFragment, - releasesFragment, - releaseFragment, - siteFragment, - sitesFragment, + releaseActorsFragment, + releaseFields, + releaseTagsFragment, + releasePosterFragment, + releasePhotosFragment, + releaseTrailerFragment, + releasesFragment, + releaseFragment, + siteFragment, + sitesFragment, }; diff --git a/assets/js/get-date-range.js b/assets/js/get-date-range.js index ce486752..4eb86ba8 100644 --- a/assets/js/get-date-range.js +++ b/assets/js/get-date-range.js @@ -1,30 +1,30 @@ import dayjs from 'dayjs'; const dateRanges = { - latest: () => ({ - after: '1900-01-01', - before: dayjs(new Date()).add(1, 'day').format('YYYY-MM-DD'), - orderBy: 'DATE_DESC', - }), - upcoming: () => ({ - after: dayjs(new Date()).format('YYYY-MM-DD'), - before: '2100-01-01', - orderBy: 'DATE_ASC', - }), - new: () => ({ - after: '1900-01-01', - before: '2100-01-01', - orderBy: 'CREATED_AT_DESC', - }), - all: () => ({ - after: '1900-01-01', - before: '2100-01-01', - orderBy: 'DATE_DESC', - }), + latest: () => ({ + after: '1900-01-01', + before: dayjs(new Date()).add(1, 'day').format('YYYY-MM-DD'), + orderBy: 'DATE_DESC', + }), + upcoming: () => ({ + after: dayjs(new Date()).format('YYYY-MM-DD'), + before: '2100-01-01', + orderBy: 'DATE_ASC', + }), + new: () => ({ + after: '1900-01-01', + before: '2100-01-01', + orderBy: 'CREATED_AT_DESC', + }), + all: () => ({ + after: '1900-01-01', + before: '2100-01-01', + orderBy: 'DATE_DESC', + }), }; function getDateRange(range) { - return dateRanges[range](); + return dateRanges[range](); } export default getDateRange; diff --git a/assets/js/main.js b/assets/js/main.js index ad41d4cc..b50ac989 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -14,48 +14,48 @@ import Container from '../components/container/container.vue'; import Icon from '../components/icon/icon.vue'; function init() { - const store = initStore(router); + const store = initStore(router); - initUiObservers(store, router); + initUiObservers(store, router); - if (window.env.sfw) { - store.dispatch('setSfw', true); - } + if (window.env.sfw) { + store.dispatch('setSfw', true); + } - Vue.mixin({ - components: { - Icon, - }, - watch: { - pageTitle(title) { - if (title) { - document.title = `traxxx - ${title}`; - return; - } + Vue.mixin({ + components: { + Icon, + }, + watch: { + pageTitle(title) { + if (title) { + document.title = `traxxx - ${title}`; + return; + } - document.title = 'traxxx'; - }, - }, - methods: { - formatDate: (date, format) => dayjs(date).format(format), - isAfter: (dateA, dateB) => dayjs(dateA).isAfter(dateB), - isBefore: (dateA, dateB) => dayjs(dateA).isBefore(dateB), - }, - }); + document.title = 'traxxx'; + }, + }, + methods: { + formatDate: (date, format) => dayjs(date).format(format), + isAfter: (dateA, dateB) => dayjs(dateA).isAfter(dateB), + isBefore: (dateA, dateB) => dayjs(dateA).isBefore(dateB), + }, + }); - Vue.use(VTooltip); - Vue.use(VueLazyLoad, { - throttleWait: 0, - }); + Vue.use(VTooltip); + Vue.use(VueLazyLoad, { + throttleWait: 0, + }); - new Vue({ // eslint-disable-line no-new - el: '#container', - store, - router, - render(createElement) { - return createElement(Container); - }, - }); + new Vue({ // eslint-disable-line no-new + el: '#container', + store, + router, + render(createElement) { + return createElement(Container); + }, + }); } init(); diff --git a/assets/js/networks/actions.js b/assets/js/networks/actions.js index b24a87f8..758c9f92 100644 --- a/assets/js/networks/actions.js +++ b/assets/js/networks/actions.js @@ -4,10 +4,10 @@ import { curateNetwork } from '../curate'; import getDateRange from '../get-date-range'; function initNetworksActions(store, _router) { - async function fetchNetworkBySlug({ _commit }, { networkSlug, limit = 100, range = 'latest' }) { - const { before, after, orderBy } = getDateRange(range); + async function fetchNetworkBySlug({ _commit }, { networkSlug, limit = 100, range = 'latest' }) { + const { before, after, orderBy } = getDateRange(range); - const { network, releases } = await graphql(` + const { network, releases } = await graphql(` query Network( $networkSlug: String! $limit:Int = 1000, @@ -107,21 +107,21 @@ function initNetworksActions(store, _router) { } } `, { - networkSlug, - limit, - after, - before, - orderBy, - afterTime: store.getters.after, - beforeTime: store.getters.before, - exclude: store.state.ui.filter, - }); + networkSlug, + limit, + after, + before, + orderBy, + afterTime: store.getters.after, + beforeTime: store.getters.before, + exclude: store.state.ui.filter, + }); - return curateNetwork(network, releases); - } + return curateNetwork(network, releases); + } - async function fetchNetworks({ _commit }) { - const { networks } = await graphql(` + async function fetchNetworks({ _commit }) { + const { networks } = await graphql(` query Networks { networks(orderBy: NAME_ASC) { id @@ -133,13 +133,13 @@ function initNetworksActions(store, _router) { } `); - return networks.map(network => curateNetwork(network)); - } + return networks.map(network => curateNetwork(network)); + } - return { - fetchNetworkBySlug, - fetchNetworks, - }; + return { + fetchNetworkBySlug, + fetchNetworks, + }; } export default initNetworksActions; diff --git a/assets/js/networks/networks.js b/assets/js/networks/networks.js index 44aeaa6c..92968ef1 100644 --- a/assets/js/networks/networks.js +++ b/assets/js/networks/networks.js @@ -3,11 +3,11 @@ import mutations from './mutations'; import actions from './actions'; function initNetworksStore(store, router) { - return { - state, - mutations, - actions: actions(store, router), - }; + return { + state, + mutations, + actions: actions(store, router), + }; } export default initNetworksStore; diff --git a/assets/js/releases/actions.js b/assets/js/releases/actions.js index d5a7a8b7..11054a96 100644 --- a/assets/js/releases/actions.js +++ b/assets/js/releases/actions.js @@ -4,10 +4,10 @@ import { curateRelease } from '../curate'; import getDateRange from '../get-date-range'; function initReleasesActions(store, _router) { - async function fetchReleases({ _commit }, { limit = 100, range = 'latest' }) { - const { before, after, orderBy } = getDateRange(range); + async function fetchReleases({ _commit }, { limit = 100, range = 'latest' }) { + const { before, after, orderBy } = getDateRange(range); - const { releases } = await graphql(` + const { releases } = await graphql(` query Releases( $limit:Int = 1000, $after:Date = "1900-01-01", @@ -18,18 +18,18 @@ function initReleasesActions(store, _router) { ${releasesFragment} } `, { - limit, - after, - before, - orderBy, - exclude: store.state.ui.filter, - }); + limit, + after, + before, + orderBy, + exclude: store.state.ui.filter, + }); - return releases.map(release => curateRelease(release)); - } + return releases.map(release => curateRelease(release)); + } - async function searchReleases({ _commit }, { query, limit = 20 }) { - const res = await graphql(` + async function searchReleases({ _commit }, { query, limit = 20 }) { + const res = await graphql(` query SearchReleases( $query: String! $limit: Int = 20 @@ -88,34 +88,34 @@ function initReleasesActions(store, _router) { } } `, { - query, - limit, - }); + query, + limit, + }); - if (!res) return []; + if (!res) return []; - return res.releases.map(release => curateRelease(release)); - } + return res.releases.map(release => curateRelease(release)); + } - async function fetchReleaseById({ _commit }, releaseId) { - // const release = await get(`/releases/${releaseId}`); + async function fetchReleaseById({ _commit }, releaseId) { + // const release = await get(`/releases/${releaseId}`); - const { release } = await graphql(` + const { release } = await graphql(` query Release($releaseId:Int!) { ${releaseFragment} } `, { - releaseId: Number(releaseId), - }); + releaseId: Number(releaseId), + }); - return curateRelease(release); - } + return curateRelease(release); + } - return { - fetchReleases, - fetchReleaseById, - searchReleases, - }; + return { + fetchReleases, + fetchReleaseById, + searchReleases, + }; } export default initReleasesActions; diff --git a/assets/js/releases/mutations.js b/assets/js/releases/mutations.js index d2c2c16a..ad325269 100644 --- a/assets/js/releases/mutations.js +++ b/assets/js/releases/mutations.js @@ -1,14 +1,14 @@ import Vue from 'vue'; function setCache(state, { target, releases }) { - Vue.set(state.cache, target, releases); + Vue.set(state.cache, target, releases); } function deleteCache(state, target) { - Vue.delete(state.cache, target); + Vue.delete(state.cache, target); } export default { - setCache, - deleteCache, + setCache, + deleteCache, }; diff --git a/assets/js/releases/releases.js b/assets/js/releases/releases.js index 3d53e4de..d2875c4b 100644 --- a/assets/js/releases/releases.js +++ b/assets/js/releases/releases.js @@ -3,11 +3,11 @@ import mutations from './mutations'; import actions from './actions'; function initReleasesStore(store, router) { - return { - state, - mutations, - actions: actions(store, router), - }; + return { + state, + mutations, + actions: actions(store, router), + }; } export default initReleasesStore; diff --git a/assets/js/releases/state.js b/assets/js/releases/state.js index 4b077775..16c8b052 100644 --- a/assets/js/releases/state.js +++ b/assets/js/releases/state.js @@ -1,3 +1,3 @@ export default { - cache: {}, + cache: {}, }; diff --git a/assets/js/router.js b/assets/js/router.js index d2f85a3f..aebece79 100644 --- a/assets/js/router.js +++ b/assets/js/router.js @@ -16,139 +16,139 @@ import NotFound from '../components/errors/404.vue'; Vue.use(VueRouter); const routes = [ - { - path: '/', - redirect: { - name: 'latest', - }, - }, - { - path: '/home', - redirect: { - name: 'latest', - }, - }, - { - path: '/latest', - component: Home, - name: 'latest', - }, - { - path: '/upcoming', - component: Home, - name: 'upcoming', - }, - { - path: '/new', - component: Home, - name: 'new', - }, - { - path: '/scene/:releaseId/:releaseSlug?', - component: Release, - name: 'scene', - }, - { - path: '/movie/:releaseId/:releaseSlug?', - component: Release, - name: 'movie', - }, - { - path: '/actor/:actorSlug', - name: 'actor', - redirect: from => ({ - name: 'actorRange', - params: { - ...from.params, - range: 'latest', - }, - }), - }, - { - path: '/actor/:actorSlug/:range', - component: Actor, - name: 'actorRange', - }, - { - path: '/site/:siteSlug', - component: Site, - name: 'site', - redirect: from => ({ - name: 'siteRange', - params: { - ...from.params, - range: 'latest', - }, - }), - }, - { - path: '/site/:siteSlug/:range', - component: Site, - name: 'siteRange', - }, - { - path: '/network/:networkSlug', - component: Network, - name: 'network', - redirect: from => ({ - name: 'networkRange', - params: { - ...from.params, - range: 'latest', - }, - }), - }, - { - path: '/network/:networkSlug/:range', - component: Network, - name: 'networkRange', - }, - { - path: '/tag/:tagSlug', - component: Tag, - name: 'tag', - redirect: from => ({ - name: 'tagRange', - params: { - ...from.params, - range: 'latest', - }, - }), - }, - { - path: '/tag/:tagSlug/:range', - component: Tag, - name: 'tagRange', - }, - { - path: '/actors/:gender?/:letter?', - component: Actors, - name: 'actors', - }, - { - path: '/networks', - component: Networks, - name: 'networks', - }, - { - path: '/tags', - component: Tags, - name: 'tags', - }, - { - path: '/search', - component: Search, - name: 'search', - }, - { - path: '*', - component: NotFound, - }, + { + path: '/', + redirect: { + name: 'latest', + }, + }, + { + path: '/home', + redirect: { + name: 'latest', + }, + }, + { + path: '/latest', + component: Home, + name: 'latest', + }, + { + path: '/upcoming', + component: Home, + name: 'upcoming', + }, + { + path: '/new', + component: Home, + name: 'new', + }, + { + path: '/scene/:releaseId/:releaseSlug?', + component: Release, + name: 'scene', + }, + { + path: '/movie/:releaseId/:releaseSlug?', + component: Release, + name: 'movie', + }, + { + path: '/actor/:actorSlug', + name: 'actor', + redirect: from => ({ + name: 'actorRange', + params: { + ...from.params, + range: 'latest', + }, + }), + }, + { + path: '/actor/:actorSlug/:range', + component: Actor, + name: 'actorRange', + }, + { + path: '/site/:siteSlug', + component: Site, + name: 'site', + redirect: from => ({ + name: 'siteRange', + params: { + ...from.params, + range: 'latest', + }, + }), + }, + { + path: '/site/:siteSlug/:range', + component: Site, + name: 'siteRange', + }, + { + path: '/network/:networkSlug', + component: Network, + name: 'network', + redirect: from => ({ + name: 'networkRange', + params: { + ...from.params, + range: 'latest', + }, + }), + }, + { + path: '/network/:networkSlug/:range', + component: Network, + name: 'networkRange', + }, + { + path: '/tag/:tagSlug', + component: Tag, + name: 'tag', + redirect: from => ({ + name: 'tagRange', + params: { + ...from.params, + range: 'latest', + }, + }), + }, + { + path: '/tag/:tagSlug/:range', + component: Tag, + name: 'tagRange', + }, + { + path: '/actors/:gender?/:letter?', + component: Actors, + name: 'actors', + }, + { + path: '/networks', + component: Networks, + name: 'networks', + }, + { + path: '/tags', + component: Tags, + name: 'tags', + }, + { + path: '/search', + component: Search, + name: 'search', + }, + { + path: '*', + component: NotFound, + }, ]; const router = new VueRouter({ - mode: 'history', - routes, + mode: 'history', + routes, }); export default router; diff --git a/assets/js/sites/actions.js b/assets/js/sites/actions.js index 619680d9..4ae530a8 100644 --- a/assets/js/sites/actions.js +++ b/assets/js/sites/actions.js @@ -4,10 +4,10 @@ import { curateSite } from '../curate'; import getDateRange from '../get-date-range'; function initSitesActions(store, _router) { - async function fetchSiteBySlug({ _commit }, { siteSlug, limit = 100, range = 'latest' }) { - const { before, after, orderBy } = getDateRange(range); + async function fetchSiteBySlug({ _commit }, { siteSlug, limit = 100, range = 'latest' }) { + const { before, after, orderBy } = getDateRange(range); - const { site } = await graphql(` + const { site } = await graphql(` query Site( $siteSlug: String!, $limit:Int = 100, @@ -37,20 +37,20 @@ function initSitesActions(store, _router) { } } `, { - siteSlug, - limit, - after, - before, - orderBy, - isNew: store.getters.isNew, - exclude: store.state.ui.filter, - }); + siteSlug, + limit, + after, + before, + orderBy, + isNew: store.getters.isNew, + exclude: store.state.ui.filter, + }); - return curateSite(site); - } + return curateSite(site); + } - async function fetchSites({ _commit }, { limit = 100 }) { - const { sites } = await graphql(` + async function fetchSites({ _commit }, { limit = 100 }) { + const { sites } = await graphql(` query Sites( $actorSlug: String! $limit:Int = 100, @@ -64,16 +64,16 @@ function initSitesActions(store, _router) { } } `, { - limit, - after: store.getters.after, - before: store.getters.before, - }); + limit, + after: store.getters.after, + before: store.getters.before, + }); - return sites; - } + return sites; + } - async function searchSites({ _commit }, { query, limit = 20 }) { - const { sites } = await graphql(` + async function searchSites({ _commit }, { query, limit = 20 }) { + const { sites } = await graphql(` query SearchSites( $query: String! $limit:Int = 20, @@ -93,18 +93,18 @@ function initSitesActions(store, _router) { } } `, { - query, - limit, - }); + query, + limit, + }); - return sites; - } + return sites; + } - return { - fetchSiteBySlug, - fetchSites, - searchSites, - }; + return { + fetchSiteBySlug, + fetchSites, + searchSites, + }; } export default initSitesActions; diff --git a/assets/js/sites/sites.js b/assets/js/sites/sites.js index 81cc0028..c29c9001 100644 --- a/assets/js/sites/sites.js +++ b/assets/js/sites/sites.js @@ -3,11 +3,11 @@ import mutations from './mutations'; import actions from './actions'; function initSitesStore(store, router) { - return { - state, - mutations, - actions: actions(store, router), - }; + return { + state, + mutations, + actions: actions(store, router), + }; } export default initSitesStore; diff --git a/assets/js/store.js b/assets/js/store.js index dce594c2..663fae27 100644 --- a/assets/js/store.js +++ b/assets/js/store.js @@ -10,19 +10,19 @@ import initActorsStore from './actors/actors'; import initTagsStore from './tags/tags'; function initStore(router) { - Vue.use(Vuex); + Vue.use(Vuex); - const store = new Vuex.Store(); + const store = new Vuex.Store(); - store.registerModule('ui', initUiStore(store, router)); - store.registerModule('auth', initAuthStore(store, router)); - store.registerModule('releases', initReleasesStore(store, router)); - store.registerModule('actors', initActorsStore(store, router)); - store.registerModule('sites', initSitesStore(store, router)); - store.registerModule('networks', initNetworksStore(store, router)); - store.registerModule('tags', initTagsStore(store, router)); + store.registerModule('ui', initUiStore(store, router)); + store.registerModule('auth', initAuthStore(store, router)); + store.registerModule('releases', initReleasesStore(store, router)); + store.registerModule('actors', initActorsStore(store, router)); + store.registerModule('sites', initSitesStore(store, router)); + store.registerModule('networks', initNetworksStore(store, router)); + store.registerModule('tags', initTagsStore(store, router)); - return store; + return store; } export default initStore; diff --git a/assets/js/tags/actions.js b/assets/js/tags/actions.js index f86bf235..8a435907 100644 --- a/assets/js/tags/actions.js +++ b/assets/js/tags/actions.js @@ -1,15 +1,15 @@ import { graphql, get } from '../api'; import { - releaseFields, + releaseFields, } from '../fragments'; import { curateTag } from '../curate'; import getDateRange from '../get-date-range'; function initTagsActions(store, _router) { - async function fetchTagBySlug({ _commit }, { tagSlug, limit = 100, range = 'latest' }) { - const { before, after, orderBy } = getDateRange(range); + async function fetchTagBySlug({ _commit }, { tagSlug, limit = 100, range = 'latest' }) { + const { before, after, orderBy } = getDateRange(range); - const { tagBySlug } = await graphql(` + const { tagBySlug } = await graphql(` query Tag( $tagSlug:String! $limit:Int = 1000, @@ -85,24 +85,24 @@ function initTagsActions(store, _router) { } } `, { - tagSlug, - limit, - after, - before, - orderBy: orderBy === 'DATE_DESC' ? 'RELEASE_BY_RELEASE_ID__DATE_DESC' : 'RELEASE_BY_RELEASE_ID__DATE_ASC', - exclude: store.state.ui.filter, - }); + tagSlug, + limit, + after, + before, + orderBy: orderBy === 'DATE_DESC' ? 'RELEASE_BY_RELEASE_ID__DATE_DESC' : 'RELEASE_BY_RELEASE_ID__DATE_ASC', + exclude: store.state.ui.filter, + }); - return curateTag(tagBySlug, store); - } + return curateTag(tagBySlug, store); + } - async function fetchTags({ _commit }, { - limit = 100, - slugs = [], - _group, - _priority, - }) { - const { tags } = await graphql(` + async function fetchTags({ _commit }, { + limit = 100, + slugs = [], + _group, + _priority, + }) { + const { tags } = await graphql(` query Tags( $slugs: [String!] = [], $limit: Int = 100 @@ -133,28 +133,28 @@ function initTagsActions(store, _router) { } } `, { - slugs, - limit, - }); + slugs, + limit, + }); - return tags.map(tag => curateTag(tag, store.state.ui.sfw)); - } + return tags.map(tag => curateTag(tag, store.state.ui.sfw)); + } - async function fetchTagReleases({ _commit }, tagId) { - const releases = await get(`/tags/${tagId}/releases`, { - filter: store.state.ui.filter, - after: store.getters.after, - before: store.getters.before, - }); + async function fetchTagReleases({ _commit }, tagId) { + const releases = await get(`/tags/${tagId}/releases`, { + filter: store.state.ui.filter, + after: store.getters.after, + before: store.getters.before, + }); - return releases; - } + return releases; + } - return { - fetchTagBySlug, - fetchTags, - fetchTagReleases, - }; + return { + fetchTagBySlug, + fetchTags, + fetchTagReleases, + }; } export default initTagsActions; diff --git a/assets/js/tags/tags.js b/assets/js/tags/tags.js index 47b5b20f..92a56f8d 100644 --- a/assets/js/tags/tags.js +++ b/assets/js/tags/tags.js @@ -3,11 +3,11 @@ import mutations from './mutations'; import actions from './actions'; function initTagsStore(store, router) { - return { - state, - mutations, - actions: actions(store, router), - }; + return { + state, + mutations, + actions: actions(store, router), + }; } export default initTagsStore; diff --git a/assets/js/ui/actions.js b/assets/js/ui/actions.js index 6481ddd5..a53d2b3a 100644 --- a/assets/js/ui/actions.js +++ b/assets/js/ui/actions.js @@ -1,35 +1,35 @@ function initUiActions(_store, _router) { - function setFilter({ commit }, filter) { - commit('setFilter', filter); - localStorage.setItem('filter', filter); - } + function setFilter({ commit }, filter) { + commit('setFilter', filter); + localStorage.setItem('filter', filter); + } - function setRange({ commit }, range) { - commit('setRange', range); - } + function setRange({ commit }, range) { + commit('setRange', range); + } - function setBatch({ commit }, batch) { - commit('setBatch', batch); - localStorage.setItem('batch', batch); - } + function setBatch({ commit }, batch) { + commit('setBatch', batch); + localStorage.setItem('batch', batch); + } - function setTheme({ commit }, theme) { - commit('setTheme', theme); - localStorage.setItem('theme', theme); - } + function setTheme({ commit }, theme) { + commit('setTheme', theme); + localStorage.setItem('theme', theme); + } - async function setSfw({ commit }, sfw) { - commit('setSfw', sfw); - localStorage.setItem('sfw', sfw); - } + async function setSfw({ commit }, sfw) { + commit('setSfw', sfw); + localStorage.setItem('sfw', sfw); + } - return { - setFilter, - setRange, - setBatch, - setSfw, - setTheme, - }; + return { + setFilter, + setRange, + setBatch, + setSfw, + setTheme, + }; } export default initUiActions; diff --git a/assets/js/ui/getters.js b/assets/js/ui/getters.js index 9b6a9611..9352d136 100644 --- a/assets/js/ui/getters.js +++ b/assets/js/ui/getters.js @@ -1,47 +1,47 @@ import dayjs from 'dayjs'; const dateRanges = { - latest: () => ({ - after: '1900-01-01', - before: dayjs(new Date()).add(1, 'day').format('YYYY-MM-DD'), - orderBy: 'DATE_DESC', - }), - upcoming: () => ({ - after: dayjs(new Date()).format('YYYY-MM-DD'), - before: '2100-01-01', - orderBy: 'DATE_ASC', - }), - new: () => ({ - after: '1900-01-01', - before: '2100-01-01', - orderBy: 'CREATED_AT_DESC', - }), - all: () => ({ - after: '1900-01-01', - before: '2100-01-01', - orderBy: 'DATE_DESC', - }), + latest: () => ({ + after: '1900-01-01', + before: dayjs(new Date()).add(1, 'day').format('YYYY-MM-DD'), + orderBy: 'DATE_DESC', + }), + upcoming: () => ({ + after: dayjs(new Date()).format('YYYY-MM-DD'), + before: '2100-01-01', + orderBy: 'DATE_ASC', + }), + new: () => ({ + after: '1900-01-01', + before: '2100-01-01', + orderBy: 'CREATED_AT_DESC', + }), + all: () => ({ + after: '1900-01-01', + before: '2100-01-01', + orderBy: 'DATE_DESC', + }), }; function rangeDates(state) { - return dateRanges[state.range](); + return dateRanges[state.range](); } function before(state) { - return dateRanges[state.range]().before; + return dateRanges[state.range]().before; } function after(state) { - return dateRanges[state.range]().after; + return dateRanges[state.range]().after; } function orderBy(state) { - return dateRanges[state.range]().orderBy; + return dateRanges[state.range]().orderBy; } export default { - rangeDates, - before, - after, - orderBy, + rangeDates, + before, + after, + orderBy, }; diff --git a/assets/js/ui/mutations.js b/assets/js/ui/mutations.js index 85ed471a..728955ce 100644 --- a/assets/js/ui/mutations.js +++ b/assets/js/ui/mutations.js @@ -1,27 +1,27 @@ function setFilter(state, filter) { - state.filter = filter; + state.filter = filter; } function setRange(state, range) { - state.range = range; + state.range = range; } function setBatch(state, batch) { - state.batch = batch; + state.batch = batch; } function setSfw(state, sfw) { - state.sfw = sfw; + state.sfw = sfw; } function setTheme(state, theme) { - state.theme = theme; + state.theme = theme; } export default { - setFilter, - setRange, - setBatch, - setSfw, - setTheme, + setFilter, + setRange, + setBatch, + setSfw, + setTheme, }; diff --git a/assets/js/ui/observers.js b/assets/js/ui/observers.js index 7f4cf96f..1094c270 100644 --- a/assets/js/ui/observers.js +++ b/assets/js/ui/observers.js @@ -1,25 +1,25 @@ function initUiObservers(store, _router) { - document.addEventListener('keypress', (event) => { - if (event.target.tagName === 'INPUT') { - return; - } + document.addEventListener('keypress', (event) => { + if (event.target.tagName === 'INPUT') { + return; + } - if (event.key === 's') { - store.dispatch('setSfw', true); - } + if (event.key === 's') { + store.dispatch('setSfw', true); + } - if (event.key === 'n') { - store.dispatch('setSfw', false); - } + if (event.key === 'n') { + store.dispatch('setSfw', false); + } - if (event.key === 'd') { - store.dispatch('setTheme', 'dark'); - } + if (event.key === 'd') { + store.dispatch('setTheme', 'dark'); + } - if (event.key === 'l') { - store.dispatch('setTheme', 'light'); - } - }); + if (event.key === 'l') { + store.dispatch('setTheme', 'light'); + } + }); } export default initUiObservers; diff --git a/assets/js/ui/state.js b/assets/js/ui/state.js index 0ade8df6..ba91e6c8 100644 --- a/assets/js/ui/state.js +++ b/assets/js/ui/state.js @@ -4,9 +4,9 @@ const storedSfw = localStorage.getItem('sfw'); const storedTheme = localStorage.getItem('theme'); export default { - filter: storedFilter ? storedFilter.split(',') : ['gay', 'transsexual'], - range: 'latest', - batch: storedBatch || 'all', - sfw: storedSfw === 'true' || false, - theme: storedTheme || 'light', + filter: storedFilter ? storedFilter.split(',') : ['gay', 'transsexual'], + range: 'latest', + batch: storedBatch || 'all', + sfw: storedSfw === 'true' || false, + theme: storedTheme || 'light', }; diff --git a/assets/js/ui/ui.js b/assets/js/ui/ui.js index 35b0e5ef..f569a9a7 100644 --- a/assets/js/ui/ui.js +++ b/assets/js/ui/ui.js @@ -4,12 +4,12 @@ import getters from './getters'; import actions from './actions'; function initUiStore(store, router) { - return { - state, - mutations, - getters, - actions: actions(store, router), - }; + return { + state, + mutations, + getters, + actions: actions(store, router), + }; } export default initUiStore; diff --git a/config/default.js b/config/default.js index 80c90510..03f0f3d3 100644 --- a/config/default.js +++ b/config/default.js @@ -1,177 +1,181 @@ module.exports = { - database: { - host: '127.0.0.1', - user: 'user', - password: 'password', - database: 'traxxx', - }, - web: { - host: '0.0.0.0', - port: 5000, - sfwHost: '0.0.0.0', - sfwPort: 5001, - }, - // include: [], - // exclude: [], - exclude: [ - ['21sextreme', [ - // no longer updated - 'mightymistress', - 'dominatedgirls', - 'homepornreality', - 'peeandblow', - 'cummingmatures', - 'mandyiskinky', - 'speculumplays', - 'creampiereality', - ]], - ['aziani', [ - 'amberathome', - 'marycarey', - 'racqueldevonshire', - ]], - ['blowpass', ['sunlustxxx']], - ['ddfnetwork', [ - 'fuckinhd', - 'bustylover', - ]], - ['famedigital', [ - 'daringsex', - 'lowartfilms', - ]], - ['pornpros', [ - 'milfhumiliation', - 'humiliated', - 'flexiblepositions', - 'publicviolations', - 'amateurviolations', - 'squirtdisgrace', - 'cumdisgrace', - 'webcamhackers', - 'collegeteens', - ]], - ['score', [ - 'bigboobbundle', - 'milfbundle', - 'pornmegaload', - 'scorelandtv', - 'scoretv', - ]], - ], - profiles: [ - [ - 'evilangel', - 'famedigital', - ], - [ - // Gamma; Evil Angel + Devil's Film, Pure Taboo (unavailable), Burning Angel and Wicked have their own assets - 'xempire', - 'blowpass', - ], - [ - // MindGeek; Brazzers and Mile High Media have their own assets - 'realitykings', - 'mofos', - 'digitalplayground', - 'twistys', - 'babes', - 'fakehub', - 'sexyhub', - 'metrohd', - 'iconmale', - 'men', - 'transangels', - ], - 'wicked', - 'burningangel', - 'brazzers', - 'milehighmedia', - [ - 'vixen', - 'tushy', - 'blacked', - 'tushyraw', - 'blackedraw', - 'deeper', - ], - [ - // Nubiles - 'nubiles', - 'nubilesporn', - 'deeplush', - 'brattysis', - 'nfbusty', - 'anilos', - 'hotcrazymess', - 'thatsitcomshow', - ], - '21sextury', - 'julesjordan', - 'naughtyamerica', - 'cherrypimps', - 'pimpxxx', - [ - 'hussiepass', - 'hushpass', - 'interracialpass', - 'interracialpovs', - 'povpornstars', - 'seehimfuck', - 'eyeontheguy', - ], - [ - // Full Porn Network - 'analized', - 'hergape', - 'jamesdeen', - 'dtfsluts', - 'analbbc', - 'analviolation', - 'baddaddypov', - 'girlfaction', - 'homemadeanalwhores', - 'mugfucked', - 'onlyprince', - 'pervertgallery', - 'povperverts', - ], - 'private', - 'ddfnetwork', - 'bangbros', - 'kellymadison', - 'gangbangcreampie', - 'gloryholesecrets', - 'aziani', - 'legalporno', - 'score', - 'boobpedia', - 'pornhub', - 'freeones', - 'freeonesLegacy', - ], - proxy: { - enable: false, - host: '', - port: 8888, - hostnames: [ - 'www.vixen.com', - 'www.blacked.com', - 'www.blackedraw.com', - 'www.tushy.com', - 'www.tushyraw.com', - 'www.deeper.com', - ], - }, - fetchAfter: [1, 'week'], - nullDateLimit: 3, - media: { - path: './media', - thumbnailSize: 320, // width for 16:9 will be exactly 576px - thumbnailQuality: 100, - lazySize: 90, - lazyQuality: 90, - videoQuality: [480, 360, 320, 540, 720, 1080, 2160, 270, 240, 180], - limit: 25, // max number of photos per release - }, - titleSlugLength: 50, + database: { + host: '127.0.0.1', + user: 'user', + password: 'password', + database: 'traxxx', + }, + web: { + host: '0.0.0.0', + port: 5000, + sfwHost: '0.0.0.0', + sfwPort: 5001, + }, + // include: [], + // exclude: [], + exclude: [ + ['21sextreme', [ + // no longer updated + 'mightymistress', + 'dominatedgirls', + 'homepornreality', + 'peeandblow', + 'cummingmatures', + 'mandyiskinky', + 'speculumplays', + 'creampiereality', + ]], + ['aziani', [ + 'amberathome', + 'marycarey', + 'racqueldevonshire', + ]], + 'boobpedia', + ['blowpass', ['sunlustxxx']], + ['ddfnetwork', [ + 'fuckinhd', + 'bustylover', + ]], + ['famedigital', [ + 'daringsex', + 'lowartfilms', + ]], + 'freeones', + ['pornpros', [ + 'milfhumiliation', + 'humiliated', + 'flexiblepositions', + 'publicviolations', + 'amateurviolations', + 'squirtdisgrace', + 'cumdisgrace', + 'webcamhackers', + 'collegeteens', + ]], + ['score', [ + 'bigboobbundle', + 'milfbundle', + 'pornmegaload', + 'scorelandtv', + 'scoretv', + ]], + ['mindgeek', [ + 'pornhub', + ]], + ], + profiles: [ + [ + 'evilangel', + 'famedigital', + ], + [ + // Gamma; Evil Angel + Devil's Film, Pure Taboo (unavailable), Burning Angel and Wicked have their own assets + 'xempire', + 'blowpass', + ], + [ + // MindGeek; Brazzers and Mile High Media have their own assets + 'realitykings', + 'mofos', + 'digitalplayground', + 'twistys', + 'babes', + 'fakehub', + 'sexyhub', + 'metrohd', + 'iconmale', + 'men', + 'transangels', + ], + 'wicked', + 'burningangel', + 'brazzers', + 'milehighmedia', + [ + 'vixen', + 'tushy', + 'blacked', + 'tushyraw', + 'blackedraw', + 'deeper', + ], + [ + // Nubiles + 'nubiles', + 'nubilesporn', + 'deeplush', + 'brattysis', + 'nfbusty', + 'anilos', + 'hotcrazymess', + 'thatsitcomshow', + ], + '21sextury', + 'julesjordan', + 'naughtyamerica', + 'cherrypimps', + 'pimpxxx', + [ + 'hussiepass', + 'hushpass', + 'interracialpass', + 'interracialpovs', + 'povpornstars', + 'seehimfuck', + 'eyeontheguy', + ], + [ + // Full Porn Network + 'analized', + 'hergape', + 'jamesdeen', + 'dtfsluts', + 'analbbc', + 'analviolation', + 'baddaddypov', + 'girlfaction', + 'homemadeanalwhores', + 'mugfucked', + 'onlyprince', + 'pervertgallery', + 'povperverts', + ], + 'private', + 'ddfnetwork', + 'bangbros', + 'kellymadison', + 'gangbangcreampie', + 'gloryholesecrets', + 'aziani', + 'legalporno', + 'score', + 'boobpedia', + 'pornhub', + 'freeones', + ], + proxy: { + enable: false, + host: '', + port: 8888, + hostnames: [ + 'www.vixen.com', + 'www.blacked.com', + 'www.blackedraw.com', + 'www.tushy.com', + 'www.tushyraw.com', + 'www.deeper.com', + ], + }, + fetchAfter: [1, 'week'], + nullDateLimit: 3, + media: { + path: './media', + thumbnailSize: 320, // width for 16:9 will be exactly 576px + thumbnailQuality: 100, + lazySize: 90, + lazyQuality: 90, + videoQuality: [480, 360, 320, 540, 720, 1080, 2160, 270, 240, 180], + limit: 25, // max number of photos per release + }, + titleSlugLength: 50, }; diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js index 7801b468..043c8add 100644 --- a/migrations/20190325001339_releases.js +++ b/migrations/20190325001339_releases.js @@ -1,758 +1,773 @@ exports.up = knex => Promise.resolve() - .then(() => knex.schema.createTable('countries', (table) => { - table.string('alpha2', 2) - .unique() - .primary(); + .then(() => knex.schema.createTable('countries', (table) => { + table.string('alpha2', 2) + .unique() + .primary(); - table.string('alpha3', 3) - .unique(); + table.string('alpha3', 3) + .unique(); - table.string('name') - .notNullable(); + table.string('name') + .notNullable(); - table.string('alias'); + table.string('alias'); - table.integer('code', 3); - table.string('nationality'); - table.integer('priority', 2) - .defaultTo(0); - })) - .then(() => knex.schema.createTable('media', (table) => { - table.string('id', 21) - .primary(); + table.integer('code', 3); + table.string('nationality'); + table.integer('priority', 2) + .defaultTo(0); + })) + .then(() => knex.schema.createTable('media', (table) => { + table.string('id', 21) + .primary(); - table.string('path'); - table.string('thumbnail'); - table.string('lazy'); - table.integer('index'); - table.string('mime'); + table.string('path'); + table.string('thumbnail'); + table.string('lazy'); + table.integer('index'); + table.string('mime'); - table.string('hash'); + table.string('hash'); - table.integer('size', 12); - table.integer('quality', 6); - table.integer('width', 6); - table.integer('height', 6); - table.float('entropy'); + table.integer('size', 12); + table.integer('quality', 6); + table.integer('width', 6); + table.integer('height', 6); + table.float('entropy'); - table.string('scraper', 32); - table.string('copyright', 100); + table.string('scraper', 32); + table.string('copyright', 100); - table.string('source', 2100); - table.string('source_page', 2100); + table.string('source', 2100); + table.string('source_page', 2100); - table.text('comment'); - table.string('group'); + table.text('comment'); + table.string('group'); - table.unique('hash'); - table.unique('source'); + table.unique('hash'); + table.unique('source'); - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('media_sfw', (table) => { - table.string('id', 21) - .primary(); + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('media_sfw', (table) => { + table.string('id', 21) + .primary(); - table.string('media_id', 21) - .references('id') - .inTable('media') - .unique(); - })) - .then(() => knex.raw(` + table.string('media_id', 21) + .references('id') + .inTable('media') + .unique(); + })) + .then(() => knex.raw(` CREATE FUNCTION get_random_sfw_media_id() RETURNS varchar AS $$ SELECT media_id FROM media_sfw ORDER BY random() LIMIT 1; $$ LANGUAGE sql STABLE; `)) - .then(() => knex.schema.alterTable('media', (table) => { - table.string('sfw_media_id', 21) - .references('id') - .inTable('media') - .defaultTo(knex.raw('get_random_sfw_media_id()')); - })) - .then(() => knex.schema.createTable('tags_groups', (table) => { - table.increments('id', 12); - - table.string('name', 32); - table.text('description'); - - table.string('slug', 32) - .unique(); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('tags', (table) => { - table.increments('id', 12); - table.string('name'); - - table.text('description'); - - table.integer('priority', 2) - .defaultTo(0); - - table.boolean('secondary') - .defaultTo(false); - - table.integer('group_id', 12) - .references('id') - .inTable('tags_groups'); - - table.integer('alias_for', 12) - .references('id') - .inTable('tags'); - - table.string('slug', 32) - .unique(); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('tags_posters', (table) => { - table.integer('tag_id', 12) - .notNullable() - .references('id') - .inTable('tags'); - - table.string('media_id', 21) - .notNullable() - .references('id') - .inTable('media'); - - table.unique('tag_id'); - })) - .then(() => knex.schema.createTable('tags_photos', (table) => { - table.integer('tag_id', 12) - .notNullable() - .references('id') - .inTable('tags'); - - table.string('media_id', 21) - .notNullable() - .references('id') - .inTable('media'); - - table.unique(['tag_id', 'media_id']); - })) - .then(() => knex.schema.createTable('networks', (table) => { - table.increments('id', 12); - - table.string('name'); - table.string('url'); - table.text('description'); - table.json('parameters'); - - table.integer('parent_id', 12) - .references('id') - .inTable('networks'); - - table.string('slug', 32) - .unique(); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('networks_social', (table) => { - table.increments('id', 16); - - table.string('url'); - table.string('platform'); - - table.integer('network_id', 12) - .notNullable() - .references('id') - .inTable('networks'); - - table.unique(['url', 'network_id']); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('sites', (table) => { - table.increments('id', 12); - - table.integer('network_id', 12) - .notNullable() - .references('id') - .inTable('networks'); - - table.string('name'); - table.string('slug', 32) - .unique(); - - table.string('alias'); - - table.string('url'); - table.text('description'); - table.json('parameters'); - - table.integer('priority', 3) - .defaultTo(0); - - table.boolean('show') - .defaultTo(true); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('sites_tags', (table) => { - table.integer('tag_id', 12) - .notNullable() - .references('id') - .inTable('tags'); - - table.integer('site_id', 12) - .notNullable() - .references('id') - .inTable('sites'); - - table.boolean('inherit') - .defaultTo(false); - - table.unique(['tag_id', 'site_id']); - })) - .then(() => knex.schema.createTable('sites_social', (table) => { - table.increments('id', 16); - - table.string('url'); - table.string('platform'); - - table.integer('site_id', 12) - .notNullable() - .references('id') - .inTable('sites'); - - table.unique(['url', 'site_id']); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('studios', (table) => { - table.increments('id', 12); - - table.integer('network_id', 12) - .notNullable() - .references('id') - .inTable('networks'); - - table.string('name'); - table.string('url'); - table.text('description'); - - table.string('slug', 32) - .unique(); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('actors', (table) => { - table.increments('id', 12); - - table.string('name') - .notNullable(); - - table.string('slug', 32); - - table.integer('network_id', 12) - .references('id') - .inTable('networks'); - - table.unique(['slug', 'network_id']); - - table.integer('alias_for', 12) - .references('id') - .inTable('actors'); - - table.date('birthdate'); - table.string('gender', 18); - table.text('description'); - - table.string('birth_city'); - table.string('birth_state'); - table.string('birth_country_alpha2', 2) - .references('alpha2') - .inTable('countries'); - - table.string('residence_city'); - table.string('residence_state'); - table.string('residence_country_alpha2', 2) - .references('alpha2') - .inTable('countries'); - - table.string('ethnicity'); - - table.string('bust', 10); - table.integer('waist', 3); - table.integer('hip', 3); - table.boolean('natural_boobs'); - - table.integer('height', 3); - table.integer('weight', 3); - table.string('eyes'); - table.string('hair'); - - table.boolean('has_tattoos'); - table.boolean('has_piercings'); - table.string('piercings'); - table.string('tattoos'); - - table.integer('batch_id', 12); - - table.datetime('updated_at') - .defaultTo(knex.fn.now()); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('actors_profiles', (table) => { - table.increments('id', 12); - - table.integer('actor_id', 12) - .references('id') - .inTable('actors'); - - table.integer('network_id', 12) - .references('id') - .inTable('networks'); - - table.date('birthdate'); - table.string('gender', 18); - table.text('description'); - - table.string('birth_city'); - table.string('birth_state'); - table.string('birth_country_alpha2', 2) - .references('alpha2') - .inTable('countries'); - - table.string('residence_city'); - table.string('residence_state'); - table.string('residence_country_alpha2', 2) - .references('alpha2') - .inTable('countries'); - - table.string('ethnicity'); - - table.string('bust', 10); - table.integer('waist', 3); - table.integer('hip', 3); - table.boolean('natural_boobs'); - - table.integer('height', 3); - table.integer('weight', 3); - table.string('eyes'); - table.string('hair'); - - table.boolean('has_tattoos'); - table.boolean('has_piercings'); - table.string('piercings'); - table.string('tattoos'); - - table.datetime('scraped_at'); - table.boolean('scrape_success'); - - table.datetime('updated_at') - .defaultTo(knex.fn.now()); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('body', (table) => { - table.string('slug', 20) - .primary(); - - table.string('name'); - })) - .then(() => knex('body').insert([ - // head - { slug: 'head', name: 'head' }, - { slug: 'face', name: 'face' }, - { slug: 'scalp', name: 'scalp' }, - { slug: 'forehead', name: 'forehead' }, - { slug: 'temple', name: 'temple' }, - { slug: 'cheek', name: 'cheek' }, - { slug: 'jaw', name: 'jaw' }, - { slug: 'chin', name: 'chin' }, - { slug: 'neck', name: 'neck' }, - { slug: 'throat', name: 'throat' }, - // eyes - { slug: 'eyelid', name: 'eyelid' }, - { slug: 'eyeball', name: 'eyeball' }, - { slug: 'eyebrow', name: 'eyebrow' }, - // mouth - { slug: 'tongue', name: 'tongue' }, - { slug: 'lip', name: 'lip' }, - { slug: 'upper-lip', name: 'upper lip' }, - { slug: 'lower-lip', name: 'lower lip' }, - { slug: 'inner-lip', name: 'inner lip' }, - { slug: 'inner-lower-lip', name: 'inner lower lip' }, - { slug: 'inner-upper-lip', name: 'inner upper lip' }, - { slug: 'philtrum', name: 'philtrum' }, - { slug: 'above-lip', name: 'above lip' }, - { slug: 'below-lip', name: 'below lip' }, - // nose - { slug: 'nose', name: 'nose' }, - { slug: 'third-eye', name: 'third eye' }, - { slug: 'bridge', name: 'bridge' }, - { slug: 'nostril', name: 'nostril' }, - { slug: 'septum', name: 'septum' }, - { slug: 'septril', name: 'septril' }, - // ear - { slug: 'ear', name: 'ear' }, - { slug: 'earlobe', name: 'earlobe' }, - { slug: 'helix', name: 'helix' }, - { slug: 'tragus', name: 'tragus' }, - { slug: 'conch', name: 'conch' }, - { slug: 'rook', name: 'rook' }, - { slug: 'behind-ear', name: 'behind ear' }, - // arms - { slug: 'arm', name: 'arm' }, - { slug: 'upper-arm', name: 'upper arm' }, - { slug: 'forearm', name: 'forearm' }, - { slug: 'elbow', name: 'elbow' }, - { slug: 'inner-elbow', name: 'inner elbow' }, - { slug: 'outer-elbow', name: 'outer elbow' }, - // hands - { slug: 'hand', name: 'hand' }, - { slug: 'fingers', name: 'fingers' }, - { slug: 'knuckles', name: 'knuckles' }, - { slug: 'thumb', name: 'thumb' }, - { slug: 'index-finger', name: 'index finger' }, - { slug: 'middle-finger', name: 'middle finger' }, - { slug: 'ring-finger', name: 'ring finger' }, - { slug: 'pinky', name: 'pinky' }, - { slug: 'back-of-hand', name: 'back of hand' }, - { slug: 'inner-wrist', name: 'inner wrist' }, - { slug: 'outer-wrist', name: 'outer wrist' }, - // torso - { slug: 'shoulder', name: 'shoulder' }, - { slug: 'collarbone', name: 'collarbone' }, - { slug: 'chest', name: 'chest' }, - { slug: 'rib-cage', name: 'rib cage' }, - { slug: 'breastbone', name: 'breastbone' }, - { slug: 'underboob', name: 'underboob' }, - { slug: 'sideboob', name: 'sideboob' }, - { slug: 'boob', name: 'boob' }, - { slug: 'nipple', name: 'nipple' }, - { slug: 'abdomen', name: 'abdomen' }, - { slug: 'navel', name: 'navel' }, - { slug: 'pelvis', name: 'pelvis' }, - // back - { slug: 'back', name: 'back' }, - { slug: 'upper-back', name: 'upper back' }, - { slug: 'middle-back', name: 'lower back' }, - { slug: 'lower-back', name: 'lower back' }, - { slug: 'spine', name: 'spine' }, - // bottom - { slug: 'butt', name: 'butt' }, - { slug: 'hip', name: 'hip' }, - { slug: 'anus', name: 'anus' }, - // genitals - { slug: 'pubic-mound', name: 'pubic mound' }, - { slug: 'vagina', name: 'vagina' }, - { slug: 'outer-labia', name: 'outer labia' }, - { slug: 'inner-labia', name: 'inner labia' }, - { slug: 'clitoris', name: 'clitoris' }, - { slug: 'penis', name: 'penis' }, - { slug: 'glans', name: 'glans' }, - { slug: 'foreskin', name: 'foreskin' }, - { slug: 'shaft', name: 'shaft' }, - { slug: 'scrotum', name: 'scrotum' }, - // legs - { slug: 'leg', name: 'leg' }, - { slug: 'groin', name: 'groin' }, - { slug: 'upper-leg', name: 'upper leg' }, - { slug: 'thigh', name: 'thigh' }, - { slug: 'lower-leg', name: 'lower leg' }, - { slug: 'shin', name: 'shin' }, - { slug: 'calf', name: 'calf' }, - { slug: 'knee', name: 'knee' }, - { slug: 'inner-knee', name: 'inner knee' }, - // feet - { slug: 'inner-ankle', name: 'inner ankle' }, - { slug: 'outer-ankle', name: 'outer ankle' }, - { slug: 'foot', name: 'foot' }, - { slug: 'toes', name: 'toes' }, - { slug: 'big-toe', name: 'big toe' }, - { slug: 'index-toe', name: 'index toe' }, - { slug: 'middle-toe', name: 'middle toe' }, - { slug: 'fourth-toe', name: 'fourth toe' }, - { slug: 'little-toe', name: 'little toe' }, - ])) - .then(() => knex.schema.createTable('actors_tattoos', (table) => { - table.increments('id'); - - table.integer('actor_id', 12) - .notNullable() - .references('id') - .inTable('actors'); - - table.string('body_slug', 20) - .references('slug') - .inTable('body'); - - table.enum('side', ['left', 'right', 'center', 'both']); - - table.string('description'); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('actors_piercings', (table) => { - table.increments('id'); - - table.integer('actor_id', 12) - .notNullable() - .references('id') - .inTable('actors'); - - table.string('body_slug', 20) - .references('slug') - .inTable('body'); - - table.enum('side', ['left', 'right', 'center', 'both']); - - table.string('description'); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('actors_avatars', (table) => { - table.integer('profile_id', 12) - .notNullable() - .references('id') - .inTable('actors_profiles'); - - table.string('media_id', 21) - .notNullable() - .references('id') - .inTable('media'); - - table.unique('profile_id'); - })) - .then(() => knex.schema.createTable('actors_photos', (table) => { - table.integer('actor_id', 12) - .notNullable() - .references('id') - .inTable('actors'); - - table.string('media_id', 21) - .notNullable() - .references('id') - .inTable('media'); - - table.unique(['actor_id', 'media_id']); - })) - .then(() => knex.schema.createTable('actors_social', (table) => { - table.increments('id', 16); - - table.string('url'); - table.string('platform'); - - table.integer('actor_id', 12) - .notNullable() - .references('id') - .inTable('actors'); - - table.unique(['url', 'actor_id']); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('directors', (table) => { - table.increments('id', 12); - - table.string('name'); - table.integer('alias_for', 12) - .references('id') - .inTable('directors'); - - table.string('slug', 32) - .unique(); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('batches', (table) => { - table.increments('id', 12); - table.text('comment'); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('releases', (table) => { - table.increments('id', 16); - - table.integer('site_id', 12) - .notNullable() - .references('id') - .inTable('sites'); - - table.integer('studio_id', 12) - .references('id') - .inTable('studios'); - - table.string('type', 10) - .defaultTo('scene'); - - table.string('shoot_id'); - table.string('entry_id'); - table.unique(['site_id', 'entry_id', 'type']); - - table.string('url', 1000); - table.string('title'); - table.string('slug'); - table.date('date'); - table.text('description'); - - table.integer('duration') - .unsigned(); - - table.boolean('deep'); - table.string('deep_url', 1000); - - table.integer('created_batch_id', 12) - .references('id') - .inTable('batches'); - - table.integer('updated_batch_id', 12) - .references('id') - .inTable('batches'); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('releases_actors', (table) => { - table.integer('release_id', 16) - .notNullable() - .references('id') - .inTable('releases'); - - table.integer('actor_id', 12) - .notNullable() - .references('id') - .inTable('actors'); - - table.unique(['release_id', 'actor_id']); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('releases_movies', (table) => { - table.integer('movie_id', 16) - .notNullable() - .references('id') - .inTable('releases'); - - table.integer('scene_id', 16) - .notNullable() - .references('id') - .inTable('releases'); - - table.unique(['movie_id', 'scene_id']); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('releases_directors', (table) => { - table.integer('release_id', 16) - .notNullable() - .references('id') - .inTable('releases'); - - table.integer('director_id', 8) - .notNullable() - .references('id') - .inTable('directors'); - - table.unique(['release_id', 'director_id']); - })) - .then(() => knex.schema.createTable('releases_posters', (table) => { - table.integer('release_id', 16) - .notNullable() - .references('id') - .inTable('releases'); - - table.string('media_id', 21) - .notNullable() - .references('id') - .inTable('media'); - - table.unique('release_id'); - })) - .then(() => knex.schema.createTable('releases_covers', (table) => { - table.integer('release_id', 16) - .notNullable() - .references('id') - .inTable('releases'); - - table.string('media_id', 21) - .notNullable() - .references('id') - .inTable('media'); - - table.unique(['release_id', 'media_id']); - })) - .then(() => knex.schema.createTable('releases_trailers', (table) => { - table.integer('release_id', 16) - .notNullable() - .references('id') - .inTable('releases'); - - table.string('media_id', 21) - .notNullable() - .references('id') - .inTable('media'); - - table.unique('release_id'); - })) - .then(() => knex.schema.createTable('releases_teasers', (table) => { - table.integer('release_id', 16) - .notNullable() - .references('id') - .inTable('releases'); - - table.string('media_id', 21) - .notNullable() - .references('id') - .inTable('media'); - - table.unique('release_id'); - })) - .then(() => knex.schema.createTable('releases_photos', (table) => { - table.integer('release_id', 16) - .notNullable() - .references('id') - .inTable('releases'); - - table.string('media_id', 21) - .notNullable() - .references('id') - .inTable('media'); - - table.unique(['release_id', 'media_id']); - })) - .then(() => knex.schema.createTable('releases_tags', (table) => { - table.integer('tag_id', 12) - .notNullable() - .references('id') - .inTable('tags'); - - table.integer('release_id', 16) - .notNullable() - .references('id') - .inTable('releases'); - - table.unique(['tag_id', 'release_id']); - })) - .then(() => knex.schema.createTable('releases_search', (table) => { - table.integer('release_id', 16) - .references('id') - .inTable('releases'); - })) - .then(() => knex.raw(` + .then(() => knex.schema.alterTable('media', (table) => { + table.string('sfw_media_id', 21) + .references('id') + .inTable('media') + .defaultTo(knex.raw('get_random_sfw_media_id()')); + })) + .then(() => knex.schema.createTable('tags_groups', (table) => { + table.increments('id', 12); + + table.string('name', 32); + table.text('description'); + + table.string('slug', 32) + .unique(); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('tags', (table) => { + table.increments('id', 12); + table.string('name'); + + table.text('description'); + + table.integer('priority', 2) + .defaultTo(0); + + table.boolean('secondary') + .defaultTo(false); + + table.integer('group_id', 12) + .references('id') + .inTable('tags_groups'); + + table.integer('alias_for', 12) + .references('id') + .inTable('tags'); + + table.string('slug', 32) + .unique(); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('tags_posters', (table) => { + table.integer('tag_id', 12) + .notNullable() + .references('id') + .inTable('tags'); + + table.string('media_id', 21) + .notNullable() + .references('id') + .inTable('media'); + + table.unique('tag_id'); + })) + .then(() => knex.schema.createTable('tags_photos', (table) => { + table.integer('tag_id', 12) + .notNullable() + .references('id') + .inTable('tags'); + + table.string('media_id', 21) + .notNullable() + .references('id') + .inTable('media'); + + table.unique(['tag_id', 'media_id']); + })) + .then(() => knex.schema.createTable('networks', (table) => { + table.increments('id', 12); + + table.string('name'); + table.string('url'); + table.text('description'); + table.json('parameters'); + + table.integer('parent_id', 12) + .references('id') + .inTable('networks'); + + table.string('slug', 32) + .unique(); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('networks_social', (table) => { + table.increments('id', 16); + + table.string('url'); + table.string('platform'); + + table.integer('network_id', 12) + .notNullable() + .references('id') + .inTable('networks'); + + table.unique(['url', 'network_id']); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('sites', (table) => { + table.increments('id', 12); + + table.integer('network_id', 12) + .notNullable() + .references('id') + .inTable('networks'); + + table.string('name'); + table.string('slug', 32) + .unique(); + + table.string('alias'); + + table.string('url'); + table.text('description'); + table.json('parameters'); + + table.integer('priority', 3) + .defaultTo(0); + + table.boolean('show') + .defaultTo(true); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('sites_tags', (table) => { + table.integer('tag_id', 12) + .notNullable() + .references('id') + .inTable('tags'); + + table.integer('site_id', 12) + .notNullable() + .references('id') + .inTable('sites'); + + table.boolean('inherit') + .defaultTo(false); + + table.unique(['tag_id', 'site_id']); + })) + .then(() => knex.schema.createTable('sites_social', (table) => { + table.increments('id', 16); + + table.string('url'); + table.string('platform'); + + table.integer('site_id', 12) + .notNullable() + .references('id') + .inTable('sites'); + + table.unique(['url', 'site_id']); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('studios', (table) => { + table.increments('id', 12); + + table.integer('network_id', 12) + .notNullable() + .references('id') + .inTable('networks'); + + table.string('name'); + table.string('url'); + table.text('description'); + + table.string('slug', 32) + .unique(); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('batches', (table) => { + table.increments('id', 12); + table.text('comment'); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('actors', (table) => { + table.increments('id', 12); + + table.string('name') + .notNullable(); + + table.string('slug', 32); + + table.integer('network_id', 12) + .references('id') + .inTable('networks'); + + table.unique(['slug', 'network_id']); + + table.integer('alias_for', 12) + .references('id') + .inTable('actors'); + + table.date('birthdate'); + table.string('gender', 18); + table.text('description'); + + table.string('birth_city'); + table.string('birth_state'); + table.string('birth_country_alpha2', 2) + .references('alpha2') + .inTable('countries'); + + table.string('residence_city'); + table.string('residence_state'); + table.string('residence_country_alpha2', 2) + .references('alpha2') + .inTable('countries'); + + table.string('ethnicity'); + + table.string('bust', 10); + table.integer('waist', 3); + table.integer('hip', 3); + table.boolean('natural_boobs'); + + table.integer('height', 3); + table.integer('weight', 3); + table.string('eyes'); + table.string('hair'); + + table.boolean('has_tattoos'); + table.boolean('has_piercings'); + table.string('piercings'); + table.string('tattoos'); + + table.integer('batch_id', 12) + .references('id') + .inTable('batches'); + + table.datetime('updated_at') + .defaultTo(knex.fn.now()); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('actors_profiles', (table) => { + table.increments('id', 12); + + table.integer('actor_id', 12) + .references('id') + .inTable('actors'); + + table.integer('network_id', 12) + .references('id') + .inTable('networks'); + + table.integer('site_id', 12) + .references('id') + .inTable('sites'); + + table.unique(['actor_id', 'network_id']); + table.unique(['actor_id', 'site_id']); + + table.date('birthdate'); + table.string('gender', 18); + table.text('description'); + + table.string('birth_city'); + table.string('birth_state'); + table.string('birth_country_alpha2', 2) + .references('alpha2') + .inTable('countries'); + + table.string('residence_city'); + table.string('residence_state'); + table.string('residence_country_alpha2', 2) + .references('alpha2') + .inTable('countries'); + + table.string('ethnicity'); + + table.string('bust', 10); + table.integer('waist', 3); + table.integer('hip', 3); + table.boolean('natural_boobs'); + + table.integer('height', 3); + table.integer('weight', 3); + table.string('eyes'); + table.string('hair'); + + table.boolean('has_tattoos'); + table.boolean('has_piercings'); + table.string('piercings'); + table.string('tattoos'); + + table.datetime('scraped_at'); + table.boolean('scrape_success'); + + table.datetime('updated_at') + .defaultTo(knex.fn.now()); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('body', (table) => { + table.string('slug', 20) + .primary(); + + table.string('name'); + })) + .then(() => knex('body').insert([ + // head + { slug: 'head', name: 'head' }, + { slug: 'face', name: 'face' }, + { slug: 'scalp', name: 'scalp' }, + { slug: 'forehead', name: 'forehead' }, + { slug: 'temple', name: 'temple' }, + { slug: 'cheek', name: 'cheek' }, + { slug: 'jaw', name: 'jaw' }, + { slug: 'chin', name: 'chin' }, + { slug: 'neck', name: 'neck' }, + { slug: 'throat', name: 'throat' }, + // eyes + { slug: 'eyelid', name: 'eyelid' }, + { slug: 'eyeball', name: 'eyeball' }, + { slug: 'eyebrow', name: 'eyebrow' }, + // mouth + { slug: 'tongue', name: 'tongue' }, + { slug: 'lip', name: 'lip' }, + { slug: 'upper-lip', name: 'upper lip' }, + { slug: 'lower-lip', name: 'lower lip' }, + { slug: 'inner-lip', name: 'inner lip' }, + { slug: 'inner-lower-lip', name: 'inner lower lip' }, + { slug: 'inner-upper-lip', name: 'inner upper lip' }, + { slug: 'philtrum', name: 'philtrum' }, + { slug: 'above-lip', name: 'above lip' }, + { slug: 'below-lip', name: 'below lip' }, + // nose + { slug: 'nose', name: 'nose' }, + { slug: 'third-eye', name: 'third eye' }, + { slug: 'bridge', name: 'bridge' }, + { slug: 'nostril', name: 'nostril' }, + { slug: 'septum', name: 'septum' }, + { slug: 'septril', name: 'septril' }, + // ear + { slug: 'ear', name: 'ear' }, + { slug: 'earlobe', name: 'earlobe' }, + { slug: 'helix', name: 'helix' }, + { slug: 'tragus', name: 'tragus' }, + { slug: 'conch', name: 'conch' }, + { slug: 'rook', name: 'rook' }, + { slug: 'behind-ear', name: 'behind ear' }, + // arms + { slug: 'arm', name: 'arm' }, + { slug: 'upper-arm', name: 'upper arm' }, + { slug: 'forearm', name: 'forearm' }, + { slug: 'elbow', name: 'elbow' }, + { slug: 'inner-elbow', name: 'inner elbow' }, + { slug: 'outer-elbow', name: 'outer elbow' }, + // hands + { slug: 'hand', name: 'hand' }, + { slug: 'fingers', name: 'fingers' }, + { slug: 'knuckles', name: 'knuckles' }, + { slug: 'thumb', name: 'thumb' }, + { slug: 'index-finger', name: 'index finger' }, + { slug: 'middle-finger', name: 'middle finger' }, + { slug: 'ring-finger', name: 'ring finger' }, + { slug: 'pinky', name: 'pinky' }, + { slug: 'back-of-hand', name: 'back of hand' }, + { slug: 'inner-wrist', name: 'inner wrist' }, + { slug: 'outer-wrist', name: 'outer wrist' }, + // torso + { slug: 'shoulder', name: 'shoulder' }, + { slug: 'collarbone', name: 'collarbone' }, + { slug: 'chest', name: 'chest' }, + { slug: 'rib-cage', name: 'rib cage' }, + { slug: 'breastbone', name: 'breastbone' }, + { slug: 'underboob', name: 'underboob' }, + { slug: 'sideboob', name: 'sideboob' }, + { slug: 'boob', name: 'boob' }, + { slug: 'nipple', name: 'nipple' }, + { slug: 'abdomen', name: 'abdomen' }, + { slug: 'navel', name: 'navel' }, + { slug: 'pelvis', name: 'pelvis' }, + // back + { slug: 'back', name: 'back' }, + { slug: 'upper-back', name: 'upper back' }, + { slug: 'middle-back', name: 'lower back' }, + { slug: 'lower-back', name: 'lower back' }, + { slug: 'spine', name: 'spine' }, + // bottom + { slug: 'butt', name: 'butt' }, + { slug: 'hip', name: 'hip' }, + { slug: 'anus', name: 'anus' }, + // genitals + { slug: 'pubic-mound', name: 'pubic mound' }, + { slug: 'vagina', name: 'vagina' }, + { slug: 'outer-labia', name: 'outer labia' }, + { slug: 'inner-labia', name: 'inner labia' }, + { slug: 'clitoris', name: 'clitoris' }, + { slug: 'penis', name: 'penis' }, + { slug: 'glans', name: 'glans' }, + { slug: 'foreskin', name: 'foreskin' }, + { slug: 'shaft', name: 'shaft' }, + { slug: 'scrotum', name: 'scrotum' }, + // legs + { slug: 'leg', name: 'leg' }, + { slug: 'groin', name: 'groin' }, + { slug: 'upper-leg', name: 'upper leg' }, + { slug: 'thigh', name: 'thigh' }, + { slug: 'lower-leg', name: 'lower leg' }, + { slug: 'shin', name: 'shin' }, + { slug: 'calf', name: 'calf' }, + { slug: 'knee', name: 'knee' }, + { slug: 'inner-knee', name: 'inner knee' }, + // feet + { slug: 'inner-ankle', name: 'inner ankle' }, + { slug: 'outer-ankle', name: 'outer ankle' }, + { slug: 'foot', name: 'foot' }, + { slug: 'toes', name: 'toes' }, + { slug: 'big-toe', name: 'big toe' }, + { slug: 'index-toe', name: 'index toe' }, + { slug: 'middle-toe', name: 'middle toe' }, + { slug: 'fourth-toe', name: 'fourth toe' }, + { slug: 'little-toe', name: 'little toe' }, + ])) + .then(() => knex.schema.createTable('actors_tattoos', (table) => { + table.increments('id'); + + table.integer('actor_id', 12) + .notNullable() + .references('id') + .inTable('actors'); + + table.string('body_slug', 20) + .references('slug') + .inTable('body'); + + table.enum('side', ['left', 'right', 'center', 'both']); + + table.string('description'); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('actors_piercings', (table) => { + table.increments('id'); + + table.integer('actor_id', 12) + .notNullable() + .references('id') + .inTable('actors'); + + table.string('body_slug', 20) + .references('slug') + .inTable('body'); + + table.enum('side', ['left', 'right', 'center', 'both']); + + table.string('description'); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('actors_avatars', (table) => { + table.integer('profile_id', 12) + .notNullable() + .references('id') + .inTable('actors_profiles'); + + table.string('media_id', 21) + .notNullable() + .references('id') + .inTable('media'); + + table.unique('profile_id'); + })) + .then(() => knex.schema.createTable('actors_photos', (table) => { + table.integer('actor_id', 12) + .notNullable() + .references('id') + .inTable('actors'); + + table.string('media_id', 21) + .notNullable() + .references('id') + .inTable('media'); + + table.unique(['actor_id', 'media_id']); + })) + .then(() => knex.schema.createTable('actors_social', (table) => { + table.increments('id', 16); + + table.string('url'); + table.string('platform'); + + table.integer('actor_id', 12) + .notNullable() + .references('id') + .inTable('actors'); + + table.unique(['url', 'actor_id']); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('directors', (table) => { + table.increments('id', 12); + + table.string('name'); + table.integer('alias_for', 12) + .references('id') + .inTable('directors'); + + table.string('slug', 32) + .unique(); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('releases', (table) => { + table.increments('id', 16); + + table.integer('site_id', 12) + .references('id') + .inTable('sites'); + + table.integer('network_id', 12) + .references('id') + .inTable('networks'); + + table.integer('studio_id', 12) + .references('id') + .inTable('studios'); + + table.string('type', 10) + .defaultTo('scene'); + + table.string('shoot_id'); + table.string('entry_id'); + table.unique(['site_id', 'network_id', 'entry_id', 'type']); + + table.string('url', 1000); + table.string('title'); + table.string('slug'); + table.date('date'); + table.text('description'); + + table.integer('duration') + .unsigned(); + + table.boolean('deep'); + table.string('deep_url', 1000); + + table.integer('created_batch_id', 12) + .references('id') + .inTable('batches'); + + table.integer('updated_batch_id', 12) + .references('id') + .inTable('batches'); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('releases_actors', (table) => { + table.integer('release_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.integer('actor_id', 12) + .notNullable() + .references('id') + .inTable('actors'); + + table.unique(['release_id', 'actor_id']); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('releases_movies', (table) => { + table.integer('movie_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.integer('scene_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.unique(['movie_id', 'scene_id']); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('releases_directors', (table) => { + table.integer('release_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.integer('director_id', 8) + .notNullable() + .references('id') + .inTable('directors'); + + table.unique(['release_id', 'director_id']); + })) + .then(() => knex.schema.createTable('releases_posters', (table) => { + table.integer('release_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.string('media_id', 21) + .notNullable() + .references('id') + .inTable('media'); + + table.unique('release_id'); + })) + .then(() => knex.schema.createTable('releases_covers', (table) => { + table.integer('release_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.string('media_id', 21) + .notNullable() + .references('id') + .inTable('media'); + + table.unique(['release_id', 'media_id']); + })) + .then(() => knex.schema.createTable('releases_trailers', (table) => { + table.integer('release_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.string('media_id', 21) + .notNullable() + .references('id') + .inTable('media'); + + table.unique('release_id'); + })) + .then(() => knex.schema.createTable('releases_teasers', (table) => { + table.integer('release_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.string('media_id', 21) + .notNullable() + .references('id') + .inTable('media'); + + table.unique('release_id'); + })) + .then(() => knex.schema.createTable('releases_photos', (table) => { + table.integer('release_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.string('media_id', 21) + .notNullable() + .references('id') + .inTable('media'); + + table.unique(['release_id', 'media_id']); + })) + .then(() => knex.schema.createTable('releases_tags', (table) => { + table.integer('tag_id', 12) + .notNullable() + .references('id') + .inTable('tags'); + + table.integer('release_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.unique(['tag_id', 'release_id']); + })) + .then(() => knex.schema.createTable('releases_search', (table) => { + table.integer('release_id', 16) + .references('id') + .inTable('releases'); + })) + .then(() => knex.raw(` + ALTER TABLE releases + ADD CONSTRAINT ensure_site_or_network CHECK (site_id IS NOT NULL OR network_id IS NOT NULL); + ALTER TABLE releases_search ADD COLUMN document tsvector; diff --git a/public/img/logos/gamma/favicon.png b/public/img/logos/gamma/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..b3dc4c6efc8ac5aea39f8f674542ffbad8f1c7d7 GIT binary patch literal 2397 zcmV-j38MCiP)EX>4Tx04R}tkv&MmP!xqvQ%gmv4t5Z6$j~}j5G~@URVYG*P%E_RVDi#GXws0h zxHt-~1qXi?s}3&Cx;nTDg5VE`lcSTOi%a0@IUxHTdOcN=_Z8|K;Xr;KcYZz7icwX`}^3oTPJ}38Mx9r{%RAL`6Rv3 z(IQ8{;5Kk^-O-di;Bp5TellcJb|gP7p-=$c&*+h($24YJ`L;(8$9sn0p%+Zbj000SaNLh0L z01FcU01FcV0GgZ_00007bV*G`2jm3~0XGk5MBL>7000?uMObu0Z*6U5Zgc=ca%Ew3 zWn>_CX>@2HM@dakSAh-}000LxNkl%SNH?eQ(F0 zLWYYLmc%&}z)gltQUn?@tffe8MwKV)$j7bm!7GeZ=Ziop$y2IEG7 zLEn2@ItCP&E0neOZQuKz$3O1d($d=kp5)}-obx`<_j{h7=g6DiNpYo-N;=d|t7cO5 zH~`L{@8ZIR^H|k^Z<^+L1U)hnkqsb=QT+)~-+I5bZB*UvXq#ixE;FW8GqI`yRZC~* zc`kHyp@V<{=GIjcbaOMt>;k3$Ao3iJy9apZnhA^_mt@AYN~$U=D27GOoHgCG{|QV*^HP6u8BZpMS>YMlc=09mO`PI(;esN?*4dG6M7AGf^mKg zj74>RI?>f0^o}`%lX@0d;59Vh=kuczsJw0rwbQGZP?4s)(8sBhr|B*ft{59lcQp{S zx(|UYLiPTnDYiEs>A>?EyMZ1ci*ZJef!TGFnOHrZLZO!rPM)R^1eZ>iX-FgPw-9at z=|b&cRDWmHcI5MUg1niE$VSkeV*6e@?Dq$p0#XEVuXG5vs&X8sJI)R>b+(~i)VQyL z+y~kNq!8mEY&L3s?s?ui#B2sqsIG0v4~lj%07;-wA~W(ZCr+F!>t{9Ya*%t`Xel-t zN7PpX`fWfES%Ww}$Z}MFrBPpQh_DT(0^>n@&}ei5VX-*exA)Y6$UUGZamVfOb2$xzD)?=duzyY8VaDam2Ugl?UYD#-@7#uvW5zouwHPl~S8WxU5h|3hf5_S))A8*S6 z9P+&EcM-EI&i6G|-L#j!6cAaR;!rDaydVrQ*80Em?Xfh z+1d%0^xjY&&$>^cFJxgx#Ay$>-5Ku@D~ZeMTV$9}#wjALOp=<6}gBqUKkV@gGtn(EXb@_8UYbsJ!T+8EqN z?Fz!4JAJ=p035(IPGTNnUIX2Y@p z`q3cS2wPm}=W-vG?FEQz0t`0#n(rSQ4ld?4kU78+pa{&zxG#BLcCnXjd=4k^Z=B>s zkd?8-dC-?pdpGDlz`-~xo8~l(cp<3+T#U2Q%VgeAadL64A~wt-{fe~%s0Nt^vJ{b} zWpei=Y_v+FFvzvF<7K=ajI$49br3|Gfxh7uP@XK6PT7cYeGyR+Q31syj)8n7HpQY6 zID^|~7izx@1VD(|@B6u2kZWu2C-gVaJ-~EK;&bKOaTvai+Uck+M)eucpMh=#{SD}w zv7&cCSD?{N*y04#K0VNE#JmWkQQe}pIOGoQ=W?L>ix}#YWnFOmcq>2%?2YH8)bbiv zBispW@>`C*(!ZX`WS$kIC+-XBnB%O4qtjJ_%Uxtu416l-1pz&Vg*h|{w181ur%fcMokS>d#&1O+d5%e&&I1eO+IO`C3 z5_AvB65l^IEU%kx&k(ft8k{j%APbVCc;P+2twXQrahjr&fQf$qS%OLY3#b9E2i=0| zP_P+n@oB5g6ad?>M8kmF_8CscISQ#2<8yHv_7%UdQHl2Zi mLAAs+q9i4;B-JW6KP5A*lEKKp$N;DsQ^O^%JnhM>+;adTj2dnL delta 99 zcmZ1|zEFGv8xtoJr8yHv_80`5iD$T&a npjzS@QIe8al4_NkpOTqY$zWt)WT0z+MZ?);YnM-E<(>lo|GpaY diff --git a/public/img/logos/mindgeek/lazy/favicon.png b/public/img/logos/mindgeek/lazy/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..d0c145d9581e9ac88573f6902e82c5aa71756665 GIT binary patch literal 2521 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 diff --git a/public/img/logos/mindgeek/lazy/trueamateurs.png b/public/img/logos/mindgeek/lazy/trueamateurs.png index 25174584b884a22bf281eae6159cdaf58f8cdae3..a46cc1620e71b34019f351cda0fad590aafec457 100644 GIT binary patch delta 95 zcmeyZ_FHX2HM^LYr1a%9pN&loLLR2NhQ=XAMplLvRtBcp1_o9J26MGTdl(oPR7+eV iN>UO_Qmu0HQ!>*k8H@~!40H{!Xn0fh)?l)iupt0dPaA~* delta 95 zcmeyZ_FHX2HM@wViT{*qRvVidggi`i4NO9e46RHptPBmb4GgRd3>J%Do6f+%pjzS@ iQIe8al4_NkpOTqY$zWt)WB^o+so|1Wp7vxfVM73#02{;r diff --git a/public/img/logos/mindgeek/lazy/tube8vip.png b/public/img/logos/mindgeek/lazy/tube8vip.png index 19e41bb47610b5d3593a1823eaa8184a73c24b67..785716c32d7eae676195ead2cc79f4d9d3f6f9ff 100644 GIT binary patch delta 77 zcmbQBJVAK_2NS!Pn56XOG@s4p^0YTI3iLAq05tRz#{d8T diff --git a/public/img/logos/mindgeek/misc/porn-hub-gay.png b/public/img/logos/mindgeek/misc/porn-hub-gay.png new file mode 100644 index 0000000000000000000000000000000000000000..bb7dfc3e14f053a84a00e8de73b4d9d30b1f1ef5 GIT binary patch literal 4110 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 literal 0 HcmV?d00001 diff --git a/public/img/logos/mindgeek/thumbs/favicon.png b/public/img/logos/mindgeek/thumbs/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..692f41d5a4a2acf468c4f9bd9b40b0574551879d GIT binary patch literal 9334 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 literal 0 HcmV?d00001 diff --git a/public/img/logos/mindgeek/thumbs/network.png b/public/img/logos/mindgeek/thumbs/network.png index 6a5cd428c7a9a14f82d6a8628f82e7e00a392853..0fca53542f86625ae929f4658d64986cba64dbf7 100644 GIT binary patch delta 97 zcmX?KdcJf62NS!Pn4l=fySmN%On1yZOmz*7LyU~93@xk-OtlRRtPBk1YKQhPFfgc= kxJHzuB$lLF<>sekrd2W+85kMp8eq}zrtGc3WMiv2099Zc=l}o! delta 97 zcmX?KdcJf62NS!9g{Em|_oB`GOn1yZOmq!QLW~TpOf9So4YUmmtPBhmi(i}0z`&qd k;u=wsl30>zm7AZEnO4bQWME_fRE??Ol2@MgWMiv20G^Q?G5`Po diff --git a/public/img/logos/mindgeek/thumbs/pornhub.png b/public/img/logos/mindgeek/thumbs/pornhub.png new file mode 100644 index 0000000000000000000000000000000000000000..eb275b6aba894ef8e822f4943dc199870a06ecf7 GIT binary patch literal 8081 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 literal 0 HcmV?d00001 diff --git a/public/img/logos/mindgeek/thumbs/transangels.png b/public/img/logos/mindgeek/thumbs/transangels.png index 6d36404b7fdcdb6d3ffb8419a93463df22e1b8f9..a45a3100ca4385575290f54dae9a53614176ddbb 100644 GIT binary patch delta 78 zcmZ2Gn{n-I#tj@y>|$bqq8#t)HuE#>@)9@IH8c(}GO{wXure^!HZZU>?JLrk&l3HuE#>@)9@EH82S=GPE+aurf5zHZZUpZTjQ~ RK2q4^E_vl?PZso@0|1Z27P0^U diff --git a/public/img/logos/mindgeek/thumbs/trueamateurs.png b/public/img/logos/mindgeek/thumbs/trueamateurs.png index 3acf2280fa16ca81b2a1c73657becd8bdc09140e..177390aa26c90e628be6f48d82d5e67f9ce231ce 100644 GIT binary patch delta 76 zcmbPulX2or#tGHzVq$`#9PjEjHciVGH`O&X4ly#aGPJNVFx56Nure^1s~y@iSwBY# Oo7|hSw+53Fatr~55*FJ4 delta 76 zcmbPulX2or#tGHzA{Lsao!yHzHciVGH_p^0X%>)! lag8WRNi0dV%FR#7OsixtGB7d#s>alC$tzEL^EBgrMgU>M8Q{_-RLW~TpOf9So4YUmmtPBhmi(i{QnNeN} Oo7^R@JnhLw@`eCu5EU)} delta 76 zcmaEC_tjVMV;EJ?M>%}>cpt7I@TFfstD#?)}hD^Gj# I6p?;L0DYJuvj6}9 delta 121 zcmZ3ivsh=tB^EhuAqEC+A;wF0>;5n>FtC?+`ns||Vd4}MQSmv?JY(|*mKc5yLtP`w z5FjVMV;EJ?M>%}>cpt7I@TFf!0Jz@lN{Sx4s0 IQ$+e10czSJ^#A|> diff --git a/public/img/tags/bukkake/0.jpeg b/public/img/tags/bukkake/0.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..f1da17ca3fb2bc22ed8fbd659c499fb23dae8320 GIT binary patch literal 700682 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

_Lr`wNh{R$po3=Ep6Y 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

t_3*V&rtvp5YrT`D{mh=G%y` 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�FAvtp5PPzs=D! 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Ï8NyNRcX{7>k-&qS4TB#EgMwkRg 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
  • s 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

    GN%VZ=z0eaPA%@4@mI~lX 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 rCZ1y71~$d$oAGW8*l7LSd|(Lz ze4BfNKJ*Twm!Gxdq$~#$DE)`q75Ge^77X~EVrha{B2w;3)sq=<4ivJoSuqHN$a)6^ zl)|E9Yr`G(Oy%)OA{;IZNIXfCWywE1nQgk$5-tz{z(PQFHf<19gB62IK1(i6IH{;{0Z@ z_J$P8thJY!nfIez;k+kdSc`3$3`U%|H-?q^?2f3M@cM9Jh>wSF(6PNkuebs^UOyCX+nJ#u^38 z1|5iwDdun#CQ5d@3)V}SkkW@sL`@$#0wzZCR763tu|r8D!F|jSP8L9)qlmqbY0hfX zR7SBg^k6X9lUDLmaLxkLTN(udJbjq9VAp&^sCxq<+Y*pqapw}!Z^!GbM5ASm3-x|; zW&qs4>c!hxev)F=?bCT8P`*=8wQ7dLBYXzyf7`{$v}P3}hZ=*DMHTUk+W<_b+W!E| zX1d%b^kfJABLqO1XY5u=b+CQhokc-Uo&EKjs!zTU4cdMu9QnRuHY8!r6ED?*WLL-Z z=I+eqJP?OOD7qumPVr5egrS1b{d-?Z`QXW|LeM-loD7Z~`z% zqO*zFAelDVJT63nP`w(@Llqe|EMuj2!E{U(vw76-Iv_G}lHkB3;EzsGTpLYNVQ7G& zl-99gK!R5RJ=@PlLFy!&AWU|;URg24+nJTqkrQCXuxTvLjg+}dqZZ)MHO6tSYHW?& z1yD%Aa29tVSPcLiB;BECWQUAFDMdw|DICq6EFMBcA`(53LEiFL21`j|M82Km;2ntw zIhx)2?<$TYYnyLE@TkDIWqk`w5K5ApNZcbw-!5n=g{nlc^PR)UAJz}fM0?Z5=kJ5U z35s#t{J9-MXO5f}P}5PUtde7t^VNQ_#6yk=2>F3xzA(0+5n_sdnY_u;H?Q{(aNCr@ zH$SW^e^xPQ+{EUOU-tpR0yTT60MA6cc=6O;z|jYBF%q+C|pZ?aEa zV8FK2WNBfx?;6myzaIQyuFy6JZnz=L-bmsJ8ilv>is;Wc@-*H-LoN9av5O4S#@JCS z%I}N8iKcVEPxm}^fg>E5b1?b9B)J&^?C%x9zVHj9-u~D{A7)Mqp5gvs9oI7C4Kwx4 z66PW@pE(1U_`#&>B4_oC3CRbF$U9GrNwp7O_dJ2${LXATXZ~PUSZ!%LpZT9MxN(2l z#Ob#<5*m+9pXO>L+$Y9Am%?AFVTk$93wPPn^Yk!UTI^`?z4L~01k5X)8ucHXU=qku zGD^INdC3MfAy%YUI`N#6$Yk1L-lEQMivfmADEJg}6E_)s5+{>?DZ}xKwjATFFWVH^ zjh4NVR(pbX87OpFkY#Ye!aC-0^zuL-TLb09WNldZ6xX~(gi8pbF(qT^lXx}=Bpobd z8@R$zp*)2)w@s&}UZ<*vC3ylj40W1FrsZ28D{Eb#CD05Vnq%bAa)XHWqV8vEoRI<@8X ziAo_Wv*u&x+A9Hx`4enZ+A%HEN-0ik`+43)MIKy8`le>Ai~#D4pP>{mRbw%p zawS@#w-Kw1RFa7t0AIXwxuNrYT9;7|c& z#WO))g25RJN%0-%c@M72*g=mm@ZO;5$h^C!>k?k!(8; zy}z_!GJS}O^?#h7o1)~`5&OjCs1v~a?+D%j^K#+&7f~CZcxdkv%x~k)2!W@R3*pM& z+aOt2QEC0;00O5%i?4YasHcP6{{YO3X!Dz&sUN2wN@s`r>4)hYeSf3lI*nmEmV^{# zwMKbhCZaz0>|_LEh)7~Og_1C&oGcW+zwSla3HayrkpkQ7ruBI^sL1v4KfEeH#6_Q; zUNVEfdQZkfvJQ~?%R1{8JklK<{{Xo>R>>?r-X|rnr-*nt{{WcSeu~Eym(<3}=yM>4 zsA^?6LKb^X5&FkNW1N*iwCn!k6+_O~;=YrIb$?>ps_ri-JaL>x(d#2-e&6>gcH3IU zI4cwe2uuP_xyj76m!c7?nfqjeC#e18XrD9x05B#TjH_f!<=<3C{mm7T=zP!PIJK7k z{{XnwCts|kN8ct7AAFA=t_U>;Uq9Son;>vQKT(Cgu?X@{-(THf!cfVMER5QJpXLif zi6aqt98LX9wuAPx=^bP}{{Uuk-AzI=6v0D`H-0*dR^J8dU0^_0rJ2vpn1)qw1Oy*v($q^?;f-+z)po%gA=R)>;i!0#qBd^ zKHhX@KDjO=nn^AjE58`!M>4rN76p+R;{}ct%|RJO2!s}~m)K}Pyf<$;3yD7`D1?(6 z@8=~DjC;u3*R^)WCK2Xx9Hwd$<%&4?P8wYu$DL(C*ka8ZPFVi{@K3bN=l<9H$F->Q zmYt-_)>-Q%m$R=OliqhXh_I*`p9ULCQ^q8X`N0=u$kLhouPrtD#_|4g z%GaY)>A#~8j9`^i28i~W1{}f@VnU4nXh>nhnf7=c!mXj_$gaJ_{B>6xh7y}^pB2jQOoO|K2dT8KLDP6FX1T3I6Q8Glx&A3R6IJ3PXi{L^I zGnj^$<#t~vF?O_O86p-of#4fhx{zT)KnMoJfQ%$Ez)q2nG6an*7Pv4B#|0*s1rG^z z4RO=0Fw-UGJr~Xbh-H&%v#7%ptYJ>{(ZN9&H4Z^|G84)jU}b-_4!A-9nlJ_~2nmEZ z%0P%!j0=t^KDz0HK?KGwT^XdDOioJ7zZd|7RWm?x%K^#w3NI)Td72c&!Bfb9c`Dzw zGm-)vCWT6!bUA($2V7N?X9htApvXM;;Q7f5kOePMl*DSIyqE+S6C|ZkZjPIp-|BOV zesL8f+T!zCUA&XH{{XlQ6YA&V0)YTRaH6V-?%Tx;oBLKJz{H}p{{66Mlror@*(02< z0WCTp`#YbELnw$=dFTHCa!wA8e{7YN3;{EtnfzsN*D!1^a$5od?u;R8>ce0}C1YVGEKfBOYQ2vb6ni?WL~Y`K{yt zkklEt1I%Pv6BhfrdYs`bFpU$GqQmfEDK##)g<;2e6&R-WeKDXMRz_z3<{;d}u}+^vK0g~}0~n|><^ zjco+{97k9ZcFv#StZQJ@5G83$@8<%c1DI;Gd&5|QfCAt|x?7FpztW_VINF0MoJ+SL zxl|e;_`(4ulzN9t%uYliF0%E5zJA!P$th@UEWzQ8@YW({mK>G{*o-n8qLWe{N$zhr zlCh#t%!aZP?bsp9n&Q?*_Z~%`cU!zz< z^E-C;fI1}Da&#ahhWOoL5wjoj2=@yqps7^J}ZahNC@F7N^t^FM4+ho{>Mm1uDMhHn0tFWFfv zQt?trTw};Fyhea`ACImnSPtNr^OFLJx4%C**BXr}`T8=>vzsM^9Ns?Dn=kQ@9%nAU*GxuE617&mXBISSI+s|tKYpjHUT8v?VqVPI0`u_ki zZ~%yd^?|)8gp%uBWV?qYMYT)uIIzSZrW6JG9b^qqNAl+69Q|=w>2Um{hr{C{8351J z!tyYf=O0CPK5&H98Kwdd&P2Q>py*{eoFJ$if$-i;=$B%JHRbMbn<&>(8c22sr55JA zmfmW5h=|29Zp5V8ao|6hjsQxIy7xQ9TxCUJK1uq+_`l4F3>%CEg<`JxKUia}VkOGP zbE^vGY`QvYPAD%Nr%`Xr&Pb-3fj+bP#x30U{l-I3?B+FIGG{o!Hv9hom;oC)#*6^y z{_i%z(q6vV%1{H>`--HH-=Fiw1S}VM6KZ^anJ!}lcK3uDKTvx{M=@lqppQoWj4&4R zU}LlMtTZY^c%TKS>LmLm^BiNr3}m2$$vij_#sMVbPg|U*So4rT#ZSKvj1D?CcGvCi z2I?d!)2}(CZa%*syz|j~lx9=BZZdM}6<~y^lI@|vX$3(%zD{IDVWSe+uqsj@>~K~M zC_y3;h1{qHL5TM#z!3lh=+U-Fcd6g5xuO-2Syp3g$e^610;vQGDu)shWWfvsHboeP zz}PB{E?H&bUSz0tqu@kPwUl8)EJ`t)QZD$DQIlzM8V!UU4a#z$20UBffn+JmpVtgn z2#6I0xI7Uw#V)-*Bm^Ct*n^e17j#t+ot78n`=}D}!!_s#;3dk!c4VI%Lzywb&}@#Esyp_R7@=4pLJhl|9eSfP9OhN44LqLk`=5t?Q zr>Kuj&H-#})UR(p43-ih3L3Pv^kdl~Nqn8-&Nq>A0RqXv9b{C~MiFE1`r+Q`2=bff zTJeh5_v-%mwo&MB4~7rSelu^^QgZW<$33wyJSPd#a|DmEI2mE`)8c>J+=Y*U>iUde zQq4@nf^{Dm8tfNC`MJt}BLfpl5h7Ovb5Il*f^>QR0K+ZP{-!9QC~yw>`WlBFgCl)>c*=v5j0^o7 zWP8schQk1xizDX|lPki2FQ3j(1-^OBR$#;dsmKrn>lsaJ`akYy0~Qf)oIj)d&tJAg zuO-op0fXt5WlI!qT;K<9oP%FZF$Uvb<}0}MkZni87)x|Zr~YCvux@wbtVA8<5u?^{ zl*USE;qv#A5mM0)KYV9%oQT}vgtv}BY!XRs+wq1chz>;D$WXmTMO$Mn2vG~+5m_^F z2Ov5G4bt+tjDr6F5u>2SRMsgS?wS(GgFBZBgs6)fsJ~qLZi5+~%RFOJ72!2RtE3NU zifceeN50*}WCl||A3wZpF%#A&5_!f{J~4>s`r)GR5%cuaYUb%RB`hdP}y0GZSe-fUP22q%|*eljU?DnAZ>u3SP$ zPQN~}%4GvJ5KZm2Ly*o@Eg?+GK|xh87QHiNv$DZbu`g_vs<96trVs;%u$9DQEDIet zfYBS0j9tQWA>=k;gkvOVvC`04Nl2h9IfHxTA>fGhjhqHHFf?)~NYGpgj;*uYg86Gw zCJ0dMO=341)j*MNF@A;Qf?6b%o9=|o*Cy@Ca1tYgY=0B?#8m_%9gu;r!e(p$dL*SQ zl@h)(DPcqz2$Um(r{!Oex)eUUfw@hLAooYBb{Y-}B;z%i-`jSVQ8}+b^=c8ME_1QF zh~?hOOV7njlc_L()Ru#@;hGUSzrjIv;a>FFzVL*EX!96?m5m!83Xda+s6ihGk*+zx zTC^WgZAqxX+G4mOl&p7m_K> z@1I@7_{KL(2ZVhx5DL%ye>ohLwVgimUF4L~Gmu0Hm3O?Beo5TN7!(s|JkNhj)oGucfV3#6{7x+m zxvUG@oaPV0W6OZ2hp+oM6}HJg#zBk1+H!;}@%*`@tMY*YWghFPGAUst}U;zOU zjfSuQ$*79P6i{Kkf)qj=86tJ9lksc9rx1$o3TQ+ZI`0`nA!09itBqDMfo~!~C=c+Q zvluN8{n$qwOr*8T#JA!y3KY(Fd^~28#?W*g@>4QSF(cOUd2{ao)&eNJdul%Uz?q-6 zU7{Igb*jigq7Tj|@B3mOO-Gz>!ZI-Wr1*XCDhX#1V6Dw>0ti^1(dvFN(iIV}c;y%U z#w@)50KdH91fU)w^^8f?pX={}AuKuO$Wlq)SVk-@PLl&?(Vl14NFU?lAu$k*UhF=Bg3B)s{aSeQh-AU_AdJ35uBGo5@#sP*I74w6kmnZA0GX^TDka1M zsgBT4>TJ@4K-*cbN6un_kO0loAOb{5q{4vb~C{2WAG8;uHvn~f| z*-{udI&Cnb9drS`GL@E@$x@m%*myqpKDO8Zh2cNOu*O+|zm$nV-bb?13< zvWlXT=aK$kf!TzuaDAV_ie!_!XhYe3Frlk-qDsI7@DRek<>3I*y}ocEw02GXau9}K z8#z{Ni1xrC7N-Fk)M4PpLNu6;62$jWK5+pACFdYd;{$U(JRjCyw$lMH)Cw@un{O5L z%8Mi*cm>|)1u*(u?Zu@@GEFz%?;mWas>H?pGUYXZIZdZ{;9LYp86PA{SJ&&7D}NTh z+#I#(x}17JK#RV*kDQ94K)ysj4m{qR2&s?WYFz$R&OlB>==@+^xstIiM>FFQ0AA&f zp_YUinAz4O0Iw^_JVYNEuF}}w(_euh#IMlME3F{$CklDftwUpJJ~zpfgdzW(tgBBMMHtS5i`&;;Lp zKe@*0Ly}t)%Nz=P<7TqK#f0ksWgKF&Id^!HboyR18k1N8f%A=hGIWlwErt$f?HNJy z=M@Q}`O21sL4*CB{NgM#BvXKG3uB4P(E=_|)#bySl2MsbLMz`n%*8+qD#T$laN^;? zfZskFRAqRkU}0$j2=R-8Br(W6ojYXPm`aV zgu(`qS6-mk`+-n|Sb-tUtXEhEJ(F?c17-cNofPZ4w*Afooc{pxCj$zt@skqvIp#=@ zfu-{Enp|O8f_0WMpWNpQhezbVIT~mS#wX>I7)@*b{{Y+@)BtH-9EqM-ebmaXyxtt3 z1W>#}42ZCYE#tKVXf%BY#O?vf${6F<4IWHKK!LOvwB8eeW za59kYTQZPpI}FgEtA_>2EOp45P(Zsz@DRX&DF}EM^5x0CNH97R@^R;ZLXEr)Wui~e z$1**n@yN{0HIs^kD1i|>X}g-ljBQLyUKP<9G6Qzkc`0^1r{ek;_#sLAF0&X$M>hqVssPWT^7$eC63F)i|dL{n=$5_$S!@z1j zaDjyWdw7x;p$tLE=Nfue9irlSvlTo;i}=7`AVVf$!SB|!ktX(mZAaX$vaxaLl7;mo&JIXe z@VYROt}OfKe;G6mUAKyu(p%Bzy=9I`zeXT$Nt9t&U8LM_`$>@!Op@>G`qnTOGQSXH z5KEN$tcd^)urwr--lOS&V+CDj2u(G{Yea4H&IwR_A<-Jgi-Jm)UEjQpK!w?@uC<23 zZOl7H@KRHW^)c6o2?%S=$GgXO-5W@a6ChBlhE7FJtvbVZ;gn?FN35gepO^QEUh_;E zUOf21%p7qMzVW4CkvzZOj1Ys92qnLq0l`X+JDq%ENOhKjsKuvw2DgGqE_-si#3_W> z{TVGkIzP-~mG2-a9P0z$J}_V{H-Cx3BjP^zYF*^;+}WE}qa@@{8G4V55P>~F{rC66 zQEHGKSJa2+8aBw__gC|RD2oD&NZEWR&NAf5wNkY?$nJf>A_XBviYHBA$$*h^fhp3m z+lDcUpq&ffjd7Bl7(Xn0WLpGQEu~&Jq7N8M8O*IK-7n)Ky$M&jO}`m4a7-!oOySQU zpznA+bBQ}djn^defhYI8i3shNe_#E^kjW7rOuBUM5ePoA82e3rBM8A)=dUw}kv>gnS;*gV-0?mepS~1|h*O_emTXdStHHBDA)mHIT13a1@89k2o0NMHIg;OMJy2cY6L<;m2 zo^PCzga`rMM5B`?g;z&tG9P&Wxe&vJfNRLaj}wa!+cv<;-h{?y7f(27h@7OMRKbZKy0{%Yft^8%_ zG-9k|)L?c*I03XPpgY~*2u`KGd7a^MX4wtLPtF226iC?SeEZ0NCJ2%c7OuOTq=ew? zy*c=t*CJxT;QVHpr25$&C+8&^$w9X$-Bt6<LaX3b~PWIAHG1FR9*KO zVkOu8{9@+;V%+A>5?2kE3il-86B-2&mL%duC+OhgCRq!gt`ekD{zT^p!h<}5cfI`n za^g5Vcl*XH^x$g(F}{s?SE`4+D$0y6ce<^;xk8s3<>)Z z8F@((1Tfjasv@H`4}uMFL>~cLCC!MZzx>Sy?UHQ9GR@&p-x$T~!H>i^{=AtEKfZGF z>i+=U;$>6Z;}c`5%@66q8~*^*`^!StRsR6FDMbjaZhm;gcZek4eltPZAAHzTBn&OQ z73(6|cr=_j&MJT0HC7p@zwU5T^cb-WnHE;?9~cEn>sXwqJ!hnCu3wB)(Fr8)jeW3K zA6d8xxw^zNO4)A&?;S~0@LpzT6OCMR+#@s0u1Nq&3-hFj!Dy%zvMGN#zGFV0B6n*E ziO&K$HLpAKl_p?7nx3xj3;Pj+(LTh8R!E!3MfC!}!HyUgkX-`Nbj70^T69(hgJA4H z#4zQVqd*W+QM8tISSO<%<}gl!`OZVmG6Bc5bg$Wfpk4%%x&2|ja*f{neX&4yIFOVY zxwiiRy32%y$KPA|$=pbm!+(YVp+soFT8=b$F|7<9JHEW*GAKMoKPXA-5kRfR{Pl*H!)AMCew! z!bG#r(-@0HcjFF)R8Q066EZg3cZ(TyC#`wQv$lK}Y?pP;CWUH+{d&fb4%2oUy>j&!gc~*@JwEt-k|JPQ@vpW` zWbJuheK{LwrQA=pwcaY0xSqEci0fM2Wl#=AtKu?5MimMkY@pB*MVS*=83svznYj5G zXqKi$eAC$uDdid_I~)FWx6Rt|J5s@*=7(lG?3ji%^7GN_4 zpi#(?Fdlf$=CUZ-n$;D>L}36d#?xlrpBlr-BmKqX2!rnb0JyOgF|QcOT1od}bQw~D zNIRKh8VuYQ%Bs~Yd}OylIgQVpB9dwZqufu?!^XPhfLDlX9UVzv-lqND@~a_3GQi$c zWe(c$yUBDM;7ukNB-_p4+PC@?AK!e00$nodaC}vTq3;#P`-;zhvl5^|!AD4j>k-ue z^~mdhh}rn}k@bENj-OzDjA$%f{XG7#<(eEsM@C<~<5)%9?oM~9@st4+S-&&-$buno zc=Nx$N|=CZXy*EtE9tU4l<{$_;3e4Ka{k!`B%*qrsPE2KGUZ1oif3u!Z!66!!oJax zLH__wcX&vKcXDmqVzt_uePbG=ksVgqiv3uDIG+VBTJz%$$U(??Z42aXa6$RX)y1N} z^E(&-4#SW)USL4YL+0@t4W&JfUq9uLM|_&B%*8cd(GbB!;K84VK| z48@_E#7_qC4RS?N!_?8fan;c(F6TxyxRn_Tcw5++yawuH>sV$;Kb&*o&(k6kOk*=k zel7gs;Aig=6d|bN7E(C;zr16YMaC;aSlXD$loGQcla=H-A|f(&q&2q*8VCouCHuH? z;nY@Y0_(;u43oRO5H}R~*V`gdKy+boJ0|s;O}+Dp=hj9+cbk^L2oyCL$l=+LLmF~$ z9D(?}r*jXLWK>|E6InRC4P`dt_lrl*+Ys&M{KxtG!zuvN;&DiN!0l~ftEVD36&7o^ zc&&ztF8tymx5MWhrJYarSwu-Wd{rrci#e zArG*9;~APl^}&b-MF*hwZ^mnKOuO`AoZv|0Yl>ANQIZVbBgRpVh?D@EN5Kn|0t<Coc7&$K8Kki>5xWb)O)eY|#f3_twcb-GaF78b&Ex{sca~JgP97TT zDQzBqIOQ1_QVowuBkhqPr1=yci$^RH+ACWDW*IP51MX%v{lP_gC(m(8Q=TN?`DD{{YOAUH6IC_OV`YhP9S)TaP#( zMft>=>+YC9nnXPle{66^8^$#ZZ{)`S6Oy4JxBA4v(r4|G7kD7UZaa_j0D<;Y`^8r8&LWc{y|cHdGk{)Gd~N5H!%PRE zI3Ww3GS~^TPo_4-aFfaRk^oi&1l&|$sR|ITU|xBB@~eBrY@F)*}IGU(n4U^GAl4LqLnm6Wj}*J5EEB;mvZ#_{^J0){oDD7_mDxuN;&w!3Umj<1HCR0;Ln}l zlmXId)%uJ<+oP+ z%hyk2`+ej9P2;O5_m+Sq1m(a7tI0eUQm^CQ6Kp@#Xg-{c-xqMc!eeJ zxA-1aK1?LuNDg)fkfur)FDsH3GtL4?hdyu;MSHmQj3;9!(~NAOMbqBC3lqX%gm=HP zb0*`XArg4Mc?-3fGZEKVE3o@892vOKl}(jhv_Os`o<7m$VfSQ0JiADn`L zdFKXV5nK*CPppi{jkAaw zKNu0WPSg}9%@Lnu&Zi;?En{soyq-K_s|ynjRMr~mIw|8=O$7=&_`%>skmdQ;IRn9l z4hFvX1HalK(GEK&T2F&sHC^HG%<$BA8qK+9->lRyuKeI&X}6he+(v8^AhPp^*1`e; zqm)f&7A>*_TYjcOLn~32BOdDEz_$&T%i8|XhD&J319+{zt@%{gD(ZEjE^U1 zFk9hR$z?Jt(y>;DAa+2f1}rvF8z3M73-FYu%S{{huBlJ`stUXy0%(MYW>!NEtKJ^U zi|pakDd)lhWg!Wig5sbdxQzlKV-h$Z5a}W#3!GbBAdw;QSoL948FRKQ1p;9LuU>@~ z+a8EPlJ^?uP;VionGJULshVuD{>F3U=0^Vj${M!)V(zU<|E z$?BUpqF8Hg2RKsfxt0Vls*x+S*%(n<8Gx8;1cm`6`)IAPESp+=4j0Y>##-d{e=306 zqZ~L!0(XU`*hF8ZGmunx7wjoq#_cfxC8TZ5$GMf-0fuzVl?*(wEW=(Q`lk$xJHG(H zkxpmf&?<=~EZL)GTnnCZxlOQo290l-*jQze*!ADSSpX>G5-Elf0aWFFOekz5vM_9P z+xm!NXAF(h3<`j>12gO?3uuh#DE{11vyKlzHpJM}6PXolF2K%rM22yo5P9VX{o+?O zm^AWW^Go5$q`ttS^Ue=sHva&$@TA#cmj%ojwVbFP%8ab@NizdaGP6TCoCqTv>b-Ux zk%R)Cf+WurDE>mZ)l)-7iCja(LVN?>+Mc9;dU3A9>Gb&nfr`nH%aC&&E7gEmnI5=s!W`!=JfUxS}X#z!KO zlH$EOJe`_=WZb5UcLlK5PLVSN4&1tC?)o^(F_G^))rEW4Z9QPAU=jf!z=XS0;H2T_ zy6g%4NfkiPy100myj0_HA>4K66XyI8#1LDdViwbn*yd*e!2?0}h%5CE$U%WJ2>T@z zJsAlA7%zZX;DnMeG|O!}%!~#=E|_8x8>V;cg0cw#CK{P#08`O|kt1V+QC%z^5}Dr8pZ@@*FMq0j zE~*(p7BC`+NC1*Us-D!t6KCK=V8g;oKa}(sAwZ2uOG}P>^vo+TwASxyJrdFP`YQ?Z z@k_y^2Kd?0@41|=o`@h5Md#RHq#Sf77l;1^o zSw(JC(z~!O?ELC}1whoP;1H;=RxW1J3q(hcs8eZ2IHY%YKn3;zj?EMk zvl0M234Sytm<{m3#L2R_F6lrE4ch=T6@&o*07y0bSU9@^GkZ<<{#Ky=?LLzI37tdh z^|fV#gHZv)LdG|15}@u>xFHT-syY%9$NvB{PnRNsNdTh2?6r&#Vo*Pra(=UT;788P zvc+@~>71j34i}s#b{Iaxu@ITM@&N({updo@IfOA9qP)-JC`ljyom>M9ysM+gkZn=W zYmI!JAu65UVOhP)fVF3YLF8hIt&g4I&N~nSl3X@R) z7v2Oic1X$Th4tpQPBqIDS>H)?$)TDo^^30`b29chXe1@K67+3-OX&&|%PJT`KD1Tm zC0V6JBMUj8&yaatGLPoY$tesvhzyk>qOZ$voOpFwsFeqp?v4tv8BYv)Re`7F01JLG9l8P~Ft@ybOEPktJLWXh<#S{T{%|s$>ZfXJQ2zizyM1Zy=%AdkXKB)I zXeBQ~N?>0~KqS!;S-R{*Tg$BBJd)O!!oG*r!e3|(63?v!7J@X+677+AIgot0*1+C$NmEqXo=R55}MVIY;dBBOwn+=`O ztaMDAPWMV+i!om}qrLI$6bU97iXnD>e7=1&BE6wU0bNP33G}8$b7ZSyK5eo+N+I#i zze?^DmiEexot?MDm0RIoD=L+2m$sPWD7F#CW}A_{!-Pz8o%KXe7!|<{z*e|3kxELH z4yEpRysrYmghK&8*E-WjMH+QMV)kWnEJOrX^eavuyEwuRDJ^(K15-alm!a(lP71lm z{?o}df_~tl!JtQO#3emE!hu+Ug>s=WM6&xuf%pLeR6oGVvIE=#JIZ&oafhC47bf^v zEns|F3y>58!q_M@kWdSEP+*m^Ffx!krU!KY0Qe6i!lfJlB09hjHt)+lRB&ZT45yXE*2BO(&w;Ax|KYv9XOwkN`K9(#X5KvJIim|XRh~wMg zw~5DAoqR{g{(gQw(;!VU*fpk};Hc;~PTV)dGfAx6uu?gHl6^Sp;Q*6$n4_Q-zZ{>o zfQg^W7BI>2TgU^rq6C?k4QxRkmdzZ5y&2X*RXAvN5=Gw}py#^@8)GtywS`ejBvUmh zQ?X=50NIah33L(VXO{f+ndE5!~Fqek?FQM?M&?O~%^>x_9&*tBsAF1sV3#@Hny9LrV@z zh2t+J(3F8a-z<%HTrNWL!W$>6FXX6>P&JrzaK`shfLmp~DkdzEC281NlX#~h6{jQ+ zMtXlkmX#ZDNk3tHEJ`6g8M3_W11vD&tS^|`T5%wpTu;@0NUlsqj%yQ26iL%b>3ZgJ z4Cy*p>@)DvfecLOiea_29mfZ?CQx&Ss#DqSPs=FfKa7;Nrd$8x~k8B6(|&L87G-ewUB>?q>Pyb9vDtI z!>VXD*y{D5=+M!JDxAqJpz43|L*$f5uHzXd@%q`_(sNZFtopcpuKE7gaT~B`em~0s zoSV6-YWWs_(PqvQju_)yYxyqJEqDzOd- zKnd-L4ED>SVT>IC?xx_01P18?^eF|e3Hj{JXT)L!4zxj6y$)uy1b0Ay8X9W#i;npe zI4j6tfJg)qtvbYUlJ4UvvdfO74Cw(ET3`Tnx%Ehk=>k^MR!I^>JagD=I=$@u65G5d zsngimZi9fy2as3*d&Y>I;qu&SlAe(5&`yLSD(EsGzL@3A2cD3~P$I!uqRTM!IMaF1 zn8-T`VBm;D^@tEO0RSWbGC>E~&ovi|FTFddrmtK#zzh2%g)a-r_YBiV&03sXOWf zX~}zjqiGHv>daNMhm~BPgG=FP{{Wk2=2hQRcNl;g1OfzO;4xstGP4Dc^`%E!O)FDV zkfv5(Hhuw`wri(+Hsu#&vvtY@u@hdS;-}Z~NNYcMHZ=uUhDZMh zk0hgi#GkqY_XOpTewgUbW8mF8xob+1HH?@j_z~*GzNd(1q%lYjfB_$3O8}c{(Tx@B zN6o5n25xTK!tJ;7;3BAkBN-;2AX1c@{Gh;?5^VuZX3E6H)+XZt6_~<7>5ezeqQE3c zaeE0DkX|s0i9nC=3%o*6NrzK%j(QQ}3lB+%i}9Yk5z1Vqa$lyI2Ys)g6KVi}EXXqo z2%aG&7+N6vLrC+{r*YNOU_fCl(n0l&p6v44p;7+v>Bjq2J6$BiQIv`816iFOX7451 za@jH4=rXbar{t-*A3j^JzR+Hi<1EXn>>HgBww$1;^LGCLPsl;W`B9^W=POFAo}X7n zF$e?@1;w?10D`h@{P>)<1+{p1NRNPlxTlI_-Nd*9zY)T*qkYObdW6@10qSfXAU_Pg z5Eia$V1k-#032YIxf<5X03(bi*%;WLvnON|hajN@ANUkI^T;%8WDE)veI1$GFB(4#(yJ&yuyNuXBR`yXZi9WV4LD}ku33Yl z^p?yR6u{mcM>ujY1WAkxw*-Vi0uspu@Co|woxZDt$uBaHX*46GAWkuk5AX++D8;$; zj<+$P{ItNOt)Bvo=Hz>Rsqq8cyBo}^Bk_;w*5(tuK0tzD^ zkZf$tcm#jJFq*8Pfe`c!VkJtx#5yFi1zz)k0T?6{f6cHpVa+k>?N&B)eie=x6yQe}6veOU*P$U3J zL?#0mf_{~)=L)<_tCve*2Xuv*kp%L={YyNnUYi*LsEQxso zo(Ld-3iOo(BvUguwJtz9y3IEY_oqCC?T zZ{^RRhfPQ&;BcV>K@b$Wp@L^QQSVW0R`5f;lJYPJ1F8WvIR>d$K6e1U2wo*SKc*~) z1u@OT<9oIh1>k}~WT2SD!jvGhV39!>HRDn`5fCeT{{YbD_)|`Mw&8HI1bE@E?nDpX zy@gqcOHjVU@>r>0_JYU;M35HDKouW=0P}48ut7*WhC4(Nh{Zh}+)RAez^huF#QoKh zlt2ex33CmqSn>!Io_f1;FJ_PcBLE5hycf>l16GJ1B#EG5SuI#?e#jLhGE)5D^K05_RQ+Ng*9c3_~-BB83G1_$EUI@c@EbWof7G}{yi3CI)x1j0m@_eFTW_Z&JxJsxbw zgg`B$0fU>8N&uYHFYkaBQACF9YQnDjafsj&*s_8}&<<&m00CJYcQp6$5AhS`R>M@qid!VE{oAO#ud~ z1q|!aqYwxMjkje)wa4r?1JF)h>hjZ(C60P4F2)(pbc4Of?>)moBsMgyd>ADHkw@7D zHjou3!RI24>md+mWIJiCy>iy#7D|VKUOagQEf*2JL|(kq_(v?emNs~CB>k^Z#6&@D z5(t0-0VD573S_Dr-gKaeLCLIWYgQ5SKtyViTM{>y67>~`0vSYLjS%{)q=|vJ6A&J8 z4_5$bqyTv^(LpPSk1DC2)Rz(fL8qxbg}xRf)t=9&-f*5_ zbfO(tZ2;z2Fa(-J0DwpUD@Q`P;k*D?!o)@*3ROLcG&q7L0DSdTs2&heAccAK z0B|jWC}5g_CIK@fNDvh=0W?AAJTw8Ilh{6>X?U6jL!3Y82?4oJ=^gG1=F1v-v@Oby zAC}-lrWlA7fhw~Y%c&l11_F&B5C8_UKq}=|bZ8_Q^okr1(?!}fAR6195)M6(dS%wa z5|94?Wa<7fs)L388ze^TY*vQ@3PB_DKmWu4C=mew0s;X80|fyA0RaF20003IApkK! zQDJd`k?;_qvBA;d@gOk&+5iXv0|5a)5Of~_c4Y@aEwc3q!7);kW7paDj+iAR8q+sT zOnUn9X(V;u&-;WgM8!pGymh@>Jws!UY z{)|AKo-5zy_OoZ7x%nSN5*z@N$Iom*-T@KcexKikY^MCnQ`pq4|?G9@^Ayd&6 z({TCilxTu~Jo`?%Vhjs)ir=VsuY5?rgn|?j6*J2>9gHO4NDE;? z237>J!mgOd)k`CDxA4hP2Xw_CB-Bw|3VKV#a!nDoIm(5Usipzz)+C-;aT`Z-dTXKh z#iUl*+0qRTx8DLc*(v<4y5#C$HVlHqlEFKU*&&Xg+_)N#?k_L|MJ0!Y=<8g#r0A{| zSju*#=HaVjM1cxkw0P)S-(iiKkRhTJ0-J{uF^n406v_-lZfjREh2angoq(_bJsqMgp01ydSBmY?Bt}I9FaTWH5jcD36*r5>zue44v4ln9*@582n3Lr2QN%aOI1WR zPU}~PP0gxl?Rb=sl*=)0e0>y~LKC2_qGnyrv`yjy#>I3XCilNc&EB7bAn1Ewq3hFI zduN;6{$K$e51f2&K79H9T&)(#5pwtEznHv(q4eq^-;bZ)qeAo?5`z|>oDwIF-2Sk% znu#Z&HF_m48`#E?EVV2S_j-9C7Q>-v*oVaWWgRl*VJBv$d2{~eANMheTUfnM$DFz* zCr@Wp^nwPErK0XQ52pT~-~3<6_yZat8h$n5j<~lrObO3Q*Gz%K>VGq4))XgkKT8C~ zY@x4nn2RDwaR?>}DO(Bygi68VCkYU;5I|yEJbB>AEXXC;M&;+C zL3gMH6AYjn+)oW+M5Lk$GRcH>OWr+*Tmlv&%=2Gn696L6%@fM!7cl+j+;BvWVq?l) zyFu%MlykhS7LAX7`3hxGsUe1zHsY`Vu>cQN&s|hzpAX^m!MlUe{?YO=N)U+OU&qo29Zc6_1G0Vp0L%uHsQF((jeY)njGsgA{lXS-MQ(Sw z*AdsYLI<0jB!TohZ_Y1nsu4im<(JGw;tUnbSIN(hwno{vM~^!A;;Sz4AOQz!_rXa= zvcbXgrY2LP(|%`h?E7T7x{D9jf2W)0i2Gx=Nq;?m&!EPu=g-bc$8CKQgJ7pNYEDkJ zV$S`OU6u3i@8e%d{IAbQ?jGOnNRbmdy4TO)7|^c2+)_i2S2MD8AKYJY+PnGj)1JBF z_WnizsOj?X!2pxVnN7~SV-!^$5In8R?O67$AAh?40L=ME-{p48s#y#af+^{Kx${5f ze9!rdWZj)Q#A}!Q#_PmQu#o#6`QrBZ@q0x3eD%+tBML{REFHsKAnUmM=IwT^eB@VvQFh%gXh&FP^ez=-xCGW32r{w0KWNHn1;@gdM@r@D-4NoQFz;x@Yfn3dj0TFzT`D0P9&9+pqWu+5;UPZl_!jklE~bV?L2kD23i*?mSXC9?y1e` zEd&y!t_fy6?-tf-I#gFZiRyjF5?MV&)qi2@lEcgmJn(kOsT0hTT6Yx^b*Y?mTLFMn zq**1Lj7{PkdnIHMI+hr3cUaTHgHhwBik4YtC$B?9Nr+=(@2zQb1Q^tI7*e$NHf)5Rn#CZ26o$g264>XlZY9}bKo=$NyV9U$ zar1EVs}7O&+;1gqaWc-QUj6V+>Da~ub8lv9`w{8Ct09o+3ItR!w|g?hM6I&R6L3{g z#wBL`^UWSl%zm&Sfqs0Cq6>VnMt4td{8|r!eD|yA^kN(bJ|9n?KG|JG z1f7Q2Ie6{g7FT7NkJQ9uSr(;A>8;1##!Ab!@6M{f%s^qO8}A=0IsgF`rN9PYN;fuR zprTrLcZ9xAf=z5Ss@zSmh*1W`P;d?-v~pRG)MN?->^ozT0NfX-6u}A+dfp^pk)lqc zEKS7KTaaM1&Tz_8a@3un!$zdU7!e_vEWX(V2>BmB=6$3uw@!ap!e_4fapM=VSTf0Q z{8scA{{WAlKi{F~=kNaj8DyI3dY^yAQeB-6@$HRf^?zSiaoc@ksr_gEbv!XPoDn(UI~de)`9c; zpJVdJ$CciXU(1mo;{;RCq9S4^t})taA~yDzo?~9S&$vCqhK@Uav62MoA|9F!dvitJ zFjyA^i==Tu-+4VLx}caa$*^IhrZO?5ut9-KDaTAw5d^HWUD`P%D^1P{_6i0OLRot< z@Ag#41b~3!I==mIVopF!GLl%${H4d63%h)B` zUHvh@MxA+{a&0>wzvg`2nfkwfSSE|W`{V)8a)qE8XFUJiKAFTPxfkn6Gb z(Rd;qe19=c-^bIJr|%Gm@A}DYzI%4AdgS=E2;TPh*Zp;Peq8g3L;=9;o_G4ZcGiT{ zXaV*=k&*?ZAlDJch4t%Gj>(1XI^#{piSaol2o5AocmDvK&A33N40#BriKzW#3nf(v zHpnvs@@tn)67>uMGOG{^dtgyiMmj)n%8QAau0Li06q9Q~?)rOijL8OajuL(P?=%fe zqB0D;NLIRbz#)-nBor(FPKK<@x`G6WW&k19x_A|WMaUDZHX2v{tV&F_>B*$7+;ZYVJe@7A&K z$^^8vPxIBe6BH4U?q4pm@-f2zvd%$)Q&V2o^wsP0zy1=e&aN-qoKhnZGi$wVd*p~< zAg4A;((MZ-cx)aJbWdr1d7Nwrkd)oowq6K^I^Z6_mrqh7sZKgsnF+NfN|FUgQyC>lD|h)EDb zms&|KZFi`LU__<3ktWnRJzMGykly*OFK$m>Dt&nxo#%3WDc@MKtiqI8+=rb^Yqk_2 z+`sDpBq3g}N9clAni{^R;|8|1px^xYSpbNLx;5>^cX7yjaM$7aa=M7IC9+M6igK$2 zz)|20d*Bc?0Mo(y{&9?;H6&U;9EavHZvl!nCC$sP%AD&SW+{S-R+Iy1^tu(51`(-1aQ% zk_b}(&P4>GFOnO)ktWIrBd};;jvea|M2IvbiQppi@4g&>LQ71@N-BOgQ-)FFnB?aG z6gqxP{9is_?E3uPpFieELlgInao@%$iBwcnbvU12`7k9TA!;6Hnon# z{qaE=6)$2q*5?@T0Fp?sAc{-G_2U~r27svrv|ai3DG;S*UUm=0eE0h7`F}r7F*>gw zI^Is^Kfd2PXYBhl-~}uSM9hSbcuB>`T1ZIc;K3u^{{XE|Kd0%T=6>Jr-~Rv`o(2+=jETkq97clOPl zE9TG1^!R>{qeIRz2Q1fbF&s`cNI?)Ju#1u8^~pr?N8v^56iRGJC1-l#Fa7#&^8A<* zNrN8O!vGL3k@ec2?@ZIn4j#X(-{kfy?q$Qvz)$VXqAqyert!?CS$b*&>EG!H_Kvl0 z>(kd1_P!Nwe!J+YHMVD9E(m?Uc)UswB_L`kC(mqTSftr8z?Y%dq)grzAfRmFD|?b^ zIN&gZlj2@Id%bzhkP$P@?(A<*ra}!$_NXW#dVe>I0hKibgge)%6!bFtbm)2<`W?4; zj-zJ!!}ixb@aPeBYvLzecq<|*!vjP_Iu-WM&h)=ePR8@y_^JN@aRH=xeYxkJ)0bSo ziQM_G_jkwa`aw=&PIpfA!QaFrgUF*lsWZ>SlFY!+(kweJ_|@XYL?o~UcbWWiSc8hT zY!lV9uS^IL0D@;kb#Ko7^I7;y`Cp!K2rxySrjDHz>4DH!(z-GD_r?|Ex>YdW-`gTY zq9oSCcf|Fsya*X&gv(5W3=DJfG1KAw@$zxUwhcXqpD|L8h%@E{acIYy_QC+eGe2=< zBh7>x@VY z<)MS4a8~}TG?Jj2S|OVI9W-GDNjvF1Zap(+r{u)~n>F~sOC0V#`P_0|ue*t2Z+@EB zOl`7mj{XgfO}X6s*S0PqgWr!% z)#EmN=OyaRFEEU{3-f{?8HqVJKe1(dGPmXTbjeGgsNoN*9!AP<;R*AjxH?tgLq zIV9>@s{a5h`H@v8FG=Ia-x)GPtbT*0Z0qh#MQhcq^unYDe`bB>-v|zdHOBY8f81b# z2-m;TdJ=ocd(lx<8|p|vl73RuNOFj0HCSdZN7#< zLTL9j)^2W~{A5T}3Ir8?_0n9Q7g|0ZafY#ok|Bdn$-GRpi9Qp@`|E(BQoO*A-!CNT z0pL*H;-|Kb?R)lLfA6Lc015}CEO1vj$hak@NlLk9iM#;GO16axDY6sf;J^{9ijTz4 zPP2|&Qg_q)pVZ)7Le)-1bmMW~=WiIJ3ZBG$&w0WtDtl@k4b!Ib4U75n{^DH>7?Y{Q zJa_z30uVBTuka1$+Yr*^h(GjT^o*9&bsvV z%0dx_NT2~%iW@^=n~)HxlthWMtj8E^f{;g9ICKudngmiDCjCA|V0PEFCD~J~?(bc2 z;x33K1Azx>=N`gO*nwC>DG-&q^UCiYqDk%gK6J(=TEnKJT9ozkhzg_&I-H)_Dpm<1 z7LPZkEuR=a1=3Lg>b*MC(s8nILWGJYQB1;F(SU6ndKmQ(e4e;Udqh$9o4EC(79mN| zk9}`GeEa)rbz5f8=h+r~wT1YKi5pPK|Pl5uH(naCphc~)KECK_y z69{QsV?ziqo(mv>1S};|pK5YRh3U9>Hbl%b0ShIYJiGjZ5Q356UXp*@f!uh0dgMAD z*g(-U!RO_3=j+4Q^^lYk5)4dCPgo}>fE17-Vq>7Z6T=-No$ud#->&^}po7m9pfRZ? zZXSaENA%n=NUB&Z5+3#BmlZVCS2sO(&3}-Agc0L_uRY^+n!SH69{c?MwU9g>-&7iY zclwS3WXLB^?VqI;#cw6*`~LvvQ_t!D0LKqsp)4%QqON)43xE|9ACEZHRaEU}2rI$= z0Ee$oe#T3M#4VM&JNox;6&i+Cjm%7^Q=76V$4tvpKkpzkMsMl;`V0UC5W%Hr?#siI88q_D zoX>haGHelDwPc_3Ej)sZPtVTr+_3{}+H0n}U#0*Ox*f@8`5o^e>qg+-=6d{Z2*`>m zpUhw-a-iJ9zcV`7iAGA2X&pw1*QP6Gq_r_11M+yYKH2l1p#UHVBw-sgl5>S2rt|od zLCo{F5lBq{Z6ui1{oHX!f$tC7_ty;QC#L>UuboVBS z;-5WmO}K=5zTG|dIP|bY3xYdNrhT`KmRSlX8{#^ZSXK$E7f|bsl0KyJbg{p`oMAaG zlz(6DJ!PyG(9LJN|KQR1qtQiMuaY#GwW}gXuLs zYa)nMLfpsmq?*8LcuI7-y??!NxgazUi-|=!ZV8Z_Uxwx;M_|F92vDYIM334*JIO_So zJ-lF33(*VrWb~}Q?0U1-{p$^>q8=1S2w%L(!Q3Fs#|dh0o;!EN28_mzklI4Y*06vC zk~&aD%4SGz8u`Dt~R*^Ci z5wjrZdl=|ZO_rdk@_V@W0iD4V>P%IGZ`q)%6uu7}To~Dfs$B3WNgVD$8?XqDjtYV~ybuU4{POcY0QI&Eg+xV_+!sp$_9<^KS= z{{Te&VH6}xf{RJjy48r481oWkNw>}K83i;n4qhV&Fam}EH`v1(1xUEvJB*JD&6S0$>4I2-8i+co z+DU2KD#=~W*NjUmYD%<9w>3`p&EcLB4ccj8c*HRlm#8Caon*q=({B~(`>%WkQ$?1D zjo&)s3}^+gI@G8>PL2s(Z1+uZ#mgJwhLH9GFL8HdK{Af6<@!AKV9DGroK? z_lvqkWii_>OV#&(xC#rrSu>AhER1w}I8|pYtEjp%KzJe*TW19x_NlNM!|+OtQ(sIDA>q zq)dncAqmM?vPmK&q;02UJ;LWzjjl$=32@xRd5lt&Ix3#i7?6=isG@q7blLh~hWYk9 z`+c>ODiQ)(R@~VX#ZE=b)qD2!^yz|xEZpz4N14`2$)|T!e;=@i+$I8MBT8Z)7{u<= z+4%RYmRO6ZNi{M*X!NFoF%q>9VrKWn%abJwjp2!OA*hZe^e0^4@3=Q>jzt@<6ZX+}x85MoFb=f<3ItTri`Abix^_6H^=1d0!M zw-s-AELLwbPzFpL9M0POk!(qo^WD-3KFz-L3diN!3J_S3 z*+N1b3|0-m>iFpp>9Ns@GDH}q5;smbnYD(cbV^NXE=+@#DxO3hb-F$BNa|XS-LpJx z@ytAuWThq3xs>a;zH$LJmObZ~=Ld&+N2Gov!bJx7J2>!B1oOFy^xyX{b790>*x!!ZbE!6!6Se-#Y&Q*1r$K{{Ww#K7RA#Ui0VoH#~oSj)z}*A5v$wp00S^Zie5# zr%p1*(ocvqkjwop3}q4`q2#`eOcU#XhTyq#Rb9GJ!G{I%NKQGe`pZ`4f@YNv#CF%T z?6)DRc=i7Pze!iW(Dx(H(AgWB^V_CLc3RtUW}d&-4bgRc?B_W+i3%&z+PlAhuf5te z8Yg0R;qic~Ihy?So6gTylTW7;Ow#e+w;s#W214hrpQeu=OLK37AQw!x5bih6!;Gdb zQ2;MN(7qEYpJpl14;d}tL`ugpx}cS6H5kInLlZHrUZZsYClVweK>$Pm)C0k+K#hrG zD-1|aAaOo1No9eza&_+)kYNZlCtgw4`-lsI#b~Op*Kp9|;BewVsT3scH4wyMJWpur zU*EQ4%qj%UW_tawe}f=v;@b>n#tOieIq*zX(m@ZR%b;Hc;1-!u~Rc1z3=|zot$xGbvX}nr|iOi##OzgHboPy;X)1hpIDvlh_@o} zr@mZ5wsz896|opeAdh z$6TsoG_XuyybX3wO^6;?Tip4Sj!IXH*whX zNh6{b3hlkuJnxKSD#oQZ>5F}vq3f&-klvsthAYz{wA4gi=AZZ9S@@rm=jmRTvTh|&fut?6_pPfU%INCEWyVN?RV6U)8iGSMKN5-KHcuitb?JAA{3dJ zQguvm+ZA@Nm1D%U>74#=e(`vN$Gzl&wCGJwY31#GoPlKt>jcM`$#em@0QMf)0Z`bJ z!>iKYVtF-R-@Ew6ND|G#DlfnDrZfRSKure#))G=dtDr4OYp*wmiENgeNmHYyup}zX z^6#u^!Yia5`10ScwgMj>yyS#(Zcoqh{PjxA=$&xsKUptV-N|~s@BTkGSnKrE^ZI}B z^Uu@bcXQ{qZ|VDfo4gvR`LE4n5EzXxvdUY!)l*S|HKNk6(uJP8pBIWPaWfZvw{xqQ zFw3x=8rS1|>w3nk`mD`(?!CL^gv>#`>$c0NKUiq_`M-RZtM2~*aae8B%aZDAr_x@p zzFkjm$(=y*--C+8723CRRC0CkzOp`*uqB1%$? z*#)uvae-criCMhU&rc2FMZwmt9>qU;$^tv5dh+`7&H@4mBh`O#>DwjLzKUcFNMmfZ zeeaN;5!Y;Os~^7odBoKu^#u+zZNfkvg-df2!+*`;L{=b2s=_5Xz8dX|6M(lG&`iq| zQb~7hSruNWP?k>`9%8X)6Bt4@h){$t9V-_ns$Ggp?BI?^6uWn$pYv5W33VWm#nA$d z3PWP7Qb`Cz&` z=;Zo7dSnZLgeOu5_k8s-3799ZIAUPOAW1ogr11|(1sFVN!F&#Wd z>mZp1iIACBx4gIw1C4t9HTC{v8V^_dh)SJh_}^b{dyX|q5es@Az4+gJm#gmK8ihuA zSMp?tm?bHVcG>NpZ12WF(DugccIE2iyigld$@$IYW;+g>J;(2YDNNFox^>r{ zIMym8tQ92_eHrG;p8CU>{m0sJPGiV^Z~NaEKn#;_-Tivx1*0b{9{&L5O7i=-lB>yH zQR4pqeex+@ia3s9{dKNz@78YV8TX#ElA3UCkP=J;IU1i$;mT)m0X@K}J^Et-C2pvL zr%{e~j)>EzUFdfDa|GpRZ*Cju9rvzxf&fq{3=W}uS38x-GReUeB5AJ4dfSYW)XJ5U zLbF@Cj46V&nie$+h;!B?9P&4;W=J~{nVx`jc>Si_Vyg&dWl5C#V{G5Z^5=Z%Q z!|CV7@dAxV+*GZ59R4d3gfd(l`L3SW1r1wQJWKcK#8v?s3@Li9s>STgdB;EXyNy0*nI6C1*i5mNKiN{S$44$64@9UE)v?ObfO7`@~ z3KmOKu_n3s+T$UJi4fJ)OvH}%g}E99n!ZF48Zz&;@c_$bu1YYFl*O_)B($-eVn9L! zCZy!rJqB^hMM)v6{m&na+`1Qyo9VoaQ&TtI{B`X2$S`$Gc-tO7KTaOMtmWzZ&tIGT zeY11N_vyLw@A5r!p_={#;DUSpT;21GBCJ@$Jt2o&+5Z4;K+Gsxa7V5G0Gng(hul1Q z`WieRgXibz5v!|XUi0sk7?`}7ZB%;Pbd&!8TAqJT(@$U1ieYE{x_~12=yWZ?I<~R1PaMC&-kE>#JAEC3! z`Olv|*$I5V`}DIq{!gFN2#rLibF;k1uS}N!NI28}We8j8{?#`^GaUG#VSCz6~|sER2GhcqE#oIznA-!<2{x%f-Z@zJy-uA?ox$oREqI zRB>&dx7)J-RFaN|)8nM^gIQ@Xl0XkAc$srimjnwCk}ApVQ+FF&s&X?3K!p*zv|+X; zBMA{ek~s8lznlOR?=v9OmTDu9LUpkUETw}Q&{88kTS!}xv><|NiRZM;w=%G|8W@z> znWs#E?kUMETrw+c&ZEy?&P0+aF6ar1+{4zo%k=C??nmz+c05Ppc##xP@yeSO?OkuQ zJs)QT1|fxKQ6HVKVrWg@&ztLvL=z91Pqh9qQ0#cj*H`8C&#~>)`B&*bPlgF2x&HmS)aIw3)AaoB;qHENJp>-r z{1Nxbd|p4DwUVyc*FAr@V#%hip#K0pK8O%Ft95-IF;%0Ok5S0~TXO zi+Gha5S7NKj;u;J+&=JD5_(>QMF_jOmfeu!q@05UfS#&hgRg2rN-Qvx zOfwd|ZRZz?hn};{#ZMm7Uq#a9KQo^`PtYQJv;6x<{LS8JA={XMJ$X4+-e-o9S2(s_2oP&0SHkc*T4Q z69^svfJh|Ch&V!67Fi5Ix!3?Y8o?kjF=O0!h_cktEUGl5>U310jPYb($q+sCQ)AL{ zFH!+Li0R&AbjggKd0f61n(j^k4UWC z@%w&^7lghx^8Wxb%_&a^PMs8Y+)Hwkavk&s>(5)wA31WjuFjHq(aze$N)jewWxalX z-nNUY^5BqP0ls5iaG*K{em;ILzfS1P%En0P+rNq5e3erC`(#)TF}^<@e%Mh{?Ee7c zca!tmK5N(Kdfo{HEh-o6eezvi&tDz=cahXjB71f37C5(Kr$r5!*y`= z&z^A6%Wqsp-z{=e|fKf_FRg{{UBwn4=)I zD7&q#oy~TfhHXb1auzVpVUM`OP%ShOCY?lEc5k@yFx=t!Q{{V&wU~8Cmb%Tf3*9&@+vi|@R(Zgqx z^WX3G$En!ocRsoHSW%G+wC>68zrV=)Wc@?lPtV`eBV^#Yqj*;-st_85! zX)892CtIIvqOFj#lto_O+jErw-k1yP99m;>!Df#Jeg2;Z(ToW}YEX64e_h_6oclIs zb1Uh zom6hv<*abgk+m8(zQA?YZMt_{Mkm*uW8W--V1^VTV6Mj)$Pj`ic9PqWwBK09ka+J4 z72fg%vVx|80YFJ80I`&e3ChC+C-Otwn~9uS2$Z`JSQG|nVh0q}N{JI1g&lFH7?TnL zSQt}k4~gj|;4uOv1Yu$(Ij`nQr#z#2Z%*S_7@&w|6Abeb6UXO_dPB*=bJ*{i{SS+# zFo6_UKz1Ty)5VFAS;h-|(Hd~}WsHiC1E=J1r1j^c1{?#u0KaAJmI78(S}M?Yr&ph) zvt2(2+Zi6K@Ql{*NG#FGlhk{dFH}2YW8v{*f|M9fKRta+lB8;NFkHlbyJS5ahF}%w z3iZ*@x1nugTNBA2oMKXMuhQWcf82+o(_GAcn88IYm`qUcS&{nwl{|g0d54dNv*6u( zf8SgL)Eh+6sXa0qrngVe?lOo&LiNx4kLDm2MIoO~@VMmnE{C#Y4g}#59|$KI144Q~ zQ32QuCi+%iI>IvnhDo8ahUJP}LeB#S} z21$*g^!2&ME=tiI_G?N=_mISq=ACW^k6yO9byRuNCgRicuv?WUZ{cB z0aNeyz@kA*0RlwoGgVvGBOwVB0uE$tl0rJg7`O}~37sAiaWf-NTyTjejf~ZDBL~-b zgWOXf9!U-z^Zrx&h#?rzpWHwd0GnFV&ancvynw6{pc25rftw6F4o=^)o#135M$$N- zn3tIKj!W#st{9k#VkGIjDVIBqC3K1CU%y{`54`^XGoPpLJZt{`r|*aq!F4q?orhhY z-D4i{$Z_G@mrhC7*#75m_c9$G_{Iigleu$anhIN3z@dms1jko0|@<^v@=IPd=ekv^mMn)?2nKotYMa`nV8 zhp{}HeO}&!tW-Ao-`J1$I}Xv(zjFHIJPY8T##Cktn}LWr#N5ZPOp~eZV;UnyndSa` zuc|^O>Wvlq+t(-P6fniW@(U5KT`>&_5Hl&Io;=yTCDS>SnA7S5LzB_I(?_~pV}KQ?%d#q2z9RL35}<{1)xG0m8_UVi7o0mEOm00P0k58bi=A=AyNV>M8P0xn&#^?9Fe3m zO{|L*dosol7Z58u?nT_4OxPTHY1?&^7)C%A7jSW2v)b^Fm&=~MUfCP%JMDs4ka!oo zHPaC$N01_I<6jSGNtQB&rky)=Fp{M!5nD)|&#olU!qW^vG&1@rBgXsB&FV6B+w1s`-?z^H0Qmd!@O?IXznq7o?VA$>jX|O>(~l2yl0wMq zh1S1r`Eii(wwT&yc zTa7Gp3Y7rhSuA_iR?L@wGWYvGm%LFwcg;Kd)r>m8g>_TY!S}{Qb}zHDBWq%d{3y!3hTBrZ2TF9fCY8 z1mTbfZbcbHI3WN^V&O9;A+}&S2#|z~aeS~yPu?&YZl-J6@r*({*Qe8em*D>Z z!h_*IE2zEw<>xI>?k4Y}H(ef+80MZ+JSAPGYGLx-**FpV_N4j^?4ww*o7zI*J#jS3`#EvtTS*~z2^x9gRe zt!}#Y#w{~nxBmbl?y>eWXQ$xBG^d6>^XZTiNP^IH(DV*Yyx?EjG>Rfrj%Ix&!0Dj{EpoGucyIveK&f3Pp1!G)_s@l{{Wsz$$?a) z1!0?l;(a)A3M`xq@EC8KshFNH1BoL+PA(y7P#Vh{T3^oI1mK<_zTf zM0co-MnaMZWr`4rwiP_qJd#ELGKmO_>0H(x@ro1#GP;9>psM0?Nh*Xz2wWbLkG=vB zVG2qTCngw4nx6PpB4L=|EcW9Z#0MauZNDzC;Af;-9P6VK9O58R0%ywpRv;850K}~~ z@vPiwf#97?b>pjB$$->C8AOyZHv*n8$80Pq&%VA{q%wRC2{e zA+Aa6;)w$-0NS&oa{hbd24V8+*HazlILCmmsr~Ekkb6Yb5q2dm>sz?`eEIYID*!FLE0XH=`SHS+Q4uk?o{~?KseuU$*$(P!ufuqRNJ%7( zoqSeHqwSZ1QLitS_|N;yLu=XoWUAiZI%5Fh(kMa--_Du;09nTP$OWR!s`3iTIdsAh zM3%(GYtvn-GlBNQjFFrHo}^?=r9MutW~Qfy1ePc?i1M62uDCl1C045Tov>tph^kJu z%5>}A$;hx0pj1OpEDD05w8gOspkxyuNRUaSc9|d*vf!D76awD6b~Iq5DJa4O1nBP_ zy5c<`MoK_pNrS}G!;-Ra8DblblT(bASgx_jfk70oQ*BIg#F-n9lt`$WIyEGhRGpBW zsw!cRST$3Ur53?O;Ha%zDPtOpSQOZzPugUXPxAo)shE`=Qbogj`VuK4-F*6}>~z5x z9>C0V#H%2eF%rUAa7-$7;@zS_0|@MLY(SPYZa#l9b<4Sf)0X|n@)Ur;Wrjk{mM}JS zn$2FdDvS4;?Y?kf@fRFDC&z4HQV)K7qH*leWm$&G?Bp=6=pHuTwv4M#E3tKZhq8qJn9c^VYlN^olCB zRpzfK(-y?rXQF42lUkEP=#e6tRs=Z=N0MJ76jiyF}FN*Gu%=H8)}l+p5&@ zGtZ1fLR8VaR2}Hx@15v6bbI8Wr$BV`)1;?K!;%O?nP^8zdA0L|0sxqzVI-KHS8$7{ z0}#xTcq#UG(-cjK`94cWITT?5LQK_Mvc{Mj=OO_mAoNO0cf3H#!5`0)nj`%uU!U_C z5F82B^5;U7aA0ZKv%6h4$}TzwB{a=nb zW(d_gu=;NF{1{nBjGnCcN!53kCZa@BO+1>&#EJlxMuDQIQ{xC{Tv8R^J*(5xE>l(F zB3bk;cWQCtK6{f|Df5%FZts;xw?iRCA~Ke(bv-6`*~N7dNh}b(s2tC%-%`8Z9XPCL*F4u>oIaR|v**sY{{WE6 z8YMa<_j2owagf*uriB>dlB7iA-}Wj{$nD*i1A$Wi3*4| z^o^``zL*4Hf@M;INT1zS@-PrFJLh`*{)|cDz~vMT8cb9K;cBpCCh}PJsNg7^2y!4z zfLn-Qo_DJ<37%eZg_hur8*L7LUL!IQy+4J?-a^K%yDjUztg1YhJVb6H z=3qdW%EA*9J$qKFU6YCRnNfObUgOZdFkFU$dPNgNqqm+X7=Vnqa2=lAC%zRBNyDTY z)q46y2&y_lAqd}TpBVsVQe{h9VVIKlyMiRi;E`dQ5XQzNX^uv+#}m&oNvX8Hm&Q8{lE3|9S{7vd_6jMucpV7^Zx*k$am!KbLYlrc|UQ2&Jhp3z3|`8H-b#t zL##|6n8vUerZ{J(x$tsbn7-pbefREv<8#N+{T`i;-TBWLRY66abq~f054V>-3#;hV zluG!8e^?k_PMs^Bk@B-&;=Vdo0*t9Xnm!-K58PJ~^Zd;pA3kxvr4FXPe%STIC4lq0 z6X$jR0CB3+(Q`;*v7$o^!`5l;4;*@6V+f=(#GuE{Gk?bZbN-sc4KeHHVmGv&mSy8H zYGfgid;AQ7dRy}CXVz<5#Y)9*gTGf;6itH~->rPvycGt;PTl?KX62kxs4fRigRk>1 zLufB4U;!z223?&5WwKuVoqeJ)fousnjdViMl#0e}QJvT-j4cQuI1s4B$VkAD4hE)T z1_Dly3)g04_xTNx0F;F%NCD;qYEu`Muu-n;_M0`!5r6=t3L-mV5~3z(-VCC|edomW z_rNs7^fznQS?`Bd7Id>m1ARY?;0YN$PtH6N8zkTzPES9a8%iQ)Po2M5wPkPE`##>d zENGH~(n34oXN*!sl^w;`LVUcDW0pK2A1CSBJ{{{jkB84cY)xnnX4IRl@2`2qh$zG} zASJ6}-ERO$KoiZ0Jgzx6#$UWeRA-6`4OHmCK^u7$;Zv@TGpTjYG{lIdQbPSdeYcL@?EB(sAW0KKFq2Pg z>IAR?t@!WDJ*Nz`gxwumW6b#RkeF>DCdu)cmCF!O7b1VQSP%t@HZ721?p_hdZWAF^G{)f& zSbz?QF(;831@$8w+Z3if@0|M0^N6g)_fQmDbsuV|@|N1P7c3KZ&Z}7J#`#uRAwEZm z+`&Eo?=HwX>eT!F3RPe#AT&Lw22YL=*hujLbIhC*(q%Hc^eGd59+;Yq%1s|^V&#etWm7ehk~XBXl1_R505_ThQf%B4&tLp# z_Eie!*jWAW*>Rf0bpxDeUQl0ybJ+)L@mkm%@FxwJO(bF9t$IltD z82LsBfjyt;YHj)8|bHYcg+~6ziY|nP*I>z zXcQ*~<_W_|R+u14Sx9XWG(!!zg2_qS=Uh~T$l0iZWtKhW_*`L0h@psqk9#WWELd~cfhA4Y?fh2a2_Aq3vDy#8Mi*Nk0Z#MMQ84H`Ur>-lq7z>~4z z>Hh$apE_o*U*2!>{GD=1f?==v9C^T9esWUCC9s3l!l)ZJ+JED$2oG3A@Xd;RoY!vLAEq(NELMtnCz-(z zpR@eIHy)6Gqul&sRyPc9lNC;YFHliDL>=kIGK3{1BcATwaSBlSS}CEJdOUk*iBhUS zz*}y430G)?RP3%JbM4nS-}&DWZ~#=!9!iOyIC^SU%TPj)iDnQ6H#jpURz$XhsWPOK z4#y6e>LFHcToVGxPbmooA*2Bd9z^EI0;9-ILKr}Vk`E-%aU|a6FtnH@=(v^-OCoF1b?TDbZ79=R%(*%(O#>Ve~o->8GuQ|FpavVS3(D9o6KL^sM73<^Y?>_VO zn!bOZU0%PRrteR|^t*NZZ>QWpKEI!?kB9O90Hq6Xkd4US-`D62Q6edAriLinlakbc z_XNqht{k9xz|#t+xfxFp_q<^;^!?{^?Ee5V3Zu_|e|f-yHZk}A0CGf~jyiPz0C|~l z3a8Mk1dlb_t=6hLIF|~U^gE;MVj_;lA1Bhiuf$=ZIv zA`QKFEuaB724xZ)^~Q0ui4yG(lm$lydA+hlQYD|hzQ4>u04Zi!MMDP` zo;bXpQ&lP_RukR|T}8UXrO{jW@i8t-stlA=?~}QIZZUk8luaSU@x=J&If+Zlw!Ho0 zwhf_as708ahzEW zeY4B$i-|3r9(d08+5TXaKcAw7x!(IYfg6SBQ1#7A1*w`G?eW%}KvpK98;*kUTE$`# zNtQVdUVm7cup%6WS%*=l?3m1H8{aa_oGp3I;GIM|{>))=QI$f~jUBHU7R9HOr~<>j zD~*mj0u-LLDf+=6B*lWyK2AEs#AILwrjD)b5eT=M*V_QAL};C$ zSC;}>m;=1!zTKv9#6%niVbiOa?bk2xW}ydza}BN@k_gl#P#C0!q$P_jaRVYyBA}HM z071u0Fs8+@uB#EVb%p^XDul>|i9|b`Pnl(~LLi=<9-PT@QkjfQ0@aB~NsYUOL9~K^ zwdAtyB1u}nDB?it#R-^iR}qgR+0d&ekA1{-f(W#rTyk?jc#gG=Q<4E_)Sy^GBZx`I zGwy{FsS+`$PVJ>YgsAy=;84uzcsdVE5X~o}>-xcENip;R z4*MS7Ydf#DN?H_2fucEn6Fgr30Ld|IvL+5d82DjCuKGUz02#z94%O+;Pn-HjzqkA6 ze*IPw>|&mt=d68wBir=3>FZzF`+YU}U))d=;*Zhu(-0>7Lp=WgF@!M(bD)o38*v1> zR};zn4n>6!4OKcZgLDR+l$z_-J+VE>>E193%9v~D0S{O3Vir%IK6QSP6MBd`n5A8L z-yJ6_Ec12S@%;0QiE|x0$H~Eh&`*P?eBMGYgNLV#?|q-X)$;YfmpYBFt=f^!WN5{{daLePo zKino(FLQ>mKzWn~M!m_5d5F3YveK15bzMebafkcae z*)mE-9gT{f_wRWU=+b(A{Cre+nEOr{i>wCyTBE#gj9L^^wwvkKk6qwF

    yMnEg*| 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%0Ao3sm;M!y*H9L40oV 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 z

    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%0A1tZWMKvsT+D-0 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νmn7sn%$`@bA>uIIQt(5@`%jY9_Jc2Wjjw*a1@yqJc)F7f>+Aq@wmbrE-kG z6?B6HO;0n3mujT3CLYlqwsD4+KM5J|&WL~8Mz1pJsE02rII&m^RT9gXDtBs-nDvZjawO zkQ63jjT+L72JB^ZJt5z3`2PSiRN{&Sf7jQadHLf53py8|K4%2xKK}qGoAboMK9z0x z>^$mE?}bZ#u8#^Het0TF6#Bvc0B;Zm7p(dIeC5NG`Z8Y+e_z)y8YFc808tCsPX73G zqaZ#5DZ81u_t6E4O8LI8`~G)~l2@y3`0MIK>p5atO`SEL0MD=Ij_}mHo8n8;-9LSR zs5)Tf38FPOF4M<~@!Qm6<{fDH_khA~m|Na{)A>B(F0fiXJy=FMorF%f#G6Z?0qB=U zA@O|506}|KXGh1W^Z4ONccy=P{{VlULDOmUB&_>cyx_(z?7I4_l05!#q5uY1aQBd{v; z=ZD69@&tyESv&SP=f~aV+^UN1m*%+U@7E+?#tH$%=v#ca^0l5w7ouQPpB;Qb@l0~k z;c6Lb`R@Z$0kQ#1`4S+u@F^aMPjx{QJ)rHp1q7`ouy!xj%4y1Fdjf^177+SB92Ubv zf-tm3y|jDd;k5X5ZI%gL*MLwG4zhzT#4>o!2C1~$;!Zt zqLJr`bNS%Xw@XZq8unN5#*uU;(izr%M$V?$V2aq58UnkcA-hjSKta^-SGWTILT&&qR1-7tuQ%8GC+26 z!fa$l3(>yHE`;Ji-s}?4HnIpkoQ>)fxTPjF{G{XdMhIBR7206EJWtd_MJ*jdNnV44 zjyf2nHr%u;!t`E2UIkJJiLHVJRG^2byEtdG45Q!*4ipf2UX1a<05peU2;Z%GAZWoiF%)6cRz#Rz zq_PQlmW)$I*lg+(3<`2Mkd!A{u^2KedU+&6q*iFtTrVx9-+IxlYL!o3F}=i80|Lx^ zIuW4>>41VvDnnkI#Kn=&%Tr@XGvA&ThT|3r42UQmv-e9ToB|F%2RgH3iE<{cs9J@# z#Bd?Bw9JoJ&J|v*C@-6Ry*yz@4n?KSxUK$&xF^T~Kp}a;w7)zF0tmzk2p|W&Zr)C+ zsi=@ngl(QPvd?+k@@6}%%acR-?#pbNMTdx*58nRc%maAeJ`H= zJ^p!3hKqs058r+K4xGtwGGZP5cdvi0xT;DKfM)giH7x7u@OtlZ#E>JSf5NzOxr>&; z@)}a4XO<=$egSWdwyasso(1d5-$AY6&M1 zB!ejQ1qAzfM4{<~fNi+f%1!1314m6EQqaS|o^qm*`2Cy?qeXM?j>v-})E>B+B~Y%a zm47?{fsLoRe!l+zH;n)_WApz2zW)HNdOFK@F=5pw$h9PO$6+39A*~SBEdORw{+F&m-=*=X=4OoD`u57?i zCEy=-zSdWd(?%pT05eMe0Q`G2o}r9gauMPxPY&pH;WZ| zRS0&Sue|S$M6-3NZkB$IML<+g2H>gqoyr+$$g(98Q5>)IaeU`YTOmFVPmcY=pyghB z)hb@h1p}!Mt^joGLlH(XmRdhO-T>N!s%U{-fg{_8&wly2*ehNNwiB%4W1?-WQwBbP zI}?x46e_ErO%(C^aOeTxsS8_^{eDkbw8Vc`FX;I$@ndLdQ!+pEth^=15L#J5R3D(} z^EgVf6)Q5m%P)!ZP9s@)ODviM=_J`a;wJ(LvvZ*rc6WPu+kuKPfD{V5$@yo9DkK*Z z>O_qN!`1-8BNT|E)`ZE$UZN7gmQ>&I>&4t+g;!8ucS-)7X*j|p2?!3@sO8|}RUpoh z1V=1V#i4E#l8HB+FpYuSZc#fUOhaV<04HZWUPaL0`W-FppKY$;*JDrsJ$!uoAbbFn zOauc6f+@F!h1!Z`9Xo@D1mg@)Y0(&!4N<_%oFQ5OAS7lZ!{4KVckE(eBD7O)-v*(h z0;>X^hQJ;M$x8hMQe||4wRr1*%D9x!0i-c#W+>)U%fA4K@QANKQ9&9~k87QiD zsoY+L^Dy7jNNEAl)*BRNl`?-N5r6$ynpyn+slt^@$$94JslzM9rB-VeYmd*qb!^(Mwa zKnYQg2N?2v{eUQj3Wo;Ky&Fkz+gcXCVdKZkmAB)s!0jJhyV{XQ5a~0a1oGD?X`|547Q8YQ4@r6=d7mydRRk@6r>5~QwT8(NvR6X zXYWC=TmT#mbxlM?1}K7LI^u|nMbM;Dr8EHKyam;?kY-(lN)<%s!z^$&0w@5G!22hmC&xbQrBrWl9546AR264l*DUC||yO4pX6_s|cNZ0kV@Nfr-+fKv2NafcXZ2 z7%(z$qtp?Ao5Q?UVPrgZ058Z8aC|_Uyd=pRY6T!gBHH9zsfhsKMWxw*0Kh~oTDE)# z<1gp$jo*>=^ZDQf0&PWmPTRl&03QY%DFu&DO=~;vuKxPtkD0$ito}UXsjb~t@!J6U z{{T3$lU?+qdTn*Gy8I%94446#W(@p%_8c@YtuSD8!QY(oH;QmOB3`_I_s<>-7IXku z<(7KZZXNK%SdgZW>|iKPlHM+Aft3!v%=`ZUJa`MTjAzU{_qIQT;wUgcX&Mn2bQ@eH z0X1xoc9sf8S0GZ;BIsar3j^YD$Xl>V02YTaqd7nQ3`1p4J)Ce!w!Fxb@V+*3kmGk7 zw5$sbkCT(!98aCAjphM_?gspPMD!AXU~z&G<3xHWa7jou!_Z??5=z*P5%UdLCTJg6 z*7D9~7I<#ROw9!j06;DQE=AbTxZu@veMFH60JgfXC`XC3pVF5{fMi!e6g&)Wfhew` z@gah(D^Xxg=Xq1xKw~0IzaKn&Vq_uLL?`F!XDL#q6zU7^jXcjU3+dfO!0)F16T8=Z z60Am+XT%=AgYVfu5wruZrzi_n0^LUycF*(o$<17lp2P&ZZ-0C|D6wcQ3w|d50ACy= z)3(3|p-|{QZgSg2$Ac(e&+~ZdJI;(jPR_T2I1IRyix0=AoBq4+)MhkT=&;Ht^ki5baEJhL%6UQcgC`Inqy^R> zW$WTmx6?IrcT-d28OKHd>$$wU4hYuIv$3HN$E1YAk_E(%lYry!~(#Y02Nio=RZ6# z)Z4m|`0xBL9#Fu6U6BXNZ+yNm01ZQ04^H{iI43|CNW`8`eEz-SwTud@0+gt@nBG)0 zsDKhf4+M1N&NvJ_0D%BOdWo@x?Ct z&s0DlgU*T9>-ggl1E>ZDFK@=I`_6Rmy&2dp{Y_3N3}~Ph;86bn=Mc3Vq!==KX=|6h zJ+VMl`CvKj{a*NQ7#$9vQ7NZp@S8l)Xmmj|Af!zs@y<84kYu>w<{a{XNYK&MIf|7m&E*p3{pa!WxYYM~H zAtFhPE`UXf4~`_|@Hf(Fa)GVAjeX{1i6e?lHr~V5@Yte0gFuf40jkAS7q?iZwhyBB zzZ=7HRN~(TA9#Lj;sv%B0Os5u)O+I0ESfoiL;$ZqP`?&~2tga@a}YAJF|h{-SJH5( z$SW`!GDNDtO&GPJG8_Zf0NgtWy)7Kr?$OMc`aSst*AMEIV<-?w|x#4!3=bG7es}dQH5dVqy^4&92B0+5!K4d zg(-SKa-??v)Qi9LDl=WgM-vD$FqCvZVq;pAuLMy7vSh>?tqPwR$#`{>yYNQH6s@lX z{Syv@5w}=m-k~=PrO*J0t(EiNY_P;*pvVme1KHbjT@hA5VP6IwW6i(|AiyX+Do**l z_z49hfecuMuXdAA3Tp>w>#raE;9VFfXN9U`x8U!NYzG3U2(ac7?1OXsIn36t? z)jMRN0@qOba5yoi)u@x;3;7RFr(n@V1X3d#{{YSullQ(& z;NF0uM@%)eVG@*4#-ln5{{Yuqm`a09FR$s<52QAyg)WDN!TENz$i{6rxkl{^D6Pde?r?0oa1aBd3MjBwjPz9(Z{p%Da3aLO2Q&F zDs{p|((?fVspu-6DNd6*pu1s!KHO7V1F{}+G?Vhs;;N*o242cscjukF6$e!m>*TNB z@xjowTf7SCh8Cqe;y9T{3b$J?&-dS_BH2ZL&?Wx>^x<)YPznZ$Z(Z-P_~Q6V1Um|3 zo_J2C^60`+T1cfZw30L8aFhx$K}%vXle&G!oxUT%VX>FqzJ4|+0b;2rC+t#t?s5PY z00adZhJD?1dTxhWXK8D!G$3=!cVMX1FDLl_>%xkre20R^ZM-6GSb{qf<5u#W7=Q}(yV7C{W);LwJX z!2Y;q%NDdBlcT8S-W>)qbV1Pk;{4~w7&-u;>_VX)APo$GAjO78#4?56w z4-VnZ$TMz{eNNPM@4mS4Q;91Of#M1bTnWG!jpG6Zfa6+gcg{Q)84ZU-a_fG3%gLcpO4HQoBxgkHg-{FH z0eelwU-e~b9RytjQ$-wl@9OYf6R;VkhNIfgxM8Jz1BnzY2h6%lLkTVoD7wmAB!&jRS4HvXH&45Mj zQlFt-sDY)l`UL6mKcKJ&FtK|Xyi#Ne#8d}%g@DPf1#61SG^9?Q8Np9X)HXp5>wh`u*UPhnIv|Y>uW^xT zAf9p#45bouTOfT5y9;J&`j3P@%Z2-Q32k9Vu(TyeSDCpz*@0W=qi4HjvO6Ufx2lS?I!siIm2RJEhE80 zCv}M?S!hry6OzwYx1uh9kBj&LW#(k(1zCxnydqk{iyTU_&;^uZ$mY1TBDgUWq)HPz zIpRVC8vLE3*}y>qg+m%394NG}5LK|X`yUt4?~*)u+>opX$z;Re_p=i%Jm|FYW=4tz z%8iPP3h|IeT@~Y4qhrC*lPVgjIv6nMX(>AKr$tzds~0XqQ;DXSvM2=<=2@2Fqj?eF^k0N?)rf)rbE`d`!d;u8Ur3I70@{{VY0 z3l}RN9bxz%&K#fyAZYv__v3)dP%l+OSM%Qmb4g$qJy5^5KYTALpC1qI)bsToH6S9- zk8m~m{{Z)ab)bl%fI`L7qw}w{m8lSxn?rEi)&=~0o&cMHc~Q_i13@)lTuq8IifUd?13j4de>U90{SIEWh}fESO$T> z?X{8JIExj*D!q;O?|ymh?04SJd*dLkGQSdgd+V#sNk%~gAFS?QFUImkDc3|Zz4P%AyVDXpP75>i4uKKIrV(}e;)B)fw*-$@c#fgzV>LvAHu89piRQ(5`zNN@Lh@u z&hBq4k5G1(2g68Z3pp~^1W&Cj9Xcx)> zJmql; z7)4(=1yE#mo$u9i`{L0$6VONJ)8Ff* z0eM?5VyD)6dBzx{S9q*$K9tGuCp(|2*XZAbe+l^Fh>;%*$LfdmKDaGte;WhuMTg${ zaD3f`1dK9vf%LS+<)I>Ogs44;6iMB@ZiYso@Es7}@$U=U1w1RCKX1R+bO4b$buyi! zJX_Uyvj#)uTnE|-yPqKU4uy&6%{33>IW6FUF#!gfXwc($6ws82UW-s7zMb=kUm-0O zSAVCk=a7VmHJ9!2oBSst4S;@x`CSp;it$rRyGwrv&SAsqJaS|5^x6*$kh)My-i;x4 zbaBSuIshEZ^?+Cv;)bzBQBm1ye{}p7y$+htQ}fXL?cL$d0z=;6za<|RuaOjTN1z)* zJbQHZ13MGIR6%R;b_ChV`mHKMI<}kKzT?UD0;5DxuLtbZ`4DM%G4up_240V@K+IJ@ zGz3Vhs*xjH5rOd658xNyGH_^8(^Q~Lss~3$Aewr(gOFCd4*TIpMieWy`@xfS^Nxb5 z0{{kmH0~Amf%IhT1~v?y>zLo+y1e}^H}A`cFrte@E8HJw<=(jf0T~9^MQa_NxMal1 z?vDYfM?5*))DkTNG^{J$)3d0RT#JjHVD+&S;OSqrg6sBXH1pZW3(7yVr`+xR?=)nL zfzgZw2i^#!bO_%-d--YUIBF`Wr$C=bxxKk_mY|TNM!!gZ1^eK6qRK96dwTay3m9`j z&|EAJe(dCPAbujNh*bGeoGB>?LW^-W3o?E1!N?162^H_`)=!8U$^8h#9)F3~ygFJu z=pqxUi~ddxBIpHDi*eSro%2+*MO_^P?rit-oGZ2t)`*I{u@V;see!OoR?6*a=-VKa1&fOyn%~{{YXsO#XW^zz+z_ zSM7E3^I)87ME0pwO?_O#3u+%y)pFQ{QhkbDP zv&22m_sc*BQ29isdUku6yoUn_B&{V{wY@ii5MU873cFR1xbvQ$XK-OXfrF>dBTCkm zum1pfyuB&>4hYykI9{Xg{`~NY8vwob z(7&Je^T{A2(;#ocKj!h3aO7I+__AE@?~Py!M$v$Xh%7#%Ev5DDfv;F}9z1niuSB&L zvKVO(FAUv+Az@B1y@Zf<@m$C*wwjVHCua4)NIJc%NKeVgZ}Tw;veakF-2Od11Htg2 zpS1q~tDKP##OvvKC)@ct2!0Op`FWlS98XE%hV1jc1JGB5C;IQ$>E5EDtI+m=*XQKJ zfsWA)g8sm--s{OQMl!x0zka9l!J>qZX(v%!<$$dhw?^9h-|61@9k_Nr2haZii^YXr zjK7Wd`Tqbuwyrzf8=YrTOegct3c3X*+9)21Vw-Ify{B`LtPAImEi?noedEi78`Trcul4cB zp5Xn8Ua&G7^TGCkJ^>azK_@}-@Xa2DJzKxLhe7)oAy9L=G0ch9eD%r#Z@nmVQ@`GG z2{WNSJAWUK-x-deb?g1lj!+1A5yem^fBvcXj< zpPf#bHTmK{Jw9Q6ef@`gJ*9oS$Ddp%yAP6}CV!5;z2)xKr>l1}>dvt_JGfZM4Wg|1iwD{Aim7} zur+~i7q`cLpzP^~^jv5Nr${;~0=y)G#I%@p`P*Ns!uw%ZLnV4&f&+kr)5x@sLT`1EKR6N}ny=${K+gU`kv>=Q$-<3_4yq1{AW83E zA*^z`0H)E+o|%l~utGP*DT*G4{yZp;Dj=Lk<10+~dLaJ*N7YeaeR;ldg+)~DiiNcn zNUABEY}WuqDio@M2R!<0sR(cgtMNcR(!K8pn)JA!p>jbZHhXa0h!6{-Y2EL9@0%*C zBp4fT^qfgs0a~SW^t!HA;olmrl(IIf`RS4*@bpg6`&aLa+4_-HV4byu`uO9m zMiwtjK^Z-qy=mJozZMB8{VzAF3~1FlPl`*(k`?e6WusU+{4=HlpaOt}?tmy+T0O@{ z+Oj~{_I%H;z91IUzo_i?{6_i7C3kbn3{C+s-tP|Tr57Dt6zi0;m`cbS#f<9Z?Pp51}dwODF zudU=P4%Fz;ufo3@#S4KqNN;0Xr)GC~Z;G`7kLAVb$F3zr&E<}90TB00*OJh+9)r*k zCx3q&!Gtaaws*T}x7?hOaESE#I~6_74$+eCQ52_5Gu{Zn>4?yF;}*t#IGD0V+c~?9M9*6V;^!w4K;^et8A#SK|~s?bo-dCF99F zJ3fl%aW9|imJp!&piARM;vR9{dQ54Q^JCv(vx>`HPCpnS-(U^lgVJ=AfHJc6_`f`A zY{hl1qsba)Oyw$Jo9AmL0}vs{+64^{1Dchq|SnUYv%$Z7d}Uim=>8F?XyRCdp>{b%Le(di%xU?gY& z?xN!o*rTzoiu}I){vI;+oxpeU$5ZEbvzW$fYQRxmzZC5Ek}w8UaPIy5{{T;6#84U` zj-!|G_5Qez*Ue0%bHh9M`n+ue3Va3V!v*pDXBJ^V-1TyD#7>?@C`^q=WYy{{Xx9cqv|c@$Y-TuUUsG{E@$; z@&5pavz>+qT7rq~2twbExVhm?EI$X+tJlMg5!)wjQE=~}t;w7+A-}ez@-GiFl24od z5BlZ>O*hQn-uOR@oVV&D;S$fAo)6~pr%H{zD)^m+_vbGfsn{KV-@iJ(a1WFpl~=F) z=j!YJ9sc^_R?WmnEe?iG-sAb=VU$Eg11@U#2Ac9Esxh?%i|+T=`1hl} zOvp)+8oCVvFWze_&tl|eRiBpp{R^Nh8QR3Gm0)%Vs#H@lk5F+Vy&Q4;kb4G z0Nyrf>9rp2y)2X*%H`xv1q_(tgphGgbxEz`IwR>bVv*CbY?+#yia zqO-+Z;wwN&5kio-W_Z7n6zEQl%B;@ObNq7-8D<{rz%Ks)ry<%Q(kc{{U73F~f?rtL zccFZn(x09L0CfOBz%qH}@>UTj53x)-4*vkYAw4t`F@zq$`t#$G0s@`3u^~_~v^wMn zBO)|R9{&Kd_r;`y4M7K{dpskV$KnZX&|0(e<9_}g*@c9F?80YT^-MW(gbvDtb)`*e zw}6i1v2)AxESbVwz88;u1t0XE}5(fzYFv?006idHW z)OV~*N{E5eqxmn_oHKO(c6%nf4g8_P9)t)~7=ofJZjsEq7VlvMu(2!G_3tkQ-k3%T z0TB^9Ce4hqY|0DB=N5|u@TXkNKo?gEVnR4W!Wmze{0Tm&Inih;V}r9fo`RX#1yjz@}% zv__WVlEGN1`Q(PEQBdAR-;T8PYPOC6IKgBn2c+c1LUwBP)q@kdJ!#ICpopD@ljZCK zCj-D~L2XKs(f4cP4M#z}m4E_LVdIUJMKrvpTm%gKq^QSz7=E3im}KAfV)CmuOBA*oc+2#-)#9y&4o3>i`~ zaVRBk?09Qa7b!yc3EUDAQ@Us+3B#8!1PY7<5TvIAJY-*5_sM{yiUfn%R!>VbvUY*P z6@<>kjr5z}UY|TW(aa)Z;6>F-EO@I)0C5^Iw1=_-!%et5^4y2@Kc|gAmTQ;7eB}PK zP9_dN3W=$-`=`}-t&KxrNAWwk{pXyv;0~FutoaPgJKQ1R*L%ZI%uP&5K3}iToWmzRw?0Dd{{Uya*(&A$dK!oE*Jf}iIuVS=#(DYU*4sl} zFsCcRdRHGEeD8CJDKI4jV#hv{r|8Z+lGG5!h&mJ&y`y;*Ss0-KB)XRGL$4epbvE(n z(qE5yz_3e&BJWW45ij${oP%N&s0bSd9f!v$3{jx6`8t-V+22PePqQK~%0R|!_{Wkn zP$s|#i}q4Sz7YrzFf>uUkqocNz=Oy@5R9NP&e+ZYKt=`#Anl1w`#!kHg%yppo_1${ zuJejo?3Q~2Mo1p#+~x1V>L!&dt_YbpD7MQx+8-~upT2bq8jj10C?TGcI^qH%<<{2* zgVY%wIEuZ9Lh=p=hcRq-h$zb21b23EE6SrSD#zGY(QY(yEXPP72MY&&S;a759N9}R z*9946dcN>3#S6MWm>zfboJ7II=(+bSK0nU^N35RGE_Z$ppNu$S*SUfQD&QT)5#d;4 zH2?>1LFP4H+&EGZy^3~f$>-PA;t9ijHIR1n{{VbS#83_gAs6a(@@v6Qhs7e>(LcTm zErou+>T!e^3dlVf_!qtW@(u!mto(ZUr+>4asJ+TRk>}*!>^kBFCnCY>;D)?b;bT$U zCL!uj?ZS>h3>dW2Hd_StyxE|=MXPby_p|fQI8GpmvAw3mLwNZl4vh-qv1Nep zfT|vZg5`9V%m}W;cFBrYyt!~CVVr7GI^4z=m`Czm}TWE z7U7t{2LV8&T%16R#t=Z!fmaMciM$qABoQ+aQf~D)9xx)8g;w7CK4%PCu(C1^39=+w!x6vHe&O6)Xa@_(X!r;%6El_;r{SyuY-o5cp^0 zqY`(-a@`=(C|XEmB69_h&=bKosjr>)!LQ5i2gQxJ?sd$_leH-t@RutJSKz_xm*EI7Hsq6P{s{X zlwv<~&yFFpI8C`x`RlH^a5YM06bwF4$er=TbwEmy8y9Mg3UyQNN{U6{0Y@(`NumN^ z;XMHlj`}-9?SX)#x$xKX!T^m<)?*nUYtWh(n;~v3#jbh0WuZ>SfQQl@Z}-oTd=!zx zP@~glyuczWLjaF3h9IvO@Bl*0f!kexqG$s`2E&QK6{<=nG$>h9)S`7#ikv3ftQAu* z1652}j8G-bDI(IblBDqMA8KSG8q^99-oOjRAQ2S-S)tN{S?ck;n2-Wh%fD?!E^!?K zV~R!bP#RmlZxUq=8Uco%0=);QFlvd0;(=^U253P%*@Hp4h%TPe;vxJMGz3IVax`KK zjX*8Ourc95l2kH82otyrP{)eN!ylMZ-oL5;9bf%B}D;IsWgk!D*$rK7I+e?Py|6oQ?(Sbss;2UmQI1~WQG(g z!Ht6&OU!dcz*QGs34vqPvYeFD?;p*wikcPB$odZB9 zwtNW*gMd|0H-SMopn{rdKs$^QaS{q`5lGc2LJdd*Yo<& z2&qMHebVsAQj5`2`nr6f%2|q-1gc>CJ!$ttu389z)lY#hUs=rrLqJU>VehPczZ1?N zJ6E7n=jLg~>OTq$p(-Ecxx~{1Mlu$$M_@fhXARJ$9<8B;m4*))7h>0Nc8}^D0aDt} zkfxXk=)Q1l!jaGc(uyZ~a4BY^f_@9xd(O|UXrqN#e zoTEiTk$c~)ye9}EL>TtgdEd{MgTre>sXNeS5A%DXH3FUWqWoMBpU-v_D2wPoC>K-? z!vLU=u+9030jcgeOu(V6aJ- zkz9lX+t4M00|ekEGlW0_*n;%5?ke#v-~yu}WOv)n;GU~tR6zt;Po6-4!uRKcgBKWc{U{{Z)az)T>e@ExCibFMCeNegZWghl%RaAxHkmWmuh z1bRhsZ^W&F&j%s{$pIG!v1mhNk&%NrF_L&_XKN9#(R$qA2o^y76up1znF6nVkC~#8 zMG!-v1!WOdiAF&#ZRNrvIxcn-rn7hDI7^&ht!DsQ0Rx>dD7D=|Tm+Mko!bDNZ(%5l zNr!|sT1|EamWGh-o;!4;%pz<8kDfHmCW@$~5Du_|W-l0RWQRg@LkLp891-jp3CeD1wK!fH!llJdD%H8l)6EEl7g|60FpoO) z7N8`h8lecI(11WMUu73ra@D$a-M=4(nX~iJ43WO7p>scDhL=M0w9afI=opSYDuYmP*Vzo zY&Q0V6x^wzslpktZ?P%RK#Hh=j|em6cThJC34y`IdqT0Xol78-x`{z_S_Ylrt1tj- zX#8OG;zJ~DYNaNPte7t-NVOW7vXmOl;3`^w^1{Ye;Y@u%dx2@Y>lMH>D(8I!AZcD|P{#K2 zlhP^BO1=haxF0-J2)KpRXsSfFC!A(r7#N!i(A0fCO-2B?=naPYbp_tuB%ta->g@PD zNaxoAML~g9N3P}9)0UH9C9T#TX1knFnzNc}cICs;go)lC|06DyR@vw@EF z2vFD2PqXsz@1aG4jr2j^Q~8$I62_xscwg1)hjj%}ROlT#%kkF}ci1FYNd6Lu7-uX7 zC7np50HG+>)<(nK1egjbqCHLktd}DKAkgn{wsHw-)vL)K@CWyGdV_)Vl)b>b^)s3ZxSNfDWFAJ zU>t)9Q=(xK{U1GJe*(9uNmeh)Zgu<4JLq1J2=>XQcCG2_jGzZ%6=5SK^Uppw!8;K6 zRG4S*ztO-N0fMrN0?LK^;Z#5s5FV8b=cT-TAVI!*elBv|QP=nu{^!4#IO%V8Vbo?O z+~UBl85oc*udl*RRfqoUX|cdKF>>=la48^)6cFnNPB_ki{#)zz)bEnO*eDQ%Pzvq% zj~M{~_|0cpsk%=&lG&dVEJP`Gq^cq;+@vavJ~BOdfM>vqF?CBYOYZQfgoe0Or_Aa1 zIEkN9aIx4Mq-W0$XB7^&M*};V_;|?8g;!Srq#@+{aJ>ReIu;@6eR}+yz!9;J6@DkT zIN~74fPw(~5{~v4hg7ZwjN?%QY=@kb;)E+)UD4C*<5-BDd)z(v+5Ek8MIjI+0-h5e zUySNaSQq%u$8VpGK?KsA(nrp9^7q8NSzBgS@l;Q##4^0R0)^na_6NJaqqu(p_j~=} z?;DX%uiNqcU#@90KF`l3cnXZCi1_VIpA!u`r6Ws*9~VX2$g5AJ zhW-UJE)9k7ix(e2iF!j-VIP*DzvQL zT%!oA05Tm+r+hRqDjKK`>Lb>PnS{~~9jc=YoBQ*GT^_nF0b@$hJ#bDk9*7SmS)yq> z;##nPcZN>*2`ZeXcmy2j6Ii^4@xj{o4blk~{HBwMyi(54{F0&vd?DJ9l!U&{eM8rt zw}jNo=rppV{K~x2QH>eXX&;H+zU2Zba5H4A?04bihg%Q`>Ao`bUmgW?&-XsTJM(=U zS@_L{R0i*^7mU5sp@bBK*r_T}46VH&gT{;&cyrbguxJ1R&=46;ShVRZqxOkj zQJb{xgj%ltxemFE(M1D;-&7PUzYV~S;i~QLt|(Rl0W_ij06BgHA&FSWFlTxSi1y%l z5Re3#1PdKv`$bvg4KR&Dr$pa)KuyVwadp$>LvI7{)(6BAF#5gY!nl=~r52BEzjxmA zG{H#O0ost~u;gW4Qwb0YQGV7OM4n$>-65N1CJOPX$WUg42W zM2fjA(|;f^AT?Ve9H})3z6O!*p*31kXvhFSLy-7NVq+Fr8Lc*Jqy@^X^9J$WwxGI3B){FVUqLzfLb4`lv3%h`^^ z4Vbdl`?HpfkQ!@u#Q67_(Qyh-m*0~Q&m_)Ny}A(kl_c91@~!gCCtzWv()!`x^Qa(v zDs9o5-n=yg02)R!u7{)FdvuxEprja9#$Kz$y-iPEwY{fbh2sS^_(FsIz5V`;vPy;8 zetu_HC)WX@+5_vVVe|9vB&VPsXbky#d${p6M}j2>toz`Db|g}|4b->K)PzN@Qj{>j z>FwX-5$HhezRPJL)87M7Kq$j z1Rst&iDv#=zpne}&R`BiZhOWr_{w~KKEM0CbHp$-&~6=dDfib3fWnV`M11L=zn&G% zm_2CO*-zi!4a5-8L&5U&dfVeNEfFR+U*3Lrhzc|SG_4!c=g*#n2`GnT2kgPW9IPTZ zm#&`<{{A>hvmjulGtQHL_l=35q9JLYko^AuJTkHs@i3x%RDLPb-xgB2;BumE>-zJD zs{nv{v!gWLvx>u_MU92;+oQXOH&}BhD9>Z3PRP|MSsNfN0;n-2xyD)}<7iR>tF#8Z zxzV7?UdffPYcw^POmjlXK?YDBHY8w>BR^FNHQmPM#g@PkOYisQ;Jy0<8XSm z2Vr~5Q#2-f%67Go62B&IMZY`6stANBH3@bmPP*O41A0TaV&K-DPnie-e2T6uSFQg5 zLWy~N4+VyrCdaRM;?spMu@xq-Eti@K+84J_IUJ+&bAmQX;81r}JH5#LaZJUh$plwQ zHdj`0(8?7^W7kK$TeLI zZ9s_%A}SE7`NHp1`EZN%0>V$y1qtknEhT$A%M9Qu`j|?-Mm?Pu%ItKaf}IB0-J|DR zpzT7vjD5i$+406MVFipNEYTGtdt<<;g-fnuDx|0;{%2@Sle|AE=J?6@;EhH!SyzPG zKGuQHWQ0X3lUzm2c_>4}H((av3Q0m_4sWV7)}+9Yf4zs1y=$Xc8KMI6XatQFKn;rY zBsynYVxTB=sp?7AhqJC@1}*~H3PZYxzkQswvDeW68f@%8Cj~!72j-|6X#BjFsR0pV z031lc zPY}?6n92yj_fY%54Zz$htb6h1*NKfQpgQ~Cr{8_v0u+Lh4wWG$i~93{>`{VM*p82# z@4LdHoI(fAwZB8e4c#oG_&hR&(D{#ali!XZIH3<=P9#3BeL2U^5(0t2pHtw4@11p4 z37~|FKJV*@z1=6p5i68b>)r;KWauL;3t83t{3nAr3Q`hIxOGfY`LF~#a{=aB@! zlq7egtyTCVyd*)X8Uf~-r^lRXp(v_p+xaB>o;)H5gf+L|dim6vZxV?~ z$)-OV@0YKh7AXJ(4@P+r>>sJabs}PSe%~J%)^Lc(y^eQDVEA1}kZ&d$6KU!!Gk-j} zTEJM1AO_gF8N;}bK#f3$LP*B)>Tn_mwjXta?tF8-2t@-ec9XiiGc0qt!`^n-PhWQg z(Z6Wrv1{gAyg^{CKMsj6$@uti1P~&CUdwGy=ay^t{{1u2+i|=fut6i+pq74kN2rz+w)fF4L&LrU%A3+ysz{7Z_>T!A6tOXd z6koS!!JvvpoInKvsoU#VC-NhBh6KppYeO^av5)Hoh|sXbV)T;+tzJ3`7fRp#&H_w7FGef+ZzV6L`#bOzErTi5{)s zICTIwS`haXR1a`4NMrDAbima}aD^R#16KhmL@vS{R967*A=GW?!! zQXr~C&yoN>6#n?|$Et;-9oJY6w~HIFL{O`R1#w=&^~J4ZAdZ;q`S|_$!m5Z0sJYvp z-}S-L1f+p!+*dII`0)-9GjP7f3)1;%!!CzJcO#zxZ(kRVN&#qqP^hVB6?7@eYblfq zpqGmf-FY_*Qz^R?ptH3J!RayFFlgWe#Kc3W@R>9Uh_YJOre1%1e+$dhb1) zE5oA5<(rfW4L9Hbfiw>HQxecgI#9D{4oxwz+Hm46z!^?BdU4|(2o!_`NYh{lKDSSP zmjuCNrmzU%fNPr6CX1027v3Fp6ema9Hvwu#C3_=Di3C45J>2GJ%s}mHgld2_oJf_k z$P?6n^uj6+WJdTh5hH6oF}xATgfP-TXp7RrzIF!prXo}tTqtA06beG5ssQSw5V#1$ z>8y|1Agu-C!vsPP~Gb=%W<-Mla&h#wFFJSl}w!4=6cz$sEQo!)>BNpJ-M z%97Qza*mWE87UO1F3uy`kijQ3PBi%HNmT;G>L*F^kLF0K0uMu{d&7qX2qgqi+8YtH zZ#w?~0#nvBgYYNy$03~6Ps~M(uoZcu%-dF#2e5kv@Z%f?M1qMr1xa`vAwvu%iAzz3 zyavWV<4{2#$ve|RI}!;IpuhnVlin>pa|Q?$NC?6T0P z1k@B}oJ_4L2pbS=6m|vRWs}$@%QUJ0OdvTq|A3*;RQ+m8zMutft5 zE{Rb1?(JOK%h8!{**2Xms(fT0fv2if&V`^O?IV-1l5d!VP@2j!)a zN=i!Ez2;oV5akwvfI%dB;R1;}-y<3o)`fTB<)3I)L`+IQu%5GqHh>foz7#yp{ytSf zplOPOy||9gC3A#%VV@#v1Y%y!iKi;SfXG1wk+%q3;H3o-MMCzKe_Qm~jbKJqQ_0c% z@Qnw1MRDKb&M(Oy;4}ODe4J$M4yc+or3MbQVT(g&P4|H%8&nM{K>+J$m&M)GksS;$kO(o(KD~g$QBSQ; zh-PmKq6!eQk4oQ70`krVSSkrZoBGc0&DzgpKtn|Kc6oRgfq_C!jYG=MJm44UdLaTZ zqILo2&k7sWeiG0`@Al?L^NGK)N9{i=sI^#+uAAaNID6&bqu>EKmLcK5npyz`EFH$e ztcQGaUp5bJ@E7x&*BL~ISFL%umERr)fT#l8J(jP-53cyJMDf}a>m}LW%K;?-u8YMAvBw%8-iu@gH+#BN~$Q73XGQZlO`6K;rH6-BA12h2^C`CU|WP3&{e5aLWm?RnEGbs%`2b^AROaT zwy|L%A4DKUD`ewmY6Yh$a1Kk?h#@L890R_>Zxr!0EL0+iqACO8aV5j!r=bGr3x=c3 z6AP-E%=CZ_t2qAvDyrBjro-ur;hbUO12mU6y=boE*BFr#(RI!JnLGOA`jm=E15pM( zPPqD3B07Mi@BzTk>x(W%tjZD)AjdlFL*pW_fmhl|Fce14Jc@ieL%j|88}Y*ufPI8B z7KZw0BOz)?hb(U@lMfuEl{y0BcUzC2opj70D-2=NlOr5Ftv(cykAdIAl0N*Mkrn4 z#^lsOLp{pWBiqj4@jxme5l{{zk9<6!WTn`%;P#N9rz9|t1`Hpd0igBm5K&wOFo9iw zft0hB*Klf~QYH38x!1f+NxYaM1QW6bzi?@;q>Noow(q|ut03qlrdLc%-~G;88himq z>6mH2wBQXBlcYr@>J+8Zoo!UabOQ&X_^urFB`k6xP#mN<-Q|FnKnMdwgh_8AH-L7m zL+gkj-i|6`z^oe48Skfj_9Do9xM_t!s0KN>7@BJ_(8#WZ*9ijQfJ5gAD+CsDgudlm zevo6}@U@iyQh>oeM6^71h+>HZH*T6~)gEDG#hfHbG9bywLM2|or=rEiB3=R(d3VqH zMbMgZRIBt4y#&)%6%RP?>O=^@#Cn3g<~GvC0#WG35#-6YWx|B4;%8)$`eI8 zG$o80RpLoD4>3_pqSr_zh{}MY5d#5q5_Q0U@|qkYu*R@K_l^UA%|vu)h-SOq98#;O zLlF$YDY+d*2@(K$0S>Af&Ea!`B+RjR7j+CAngrZjgM}kUexroTB*K$qP&$yI5#w?; z@GiRw6@_p~1sHJ~1QUW>Lb^axv=kf|oEnjck5Q;LmN9W}09fknCyE#QelBpt?qOmN zix3=C`I)k&_3B2pPnpP)ib61eRSbTk;9qBTVBF5bS7giDtw0x7FnQA`^OEJYN6 z2{4C7@YU#vaSas>1RJaOgho9GtSpbUpPpz+B#ehZPb=*F3zD%UFlxvH)I$6PSE!$Y zj^<~mMdSxjMkh+(dHD))l~<_2DNvKuiGuJQkl@kAtN=8Gmm#4QPV*Ah=sui=!{+M~ zeGnM9;B{h%nt;O+EM4J|iacu2TLcUno2^i+fs%>nMCZpcL^MLc$f7p#sjR{x$K@rYY2R zeV6Ylvtaf@JMGthnb#H^?t`XjL6$>f)E+DgAh%lNFV|>s%F0_uu@CTn_Q~v|8AS>d zuq-KWMdJz%*@)OFg{KM+3LwCN6_SIbj6o#MWVnq66MDBJ6Z_;4(-jhkLS$aDlXxzI zB>00qnQK|RNPPJyv_TCH%omprc!Cgypn;&EGM^}>z8e6$Sefhoc(l<#xPBEI*_pH1 z;Y?Igg(wOt?wso>iNfKeDSH7Fk>Dw`AdnhMQ?%3Fz&pdN5qv40mFvCAf?+jAD8>zT zXgSwAXsXO3qSvp@(}oEubnM@~e!b2}k6|ul%ZK>i-xeRRyJr3W0A=4fuqaNAm*d!9 zK4;#PDgd28V!sz{Aa-xpUtRJ*fLpiHb=G|FzETG|{{RI%yVhP8U?PV=XJ&SWH}lBi zBt+~x(>r>m8z9ojKC|!PM{nrvkz0udB9X0v!!$B?)2lD>_C+mgei0>nf zboBW)@pYPF%8-aX1XroT=|TDkS0nc)0yG%UP*Y>Kcn={Qz5{B@g^1Ijk|EQv zxnly6Stf*tvLrxNiP3ezvx3kda55X&htFN{_UB;KVN(Gi4uLso=|~ByG(8?aISByh zpzZc#jc)qFNI(a)ZKMQ6?!Oa?29o4R9igH{BYj4~{nrB&sa%j0gS%Rto7aCa;uQ@8 zS$6IDt-I$EEr36@GA6b8&(7fyQc5vC?S|*tJv3Y-GJ7%$0B0^(1XFlX8fiM}J@R4z z#J{=s-BXFd1wbJ^AMq#gytvq4EFd2MrDj6$;D-eVA*Mm_8V3}%=dG}8cf*jcwdR+FR#cpwrR)N9VWTqdU{CSLZ-~fV0)`9?mao4Q>-2`+h%seF6YN zSyoRu{J21ng$5$!y}uvlf#?apk?-&weQ+(;K#1=(3)k44@`7|qLN&r7r)3_v$bft` z(D;ZdIuhR;N||d5nJThsqjW z`(_jhr%;ZEMm2|JH;mT;2%wmvuSGZ*QvurE1g~&@nWL0a${)?$M4LHJdt_osW(TPu zdmaM-F*U$2x0JIuy{(OrVyX{l)4UdEWdj@Zf{~t_R6`a8Wnmg2v1H-P1eX4s2LV~0 z_LJ5nZU9*eNIO34+)6c12-ncX`Jj>-!G3U4ww8QxwEPA!O`e#ubMJ@xH&bwBr(g!t zBn}u$F)EXKqNl)_$f$v#SnTXmVXQdVY+(V;?N~hcVX!37SL>+TbghMpv)ALNEuo8 zr#mag{Xl-l!tWfhkOO9z6)@4A*M*29hgt;qKDdDJn1<8L8jwFZ!E+Ur^EIMCD)*-d zi4f2c9LpYu-bx`LBMHST7f)TBK@^77;1B?t^ykkc8~*_G=xgHickf)&A-I4SS#*$> zw}8;-$N@)fbk;A=AETRSH33f6=5@Stv_fDj$jTN@yyT6)3!{T_W_c8H%^;uS1AhrP z4^@=NLW>e|(7zKhcr3U<1n~{zEcYtz4h!0qdE^8E*-DXEbWdMgYz)jK(+Gh0N8Tnx z&4r<`D05NY6quw^8VZ9$(DLx)IhrVgfMgYC*Byg^y9x7wosPB~r7&pXrU9W^>ha>O zgO+sAiaJ!;$t=l$9UlAX`|pazwxiv#Zp-)clbbE-$O=)0ofvvp>vRGhrZfzz062Vm zI>48xynpkAxzb$4#~JJOKEfOfBos?ouxs#en-zq<5>KqNy*HN&5DSeIz*2txIRYO( z2(~~pU(n|b2GEQns4b6VGK=xc6fA@ci1+nFZ`CgiCrX~lexGLl0Ph4E0d#}xA5$2A zJV8jSMw3WDn-^MMB+4qRQ40#{ygxvN&gaO#%dWfSj3~wt^7+@CMU|o{bKQV{gzJ)` zAOu5Ea?<_|O(KY)>JMR!-jO1ZGL|whSrImY)vc}-NeK;##GwBG>j!|^a6ssBj*Wg< zItB<(P(+VQRFdz!ut?PqRp@~kJ3X8=);Jb|S)>zs`rr#FyMw;p%X9o!omF$LkH=^K z08U7N=q{koI$7TrO#tZPmX`J337wDIKd=7)GvkZbuJ)Arz5IT8(7?gn0_eJj40K|| z@ftl-{O8XKv2VE`E50FrI$-dY@opr+O+HiW=JPv!gcrm0Ki2tL`VejHq}2m za3tlKRSD2t1&+8BRQ>SlWfYUJcmN3*qWhCRWBd1oPN`YNCZdWgp?lz+o3QZ>mpDS_ zF)@Fdn`9kH0O8>nJ}C}INP>#|7U=7XH)znZ@3i8+GCdLv<|6W&44 z==3OQQc;B7bWwORSnPrbum=`O`S<#I??6P=Av(2_ z${M=j7}{#&=1^j6kr}U%!M>*hsk0VTz;QJ(LG9%pB~BaSOf0EFnz$L^-NHLph%u)m z;si0R3Vs@!+sk`<_wgavN;AgrwTpT|C=|h1CvIs=d?qR%245LAL04-m6*GCU61cox; z+bX%8@~@ammB0!i^K;HXDY%ak2=@cI-<$`wClIK^YKeo>-kR=4I0=T_?YnK_C?*Pq zmH7{oF1@@RrA!D#bU?tp72vZ%!WA)hpcBzLbCea{K=e1F_vyeogm72~)R^k@^~=b* z3W9>@3sn<892B%w)LjNe`%~`$^6Kh8Lnws^;e3b7u+mdLjQHLqNh`t<5Cj;Rcn`v{ zZLF%0Ds&D{QHe5$=K?4}c!d7|g8(C3K)b=Xw|D*RW1EabzHx0hTY5Pu7HQ)@7&sVj zf)HFNH;PoiF?CA;7TCVNoajU-dWeFH7ejZ5G3N7BDp@4TOY5f}Sf1veVAvXq~Fyq?_l-Y+13Q%6}inRyTWZ+aA zV4SE0(zo+YC@#IaC|Lr%dG*A?ofHFcBH^h8c5IClA|Di*#q1>EmMkgc5kZ7)Uojj$ zy?_cxbjW}Rj)qiR2vxH~R(c8ZdA-d5(uFeX#*+=<6NU=+$bzMh!O4ITAcRK&DRpqE zxYU3^C}7cI#DpxchtkY}Y^X;-JU4o|2%2}_9tS09wS(eNAw)Dh0M|4!&|c;ZN?n{$ zgo3h)kH8gs;9@ulPLrwkEP(&I--K1t4CI8{IU=N zLxNPmsv)@G>D2XMk#w~xQ{#-W2C|)^#2LM_W&^K@tLl4%4HbzFsT9 zl*DN(`JaC=9-5EK6kSqCHu=Ck#0w32>dnv3B?{|lnfN;Y0PkEFj*PuopDVxD1Zg>-5kKE^-b!V#d^W>X-IN(y7FUcPvS@pfRh0T?u+2QF~aStx~Kh<9YU@Vo#KH1stN3ik?} zo=SirFQyWsDlx$z)ajs#Ag*JZA=4QRalwK|h8#>N<cMqeL>Z~Og*#~hCnCrigc>er1=|Dy?nIj|)&nY#VbCjjJ{O_l z8O9=ss`ox+bj~P%N$FCOgXggIuOu?7uI!>acpYxMI8_Y>C5vAG1rOlv*?0FKpfOg7E(2=8(2<+E~3Hh>Ep>{vL*r7VcgKXL_^^)?O|*S$r~?8ie*J~ zH-by<4G={JyYG`P2RN)mRSLcV=Cg!{n-yNw#4MTuup2NMoa3tv{|#O04(M?mhXLbIviV=pV3cOW^ux#1>R%sX)Tp0E8aaakRC-E)be!+ zfeF~)4Q~ym6a()Fh$4zg^A( zMz|ru0WQV+CkGOB6Dc625x5$6;df6h2;HN?cyyt}D1c2u5SUN~0eDt)gy{tq($ufr z^(cgAAEe_CG77$;U4!Zj?B-FDM$dk97{XUM zNb)pjTN1?t>^08+06ERH2%D$~Y;r6U6tk>4kt3E$rOG-q2uIQtBn07C5~I-%3X-_PI1#ZF8c!gXYSCuDYhzJ1}5nE}TZZymdM~Dayi3!6u zQ`;85#NCq!I$i`txdI5M5f3k_i@!}H7ET}_2$V>4C?ElW281Xgz^x(Rh^avs6MJ9xP!~%n6S#6$AjF zQ7j74vqJ(buu`?OZ4q-cmEo3{5bs7;LPqfGP>NK5)**lviOC3N>0FCC0|(vYG7$+` z-qA8W)j2&uq!J8e1xatmLFt2g*>ogtMOD+5O&UH)dJYi&cgVmVLJG8$%r$Ifg~e8* zyITM>>HN0?xL?GiI8Mg1;UznqM$;%E*E#~fE<~fUweCa;1YXYP2DOKEX_jJjwk1UdOsW^m+Fsd*m zhrkkCcyI%8ouLkceZZW@#kK;NWI@uF#UsJouqIF@K!9R4qbtZ{cBo*zOUvQcygzXV zLWZemBud3Ml!_-%LZ2uhL7Dt;~fwfMGAn&V2s9< z(ENe0c*>;I!j%xx%sHg4PRkX=Z=Ywr%f)I%aJZ^1s#oP#8UoS<5)4EXHKX!x1_5+6 zCBBDGgX@_EDihJsurz(FcxM{cs9X8lRS2a!o!%mTHLJ8n$WHCa;rMF$bjO7Y4 zBqg^~uEEatIfj3czO@4FxN}R$Ad61rKBRV!2zV6TVI+VsW{E@qu{jDl^BY+xvLnQ_H_a1GZQP1^`G1Z&2|+ zwf@2?pmniidE4{T=Ne%)q(WnRO#b`gScou_(h6CF_~P{vqP1jpqoQfDJVFwzrK=D` zrUL5=!}Jp-ltc;8l-C3v5KOEP{{RU*8MGwnszs>Q{TwK0!P%FfUs?B`e8d@mA3`#$Pme<0 zR2n7|CTU8m`6u5PkWv6y)x|!K{{T1~i)(XH+Wa-0ByS#;5r6uH{{Z)cRRjXC3|go* z8N6nUN-HQZs?)vWfEVm&cu3NG3Db$wL5zXs?E3gONN^r9l6nH3hrLpL@yTmq8iM(i z>b#sFRztd#?2M_MoFfVJN<<4Qo!y*Cb&*<*=-dxc-eF7>R7D2#G@&Hn#NU)Pw6+94 zL?{Z10+4t>m@|W9dr1wWYb6OO?81h%6gj8_5~#*OQJCXQhVUoP4Z}eOpc0x*+OP+N zQ3>1yD-}aVLUsh@NJya&p+Za1`wK0GC`pPD1qpNB5)71dfUk}7dpOb$BKYdT7L3_{I| z#)GAK_>e+U2jWXGnxw?W2o4Gq!?-Azf`VRv><3`bl~)57kK>eR+7b)~_<#sOMS}@6 zlnNli4k;_e0d^oihh!$ketZWsGO859w*w0W1IpEl(+whu-|vf{3*80LisNz4Z31X2 zgnbx%oy_ADvgS0yN3Zteg6SxMWDHCmng;=rs@+3;c7z56 zD9ZJCB+#4)lBi-!LZ#F^V)57k6lP_2uH8Sv zH1L!)u14`iXsDp5x+a}c=Kwwxh2-I4f>IRfC7bv)5Gnwv3C)b!XcgL5=G!uH`oJRW zv?LuTXP*HbCrb64DaWYwbAV7rOOdD(Ljcf8%4)?IX26V1pJ*oV0)!5dKx-TY;5#;i z4_Gft{{ZWmjfAj%lr+x4%KUP;3Q+{WPYA_cx~vif0PkMNF_lyD=5ZCk14s%bZ$e{X z!lD6(vGT{hwjHMf&hNBo`s{NrU7fxiV|a&&0+{6er1EtY}hl-)_*A1bSb?*WVa}6+VWQ_V-7>pLswOpDFe6_Twg? z$pl}D0O_33=Ok@_ZtJXi_55-Hlh}wc?)UKj06rYLAOM|$ z*)Ljr-e!A}Dq?SsUib0CQFSeJEfar*`rur~j~6BT!LM?Yk^Nsf+W;vVc1|uSfH16} zesb7BsFGMTiBuw6+tT^(F93@*P^$tVhSJ|ZJg(@qj8#u?p%pR%L)!%mKB(x6hx5oC z(g8#+B2RsOc@NMD8t$iNKmI%tx-G&B5ViwJI%y@61hQkAW)*rsd*&`0nRJTKnG83C zBF$bQV3aKa)c6A6znve0XBZ`CT|ExL&fj#&N4_HnFV9K25B=jxgnAoSD!bUQ z-ZaPy(lWUZzt_(c1f-Rq2l7+k--nI>7}i^_-&x$?xQ;A8gJt!N&QSFNDg#;tAD;gJ zk%jS4b3P7#8t2L|xMczTdES1vUTHv^OM`lKCtRZp*eXUL@A&@tSWY%Z8p>85fRV zL59Mgk5T#g?$g;Bwb56xQPNG}B*BzLTEdz&*l-!Fj+W$u8j*YJzI;50glk^cC&yeU zEDmLBMhJlm9Q6)T5QkQ{PT?iq=Lnc;z$9R8chnrShGPOs9Tj12u(O&6stbJ}7fk;E zSCjPG7qM3EfW(dXaHbTZK@Kwq&i$`1AH8k(o05RgqChB-fTNO)Z2*T#20(v& zzR;skFc=^>ppU5V^Jj3(g^DXNPfYKoME9pYc>+6vr=oIJ9ykDk_mCuDfmhi>mE?yF z1AvaeyQXm5g+$sLAsT5%$1_cTDQx;84}V6v24c`5=%RWF)7jx5pvr_n0FH}m9uI=R zZh&O&T)$JAT~xhg00kyB?l$Qq9t<49F1sGMArV0xzZ@z{7zd*0b(Xdov~jzBJ>6`OOwb`zbt)xH&=m{LK)Vq^Rl-eALS7D&&r_@d_s4LQdL)ceMPWc`0km%u{;XXZ zhK;bmrzlBUf~^LW3yb7;%L)U83=7x=$|~W^B%uHfkaE+r54VQXPJ<1n?>yA~aL`A0 zafd_bPUn2LoYq!>F+tEqH_jRbaMlB)gw<}3$qHE-wW(0K6Yq^T=`k(`Ac3XJ3PzF? zr4V`$5h4MBq6O)KQ43vfI;Aj87i#d}j#W?)Kr0lHT1X(}=@ebkXyJXi5;+I}<0HgOFp_g-tPz7}-3DIMQXU*qt@1Q|t%RZ?T$eDKbyYOoVnsEf zTanlfxN+Dcid0ZCq%Lmqh? zV3%iXC%avo=v1{p!L&v!hKtG7ph)Nq?$|dY<9O>vU?3P*JJvt0xTg@OU6eK^0Qn!D z4kpDG@&jZSCj4;zyo!VgRqPS~N5_sgii(cYl8RmLp5{nOr4Gmr zO+HZbjYleBejS~4{rx;SM_M8VeXmD-es~9>O&W~Md+&GemJ5c4ED8^cF`N@uv3C8C zzx(;*pnag^5)lg3oo_mPz^!R%lkh)Ib;YN$H=q<-^*ZYOyA*k} zZGsX7Dw$52{B;SPfil8DhJD?fJZG>{LD|2Mj(Op%s$3yfxq#AWK}>3Z?sh1f=05RD zh0dF>4Xb(`OUk^)?^m=37-eV9;7SVIPXyk!w8l*2y`Z5Y{i`3Nx$(g|DC%4`ldg}P zcrrndS}kj}I?m(szzG(M`3(E?KRX;_`o-`Mn*885fV>Y=JOdKAOs{p0)2c=0p*HopgYRt zDVh5w z6Lw)JA31y%7NltfQDI1OM%khup*D#F(s8H(1_FQ`N~W3V$bz9l0H_zHH%jo9;lggH zDH?Z}8gC9{P=Z>4DN&sgq*zqmG0eIISFG#1${&n7$|ZL~QZh?cpIGJ!O>}oI=3=$F zz`{$YBA`q^VF^LNJ=i$YtStdiQY~qV)>kY6XbA%g16VEk;i%&Y0wF1W8jlr!JOveq zG9DRj@kFeK8SWqq4+YLGG2A2$B9Zr?RH(d|wk16w?G+j5;;$$?BBwMa5MT^8Pw~C9-0Zn}OmJ_fe)a-3Wr6Gu!S8?gx zG%)E$NHc&G@)uCto*|TE#<7Dgq&VZ1(Wu!RssRHz$J+~arv!ee+T+pf$P(zb)`msE zjkh@3LJ}$!(O|n^9D#dKr4*o!rIoHs)M*Gc2Vf==@XV2A2==1)ob@poC<8#2HP_3X z%ULWja_YivZ+yo#!4S5#aY(OA!TO~WTHu5llvq4%^(Pd3j?2>z-+@95V6fAnwJDR^ z7Lg^;32?Bv1xZf$jD=mBvAm2g6A8jrr_hB2eQYGTBSQSYj0j8k{E0bQFAR@D493@1 ze1R>%rorZ_FX%R*!8&ph8X+o*=*#vy3TJDQf{aWH%pO290}ucZb1HP6TN4M6bQE%h zRC~j`-W+12OLS=HW$jH_#mqs{gfnHgwk(b-k<1fkPdESbTfm9FmiQB|y~$#(!DH z6IF!ML|(=&Uyp|gJ1HY~OZo2pH~d+BG9B5%VnN; z{eE{)7_MULKlML6Y|vjeAYy*nf&1b_5z-uz8k(6t=WiEqzC{HCX(b+)&E71UBrDa| zN!Rb|l7+kcJ?iFZ{{Y?&swlI4bQu0Wu25(m>WE?`wijJCi&V2-vDEDK=n$fDWKJaE#(;l z`mQE~8uh2Va{=itFv$@COvdt0Q9_jkLFr@QN3;Z@0VaovgSfMzbd#CR9UX3m33lS9{Cjfpep(Vz$A2bJPSi^^)nrx z9~+0?5HQ%tEsx;(yh3axTOf<3&*i)%b=LSJJ4-7s_?*+oHTDT+O1Bq1+xZMq+kHEx$g}p@Bjn?CA8;$a{^GR zCA+7<=Nf$ZT}AEc^bCk~ez;Bmpt14BC$`#kI3&;^@d%&<7T34- zuC#YnpB`Da4IYjY9jw?12k~CdR}8!I)PXuHT6ZK>B*2$St6b807aj0|Z(fYOJy`=FF`kK}MALwDS|gJVQWZ8t4u4vBLd;=p8%{;fDNm%f(2d*1gK^ z8FY9Si^H_ypa6&x1SUQsbb(?)t@sek z7T{_shz_SwbSPWeovZ{Gii$@LxWW%ckOyh>b{`*P^rrY-h+ah22NMd=YJdU-2!x_4 zh=Itg6D*5-(||}%YtUnKQKVxh6PG3kI>4qh2-+(6U(YUzAQw}^pvj7t#z!GCul;dSG z6-x}-sfFU?kC+s?BNt=B&Q&wG2+^qkcLDc%I zfuK^r+sBbqrhuR@tpXB;P=ah_Bu+6m8kTyZMVOskE`q;_(Xa812ounCd|^ zSc!qASA|8+c`L09R^kjQWJG#f0BBe!r+icgvek%CgT!B+9vs2d9)>+hBGz)#(9{4S zMMBA5!^1^LsNJYbC;&nirEJN{M5$8eL6=_OVabshl^WoEq-yF&SeX6LwN^lM6g0fb z&n5MdAqgR==nzq(B63(m@=B#3wHGYGoLW~Qt)?Dt;o97c6bceMBBfHcZVVP6gM5im zPjnd4hAFW`sjJ{6mUAJ(BsRMi(aNDoASz1z8xaA3!Cwei%s?)!^mKZ-MLbag%?1m5 zwc+<6ejb$o7K&F?yuWVy&_G!r5cX#3KGmBIUc$7fzmIuaVGGcg1Q?>(Vb>G|1rFw} z&jBQuuY9%V+<-J*!fT#qyas^5AW_lTTyf_WuSC%rHHzUDOShNlxM=_;uE<%Zz5{Z* z!0E$21&Zoc9*TsK4b4FKc{rUL7%fpcfa*4lAU!ECV4|uSdrR}j0O=l#hGlp?LUXat z1hn-c3=vSlI)$1g84pqKyz8ywWJ`rprkie0&lpP-BLPSd3@$r*MPP;vbD;CQ z9D0MwQiJGwz&45mRceX7pTT)(;(ibq=(2}VH-(&tLLfqrR)nhn5Kk_Vx*K8G0G?}o zXntK7?i=yV<1eV1D}zPtAD=X+Va0Xc?abegI12ST@2GqnstfGx@`^L^{cb=aa_*b* zS<+pZy>KNN64_6fek8<>b1QhcOXj~l$@}022cx|Enbt~`<97^3B`-#?N&EMJA(1Qs zgzmC;(_&^MfDCppx2Ru^{{T0xJRm@gbquF{JNL_K7(JI~Q#<({1*jooijksz$GrGz z>XNO|dSW{pUZnV5M2p;42XXks#^tg zI{D!RtN;YMhz8GG(P#+2QnIN&1iv)bjv5Ta7AcZxc6!7VnvrXCXav;S{UGc>BB8^nLy3I;5sjcz3PO zIgzAaa;Yck+wa#Rc|x7RH{SkX@x5>c6iqac*1rtd-zZj=Na?5IqpzP%MWhIx(#L*# z{2AvF9$Qh}&x2?4$*!%~-?d!PK|E+mzYu#!VOJL{Ps5Tevprg(sA z$(%t_2?ymCvUN|6k7FhHQ@@{|slp+xK?o*@8~fu+3@tAlfgr7YS( zK1Du&YB<|1jt|u|u^UcVre4=VptXux?c^kT6*ZcaPQc^NF&e@Z8fFz|*RCd^#B?qK zhL-m}5~2XC06q>b0*5Zl1yliQvlqkbksu--(!9m+enTF6K`}_U^9l(iIb8)3KrEh- zCA7Tg<*cx26zoJ)7m*JzOsz%2`!x$WVnR&dgGES#s_n#<>k)S0QAjB!_m^!QnSd*4 zj8%%^U7=y%1R$U&Zy5~0S%(3YPy)w-DDsM@gFULrB7y=sE!jo&&(YyE zz*Q^9DP4QfHPq8qy&-l16(Epc5OnUhqlQXaPfR=(Cg)V(X(=rYz{gf0P;-3YKyc2C z+LG6bDa4Rt^gzHUTJe#x1W>Xg8$OP>eKi|g29N~=)G6-5+?pT<2;nb(9dQkN3oM#2 zj1%7km!R<|iMS)kJl_&c5CasV)kK_eduQGmt0glCPCcdKDA)r4^po$8Z0yP&CMx&e z^~B2xw4sW62D$P2TgtQ;MLt38kNO%TN3S5eLhI90zCVyyy6_A(QV}As@`c=8uBcF6 z5*mW3R>3FO!bguXVvH)7y(JdeWr~u75H8S<2=BE52XUU2cTldim004AlnJ(v76CyI z7BB|f?MWDY^zo;FHj4*_kZ%{w_nMNC4IUMoJfb@R2zq@kc%-N*K*woMIvg8TVspjv z0bJp8g@gx`PLKhrICd35l5`MKG(bDd4IpEaMC=4tu3p&S5FVqS-9?0cgA@mIrtl`39OppLG$gQX;;;r9cFGV% zdrRrd#zD3oHCcOsG2!dmwQWs-3REZG8mLfBP(JEZ9CPkO0ICE*vYD+7`bR|tEfA-s zXgYs3y>ipl0mw2315T>QB;MQZb zsxl^YNnM1Qzm6kVJe}6Q^iHp+a;TOpITGqU=-~Q_f$cTadj9|)t|CREmnqr&Iv(;UWfgcKwu;lPj9asv0b2>*yXS0e&fJ2 z10z<*KMti=@yMnUOOI1;Ch+nDDFA}6LsNTB z`rwEAn(thwp&0P1zR)&W?h~f{0lI37Y8dm|yYXO#^FxvEKoB)ZB9T zx4;j_3yKg=Wv_M9@%?iQ0B-gJ{+t2=qZQisiSgFq^UK9Hfs?fa@2p&S$iph^O>g9g zM|BMfg1r)ypT6(!&}d?M8#KA|^naK{C;<jaLs z*H?~wlh?*)xE`wwz!Ki?!OlpX>VD6|#Yem9K@4uh+v zR+;Ajhzw^3%*J+KLschg-k{60nP=DwZ&a4)8&E25?AK(gq;=j_d?m z3pD}5hq=waFFBHhS!Tt8T1wGfJL03vf+%PCvf-t9KOU1h~aiCySDl`OQ+6A6qA%H>A*xAZg zV5-WgaN%?@#*qDlNw7u8Hrd}Lj-c0~CY=a^HRFE32vD#)5bNZJ7#qbkaD^=^dOVhO zrmiC?lp+LPIqvuB{Tv6Ctw}vv|kJ>7RfdZZSkxR2B#78 z0zoXox-h+q!vZ}s3m%aIbb;Z;qJV+9TB?EqDa$14C@~D8gn`Cvn5IxqWl&S%br2H)=TjGL1M3(;P?9d@&dxhfOU)2k4Vyb+}t5vyog5)n}e!@t(#xL_`TB237> z_BE}nJtzT#g=MgqNZt=fNTSz*i6Ys;EP@FHj%%ZN!Y-Cl^6QWVu_j$4FabhglO4nG zmOzC#TKrTX!kviLL_{vZ2C|}L6G0L)jX+#-+c`uzJ5+l@sAUMHHVaN{3cyrFMT|w| zErj-!B}!P3l;G37kof@fFQFcH*vQb-*@Ls8%RUZ@NDCGj3vGC9B|rp55ePL4JXLl9 zktEAhIwhvH$__@7^v4}*YTt>YX_^ypc z!CH2Jp`9?9vpy*_0DXZ=kkhU@izTBPF>oQOW8hxYNG4Um1tW+@9!_ZO>2cI;yj$E7u!$5_J7sjs+(nOL6K*5LR``=g-eTezQ3#~=s z3~ATn<96)p`Qq)V(ic{c4xfzkc+SIG*m{-h8f?$vC{QN${wn_feenSTrJhxC9WGJ!YNz^JqH1ZrxqBSH)<2=>i&K`6E{gq=_a?>Je2NGbsqAB6a`cp%asmLRGXELl>|cTGuLvBXem4#ZVAb2%s6D4ws>lxYkJNzxI95(e*XY)J~~j7u!aPvdls+ia4eO|f(eH;93L2H8Cmqg^F9c)fXnx)ZW0Ag4MAZj=h3 zP62B1`)Y;hb4t0kC-^$yp+X5DR2czK8i#7{5j3vh-8$$q*8Hi?qJYC$h?!Xkf$QCO z-0uDwzF>Yhc!-`WYaR}*M903_*@#)nOFpUA-nv2=E z@~{c;2EPp*6I0F&pi)6dE`p#5OhDyVxd9{EsVMrYa->ly3n*H4N1rlr@F+bx9L#-Z zTX;`qP>L5$(K1Q2e6y$9MHEnzECa`h!2D7nqFR}H-nW9fYQ|gAAa4A;`Qp{E0wV(w zYT6fqN8iV$m@DDk%|QmlTeYaj7H4F?>}5>moZO%P^)N z3#MqOg|mSbH5C&)GG^GtC1EP)go7)jtEdcHUq(t`#;y({u$fL+ogn%3_TgU@Jtg}xFk+-bn&l~_t0H!!=w@z13uLT9uH?)OAsB%^t0#&Gn2{6tHSTxfLfXRy# z(ILUP7#jGkQ(`#~eN?ds0s;WvLoIk@h+)c8K!q}3_HcJV9~L_R609)mgHchf!%6Un z0zo*`G<0DD0K^@3f+|BzT^KwTh;)g7i&b!OmkWYATnCn#sH82(n9s-xVl$A2SZP7h&}COXGndSSQxXBRi1aq32w7HA#oo~6 zGcORU2;Jq1omqG;mH~hn7JxEtB7dVw(nYv=hLPAp{mq ztSwO%HDoG2z}xUlGo9h&U==6=k#scx1h`kgtTZUrsn=2iAaZngYEZapR9+?liADt) z9?lEUXD)tt0fLgP?1i0=0=X8b04+e_k!Vw3D2Z?}->AP`-XJdmsb(N0^%oyG5iAPm z!y9P<*sr9R&#eIzM4o_B12`f}Bdo#~6?I>(GH1fE?Ir)>I}=Q?JBZ6GlayRNCy zXB5!prL6@Ds3I0E2~n;Z=o7SQ7Q`_WGAd{h37G)ayXOS>s7wIB5lR(F=K_#e-QKt$ zSRj*?__})KRjBad%&x@x8tCeWxvW z1ytKKH}yGPmdc_AEA#8|4duj_m>5%gI=)w};~-MKA(hhUv9C5zQUVs0<$AVHI7Ojo zZz4n6?ep)D##A6ihen_{2D&8Zg#fmqX+SjrIHuKt2H*>jVE5)OD59U745l;!CjyA5imB^x z=x@)g<x>iUxQtLK|)ICnPZdFjk#K>fiS9#FHTl^kjO8?*aEcN5EjHMzXVo!+oTZi^wrhB=3&|nlNWl zfB;U`uDFRX@MoCDmKm;E2Dl9SXo42Qi=l1 z)k^1z7`uHCq~@ceh7_&r6qdXy(7vIgpCr`vDgmb@F@+Z;Y5`d$hL=Fyz=vT>R44!x zIvIk1n5@1rgNWF89N2_+FOLiA4v={QN5i4;yhf|~6}Bn@38LaXS_mcPoW)SvLGF-y zilGcs(5MG_U+dFG0Ix@VOTL_xo`X8q1XZ`8-!Z( zB9VZAYDuT-kp?v_b<_a>yg)<0kv3C81p^#JKxH%vC0YZi2VY^JF90z5-XK^EA}Y$p zkqV-5qNMz+rTu`Si^Q<4fFy#tt#}aQVxAq67*V1dR`PK~;KvaSG{;AysBI)B?_jJFIJTQtEZbaPxSQH2FjT zXLb%D6;&QA()$$T&=aA?Dvpfh-j#tYeXV5LrXHxldm^X9&p!H2b?OFDDG~@Yk@Aygp~oqZZuyVReeoFUB6{9Y9Fe{I*4#f`_Qt|7)!x0 zGFV^ zaYCZAu)hJq%BiO8fPsh@1{aJa;7_F>=wCZT%cj!_3_2!Πq9C>2ox5AVsCTfyy6%&4O-3YZG@WM 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 F|JkFQ!*T!s literal 0 HcmV?d00001 diff --git a/public/img/tags/bukkake/lazy/0.jpeg b/public/img/tags/bukkake/lazy/0.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..a3c3c0ebdb6cc47538b03663f3afdb574a1b13d7 GIT binary patch literal 8430 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 literal 0 HcmV?d00001 diff --git a/public/img/tags/bukkake/thumbs/0.jpeg b/public/img/tags/bukkake/thumbs/0.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..4be40d7d614c8786ee8d969c5f97848680f0b7b6 GIT binary patch literal 36795 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 literal 0 HcmV?d00001 diff --git a/seeds/02_sites.js b/seeds/02_sites.js index 94dc70dd..2adf6a7b 100644 --- a/seeds/02_sites.js +++ b/seeds/02_sites.js @@ -2,475 +2,475 @@ const upsert = require('../src/utils/upsert'); /* eslint-disable max-len */ const sites = [ - // 21NATURALS - { - slug: '21naturals', - name: '21Naturals', - alias: ['21 naturals', '21na'], - url: 'https://www.21naturals.com', - network: '21naturals', - parameters: { - extract: '21naturals', - }, - }, - { - slug: '21footart', - name: '21 Foot Art', - alias: ['21fa'], - url: 'https://21footart.21naturals.com', - network: '21naturals', - }, - { - slug: '21eroticanal', - name: '21 Erotic Anal', - alias: ['21ea'], - url: 'https://21eroticanal.21naturals.com', - network: '21naturals', - parameters: { - scene: 'https://21naturals.com/en/video', - }, - }, - // 21SEXTREME - { - slug: 'grandpasfuckteens', - name: 'Grandpas Fuck Teens', - alias: ['gft'], - url: 'https://grandpasfuckteens.21sextreme.com', - network: '21sextreme', - }, - { - slug: 'oldyounglesbianlove', - name: 'Old Young Lesbian Love', - url: 'https://oldyounglesbianlove.21sextreme.com', - network: '21sextreme', - parameters: { - scene: 'https://21sextreme.com/en/video', - }, - }, - { - slug: 'lustygrandmas', - name: 'Lusty Grandmas', - url: 'https://lustygrandmas.21sextreme.com', - network: '21sextreme', - }, - { - slug: 'teachmefisting', - name: 'Teach Me Fisting', - alias: ['tmf'], - url: 'https://teachmefisting.21sextreme.com', - network: '21sextreme', - parameters: { - scene: 'https://21sextreme.com/en/video', - }, - }, - { - slug: 'zoliboy', - name: 'Zoliboy', - alias: ['zb'], - url: 'https://zoliboy.21sextreme.com', - network: '21sextreme', - parameters: { - scene: 'https://21sextreme.com/en/video', - }, - }, - { - slug: 'mightymistress', - name: 'Mighty Mistress', - url: 'https://mightymistress.21sextreme.com', - network: '21sextreme', - parameters: { - scene: 'https://21sextreme.com/en/video', - }, - }, - { - slug: 'dominatedgirls', - name: 'Dominated Girls', - url: 'https://dominatedgirls.21sextreme.com', - network: '21sextreme', - parameters: { - scene: 'https://21sextreme.com/en/video', - }, - }, - { - slug: 'homepornreality', - name: 'Home Porn Reality', - url: 'https://homepornreality.21sextreme.com', - network: '21sextreme', - parameters: { - scene: 'https://21sextreme.com/en/video', - }, - }, - { - slug: 'peeandblow', - name: 'Pee and Blow', - url: 'https://peeandblow.21sextreme.com', - network: '21sextreme', - parameters: { - scene: 'https://21sextreme.com/en/video', - }, - }, - { - slug: 'cummingmatures', - name: 'Cumming Matures', - url: 'https://cummingmatures.21sextreme.com', - network: '21sextreme', - parameters: { - scene: 'https://21sextreme.com/en/video', - }, - }, - { - slug: 'mandyiskinky', - name: 'Mandy Is Kinky', - url: 'https://mandyiskinky.21sextreme.com', - network: '21sextreme', - parameters: { - scene: 'https://21sextreme.com/en/video', - }, - }, - { - slug: 'speculumplays', - name: 'Speculum Plays', - url: 'https://speculumplays.21sextreme.com', - network: '21sextreme', - parameters: { - scene: 'https://21sextreme.com/en/video', - }, - }, - { - slug: 'creampiereality', - name: 'Creampie Reality', - url: 'https://creampiereality.21sextreme.com', - network: '21sextreme', - parameters: { - scene: 'https://21sextreme.com/en/video', - }, - }, - // 21SEXTURY - { - slug: 'analteenangels', - name: 'Anal Teen Angels', - url: 'https://www.analteenangels.com', - description: 'AnalTeenAngels is presented by the 21Sextury nextwork and features young, European teens in hardcore anal porn. Watch these barely legal teens have their first anal sex and give up their ass for some anal pounding!', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - }, - }, - { - slug: 'assholefever', - name: 'Asshole Fever', - url: 'https://www.assholefever.com', - description: 'Welcome to AssholeFever, the most hardcore anal site on the net. Watch your favorite pornstars and anal sluts from all over the world in big booty hardcore porn, anal gape, beads, anal creampie and more! Look inside if you dare!', - network: '21sextury', - parameters: { - networkReferer: true, - }, - }, - { - slug: 'buttplays', - name: 'Butt Plays', - alias: ['bp'], - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'clubsandy', - name: 'Club Sandy', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'deepthroatfrenzy', - name: 'Deepthroat Frenzy', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'dpfanatics', - name: 'DP Fanatics', - alias: ['dpf'], - url: 'https://www.dpfanatics.com', - description: 'Welcome to DPFanatics, brought to you by 21Sextury. DP Fanatics brings you the best DP sex and double penetration porn you can find. Double vaginal penetration, double anal, amateur and teen DP inside!', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - }, - }, - { - slug: 'footsiebabes', - name: 'Footsie Babes', - url: 'https://www.footsiebabes.com', - description: 'Welcome to FootsieBabes.com, bringing you the best foot porn, teen feet and foot worship you can find on the net. Watch stocking porn, footjobs, feet tickling and more inside!', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'gapeland', - name: 'Gapeland', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'lezcuties', - name: 'Lez Cuties', - url: 'https://www.lezcuties.com', - description: 'LezCuties brings you the cutest lesbian coeds and tiny teen lesbians in HD lesbian porn. Watch as European teens explore themselves and lick each other\'s tight lesbian pussy while their parents aren\'t home.', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - }, - }, - { - slug: 'onlyswallows', - name: 'Only Swallows', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'alettaoceanempire', - name: 'Aletta Ocean Empire', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'analqueenalysa', - name: 'Anal Queen Alysa', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'blueangellive', - name: 'Blue Angel Live', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'pixandvideo', - name: 'Pix and Video', - alias: ['pav'], - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'cheatingwhorewives', - name: 'Cheating Whore Wives', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'cutiesgalore', - name: 'Cuties Galore', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'hotmilfclub', - name: 'Hot MILF Club', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'letsplaylez', - name: 'Lets Play Lez', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'nudefightclub', - name: 'Nude Fight Club', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'sexwithkathianobili', - name: 'Sex With Kathia Nobili', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - { - slug: 'sweetsophiemoone', - name: 'Sweet Sophie Moone', - network: '21sextury', - parameters: { - referer: 'https://www.21sextury.com', - scene: 'https://www.21sextury.com/en/video', - photos: 'https://www.21sextury.com/en/photo', - }, - }, - // ADULT TIME - { - name: 'ASMR Fantasy', - slug: 'asmrfantasy', - url: 'https://asmrfantasy.com', - network: 'adulttime', - parameters: { - referer: 'https://freetour.adulttime.com/en/join', - deep: 'https://21sextury.com/en/video', - scene: false, - }, - }, - { - name: 'Bubblegum Dungeon', - slug: 'bubblegumdungeon', - url: 'https://www.bubblegumdungeon.com', - network: 'adulttime', - parameters: { - referer: 'https://freetour.bubblegumdungeon.com/en/join', - deep: 'https://21sextury.com/en/video', - scene: false, - }, - }, - { - name: 'Lady Gonzo', - slug: 'ladygonzo', - url: 'https://www.ladygonzo.com', - description: 'LadyGonzo.com is a new Adult Time porn series featuring Joanna Angel shooting hardcore sex and gonzo porn movies the way she\'d like to see it!', - network: 'adulttime', - }, - { - name: 'Girls Under Arrest', - slug: 'girlsunderarrest', - url: 'https://www.girlsunderarrest.com', - parameters: { - referer: 'https://www.isthisreal.com', - scene: 'https://www.isthisreal.com/en/video/girlsunderarrest', - }, - network: 'adulttime', - }, - // AMATEUR ALLURE - { - name: 'Amateur Allure', - slug: 'amateurallure', - alias: ['aa'], - url: 'https://www.amateurallure.com', - parameters: { - upcoming: false, - latest: 'https://www.amateurallure.com/tour/updates/page_%d.html', - photos: 'https://www.amateurallure.com/tour/gallery.php', - }, - network: 'amateurallure', - }, - { - name: 'Swallow Salon', - slug: 'swallowsalon', - alias: ['swsn'], - url: 'https://www.swallowsalon.com', - parameters: { - upcoming: false, - latest: 'https://www.swallowsalon.com/categories/movies_%d_d.html', - photos: 'https://www.swallowsalon.com/gallery.php', - }, - network: 'amateurallure', - }, - // ASSYLUM - { - slug: 'assylum', - name: 'Assylum', - url: 'https://www.assylum.com', - description: 'At Assylum, submissive girls get dominated with rough anal sex, ass to mouth, hard BDSM, and sexual humiliation and degradation.', - network: 'assylum', - tags: ['bdsm'], - parameters: { - a: 68, - }, - }, - { - slug: 'slavemouth', - name: 'Slave Mouth', - url: 'https://www.slavemouth.com', - description: 'Submissive girls get their mouths punished hard by Dr. Mercies, with facefucking, gagging, frozen cum bukkake, face bondage, ass eating, and sexual degradation.', - network: 'assylum', - tags: ['bdsm'], - parameters: { - a: 183, - }, - }, - // AZIANI - { - slug: 'gangbangcreampie', - name: 'Gangbang Creampie', - url: 'https://www.gangbangcreampie.com', - network: 'aziani', - tags: ['gangbang', 'creampie'], - }, - { - slug: 'gloryholesecrets', - name: 'Glory Hole Secrets', - url: 'https://www.gloryholesecrets.com', - network: 'aziani', - tags: ['gloryhole'], - }, - { - slug: 'aziani', - name: 'Aziani', - url: 'https://www.aziani.com', - network: 'aziani', - }, - /* offline + // 21NATURALS + { + slug: '21naturals', + name: '21Naturals', + alias: ['21 naturals', '21na'], + url: 'https://www.21naturals.com', + network: '21naturals', + parameters: { + extract: '21naturals', + }, + }, + { + slug: '21footart', + name: '21 Foot Art', + alias: ['21fa'], + url: 'https://21footart.21naturals.com', + network: '21naturals', + }, + { + slug: '21eroticanal', + name: '21 Erotic Anal', + alias: ['21ea'], + url: 'https://21eroticanal.21naturals.com', + network: '21naturals', + parameters: { + scene: 'https://21naturals.com/en/video', + }, + }, + // 21SEXTREME + { + slug: 'grandpasfuckteens', + name: 'Grandpas Fuck Teens', + alias: ['gft'], + url: 'https://grandpasfuckteens.21sextreme.com', + network: '21sextreme', + }, + { + slug: 'oldyounglesbianlove', + name: 'Old Young Lesbian Love', + url: 'https://oldyounglesbianlove.21sextreme.com', + network: '21sextreme', + parameters: { + scene: 'https://21sextreme.com/en/video', + }, + }, + { + slug: 'lustygrandmas', + name: 'Lusty Grandmas', + url: 'https://lustygrandmas.21sextreme.com', + network: '21sextreme', + }, + { + slug: 'teachmefisting', + name: 'Teach Me Fisting', + alias: ['tmf'], + url: 'https://teachmefisting.21sextreme.com', + network: '21sextreme', + parameters: { + scene: 'https://21sextreme.com/en/video', + }, + }, + { + slug: 'zoliboy', + name: 'Zoliboy', + alias: ['zb'], + url: 'https://zoliboy.21sextreme.com', + network: '21sextreme', + parameters: { + scene: 'https://21sextreme.com/en/video', + }, + }, + { + slug: 'mightymistress', + name: 'Mighty Mistress', + url: 'https://mightymistress.21sextreme.com', + network: '21sextreme', + parameters: { + scene: 'https://21sextreme.com/en/video', + }, + }, + { + slug: 'dominatedgirls', + name: 'Dominated Girls', + url: 'https://dominatedgirls.21sextreme.com', + network: '21sextreme', + parameters: { + scene: 'https://21sextreme.com/en/video', + }, + }, + { + slug: 'homepornreality', + name: 'Home Porn Reality', + url: 'https://homepornreality.21sextreme.com', + network: '21sextreme', + parameters: { + scene: 'https://21sextreme.com/en/video', + }, + }, + { + slug: 'peeandblow', + name: 'Pee and Blow', + url: 'https://peeandblow.21sextreme.com', + network: '21sextreme', + parameters: { + scene: 'https://21sextreme.com/en/video', + }, + }, + { + slug: 'cummingmatures', + name: 'Cumming Matures', + url: 'https://cummingmatures.21sextreme.com', + network: '21sextreme', + parameters: { + scene: 'https://21sextreme.com/en/video', + }, + }, + { + slug: 'mandyiskinky', + name: 'Mandy Is Kinky', + url: 'https://mandyiskinky.21sextreme.com', + network: '21sextreme', + parameters: { + scene: 'https://21sextreme.com/en/video', + }, + }, + { + slug: 'speculumplays', + name: 'Speculum Plays', + url: 'https://speculumplays.21sextreme.com', + network: '21sextreme', + parameters: { + scene: 'https://21sextreme.com/en/video', + }, + }, + { + slug: 'creampiereality', + name: 'Creampie Reality', + url: 'https://creampiereality.21sextreme.com', + network: '21sextreme', + parameters: { + scene: 'https://21sextreme.com/en/video', + }, + }, + // 21SEXTURY + { + slug: 'analteenangels', + name: 'Anal Teen Angels', + url: 'https://www.analteenangels.com', + description: 'AnalTeenAngels is presented by the 21Sextury nextwork and features young, European teens in hardcore anal porn. Watch these barely legal teens have their first anal sex and give up their ass for some anal pounding!', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + }, + }, + { + slug: 'assholefever', + name: 'Asshole Fever', + url: 'https://www.assholefever.com', + description: 'Welcome to AssholeFever, the most hardcore anal site on the net. Watch your favorite pornstars and anal sluts from all over the world in big booty hardcore porn, anal gape, beads, anal creampie and more! Look inside if you dare!', + network: '21sextury', + parameters: { + networkReferer: true, + }, + }, + { + slug: 'buttplays', + name: 'Butt Plays', + alias: ['bp'], + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'clubsandy', + name: 'Club Sandy', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'deepthroatfrenzy', + name: 'Deepthroat Frenzy', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'dpfanatics', + name: 'DP Fanatics', + alias: ['dpf'], + url: 'https://www.dpfanatics.com', + description: 'Welcome to DPFanatics, brought to you by 21Sextury. DP Fanatics brings you the best DP sex and double penetration porn you can find. Double vaginal penetration, double anal, amateur and teen DP inside!', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + }, + }, + { + slug: 'footsiebabes', + name: 'Footsie Babes', + url: 'https://www.footsiebabes.com', + description: 'Welcome to FootsieBabes.com, bringing you the best foot porn, teen feet and foot worship you can find on the net. Watch stocking porn, footjobs, feet tickling and more inside!', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'gapeland', + name: 'Gapeland', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'lezcuties', + name: 'Lez Cuties', + url: 'https://www.lezcuties.com', + description: 'LezCuties brings you the cutest lesbian coeds and tiny teen lesbians in HD lesbian porn. Watch as European teens explore themselves and lick each other\'s tight lesbian pussy while their parents aren\'t home.', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + }, + }, + { + slug: 'onlyswallows', + name: 'Only Swallows', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'alettaoceanempire', + name: 'Aletta Ocean Empire', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'analqueenalysa', + name: 'Anal Queen Alysa', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'blueangellive', + name: 'Blue Angel Live', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'pixandvideo', + name: 'Pix and Video', + alias: ['pav'], + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'cheatingwhorewives', + name: 'Cheating Whore Wives', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'cutiesgalore', + name: 'Cuties Galore', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'hotmilfclub', + name: 'Hot MILF Club', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'letsplaylez', + name: 'Lets Play Lez', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'nudefightclub', + name: 'Nude Fight Club', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'sexwithkathianobili', + name: 'Sex With Kathia Nobili', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + { + slug: 'sweetsophiemoone', + name: 'Sweet Sophie Moone', + network: '21sextury', + parameters: { + referer: 'https://www.21sextury.com', + scene: 'https://www.21sextury.com/en/video', + photos: 'https://www.21sextury.com/en/photo', + }, + }, + // ADULT TIME + { + name: 'ASMR Fantasy', + slug: 'asmrfantasy', + url: 'https://asmrfantasy.com', + network: 'adulttime', + parameters: { + referer: 'https://freetour.adulttime.com/en/join', + deep: 'https://21sextury.com/en/video', + scene: false, + }, + }, + { + name: 'Bubblegum Dungeon', + slug: 'bubblegumdungeon', + url: 'https://www.bubblegumdungeon.com', + network: 'adulttime', + parameters: { + referer: 'https://freetour.bubblegumdungeon.com/en/join', + deep: 'https://21sextury.com/en/video', + scene: false, + }, + }, + { + name: 'Lady Gonzo', + slug: 'ladygonzo', + url: 'https://www.ladygonzo.com', + description: 'LadyGonzo.com is a new Adult Time porn series featuring Joanna Angel shooting hardcore sex and gonzo porn movies the way she\'d like to see it!', + network: 'adulttime', + }, + { + name: 'Girls Under Arrest', + slug: 'girlsunderarrest', + url: 'https://www.girlsunderarrest.com', + parameters: { + referer: 'https://www.isthisreal.com', + scene: 'https://www.isthisreal.com/en/video/girlsunderarrest', + }, + network: 'adulttime', + }, + // AMATEUR ALLURE + { + name: 'Amateur Allure', + slug: 'amateurallure', + alias: ['aa'], + url: 'https://www.amateurallure.com', + parameters: { + upcoming: false, + latest: 'https://www.amateurallure.com/tour/updates/page_%d.html', + photos: 'https://www.amateurallure.com/tour/gallery.php', + }, + network: 'amateurallure', + }, + { + name: 'Swallow Salon', + slug: 'swallowsalon', + alias: ['swsn'], + url: 'https://www.swallowsalon.com', + parameters: { + upcoming: false, + latest: 'https://www.swallowsalon.com/categories/movies_%d_d.html', + photos: 'https://www.swallowsalon.com/gallery.php', + }, + network: 'amateurallure', + }, + // ASSYLUM + { + slug: 'assylum', + name: 'Assylum', + url: 'https://www.assylum.com', + description: 'At Assylum, submissive girls get dominated with rough anal sex, ass to mouth, hard BDSM, and sexual humiliation and degradation.', + network: 'assylum', + tags: ['bdsm'], + parameters: { + a: 68, + }, + }, + { + slug: 'slavemouth', + name: 'Slave Mouth', + url: 'https://www.slavemouth.com', + description: 'Submissive girls get their mouths punished hard by Dr. Mercies, with facefucking, gagging, frozen cum bukkake, face bondage, ass eating, and sexual degradation.', + network: 'assylum', + tags: ['bdsm'], + parameters: { + a: 183, + }, + }, + // AZIANI + { + slug: 'gangbangcreampie', + name: 'Gangbang Creampie', + url: 'https://www.gangbangcreampie.com', + network: 'aziani', + tags: ['gangbang', 'creampie'], + }, + { + slug: 'gloryholesecrets', + name: 'Glory Hole Secrets', + url: 'https://www.gloryholesecrets.com', + network: 'aziani', + tags: ['gloryhole'], + }, + { + slug: 'aziani', + name: 'Aziani', + url: 'https://www.aziani.com', + network: 'aziani', + }, + /* offline { slug: 'portagloryhole', name: 'Porta Gloryhole', @@ -509,3876 +509,3883 @@ const sites = [ network: 'aziani', }, */ - // BABES - { - name: 'Babes', - url: 'https://www.babes.com/scenes?site=213', - slug: 'babes', - network: 'babes', - }, - { - name: 'Babes Unleashed', - url: 'https://www.babes.com/scenes?site=218', - slug: 'babesunleashed', - network: 'babes', - }, - { - name: 'Black Is Better', - url: 'https://www.babes.com/scenes?site=217', - slug: 'blackisbetter', - network: 'babes', - }, - { - name: 'Elegant Anal', - url: 'https://www.babes.com/scenes?site=216', - slug: 'elegantanal', - network: 'babes', - }, - { - name: 'Office Obsession', - url: 'https://www.babes.com/scenes?site=214', - slug: 'officeobsession', - network: 'babes', - }, - { - name: 'Step Mom Lessons', - url: 'https://www.babes.com/scenes?site=215', - slug: 'stepmomlessons', - network: 'babes', - }, - // BAM VISIONS - { - slug: 'bamvisions', - name: 'BAM Visions', - url: 'https://tour.bamvisions.com', - parameters: { independent: true }, - network: 'bamvisions', - }, - // BANG - { - name: 'Trickery', - slug: 'bangtrickery', - url: 'https://www.bang.com/original/4800/bang-trickery', - parameters: { siteId: 4800 }, - network: 'bang', - }, - { - name: 'Yngr', - slug: 'yngrcom', - alias: ['byngr'], - // url: 'https://www.bang.com/original/5010/bang-yngr', - url: 'https://yngr.com', - parameters: { siteId: 5010 }, - network: 'bang', - }, - { - name: 'Roadside XXX', - slug: 'bangroadsidexxx', - // url: 'https://www.bang.com/original/4864/roadside-xxx', - url: 'https://roadsidexxx.com', - parameters: { siteId: 4864 }, - network: 'bang', - }, - { - name: 'Surprise', - slug: 'bangsurprise', - url: 'https://www.bang.com/original/5000/bang-surprise', - parameters: { siteId: 5000 }, - network: 'bang', - }, - { - name: 'Real Teens', - slug: 'bangrealteens', - alias: ['brealteens'], - url: 'https://www.bang.com/original/3366/bang-real-teens', - parameters: { siteId: 3366 }, - network: 'bang', - }, - { - name: 'FCK.news', - slug: 'bangfakenews', - // url: 'https://www.bang.com/original/4998/bang-fckNews', - url: 'https://fck.news', - parameters: { siteId: 4998 }, - network: 'bang', - }, - { - name: 'Pretty & Raw', - slug: 'prettyandraw', - // url: 'https://www.bang.com/original/4792/bang-pretty-and-raw', - url: 'https://prettyandraw.com', - parameters: { siteId: 4792 }, - network: 'bang', - }, - { - name: 'Japan', - slug: 'bangjapan', - url: 'https://www.bang.com/original/3079/bang-japan', - parameters: { siteId: 3079, ignore: true }, - network: 'bang', - }, - { - name: 'Rammed', - slug: 'bangrammed', - url: 'https://www.bang.com/original/4836/bang-rammed', - parameters: { siteId: 4836 }, - network: 'bang', - }, - { - name: 'Glamkore', - slug: 'bangglamkore', - alias: ['bglamkore'], - url: 'https://www.bang.com/original/4586/bang-glamkore', - parameters: { siteId: 4586 }, - network: 'bang', - }, - { - name: 'Screw The Cops', - slug: 'screwthecops', - url: 'https://www.bang.com/original/4710/bang-screw-cops', - parameters: { siteId: 4710 }, - network: 'bang', - }, - { - name: 'Real MILFs', - slug: 'bangrealmilfs', - alias: ['brealmilfs'], - url: 'https://www.bang.com/original/4448/bang-real-milfs', - parameters: { siteId: 4448 }, - network: 'bang', - }, - { - name: 'Confessions', - slug: 'bangconfessions', - alias: ['bconfessions'], - url: 'https://www.bang.com/original/4308/bang-confessions', - parameters: { siteId: 4308 }, - network: 'bang', - }, - { - name: 'Casting', - slug: 'bangcasting', - alias: ['bcasting'], - url: 'https://www.bang.com/original/3261/bang-casting', - parameters: { siteId: 3261 }, - network: 'bang', - }, - // BANGBROS - { - name: 'Ass Parade', - url: 'https://bangbros.com/websites/assparade', - slug: 'assparade', - description: null, - network: 'bangbros', - parameters: { code: 'ap' }, - }, - { - name: 'AvaSpice', - url: 'https://bangbros.com/websites/avaspice', - slug: 'avaspice', - description: null, - network: 'bangbros', - parameters: { code: 'av' }, - }, - { - name: 'Back Room Facials', - url: 'https://bangbros.com/websites/backroomfacials', - slug: 'backroomfacials', - description: null, - network: 'bangbros', - parameters: { code: 'brf' }, - }, - { - name: 'Backroom MILF', - url: 'https://bangbros.com/websites/backroommilf', - slug: 'backroommilf', - description: null, - network: 'bangbros', - parameters: { code: 'mf' }, - }, - { - name: 'Ball Honeys', - url: 'https://bangbros.com/websites/ballhoneys', - slug: 'ballhoneys', - description: null, - network: 'bangbros', - parameters: { code: 'es' }, - }, - { - name: 'BangBros 18', - url: 'https://bangbros.com/websites/bangbros18', - slug: 'bangbros18', - description: null, - network: 'bangbros', - parameters: { code: 'bbe' }, - }, - { - name: 'BangBros Angels', - url: 'https://bangbros.com/websites/bangbrosangels', - slug: 'bangbrosangels', - description: null, - network: 'bangbros', - parameters: { code: 'bng' }, - }, - { - name: 'Bangbros Clips', - url: 'https://bangbros.com/websites/bangbrosclips', - slug: 'bangbrosclips', - description: null, - network: 'bangbros', - parameters: { code: 'bbc' }, - }, - { - name: 'BangBros Remastered', - url: 'https://bangbros.com/websites/remaster', - slug: 'bangbrosremastered', - description: null, - network: 'bangbros', - parameters: { code: 'rm' }, - }, - { - name: 'Bang Bus', - url: 'https://bangbros.com/websites/bangbus', - slug: 'bangbus', - description: null, - network: 'bangbros', - parameters: { code: 'bb' }, - }, - { - name: 'Bang Casting', - url: 'https://bangbros.com/websites/bangcasting', - slug: 'bangbroscasting', - description: null, - network: 'bangbros', - parameters: { code: 'hih' }, - }, - { - name: 'Bang POV', - url: 'https://bangbros.com/websites/bangpov', - slug: 'bangpov', - description: null, - network: 'bangbros', - parameters: { code: 'bpov' }, - }, - { - name: 'Bang Tryouts', - url: 'https://bangbros.com/websites/bangtryouts', - slug: 'bangtryouts', - description: null, - network: 'bangbros', - parameters: { code: 'bto' }, - }, - { - name: 'Big Mouthfuls', - url: 'https://bangbros.com/websites/bigmouthfuls', - slug: 'bigmouthfuls', - description: null, - network: 'bangbros', - parameters: { code: 'bmf' }, - }, - { - name: 'Big Tit Cream Pie', - alias: ['btc'], - slug: 'bigtitcreampie', - url: 'https://bangbros.com/websites/bigtitcreampie', - description: null, - network: 'bangbros', - parameters: { code: 'btcp' }, - }, - { - name: 'Big Tits, Round Asses', - url: 'https://bangbros.com/websites/bigtitsroundasses', - alias: ['btra'], - slug: 'bigtitsroundasses', - description: null, - network: 'bangbros', - parameters: { code: 'btra' }, - }, - { - name: 'BlowJob Fridays', - url: 'https://bangbros.com/websites/blowjobfridays', - slug: 'blowjobfridays', - description: null, - network: 'bangbros', - parameters: { code: 'bj' }, - }, - { - name: 'Blowjob Ninjas', - url: 'https://bangbros.com/websites/blowjobninjas', - slug: 'blowjobninjas', - description: null, - network: 'bangbros', - parameters: { code: 'aa' }, - }, - { - name: 'Boob Squad', - url: 'https://bangbros.com/websites/boobsquad', - slug: 'boobsquad', - description: null, - network: 'bangbros', - parameters: { code: 'bs' }, - }, - { - name: 'Brown Bunnies', - url: 'https://bangbros.com/websites/brownbunnies', - slug: 'brownbunnies', - description: null, - network: 'bangbros', - parameters: { code: 'bkb' }, - }, - { - name: 'Can He Score?', - url: 'https://bangbros.com/websites/canhescore', - slug: 'canhescore', - description: null, - network: 'bangbros', - parameters: { code: 'bd' }, - }, - { - name: 'Casting', - url: 'https://bangbros.com/websites/casting', - slug: 'casting', - description: null, - network: 'bangbros', - parameters: { code: 'ca' }, - }, - { - name: 'Chongas', - url: 'https://bangbros.com/websites/chongas', - slug: 'chongas', - description: null, - network: 'bangbros', - parameters: { code: 'ch' }, - }, - { - name: 'Colombia Fuck Fest', - url: 'https://bangbros.com/websites/colombiafuckfest', - slug: 'colombiafuckfest', - description: null, - network: 'bangbros', - parameters: { code: 'cff' }, - }, - { - name: 'Dirty World Tour', - url: 'https://bangbros.com/websites/dirtyworldtour', - slug: 'dirtyworldtour', - description: null, - network: 'bangbros', - parameters: { code: 'bf' }, - }, - { - name: 'Dorm Invasion', - url: 'https://bangbros.com/websites/dorminvasion', - slug: 'dorminvasion', - description: null, - network: 'bangbros', - parameters: { code: 'di' }, - }, - { - name: 'Facial Fest', - url: 'https://bangbros.com/websites/facialfest', - slug: 'facialfest', - description: null, - network: 'bangbros', - parameters: { code: 'ff' }, - }, - { - name: 'Fuck Team Five', - url: 'https://bangbros.com/websites/fuckteamfive', - slug: 'fuckteamfive', - description: null, - network: 'bangbros', - parameters: { code: 'bbw' }, - }, - { - name: 'Glory Hole Loads', - url: 'https://bangbros.com/websites/gloryholeloads', - slug: 'gloryholeloads', - description: null, - network: 'bangbros', - parameters: { code: 'ghl' }, - }, - { - name: 'Latina Rampage', - url: 'https://bangbros.com/websites/latinarampage', - slug: 'latinarampage', - description: null, - network: 'bangbros', - parameters: { code: 'lrp' }, - }, - { - name: 'Living With Anna', - url: 'https://bangbros.com/websites/livingwithanna', - slug: 'livingwithanna', - description: null, - network: 'bangbros', - parameters: { code: 'lr' }, - }, - { - name: 'Magical Feet', - url: 'https://bangbros.com/websites/magicalfeet', - slug: 'magicalfeet', - description: null, - network: 'bangbros', - parameters: { code: 'fj' }, - }, - { - name: 'Milf Soup', - url: 'https://bangbros.com/websites/milfsoup', - slug: 'milfsoup', - description: null, - network: 'bangbros', - parameters: { code: 'ms' }, - }, - { - name: 'MomIsHorny', - url: 'https://bangbros.com/websites/momishorny', - slug: 'momishorny', - description: null, - network: 'bangbros', - parameters: { code: 'mih' }, - }, - { - name: 'Monsters of Cock', - url: 'https://bangbros.com/websites/monstersofcock', - slug: 'monstersofcock', - description: null, - network: 'bangbros', - parameters: { code: 'mc' }, - }, - { - name: 'Mr CamelToe', - url: 'https://bangbros.com/websites/mrcameltoe', - slug: 'mrcameltoe', - description: null, - network: 'bangbros', - parameters: { code: 'ct' }, - }, - { - name: 'My Dirty Maid', - slug: 'mydirtymaid', - alias: ['mdm'], - url: 'https://bangbros.com/websites/mydirtymaid', - description: null, - network: 'bangbros', - parameters: { code: 'mda' }, - }, - { - name: 'My Life In Brazil', - url: 'https://bangbros.com/websites/mylifeinbrazil', - slug: 'mylifeinbrazil', - description: null, - network: 'bangbros', - parameters: { code: 'mb' }, - }, - { - name: 'Newbie Black', - url: 'https://bangbros.com/websites/newbieblack', - slug: 'newbieblack', - description: null, - network: 'bangbros', - parameters: { code: 'blkg' }, - }, - { - name: 'Party of Three', - url: 'https://bangbros.com/websites/partyofthree', - slug: 'partyofthree', - description: null, - network: 'bangbros', - parameters: { code: 'ls' }, - }, - { - name: 'Pawg', - url: 'https://bangbros.com/websites/pawg', - slug: 'pawg', - description: null, - network: 'bangbros', - parameters: { code: 'pwg' }, - }, - { - name: 'Penny Show', - url: 'https://bangbros.com/websites/pennyshow', - slug: 'pennyshow', - description: null, - network: 'bangbros', - parameters: { code: 'ps' }, - }, - { - name: 'Porn Star Spa', - url: 'https://bangbros.com/websites/pornstarspa', - slug: 'pornstarspa', - description: null, - network: 'bangbros', - parameters: { code: 'pos' }, - }, - { - name: 'Power Munch', - url: 'https://bangbros.com/websites/powermunch', - slug: 'powermunch', - description: null, - network: 'bangbros', - parameters: { code: 'pm' }, - }, - { - name: 'Public Bang', - url: 'https://bangbros.com/websites/publicbang', - slug: 'publicbang', - description: null, - network: 'bangbros', - parameters: { code: 'pb' }, - }, - { - name: 'Slutty White Girls', - url: 'https://bangbros.com/websites/sluttywhitegirls', - slug: 'sluttywhitegirls', - description: null, - network: 'bangbros', - parameters: { code: 'swg' }, - }, - { - name: 'Stepmom Videos', - url: 'https://bangbros.com/websites/stepmomvideos', - slug: 'stepmomvideos', - description: null, - network: 'bangbros', - parameters: { code: 'smv' }, - }, - { - name: 'Street Ranger', - url: 'https://bangbros.com/websites/thewheeler', - slug: 'streetranger', - description: null, - network: 'bangbros', - parameters: { code: 'sg' }, - }, - { - name: 'Tugjobs', - url: 'https://bangbros.com/websites/tugjobs', - slug: 'tugjobs', - description: null, - network: 'bangbros', - parameters: { code: 'hj' }, - }, - { - name: 'Working Latinas', - url: 'https://bangbros.com/websites/workinglatinas', - slug: 'workinglatinas', - description: null, - network: 'bangbros', - parameters: { code: 'lw' }, - }, - { - name: 'MILF Lessons', - url: 'https://bangbros.com/websites/milflessons', - slug: 'milflessons', - description: null, - network: 'bangbros', - parameters: { code: 'ml' }, - }, - { - name: 'Mr. Anal', - url: 'https://bangbros.com/websites/mranal', - slug: 'mranal', - description: null, - network: 'bangbros', - parameters: { code: 'ma' }, - }, - // BLOWPASS - { - slug: '1000facials', - name: '1000 Facials', - alias: ['1kf'], - url: 'https://www.1000facials.com', - description: 'Welcome to 1000Facials.com, your source for the best facial porn with huge cumshots on your favorite teen and MILF pornstars. Watch all the blowjob action inside!', - network: 'blowpass', - parameters: { - latest: '/en/scenes/updates/%d/Category/0/Pornstar/0', - upcoming: '/en/scenes/upcoming', - }, - }, - { - slug: 'immorallive', - name: 'Immoral Live', - alias: ['il'], - url: 'https://www.immorallive.com', - description: 'Watch live sex shows and videos on ImmoralLive.com, featuring wild and crazy sex orgies, group sex, blowjob competitions and toy play from the famous Porno Dan. The hottest pornstars and amateur girls cum hard inside', - network: 'blowpass', - parameters: { - latest: '/en/videos/All-Categories/0/All-Pornstars/0/All/0/', - upcoming: '/en/videos/All-Categories/0/All-Pornstars/0/All/0/1/upcoming', - }, - }, - { - slug: 'mommyblowsbest', - name: 'Mommy Blows Best', - alias: ['mbb'], - url: 'https://www.mommyblowsbest.com', - description: 'Welcome to MommyBlowsBest.com. Home to thousands of MILF blowjobs and hot mom porn! Come see why experience counts, right here at MommyBlowsBest.com!', - network: 'blowpass', - parameters: { - latest: '/en/scenes/updates/0/Category/0/Actor/', - upcoming: '/en/scenes/upcoming', - }, - }, - { - slug: 'onlyteenblowjobs', - name: 'Only Teen Blowjobs', - alias: ['otb'], - url: 'https://www.onlyteenblowjobs.com', - description: 'OnlyTeenBlowjobs.com brings you the best teen blowjob porn featuring today\'s hottest young pornstars and amateurs. Watch as teens use their little mouths to suck and deepthroat the biggest of cocks!', - network: 'blowpass', - parameters: { - latest: '/en/scenes/updates/0/Category/0/Actor/', - upcoming: '/en/scenes/upcoming', - }, - }, - { - slug: 'throated', - name: 'Throated', - alias: ['ted'], - url: 'https://www.throated.com', - description: 'Throated.com is your portal for extreme throat fuck porn, face fucking videos and deepthroat gagging pornstars. Watch teens and MILFs go balls deep, swallowing cock in HD!', - network: 'blowpass', - parameters: { - latest: '/en/videos/latest/All-Categories/0/All-Pornstars/0/', - upcoming: '/en/videos/upcoming', - }, - }, - { - slug: 'sunlustxxx', - name: 'Sun Lust XXX', - url: 'https://www.sunlustxxx.com', - description: '', - network: 'blowpass', - show: true, // site offline, use only for indexing old scenes - }, - // BOOBPEDIA - { - slug: 'boobpedia', - name: 'Boobpedia', - url: 'https://www.boobpedia.com', - network: 'boobpedia', - }, - // BRAZZERS - { - slug: 'momsincontrol', - name: 'Moms in Control', - alias: ['mic'], - url: 'https://www.brazzers.com/sites/view/id/155/moms-in-control', - description: "There's nothing hotter than seeing a wholesome MILf get dirty, and that's exactly what MILFs in Control is all about: the hottest, sluttiest cougars in the business taking control of sexy situations to get exactly what they want. Feast your eyes as these mature beauties suck and fuck huge cocks, dominating big-dick studs and hot teen sluts until they get the cum that all MILFs crave!", - network: 'brazzers', - }, - { - slug: 'pornstarslikeitbig', - name: 'Pornstars Like It Big', - alias: ['plib'], - url: 'https://www.brazzers.com/sites/view/id/24/pornstars-like-it-big', - description: "A real big dick, that's what everyone wants. Porn-stars are no exception, all the biggest stars agree; BIG COCK is for them. Check out how it stretches their tiny pussies and cums on their round tits. We've got the best chicks jocking the biggest dicks.", - network: 'brazzers', - }, - { - slug: 'bigtitsatwork', - name: 'Big Tits at Work', - alias: ['btaw'], - url: 'https://www.brazzers.com/sites/view/id/15/big-tits-at-work', - description: 'Sitting at your desk, wishing you can fuck every busty coworker you have? Well, stop dreaming and step inside Big Tits At Work where you can watch real life work adventures caught on tape. Nothing But Big Breasted Work Professionals getting drilled all day long...', - network: 'brazzers', - }, - { - slug: 'bigtitsatschool', - name: 'Big Tits at School', - alias: ['btas'], - url: 'https://www.brazzers.com/sites/view/id/20/big-tits-at-school', - description: "The windows have been fogging up at Big Tits At School. Just take a peek inside one of our classrooms and you'll see our smoking hot busty students and big boobed dominant teachers getting their wet pussies stuffed with cock. Stay in your seat! you haven't been dismissed yet.", - network: 'brazzers', - }, - { - slug: 'babygotboobs', - name: 'Baby Got Boobs', - alias: ['bgb'], - url: 'https://www.brazzers.com/sites/view/id/9/baby-got-boobs', - description: "From fresh-faced teen to total slut, baby has boobs and she isn't afraid to show them. But does she know how to use them? These teens crave monster cock in their tight pussies, whether they're ready for a big dicking is another matter.", - network: 'brazzers', - }, - { - slug: 'realwifestories', - name: 'Real Wife Stories', - alias: ['rws'], - url: 'https://www.brazzers.com/sites/view/id/52/real-wife-stories', - description: "You might bring home the bacon, but your wife is still starving. That slut is hungry for cock, she can't get enough, and if you starve her any more she'll get it wherever she can. Better leave work early, or your big-titted wife might just have some giant cock getting squeezed into her waiting pussy, and it won't be yours.", - network: 'brazzers', - }, - { - slug: 'teenslikeitbig', - name: 'Teens Like It Big', - alias: ['tlib'], - url: 'https://www.brazzers.com/sites/view/id/51/teens-like-it-big', - description: "Whether they know it or not, teens love big stiff cocks in their tight pussies. Nothing goes better together than a tight, willing teen and a huge dick. In her bedroom or sneaking out to her boyfriend's, teens just want it all. Cum inside to see greedy sluts get more than they asked for", - network: 'brazzers', - }, - { - slug: 'zzseries', - name: 'ZZ Series', - alias: ['zzs'], - url: 'https://www.brazzers.com/sites/view/id/81/zz-series', - description: 'This is the spot for all our high-end content. ZZ series is exclusive footage that offers only the best in terms of story, stars and action. Check out the hottest porn-stars having the nastiest sex here at the ZZ series', - network: 'brazzers', - }, - { - slug: 'mommygotboobs', - name: 'Mommy Got Boobs', - alias: ['mgb'], - url: 'https://www.brazzers.com/sites/view/id/10/mommy-got-boobs', - description: "When hubby's away MILFS will play. Older women just crave cock, and they're experienced enough to know that only a young stud will do. Big-titted sluts everywhere are sucking and fucking in secret, giving it away to anybody they can. At Mommy Got Boobs, you can get some MILF of your own.", - network: 'brazzers', - }, - { - slug: 'milfslikeitbig', - name: 'MILFs Like It Big', - alias: ['mlib'], - url: 'https://www.brazzers.com/sites/view/id/36/milfs-like-it-big', - description: "When hubby's away milfy will play. These bored housewives want to get fucked and they want it now. They're experienced and know what they want. America's suburbs are full of these cum-de-sacs just waiting to get laid. Their round tits and thick asses are just begging for it. Cum inside, but don't park out front!", - network: 'brazzers', - }, - { - slug: 'bigtitsinuniform', - name: 'Big Tits In Uniform', - alias: ['btiu'], - url: 'https://www.brazzers.com/sites/view/id/73/big-tits-in-uniform', - description: "Big titted wonders are all around us, doing the toughest jobs in the tightest uniforms. Look at them just bursting out of that blouse, or over there, bulging under that nurse's uniform. You know when those tight uniforms come off these sluts go wild, sucking and fucking cocks left and right, their big tits just bouncing. I can't wait to punch the clock.", - network: 'brazzers', - }, - { - slug: 'doctoradventures', - name: 'Doctor Adventures', - alias: ['da'], - url: 'https://www.brazzers.com/sites/view/id/5/doctor-adventures', - description: 'Ever had fantasies about fucking your hot doctor? Live out your fantasies on doctoradventures.com. Countless doctor, patient scenarios come to life on this site with the sexiest and bustiest doctors imaginable! This is your one stop for the best in doctor porn in the world!', - network: 'brazzers', - }, - { - slug: 'brazzersexxtra', - name: 'Brazzers Exxtra', - alias: ['bex'], - url: 'https://www.brazzers.com/sites/view/id/152/brazzers-exxtra', - description: "\"Brazzers Exxtra\" is a doorway to new, unseen hardcore content! There are countless Brazzers videos that were not released throughout the years and we haven't been able to show them to you until now. Random videos staring the world's most popular pornstars, fresh new industry faces and a whole lot more! We'll even throw in an occasional free video from our friends at Mofos, Twisty's and Babes! Check it all out and let us know what you think. If you want more, we'll get it for you!", - network: 'brazzers', - }, - { - slug: 'bigtitsinsports', - name: 'Big Tits In Sports', - alias: ['btis'], - url: 'https://www.brazzers.com/sites/view/id/54/big-tits-in-sports', - description: 'Watch them bounce, watch them score and look at the way they handle those balls! Big tits in sports is here and so are the best big titted, athletic babes. Facials on the court and threesomes on the field, these busty sluts are ready for anything, even if it means playing dirty. Could you take them 1 on 1?', - network: 'brazzers', - }, - { - slug: 'brazzersvault', - name: 'Brazzers Vault', - url: 'https://www.brazzers.com/sites/view/id/56/brazzers-vault', - description: "We've got a whole super computer full of this stuff, technicians are working round the clock in the basement just to keep the thing from overheating. Yeah, this porno is hot. We need to get it out of before the whole thing melts down, that's why it's on the net, for you our loyal Brazzers Members. All the best scenes from all the best girls. In the World. Period.", - network: 'brazzers', - }, - { - slug: 'bigbuttslikeitbig', - name: 'Big Butts Like It Big', - alias: ['bblib'], - url: 'https://www.brazzers.com/sites/view/id/53/big-butts-like-it-big', - description: "You have to pair like with like. And big butts have to have big dicks to go with them. There's really no choice for these big round asses and the babes who fuck with them. Big assed bitches love it hard and deep, and won't have it any other way. Let the ass stuffing begin.", - network: 'brazzers', - }, - { - slug: 'bigwetbutts', - name: 'Big Wet Butts', - alias: ['bwb'], - url: 'https://www.brazzers.com/sites/view/id/8/big-wet-butts', - description: 'A nice, big, round butt is a special shape. Begging for powerful doggy style or straight anal penetration, cover a big butt in oil and it becomes a big wet butt, a true rarity. Watch these soft, tight asses get slathered and pounded like you only wish you could. Look at it bounce!', - network: 'brazzers', - }, - { - slug: 'daywithapornstar', - name: 'Day With A Pornstar', - alias: ['dwp'], - url: 'https://www.brazzers.com/sites/view/id/59/day-with-a-pornstar', - description: "We all know what our favorite stars can do on camera. We're familiar with the way they fuck and suck. What you don't get to see is what they do on their own time. Day With a Porn-star will show you everything, from crazy parties to total babe pals. Nobody else has access like this, it's the closest you get to living the dream.", - network: 'brazzers', - }, - { - slug: 'dirtymasseur', - name: 'Dirty Masseur', - alias: ['dm'], - url: 'https://www.brazzers.com/sites/view/id/150/dirty-masseur', - description: "Take a moment and unwind. Lay down, relax, and enjoy watching and wanking to these luscious Brazzers beauties getting good and greasy. Boobs, butts, and other lady-parts are at their prettiest when shimmering with slick oil. Book an appointment, and slide on in with a lubed babe. Believe me when I say, you'll have the happiest of endings...", - network: 'brazzers', - }, - { - slug: 'hotandmean', - name: 'Hot And Mean', - alias: ['ham'], - url: 'https://www.brazzers.com/sites/view/id/78/hot-and-mean', - description: "The hottest bitches run together. Hot, mean lesbians love to fuck each other and hate each other for being so beautiful. These lesbo sluts can't get enough pussy and love girl on girl action. Forget the dicks, these chicks don't need 'em. You can watch though, they love that.", - network: 'brazzers', - }, - { - slug: 'brazzersenespanol', - name: 'Brazzers en Español', - url: 'https://www.brazzers.com/sites/view/id/157/brazzers-en-espanol', - description: 'Brazzers en Español - El mejor sitio porno en alta definición del mundo ¡Ofreciéndole los vídeos para adultos en alta definición, descargables y en streaming, más exclusivos de Internet! Brazzers cuenta con las estrellas porno más sexys a través de los sitios más calientes en la red. Las estrellas porno y las escenas más calientes en internet. ¡Tendrá acceso a más sexo anal, tetas grandes y culos calientes de los que jamás soñó!', - network: 'brazzers', - }, - { - slug: 'brazzerslive', - name: 'Brazzers Live', - url: 'https://www.brazzers.com/sites/view/id/156/brazzers-live', - description: 'Brazzers is the industry leader for premium porn that breaks the mold. Pioneering its legendary LIVE SHOWS, ZZ is constantly redefining what hardcore erotica is about. Our wild fuck marathons are loaded with the steamiest improvised sex around. Catch a bevy of naked bodacious babes who ravage the biggest dicks with ease and in real-time. Our monster cock hunks rise to the occasion and feed these ravenous vixens who possess an insatiable appetite for cum.', - network: 'brazzers', - }, - { - slug: 'sexproadventures', - name: 'SexPro Adventures', - url: 'https://www.brazzers.com/sites/view/id/23/sexpro-adventures', - description: "Having trouble with your dick-style? The sex pros are here and they'll teach you everything you need to know to be a better man. At your place or theirs, these sluts just want to have a good time. Don't worry, she's a professional.", - network: 'brazzers', - }, - { - slug: 'shesgonnasquirt', - name: 'Shes Gonna Squirt', - url: 'https://www.brazzers.com/sites/view/id/151/shes-gonna-squirt', - description: "Enter the wet world of female ejaculation at shesgonnasquirt! Exclusive hardcore porn of your top pornstars squirting will excite you beyond belief. She's Gonna Squirt is home to the best in HD squirting sex videos. How to make a girl's pussy squirt is an art and should no longer remain a mystery, so join now to become a master.", - network: 'brazzers', - }, - { - slug: 'assesinpublic', - name: 'Asses In Public', - url: 'https://www.brazzers.com/sites/view/id/50/asses-in-public', - description: "Sex in public can present its challenges, never fear, we're willing to accept them. There's something hot about asses out in the street that we just can't deny. Porn-stars fucking on public or just hot girls showing their asses in the airport, we've got both and then some. Asses in Public has the roundest asses and the biggest tits just hanging out, where WILL we show up next?", - network: 'brazzers', - }, - { - slug: 'bustyz', - name: 'Bustyz', - url: 'https://www.brazzers.com/sites/view/id/6/bustyz', - description: "If the internet was a town we'd have the biggest tits around. We still do though, because Bustyz features only the best endowed porn stars in solo and group action. Watch these big-titted babes take cock, suck twat and show off their massive jugs. Real or fake, we don't judge, everyone's welcome under the big tit tent", - network: 'brazzers', - }, - { - slug: 'bustyandreal', - name: 'Busty & Real', - url: 'https://www.brazzers.com/sites/view/id/2/busty-real', - description: "Sometimes you need to take a break from the silicon football set. Busty and real has all the real jugs you need. Round. Soft. and as real as they come. These babes are rocking exactly what momma gave them. They've not afraid to show off their assets and get slammed with dick in the process.", - network: 'brazzers', - }, - { - slug: 'hotchicksbigasses', - name: 'Hot Chicks Big Asses', - url: 'https://www.brazzers.com/sites/view/id/7/hot-chicks-big-asses', - description: 'Everyone gather round; the giant ass. A babe can be hot in a lot of ways and having a big round ass is one of the best. All shapes, sizes and types these girls are the best of the best. Round, supple, jiggling asses taking on dicks and other pussies in equal measure.', - network: 'brazzers', - }, - { - slug: 'cfnm', - name: 'CFNM', - url: 'https://www.brazzers.com/sites/view/id/154/cfnm', - description: "Welcome to the world of clothed female sluts fucking, humiliating and dominating naked men, giving them a dose of what it feels like to be owned. If you love women with power dominating wimpy guys and showing them who's boss; women who crave for cock but get it exactly how they want it, that's what you'll find here. Simply put, the guys don't fuck the women, the women fuck the guys and make them feel like whores!", - network: 'brazzers', - }, - { - slug: 'jugfuckers', - name: 'JugFuckers', - url: 'https://www.brazzers.com/sites/view/id/12/jugfuckers', - description: "Like a sex hot-dog, a big dick fits nicely between two soft, round tits. Tit-fucking isn't easy and never will be. Our girls are pros and take big loads on their faces and tits with a smile. From DD to the smallest things going, we've got every type of tit- fuck around.", - network: 'brazzers', - }, - { - slug: 'teenslikeitblack', - name: 'Teens Like It Black', - url: 'https://www.brazzers.com/sites/view/id/57/teens-like-it-black', - description: "Teens just wanna piss their parents off; no rules, spring break, big black cocks. They love pushing things to the limit, how big and black can it be? Only teen girls know. Watch them get more than they bargained for, long black cocks drilling their tight, inexperienced pussies. It's an epic fuck when the biggest and the tightest meet.", - network: 'brazzers', - }, - { - slug: 'racksandblacks', - name: 'Racks & Blacks', - url: 'https://www.brazzers.com/sites/view/id/11/racks-blacks', - description: "All the interracial action you need is here. Big 'ol black cocks ramming and jamming pussies to the limit. All types of different girls fall prey to the venerable black dick. Wet pussies and fat asses? Bring it on. There's nothing our stable of asses can't handle, they'll keep cumming and cumming.", - network: 'brazzers', - }, - { - slug: 'buttsandblacks', - name: 'Butts & Blacks', - url: 'https://www.brazzers.com/sites/view/id/3/butts-blacks', - description: "Giant black dicks paired with round asses and garnished with the tightest pussies of all colors. Butts and Blacks delivers on its name sake, only the biggest dicks rocking the thickest chicks. These round honeys can take it all in and bounce around like it's a pogo stick. Come check out these soft round asses getting the attention they deserve.", - network: 'brazzers', - }, - // BURNING ANGEL - { - name: 'Burning Angel', - slug: 'burningangel', - alias: ['burna'], - url: 'https://www.burningangel.com', - network: 'burningangel', - parameters: { independent: true }, - }, - // CHERRY PIMPS - { - slug: 'cherrypimps', - name: 'Cherry Pimps', - alias: ['cps'], - url: 'https://cherrypimps.com', - description: 'CherryPimps your premium porn site to Download and Stream the hottest and most exclusive 4K HD videos and pictures on your phone, tablet, TV or console.', - network: 'cherrypimps', - parameters: { - extract: true, - }, - }, - { - slug: 'wildoncam', - name: 'Wild On Cam', - alias: ['woc'], - url: 'https://wildoncam.com', - tags: ['live'], - network: 'cherrypimps', - }, - { - slug: 'britneyamber', - name: 'Britney Amber', - url: 'https://www.britneyamber.com', - network: 'cherrypimps', - parameters: { - extract: true, - }, - }, - // DDF NETWORK - { - slug: 'ddfbusty', - name: 'DDF Busty', - alias: ['ddfb'], - url: 'https://ddfbusty.com', - description: 'Gorgeous Babes with big tits and Euro pornstars with huge natural boobs filmed in Exclusive Full HD, 4K, & VR porn videos.', - network: 'ddfnetwork', - }, - { - slug: 'handsonhardcore', - name: 'Hands on Hardcore', - alias: ['hoh'], - url: 'https://handsonhardcore.com', - description: 'Hardcore Sex & Anal Fucking Exclusive XXX Videos in VR, 4K and full HD with Hot European Pornstars', - network: 'ddfnetwork', - }, - { - slug: 'houseoftaboo', - name: 'House of Taboo', - alias: ['hotb', 'hotab'], - url: 'https://houseoftaboo.com', - description: 'Exclusive BDSM Porn & Extreme Sex Videos Produced in VR, 4K and full HD with The Hottest European Fetish Pornstars', - network: 'ddfnetwork', - }, - { - slug: 'ddfnetworkvr', - name: 'DDF Network VR', - alias: ['ddfvr'], - url: 'https://ddfnetworkvr.com', - description: 'VR Porn Videos shot Exclusively in 180 3D 4K Virtual Reality featuring the Hottest European & American VR Pornstar Babes', - network: 'ddfnetwork', - }, - { - slug: 'eurogirlsongirls', - name: 'Euro Girls on Girls', - url: 'https://eurogirlsongirls.com', - description: 'Hot Lesbian Sex & Glamour Lesbian Porn Videos and Photos Starring Gorgeous European Pornstars in 4K and Full HD VR.', - network: 'ddfnetwork', - }, - { - slug: '1byday', - name: '1By-Day', - url: 'https://1by-day.com', - description: 'Ultra Sexy Exclusive Solo Masturbation Videos in VR, 4K and full HD showcasing Glamour Babes & Intense Orgasms', - network: 'ddfnetwork', - }, - { - slug: 'euroteenerotica', - name: 'Euro Teen Erotica', - alias: ['ete'], - url: 'https://euroteenerotica.com', - description: 'Teen Threesomes & Barely Legal Porn Videos in 4K, VR and FULL HD with Hot Nymphomaniac Teen Babes', - network: 'ddfnetwork', - }, - { - slug: 'hotlegsandfeet', - name: 'Hot Legs and Feet', - url: 'https://hotlegsandfeet.com', - description: 'Foot Fetish & Sexy Legs Porn Videos with Hot and Sexy Euro Pornstars', - network: 'ddfnetwork', - }, - { - slug: 'onlyblowjob', - name: 'Only Blowjob', - alias: ['obj'], - url: 'https://onlyblowjob.com', - description: 'Fantasy Blowjobs & POV Cock Sucking Videos and Photos Produced in VR, 4K and full HD featuring Sexy European Pornstars', - network: 'ddfnetwork', - }, - { - slug: 'fuckinhd', - name: 'Fuck in HD', - url: 'https://fuckinhd.com', - description: 'HD Hardcore Sex & XXX Fantasy Porn Videos and Photos Produced in full HD featuring a Variety of Hardcore Porn Niches.', - network: 'ddfnetwork', - parameters: { native: true }, - }, - { - slug: 'bustylover', - name: 'Busty Lover', - url: 'https://bustylover.com', - network: 'ddfnetwork', - parameters: { native: true }, - }, - // DIGITAL PLAYGROUND - { - slug: 'digitalplayground', - name: 'Digital Playground', - url: 'https://www.digitalplayground.com/scenes', - description: '', - parameters: { extract: true }, - network: 'digitalplayground', - }, - { - slug: 'episodes', - name: 'Episodes', - url: 'https://www.digitalplayground.com/scenes?site=206', - description: '', - network: 'digitalplayground', - }, - { - slug: 'flixxx', - name: 'Flixxx', - url: 'https://www.digitalplayground.com/scenes?site=207', - description: '', - network: 'digitalplayground', - }, - { - slug: 'rawcut', - name: 'Raw Cut', - url: 'https://www.digitalplayground.com/scenes?site=208', - description: '', - network: 'digitalplayground', - }, - { - slug: 'dpstarepisodes', - name: 'DP Star Episodes', - url: 'https://www.digitalplayground.com/scenes?site=209', - description: '', - network: 'digitalplayground', - }, - { - slug: 'blockbuster', - name: 'Blockbuster', - url: 'https://www.digitalplayground.com/scenes?site=211', - description: '', - network: 'digitalplayground', - }, - { - slug: 'dpparodies', - name: 'DP Parodies', - url: 'https://www.digitalplayground.com/scenes?site=212', - description: '', - tags: ['parody'], - network: 'digitalplayground', - }, - // DOGFART NETWORK - { - slug: 'blacksonblondes', - name: 'Blacks On Blondes', - url: 'https://www.blacksonblondes.com/tour', - description: 'Blacks On Blondes is the Worlds Largest and Best Interracial Sex and Interracial Porn website. Black Men and White Women. BlacksOnBlondes has 23 years worth of Hardcore Interracial Content. Featuring the entire Legendary Dogfart Movie Archive', - network: 'dogfartnetwork', - }, - { - slug: 'cuckoldsessions', - name: 'Cuckold Sessions', - url: 'https://www.cuckoldsessions.com/tour', - description: 'Dogfart, the #1 Interracial Network in the World Presents CuckoldSessions.com/tour - Hardcore Cuckold Fetish Videos', - network: 'dogfartnetwork', - }, - { - slug: 'gloryhole', - name: 'Glory Hole', - url: 'https://www.gloryhole.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'blacksoncougars', - name: 'Blacks On Cougars', - url: 'https://www.blacksoncougars.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'wefuckblackgirls', - name: 'We Fuck Black Girls', - alias: ['wfbg'], - url: 'https://www.wefuckblackgirls.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'watchingmymomgoblack', - name: 'Watching My Mom Go Black', - url: 'https://www.watchingmymomgoblack.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'interracialblowbang', - name: 'Interracial Blowbang', - url: 'https://www.interracialblowbang.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'cumbang', - name: 'Cumbang', - url: 'https://www.cumbang.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'interracialpickups', - name: 'Interracial Pickups', - url: 'https://www.interracialpickups.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'watchingmydaughtergoblack', - name: 'Watching My Daughter Go Black', - url: 'https://www.watchingmydaughtergoblack.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'zebragirls', - name: 'Zebra Girls', - url: 'https://www.zebragirls.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'gloryholeinitiations', - name: 'Gloryhole Initiations', - url: 'https://www.gloryhole-initiations.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'dogfartbehindthescenes', - name: 'Dogfart Behind The Scenes', - url: 'https://www.dogfartbehindthescenes.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'blackmeatwhitefeet', - name: 'Black Meat White Feet', - url: 'https://www.blackmeatwhitefeet.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'springthomas', - name: 'Spring Thomas', - url: 'https://www.springthomas.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'katiethomas', - name: 'Katie Thomas', - url: 'https://www.katiethomas.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'ruthblackwell', - name: 'Ruth Blackwell', - url: 'https://www.ruthblackwell.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'candymonroe', - name: 'Candy Monroe', - url: 'https://www.candymonroe.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'wifewriting', - name: 'Wife Writing', - url: 'https://www.wifewriting.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'barbcummings', - name: 'Barb Cummings', - url: 'https://www.barbcummings.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'theminion', - name: 'The Minion', - url: 'https://www.theminion.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'blacksonboys', - name: 'Blacks On Boys', - url: 'https://www.blacksonboys.com/tour', - description: '', - network: 'dogfartnetwork', - }, - { - slug: 'gloryholesandhandjobs', - name: 'Gloryholes And Handjobs', - url: 'https://www.gloryholesandhandjobs.com/tour', - description: '', - network: 'dogfartnetwork', - }, - // EVIL ANGEL - { - slug: 'evilangel', - name: 'Evil Angel', - url: 'https://www.evilangel.com', - description: 'Welcome to the award winning Evil Angel website, home to the most popular pornstars of today, yesterday and tomorrow in their most extreme and hardcore porn scenes to date. We feature almost 30 years of rough sex videos and hardcore anal porn like you\'ve never seen before, and have won countless AVN and XBiz awards including \'Best Site\' and \'Best Studio\'.', - parameters: { independent: true }, - network: 'evilangel', - }, - // FAKE HUB - { - slug: 'fakeagentuk', - name: 'Fake Agent UK', - url: 'https://www.fakehub.com/scenes?site=277', - description: '', - network: 'fakehub', - }, - { - slug: 'fakecop', - name: 'Fake Cop', - url: 'https://www.fakehub.com/scenes?site=278', - description: '', - network: 'fakehub', - }, - { - slug: 'fakehospital', - name: 'Fake Hospital', - url: 'https://www.fakehub.com/scenes?site=279', - description: '', - network: 'fakehub', - }, - { - slug: 'fakeagent', - name: 'Fake Agent', - alias: ['fka'], - url: 'https://www.fakehub.com/scenes?site=280', - description: '', - network: 'fakehub', - }, - { - slug: 'faketaxi', - name: 'Fake Taxi', - alias: ['ftx'], - url: 'https://www.fakehub.com/scenes?site=281', - description: '', - network: 'fakehub', - }, - { - slug: 'publicagent', - name: 'Public Agent', - alias: ['pba'], - url: 'https://www.fakehub.com/scenes?site=282', - description: '', - network: 'fakehub', - }, - { - slug: 'femaleagent', - name: 'Female Agent', - url: 'https://www.fakehub.com/scenes?site=283', - description: '', - network: 'fakehub', - }, - { - slug: 'femalefaketaxi', - name: 'Female Fake Taxi', - alias: ['fft'], - url: 'https://www.fakehub.com/scenes?site=284', - description: '', - network: 'fakehub', - }, - { - slug: 'fakedrivingschool', - name: 'Fake Driving School', - alias: ['fds'], - url: 'https://www.fakehub.com/scenes?site=285', - description: '', - network: 'fakehub', - }, - { - slug: 'fakehuboriginals', - name: 'Fake Hub Originals', - alias: ['fho'], - url: 'https://www.fakehub.com/scenes?site=287', - description: '', - network: 'fakehub', - }, - { - slug: 'fakehostel', - name: 'Fake Hostel', - alias: ['fhl'], - url: 'https://www.fakehub.com/scenes?site=288', - description: '', - network: 'fakehub', - }, - // FAME DIGITAL - { - slug: 'devilsfilm', - name: 'Devil\'s Film', - url: 'https://www.devilsfilm.com', - description: 'Welcome to the best porn network, DevilsFilm.com, featuring teens, MILFs, trans and interracial porn with all of your favorite pornstars in 4k ultra HD!', - parameters: { api: true }, - network: 'famedigital', - }, - { - slug: 'lowartfilms', - name: 'Low Art Films', - url: 'https://www.lowartfilms.com', - description: 'Artistic Hardcore Porn Videos', - network: 'famedigital', - parameters: { - latest: '/en/All/scenes/0/latest/', - upcoming: '/en/All/scenes/0/upcoming', - }, - }, - { - slug: 'daringsex', - name: 'Daring Sex', - url: 'https://www.daringsexhd.com/', - description: 'Welcome the official Daring Sex site, home of high quality erotica, sensual porn and hardcore exploration of the darker side of sexuality. Here you will find a variety of videos for lovers looking for a bit of extra, or something darker with an element of control.', - network: 'famedigital', - parameters: { api: true }, - show: false, // no data sources - }, - { - slug: 'peternorth', - name: 'Peter North', - url: 'https://www.peternorth.com', - description: 'PeterNorth.com features hundreds of cumshots and deepthroat blowjob videos with the hottest teens & MILFs. Watch 25 years of Peter North inside!', - network: 'famedigital', - parameters: { - latest: '/en/videos/AllCategories/0/3/0/All-Dvds/0/latest/', - upcoming: '/en/videos/AllCategories/0/3/0/All-Dvds/0/upcoming', - }, - }, - { - slug: 'roccosiffredi', - name: 'Rocco Siffredi', - url: 'https://www.roccosiffredi.com', - description: 'Welcome to the official RoccoSiffredi.com, the Italian Stallion, with hardcore anal fucking and rough sex from the man himself who has coined the term hardcore.', - parameters: { api: true }, - network: 'famedigital', - }, - { - slug: 'silverstonedvd', - name: 'Silverstone DVD', - url: 'https://www.silverstonedvd.com', - description: 'Welcome to SilverStoneDVDs.com to enjoy unlimited streaming & downloads of teen porn, hot latina anal, young and dumb blowjob, DPs and hardcore porn.', - network: 'famedigital', - parameters: { - latest: '/en/All/scenes/0/latest/', - upcoming: '/en/All/scenes/0/upcoming', - }, - }, - { - slug: 'silviasaint', - name: 'Silvia Saint', - url: 'https://www.silviasaint.com', - description: 'Welcome to Silvia Saint official website. You can see Silvia Saint videos, pictures and blog!', - network: 'famedigital', - parameters: { - latest: '/en/scenes/All/0/', - upcoming: '/en/scenes/All/0/1/upcoming', - }, - }, - { - slug: 'whiteghetto', - name: 'White Ghetto', - url: 'https://www.whiteghetto.com', - description: 'Welcome to WhiteGhetto.com. Home of MILFs, GILFs, Midget porn, Indian babes, hairy pussies and more unusual and oddity porn!', - network: 'famedigital', - parameters: { - latest: '/en/scenes/All/0/superCat/0/latest/', - upcoming: '/en/scenes/All/0/superCat/0/upcoming', - }, - }, - // FANTASY MASSAGE - // Club Fantasy Massage is an aggregate site - { - slug: 'fantasymassage', - name: 'Fantasy Massage', - alias: ['fms'], - url: 'https://www.fantasymassage.com', - network: 'fantasymassage', - parameters: { - latest: 'https://www.fantasymassage.com/en/allvideos/fantasymassage/AllCategories/0/AllPornstars/0/updates/', - upcoming: 'https://www.fantasymassage.com/en/allvideos/fantasymassage/AllCategories/0/Actor/0/upcoming/', - }, - }, - { - slug: 'allgirlmassage', - name: 'All Girl Massage', - alias: ['agm'], - url: 'https://www.allgirlmassage.com', - network: 'fantasymassage', - parameters: { - latest: 'https://www.fantasymassage.com/en/allvideos/allgirlmassage/AllCategories/0/AllPornstars/0/updates/', - upcoming: 'https://www.fantasymassage.com/en/allvideos/allgirlmassage/AllCategories/0/Actor/0/upcoming/', - photos: 'https://www.fantasymassage.com/en/photo', - }, - }, - { - slug: 'nurumassage', - name: 'Nuru Massage', - alias: ['num'], - url: 'https://www.nurumassage.com', - network: 'fantasymassage', - parameters: { - latest: 'https://www.fantasymassage.com/en/allvideos/nurumassage/AllCategories/0/AllPornstars/0/updates/', - upcoming: 'https://www.fantasymassage.com/en/allvideos/nurumassage/AllCategories/0/Actor/0/upcoming/', - photos: 'https://www.fantasymassage.com/en/photo', - }, - }, - { - slug: 'trickyspa', - name: 'Tricky Spa', - alias: ['tspa'], - url: 'https://www.trickyspa.com', - network: 'fantasymassage', - parameters: { - latest: 'https://www.fantasymassage.com/en/allvideos/trickyspa/AllCategories/0/AllPornstars/0/updates/', - upcoming: 'https://www.fantasymassage.com/en/allvideos/trickyspa/AllCategories/0/Actor/0/upcoming/', - photos: 'https://www.fantasymassage.com/en/photo', - }, - }, - { - slug: 'soapymassage', - name: 'Soapy Massage', - url: 'https://www.soapymassage.com', - network: 'fantasymassage', - parameters: { - latest: 'https://www.fantasymassage.com/en/allvideos/soapymassage/AllCategories/0/AllPornstars/0/updates/', - upcoming: 'https://www.fantasymassage.com/en/allvideos/soapymassage/AllCategories/0/Actor/0/upcoming/', - photos: 'https://www.fantasymassage.com/en/photo', - }, - }, - { - slug: 'milkingtable', - name: 'Milking Table', - url: 'https://www.milkingtable.com', - network: 'fantasymassage', - parameters: { - latest: 'https://www.fantasymassage.com/en/allvideos/milkingtable/AllCategories/0/AllPornstars/0/updates/', - upcoming: 'https://www.fantasymassage.com/en/allvideos/milkingtable/AllCategories/0/Actor/0/upcoming/', - photos: 'https://www.fantasymassage.com/en/photo', - }, - }, - { - slug: 'massageparlor', - name: 'Massage Parlor', - url: 'https://www.massage-parlor.com', - network: 'fantasymassage', - parameters: { - latest: 'https://www.fantasymassage.com/en/allvideos/massage-parlor/AllCategories/0/AllPornstars/0/updates/', - upcoming: 'https://www.fantasymassage.com/en/allvideos/massage-parlor/AllCategories/0/Actor/0/upcoming/', - photos: 'https://www.fantasymassage.com/en/photo', - }, - }, - // FREEONES - { - slug: 'freeones', - name: 'FreeOnes', - url: 'https://www.freeones.com', - network: 'freeones', - }, - { - slug: 'freeoneslegacy', - name: 'FreeOnes (Legacy)', - url: 'https://www.freeones.com', - network: 'freeones', - }, - // FULL PORN NETWORK - { - slug: 'analbbc', - name: 'Anal BBC', - url: 'https://analbbc.com', - tags: ['anal', 'bbc'], - network: 'fullpornnetwork', - }, - { - slug: 'analviolation', - name: 'Anal Violation', - url: 'https://analviolation.com', - tags: ['anal'], - network: 'fullpornnetwork', - }, - { - slug: 'analized', - name: 'ANALIZED', - url: 'https://analized.com', - tags: ['anal'], - network: 'fullpornnetwork', - }, - { - slug: 'baddaddypov', - name: 'Bad Daddy POV', - alias: ['bdpov'], - url: 'https://baddaddypov.com', - tags: ['pov', 'family'], - network: 'fullpornnetwork', - }, - { - slug: 'dtfsluts', - name: 'DTF Sluts', - url: 'https://dtfsluts.com', - network: 'fullpornnetwork', - }, - { - slug: 'girlfaction', - name: 'Girlfaction', - url: 'https://girlfaction.com', - tags: ['lesbian'], - network: 'fullpornnetwork', - }, - { - slug: 'hergape', - name: 'Her Gape', - url: 'https://hergape.com', - tags: ['anal'], - network: 'fullpornnetwork', - }, - { - slug: 'homemadeanalwhores', - name: 'Homemade Anal Whores', - url: 'https://homemadeanalwhores.com', - tags: ['anal'], - network: 'fullpornnetwork', - }, - { - slug: 'jamesdeen', - name: 'James Deen', - url: 'https://jamesdeen.com', - network: 'fullpornnetwork', - }, - { - slug: 'onlyprince', - name: 'Only Prince', - url: 'https://onlyprince.com', - tags: ['bbc'], - network: 'fullpornnetwork', - }, - { - slug: 'pervertgallery', - name: 'Pervert Gallery', - url: 'http://pervertgallery.com', - network: 'fullpornnetwork', - }, - { - slug: 'povperverts', - name: 'POV Perverts', - url: 'http://povperverts.net', - tags: ['pov'], - network: 'fullpornnetwork', - }, - { - slug: 'teenageanalsluts', - name: 'Teenage Anal Sluts', - url: 'https://teenageanalsluts.com', - tags: ['anal'], - network: 'fullpornnetwork', - }, - { - slug: 'twistedvisual', - name: 'Twisted Visual', - url: 'https://twistedvisual.com', - network: 'fullpornnetwork', - }, - { - slug: 'yourmomdoesanal', - name: 'Your Mom Does Anal', - url: 'http://yourmomdoesanal.com', - tags: ['anal', 'milf'], - network: 'fullpornnetwork', - }, - { - slug: 'yourmomdoesporn', - name: 'Your Mom Does Porn', - url: 'https://yourmomdoesporn.com', - tags: ['milf'], - network: 'fullpornnetwork', - }, - { - slug: 'mugfucked', - name: 'Mugfucked', - url: 'https://mugfucked.com', - tags: ['facefucking', 'blowjob'], - network: 'fullpornnetwork', - }, - // GIRLSWAY - { - slug: 'girlsway', - name: 'Girlsway', - alias: ['gw'], - 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!', - tags: ['lesbian'], - network: 'girlsway', - parameters: { - scene: 'https://www.girlsway.com/en/video/girlsway', - }, - }, - { - slug: 'girlstryanal', - name: 'Girls Try Anal', - alias: ['gta'], - url: 'https://www.girlstryanal.com', - network: 'girlsway', - parameters: { - referer: 'https://www.girlsway.com', - mobile: 'https://m.dpfanatics.com/en/video', - }, - }, - { - slug: 'mommysgirl', - name: 'Mommy\'s Girl', - alias: ['mmgs'], - url: 'https://www.mommysgirl.com', - network: 'girlsway', - parameters: { - mobile: 'https://m.dpfanatics.com/en/video', - }, - }, - { - slug: 'webyoung', - name: 'Web Young', - url: 'https://www.webyoung.com', - network: 'girlsway', - parameters: { - referer: 'https://www.girlsway.com', - mobile: 'https://m.dpfanatics.com/en/video', - }, - }, - { - slug: 'sextapelesbians', - name: 'Sex Tape Lesbians', - url: 'https://www.sextapelesbians.com', - network: 'girlsway', - parameters: { - scene: 'https://www.girlsway.com/en/video/sextapelesbians', // sextapelesbians.com redirects to isthisreal.com - referer: 'https://www.girlsway.com', - }, - }, - { - slug: 'momsonmoms', - name: 'Moms On Moms', - url: 'https://www.girlsway.com/en/videos/momsonmoms', - network: 'girlsway', - parameters: { - scene: 'https://www.girlsway.com/en/video/sextapelesbians', - referer: 'https://www.girlsway.com', - }, - }, - // HUSSIE PASS - { - slug: 'hussiepass', - name: 'Hussie Pass', - url: 'https://www.hussiepass.com', - network: 'hussiepass', - }, - { - slug: 'eyeontheguy', - name: 'Eye On The Guy', - url: 'https://eyeontheguy.com', - tags: ['male-focus'], - network: 'hussiepass', - parameters: { - t1: true, - }, - }, - { - slug: 'seehimfuck', - name: 'See Him Fuck', - url: 'https://seehimfuck.com', - tags: ['male-focus'], - network: 'hussiepass', - parameters: { - tour: true, - }, - }, - { - slug: 'interracialpovs', - name: 'Interracial POVs', - url: 'https://www.interracialpovs.com', - tags: ['interracial', 'pov'], - network: 'hussiepass', - parameters: { - tour: true, - }, - }, - { - slug: 'povpornstars', - name: 'POV Pornstars', - url: 'http://www.povpornstars.com', - tags: ['pov'], - network: 'hussiepass', - parameters: { - latest: 'http://www.povpornstars.com/tour/categories/movies_%d_d.html', - profile: 'http://www.povpornstars.com/tour/models/%s.html', - tour: true, - }, - }, - // HUSH PASS - { - slug: 'shotherfirst', - name: 'Shot Her First', - url: 'https://shotherfirst.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/shot-her-first_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'whitezilla', - name: 'WhiteZilla', - url: 'https://whitezilla.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/whitezilla_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'frathousefuckfest', - name: 'Frat House Fuck Fest', - url: 'https://frathousefuckfest.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/frat-house-fuck-fest_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'freakyfirsttimers', - name: 'Freaky First Timers', - url: 'https://freakyfirsttimers.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/freaky-first-timers_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'milfinvaders', - name: 'MILF Invaders', - url: 'https://milfinvaders.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/milf-invaders_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'housewivesneedcash', - name: 'Housewives Need Cash', - url: 'https://housewivesneedcash.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/housewives-need-cash_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'bubblebuttbonanza', - name: 'Bubble Butt Bonanza', - url: 'https://bubblebuttbonanza.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/bubble-butt-bonanza_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'suburbansexparty', - name: 'Suburban Sex Party', - url: 'https://suburbansexparty.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/suburban-sex-party_%_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'buttnakedinthestreets', - name: 'Butt Naked In The Streets', - url: 'https://buttnakedinthestreets.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/ButtNakedInStreets_%d_d.html', - media: 'https://hushpass.com', - match: 'Butt Naked In Streets', - t1: true, - }, - }, - { - slug: 'muffbumperpatrol', - name: 'Muff Bumper Patrol', - url: 'https://muffbumperpatrol.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/muff-bumper-patrol_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'biggathananigga', - name: 'Bigga Than A Nigga', - url: 'https://biggathananigga.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/bigga-than-a-nigga_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'bachelorpartyfuckfest', - name: 'Bachelor Party Fuck Fest', - url: 'https://bachelorpartyfuckfest.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/bachelor-party-fuck-fest_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'teencumdumpsters', - name: 'Teen Cum Dumpsters', - url: 'https://teencumdumpsters.com', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/teen-cum-dumpsters_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'povhunnies', - name: 'POV Hunnies', - network: 'hushpass', - parameters: { - latest: 'https://hushpass.com/t1/categories/POVHunnies_%d_d.html', - media: 'https://hushpass.com', - t1: true, - }, - }, - { - slug: 'hushpass', - name: 'Hush Pass', - url: 'https://hushpass.com', - network: 'hushpass', - parameters: { - t1: true, - accFilter: true, - }, - }, - // INTERRACIAL PASS - { - slug: '2bigtobetrue', - name: '2 Big To Be True', - url: 'https://www.2bigtobetrue.com/', - tags: ['interracial'], - network: 'interracialpass', - parameters: { - latest: 'https://www.interracialpass.com/t1/categories/2-big-to-be-true_%d_d.html', - media: 'https://www.interracialpass.com', - t1: true, - }, - }, - { - slug: 'abominableblackman', - name: 'Abominable Black Man', - url: 'https://www.abominableblackman.com/', - tags: ['interracial'], - network: 'interracialpass', - parameters: { - latest: 'https://www.interracialpass.com/t1/categories/abominable-black-man_%d_d.html', - media: 'https://www.interracialpass.com', - t1: true, - }, - }, - { - slug: 'bootyannihilation', - name: 'Booty Annihilation', - tags: ['interracial'], - network: 'interracialpass', - parameters: { - latest: 'https://www.interracialpass.com/t1/categories/BootyAnnihilation_%d_d.html', - media: 'https://www.interracialpass.com', - t1: true, - }, - }, - { - slug: 'daddysworstnightmare', - name: 'Daddy\'s Worst Nightmare', - url: 'https://www.daddysworstnightmare.com/', - tags: ['interracial'], - network: 'interracialpass', - parameters: { - latest: 'https://www.interracialpass.com/t1/categories/daddys-worst-nightmare_%d_d.html', - media: 'https://www.interracialpass.com', - t1: true, - }, - }, - { - slug: 'monstercockfuckfest', - name: 'Monster Cock Fuck Fest', - url: 'https://www.monstercockfuckfest.com/', - tags: ['interracial'], - network: 'interracialpass', - parameters: { - latest: 'https://www.interracialpass.com/t1/categories/monster-cock-fuck-fest_%d_d.html', - media: 'https://www.interracialpass.com', - t1: true, - }, - }, - { - slug: 'mydaughtersfuckingablackdude', - name: 'My Daughter\'s Fucking A Black Dude', - url: 'https://www.mydaughtersfuckingablackdude.com/', - tags: ['interracial'], - network: 'interracialpass', - parameters: { - latest: 'https://www.interracialpass.com/t1/categories/my-daughters-fucking-a-black-dude_%d_d.html', - media: 'https://www.interracialpass.com', - t1: true, - }, - }, - { - slug: 'mymomsfuckingblackzilla', - name: 'My Mom\'s Fucking Blackzilla', - url: 'https://www.mymomsfuckingblackzilla.com/', - tags: ['interracial'], - network: 'interracialpass', - parameters: { - latest: 'https://www.interracialpass.com/t1/categories/my-moms-fucking-blackzilla_%d_d.html', - media: 'https://www.interracialpass.com', - t1: true, - }, - }, - { - slug: 'mywifesfirstmonstercock', - name: 'My Wife\'s First Monster Cock', - url: 'https://www.mywifesfirstmonstercock.com/', - tags: ['interracial'], - network: 'interracialpass', - parameters: { - latest: 'https://www.interracialpass.com/t1/categories/my-wifes-first-monster-cock_%d_d.html', - media: 'https://www.interracialpass.com', - match: 'My Wifes First Monster Cock', - t1: true, - }, - }, - { - slug: 'interracialpass', - name: 'Interracial Pass', - url: 'https://www.interracialpass.com', - tags: ['interracial'], - network: 'interracialpass', - parameters: { - t1: true, - accFilter: true, - }, - }, - // INSEX - { - slug: 'sexuallybroken', - name: 'Sexually Broken', - alias: ['seb'], - url: 'https://www.sexuallybroken.com', - tags: ['bdsm'], - network: 'insex', - }, - { - slug: 'infernalrestraints', - name: 'Infernal Restraints', - alias: ['infr'], - url: 'https://www.infernalrestraints.com', - tags: ['bdsm'], - network: 'insex', - }, - { - slug: 'hardtied', - name: 'Hardtied', - url: 'https://www.hardtied.com', - tags: ['bdsm'], - network: 'insex', - }, - { - slug: 'realtimebondage', - name: 'Real Time Bondage', - alias: ['rtb'], - url: 'https://www.realtimebondage.com', - tags: ['bdsm', 'live'], - network: 'insex', - }, - { - slug: 'topgrl', - name: 'TopGrl', - alias: ['tg'], - url: 'https://www.topgrl.com', - tags: ['bdsm', 'femdom'], - network: 'insex', - }, - { - slug: 'paintoy', - name: 'Paintoy', - url: 'https://www.paintoy.com', - tags: ['bdsm'], - network: 'insex', - }, - { - slug: 'aganmedon', - name: 'Agan Medon', - url: 'https://www.aganmedon.com', - tags: ['bdsm', 'animated'], - network: 'insex', - }, - { - slug: 'sensualpain', - name: 'Sensual Pain', - url: 'https://www.sensualpain.com', - tags: ['bdsm'], - network: 'insex', - }, - // JAYS POV - { - slug: 'jayspov', - name: 'Jay\'s POV', - url: 'https://jayspov.net', - network: 'jayrock', - }, - { - slug: 'cospimps', - name: 'CosPimps', - url: 'https://cospimps.com', - network: 'jayrock', - }, - { - slug: 'blackforwife', - name: 'Black for Wife', - url: 'https://www.blackforwife.com', - network: 'jayrock', - parameters: { - referer: 'https://freetour.adulttime.com/en/blackforwife', - useGamma: true, - scene: false, - deep: 'https://21sextury.com/en/video', - photos: false, - }, - }, - // JESSE LOADS MONSTER FACIALS - { - slug: 'jesseloadsmonsterfacials', - name: 'Jesse Loads Monster Facials', - url: 'http://www.jesseloadsmonsterfacials.com', - network: 'jesseloadsmonsterfacials', - tags: ['facial', 'blowjob'], - parameters: { - independent: true, - }, - }, - // JULES JORDAN - { - slug: 'julesjordan', - name: 'Jules Jordan', - url: 'https://www.julesjordan.com', - description: 'Jules Jordan\'s Official Membership Site', - network: 'julesjordan', - }, - { - slug: 'theassfactory', - name: 'The Ass Factory', - url: 'https://www.theassfactory.com', - network: 'julesjordan', - }, - { - slug: 'spermswallowers', - name: 'Sperm Swallowers', - url: 'https://www.spermswallowers.com', - network: 'julesjordan', - }, - { - slug: 'manuelferrara', - name: 'Manuel Ferrara', - alias: ['mfa'], - url: 'https://www.manuelferrara.com', - network: 'julesjordan', - }, - { - slug: 'girlgirl', - name: 'Girl Girl', - url: 'https://www.girlgirl.com', - tags: ['lesbian'], - network: 'julesjordan', - }, - // KELLY MADISON MEDIA - { - slug: 'teenfidelity', - name: 'Teen Fidelity', - alias: ['tf'], - url: 'https://www.teenfidelity.com', - description: 'Home of Kelly Madison and Ryan Madison', - network: 'kellymadison', - }, - { - slug: 'pornfidelity', - name: 'Porn Fidelity', - alias: ['pf'], - url: 'https://www.pornfidelity.com', - description: 'Home of Kelly Madison and Ryan Madison', - network: 'kellymadison', - }, - { - slug: 'kellymadison', - name: 'Kelly Madison', - url: 'https://www.pornfidelity.com', - description: 'Home of Kelly Madison and Ryan Madison', - network: 'kellymadison', - }, - // KINK - { - slug: 'thirtyminutesoftorment', - name: '30 Minutes of Torment', - url: 'https://www.kink.com/channel/30minutesoftorment', - description: 'Thick-Muscled Men Endure 30 Minutes Of BDSM Torment By A Pain-Inducing Dom. Can they take 30 Minutes of Torment? Watch as top gay pornstars take on the challenge of a lifetime. Bondage, BDSM, punishment, huge insertions, & more!', - network: 'kink', - }, - { - slug: 'boundgangbangs', - name: 'Bound Gangbangs', - alias: ['bgb', 'bgbs'], - url: 'https://www.kink.com/channel/boundgangbangs', - description: 'Powerless whores tied in bondage and stuffed with a cock in every hole. At BoundGangbangs women get surprise extreme gangbangs, blindfolds, deepthroat blowjobs, sex punishment, bondage, double penetration and interracial sex.', - network: 'kink', - }, - { - slug: 'boundgods', - name: 'Bound Gods', - url: 'https://www.kink.com/channel/boundgods', - description: 'Muscle Studs Are Bound, Gagged & Spread For A Deep Cock Pounding. Not even the most rock hard muscled studs can escape punishment & submission on BoundGods.com Watch the hottest studs get tied down, fucked & submitted.', - tags: ['gay'], - network: 'kink', - }, - { - slug: 'boundinpublic', - name: 'Bound in Public', - url: 'https://www.kink.com/channel/boundinpublic', - description: 'Cum Starved Sluts Humiliated And Fucked Hard In Public By Hung Studs.', - network: 'kink', - }, - { - slug: 'brutalsessions', - name: 'Brutal Sessions', - url: 'https://www.kink.com/channel/brutalsessions', - description: "Hardcore BDSM jam packed with XXX fucking in bondage! We're taking dungeon sex beyond the castle!", - network: 'kink', - }, - { - slug: 'buttmachineboys', - name: 'Butt Machine Boys', - url: 'https://www.kink.com/channel/buttmachineboys', - description: 'Powerful Fucking Machines Pound Hot Men Hard & Deep.', - tags: ['gay'], - network: 'kink', - }, - { - slug: 'devicebondage', - name: 'Device Bondage', - alias: ['deb'], - url: 'https://www.kink.com/channel/devicebondage', - description: 'The Domination Of Sluts In Barbaric Metal Devices. Device Bondage takes BDSM porn to new levels with extreme restraints & unique devices with beautiful pornstars to huge, forced squirting orgasms.', - network: 'kink', - }, - { - slug: 'divinebitches', - name: 'Divine Bitches', - url: 'https://www.kink.com/channel/divinebitches', - description: 'Beautiful Women Dominate Submissive Men With Pain, Humiliation And Strap-On Fucking. The best in femdom and bondage. Men on Divine Bitches respond with obedience, ass worship, cunt worship, oral servitude, pantyhose worship, and foot worship.', - tags: ['femdom'], - network: 'kink', - }, - { - slug: 'electrosluts', - name: 'Electrosluts', - url: 'https://www.kink.com/channel/electrosluts', - description: 'Lezdoms Take Submissive Sluts To Their Limits, Shocking & Tormenting Their Wet Hot Pussies. Pornstars live out their electric bondage fantasies while dominatrixes use electrodes, paddles, caddle prods, & more to bring them to intense orgasms!', - network: 'kink', - }, - { - slug: 'everythingbutt', - name: 'Everything Butt', - url: 'https://www.kink.com/channel/everythingbutt', - description: 'Gaping Anal Holes Are Stuffed & Stretched To The Max. Anal Fisting, Enemas & Rimming Has Never Tasted So Good. EverythingButt.com explores the extreme limits of FemDom lesbian anal. Watch asses get destroyed by brutal fistings, huge insertions, double anal & more!', - network: 'kink', - }, - { - slug: 'filthyfemdom', - name: 'Filthy Femdom', - url: 'https://www.kink.com/channel/filthyfemdom', - description: 'Powerful women dominate your dirty dreams of sweet pain, seductive bondage, and sexual servitude.', - tags: ['femdom'], - network: 'kink', - }, - { - slug: 'familiestied', - name: 'Families Tied', - url: 'https://www.kink.com/channel/familiestied', - description: 'Intense BDSM family role play threesomes & more.', - network: 'kink', - }, - { - slug: 'footworship', - name: 'Foot Worship', - url: 'https://www.kink.com/channel/footworship', - description: 'Satisfy Your Foot Fetish With The Kinkiest Foot Action. Enjoy Trampling, Foot Jobs, High Heels, And Pantyhose.', - network: 'kink', - }, - { - slug: 'fuckedandbound', - name: 'Fucked and Bound', - alias: ['fab'], - url: 'https://www.kink.com/channel/fuckedandbound', - description: 'Extreme Anal, Rope Bondage, & Brutal Face Fucking.', - network: 'kink', - }, - { - slug: 'fuckingmachines', - name: 'Fucking Machines', - alias: ['fm', 'fum'], - url: 'https://www.kink.com/channel/fuckingmachines', - description: 'Machines Fucking Squirting Pussies With Extreme Insertions. Fucking Machines is the ultimate hardcore sex toy porn. Huge dildos strapped to sex machines relentlessly fucking pornstars to real squirting oragsms!', - network: 'kink', - }, - { - slug: 'hardcoregangbang', - name: 'Hardcore Gangbang', - url: 'https://www.kink.com/channel/hardcoregangbang', - description: "Where all women's hardcore gangbang fantasies come true. Watch extreme, brutal gangbangs with pornstars, models, & MILFs that crave cock in every hole. HardcoreGangbang.com has the best creampie gang bangs online.", - network: 'kink', - }, - { - slug: 'hogtied', - name: 'Hogtied', - alias: ['ht'], - url: 'https://www.kink.com/channel/hogtied', - description: 'Your favorite girls restrained with rope, punished & trained. Hogtied is the original extreme bondage porn website. Watch top pornstars and pain sluts in brutal bondage, getting tormented, and forced to orgasm!', - network: 'kink', - }, - { - slug: 'kinkfeatures', - name: 'Kink Features', - url: 'https://www.kink.com/channel/kinkfeatures', - description: 'Curated scenes by Kink\'s very best directors.', - network: 'kink', - }, - { - slug: 'kinkuniversity', - name: 'Kink University', - url: 'https://www.kink.com/channel/kinkuniversity', - description: 'Learn BDSM Technical Skills & Theories From Respected Teachers In The Kink Community. Learn BDSM skills and improve your sex techniques. Video tutorials feature top sex ed experts and hardcore demos on topics from bondage to relationships.', - network: 'kink', - }, - { - slug: 'meninpain', - name: 'Men In Pain', - url: 'https://www.kink.com/channel/meninpain', - description: 'Submissive Men Violated With Verbal Humiliation And Harsh Punishment By Beautiful Dominatrices.', - network: 'kink', - }, - { - slug: 'menonedge', - name: 'Men on Edge', - url: 'https://www.kink.com/channel/menonedge', - description: "Hot Guys Begging To Cum Are Brought To The Edge Of Complete Submission And Allowed To Blow Their Loads. Men on Edge has perfected the art of gay BDSM & edging porn. Watch straight men bound up & edged by dominant gay pornstars until they can't help but cum!", - tags: ['gay'], - network: 'kink', - }, - { - slug: 'nakedkombat', - name: 'Naked Kombat', - url: 'https://www.kink.com/channel/nakedkombat', - description: 'Fight Fit Studs Go Head To Head In A Battle For Dominance. The Loser Gets Pinned And Punish Fucked Without Mercy', - network: 'kink', - }, - { - slug: 'publicdisgrace', - name: 'Public Disgrace', - alias: ['pud'], - url: 'https://www.kink.com/channel/publicdisgrace', - description: 'Women Bound Stripped And Punished In Public Get Hardcore Fucked Where Everyone Can See. Unscripted public humiliation & punishment of submissive slaves in real life locations. PublicDisgrace features the best outdoor BDSM & voyeur porn!', - network: 'kink', - }, - { - slug: 'sadisticrope', - name: 'Sadistic Rope', - alias: ['sr'], - url: 'https://www.kink.com/channel/sadisticrope', - description: 'Innocence Taken By Extreme Rope Bondage, Hardcore BDSM And Pussy-Destroying Orgasms.', - network: 'kink', - }, - { - slug: 'sexandsubmission', - name: 'Sex and Submission', - alias: ['sas'], - url: 'https://www.kink.com/channel/sexandsubmission', - description: 'Submissive Sluts Are Dominated With Rough Sex And Bondage. Real pornstars, hardcore bondage, master & slave roles are what SexAndSubmission.com is all about. Watch submissive sluts give in to total domination!', - network: 'kink', - }, - { - slug: 'strugglingbabes', - name: 'Struggling Babes', - url: 'https://www.kink.com/channel/strugglingbabes', - description: 'Demystifying and celebrating alternative sexuality by providing the most authentic kinky videos. Experience the other side of porn.', - network: 'kink', - }, - { - slug: 'thetrainingofo', - name: 'The Training of O', - alias: ['tto'], - url: 'https://www.kink.com/channel/thetrainingofo', - description: 'Slaves Are Trained And Rewarded With Hardcore Bondage And Sex. Watch real pornstars undergo extreme slave training through hardcore bondage & BDSM porn. The Training of O is the ultimate slave / master experience!', - network: 'kink', - }, - { - slug: 'theupperfloor', - name: 'The Upper Floor', - alias: ['tuf'], - url: 'https://www.kink.com/channel/theupperfloor', - description: 'Trained slaves serve the house and their master in intense BDSM and kinky threesomes. The Upper Floor is a voyeuristic look into BDSM and fetish porn shoots with real submissive pornstars living out their kinky fantasies live on cam.', - network: 'kink', - }, - { - slug: 'tspussyhunters', - name: 'TS Pussy Hunters', - url: 'https://www.kink.com/channel/tspussyhunters', - description: 'Hot TS cocks prey on the wet pussies of submissive ladies who are fucked hard till they cum. Dominant TS femme fatales with the hardest dicks, the softest tits, and the worst intentions dominate, bind, and punish bitches on the ultimate transfucking porn site.', - tags: ['transsexual'], - network: 'kink', - }, - { - slug: 'tsseduction', - name: 'TS Seduction', - url: 'https://www.kink.com/channel/tsseduction', - description: 'Sexy TS Women With Huge Cocks Dominate The Holes Of Straight Boys. Real TS women who are drop-dead gorgeous from their pretty faces to their big tits to their hard TS cocks. TS Seduction is the ultimate in transsexual bondage porn.', - network: 'kink', - }, - { - slug: 'ultimatesurrender', - name: 'Ultimate Surrender', - url: 'https://www.kink.com/channel/ultimatesurrender', - description: 'Competitive Female Wrestling Where The Loser Gets Strap-On Punish Fucked. Ultimate Surrender features hardcore naked female wrestling porn videos where the winner gets to dominate the loser with some kinky lesbian FemDom!', - network: 'kink', - }, - { - slug: 'waterbondage', - name: 'Water Bondage', - url: 'https://www.kink.com/channel/waterbondage', - description: 'Helpless Bound Beauties Sprayed, Dunked And Tormented Until They Cum Hard & Wet.', - network: 'kink', - }, - { - slug: 'whippedass', - name: 'Whipped Ass', - alias: ['wpa', 'wa'], - url: 'https://www.kink.com/channel/whippedass', - description: 'Beautiful Submissive Sluts Take A Hard Fucking From Powerful Dominant Women. Watch brutal lesbian dominatrixes push submissive sluts to their orgasmic breaking points on WhippedAss! Hardcore fisting, huge strapons & face sitting!', - network: 'kink', - }, - { - slug: 'wiredpussy', - name: 'Wired Pussy', - url: 'https://www.kink.com/channel/wiredpussy', - description: 'Gorgeous Women Submit To Electricity, Are Zapped, Shocked & Prodded To Orgasm.', - network: 'kink', - }, - // LEGALPORNO - { - slug: 'legalporno', - name: 'LegalPorno', - alias: ['clip'], - url: 'https://www.legalporno.com', - description: 'The Best HD Porn For You!', - parameters: { independent: true }, - network: 'legalporno', - }, - // METRO HD - { - slug: 'devianthardcore', - name: 'Deviant Hardcore', - url: 'https://www.devianthardcore.com', - tags: ['bdsm'], - parameters: { - siteId: 305, - native: true, - }, - network: 'metrohd', - }, - { - slug: 'shewillcheat', - name: 'She Will Cheat', - url: 'https://www.shewillcheat.com', - parameters: { - siteId: 306, - native: true, - }, - network: 'metrohd', - }, - { - slug: 'familyhookups', - name: 'Family Hookups', - url: 'https://www.familyhookups.com', - tags: ['family'], - parameters: { - siteId: 307, - native: true, - }, - network: 'metrohd', - }, - { - slug: 'kinkyspa', - name: 'Kinky Spa', - url: 'https://www.kinkyspa.com', - tags: ['massage'], - parameters: { - siteId: 308, - native: true, - }, - network: 'metrohd', - }, - { - slug: 'girlgrind', - name: 'Girl Grind', - url: 'https://www.girlgrind.com', - tags: ['lesbian'], - parameters: { - siteId: 309, - native: true, - }, - network: 'metrohd', - }, - // MEN - { - slug: 'bigdicksatschool', - name: 'Big Dicks At School', - url: 'https://www.bigdicksatschool.com', - description: '', - parameters: { siteId: 252 }, - tags: ['gay'], - network: 'men', - }, - { - slug: 'drillmyhole', - name: 'Drill My Hole', - url: 'https://www.drillmyhole.com', - description: '', - parameters: { siteId: 253 }, - tags: ['gay'], - network: 'men', - }, - { - slug: 'str8togay', - name: 'Str8 to Gay', - url: 'https://www.str8togay.com', - tags: ['gay'], - parameters: { siteId: 254 }, - network: 'men', - }, - { - slug: 'thegayoffice', - name: 'The Gay Office', - url: 'https://www.thegayoffice.com', - tags: ['gay'], - parameters: { siteId: 255 }, - network: 'men', - }, - { - slug: 'jizzorgy', - name: 'Jizz Orgy', - url: 'https://www.jizzorgy.com', - tags: ['gay'], - parameters: { siteId: 256 }, - network: 'men', - }, - { - slug: 'menofuk', - name: 'Men of UK', - url: 'https://www.menofuk.com', - tags: ['gay'], - parameters: { siteId: 258 }, - network: 'men', - }, - { - slug: 'toptobottom', - name: 'Top to Bottom', - url: 'https://www.toptobottom.com', - tags: ['gay'], - parameters: { siteId: 259 }, - network: 'men', - }, - { - slug: 'godsofmen', - name: 'Gods of Men', - url: 'https://www.godsofmen.com', - tags: ['gay'], - parameters: { siteId: 260 }, - network: 'men', - }, - // MINDGEEK - { - slug: 'tube8vip', - name: 'Tube8Vip', - url: 'https://www.tube8vip.com', - description: '', - parameters: { native: true }, - network: 'mindgeek', - }, - { - slug: 'transangels', - name: 'TransAngels', - url: 'https://www.transangels.com', - tags: ['transsexual'], - parameters: { native: true }, - network: 'mindgeek', - }, - { - slug: 'trueamateurs', - name: 'True Amateurs', - url: 'https://www.trueamateurs.com', - description: 'TrueAmateurs.com is the best homemade porn from real amateurs. Watch these real hot couples in our exclusive scenes.', - parameters: { native: true }, - network: 'mindgeek', - }, - // MIKE ADRIANO - { - slug: 'trueanal', - name: 'True Anal', - url: 'https://trueanal.com', - description: 'TrueAnal is the hottest site with all hardcore Anal content and only the most popular pornstars getting their asses pounded and gapped with huge cock and more!', - tags: ['anal'], - network: 'mikeadriano', - }, - { - slug: 'allanal', - name: 'All Anal', - url: 'https://allanal.com', - description: 'Popular babes getting their tight asses filled with cock! Pure anal fucking only at AllAnal!', - tags: ['anal', 'mff'], - network: 'mikeadriano', - }, - { - slug: 'nympho', - name: 'Nympho', - url: 'https://nympho.com', - description: 'These Babes have an appetite for nasty, sloppy fucking!', - network: 'mikeadriano', - }, - { - slug: 'swallowed', - name: 'Swallowed', - url: 'https://swallowed.com', - description: 'Swallowed is a Premium adult website for the hottest Blowjobs content online with only the most popular pornstars swallowing cock!', - tags: ['blowjob', 'deepthroat', 'facefucking'], - network: 'mikeadriano', - }, - // MILE HIGH MEDIA - { - slug: 'doghousedigital', - name: 'Doghouse Digital', - url: 'https://www.doghousedigital.com', - parameters: { siteId: 321 }, - network: 'milehighmedia', - }, - { - slug: 'milehighmedia', - name: 'Mile High Media', - url: 'https://www.milehighmedia.com/scenes?site=323', - network: 'milehighmedia', - }, - { - slug: 'realityjunkies', - name: 'Reality Junkies', - url: 'https://www.realityjunkies.com', - parameters: { siteId: 324 }, - network: 'milehighmedia', - }, - { - slug: 'sweetheartvideo', - name: 'Sweetheart Video', - url: 'https://www.sweetheartvideo.com', - parameters: { siteId: 325 }, - network: 'milehighmedia', - }, - { - slug: 'sweetsinner', - name: 'Sweet Sinner', - url: 'https://www.sweetsinner.com', - parameters: { siteId: 326 }, - network: 'milehighmedia', - }, - { - slug: 'iconmale', - name: 'Icon Male', - url: 'https://www.iconmale.com', - tags: ['gay'], - parameters: { native: true }, - network: 'milehighmedia', - }, - // MOFOS - { - slug: 'girlsgonepink', - name: 'Girls Gone Pink', - url: 'https://www.mofos.com/scenes?site=204', - description: "There comes a point in every woman's life when she gets a little curious about what some hot girl on girl sex could be like. Whether they're lesbian or just straight and incredibly daring and open-minded, the end result is the same. GirlsGonePink.com is full of soft lips, long flowing hair, and sensual feminine figures that are enough to get any horny minx's blood pumping. Premium full-length lesbian porn videos await you full of perfect boobs, pointy nipples, round butts, and luscious legs that usually stay separated!", - network: 'mofos', - }, - { - slug: 'ebonysextapes', - name: 'Ebony Sex Tapes', - url: 'https://www.mofos.com/scenes?site=202', - description: 'Once you go black, you never go back! Did you think that was only how white women feel about black men? Well if you did, that can only mean you never had a stacked, curvy, big ass, beautiful black teen riding your hard white cock. Watch these lucky guys fuck their Ebony Girlfriends at EbonySexTapes.com.', - network: 'mofos', - }, - { - slug: 'sharemybf', - name: 'Share My BF', - alias: ['smb'], - url: 'https://www.mofos.com/scenes?site=201', - description: 'Would your cock be able to handle 2 wet pussies at the same time? One hot teen riding your face while the other deepthroats you. You know your GF tells all her friends how big your dick is. Imagine if you can fuck her and her friend at the same time? Live the fantasy at ShareMyBF.com.', - network: 'mofos', - }, - { - slug: 'dontbreakme', - name: "Don't Break Me", - alias: ['dbm'], - url: 'https://www.mofos.com/scenes?site=198', - description: 'DontBreakMe.com is about tiny spinners fucking big guys with massive dicks! Most of these chicks are shorter than 5 feet tall and weigh less than 100lbs. Meanwhile, these girls are paired up with guys who tower over them by at least 1.5 feet and have 9" dicks!! The look on their faces when they see that huge dick pop out of his pants is priceless. While it turns them on they usually get a bit nervous: "how will I squeeze that huge cock inside?" Ouch! Check it out.', - network: 'mofos', - }, - { - slug: 'iknowthatgirl', - name: 'I Know That Girl', - alias: ['iktg'], - url: 'https://www.mofos.com/scenes?site=183', - description: 'Every single gorgeous girl you see on this site is 100% Real! They are all part of the biggest user submitted, amateur video site in the world...IKnowThatGirl.com! Hot young girlfriends getting kinky on camera, sucking and fucking, even stuffing dildos up their tight pussies, all filmed on home video and leaked to us by some lowlife, soon to be ex-boyfriend or former best friend! Oh well... Enjoy!', - network: 'mofos', - }, - { - slug: 'letstryanal', - name: 'Lets Try Anal', - alias: ['lta'], - url: 'https://www.mofos.com/scenes?site=189', - description: "This isn't just another anal site! Letstryanal.com features the hottest real footage of amateur girls and their first time ass fucking experiences. Watch it all... innocent girlfriends being convinced to try anal, their faces of pain and screaming as they beg their boyfriend to \"please go slower\" while a large cock penetrates their tight asses for the first time! Let's face it, there is nothing like seeing a cock disappear in a virgin asshole. It's so hot!", - network: 'mofos', - }, - { - slug: 'latinasextapes', - name: 'Latina Sex Tapes', - alias: ['lst'], - url: 'https://www.mofos.com/scenes?site=188', - description: "100% Real Latina Girls getting fucked by their boyfriends, filmed and submitted to us for Big $$$! Watch amazing real footage and private videos of these beautiful amateur girls, their perfectly tanned bodies, mouth-watering curves, luscious round asses, and mind blowing accents! We've only kept the best, most outstanding sex videos and uploaded them for you to watch. You'll be amazed with what we received, and more is on the way!", - network: 'mofos', - }, - { - slug: 'publicpickups', - name: 'Public Pickups', - alias: ['ppu'], - url: 'https://www.mofos.com/scenes?site=190', - description: "Check out the hottest REAL footage of young girls getting picked up and fucked in public! The girls are usually shy around guys approaching them with a video camera, but that's the fun part. Besides their shyness slowly disappears after they're offered money to get dirty. While it's a real turn on seeing the girls flash and get fondled in public... the hottest part is watching them get fucked everywhere...in cars, parks, clubs, even the library!", - network: 'mofos', - }, - { - slug: 'pervsonpatrol', - name: 'Pervs On Patrol', - alias: ['pop'], - url: 'https://www.mofos.com/scenes?site=185', - description: "A while back, this beautiful girl who lived next door use to always undress with her window opened. This girl had no fucking clue that I was jerking off over her from across the yard. One day I decided to grab my dad's camera and start filming her. It was amazing... until she finally caught me. Fuck, this girl was PISSED!..., but could you fucking believe that once she calmed down she was actually a little turned on by the whole situation,... and what happened next changed my life!", - network: 'mofos', - }, - { - slug: 'strandedteens', - name: 'Stranded Teens', - alias: ['sts'], - url: 'https://www.mofos.com/scenes?site=192', - description: "Watch videos on StrandedTeens.com and you will never look at a hitchhiker the same way again! Some of these girls will do anything for a ride or simply to make a friend - even the shy ones. From giving road head to getting ass-fucked on the hood of the car, you can watch it all. Check it out now, you won't be disappointed!", - network: 'mofos', - }, - { - slug: 'realslutparty', - name: 'Real Slut Party', - url: 'https://www.mofos.com/scenes?site=184', - description: "Wanna see the most mind blowing college sex parties from across the country? It's the real deal, all caught on video and submitted by you! Insane college craziness, pussy packed house parties, holiday orgies, backyard BBQ's gone wrong and hundreds of tight, young girls getting crazy, stripped down, and on the prowl for all the cock they can find!", - network: 'mofos', - }, - { - slug: 'mofoslab', - name: 'MOFOS Lab', - url: 'https://www.mofos.com/scenes?site=205', - description: "We've received your feedback and are experimenting with turning your wildest fantasies into the ultimate POV experience; this is Mofos Lab! Featuring today's hottest and freshest talent, immerse yourself in an exciting Mofos venture that brings you the edgiest new content!", - network: 'mofos', - }, - { - slug: 'mofosbsides', - name: 'Mofos B Sides', - url: 'https://www.mofos.com/scenes?site=191', - description: "Mofos B-Sides is a doorway to new, unseen amateur video! Hundreds of clips have been submitted to Mofos through the years and we've never shown them to you until now. We'll give you a little bit at a time, from random girls in random scenario\\’s, and maybe even an occasional free video from our friends at Brazzers, Twisty's and Babes! Check it all out and let us know what you think.", - network: 'mofos', - }, - { - slug: 'shesafreak', - name: "She's A Freak", - alias: ['saf'], - url: 'https://www.mofos.com/scenes?site=187', - description: "Fresh, young amateur girls with beautiful tight bodies, pushing themselves to the limit! It's just another great way that today's hottest new models are choosing to showcase their stunning bodies and show all of us that they're ready for more! Soaking wet masturbation, fisting, squirting, double penetration and anal toys are just some of the things they do to show us how freaky they can be and how ready they are to graduate from toys to thick, fat cock!", - network: 'mofos', - }, - // NAUGHTY AMERICA - { - slug: 'myfriendshotmom', - name: 'My Friend\'s Hot Mom', - alias: ['mfhm'], - url: 'https://www.naughtyamerica.com/site/my-friend-s-hot-mom', - network: 'naughtyamerica', - }, - { - slug: 'slutstepmom', - name: 'Slut Step Mom', - url: 'https://www.naughtyamerica.com/site/slut-step-mom', - network: 'naughtyamerica', - }, - { - slug: 'openfamily', - name: 'Open Family', - url: 'https://www.naughtyamerica.com/site/open-family', - network: 'naughtyamerica', - }, - { - slug: 'sleazystepdad', - name: 'Sleazy Stepdad', - url: 'https://www.naughtyamerica.com/site/sleazy-stepdad', - network: 'naughtyamerica', - }, - { - slug: 'watchyourmom', - name: 'Watch Your Mom', - url: 'https://www.naughtyamerica.com/site/watch-your-mom', - network: 'naughtyamerica', - }, - { - slug: 'bigcockbully', - name: 'Big Cock Bully', - alias: ['bcb'], - url: 'https://www.naughtyamerica.com/site/big-cock-bully', - network: 'naughtyamerica', - }, - { - slug: 'bigcockhero', - name: 'Big Cock Hero', - alias: ['bch'], - url: 'https://www.naughtyamerica.com/site/big-cock-hero', - network: 'naughtyamerica', - }, - { - slug: 'mysistershotfriend', - name: "My Sister's Hot Friend", - alias: ['mshf'], - url: 'https://www.naughtyamerica.com/site/my-sister-s-hot-friend', - network: 'naughtyamerica', - }, - { - slug: 'myfirstsexteacher', - name: 'My First Sex Teacher', - alias: ['mfst'], - url: 'https://www.naughtyamerica.com/site/my-first-sex-teacher', - network: 'naughtyamerica', - }, - { - slug: 'slutstepsister', - name: 'Slut Step Sister', - url: 'https://www.naughtyamerica.com/site/slut-step-sister', - network: 'naughtyamerica', - }, - { - slug: 'teenslovecream', - name: 'Teens Love Cream', - url: 'https://www.naughtyamerica.com/site/teens-love-cream', - network: 'naughtyamerica', - }, - { - slug: 'latinastepmom', - name: 'Latina Step Mom', - url: 'https://www.naughtyamerica.com/site/latina-step-mom', - network: 'naughtyamerica', - }, - { - slug: 'seducedbyacougar', - name: 'Seduced By A Cougar', - url: 'https://www.naughtyamerica.com/site/seduced-by-a-cougar', - network: 'naughtyamerica', - }, - { - slug: 'showmybf', - name: 'Show My BF', - url: 'https://www.naughtyamerica.com/site/show-my-bf', - network: 'naughtyamerica', - }, - { - slug: 'mydaughtershotfriend', - name: "My Daughter's Hot Friend", - alias: ['mdhf'], - url: 'https://www.naughtyamerica.com/site/my-daughter-s-hot-friend', - network: 'naughtyamerica', - }, - { - slug: 'lasluts', - name: 'LA Sluts', - url: 'https://www.naughtyamerica.com/site/la-sluts', - network: 'naughtyamerica', - }, - { - slug: 'mywifeismypornstar', - name: 'My Wife Is My Pornstar', - url: 'https://www.naughtyamerica.com/site/my-wife-is-my-pornstar', - network: 'naughtyamerica', - }, - { - slug: 'watchyourwife', - name: 'Watch Your Wife', - url: 'https://www.naughtyamerica.com/site/watch-your-wife', - network: 'naughtyamerica', - }, - { - slug: 'tonightsgirlfriendclassic', - alias: ['togc'], - name: "Tonight's Girlfriend", - url: 'https://www.naughtyamerica.com/site/tonight-s-girlfriend-classic', - network: 'naughtyamerica', - }, - { - slug: 'wivesonvacation', - name: 'Wives on Vacation', - alias: ['wov'], - url: 'https://www.naughtyamerica.com/site/wives-on-vacation', - network: 'naughtyamerica', - }, - { - slug: 'naughtyweddings', - name: 'Naughty Weddings', - alias: ['nw'], - url: 'https://www.naughtyamerica.com/site/naughty-weddings', - network: 'naughtyamerica', - }, - { - slug: 'dirtywivesclub', - name: 'Dirty Wives Club', - alias: ['dwc'], - url: 'https://www.naughtyamerica.com/site/dirty-wives-club', - network: 'naughtyamerica', - }, - { - slug: 'mydadshotgirlfriend', - name: "My Dad's Hot Girlfriend", - alias: ['mdhg'], - url: 'https://www.naughtyamerica.com/site/my-dad-s-hot-girlfriend', - network: 'naughtyamerica', - }, - { - slug: 'mygirllovesanal', - name: 'My Girl Loves Anal', - url: 'https://www.naughtyamerica.com/site/my-girl-loves-anal', - network: 'naughtyamerica', - }, - { - slug: 'analcollege', - name: 'Anal College', - url: 'https://www.naughtyamerica.com/site/anal-college', - network: 'naughtyamerica', - }, - { - slug: 'lesbiangirlongirl', - name: 'Lesbian Girl on Girl', - url: 'https://www.naughtyamerica.com/site/lesbian-girl-on-girl', - network: 'naughtyamerica', - }, - { - slug: 'naughtyoffice', - name: 'Naughty Office', - alias: ['no'], - url: 'https://www.naughtyamerica.com/site/naughty-office', - network: 'naughtyamerica', - }, - { - slug: 'ihaveawife', - name: 'I Have a Wife', - alias: ['ihaw'], - url: 'https://www.naughtyamerica.com/site/i-have-a-wife', - network: 'naughtyamerica', - }, - { - slug: 'naughtybookworms', - name: 'Naughty Bookworms', - alias: ['nb'], - url: 'https://www.naughtyamerica.com/site/naughty-bookworms', - network: 'naughtyamerica', - }, - { - slug: 'housewife1on1', - name: 'Housewife 1 on 1', - alias: ['h1o1'], - url: 'https://www.naughtyamerica.com/site/housewife-1-on-1', - network: 'naughtyamerica', - }, - { - slug: 'mywifeshotfriend', - name: "My Wife's Hot Friend", - alias: ['mwhf'], - url: 'https://www.naughtyamerica.com/site/my-wife-s-hot-friend', - network: 'naughtyamerica', - }, - { - slug: 'latinadultery', - name: 'Latin Adultery', - url: 'https://www.naughtyamerica.com/site/latin-adultery', - network: 'naughtyamerica', - }, - { - slug: 'assmasterpiece', - name: 'Ass Masterpiece', - alias: ['am'], - url: 'https://www.naughtyamerica.com/site/ass-masterpiece', - network: 'naughtyamerica', - }, - { - slug: '2chickssametime', - name: '2 Chicks Same Time', - alias: ['2cst'], - url: 'https://www.naughtyamerica.com/site/2-chicks-same-time', - network: 'naughtyamerica', - }, - { - slug: 'myfriendshotgirl', - name: "My Friend's Hot Girl", - alias: ['mfhg'], - url: 'https://www.naughtyamerica.com/site/my-friend-s-hot-girl', - network: 'naughtyamerica', - }, - { - slug: 'neighboraffair', - name: 'Neighbor Affair', - alias: ['naf'], - url: 'https://www.naughtyamerica.com/site/neighbor-affair', - network: 'naughtyamerica', - }, - { - slug: 'mygirlfriendsbustyfriend', - name: "My Girlfriend's Busty Friend", - alias: ['mgbf'], - url: 'https://www.naughtyamerica.com/site/my-girlfriend-s-busty-friend', - network: 'naughtyamerica', - }, - { - slug: 'naughtyathletics', - name: 'Naughty Athletics', - alias: ['na'], - url: 'https://www.naughtyamerica.com/site/naughty-athletics', - network: 'naughtyamerica', - }, - { - slug: 'mynaughtymassage', - name: 'My Naughty Massage', - alias: ['mnm'], - url: 'https://www.naughtyamerica.com/site/my-naughty-massage', - network: 'naughtyamerica', - }, - { - slug: 'fasttimes', - name: 'Fast Times', - url: 'https://www.naughtyamerica.com/site/fast-times', - network: 'naughtyamerica', - }, - { - slug: 'thepassenger', - name: 'The Passenger', - url: 'https://www.naughtyamerica.com/site/the-passenger', - network: 'naughtyamerica', - }, - { - slug: 'milfsugarbabesclassic', - name: 'Milf Sugar Babes Classic', - url: 'https://www.naughtyamerica.com/site/milf-sugar-babes-classic', - network: 'naughtyamerica', - }, - { - slug: 'perfectfuckingstrangersclassic', - name: 'Perfect Fucking Strangers Classic', - url: 'https://www.naughtyamerica.com/site/perfect-fucking-strangers-classic', - network: 'naughtyamerica', - }, - { - slug: 'asian1on1', - name: 'Asian 1 On 1', - url: 'https://www.naughtyamerica.com/site/asian-1-on-1', - network: 'naughtyamerica', - }, - { - slug: 'americandaydreams', - name: 'American Daydreams', - alias: ['ad'], - url: 'https://www.naughtyamerica.com/site/american-daydreams', - network: 'naughtyamerica', - }, - { - slug: 'socalcoeds', - name: 'Socal Coeds', - url: 'https://www.naughtyamerica.com/site/socal-coeds', - network: 'naughtyamerica', - }, - { - slug: 'naughtycountrygirls', - name: 'Naughty Country Girls', - url: 'https://www.naughtyamerica.com/site/naughty-country-girls', - network: 'naughtyamerica', - }, - { - slug: 'diaryofamilf', - name: 'Diary of a Milf', - url: 'https://www.naughtyamerica.com/site/diary-of-a-milf', - network: 'naughtyamerica', - }, - { - slug: 'naughtyrichgirls', - name: 'Naughty Rich Girls', - alias: ['nrg'], - url: 'https://www.naughtyamerica.com/site/naughty-rich-girls', - network: 'naughtyamerica', - }, - { - slug: 'mynaughtylatinmaid', - name: 'My Naughty Latin Maid', - url: 'https://www.naughtyamerica.com/site/my-naughty-latin-maid', - network: 'naughtyamerica', - }, - { - slug: 'naughtyamerica', - name: 'Naughty America', - alias: ['nam'], - url: 'https://www.naughtyamerica.com/site/naughty-america', - network: 'naughtyamerica', - }, - { - slug: 'diaryofananny', - name: 'Diary of a Nanny', - url: 'https://www.naughtyamerica.com/site/diary-of-a-nanny', - network: 'naughtyamerica', - }, - { - slug: 'naughtyflipside', - name: 'Naughty Flipside', - url: 'https://www.naughtyamerica.com/site/naughty-flipside', - network: 'naughtyamerica', - }, - { - slug: 'livepartygirl', - name: 'Live Party Girl', - url: 'https://www.naughtyamerica.com/site/live-party-girl', - network: 'naughtyamerica', - }, - { - slug: 'livenaughtystudent', - name: 'Live Naughty Student', - url: 'https://www.naughtyamerica.com/site/live-naughty-student', - network: 'naughtyamerica', - }, - { - slug: 'livenaughtysecretary', - name: 'Live Naughty Secretary', - url: 'https://www.naughtyamerica.com/site/live-naughty-secretary', - network: 'naughtyamerica', - }, - { - slug: 'livegymcam', - name: 'Live Gym Cam', - url: 'https://www.naughtyamerica.com/site/live-gym-cam', - network: 'naughtyamerica', - }, - { - slug: 'livenaughtyteacher', - name: 'Live Naughty Teacher', - url: 'https://www.naughtyamerica.com/site/live-naughty-teacher', - network: 'naughtyamerica', - }, - { - slug: 'livenaughtymilf', - name: 'Live Naughty Milf', - url: 'https://www.naughtyamerica.com/site/live-naughty-milf', - network: 'naughtyamerica', - }, - { - slug: 'livenaughtynurse', - name: 'Live Naughty Nurse', - url: 'https://www.naughtyamerica.com/site/live-naughty-nurse', - network: 'naughtyamerica', - }, - // NEW SENSATIONS - { - slug: 'hotwifexxx', - name: 'Hotwife XXX', - url: 'https://www.hotwifexxx.com', - network: 'newsensations', - parameters: { - siteId: 'hwxxx', - block: true, - }, - }, - { - slug: 'tabutales', - name: 'Tabu Tales', - url: 'https://www.thetabutales.com', - network: 'newsensations', - parameters: { siteId: 'tt' }, - }, - { - slug: 'nsfamilyxxx', - name: 'Family XXX', - url: 'https://www.familyxxx.com', - network: 'newsensations', - tags: ['family'], - parameters: { - siteId: 'famxxx', - block: true, - }, - }, - { - slug: 'thelesbianexperience', - name: 'The Lesbian Experience', - url: 'https://www.thelesbianexperience.com', - network: 'newsensations', - tags: ['lesbian'], - parameters: { siteId: 'tle' }, - }, - { - slug: 'theromanceseries', - name: 'The Romance Series', - url: 'https://www.theromanceseries.com', - network: 'newsensations', - parameters: { siteId: 'rs' }, - }, - { - slug: 'talesfromtheedge', - name: 'Tales From The Edge', - url: 'thetalesfromtheedge', - network: 'newsensations', - parameters: { siteId: 'ttfte' }, - }, - { - slug: 'parodypass', - name: 'Parody Pass', - url: 'https://www.parodypass.com', - network: 'newsensations', - parameters: { siteId: 'pp' }, - }, - { - slug: 'shanedieselsbangingbabes', - name: 'Shane Diesel\'s Banging Babes', - url: 'http://shanedieselsbangingbabes.com', - network: 'newsensations', - parameters: { siteId: 'sdbb' }, - }, - { - slug: 'unlimitedmilfs', - name: 'Unlimited MILFs', - url: 'https://www.unlimitedmilfs.com', - network: 'newsensations', - tags: ['milf'], - parameters: { siteId: 'um' }, - }, - { - slug: 'heavyhandfuls', - name: 'Heavy Handfuls', - url: 'https://www.heavyhandfuls.com', - network: 'newsensations', - parameters: { siteId: 'hh' }, - }, - { - slug: 'jizzbomb', - name: 'Jizz Bomb', - url: 'https://www.jizzbomb.com', - network: 'newsensations', - parameters: { siteId: 'jb' }, - }, - { - slug: 'stretchedoutsnatch', - name: 'Stretched Out Snatch', - url: 'https://www.stretchedoutsnatch.com', - network: 'newsensations', - parameters: { siteId: 'sos' }, - }, - { - slug: 'fourfingerclub', - name: 'Four Finger Club', - url: 'https://www.fourfingerclub.com', - network: 'newsensations', - parameters: { siteId: 'ffc' }, - }, - { - slug: 'ashlynnbrooke', - name: 'Ashlynn Brooke', - url: 'https://www.ashlynnbrooke.com', - network: 'newsensations', - parameters: { siteId: 'ab' }, - }, - { - slug: 'freshouttahighschool', - name: 'Fresh Outta High School', - url: 'https://www.freshouttahighschool.com', - network: 'newsensations', - parameters: { siteId: 'fohs' }, - }, - // NUBILES - { - slug: 'anilos', - name: 'Anilos', - url: 'https://www.anilos.com', - network: 'nubiles', - parameters: { - upcoming: true, - }, - }, - { - slug: 'brattysis', - name: 'Bratty Sis', - url: 'https://www.brattysis.com', - tags: ['family'], - network: 'nubiles', - parameters: { - upcoming: true, - }, - }, - { - slug: 'deeplush', - name: 'Deep Lush', - url: 'https://www.deeplush.com', - network: 'nubiles', - }, - { - slug: 'hotcrazymess', - name: 'Hot Crazy Mess', - alias: ['hcm'], - url: 'https://www.hotcrazymess.com', - network: 'nubiles', - }, - { - slug: 'nfbusty', - name: 'NF Busty', - url: 'https://www.nfbusty.com', - tags: ['big-boobs'], - network: 'nubiles', - parameters: { - upcoming: true, - }, - }, - { - slug: 'nubilefilms', - name: 'Nubile Films', - alias: ['nf', 'nubilef'], - url: 'https://www.nubilefilms.com', - network: 'nubiles', - parameters: { - upcoming: true, - }, - }, - { - slug: 'nubiles', - name: 'Nubiles', - url: 'https://www.nubiles.net', - network: 'nubiles', - parameters: { - upcoming: true, - }, - }, - { - slug: 'nubilescasting', - name: 'Nubiles Casting', - url: 'https://www.nubiles-casting.com', - tags: ['casting'], - network: 'nubiles', - }, - { - slug: 'momsteachsex', - name: 'Moms Teach Sex', - alias: ['mts'], - url: 'https://www.momsteachsex.com', - tags: ['family', 'milf'], - network: 'nubiles', - parameters: { - upcoming: true, - }, - }, - { - slug: 'petitehdporn', - name: 'Petite HD Porn', - alias: ['phdp'], - url: 'https://www.petitehdporn.com', - network: 'nubiles', - parameters: { - upcoming: true, - }, - }, - { - slug: 'driverxxx', - name: 'Driver XXX', - url: 'https://www.driverxxx.com', - network: 'nubiles', - }, - { - slug: 'petiteballerinasfucked', - name: 'Petite Ballerinas Fucked', - alias: ['pbf'], - url: 'https://www.petiteballerinasfucked.com', - network: 'nubiles', - }, - { - slug: 'teacherfucksteens', - name: 'Teacher Fucks Teens', - alias: ['tft'], - url: 'https://www.teacherfucksteens.com', - tags: ['teacher'], - network: 'nubiles', - }, - { - slug: 'stepsiblingscaught', - name: 'Step Siblings Caught', - alias: ['ssc'], - url: 'https://www.stepsiblingscaught.com', - tags: ['family'], - network: 'nubiles', - parameters: { - upcoming: true, - }, - }, - { - slug: 'princesscum', - name: 'Princess Cum', - alias: ['pc'], - url: 'https://www.princesscum.com', - network: 'nubiles', - }, - { - slug: 'badteenspunished', - name: 'Bad Teens Punished', - alias: ['btp'], - url: 'https://www.badteenspunished.com', - network: 'nubiles', - }, - { - slug: 'nubilesunscripted', - name: 'Nubiles Unscripted', - url: 'https://www.nubilesunscripted.com', - network: 'nubiles', - }, - { - slug: 'bountyhunterporn', - name: 'Bounty Hunter Porn', - url: 'https://www.bountyhunterporn.com', - network: 'nubiles', - }, - { - slug: 'daddyslilangel', - name: 'Daddy\'s Lil Angel', - alias: ['dlla'], - url: 'https://www.daddyslilangel.com', - tags: ['family', 'anal'], - network: 'nubiles', - }, - { - slug: 'myfamilypies', - name: 'My Family Pies', - alias: ['mfp'], - url: 'https://www.myfamilypies.com', - tags: ['family'], - network: 'nubiles', - parameters: { - upcoming: true, - }, - }, - { - slug: 'nubileset', - name: 'Nubiles Entertainment', - url: 'https://www.nubileset.com', - network: 'nubiles', - }, - { - slug: 'detentiongirls', - name: 'Detention Girls', - url: 'https://www.detentiongirls.com', - network: 'nubiles', - }, - { - slug: 'thatsitcomshow', - name: 'That Sitcom Show', - alias: ['tss'], - url: 'https://www.thatsitcomshow.com', - tags: ['parody'], - network: 'nubiles', - }, - // PERFECT GONZO - { - slug: 'allinternal', - name: 'All Internal', - url: 'https://allinternal.com', - network: 'perfectgonzo', - }, - { - slug: 'asstraffic', - name: 'Ass Traffic', - url: 'https://asstraffic.com', - network: 'perfectgonzo', - }, - { - slug: 'cumforcover', - name: 'Cum For Cover', - url: 'https://cumforcover.com', - network: 'perfectgonzo', - }, - { - slug: 'fistflush', - name: 'Fist Flush', - url: 'https://fistflush.com', - network: 'perfectgonzo', - }, - { - slug: 'givemepink', - name: 'Give Me Pink', - url: 'https://givemepink.com', - tags: ['solo', 'masturbation'], - network: 'perfectgonzo', - }, - { - slug: 'milfthing', - name: 'MILF Thing', - url: 'https://milfthing.com', - network: 'perfectgonzo', - }, - { - slug: 'primecups', - name: 'Prime Cups', - url: 'https://primecups.com', - network: 'perfectgonzo', - }, - { - slug: 'purepov', - name: 'Pure POV', - url: 'https://purepov.com', - network: 'perfectgonzo', - }, - { - slug: 'spermswap', - name: 'Sperm Swap', - url: 'https://spermswap.com', - tags: ['cum-swapping'], - network: 'perfectgonzo', - }, - { - slug: 'tamedteens', - name: 'Tamed Teens', - url: 'https://tamedteens.com', - network: 'perfectgonzo', - }, - // PERVCITY - { - slug: 'analoverdose', - name: 'Anal Overdose', - url: 'http://www.analoverdose.com', - description: 'Before proceeding, use caution: the stunning pornstars of Anal Overdose are so fiery that they cause heavy breathing, throbbing cocks and volcanic loads of cum. If you think you can handle the heat of smoking tits, sweltering pussy and red hot ass.', - network: 'pervcity', - parameters: { tourId: 3 }, - }, - { - slug: 'bangingbeauties', - name: 'Banging Beauties', - description: "Banging Beauties isn't just a porn site; it's the gateway to all your pussy-obsessed fantasies! Our members' area is flowing with beautiful pornstars anticipating big dick throbbing in their syrupy pink slits. These experienced babes love brutal vaginal pounding! Similarly, they're eager for anal switch-hitting to shake things up. However, it's not only about gorgeous sexperts filling their hungry holes. Sometimes, it's all about innocent rookies earning their pornstar status in first time threesomes and premier interracial scenes.", - url: 'http://www.bangingbeauties.com', - network: 'pervcity', - parameters: { tourId: 7 }, - }, - { - slug: 'oraloverdose', - name: 'Oral Overdose', - description: "Oral Overdose is the only site you need to live out every saliva soaked blowjob of your dreams in HD POV! We've got the most stunning cocksuckers in the world going to town on big dick. These babes not only love cock, they can't get enough of it! In fact, there is no prick too huge for our hungry girls' throats. You'll find gorgeous, big tits pornstars exercising their gag reflex in intense balls deep facefuck scenes. We also feature fresh, young newbies taking on the gagging deepthroat challenge.", - url: 'http://www.oraloverdose.com', - network: 'pervcity', - parameters: { tourId: 4 }, - }, - { - slug: 'chocolatebjs', - name: 'Chocolate BJs', - description: "You've just won the golden ticket to the best Chocolate BJs on the planet! We've sought far and wide to bring you the most beautiful black and ethnic pornstars. And they're in our members' area now! They can't wait to suck your white lollipop and lick the thick cream shooting from your big dick. Of course, no matter how sweet the booty or juicy the big tits, these brown foxes aren't all sugar and spice. In fact, when it comes to giving head, these big ass ebony babes know what they want: huge white cocks filling their throats!", - url: 'http://www.chocolatebjs.com', - network: 'pervcity', - parameters: { tourId: 6 }, - }, - { - slug: 'upherasshole', - name: 'Up Her Asshole', - description: "You don't need to travel the globe in search of the anal wonders of the world, because you get your own private tour right here on Up Her Asshole! Our stunning pornstars and rookie starlets welcome all ass fetish and anal sex fans, with their twerking bubble butts and winking assholes. However, big booty worship is just a slice of the fun. Combined with juicy tits (big and small, wet pussy (hairy and bald, these girls deliver a spectacular sensory experience in HD POV. Not only are you in danger of busting a nut before the going gets good, but also when the good turns remarkable with rimming, fingering and butt toys!", - url: 'http://www.upherasshole.com', - network: 'pervcity', - parameters: { tourId: 9 }, - }, - // PIMP XXX - { - slug: 'drilledxxx', - name: 'Drilled.XXX', - url: 'https://drilled.xxx', - tags: ['anal'], - network: 'pimpxxx', - }, - { - slug: 'cuckedxxx', - name: 'Cucked.XXX', - url: 'https://cucked.xxx', - tags: ['cuckold'], - network: 'pimpxxx', - }, - { - slug: 'familyxxx', - name: 'Family.XXX', - url: 'https://family.xxx', - tags: ['family'], - network: 'pimpxxx', - }, - { - slug: 'petitexxx', - name: 'Petite.XXX', - url: 'https://petite.xxx', - network: 'pimpxxx', - }, - { - slug: 'confessionsxxx', - name: 'Confessions.XXX', - url: 'https://confessions.xxx', - network: 'pimpxxx', - }, - { - slug: 'bcmxxx', - name: 'BCM.XXX', - url: 'https://bcm.xxx', - network: 'pimpxxx', - }, - // PORN PROS - { - name: 'Real Ex Girlfriends', - slug: 'realexgirlfriends', - alias: ['reg'], - url: 'https://pornpros.com/site/realexgirlfriends', - network: 'pornpros', - }, - { - name: '18 Years Old', - slug: 'eighteenyearsold', - alias: ['18yo'], - url: 'https://pornpros.com/site/18yearsold', - tags: ['teen'], - network: 'pornpros', - }, - { - name: 'Massage Creep', - slug: 'massagecreep', - alias: ['mc'], - url: 'https://pornpros.com/site/massagecreep', - tags: ['massage'], - network: 'pornpros', - }, - { - name: 'Deep Throat Love', - slug: 'deepthroatlove', - url: 'https://pornpros.com/site/deepthroatlove', - tags: ['blowjob', 'deepthroat'], - network: 'pornpros', - }, - { - name: 'Teen BFF', - slug: 'teenbff', - url: 'https://pornpros.com/site/teenbff', - tags: ['mff'], - network: 'pornpros', - }, - { - name: 'Shady P.I.', - slug: 'shadypi', - url: 'https://pornpros.com/site/shadypi', - network: 'pornpros', - }, - { - name: 'Cruelty Party', - slug: 'crueltyparty', - url: 'https://pornpros.com/site/crueltyparty', - network: 'pornpros', - }, - { - name: 'Disgraced 18', - slug: 'disgraced18', - url: 'https://pornpros.com/site/disgraced18', - network: 'pornpros', - }, - { - name: 'Cumshot Surprise', - slug: 'cumshotsurprise', - url: 'https://pornpros.com/site/cumshotsurprise', - network: 'pornpros', - }, - { - name: '40oz Bounce', - slug: 'fortyozbounce', - url: 'https://pornpros.com/site/40ozbounce', - network: 'pornpros', - }, - { - name: 'Jurassic Cock', - slug: 'jurassiccock', - url: 'https://pornpros.com/site/jurassiccock', - network: 'pornpros', - }, - { - name: 'Freaks Of Cock', - slug: 'freaksofcock', - url: 'https://pornpros.com/site/freaksofcock', - network: 'pornpros', - }, - { - name: 'Euro Humpers', - slug: 'eurohumpers', - url: 'https://pornpros.com/site/eurohumpers', - network: 'pornpros', - }, - { - name: 'Freaks Of Boobs', - slug: 'freaksofboobs', - url: 'https://pornpros.com/site/freaksofboobs', - network: 'pornpros', - }, - { - name: 'Cock Competition', - slug: 'cockcompetition', - url: 'https://pornpros.com/site/cockcompetition', - network: 'pornpros', - }, - { - name: 'Pimp Parade', - slug: 'pimpparade', - url: 'https://pornpros.com/site/pimpparade', - network: 'pornpros', - }, - { - name: 'MILF Humiliation', - slug: 'milfhumiliation', - url: 'https://milfhumiliation.com', - network: 'pornpros', - tags: ['milf'], - }, - { - name: 'Humiliated', - slug: 'humiliated', - url: 'https://humiliated.com', - network: 'pornpros', - }, - { - name: 'Flexible Positions', - slug: 'flexiblepositions', - url: 'https://flexiblepositions.com', - network: 'pornpros', - parameters: { - network: true, - }, - }, - { - name: 'Public Violations', - slug: 'publicviolations', - url: 'https://publicviolations.com', - network: 'pornpros', - parameters: { - network: true, - }, - }, - { - name: 'Amateur Violations', - slug: 'amateurviolations', - url: 'https://amateurviolations.com', - network: 'pornpros', - }, - { - name: 'Squirt Disgrace', - slug: 'squirtdisgrace', - url: 'https://squirtdisgrace.com', - network: 'pornpros', - }, - { - name: 'Cum Disgrace', - slug: 'cumdisgrace', - url: 'https://cumdisgrace.com', - network: 'pornpros', - }, - { - name: 'Webcam Hackers', - slug: 'webcamhackers', - url: 'https://webcamhackers.com', - network: 'pornpros', - }, - { - name: 'College Teens', - slug: 'collegeteens', - network: 'pornpros', - }, - // PRIVATE - { - slug: 'analintroductions', - name: 'Anal Introductions', - description: 'Private\'s Anal Introductions is all about ass. Watch these girls get their asses broken in by fat hard cocks! Hot double penetrations, gaping wide assholes and anal creampies are all standard in this exclusive site. Many of these girls have never had cock in their ass before, while others are real addicts and can only cum when being savagely sodomised. Watch which girls can take it... Private style.', - url: 'https://www.private.com/site/anal-introductions', - network: 'private', - }, - { - slug: 'iconfessfiles', - name: 'I Confess Files', - description: 'From the heart of the UK comes found footage exclusively provided to private.com which will shock and offend some viewers. Reality, perversion and unnatural lust come together perhaps as never before.', - url: 'https://www.private.com/site/i-confess-files', - network: 'private', - }, - { - slug: 'missionasspossible', - name: 'Mission: Ass Possible', - description: 'From the streets of Europe, Private\'s team of professionals find and exploit clueless young sluts, for some great sex and for your viewing pleasure. See what young hot chicks will do when their desire for easy fame or money makes them vulnerable to some of the craziest schemes and plots imaginable. Private\'s hung studs are on a mission for ass and hungry for fun. All part of the Private network of sites.', - url: 'https://www.private.com/site/mission-ass-possible', - network: 'private', - }, - { - slug: 'russianfakeagent', - name: 'Russian Fake Agent', - description: 'Direct from Russia, young naïve women pursue their dream of visas, Hollywood and fame. Eager to please and willing to do anything to get their break. Unfortunately, it’s a case of lies, sex and videotape as these gullible hotties perform for us. If these girls only knew the truth!', - url: 'https://www.private.com/site/russian-fake-agent', - network: 'private', - }, - { - slug: 'sexonthebeach', - name: 'Sex on the Beach', - description: 'Amazing locations and steamy sex in the sun, www.privatetropics.com is a celebration of tropical lust and sand covered sluts. From Private\'s exclusive line of scenes in exotic countries, watch what happens when our hot models go naked and native.', - url: 'https://www.private.com/site/sex-on-the-beach', - network: 'private', - }, - { - slug: 'tightandteen', - name: 'Tight and Teen', - description: 'Europe\'s number one teen offering and part of the Private network of sites, Tight and Teen takes you to the place that every father dreads, as 18+ teens discover just how much fun pleasing a hung stud can be. Fresh tight pussies, virgin anal initiations and teen face fuckings all brought to you exclusively by Europe\'s leading adult brand.', - url: 'https://www.private.com/site/tight-and-teen', - network: 'private', - }, - { - slug: 'blacksonsluts', - name: 'Blacks on Sluts', - description: 'See what happens when European women discover hung black studs looking to breed. Blacks on Sluts is 100% white slut and cheating wives versus huge black dicks giving them something they will never forget. Private puts its stamp on these women as our stallions stretch them to their limits.', - url: 'https://www.private.com/site/blacks-on-sluts', - network: 'private', - }, - { - slug: 'privateblack', - name: 'Private Black', - description: 'Private Black is number 1 for European Interracial Porn with exclusive interracial content in HD and Ultra 4K featuring the freshest young faces from Europe and the most popular European porn stars.', - url: 'https://www.privateblack.com', - network: 'private', - }, - { - slug: 'privatefetish', - name: 'Private Fetish', - description: 'www.privatefetish.com is here to give you that taste of dark desire that you secretly crave. Domination and Submission, Pleasure and Pain, are the drivers in this hardcore dungeon. What turns you on most? Being forced to beg for release from a sexy dominatrix or making that bitch next door beg for your cock? All part of Private\'s network of sites.', - url: 'https://www.private.com/site/private-fetish', - network: 'private', - }, - { - slug: 'privatemilfs', - name: 'Private MILFs', - description: 'Part of the awesome network of Private sites, Private MILFs is all about moms who are getting what they need when their limp dicked husbands can\'t perform. From their daughters\' stud boyfriends to their husbands\' hung black co-workers to their sons\' friends, no one is safe from their cravings for hard cock and salty cum.', - url: 'https://www.private.com/site/private-milfs', - network: 'private', - }, - { - slug: 'russianteenass', - name: 'Russian Teen Ass', - description: 'Many people say that Russian girls are the most beautiful in the world, and at www.russianteenass.com we show you why. These sexy Soviet newcomers are ready to work hard for their visa and their big shot at stardom. These barely 18+ girls know what they want and Private gives them an exclusive opportunity to get it. From Russia with lust, come see these girls in action!', - url: 'https://www.private.com/site/russian-teen-ass', - network: 'private', - }, - { - slug: 'privatestars', - name: 'Private Stars', - description: 'Welcome to Private Stars. The name speaks for itself as only top-model babes and perfect girls can join this select group of stars, all shot in HD. Part of the Private network of sites, Private Stars brings you a sneak peek into the life of seductive glamour girls who could have easily stepped from the catwalks of Paris or Milan and straight into a world of torrid sex.', - url: 'https://www.private.com/site/private-stars', - network: 'private', - }, - // PURE TABOO - { - name: 'Pure Taboo', - slug: 'puretaboo', - url: 'https://www.puretaboo.com', - description: 'PureTaboo.com is the ultimate site for family taboo porn, featuring submissive teens & virgins in rough sex videos in ultra 4k HD.', - network: 'puretaboo', - priority: 1, - parameters: { - independent: true, - mobile: 'https://m.dpfanatics.com/en/video', - }, - }, - { - name: 'Pretty Dirty', - slug: 'prettydirty', - alias: ['prdi'], - url: 'https://www.prettydirty.com', - network: 'puretaboo', - parameters: { - referer: 'https://www.puretaboo.com', - }, - }, - /* series, not sites, that appear on Pure Taboo itself + // BABES + { + name: 'Babes', + url: 'https://www.babes.com/scenes?site=213', + slug: 'babes', + network: 'babes', + }, + { + name: 'Babes Unleashed', + url: 'https://www.babes.com/scenes?site=218', + slug: 'babesunleashed', + network: 'babes', + }, + { + name: 'Black Is Better', + url: 'https://www.babes.com/scenes?site=217', + slug: 'blackisbetter', + network: 'babes', + }, + { + name: 'Elegant Anal', + url: 'https://www.babes.com/scenes?site=216', + slug: 'elegantanal', + network: 'babes', + }, + { + name: 'Office Obsession', + url: 'https://www.babes.com/scenes?site=214', + slug: 'officeobsession', + network: 'babes', + }, + { + name: 'Step Mom Lessons', + url: 'https://www.babes.com/scenes?site=215', + slug: 'stepmomlessons', + network: 'babes', + }, + // BAM VISIONS + { + slug: 'bamvisions', + name: 'BAM Visions', + url: 'https://tour.bamvisions.com', + parameters: { independent: true }, + network: 'bamvisions', + }, + // BANG + { + name: 'Trickery', + slug: 'bangtrickery', + url: 'https://www.bang.com/original/4800/bang-trickery', + parameters: { siteId: 4800 }, + network: 'bang', + }, + { + name: 'Yngr', + slug: 'yngrcom', + alias: ['byngr'], + // url: 'https://www.bang.com/original/5010/bang-yngr', + url: 'https://yngr.com', + parameters: { siteId: 5010 }, + network: 'bang', + }, + { + name: 'Roadside XXX', + slug: 'bangroadsidexxx', + // url: 'https://www.bang.com/original/4864/roadside-xxx', + url: 'https://roadsidexxx.com', + parameters: { siteId: 4864 }, + network: 'bang', + }, + { + name: 'Surprise', + slug: 'bangsurprise', + url: 'https://www.bang.com/original/5000/bang-surprise', + parameters: { siteId: 5000 }, + network: 'bang', + }, + { + name: 'Real Teens', + slug: 'bangrealteens', + alias: ['brealteens'], + url: 'https://www.bang.com/original/3366/bang-real-teens', + parameters: { siteId: 3366 }, + network: 'bang', + }, + { + name: 'FCK.news', + slug: 'bangfakenews', + // url: 'https://www.bang.com/original/4998/bang-fckNews', + url: 'https://fck.news', + parameters: { siteId: 4998 }, + network: 'bang', + }, + { + name: 'Pretty & Raw', + slug: 'prettyandraw', + // url: 'https://www.bang.com/original/4792/bang-pretty-and-raw', + url: 'https://prettyandraw.com', + parameters: { siteId: 4792 }, + network: 'bang', + }, + { + name: 'Japan', + slug: 'bangjapan', + url: 'https://www.bang.com/original/3079/bang-japan', + parameters: { siteId: 3079, ignore: true }, + network: 'bang', + }, + { + name: 'Rammed', + slug: 'bangrammed', + url: 'https://www.bang.com/original/4836/bang-rammed', + parameters: { siteId: 4836 }, + network: 'bang', + }, + { + name: 'Glamkore', + slug: 'bangglamkore', + alias: ['bglamkore'], + url: 'https://www.bang.com/original/4586/bang-glamkore', + parameters: { siteId: 4586 }, + network: 'bang', + }, + { + name: 'Screw The Cops', + slug: 'screwthecops', + url: 'https://www.bang.com/original/4710/bang-screw-cops', + parameters: { siteId: 4710 }, + network: 'bang', + }, + { + name: 'Real MILFs', + slug: 'bangrealmilfs', + alias: ['brealmilfs'], + url: 'https://www.bang.com/original/4448/bang-real-milfs', + parameters: { siteId: 4448 }, + network: 'bang', + }, + { + name: 'Confessions', + slug: 'bangconfessions', + alias: ['bconfessions'], + url: 'https://www.bang.com/original/4308/bang-confessions', + parameters: { siteId: 4308 }, + network: 'bang', + }, + { + name: 'Casting', + slug: 'bangcasting', + alias: ['bcasting'], + url: 'https://www.bang.com/original/3261/bang-casting', + parameters: { siteId: 3261 }, + network: 'bang', + }, + // BANGBROS + { + name: 'Ass Parade', + url: 'https://bangbros.com/websites/assparade', + slug: 'assparade', + description: null, + network: 'bangbros', + parameters: { code: 'ap' }, + }, + { + name: 'AvaSpice', + url: 'https://bangbros.com/websites/avaspice', + slug: 'avaspice', + description: null, + network: 'bangbros', + parameters: { code: 'av' }, + }, + { + name: 'Back Room Facials', + url: 'https://bangbros.com/websites/backroomfacials', + slug: 'backroomfacials', + description: null, + network: 'bangbros', + parameters: { code: 'brf' }, + }, + { + name: 'Backroom MILF', + url: 'https://bangbros.com/websites/backroommilf', + slug: 'backroommilf', + description: null, + network: 'bangbros', + parameters: { code: 'mf' }, + }, + { + name: 'Ball Honeys', + url: 'https://bangbros.com/websites/ballhoneys', + slug: 'ballhoneys', + description: null, + network: 'bangbros', + parameters: { code: 'es' }, + }, + { + name: 'BangBros 18', + url: 'https://bangbros.com/websites/bangbros18', + slug: 'bangbros18', + description: null, + network: 'bangbros', + parameters: { code: 'bbe' }, + }, + { + name: 'BangBros Angels', + url: 'https://bangbros.com/websites/bangbrosangels', + slug: 'bangbrosangels', + description: null, + network: 'bangbros', + parameters: { code: 'bng' }, + }, + { + name: 'Bangbros Clips', + url: 'https://bangbros.com/websites/bangbrosclips', + slug: 'bangbrosclips', + description: null, + network: 'bangbros', + parameters: { code: 'bbc' }, + }, + { + name: 'BangBros Remastered', + url: 'https://bangbros.com/websites/remaster', + slug: 'bangbrosremastered', + description: null, + network: 'bangbros', + parameters: { code: 'rm' }, + }, + { + name: 'Bang Bus', + url: 'https://bangbros.com/websites/bangbus', + slug: 'bangbus', + description: null, + network: 'bangbros', + parameters: { code: 'bb' }, + }, + { + name: 'Bang Casting', + url: 'https://bangbros.com/websites/bangcasting', + slug: 'bangbroscasting', + description: null, + network: 'bangbros', + parameters: { code: 'hih' }, + }, + { + name: 'Bang POV', + url: 'https://bangbros.com/websites/bangpov', + slug: 'bangpov', + description: null, + network: 'bangbros', + parameters: { code: 'bpov' }, + }, + { + name: 'Bang Tryouts', + url: 'https://bangbros.com/websites/bangtryouts', + slug: 'bangtryouts', + description: null, + network: 'bangbros', + parameters: { code: 'bto' }, + }, + { + name: 'Big Mouthfuls', + url: 'https://bangbros.com/websites/bigmouthfuls', + slug: 'bigmouthfuls', + description: null, + network: 'bangbros', + parameters: { code: 'bmf' }, + }, + { + name: 'Big Tit Cream Pie', + alias: ['btc'], + slug: 'bigtitcreampie', + url: 'https://bangbros.com/websites/bigtitcreampie', + description: null, + network: 'bangbros', + parameters: { code: 'btcp' }, + }, + { + name: 'Big Tits, Round Asses', + url: 'https://bangbros.com/websites/bigtitsroundasses', + alias: ['btra'], + slug: 'bigtitsroundasses', + description: null, + network: 'bangbros', + parameters: { code: 'btra' }, + }, + { + name: 'BlowJob Fridays', + url: 'https://bangbros.com/websites/blowjobfridays', + slug: 'blowjobfridays', + description: null, + network: 'bangbros', + parameters: { code: 'bj' }, + }, + { + name: 'Blowjob Ninjas', + url: 'https://bangbros.com/websites/blowjobninjas', + slug: 'blowjobninjas', + description: null, + network: 'bangbros', + parameters: { code: 'aa' }, + }, + { + name: 'Boob Squad', + url: 'https://bangbros.com/websites/boobsquad', + slug: 'boobsquad', + description: null, + network: 'bangbros', + parameters: { code: 'bs' }, + }, + { + name: 'Brown Bunnies', + url: 'https://bangbros.com/websites/brownbunnies', + slug: 'brownbunnies', + description: null, + network: 'bangbros', + parameters: { code: 'bkb' }, + }, + { + name: 'Can He Score?', + url: 'https://bangbros.com/websites/canhescore', + slug: 'canhescore', + description: null, + network: 'bangbros', + parameters: { code: 'bd' }, + }, + { + name: 'Casting', + url: 'https://bangbros.com/websites/casting', + slug: 'casting', + description: null, + network: 'bangbros', + parameters: { code: 'ca' }, + }, + { + name: 'Chongas', + url: 'https://bangbros.com/websites/chongas', + slug: 'chongas', + description: null, + network: 'bangbros', + parameters: { code: 'ch' }, + }, + { + name: 'Colombia Fuck Fest', + url: 'https://bangbros.com/websites/colombiafuckfest', + slug: 'colombiafuckfest', + description: null, + network: 'bangbros', + parameters: { code: 'cff' }, + }, + { + name: 'Dirty World Tour', + url: 'https://bangbros.com/websites/dirtyworldtour', + slug: 'dirtyworldtour', + description: null, + network: 'bangbros', + parameters: { code: 'bf' }, + }, + { + name: 'Dorm Invasion', + url: 'https://bangbros.com/websites/dorminvasion', + slug: 'dorminvasion', + description: null, + network: 'bangbros', + parameters: { code: 'di' }, + }, + { + name: 'Facial Fest', + url: 'https://bangbros.com/websites/facialfest', + slug: 'facialfest', + description: null, + network: 'bangbros', + parameters: { code: 'ff' }, + }, + { + name: 'Fuck Team Five', + url: 'https://bangbros.com/websites/fuckteamfive', + slug: 'fuckteamfive', + description: null, + network: 'bangbros', + parameters: { code: 'bbw' }, + }, + { + name: 'Glory Hole Loads', + url: 'https://bangbros.com/websites/gloryholeloads', + slug: 'gloryholeloads', + description: null, + network: 'bangbros', + parameters: { code: 'ghl' }, + }, + { + name: 'Latina Rampage', + url: 'https://bangbros.com/websites/latinarampage', + slug: 'latinarampage', + description: null, + network: 'bangbros', + parameters: { code: 'lrp' }, + }, + { + name: 'Living With Anna', + url: 'https://bangbros.com/websites/livingwithanna', + slug: 'livingwithanna', + description: null, + network: 'bangbros', + parameters: { code: 'lr' }, + }, + { + name: 'Magical Feet', + url: 'https://bangbros.com/websites/magicalfeet', + slug: 'magicalfeet', + description: null, + network: 'bangbros', + parameters: { code: 'fj' }, + }, + { + name: 'Milf Soup', + url: 'https://bangbros.com/websites/milfsoup', + slug: 'milfsoup', + description: null, + network: 'bangbros', + parameters: { code: 'ms' }, + }, + { + name: 'MomIsHorny', + url: 'https://bangbros.com/websites/momishorny', + slug: 'momishorny', + description: null, + network: 'bangbros', + parameters: { code: 'mih' }, + }, + { + name: 'Monsters of Cock', + url: 'https://bangbros.com/websites/monstersofcock', + slug: 'monstersofcock', + description: null, + network: 'bangbros', + parameters: { code: 'mc' }, + }, + { + name: 'Mr CamelToe', + url: 'https://bangbros.com/websites/mrcameltoe', + slug: 'mrcameltoe', + description: null, + network: 'bangbros', + parameters: { code: 'ct' }, + }, + { + name: 'My Dirty Maid', + slug: 'mydirtymaid', + alias: ['mdm'], + url: 'https://bangbros.com/websites/mydirtymaid', + description: null, + network: 'bangbros', + parameters: { code: 'mda' }, + }, + { + name: 'My Life In Brazil', + url: 'https://bangbros.com/websites/mylifeinbrazil', + slug: 'mylifeinbrazil', + description: null, + network: 'bangbros', + parameters: { code: 'mb' }, + }, + { + name: 'Newbie Black', + url: 'https://bangbros.com/websites/newbieblack', + slug: 'newbieblack', + description: null, + network: 'bangbros', + parameters: { code: 'blkg' }, + }, + { + name: 'Party of Three', + url: 'https://bangbros.com/websites/partyofthree', + slug: 'partyofthree', + description: null, + network: 'bangbros', + parameters: { code: 'ls' }, + }, + { + name: 'Pawg', + url: 'https://bangbros.com/websites/pawg', + slug: 'pawg', + description: null, + network: 'bangbros', + parameters: { code: 'pwg' }, + }, + { + name: 'Penny Show', + url: 'https://bangbros.com/websites/pennyshow', + slug: 'pennyshow', + description: null, + network: 'bangbros', + parameters: { code: 'ps' }, + }, + { + name: 'Porn Star Spa', + url: 'https://bangbros.com/websites/pornstarspa', + slug: 'pornstarspa', + description: null, + network: 'bangbros', + parameters: { code: 'pos' }, + }, + { + name: 'Power Munch', + url: 'https://bangbros.com/websites/powermunch', + slug: 'powermunch', + description: null, + network: 'bangbros', + parameters: { code: 'pm' }, + }, + { + name: 'Public Bang', + url: 'https://bangbros.com/websites/publicbang', + slug: 'publicbang', + description: null, + network: 'bangbros', + parameters: { code: 'pb' }, + }, + { + name: 'Slutty White Girls', + url: 'https://bangbros.com/websites/sluttywhitegirls', + slug: 'sluttywhitegirls', + description: null, + network: 'bangbros', + parameters: { code: 'swg' }, + }, + { + name: 'Stepmom Videos', + url: 'https://bangbros.com/websites/stepmomvideos', + slug: 'stepmomvideos', + description: null, + network: 'bangbros', + parameters: { code: 'smv' }, + }, + { + name: 'Street Ranger', + url: 'https://bangbros.com/websites/thewheeler', + slug: 'streetranger', + description: null, + network: 'bangbros', + parameters: { code: 'sg' }, + }, + { + name: 'Tugjobs', + url: 'https://bangbros.com/websites/tugjobs', + slug: 'tugjobs', + description: null, + network: 'bangbros', + parameters: { code: 'hj' }, + }, + { + name: 'Working Latinas', + url: 'https://bangbros.com/websites/workinglatinas', + slug: 'workinglatinas', + description: null, + network: 'bangbros', + parameters: { code: 'lw' }, + }, + { + name: 'MILF Lessons', + url: 'https://bangbros.com/websites/milflessons', + slug: 'milflessons', + description: null, + network: 'bangbros', + parameters: { code: 'ml' }, + }, + { + name: 'Mr. Anal', + url: 'https://bangbros.com/websites/mranal', + slug: 'mranal', + description: null, + network: 'bangbros', + parameters: { code: 'ma' }, + }, + // BLOWPASS + { + slug: '1000facials', + name: '1000 Facials', + alias: ['1kf'], + url: 'https://www.1000facials.com', + description: 'Welcome to 1000Facials.com, your source for the best facial porn with huge cumshots on your favorite teen and MILF pornstars. Watch all the blowjob action inside!', + network: 'blowpass', + parameters: { + latest: '/en/scenes/updates/%d/Category/0/Pornstar/0', + upcoming: '/en/scenes/upcoming', + }, + }, + { + slug: 'immorallive', + name: 'Immoral Live', + alias: ['il'], + url: 'https://www.immorallive.com', + description: 'Watch live sex shows and videos on ImmoralLive.com, featuring wild and crazy sex orgies, group sex, blowjob competitions and toy play from the famous Porno Dan. The hottest pornstars and amateur girls cum hard inside', + network: 'blowpass', + parameters: { + latest: '/en/videos/All-Categories/0/All-Pornstars/0/All/0/', + upcoming: '/en/videos/All-Categories/0/All-Pornstars/0/All/0/1/upcoming', + }, + }, + { + slug: 'mommyblowsbest', + name: 'Mommy Blows Best', + alias: ['mbb'], + url: 'https://www.mommyblowsbest.com', + description: 'Welcome to MommyBlowsBest.com. Home to thousands of MILF blowjobs and hot mom porn! Come see why experience counts, right here at MommyBlowsBest.com!', + network: 'blowpass', + parameters: { + latest: '/en/scenes/updates/0/Category/0/Actor/', + upcoming: '/en/scenes/upcoming', + }, + }, + { + slug: 'onlyteenblowjobs', + name: 'Only Teen Blowjobs', + alias: ['otb'], + url: 'https://www.onlyteenblowjobs.com', + description: 'OnlyTeenBlowjobs.com brings you the best teen blowjob porn featuring today\'s hottest young pornstars and amateurs. Watch as teens use their little mouths to suck and deepthroat the biggest of cocks!', + network: 'blowpass', + parameters: { + latest: '/en/scenes/updates/0/Category/0/Actor/', + upcoming: '/en/scenes/upcoming', + }, + }, + { + slug: 'throated', + name: 'Throated', + alias: ['ted'], + url: 'https://www.throated.com', + description: 'Throated.com is your portal for extreme throat fuck porn, face fucking videos and deepthroat gagging pornstars. Watch teens and MILFs go balls deep, swallowing cock in HD!', + network: 'blowpass', + parameters: { + latest: '/en/videos/latest/All-Categories/0/All-Pornstars/0/', + upcoming: '/en/videos/upcoming', + }, + }, + { + slug: 'sunlustxxx', + name: 'Sun Lust XXX', + url: 'https://www.sunlustxxx.com', + description: '', + network: 'blowpass', + show: true, // site offline, use only for indexing old scenes + }, + // BOOBPEDIA + { + slug: 'boobpedia', + name: 'Boobpedia', + url: 'https://www.boobpedia.com', + network: 'boobpedia', + }, + // BRAZZERS + { + slug: 'momsincontrol', + name: 'Moms in Control', + alias: ['mic'], + url: 'https://www.brazzers.com/sites/view/id/155/moms-in-control', + description: "There's nothing hotter than seeing a wholesome MILf get dirty, and that's exactly what MILFs in Control is all about: the hottest, sluttiest cougars in the business taking control of sexy situations to get exactly what they want. Feast your eyes as these mature beauties suck and fuck huge cocks, dominating big-dick studs and hot teen sluts until they get the cum that all MILFs crave!", + network: 'brazzers', + }, + { + slug: 'pornstarslikeitbig', + name: 'Pornstars Like It Big', + alias: ['plib'], + url: 'https://www.brazzers.com/sites/view/id/24/pornstars-like-it-big', + description: "A real big dick, that's what everyone wants. Porn-stars are no exception, all the biggest stars agree; BIG COCK is for them. Check out how it stretches their tiny pussies and cums on their round tits. We've got the best chicks jocking the biggest dicks.", + network: 'brazzers', + }, + { + slug: 'bigtitsatwork', + name: 'Big Tits at Work', + alias: ['btaw'], + url: 'https://www.brazzers.com/sites/view/id/15/big-tits-at-work', + description: 'Sitting at your desk, wishing you can fuck every busty coworker you have? Well, stop dreaming and step inside Big Tits At Work where you can watch real life work adventures caught on tape. Nothing But Big Breasted Work Professionals getting drilled all day long...', + network: 'brazzers', + }, + { + slug: 'bigtitsatschool', + name: 'Big Tits at School', + alias: ['btas'], + url: 'https://www.brazzers.com/sites/view/id/20/big-tits-at-school', + description: "The windows have been fogging up at Big Tits At School. Just take a peek inside one of our classrooms and you'll see our smoking hot busty students and big boobed dominant teachers getting their wet pussies stuffed with cock. Stay in your seat! you haven't been dismissed yet.", + network: 'brazzers', + }, + { + slug: 'babygotboobs', + name: 'Baby Got Boobs', + alias: ['bgb'], + url: 'https://www.brazzers.com/sites/view/id/9/baby-got-boobs', + description: "From fresh-faced teen to total slut, baby has boobs and she isn't afraid to show them. But does she know how to use them? These teens crave monster cock in their tight pussies, whether they're ready for a big dicking is another matter.", + network: 'brazzers', + }, + { + slug: 'realwifestories', + name: 'Real Wife Stories', + alias: ['rws'], + url: 'https://www.brazzers.com/sites/view/id/52/real-wife-stories', + description: "You might bring home the bacon, but your wife is still starving. That slut is hungry for cock, she can't get enough, and if you starve her any more she'll get it wherever she can. Better leave work early, or your big-titted wife might just have some giant cock getting squeezed into her waiting pussy, and it won't be yours.", + network: 'brazzers', + }, + { + slug: 'teenslikeitbig', + name: 'Teens Like It Big', + alias: ['tlib'], + url: 'https://www.brazzers.com/sites/view/id/51/teens-like-it-big', + description: "Whether they know it or not, teens love big stiff cocks in their tight pussies. Nothing goes better together than a tight, willing teen and a huge dick. In her bedroom or sneaking out to her boyfriend's, teens just want it all. Cum inside to see greedy sluts get more than they asked for", + network: 'brazzers', + }, + { + slug: 'zzseries', + name: 'ZZ Series', + alias: ['zzs'], + url: 'https://www.brazzers.com/sites/view/id/81/zz-series', + description: 'This is the spot for all our high-end content. ZZ series is exclusive footage that offers only the best in terms of story, stars and action. Check out the hottest porn-stars having the nastiest sex here at the ZZ series', + network: 'brazzers', + }, + { + slug: 'mommygotboobs', + name: 'Mommy Got Boobs', + alias: ['mgb'], + url: 'https://www.brazzers.com/sites/view/id/10/mommy-got-boobs', + description: "When hubby's away MILFS will play. Older women just crave cock, and they're experienced enough to know that only a young stud will do. Big-titted sluts everywhere are sucking and fucking in secret, giving it away to anybody they can. At Mommy Got Boobs, you can get some MILF of your own.", + network: 'brazzers', + }, + { + slug: 'milfslikeitbig', + name: 'MILFs Like It Big', + alias: ['mlib'], + url: 'https://www.brazzers.com/sites/view/id/36/milfs-like-it-big', + description: "When hubby's away milfy will play. These bored housewives want to get fucked and they want it now. They're experienced and know what they want. America's suburbs are full of these cum-de-sacs just waiting to get laid. Their round tits and thick asses are just begging for it. Cum inside, but don't park out front!", + network: 'brazzers', + }, + { + slug: 'bigtitsinuniform', + name: 'Big Tits In Uniform', + alias: ['btiu'], + url: 'https://www.brazzers.com/sites/view/id/73/big-tits-in-uniform', + description: "Big titted wonders are all around us, doing the toughest jobs in the tightest uniforms. Look at them just bursting out of that blouse, or over there, bulging under that nurse's uniform. You know when those tight uniforms come off these sluts go wild, sucking and fucking cocks left and right, their big tits just bouncing. I can't wait to punch the clock.", + network: 'brazzers', + }, + { + slug: 'doctoradventures', + name: 'Doctor Adventures', + alias: ['da'], + url: 'https://www.brazzers.com/sites/view/id/5/doctor-adventures', + description: 'Ever had fantasies about fucking your hot doctor? Live out your fantasies on doctoradventures.com. Countless doctor, patient scenarios come to life on this site with the sexiest and bustiest doctors imaginable! This is your one stop for the best in doctor porn in the world!', + network: 'brazzers', + }, + { + slug: 'brazzersexxtra', + name: 'Brazzers Exxtra', + alias: ['bex'], + url: 'https://www.brazzers.com/sites/view/id/152/brazzers-exxtra', + description: "\"Brazzers Exxtra\" is a doorway to new, unseen hardcore content! There are countless Brazzers videos that were not released throughout the years and we haven't been able to show them to you until now. Random videos staring the world's most popular pornstars, fresh new industry faces and a whole lot more! We'll even throw in an occasional free video from our friends at Mofos, Twisty's and Babes! Check it all out and let us know what you think. If you want more, we'll get it for you!", + network: 'brazzers', + }, + { + slug: 'bigtitsinsports', + name: 'Big Tits In Sports', + alias: ['btis'], + url: 'https://www.brazzers.com/sites/view/id/54/big-tits-in-sports', + description: 'Watch them bounce, watch them score and look at the way they handle those balls! Big tits in sports is here and so are the best big titted, athletic babes. Facials on the court and threesomes on the field, these busty sluts are ready for anything, even if it means playing dirty. Could you take them 1 on 1?', + network: 'brazzers', + }, + { + slug: 'brazzersvault', + name: 'Brazzers Vault', + url: 'https://www.brazzers.com/sites/view/id/56/brazzers-vault', + description: "We've got a whole super computer full of this stuff, technicians are working round the clock in the basement just to keep the thing from overheating. Yeah, this porno is hot. We need to get it out of before the whole thing melts down, that's why it's on the net, for you our loyal Brazzers Members. All the best scenes from all the best girls. In the World. Period.", + network: 'brazzers', + }, + { + slug: 'bigbuttslikeitbig', + name: 'Big Butts Like It Big', + alias: ['bblib'], + url: 'https://www.brazzers.com/sites/view/id/53/big-butts-like-it-big', + description: "You have to pair like with like. And big butts have to have big dicks to go with them. There's really no choice for these big round asses and the babes who fuck with them. Big assed bitches love it hard and deep, and won't have it any other way. Let the ass stuffing begin.", + network: 'brazzers', + }, + { + slug: 'bigwetbutts', + name: 'Big Wet Butts', + alias: ['bwb'], + url: 'https://www.brazzers.com/sites/view/id/8/big-wet-butts', + description: 'A nice, big, round butt is a special shape. Begging for powerful doggy style or straight anal penetration, cover a big butt in oil and it becomes a big wet butt, a true rarity. Watch these soft, tight asses get slathered and pounded like you only wish you could. Look at it bounce!', + network: 'brazzers', + }, + { + slug: 'daywithapornstar', + name: 'Day With A Pornstar', + alias: ['dwp'], + url: 'https://www.brazzers.com/sites/view/id/59/day-with-a-pornstar', + description: "We all know what our favorite stars can do on camera. We're familiar with the way they fuck and suck. What you don't get to see is what they do on their own time. Day With a Porn-star will show you everything, from crazy parties to total babe pals. Nobody else has access like this, it's the closest you get to living the dream.", + network: 'brazzers', + }, + { + slug: 'dirtymasseur', + name: 'Dirty Masseur', + alias: ['dm'], + url: 'https://www.brazzers.com/sites/view/id/150/dirty-masseur', + description: "Take a moment and unwind. Lay down, relax, and enjoy watching and wanking to these luscious Brazzers beauties getting good and greasy. Boobs, butts, and other lady-parts are at their prettiest when shimmering with slick oil. Book an appointment, and slide on in with a lubed babe. Believe me when I say, you'll have the happiest of endings...", + network: 'brazzers', + }, + { + slug: 'hotandmean', + name: 'Hot And Mean', + alias: ['ham'], + url: 'https://www.brazzers.com/sites/view/id/78/hot-and-mean', + description: "The hottest bitches run together. Hot, mean lesbians love to fuck each other and hate each other for being so beautiful. These lesbo sluts can't get enough pussy and love girl on girl action. Forget the dicks, these chicks don't need 'em. You can watch though, they love that.", + network: 'brazzers', + }, + { + slug: 'brazzersenespanol', + name: 'Brazzers en Español', + url: 'https://www.brazzers.com/sites/view/id/157/brazzers-en-espanol', + description: 'Brazzers en Español - El mejor sitio porno en alta definición del mundo ¡Ofreciéndole los vídeos para adultos en alta definición, descargables y en streaming, más exclusivos de Internet! Brazzers cuenta con las estrellas porno más sexys a través de los sitios más calientes en la red. Las estrellas porno y las escenas más calientes en internet. ¡Tendrá acceso a más sexo anal, tetas grandes y culos calientes de los que jamás soñó!', + network: 'brazzers', + }, + { + slug: 'brazzerslive', + name: 'Brazzers Live', + url: 'https://www.brazzers.com/sites/view/id/156/brazzers-live', + description: 'Brazzers is the industry leader for premium porn that breaks the mold. Pioneering its legendary LIVE SHOWS, ZZ is constantly redefining what hardcore erotica is about. Our wild fuck marathons are loaded with the steamiest improvised sex around. Catch a bevy of naked bodacious babes who ravage the biggest dicks with ease and in real-time. Our monster cock hunks rise to the occasion and feed these ravenous vixens who possess an insatiable appetite for cum.', + network: 'brazzers', + }, + { + slug: 'sexproadventures', + name: 'SexPro Adventures', + url: 'https://www.brazzers.com/sites/view/id/23/sexpro-adventures', + description: "Having trouble with your dick-style? The sex pros are here and they'll teach you everything you need to know to be a better man. At your place or theirs, these sluts just want to have a good time. Don't worry, she's a professional.", + network: 'brazzers', + }, + { + slug: 'shesgonnasquirt', + name: 'Shes Gonna Squirt', + url: 'https://www.brazzers.com/sites/view/id/151/shes-gonna-squirt', + description: "Enter the wet world of female ejaculation at shesgonnasquirt! Exclusive hardcore porn of your top pornstars squirting will excite you beyond belief. She's Gonna Squirt is home to the best in HD squirting sex videos. How to make a girl's pussy squirt is an art and should no longer remain a mystery, so join now to become a master.", + network: 'brazzers', + }, + { + slug: 'assesinpublic', + name: 'Asses In Public', + url: 'https://www.brazzers.com/sites/view/id/50/asses-in-public', + description: "Sex in public can present its challenges, never fear, we're willing to accept them. There's something hot about asses out in the street that we just can't deny. Porn-stars fucking on public or just hot girls showing their asses in the airport, we've got both and then some. Asses in Public has the roundest asses and the biggest tits just hanging out, where WILL we show up next?", + network: 'brazzers', + }, + { + slug: 'bustyz', + name: 'Bustyz', + url: 'https://www.brazzers.com/sites/view/id/6/bustyz', + description: "If the internet was a town we'd have the biggest tits around. We still do though, because Bustyz features only the best endowed porn stars in solo and group action. Watch these big-titted babes take cock, suck twat and show off their massive jugs. Real or fake, we don't judge, everyone's welcome under the big tit tent", + network: 'brazzers', + }, + { + slug: 'bustyandreal', + name: 'Busty & Real', + url: 'https://www.brazzers.com/sites/view/id/2/busty-real', + description: "Sometimes you need to take a break from the silicon football set. Busty and real has all the real jugs you need. Round. Soft. and as real as they come. These babes are rocking exactly what momma gave them. They've not afraid to show off their assets and get slammed with dick in the process.", + network: 'brazzers', + }, + { + slug: 'hotchicksbigasses', + name: 'Hot Chicks Big Asses', + url: 'https://www.brazzers.com/sites/view/id/7/hot-chicks-big-asses', + description: 'Everyone gather round; the giant ass. A babe can be hot in a lot of ways and having a big round ass is one of the best. All shapes, sizes and types these girls are the best of the best. Round, supple, jiggling asses taking on dicks and other pussies in equal measure.', + network: 'brazzers', + }, + { + slug: 'cfnm', + name: 'CFNM', + url: 'https://www.brazzers.com/sites/view/id/154/cfnm', + description: "Welcome to the world of clothed female sluts fucking, humiliating and dominating naked men, giving them a dose of what it feels like to be owned. If you love women with power dominating wimpy guys and showing them who's boss; women who crave for cock but get it exactly how they want it, that's what you'll find here. Simply put, the guys don't fuck the women, the women fuck the guys and make them feel like whores!", + network: 'brazzers', + }, + { + slug: 'jugfuckers', + name: 'JugFuckers', + url: 'https://www.brazzers.com/sites/view/id/12/jugfuckers', + description: "Like a sex hot-dog, a big dick fits nicely between two soft, round tits. Tit-fucking isn't easy and never will be. Our girls are pros and take big loads on their faces and tits with a smile. From DD to the smallest things going, we've got every type of tit- fuck around.", + network: 'brazzers', + }, + { + slug: 'teenslikeitblack', + name: 'Teens Like It Black', + url: 'https://www.brazzers.com/sites/view/id/57/teens-like-it-black', + description: "Teens just wanna piss their parents off; no rules, spring break, big black cocks. They love pushing things to the limit, how big and black can it be? Only teen girls know. Watch them get more than they bargained for, long black cocks drilling their tight, inexperienced pussies. It's an epic fuck when the biggest and the tightest meet.", + network: 'brazzers', + }, + { + slug: 'racksandblacks', + name: 'Racks & Blacks', + url: 'https://www.brazzers.com/sites/view/id/11/racks-blacks', + description: "All the interracial action you need is here. Big 'ol black cocks ramming and jamming pussies to the limit. All types of different girls fall prey to the venerable black dick. Wet pussies and fat asses? Bring it on. There's nothing our stable of asses can't handle, they'll keep cumming and cumming.", + network: 'brazzers', + }, + { + slug: 'buttsandblacks', + name: 'Butts & Blacks', + url: 'https://www.brazzers.com/sites/view/id/3/butts-blacks', + description: "Giant black dicks paired with round asses and garnished with the tightest pussies of all colors. Butts and Blacks delivers on its name sake, only the biggest dicks rocking the thickest chicks. These round honeys can take it all in and bounce around like it's a pogo stick. Come check out these soft round asses getting the attention they deserve.", + network: 'brazzers', + }, + // BURNING ANGEL + { + name: 'Burning Angel', + slug: 'burningangel', + alias: ['burna'], + url: 'https://www.burningangel.com', + network: 'burningangel', + parameters: { independent: true }, + }, + // CHERRY PIMPS + { + slug: 'cherrypimps', + name: 'Cherry Pimps', + alias: ['cps'], + url: 'https://cherrypimps.com', + description: 'CherryPimps your premium porn site to Download and Stream the hottest and most exclusive 4K HD videos and pictures on your phone, tablet, TV or console.', + network: 'cherrypimps', + parameters: { + extract: true, + }, + }, + { + slug: 'wildoncam', + name: 'Wild On Cam', + alias: ['woc'], + url: 'https://wildoncam.com', + tags: ['live'], + network: 'cherrypimps', + }, + { + slug: 'britneyamber', + name: 'Britney Amber', + url: 'https://www.britneyamber.com', + network: 'cherrypimps', + parameters: { + extract: true, + }, + }, + // DDF NETWORK + { + slug: 'ddfbusty', + name: 'DDF Busty', + alias: ['ddfb'], + url: 'https://ddfbusty.com', + description: 'Gorgeous Babes with big tits and Euro pornstars with huge natural boobs filmed in Exclusive Full HD, 4K, & VR porn videos.', + network: 'ddfnetwork', + }, + { + slug: 'handsonhardcore', + name: 'Hands on Hardcore', + alias: ['hoh'], + url: 'https://handsonhardcore.com', + description: 'Hardcore Sex & Anal Fucking Exclusive XXX Videos in VR, 4K and full HD with Hot European Pornstars', + network: 'ddfnetwork', + }, + { + slug: 'houseoftaboo', + name: 'House of Taboo', + alias: ['hotb', 'hotab'], + url: 'https://houseoftaboo.com', + description: 'Exclusive BDSM Porn & Extreme Sex Videos Produced in VR, 4K and full HD with The Hottest European Fetish Pornstars', + network: 'ddfnetwork', + }, + { + slug: 'ddfnetworkvr', + name: 'DDF Network VR', + alias: ['ddfvr'], + url: 'https://ddfnetworkvr.com', + description: 'VR Porn Videos shot Exclusively in 180 3D 4K Virtual Reality featuring the Hottest European & American VR Pornstar Babes', + network: 'ddfnetwork', + }, + { + slug: 'eurogirlsongirls', + name: 'Euro Girls on Girls', + url: 'https://eurogirlsongirls.com', + description: 'Hot Lesbian Sex & Glamour Lesbian Porn Videos and Photos Starring Gorgeous European Pornstars in 4K and Full HD VR.', + network: 'ddfnetwork', + }, + { + slug: '1byday', + name: '1By-Day', + url: 'https://1by-day.com', + description: 'Ultra Sexy Exclusive Solo Masturbation Videos in VR, 4K and full HD showcasing Glamour Babes & Intense Orgasms', + network: 'ddfnetwork', + }, + { + slug: 'euroteenerotica', + name: 'Euro Teen Erotica', + alias: ['ete'], + url: 'https://euroteenerotica.com', + description: 'Teen Threesomes & Barely Legal Porn Videos in 4K, VR and FULL HD with Hot Nymphomaniac Teen Babes', + network: 'ddfnetwork', + }, + { + slug: 'hotlegsandfeet', + name: 'Hot Legs and Feet', + url: 'https://hotlegsandfeet.com', + description: 'Foot Fetish & Sexy Legs Porn Videos with Hot and Sexy Euro Pornstars', + network: 'ddfnetwork', + }, + { + slug: 'onlyblowjob', + name: 'Only Blowjob', + alias: ['obj'], + url: 'https://onlyblowjob.com', + description: 'Fantasy Blowjobs & POV Cock Sucking Videos and Photos Produced in VR, 4K and full HD featuring Sexy European Pornstars', + network: 'ddfnetwork', + }, + { + slug: 'fuckinhd', + name: 'Fuck in HD', + url: 'https://fuckinhd.com', + description: 'HD Hardcore Sex & XXX Fantasy Porn Videos and Photos Produced in full HD featuring a Variety of Hardcore Porn Niches.', + network: 'ddfnetwork', + parameters: { native: true }, + }, + { + slug: 'bustylover', + name: 'Busty Lover', + url: 'https://bustylover.com', + network: 'ddfnetwork', + parameters: { native: true }, + }, + // DIGITAL PLAYGROUND + { + slug: 'digitalplayground', + name: 'Digital Playground', + url: 'https://www.digitalplayground.com/scenes', + description: '', + parameters: { extract: true }, + network: 'digitalplayground', + }, + { + slug: 'episodes', + name: 'Episodes', + url: 'https://www.digitalplayground.com/scenes?site=206', + description: '', + network: 'digitalplayground', + }, + { + slug: 'flixxx', + name: 'Flixxx', + url: 'https://www.digitalplayground.com/scenes?site=207', + description: '', + network: 'digitalplayground', + }, + { + slug: 'rawcut', + name: 'Raw Cut', + url: 'https://www.digitalplayground.com/scenes?site=208', + description: '', + network: 'digitalplayground', + }, + { + slug: 'dpstarepisodes', + name: 'DP Star Episodes', + url: 'https://www.digitalplayground.com/scenes?site=209', + description: '', + network: 'digitalplayground', + }, + { + slug: 'blockbuster', + name: 'Blockbuster', + url: 'https://www.digitalplayground.com/scenes?site=211', + description: '', + network: 'digitalplayground', + }, + { + slug: 'dpparodies', + name: 'DP Parodies', + url: 'https://www.digitalplayground.com/scenes?site=212', + description: '', + tags: ['parody'], + network: 'digitalplayground', + }, + // DOGFART NETWORK + { + slug: 'blacksonblondes', + name: 'Blacks On Blondes', + url: 'https://www.blacksonblondes.com/tour', + description: 'Blacks On Blondes is the Worlds Largest and Best Interracial Sex and Interracial Porn website. Black Men and White Women. BlacksOnBlondes has 23 years worth of Hardcore Interracial Content. Featuring the entire Legendary Dogfart Movie Archive', + network: 'dogfartnetwork', + }, + { + slug: 'cuckoldsessions', + name: 'Cuckold Sessions', + url: 'https://www.cuckoldsessions.com/tour', + description: 'Dogfart, the #1 Interracial Network in the World Presents CuckoldSessions.com/tour - Hardcore Cuckold Fetish Videos', + network: 'dogfartnetwork', + }, + { + slug: 'gloryhole', + name: 'Glory Hole', + url: 'https://www.gloryhole.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'blacksoncougars', + name: 'Blacks On Cougars', + url: 'https://www.blacksoncougars.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'wefuckblackgirls', + name: 'We Fuck Black Girls', + alias: ['wfbg'], + url: 'https://www.wefuckblackgirls.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'watchingmymomgoblack', + name: 'Watching My Mom Go Black', + url: 'https://www.watchingmymomgoblack.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'interracialblowbang', + name: 'Interracial Blowbang', + url: 'https://www.interracialblowbang.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'cumbang', + name: 'Cumbang', + url: 'https://www.cumbang.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'interracialpickups', + name: 'Interracial Pickups', + url: 'https://www.interracialpickups.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'watchingmydaughtergoblack', + name: 'Watching My Daughter Go Black', + url: 'https://www.watchingmydaughtergoblack.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'zebragirls', + name: 'Zebra Girls', + url: 'https://www.zebragirls.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'gloryholeinitiations', + name: 'Gloryhole Initiations', + url: 'https://www.gloryhole-initiations.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'dogfartbehindthescenes', + name: 'Dogfart Behind The Scenes', + url: 'https://www.dogfartbehindthescenes.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'blackmeatwhitefeet', + name: 'Black Meat White Feet', + url: 'https://www.blackmeatwhitefeet.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'springthomas', + name: 'Spring Thomas', + url: 'https://www.springthomas.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'katiethomas', + name: 'Katie Thomas', + url: 'https://www.katiethomas.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'ruthblackwell', + name: 'Ruth Blackwell', + url: 'https://www.ruthblackwell.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'candymonroe', + name: 'Candy Monroe', + url: 'https://www.candymonroe.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'wifewriting', + name: 'Wife Writing', + url: 'https://www.wifewriting.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'barbcummings', + name: 'Barb Cummings', + url: 'https://www.barbcummings.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'theminion', + name: 'The Minion', + url: 'https://www.theminion.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'blacksonboys', + name: 'Blacks On Boys', + url: 'https://www.blacksonboys.com/tour', + description: '', + network: 'dogfartnetwork', + }, + { + slug: 'gloryholesandhandjobs', + name: 'Gloryholes And Handjobs', + url: 'https://www.gloryholesandhandjobs.com/tour', + description: '', + network: 'dogfartnetwork', + }, + // EVIL ANGEL + { + slug: 'evilangel', + name: 'Evil Angel', + url: 'https://www.evilangel.com', + description: 'Welcome to the award winning Evil Angel website, home to the most popular pornstars of today, yesterday and tomorrow in their most extreme and hardcore porn scenes to date. We feature almost 30 years of rough sex videos and hardcore anal porn like you\'ve never seen before, and have won countless AVN and XBiz awards including \'Best Site\' and \'Best Studio\'.', + parameters: { independent: true }, + network: 'evilangel', + }, + // FAKE HUB + { + slug: 'fakeagentuk', + name: 'Fake Agent UK', + url: 'https://www.fakehub.com/scenes?site=277', + description: '', + network: 'fakehub', + }, + { + slug: 'fakecop', + name: 'Fake Cop', + url: 'https://www.fakehub.com/scenes?site=278', + description: '', + network: 'fakehub', + }, + { + slug: 'fakehospital', + name: 'Fake Hospital', + url: 'https://www.fakehub.com/scenes?site=279', + description: '', + network: 'fakehub', + }, + { + slug: 'fakeagent', + name: 'Fake Agent', + alias: ['fka'], + url: 'https://www.fakehub.com/scenes?site=280', + description: '', + network: 'fakehub', + }, + { + slug: 'faketaxi', + name: 'Fake Taxi', + alias: ['ftx'], + url: 'https://www.fakehub.com/scenes?site=281', + description: '', + network: 'fakehub', + }, + { + slug: 'publicagent', + name: 'Public Agent', + alias: ['pba'], + url: 'https://www.fakehub.com/scenes?site=282', + description: '', + network: 'fakehub', + }, + { + slug: 'femaleagent', + name: 'Female Agent', + url: 'https://www.fakehub.com/scenes?site=283', + description: '', + network: 'fakehub', + }, + { + slug: 'femalefaketaxi', + name: 'Female Fake Taxi', + alias: ['fft'], + url: 'https://www.fakehub.com/scenes?site=284', + description: '', + network: 'fakehub', + }, + { + slug: 'fakedrivingschool', + name: 'Fake Driving School', + alias: ['fds'], + url: 'https://www.fakehub.com/scenes?site=285', + description: '', + network: 'fakehub', + }, + { + slug: 'fakehuboriginals', + name: 'Fake Hub Originals', + alias: ['fho'], + url: 'https://www.fakehub.com/scenes?site=287', + description: '', + network: 'fakehub', + }, + { + slug: 'fakehostel', + name: 'Fake Hostel', + alias: ['fhl'], + url: 'https://www.fakehub.com/scenes?site=288', + description: '', + network: 'fakehub', + }, + // FAME DIGITAL + { + slug: 'devilsfilm', + name: 'Devil\'s Film', + url: 'https://www.devilsfilm.com', + description: 'Welcome to the best porn network, DevilsFilm.com, featuring teens, MILFs, trans and interracial porn with all of your favorite pornstars in 4k ultra HD!', + parameters: { api: true }, + network: 'famedigital', + }, + { + slug: 'lowartfilms', + name: 'Low Art Films', + url: 'https://www.lowartfilms.com', + description: 'Artistic Hardcore Porn Videos', + network: 'famedigital', + parameters: { + latest: '/en/All/scenes/0/latest/', + upcoming: '/en/All/scenes/0/upcoming', + }, + }, + { + slug: 'daringsex', + name: 'Daring Sex', + url: 'https://www.daringsexhd.com/', + description: 'Welcome the official Daring Sex site, home of high quality erotica, sensual porn and hardcore exploration of the darker side of sexuality. Here you will find a variety of videos for lovers looking for a bit of extra, or something darker with an element of control.', + network: 'famedigital', + parameters: { api: true }, + show: false, // no data sources + }, + { + slug: 'peternorth', + name: 'Peter North', + url: 'https://www.peternorth.com', + description: 'PeterNorth.com features hundreds of cumshots and deepthroat blowjob videos with the hottest teens & MILFs. Watch 25 years of Peter North inside!', + network: 'famedigital', + parameters: { + latest: '/en/videos/AllCategories/0/3/0/All-Dvds/0/latest/', + upcoming: '/en/videos/AllCategories/0/3/0/All-Dvds/0/upcoming', + }, + }, + { + slug: 'roccosiffredi', + name: 'Rocco Siffredi', + url: 'https://www.roccosiffredi.com', + description: 'Welcome to the official RoccoSiffredi.com, the Italian Stallion, with hardcore anal fucking and rough sex from the man himself who has coined the term hardcore.', + parameters: { api: true }, + network: 'famedigital', + }, + { + slug: 'silverstonedvd', + name: 'Silverstone DVD', + url: 'https://www.silverstonedvd.com', + description: 'Welcome to SilverStoneDVDs.com to enjoy unlimited streaming & downloads of teen porn, hot latina anal, young and dumb blowjob, DPs and hardcore porn.', + network: 'famedigital', + parameters: { + latest: '/en/All/scenes/0/latest/', + upcoming: '/en/All/scenes/0/upcoming', + }, + }, + { + slug: 'silviasaint', + name: 'Silvia Saint', + url: 'https://www.silviasaint.com', + description: 'Welcome to Silvia Saint official website. You can see Silvia Saint videos, pictures and blog!', + network: 'famedigital', + parameters: { + latest: '/en/scenes/All/0/', + upcoming: '/en/scenes/All/0/1/upcoming', + }, + }, + { + slug: 'whiteghetto', + name: 'White Ghetto', + url: 'https://www.whiteghetto.com', + description: 'Welcome to WhiteGhetto.com. Home of MILFs, GILFs, Midget porn, Indian babes, hairy pussies and more unusual and oddity porn!', + network: 'famedigital', + parameters: { + latest: '/en/scenes/All/0/superCat/0/latest/', + upcoming: '/en/scenes/All/0/superCat/0/upcoming', + }, + }, + // FANTASY MASSAGE + // Club Fantasy Massage is an aggregate site + { + slug: 'fantasymassage', + name: 'Fantasy Massage', + alias: ['fms'], + url: 'https://www.fantasymassage.com', + network: 'fantasymassage', + parameters: { + latest: 'https://www.fantasymassage.com/en/allvideos/fantasymassage/AllCategories/0/AllPornstars/0/updates/', + upcoming: 'https://www.fantasymassage.com/en/allvideos/fantasymassage/AllCategories/0/Actor/0/upcoming/', + }, + }, + { + slug: 'allgirlmassage', + name: 'All Girl Massage', + alias: ['agm'], + url: 'https://www.allgirlmassage.com', + network: 'fantasymassage', + parameters: { + latest: 'https://www.fantasymassage.com/en/allvideos/allgirlmassage/AllCategories/0/AllPornstars/0/updates/', + upcoming: 'https://www.fantasymassage.com/en/allvideos/allgirlmassage/AllCategories/0/Actor/0/upcoming/', + photos: 'https://www.fantasymassage.com/en/photo', + }, + }, + { + slug: 'nurumassage', + name: 'Nuru Massage', + alias: ['num'], + url: 'https://www.nurumassage.com', + network: 'fantasymassage', + parameters: { + latest: 'https://www.fantasymassage.com/en/allvideos/nurumassage/AllCategories/0/AllPornstars/0/updates/', + upcoming: 'https://www.fantasymassage.com/en/allvideos/nurumassage/AllCategories/0/Actor/0/upcoming/', + photos: 'https://www.fantasymassage.com/en/photo', + }, + }, + { + slug: 'trickyspa', + name: 'Tricky Spa', + alias: ['tspa'], + url: 'https://www.trickyspa.com', + network: 'fantasymassage', + parameters: { + latest: 'https://www.fantasymassage.com/en/allvideos/trickyspa/AllCategories/0/AllPornstars/0/updates/', + upcoming: 'https://www.fantasymassage.com/en/allvideos/trickyspa/AllCategories/0/Actor/0/upcoming/', + photos: 'https://www.fantasymassage.com/en/photo', + }, + }, + { + slug: 'soapymassage', + name: 'Soapy Massage', + url: 'https://www.soapymassage.com', + network: 'fantasymassage', + parameters: { + latest: 'https://www.fantasymassage.com/en/allvideos/soapymassage/AllCategories/0/AllPornstars/0/updates/', + upcoming: 'https://www.fantasymassage.com/en/allvideos/soapymassage/AllCategories/0/Actor/0/upcoming/', + photos: 'https://www.fantasymassage.com/en/photo', + }, + }, + { + slug: 'milkingtable', + name: 'Milking Table', + url: 'https://www.milkingtable.com', + network: 'fantasymassage', + parameters: { + latest: 'https://www.fantasymassage.com/en/allvideos/milkingtable/AllCategories/0/AllPornstars/0/updates/', + upcoming: 'https://www.fantasymassage.com/en/allvideos/milkingtable/AllCategories/0/Actor/0/upcoming/', + photos: 'https://www.fantasymassage.com/en/photo', + }, + }, + { + slug: 'massageparlor', + name: 'Massage Parlor', + url: 'https://www.massage-parlor.com', + network: 'fantasymassage', + parameters: { + latest: 'https://www.fantasymassage.com/en/allvideos/massage-parlor/AllCategories/0/AllPornstars/0/updates/', + upcoming: 'https://www.fantasymassage.com/en/allvideos/massage-parlor/AllCategories/0/Actor/0/upcoming/', + photos: 'https://www.fantasymassage.com/en/photo', + }, + }, + // FREEONES + { + slug: 'freeones', + name: 'FreeOnes', + url: 'https://www.freeones.com', + network: 'freeones', + }, + { + slug: 'freeoneslegacy', + name: 'FreeOnes (Legacy)', + url: 'https://www.freeones.com', + network: 'freeones', + }, + // FULL PORN NETWORK + { + slug: 'analbbc', + name: 'Anal BBC', + url: 'https://analbbc.com', + tags: ['anal', 'bbc'], + network: 'fullpornnetwork', + }, + { + slug: 'analviolation', + name: 'Anal Violation', + url: 'https://analviolation.com', + tags: ['anal'], + network: 'fullpornnetwork', + }, + { + slug: 'analized', + name: 'ANALIZED', + url: 'https://analized.com', + tags: ['anal'], + network: 'fullpornnetwork', + }, + { + slug: 'baddaddypov', + name: 'Bad Daddy POV', + alias: ['bdpov'], + url: 'https://baddaddypov.com', + tags: ['pov', 'family'], + network: 'fullpornnetwork', + }, + { + slug: 'dtfsluts', + name: 'DTF Sluts', + url: 'https://dtfsluts.com', + network: 'fullpornnetwork', + }, + { + slug: 'girlfaction', + name: 'Girlfaction', + url: 'https://girlfaction.com', + tags: ['lesbian'], + network: 'fullpornnetwork', + }, + { + slug: 'hergape', + name: 'Her Gape', + url: 'https://hergape.com', + tags: ['anal'], + network: 'fullpornnetwork', + }, + { + slug: 'homemadeanalwhores', + name: 'Homemade Anal Whores', + url: 'https://homemadeanalwhores.com', + tags: ['anal'], + network: 'fullpornnetwork', + }, + { + slug: 'jamesdeen', + name: 'James Deen', + url: 'https://jamesdeen.com', + network: 'fullpornnetwork', + }, + { + slug: 'onlyprince', + name: 'Only Prince', + url: 'https://onlyprince.com', + tags: ['bbc'], + network: 'fullpornnetwork', + }, + { + slug: 'pervertgallery', + name: 'Pervert Gallery', + url: 'http://pervertgallery.com', + network: 'fullpornnetwork', + }, + { + slug: 'povperverts', + name: 'POV Perverts', + url: 'http://povperverts.net', + tags: ['pov'], + network: 'fullpornnetwork', + }, + { + slug: 'teenageanalsluts', + name: 'Teenage Anal Sluts', + url: 'https://teenageanalsluts.com', + tags: ['anal'], + network: 'fullpornnetwork', + }, + { + slug: 'twistedvisual', + name: 'Twisted Visual', + url: 'https://twistedvisual.com', + network: 'fullpornnetwork', + }, + { + slug: 'yourmomdoesanal', + name: 'Your Mom Does Anal', + url: 'http://yourmomdoesanal.com', + tags: ['anal', 'milf'], + network: 'fullpornnetwork', + }, + { + slug: 'yourmomdoesporn', + name: 'Your Mom Does Porn', + url: 'https://yourmomdoesporn.com', + tags: ['milf'], + network: 'fullpornnetwork', + }, + { + slug: 'mugfucked', + name: 'Mugfucked', + url: 'https://mugfucked.com', + tags: ['facefucking', 'blowjob'], + network: 'fullpornnetwork', + }, + // GIRLSWAY + { + slug: 'girlsway', + name: 'Girlsway', + alias: ['gw'], + 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!', + tags: ['lesbian'], + network: 'girlsway', + parameters: { + scene: 'https://www.girlsway.com/en/video/girlsway', + }, + }, + { + slug: 'girlstryanal', + name: 'Girls Try Anal', + alias: ['gta'], + url: 'https://www.girlstryanal.com', + network: 'girlsway', + parameters: { + referer: 'https://www.girlsway.com', + mobile: 'https://m.dpfanatics.com/en/video', + }, + }, + { + slug: 'mommysgirl', + name: 'Mommy\'s Girl', + alias: ['mmgs'], + url: 'https://www.mommysgirl.com', + network: 'girlsway', + parameters: { + mobile: 'https://m.dpfanatics.com/en/video', + }, + }, + { + slug: 'webyoung', + name: 'Web Young', + url: 'https://www.webyoung.com', + network: 'girlsway', + parameters: { + referer: 'https://www.girlsway.com', + mobile: 'https://m.dpfanatics.com/en/video', + }, + }, + { + slug: 'sextapelesbians', + name: 'Sex Tape Lesbians', + url: 'https://www.sextapelesbians.com', + network: 'girlsway', + parameters: { + scene: 'https://www.girlsway.com/en/video/sextapelesbians', // sextapelesbians.com redirects to isthisreal.com + referer: 'https://www.girlsway.com', + }, + }, + { + slug: 'momsonmoms', + name: 'Moms On Moms', + url: 'https://www.girlsway.com/en/videos/momsonmoms', + network: 'girlsway', + parameters: { + scene: 'https://www.girlsway.com/en/video/sextapelesbians', + referer: 'https://www.girlsway.com', + }, + }, + // HUSSIE PASS + { + slug: 'hussiepass', + name: 'Hussie Pass', + url: 'https://www.hussiepass.com', + network: 'hussiepass', + }, + { + slug: 'eyeontheguy', + name: 'Eye On The Guy', + url: 'https://eyeontheguy.com', + tags: ['male-focus'], + network: 'hussiepass', + parameters: { + t1: true, + }, + }, + { + slug: 'seehimfuck', + name: 'See Him Fuck', + url: 'https://seehimfuck.com', + tags: ['male-focus'], + network: 'hussiepass', + parameters: { + tour: true, + }, + }, + { + slug: 'interracialpovs', + name: 'Interracial POVs', + url: 'https://www.interracialpovs.com', + tags: ['interracial', 'pov'], + network: 'hussiepass', + parameters: { + tour: true, + }, + }, + { + slug: 'povpornstars', + name: 'POV Pornstars', + url: 'http://www.povpornstars.com', + tags: ['pov'], + network: 'hussiepass', + parameters: { + latest: 'http://www.povpornstars.com/tour/categories/movies_%d_d.html', + profile: 'http://www.povpornstars.com/tour/models/%s.html', + tour: true, + }, + }, + // HUSH PASS + { + slug: 'shotherfirst', + name: 'Shot Her First', + url: 'https://shotherfirst.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/shot-her-first_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'whitezilla', + name: 'WhiteZilla', + url: 'https://whitezilla.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/whitezilla_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'frathousefuckfest', + name: 'Frat House Fuck Fest', + url: 'https://frathousefuckfest.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/frat-house-fuck-fest_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'freakyfirsttimers', + name: 'Freaky First Timers', + url: 'https://freakyfirsttimers.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/freaky-first-timers_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'milfinvaders', + name: 'MILF Invaders', + url: 'https://milfinvaders.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/milf-invaders_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'housewivesneedcash', + name: 'Housewives Need Cash', + url: 'https://housewivesneedcash.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/housewives-need-cash_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'bubblebuttbonanza', + name: 'Bubble Butt Bonanza', + url: 'https://bubblebuttbonanza.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/bubble-butt-bonanza_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'suburbansexparty', + name: 'Suburban Sex Party', + url: 'https://suburbansexparty.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/suburban-sex-party_%_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'buttnakedinthestreets', + name: 'Butt Naked In The Streets', + url: 'https://buttnakedinthestreets.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/ButtNakedInStreets_%d_d.html', + media: 'https://hushpass.com', + match: 'Butt Naked In Streets', + t1: true, + }, + }, + { + slug: 'muffbumperpatrol', + name: 'Muff Bumper Patrol', + url: 'https://muffbumperpatrol.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/muff-bumper-patrol_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'biggathananigga', + name: 'Bigga Than A Nigga', + url: 'https://biggathananigga.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/bigga-than-a-nigga_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'bachelorpartyfuckfest', + name: 'Bachelor Party Fuck Fest', + url: 'https://bachelorpartyfuckfest.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/bachelor-party-fuck-fest_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'teencumdumpsters', + name: 'Teen Cum Dumpsters', + url: 'https://teencumdumpsters.com', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/teen-cum-dumpsters_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'povhunnies', + name: 'POV Hunnies', + network: 'hushpass', + parameters: { + latest: 'https://hushpass.com/t1/categories/POVHunnies_%d_d.html', + media: 'https://hushpass.com', + t1: true, + }, + }, + { + slug: 'hushpass', + name: 'Hush Pass', + url: 'https://hushpass.com', + network: 'hushpass', + parameters: { + t1: true, + accFilter: true, + }, + }, + // INTERRACIAL PASS + { + slug: '2bigtobetrue', + name: '2 Big To Be True', + url: 'https://www.2bigtobetrue.com/', + tags: ['interracial'], + network: 'interracialpass', + parameters: { + latest: 'https://www.interracialpass.com/t1/categories/2-big-to-be-true_%d_d.html', + media: 'https://www.interracialpass.com', + t1: true, + }, + }, + { + slug: 'abominableblackman', + name: 'Abominable Black Man', + url: 'https://www.abominableblackman.com/', + tags: ['interracial'], + network: 'interracialpass', + parameters: { + latest: 'https://www.interracialpass.com/t1/categories/abominable-black-man_%d_d.html', + media: 'https://www.interracialpass.com', + t1: true, + }, + }, + { + slug: 'bootyannihilation', + name: 'Booty Annihilation', + tags: ['interracial'], + network: 'interracialpass', + parameters: { + latest: 'https://www.interracialpass.com/t1/categories/BootyAnnihilation_%d_d.html', + media: 'https://www.interracialpass.com', + t1: true, + }, + }, + { + slug: 'daddysworstnightmare', + name: 'Daddy\'s Worst Nightmare', + url: 'https://www.daddysworstnightmare.com/', + tags: ['interracial'], + network: 'interracialpass', + parameters: { + latest: 'https://www.interracialpass.com/t1/categories/daddys-worst-nightmare_%d_d.html', + media: 'https://www.interracialpass.com', + t1: true, + }, + }, + { + slug: 'monstercockfuckfest', + name: 'Monster Cock Fuck Fest', + url: 'https://www.monstercockfuckfest.com/', + tags: ['interracial'], + network: 'interracialpass', + parameters: { + latest: 'https://www.interracialpass.com/t1/categories/monster-cock-fuck-fest_%d_d.html', + media: 'https://www.interracialpass.com', + t1: true, + }, + }, + { + slug: 'mydaughtersfuckingablackdude', + name: 'My Daughter\'s Fucking A Black Dude', + url: 'https://www.mydaughtersfuckingablackdude.com/', + tags: ['interracial'], + network: 'interracialpass', + parameters: { + latest: 'https://www.interracialpass.com/t1/categories/my-daughters-fucking-a-black-dude_%d_d.html', + media: 'https://www.interracialpass.com', + t1: true, + }, + }, + { + slug: 'mymomsfuckingblackzilla', + name: 'My Mom\'s Fucking Blackzilla', + url: 'https://www.mymomsfuckingblackzilla.com/', + tags: ['interracial'], + network: 'interracialpass', + parameters: { + latest: 'https://www.interracialpass.com/t1/categories/my-moms-fucking-blackzilla_%d_d.html', + media: 'https://www.interracialpass.com', + t1: true, + }, + }, + { + slug: 'mywifesfirstmonstercock', + name: 'My Wife\'s First Monster Cock', + url: 'https://www.mywifesfirstmonstercock.com/', + tags: ['interracial'], + network: 'interracialpass', + parameters: { + latest: 'https://www.interracialpass.com/t1/categories/my-wifes-first-monster-cock_%d_d.html', + media: 'https://www.interracialpass.com', + match: 'My Wifes First Monster Cock', + t1: true, + }, + }, + { + slug: 'interracialpass', + name: 'Interracial Pass', + url: 'https://www.interracialpass.com', + tags: ['interracial'], + network: 'interracialpass', + parameters: { + t1: true, + accFilter: true, + }, + }, + // INSEX + { + slug: 'sexuallybroken', + name: 'Sexually Broken', + alias: ['seb'], + url: 'https://www.sexuallybroken.com', + tags: ['bdsm'], + network: 'insex', + }, + { + slug: 'infernalrestraints', + name: 'Infernal Restraints', + alias: ['infr'], + url: 'https://www.infernalrestraints.com', + tags: ['bdsm'], + network: 'insex', + }, + { + slug: 'hardtied', + name: 'Hardtied', + url: 'https://www.hardtied.com', + tags: ['bdsm'], + network: 'insex', + }, + { + slug: 'realtimebondage', + name: 'Real Time Bondage', + alias: ['rtb'], + url: 'https://www.realtimebondage.com', + tags: ['bdsm', 'live'], + network: 'insex', + }, + { + slug: 'topgrl', + name: 'TopGrl', + alias: ['tg'], + url: 'https://www.topgrl.com', + tags: ['bdsm', 'femdom'], + network: 'insex', + }, + { + slug: 'paintoy', + name: 'Paintoy', + url: 'https://www.paintoy.com', + tags: ['bdsm'], + network: 'insex', + }, + { + slug: 'aganmedon', + name: 'Agan Medon', + url: 'https://www.aganmedon.com', + tags: ['bdsm', 'animated'], + network: 'insex', + }, + { + slug: 'sensualpain', + name: 'Sensual Pain', + url: 'https://www.sensualpain.com', + tags: ['bdsm'], + network: 'insex', + }, + // JAYS POV + { + slug: 'jayspov', + name: 'Jay\'s POV', + url: 'https://jayspov.net', + network: 'jayrock', + }, + { + slug: 'cospimps', + name: 'CosPimps', + url: 'https://cospimps.com', + network: 'jayrock', + }, + { + slug: 'blackforwife', + name: 'Black for Wife', + url: 'https://www.blackforwife.com', + network: 'jayrock', + parameters: { + referer: 'https://freetour.adulttime.com/en/blackforwife', + useGamma: true, + scene: false, + deep: 'https://21sextury.com/en/video', + photos: false, + }, + }, + // JESSE LOADS MONSTER FACIALS + { + slug: 'jesseloadsmonsterfacials', + name: 'Jesse Loads Monster Facials', + url: 'http://www.jesseloadsmonsterfacials.com', + network: 'jesseloadsmonsterfacials', + tags: ['facial', 'blowjob'], + parameters: { + independent: true, + }, + }, + // JULES JORDAN + { + slug: 'julesjordan', + name: 'Jules Jordan', + url: 'https://www.julesjordan.com', + description: 'Jules Jordan\'s Official Membership Site', + network: 'julesjordan', + }, + { + slug: 'theassfactory', + name: 'The Ass Factory', + url: 'https://www.theassfactory.com', + network: 'julesjordan', + }, + { + slug: 'spermswallowers', + name: 'Sperm Swallowers', + url: 'https://www.spermswallowers.com', + network: 'julesjordan', + }, + { + slug: 'manuelferrara', + name: 'Manuel Ferrara', + alias: ['mfa'], + url: 'https://www.manuelferrara.com', + network: 'julesjordan', + }, + { + slug: 'girlgirl', + name: 'Girl Girl', + url: 'https://www.girlgirl.com', + tags: ['lesbian'], + network: 'julesjordan', + }, + // KELLY MADISON MEDIA + { + slug: 'teenfidelity', + name: 'Teen Fidelity', + alias: ['tf'], + url: 'https://www.teenfidelity.com', + description: 'Home of Kelly Madison and Ryan Madison', + network: 'kellymadison', + }, + { + slug: 'pornfidelity', + name: 'Porn Fidelity', + alias: ['pf'], + url: 'https://www.pornfidelity.com', + description: 'Home of Kelly Madison and Ryan Madison', + network: 'kellymadison', + }, + { + slug: 'kellymadison', + name: 'Kelly Madison', + url: 'https://www.pornfidelity.com', + description: 'Home of Kelly Madison and Ryan Madison', + network: 'kellymadison', + }, + // KINK + { + slug: 'thirtyminutesoftorment', + name: '30 Minutes of Torment', + url: 'https://www.kink.com/channel/30minutesoftorment', + description: 'Thick-Muscled Men Endure 30 Minutes Of BDSM Torment By A Pain-Inducing Dom. Can they take 30 Minutes of Torment? Watch as top gay pornstars take on the challenge of a lifetime. Bondage, BDSM, punishment, huge insertions, & more!', + network: 'kink', + }, + { + slug: 'boundgangbangs', + name: 'Bound Gangbangs', + alias: ['bgb', 'bgbs'], + url: 'https://www.kink.com/channel/boundgangbangs', + description: 'Powerless whores tied in bondage and stuffed with a cock in every hole. At BoundGangbangs women get surprise extreme gangbangs, blindfolds, deepthroat blowjobs, sex punishment, bondage, double penetration and interracial sex.', + network: 'kink', + }, + { + slug: 'boundgods', + name: 'Bound Gods', + url: 'https://www.kink.com/channel/boundgods', + description: 'Muscle Studs Are Bound, Gagged & Spread For A Deep Cock Pounding. Not even the most rock hard muscled studs can escape punishment & submission on BoundGods.com Watch the hottest studs get tied down, fucked & submitted.', + tags: ['gay'], + network: 'kink', + }, + { + slug: 'boundinpublic', + name: 'Bound in Public', + url: 'https://www.kink.com/channel/boundinpublic', + description: 'Cum Starved Sluts Humiliated And Fucked Hard In Public By Hung Studs.', + network: 'kink', + }, + { + slug: 'brutalsessions', + name: 'Brutal Sessions', + url: 'https://www.kink.com/channel/brutalsessions', + description: "Hardcore BDSM jam packed with XXX fucking in bondage! We're taking dungeon sex beyond the castle!", + network: 'kink', + }, + { + slug: 'buttmachineboys', + name: 'Butt Machine Boys', + url: 'https://www.kink.com/channel/buttmachineboys', + description: 'Powerful Fucking Machines Pound Hot Men Hard & Deep.', + tags: ['gay'], + network: 'kink', + }, + { + slug: 'devicebondage', + name: 'Device Bondage', + alias: ['deb'], + url: 'https://www.kink.com/channel/devicebondage', + description: 'The Domination Of Sluts In Barbaric Metal Devices. Device Bondage takes BDSM porn to new levels with extreme restraints & unique devices with beautiful pornstars to huge, forced squirting orgasms.', + network: 'kink', + }, + { + slug: 'divinebitches', + name: 'Divine Bitches', + url: 'https://www.kink.com/channel/divinebitches', + description: 'Beautiful Women Dominate Submissive Men With Pain, Humiliation And Strap-On Fucking. The best in femdom and bondage. Men on Divine Bitches respond with obedience, ass worship, cunt worship, oral servitude, pantyhose worship, and foot worship.', + tags: ['femdom'], + network: 'kink', + }, + { + slug: 'electrosluts', + name: 'Electrosluts', + url: 'https://www.kink.com/channel/electrosluts', + description: 'Lezdoms Take Submissive Sluts To Their Limits, Shocking & Tormenting Their Wet Hot Pussies. Pornstars live out their electric bondage fantasies while dominatrixes use electrodes, paddles, caddle prods, & more to bring them to intense orgasms!', + network: 'kink', + }, + { + slug: 'everythingbutt', + name: 'Everything Butt', + url: 'https://www.kink.com/channel/everythingbutt', + description: 'Gaping Anal Holes Are Stuffed & Stretched To The Max. Anal Fisting, Enemas & Rimming Has Never Tasted So Good. EverythingButt.com explores the extreme limits of FemDom lesbian anal. Watch asses get destroyed by brutal fistings, huge insertions, double anal & more!', + network: 'kink', + }, + { + slug: 'filthyfemdom', + name: 'Filthy Femdom', + url: 'https://www.kink.com/channel/filthyfemdom', + description: 'Powerful women dominate your dirty dreams of sweet pain, seductive bondage, and sexual servitude.', + tags: ['femdom'], + network: 'kink', + }, + { + slug: 'familiestied', + name: 'Families Tied', + url: 'https://www.kink.com/channel/familiestied', + description: 'Intense BDSM family role play threesomes & more.', + network: 'kink', + }, + { + slug: 'footworship', + name: 'Foot Worship', + url: 'https://www.kink.com/channel/footworship', + description: 'Satisfy Your Foot Fetish With The Kinkiest Foot Action. Enjoy Trampling, Foot Jobs, High Heels, And Pantyhose.', + network: 'kink', + }, + { + slug: 'fuckedandbound', + name: 'Fucked and Bound', + alias: ['fab'], + url: 'https://www.kink.com/channel/fuckedandbound', + description: 'Extreme Anal, Rope Bondage, & Brutal Face Fucking.', + network: 'kink', + }, + { + slug: 'fuckingmachines', + name: 'Fucking Machines', + alias: ['fm', 'fum'], + url: 'https://www.kink.com/channel/fuckingmachines', + description: 'Machines Fucking Squirting Pussies With Extreme Insertions. Fucking Machines is the ultimate hardcore sex toy porn. Huge dildos strapped to sex machines relentlessly fucking pornstars to real squirting oragsms!', + network: 'kink', + }, + { + slug: 'hardcoregangbang', + name: 'Hardcore Gangbang', + url: 'https://www.kink.com/channel/hardcoregangbang', + description: "Where all women's hardcore gangbang fantasies come true. Watch extreme, brutal gangbangs with pornstars, models, & MILFs that crave cock in every hole. HardcoreGangbang.com has the best creampie gang bangs online.", + network: 'kink', + }, + { + slug: 'hogtied', + name: 'Hogtied', + alias: ['ht'], + url: 'https://www.kink.com/channel/hogtied', + description: 'Your favorite girls restrained with rope, punished & trained. Hogtied is the original extreme bondage porn website. Watch top pornstars and pain sluts in brutal bondage, getting tormented, and forced to orgasm!', + network: 'kink', + }, + { + slug: 'kinkfeatures', + name: 'Kink Features', + url: 'https://www.kink.com/channel/kinkfeatures', + description: 'Curated scenes by Kink\'s very best directors.', + network: 'kink', + }, + { + slug: 'kinkuniversity', + name: 'Kink University', + url: 'https://www.kink.com/channel/kinkuniversity', + description: 'Learn BDSM Technical Skills & Theories From Respected Teachers In The Kink Community. Learn BDSM skills and improve your sex techniques. Video tutorials feature top sex ed experts and hardcore demos on topics from bondage to relationships.', + network: 'kink', + }, + { + slug: 'meninpain', + name: 'Men In Pain', + url: 'https://www.kink.com/channel/meninpain', + description: 'Submissive Men Violated With Verbal Humiliation And Harsh Punishment By Beautiful Dominatrices.', + network: 'kink', + }, + { + slug: 'menonedge', + name: 'Men on Edge', + url: 'https://www.kink.com/channel/menonedge', + description: "Hot Guys Begging To Cum Are Brought To The Edge Of Complete Submission And Allowed To Blow Their Loads. Men on Edge has perfected the art of gay BDSM & edging porn. Watch straight men bound up & edged by dominant gay pornstars until they can't help but cum!", + tags: ['gay'], + network: 'kink', + }, + { + slug: 'nakedkombat', + name: 'Naked Kombat', + url: 'https://www.kink.com/channel/nakedkombat', + description: 'Fight Fit Studs Go Head To Head In A Battle For Dominance. The Loser Gets Pinned And Punish Fucked Without Mercy', + network: 'kink', + }, + { + slug: 'publicdisgrace', + name: 'Public Disgrace', + alias: ['pud'], + url: 'https://www.kink.com/channel/publicdisgrace', + description: 'Women Bound Stripped And Punished In Public Get Hardcore Fucked Where Everyone Can See. Unscripted public humiliation & punishment of submissive slaves in real life locations. PublicDisgrace features the best outdoor BDSM & voyeur porn!', + network: 'kink', + }, + { + slug: 'sadisticrope', + name: 'Sadistic Rope', + alias: ['sr'], + url: 'https://www.kink.com/channel/sadisticrope', + description: 'Innocence Taken By Extreme Rope Bondage, Hardcore BDSM And Pussy-Destroying Orgasms.', + network: 'kink', + }, + { + slug: 'sexandsubmission', + name: 'Sex and Submission', + alias: ['sas'], + url: 'https://www.kink.com/channel/sexandsubmission', + description: 'Submissive Sluts Are Dominated With Rough Sex And Bondage. Real pornstars, hardcore bondage, master & slave roles are what SexAndSubmission.com is all about. Watch submissive sluts give in to total domination!', + network: 'kink', + }, + { + slug: 'strugglingbabes', + name: 'Struggling Babes', + url: 'https://www.kink.com/channel/strugglingbabes', + description: 'Demystifying and celebrating alternative sexuality by providing the most authentic kinky videos. Experience the other side of porn.', + network: 'kink', + }, + { + slug: 'thetrainingofo', + name: 'The Training of O', + alias: ['tto'], + url: 'https://www.kink.com/channel/thetrainingofo', + description: 'Slaves Are Trained And Rewarded With Hardcore Bondage And Sex. Watch real pornstars undergo extreme slave training through hardcore bondage & BDSM porn. The Training of O is the ultimate slave / master experience!', + network: 'kink', + }, + { + slug: 'theupperfloor', + name: 'The Upper Floor', + alias: ['tuf'], + url: 'https://www.kink.com/channel/theupperfloor', + description: 'Trained slaves serve the house and their master in intense BDSM and kinky threesomes. The Upper Floor is a voyeuristic look into BDSM and fetish porn shoots with real submissive pornstars living out their kinky fantasies live on cam.', + network: 'kink', + }, + { + slug: 'tspussyhunters', + name: 'TS Pussy Hunters', + url: 'https://www.kink.com/channel/tspussyhunters', + description: 'Hot TS cocks prey on the wet pussies of submissive ladies who are fucked hard till they cum. Dominant TS femme fatales with the hardest dicks, the softest tits, and the worst intentions dominate, bind, and punish bitches on the ultimate transfucking porn site.', + tags: ['transsexual'], + network: 'kink', + }, + { + slug: 'tsseduction', + name: 'TS Seduction', + url: 'https://www.kink.com/channel/tsseduction', + description: 'Sexy TS Women With Huge Cocks Dominate The Holes Of Straight Boys. Real TS women who are drop-dead gorgeous from their pretty faces to their big tits to their hard TS cocks. TS Seduction is the ultimate in transsexual bondage porn.', + network: 'kink', + }, + { + slug: 'ultimatesurrender', + name: 'Ultimate Surrender', + url: 'https://www.kink.com/channel/ultimatesurrender', + description: 'Competitive Female Wrestling Where The Loser Gets Strap-On Punish Fucked. Ultimate Surrender features hardcore naked female wrestling porn videos where the winner gets to dominate the loser with some kinky lesbian FemDom!', + network: 'kink', + }, + { + slug: 'waterbondage', + name: 'Water Bondage', + url: 'https://www.kink.com/channel/waterbondage', + description: 'Helpless Bound Beauties Sprayed, Dunked And Tormented Until They Cum Hard & Wet.', + network: 'kink', + }, + { + slug: 'whippedass', + name: 'Whipped Ass', + alias: ['wpa', 'wa'], + url: 'https://www.kink.com/channel/whippedass', + description: 'Beautiful Submissive Sluts Take A Hard Fucking From Powerful Dominant Women. Watch brutal lesbian dominatrixes push submissive sluts to their orgasmic breaking points on WhippedAss! Hardcore fisting, huge strapons & face sitting!', + network: 'kink', + }, + { + slug: 'wiredpussy', + name: 'Wired Pussy', + url: 'https://www.kink.com/channel/wiredpussy', + description: 'Gorgeous Women Submit To Electricity, Are Zapped, Shocked & Prodded To Orgasm.', + network: 'kink', + }, + // LEGALPORNO + { + slug: 'legalporno', + name: 'LegalPorno', + alias: ['clip'], + url: 'https://www.legalporno.com', + description: 'The Best HD Porn For You!', + parameters: { independent: true }, + network: 'legalporno', + }, + // METRO HD + { + slug: 'devianthardcore', + name: 'Deviant Hardcore', + url: 'https://www.devianthardcore.com', + tags: ['bdsm'], + parameters: { + siteId: 305, + native: true, + }, + network: 'metrohd', + }, + { + slug: 'shewillcheat', + name: 'She Will Cheat', + url: 'https://www.shewillcheat.com', + parameters: { + siteId: 306, + native: true, + }, + network: 'metrohd', + }, + { + slug: 'familyhookups', + name: 'Family Hookups', + url: 'https://www.familyhookups.com', + tags: ['family'], + parameters: { + siteId: 307, + native: true, + }, + network: 'metrohd', + }, + { + slug: 'kinkyspa', + name: 'Kinky Spa', + url: 'https://www.kinkyspa.com', + tags: ['massage'], + parameters: { + siteId: 308, + native: true, + }, + network: 'metrohd', + }, + { + slug: 'girlgrind', + name: 'Girl Grind', + url: 'https://www.girlgrind.com', + tags: ['lesbian'], + parameters: { + siteId: 309, + native: true, + }, + network: 'metrohd', + }, + // MEN + { + slug: 'bigdicksatschool', + name: 'Big Dicks At School', + url: 'https://www.bigdicksatschool.com', + description: '', + parameters: { siteId: 252 }, + tags: ['gay'], + network: 'men', + }, + { + slug: 'drillmyhole', + name: 'Drill My Hole', + url: 'https://www.drillmyhole.com', + description: '', + parameters: { siteId: 253 }, + tags: ['gay'], + network: 'men', + }, + { + slug: 'str8togay', + name: 'Str8 to Gay', + url: 'https://www.str8togay.com', + tags: ['gay'], + parameters: { siteId: 254 }, + network: 'men', + }, + { + slug: 'thegayoffice', + name: 'The Gay Office', + url: 'https://www.thegayoffice.com', + tags: ['gay'], + parameters: { siteId: 255 }, + network: 'men', + }, + { + slug: 'jizzorgy', + name: 'Jizz Orgy', + url: 'https://www.jizzorgy.com', + tags: ['gay'], + parameters: { siteId: 256 }, + network: 'men', + }, + { + slug: 'menofuk', + name: 'Men of UK', + url: 'https://www.menofuk.com', + tags: ['gay'], + parameters: { siteId: 258 }, + network: 'men', + }, + { + slug: 'toptobottom', + name: 'Top to Bottom', + url: 'https://www.toptobottom.com', + tags: ['gay'], + parameters: { siteId: 259 }, + network: 'men', + }, + { + slug: 'godsofmen', + name: 'Gods of Men', + url: 'https://www.godsofmen.com', + tags: ['gay'], + parameters: { siteId: 260 }, + network: 'men', + }, + // MINDGEEK + { + slug: 'pornhub', + name: 'PornHub', + url: 'https://www.pornhub.com', + description: '', + network: 'mindgeek', + }, + { + slug: 'tube8vip', + name: 'Tube8Vip', + url: 'https://www.tube8vip.com', + description: '', + parameters: { native: true }, + network: 'mindgeek', + }, + { + slug: 'transangels', + name: 'TransAngels', + url: 'https://www.transangels.com', + tags: ['transsexual'], + parameters: { native: true }, + network: 'mindgeek', + }, + { + slug: 'trueamateurs', + name: 'True Amateurs', + url: 'https://www.trueamateurs.com', + description: 'TrueAmateurs.com is the best homemade porn from real amateurs. Watch these real hot couples in our exclusive scenes.', + parameters: { native: true }, + network: 'mindgeek', + }, + // MIKE ADRIANO + { + slug: 'trueanal', + name: 'True Anal', + url: 'https://trueanal.com', + description: 'TrueAnal is the hottest site with all hardcore Anal content and only the most popular pornstars getting their asses pounded and gapped with huge cock and more!', + tags: ['anal'], + network: 'mikeadriano', + }, + { + slug: 'allanal', + name: 'All Anal', + url: 'https://allanal.com', + description: 'Popular babes getting their tight asses filled with cock! Pure anal fucking only at AllAnal!', + tags: ['anal', 'mff'], + network: 'mikeadriano', + }, + { + slug: 'nympho', + name: 'Nympho', + url: 'https://nympho.com', + description: 'These Babes have an appetite for nasty, sloppy fucking!', + network: 'mikeadriano', + }, + { + slug: 'swallowed', + name: 'Swallowed', + url: 'https://swallowed.com', + description: 'Swallowed is a Premium adult website for the hottest Blowjobs content online with only the most popular pornstars swallowing cock!', + tags: ['blowjob', 'deepthroat', 'facefucking'], + network: 'mikeadriano', + }, + // MILE HIGH MEDIA + { + slug: 'doghousedigital', + name: 'Doghouse Digital', + url: 'https://www.doghousedigital.com', + parameters: { siteId: 321 }, + network: 'milehighmedia', + }, + { + slug: 'milehighmedia', + name: 'Mile High Media', + url: 'https://www.milehighmedia.com/scenes?site=323', + network: 'milehighmedia', + }, + { + slug: 'realityjunkies', + name: 'Reality Junkies', + url: 'https://www.realityjunkies.com', + parameters: { siteId: 324 }, + network: 'milehighmedia', + }, + { + slug: 'sweetheartvideo', + name: 'Sweetheart Video', + url: 'https://www.sweetheartvideo.com', + parameters: { siteId: 325 }, + network: 'milehighmedia', + }, + { + slug: 'sweetsinner', + name: 'Sweet Sinner', + url: 'https://www.sweetsinner.com', + parameters: { siteId: 326 }, + network: 'milehighmedia', + }, + { + slug: 'iconmale', + name: 'Icon Male', + url: 'https://www.iconmale.com', + tags: ['gay'], + parameters: { native: true }, + network: 'milehighmedia', + }, + // MOFOS + { + slug: 'girlsgonepink', + name: 'Girls Gone Pink', + url: 'https://www.mofos.com/scenes?site=204', + description: "There comes a point in every woman's life when she gets a little curious about what some hot girl on girl sex could be like. Whether they're lesbian or just straight and incredibly daring and open-minded, the end result is the same. GirlsGonePink.com is full of soft lips, long flowing hair, and sensual feminine figures that are enough to get any horny minx's blood pumping. Premium full-length lesbian porn videos await you full of perfect boobs, pointy nipples, round butts, and luscious legs that usually stay separated!", + network: 'mofos', + }, + { + slug: 'ebonysextapes', + name: 'Ebony Sex Tapes', + url: 'https://www.mofos.com/scenes?site=202', + description: 'Once you go black, you never go back! Did you think that was only how white women feel about black men? Well if you did, that can only mean you never had a stacked, curvy, big ass, beautiful black teen riding your hard white cock. Watch these lucky guys fuck their Ebony Girlfriends at EbonySexTapes.com.', + network: 'mofos', + }, + { + slug: 'sharemybf', + name: 'Share My BF', + alias: ['smb'], + url: 'https://www.mofos.com/scenes?site=201', + description: 'Would your cock be able to handle 2 wet pussies at the same time? One hot teen riding your face while the other deepthroats you. You know your GF tells all her friends how big your dick is. Imagine if you can fuck her and her friend at the same time? Live the fantasy at ShareMyBF.com.', + network: 'mofos', + }, + { + slug: 'dontbreakme', + name: "Don't Break Me", + alias: ['dbm'], + url: 'https://www.mofos.com/scenes?site=198', + description: 'DontBreakMe.com is about tiny spinners fucking big guys with massive dicks! Most of these chicks are shorter than 5 feet tall and weigh less than 100lbs. Meanwhile, these girls are paired up with guys who tower over them by at least 1.5 feet and have 9" dicks!! The look on their faces when they see that huge dick pop out of his pants is priceless. While it turns them on they usually get a bit nervous: "how will I squeeze that huge cock inside?" Ouch! Check it out.', + network: 'mofos', + }, + { + slug: 'iknowthatgirl', + name: 'I Know That Girl', + alias: ['iktg'], + url: 'https://www.mofos.com/scenes?site=183', + description: 'Every single gorgeous girl you see on this site is 100% Real! They are all part of the biggest user submitted, amateur video site in the world...IKnowThatGirl.com! Hot young girlfriends getting kinky on camera, sucking and fucking, even stuffing dildos up their tight pussies, all filmed on home video and leaked to us by some lowlife, soon to be ex-boyfriend or former best friend! Oh well... Enjoy!', + network: 'mofos', + }, + { + slug: 'letstryanal', + name: 'Lets Try Anal', + alias: ['lta'], + url: 'https://www.mofos.com/scenes?site=189', + description: "This isn't just another anal site! Letstryanal.com features the hottest real footage of amateur girls and their first time ass fucking experiences. Watch it all... innocent girlfriends being convinced to try anal, their faces of pain and screaming as they beg their boyfriend to \"please go slower\" while a large cock penetrates their tight asses for the first time! Let's face it, there is nothing like seeing a cock disappear in a virgin asshole. It's so hot!", + network: 'mofos', + }, + { + slug: 'latinasextapes', + name: 'Latina Sex Tapes', + alias: ['lst'], + url: 'https://www.mofos.com/scenes?site=188', + description: "100% Real Latina Girls getting fucked by their boyfriends, filmed and submitted to us for Big $$$! Watch amazing real footage and private videos of these beautiful amateur girls, their perfectly tanned bodies, mouth-watering curves, luscious round asses, and mind blowing accents! We've only kept the best, most outstanding sex videos and uploaded them for you to watch. You'll be amazed with what we received, and more is on the way!", + network: 'mofos', + }, + { + slug: 'publicpickups', + name: 'Public Pickups', + alias: ['ppu'], + url: 'https://www.mofos.com/scenes?site=190', + description: "Check out the hottest REAL footage of young girls getting picked up and fucked in public! The girls are usually shy around guys approaching them with a video camera, but that's the fun part. Besides their shyness slowly disappears after they're offered money to get dirty. While it's a real turn on seeing the girls flash and get fondled in public... the hottest part is watching them get fucked everywhere...in cars, parks, clubs, even the library!", + network: 'mofos', + }, + { + slug: 'pervsonpatrol', + name: 'Pervs On Patrol', + alias: ['pop'], + url: 'https://www.mofos.com/scenes?site=185', + description: "A while back, this beautiful girl who lived next door use to always undress with her window opened. This girl had no fucking clue that I was jerking off over her from across the yard. One day I decided to grab my dad's camera and start filming her. It was amazing... until she finally caught me. Fuck, this girl was PISSED!..., but could you fucking believe that once she calmed down she was actually a little turned on by the whole situation,... and what happened next changed my life!", + network: 'mofos', + }, + { + slug: 'strandedteens', + name: 'Stranded Teens', + alias: ['sts'], + url: 'https://www.mofos.com/scenes?site=192', + description: "Watch videos on StrandedTeens.com and you will never look at a hitchhiker the same way again! Some of these girls will do anything for a ride or simply to make a friend - even the shy ones. From giving road head to getting ass-fucked on the hood of the car, you can watch it all. Check it out now, you won't be disappointed!", + network: 'mofos', + }, + { + slug: 'realslutparty', + name: 'Real Slut Party', + url: 'https://www.mofos.com/scenes?site=184', + description: "Wanna see the most mind blowing college sex parties from across the country? It's the real deal, all caught on video and submitted by you! Insane college craziness, pussy packed house parties, holiday orgies, backyard BBQ's gone wrong and hundreds of tight, young girls getting crazy, stripped down, and on the prowl for all the cock they can find!", + network: 'mofos', + }, + { + slug: 'mofoslab', + name: 'MOFOS Lab', + url: 'https://www.mofos.com/scenes?site=205', + description: "We've received your feedback and are experimenting with turning your wildest fantasies into the ultimate POV experience; this is Mofos Lab! Featuring today's hottest and freshest talent, immerse yourself in an exciting Mofos venture that brings you the edgiest new content!", + network: 'mofos', + }, + { + slug: 'mofosbsides', + name: 'Mofos B Sides', + url: 'https://www.mofos.com/scenes?site=191', + description: "Mofos B-Sides is a doorway to new, unseen amateur video! Hundreds of clips have been submitted to Mofos through the years and we've never shown them to you until now. We'll give you a little bit at a time, from random girls in random scenario\\’s, and maybe even an occasional free video from our friends at Brazzers, Twisty's and Babes! Check it all out and let us know what you think.", + network: 'mofos', + }, + { + slug: 'shesafreak', + name: "She's A Freak", + alias: ['saf'], + url: 'https://www.mofos.com/scenes?site=187', + description: "Fresh, young amateur girls with beautiful tight bodies, pushing themselves to the limit! It's just another great way that today's hottest new models are choosing to showcase their stunning bodies and show all of us that they're ready for more! Soaking wet masturbation, fisting, squirting, double penetration and anal toys are just some of the things they do to show us how freaky they can be and how ready they are to graduate from toys to thick, fat cock!", + network: 'mofos', + }, + // NAUGHTY AMERICA + { + slug: 'myfriendshotmom', + name: 'My Friend\'s Hot Mom', + alias: ['mfhm'], + url: 'https://www.naughtyamerica.com/site/my-friend-s-hot-mom', + network: 'naughtyamerica', + }, + { + slug: 'slutstepmom', + name: 'Slut Step Mom', + url: 'https://www.naughtyamerica.com/site/slut-step-mom', + network: 'naughtyamerica', + }, + { + slug: 'openfamily', + name: 'Open Family', + url: 'https://www.naughtyamerica.com/site/open-family', + network: 'naughtyamerica', + }, + { + slug: 'sleazystepdad', + name: 'Sleazy Stepdad', + url: 'https://www.naughtyamerica.com/site/sleazy-stepdad', + network: 'naughtyamerica', + }, + { + slug: 'watchyourmom', + name: 'Watch Your Mom', + url: 'https://www.naughtyamerica.com/site/watch-your-mom', + network: 'naughtyamerica', + }, + { + slug: 'bigcockbully', + name: 'Big Cock Bully', + alias: ['bcb'], + url: 'https://www.naughtyamerica.com/site/big-cock-bully', + network: 'naughtyamerica', + }, + { + slug: 'bigcockhero', + name: 'Big Cock Hero', + alias: ['bch'], + url: 'https://www.naughtyamerica.com/site/big-cock-hero', + network: 'naughtyamerica', + }, + { + slug: 'mysistershotfriend', + name: "My Sister's Hot Friend", + alias: ['mshf'], + url: 'https://www.naughtyamerica.com/site/my-sister-s-hot-friend', + network: 'naughtyamerica', + }, + { + slug: 'myfirstsexteacher', + name: 'My First Sex Teacher', + alias: ['mfst'], + url: 'https://www.naughtyamerica.com/site/my-first-sex-teacher', + network: 'naughtyamerica', + }, + { + slug: 'slutstepsister', + name: 'Slut Step Sister', + url: 'https://www.naughtyamerica.com/site/slut-step-sister', + network: 'naughtyamerica', + }, + { + slug: 'teenslovecream', + name: 'Teens Love Cream', + url: 'https://www.naughtyamerica.com/site/teens-love-cream', + network: 'naughtyamerica', + }, + { + slug: 'latinastepmom', + name: 'Latina Step Mom', + url: 'https://www.naughtyamerica.com/site/latina-step-mom', + network: 'naughtyamerica', + }, + { + slug: 'seducedbyacougar', + name: 'Seduced By A Cougar', + url: 'https://www.naughtyamerica.com/site/seduced-by-a-cougar', + network: 'naughtyamerica', + }, + { + slug: 'showmybf', + name: 'Show My BF', + url: 'https://www.naughtyamerica.com/site/show-my-bf', + network: 'naughtyamerica', + }, + { + slug: 'mydaughtershotfriend', + name: "My Daughter's Hot Friend", + alias: ['mdhf'], + url: 'https://www.naughtyamerica.com/site/my-daughter-s-hot-friend', + network: 'naughtyamerica', + }, + { + slug: 'lasluts', + name: 'LA Sluts', + url: 'https://www.naughtyamerica.com/site/la-sluts', + network: 'naughtyamerica', + }, + { + slug: 'mywifeismypornstar', + name: 'My Wife Is My Pornstar', + url: 'https://www.naughtyamerica.com/site/my-wife-is-my-pornstar', + network: 'naughtyamerica', + }, + { + slug: 'watchyourwife', + name: 'Watch Your Wife', + url: 'https://www.naughtyamerica.com/site/watch-your-wife', + network: 'naughtyamerica', + }, + { + slug: 'tonightsgirlfriendclassic', + alias: ['togc'], + name: "Tonight's Girlfriend", + url: 'https://www.naughtyamerica.com/site/tonight-s-girlfriend-classic', + network: 'naughtyamerica', + }, + { + slug: 'wivesonvacation', + name: 'Wives on Vacation', + alias: ['wov'], + url: 'https://www.naughtyamerica.com/site/wives-on-vacation', + network: 'naughtyamerica', + }, + { + slug: 'naughtyweddings', + name: 'Naughty Weddings', + alias: ['nw'], + url: 'https://www.naughtyamerica.com/site/naughty-weddings', + network: 'naughtyamerica', + }, + { + slug: 'dirtywivesclub', + name: 'Dirty Wives Club', + alias: ['dwc'], + url: 'https://www.naughtyamerica.com/site/dirty-wives-club', + network: 'naughtyamerica', + }, + { + slug: 'mydadshotgirlfriend', + name: "My Dad's Hot Girlfriend", + alias: ['mdhg'], + url: 'https://www.naughtyamerica.com/site/my-dad-s-hot-girlfriend', + network: 'naughtyamerica', + }, + { + slug: 'mygirllovesanal', + name: 'My Girl Loves Anal', + url: 'https://www.naughtyamerica.com/site/my-girl-loves-anal', + network: 'naughtyamerica', + }, + { + slug: 'analcollege', + name: 'Anal College', + url: 'https://www.naughtyamerica.com/site/anal-college', + network: 'naughtyamerica', + }, + { + slug: 'lesbiangirlongirl', + name: 'Lesbian Girl on Girl', + url: 'https://www.naughtyamerica.com/site/lesbian-girl-on-girl', + network: 'naughtyamerica', + }, + { + slug: 'naughtyoffice', + name: 'Naughty Office', + alias: ['no'], + url: 'https://www.naughtyamerica.com/site/naughty-office', + network: 'naughtyamerica', + }, + { + slug: 'ihaveawife', + name: 'I Have a Wife', + alias: ['ihaw'], + url: 'https://www.naughtyamerica.com/site/i-have-a-wife', + network: 'naughtyamerica', + }, + { + slug: 'naughtybookworms', + name: 'Naughty Bookworms', + alias: ['nb'], + url: 'https://www.naughtyamerica.com/site/naughty-bookworms', + network: 'naughtyamerica', + }, + { + slug: 'housewife1on1', + name: 'Housewife 1 on 1', + alias: ['h1o1'], + url: 'https://www.naughtyamerica.com/site/housewife-1-on-1', + network: 'naughtyamerica', + }, + { + slug: 'mywifeshotfriend', + name: "My Wife's Hot Friend", + alias: ['mwhf'], + url: 'https://www.naughtyamerica.com/site/my-wife-s-hot-friend', + network: 'naughtyamerica', + }, + { + slug: 'latinadultery', + name: 'Latin Adultery', + url: 'https://www.naughtyamerica.com/site/latin-adultery', + network: 'naughtyamerica', + }, + { + slug: 'assmasterpiece', + name: 'Ass Masterpiece', + alias: ['am'], + url: 'https://www.naughtyamerica.com/site/ass-masterpiece', + network: 'naughtyamerica', + }, + { + slug: '2chickssametime', + name: '2 Chicks Same Time', + alias: ['2cst'], + url: 'https://www.naughtyamerica.com/site/2-chicks-same-time', + network: 'naughtyamerica', + }, + { + slug: 'myfriendshotgirl', + name: "My Friend's Hot Girl", + alias: ['mfhg'], + url: 'https://www.naughtyamerica.com/site/my-friend-s-hot-girl', + network: 'naughtyamerica', + }, + { + slug: 'neighboraffair', + name: 'Neighbor Affair', + alias: ['naf'], + url: 'https://www.naughtyamerica.com/site/neighbor-affair', + network: 'naughtyamerica', + }, + { + slug: 'mygirlfriendsbustyfriend', + name: "My Girlfriend's Busty Friend", + alias: ['mgbf'], + url: 'https://www.naughtyamerica.com/site/my-girlfriend-s-busty-friend', + network: 'naughtyamerica', + }, + { + slug: 'naughtyathletics', + name: 'Naughty Athletics', + alias: ['na'], + url: 'https://www.naughtyamerica.com/site/naughty-athletics', + network: 'naughtyamerica', + }, + { + slug: 'mynaughtymassage', + name: 'My Naughty Massage', + alias: ['mnm'], + url: 'https://www.naughtyamerica.com/site/my-naughty-massage', + network: 'naughtyamerica', + }, + { + slug: 'fasttimes', + name: 'Fast Times', + url: 'https://www.naughtyamerica.com/site/fast-times', + network: 'naughtyamerica', + }, + { + slug: 'thepassenger', + name: 'The Passenger', + url: 'https://www.naughtyamerica.com/site/the-passenger', + network: 'naughtyamerica', + }, + { + slug: 'milfsugarbabesclassic', + name: 'Milf Sugar Babes Classic', + url: 'https://www.naughtyamerica.com/site/milf-sugar-babes-classic', + network: 'naughtyamerica', + }, + { + slug: 'perfectfuckingstrangersclassic', + name: 'Perfect Fucking Strangers Classic', + url: 'https://www.naughtyamerica.com/site/perfect-fucking-strangers-classic', + network: 'naughtyamerica', + }, + { + slug: 'asian1on1', + name: 'Asian 1 On 1', + url: 'https://www.naughtyamerica.com/site/asian-1-on-1', + network: 'naughtyamerica', + }, + { + slug: 'americandaydreams', + name: 'American Daydreams', + alias: ['ad'], + url: 'https://www.naughtyamerica.com/site/american-daydreams', + network: 'naughtyamerica', + }, + { + slug: 'socalcoeds', + name: 'Socal Coeds', + url: 'https://www.naughtyamerica.com/site/socal-coeds', + network: 'naughtyamerica', + }, + { + slug: 'naughtycountrygirls', + name: 'Naughty Country Girls', + url: 'https://www.naughtyamerica.com/site/naughty-country-girls', + network: 'naughtyamerica', + }, + { + slug: 'diaryofamilf', + name: 'Diary of a Milf', + url: 'https://www.naughtyamerica.com/site/diary-of-a-milf', + network: 'naughtyamerica', + }, + { + slug: 'naughtyrichgirls', + name: 'Naughty Rich Girls', + alias: ['nrg'], + url: 'https://www.naughtyamerica.com/site/naughty-rich-girls', + network: 'naughtyamerica', + }, + { + slug: 'mynaughtylatinmaid', + name: 'My Naughty Latin Maid', + url: 'https://www.naughtyamerica.com/site/my-naughty-latin-maid', + network: 'naughtyamerica', + }, + { + slug: 'naughtyamerica', + name: 'Naughty America', + alias: ['nam'], + url: 'https://www.naughtyamerica.com/site/naughty-america', + network: 'naughtyamerica', + }, + { + slug: 'diaryofananny', + name: 'Diary of a Nanny', + url: 'https://www.naughtyamerica.com/site/diary-of-a-nanny', + network: 'naughtyamerica', + }, + { + slug: 'naughtyflipside', + name: 'Naughty Flipside', + url: 'https://www.naughtyamerica.com/site/naughty-flipside', + network: 'naughtyamerica', + }, + { + slug: 'livepartygirl', + name: 'Live Party Girl', + url: 'https://www.naughtyamerica.com/site/live-party-girl', + network: 'naughtyamerica', + }, + { + slug: 'livenaughtystudent', + name: 'Live Naughty Student', + url: 'https://www.naughtyamerica.com/site/live-naughty-student', + network: 'naughtyamerica', + }, + { + slug: 'livenaughtysecretary', + name: 'Live Naughty Secretary', + url: 'https://www.naughtyamerica.com/site/live-naughty-secretary', + network: 'naughtyamerica', + }, + { + slug: 'livegymcam', + name: 'Live Gym Cam', + url: 'https://www.naughtyamerica.com/site/live-gym-cam', + network: 'naughtyamerica', + }, + { + slug: 'livenaughtyteacher', + name: 'Live Naughty Teacher', + url: 'https://www.naughtyamerica.com/site/live-naughty-teacher', + network: 'naughtyamerica', + }, + { + slug: 'livenaughtymilf', + name: 'Live Naughty Milf', + url: 'https://www.naughtyamerica.com/site/live-naughty-milf', + network: 'naughtyamerica', + }, + { + slug: 'livenaughtynurse', + name: 'Live Naughty Nurse', + url: 'https://www.naughtyamerica.com/site/live-naughty-nurse', + network: 'naughtyamerica', + }, + // NEW SENSATIONS + { + slug: 'hotwifexxx', + name: 'Hotwife XXX', + url: 'https://www.hotwifexxx.com', + network: 'newsensations', + parameters: { + siteId: 'hwxxx', + block: true, + }, + }, + { + slug: 'tabutales', + name: 'Tabu Tales', + url: 'https://www.thetabutales.com', + network: 'newsensations', + parameters: { siteId: 'tt' }, + }, + { + slug: 'nsfamilyxxx', + name: 'Family XXX', + url: 'https://www.familyxxx.com', + network: 'newsensations', + tags: ['family'], + parameters: { + siteId: 'famxxx', + block: true, + }, + }, + { + slug: 'thelesbianexperience', + name: 'The Lesbian Experience', + url: 'https://www.thelesbianexperience.com', + network: 'newsensations', + tags: ['lesbian'], + parameters: { siteId: 'tle' }, + }, + { + slug: 'theromanceseries', + name: 'The Romance Series', + url: 'https://www.theromanceseries.com', + network: 'newsensations', + parameters: { siteId: 'rs' }, + }, + { + slug: 'talesfromtheedge', + name: 'Tales From The Edge', + url: 'thetalesfromtheedge', + network: 'newsensations', + parameters: { siteId: 'ttfte' }, + }, + { + slug: 'parodypass', + name: 'Parody Pass', + url: 'https://www.parodypass.com', + network: 'newsensations', + parameters: { siteId: 'pp' }, + }, + { + slug: 'shanedieselsbangingbabes', + name: 'Shane Diesel\'s Banging Babes', + url: 'http://shanedieselsbangingbabes.com', + network: 'newsensations', + parameters: { siteId: 'sdbb' }, + }, + { + slug: 'unlimitedmilfs', + name: 'Unlimited MILFs', + url: 'https://www.unlimitedmilfs.com', + network: 'newsensations', + tags: ['milf'], + parameters: { siteId: 'um' }, + }, + { + slug: 'heavyhandfuls', + name: 'Heavy Handfuls', + url: 'https://www.heavyhandfuls.com', + network: 'newsensations', + parameters: { siteId: 'hh' }, + }, + { + slug: 'jizzbomb', + name: 'Jizz Bomb', + url: 'https://www.jizzbomb.com', + network: 'newsensations', + parameters: { siteId: 'jb' }, + }, + { + slug: 'stretchedoutsnatch', + name: 'Stretched Out Snatch', + url: 'https://www.stretchedoutsnatch.com', + network: 'newsensations', + parameters: { siteId: 'sos' }, + }, + { + slug: 'fourfingerclub', + name: 'Four Finger Club', + url: 'https://www.fourfingerclub.com', + network: 'newsensations', + parameters: { siteId: 'ffc' }, + }, + { + slug: 'ashlynnbrooke', + name: 'Ashlynn Brooke', + url: 'https://www.ashlynnbrooke.com', + network: 'newsensations', + parameters: { siteId: 'ab' }, + }, + { + slug: 'freshouttahighschool', + name: 'Fresh Outta High School', + url: 'https://www.freshouttahighschool.com', + network: 'newsensations', + parameters: { siteId: 'fohs' }, + }, + // NUBILES + { + slug: 'anilos', + name: 'Anilos', + url: 'https://www.anilos.com', + network: 'nubiles', + parameters: { + upcoming: true, + }, + }, + { + slug: 'brattysis', + name: 'Bratty Sis', + url: 'https://www.brattysis.com', + tags: ['family'], + network: 'nubiles', + parameters: { + upcoming: true, + }, + }, + { + slug: 'deeplush', + name: 'Deep Lush', + url: 'https://www.deeplush.com', + network: 'nubiles', + }, + { + slug: 'hotcrazymess', + name: 'Hot Crazy Mess', + alias: ['hcm'], + url: 'https://www.hotcrazymess.com', + network: 'nubiles', + }, + { + slug: 'nfbusty', + name: 'NF Busty', + url: 'https://www.nfbusty.com', + tags: ['big-boobs'], + network: 'nubiles', + parameters: { + upcoming: true, + }, + }, + { + slug: 'nubilefilms', + name: 'Nubile Films', + alias: ['nf', 'nubilef'], + url: 'https://www.nubilefilms.com', + network: 'nubiles', + parameters: { + upcoming: true, + }, + }, + { + slug: 'nubiles', + name: 'Nubiles', + url: 'https://www.nubiles.net', + network: 'nubiles', + parameters: { + upcoming: true, + }, + }, + { + slug: 'nubilescasting', + name: 'Nubiles Casting', + url: 'https://www.nubiles-casting.com', + tags: ['casting'], + network: 'nubiles', + }, + { + slug: 'momsteachsex', + name: 'Moms Teach Sex', + alias: ['mts'], + url: 'https://www.momsteachsex.com', + tags: ['family', 'milf'], + network: 'nubiles', + parameters: { + upcoming: true, + }, + }, + { + slug: 'petitehdporn', + name: 'Petite HD Porn', + alias: ['phdp'], + url: 'https://www.petitehdporn.com', + network: 'nubiles', + parameters: { + upcoming: true, + }, + }, + { + slug: 'driverxxx', + name: 'Driver XXX', + url: 'https://www.driverxxx.com', + network: 'nubiles', + }, + { + slug: 'petiteballerinasfucked', + name: 'Petite Ballerinas Fucked', + alias: ['pbf'], + url: 'https://www.petiteballerinasfucked.com', + network: 'nubiles', + }, + { + slug: 'teacherfucksteens', + name: 'Teacher Fucks Teens', + alias: ['tft'], + url: 'https://www.teacherfucksteens.com', + tags: ['teacher'], + network: 'nubiles', + }, + { + slug: 'stepsiblingscaught', + name: 'Step Siblings Caught', + alias: ['ssc'], + url: 'https://www.stepsiblingscaught.com', + tags: ['family'], + network: 'nubiles', + parameters: { + upcoming: true, + }, + }, + { + slug: 'princesscum', + name: 'Princess Cum', + alias: ['pc'], + url: 'https://www.princesscum.com', + network: 'nubiles', + }, + { + slug: 'badteenspunished', + name: 'Bad Teens Punished', + alias: ['btp'], + url: 'https://www.badteenspunished.com', + network: 'nubiles', + }, + { + slug: 'nubilesunscripted', + name: 'Nubiles Unscripted', + url: 'https://www.nubilesunscripted.com', + network: 'nubiles', + }, + { + slug: 'bountyhunterporn', + name: 'Bounty Hunter Porn', + url: 'https://www.bountyhunterporn.com', + network: 'nubiles', + }, + { + slug: 'daddyslilangel', + name: 'Daddy\'s Lil Angel', + alias: ['dlla'], + url: 'https://www.daddyslilangel.com', + tags: ['family', 'anal'], + network: 'nubiles', + }, + { + slug: 'myfamilypies', + name: 'My Family Pies', + alias: ['mfp'], + url: 'https://www.myfamilypies.com', + tags: ['family'], + network: 'nubiles', + parameters: { + upcoming: true, + }, + }, + { + slug: 'nubileset', + name: 'Nubiles Entertainment', + url: 'https://www.nubileset.com', + network: 'nubiles', + }, + { + slug: 'detentiongirls', + name: 'Detention Girls', + url: 'https://www.detentiongirls.com', + network: 'nubiles', + }, + { + slug: 'thatsitcomshow', + name: 'That Sitcom Show', + alias: ['tss'], + url: 'https://www.thatsitcomshow.com', + tags: ['parody'], + network: 'nubiles', + }, + // PERFECT GONZO + { + slug: 'allinternal', + name: 'All Internal', + url: 'https://allinternal.com', + network: 'perfectgonzo', + }, + { + slug: 'asstraffic', + name: 'Ass Traffic', + url: 'https://asstraffic.com', + network: 'perfectgonzo', + }, + { + slug: 'cumforcover', + name: 'Cum For Cover', + url: 'https://cumforcover.com', + network: 'perfectgonzo', + }, + { + slug: 'fistflush', + name: 'Fist Flush', + url: 'https://fistflush.com', + network: 'perfectgonzo', + }, + { + slug: 'givemepink', + name: 'Give Me Pink', + url: 'https://givemepink.com', + tags: ['solo', 'masturbation'], + network: 'perfectgonzo', + }, + { + slug: 'milfthing', + name: 'MILF Thing', + url: 'https://milfthing.com', + network: 'perfectgonzo', + }, + { + slug: 'primecups', + name: 'Prime Cups', + url: 'https://primecups.com', + network: 'perfectgonzo', + }, + { + slug: 'purepov', + name: 'Pure POV', + url: 'https://purepov.com', + network: 'perfectgonzo', + }, + { + slug: 'spermswap', + name: 'Sperm Swap', + url: 'https://spermswap.com', + tags: ['cum-swapping'], + network: 'perfectgonzo', + }, + { + slug: 'tamedteens', + name: 'Tamed Teens', + url: 'https://tamedteens.com', + network: 'perfectgonzo', + }, + // PERVCITY + { + slug: 'analoverdose', + name: 'Anal Overdose', + url: 'http://www.analoverdose.com', + description: 'Before proceeding, use caution: the stunning pornstars of Anal Overdose are so fiery that they cause heavy breathing, throbbing cocks and volcanic loads of cum. If you think you can handle the heat of smoking tits, sweltering pussy and red hot ass.', + network: 'pervcity', + parameters: { tourId: 3 }, + }, + { + slug: 'bangingbeauties', + name: 'Banging Beauties', + description: "Banging Beauties isn't just a porn site; it's the gateway to all your pussy-obsessed fantasies! Our members' area is flowing with beautiful pornstars anticipating big dick throbbing in their syrupy pink slits. These experienced babes love brutal vaginal pounding! Similarly, they're eager for anal switch-hitting to shake things up. However, it's not only about gorgeous sexperts filling their hungry holes. Sometimes, it's all about innocent rookies earning their pornstar status in first time threesomes and premier interracial scenes.", + url: 'http://www.bangingbeauties.com', + network: 'pervcity', + parameters: { tourId: 7 }, + }, + { + slug: 'oraloverdose', + name: 'Oral Overdose', + description: "Oral Overdose is the only site you need to live out every saliva soaked blowjob of your dreams in HD POV! We've got the most stunning cocksuckers in the world going to town on big dick. These babes not only love cock, they can't get enough of it! In fact, there is no prick too huge for our hungry girls' throats. You'll find gorgeous, big tits pornstars exercising their gag reflex in intense balls deep facefuck scenes. We also feature fresh, young newbies taking on the gagging deepthroat challenge.", + url: 'http://www.oraloverdose.com', + network: 'pervcity', + parameters: { tourId: 4 }, + }, + { + slug: 'chocolatebjs', + name: 'Chocolate BJs', + description: "You've just won the golden ticket to the best Chocolate BJs on the planet! We've sought far and wide to bring you the most beautiful black and ethnic pornstars. And they're in our members' area now! They can't wait to suck your white lollipop and lick the thick cream shooting from your big dick. Of course, no matter how sweet the booty or juicy the big tits, these brown foxes aren't all sugar and spice. In fact, when it comes to giving head, these big ass ebony babes know what they want: huge white cocks filling their throats!", + url: 'http://www.chocolatebjs.com', + network: 'pervcity', + parameters: { tourId: 6 }, + }, + { + slug: 'upherasshole', + name: 'Up Her Asshole', + description: "You don't need to travel the globe in search of the anal wonders of the world, because you get your own private tour right here on Up Her Asshole! Our stunning pornstars and rookie starlets welcome all ass fetish and anal sex fans, with their twerking bubble butts and winking assholes. However, big booty worship is just a slice of the fun. Combined with juicy tits (big and small, wet pussy (hairy and bald, these girls deliver a spectacular sensory experience in HD POV. Not only are you in danger of busting a nut before the going gets good, but also when the good turns remarkable with rimming, fingering and butt toys!", + url: 'http://www.upherasshole.com', + network: 'pervcity', + parameters: { tourId: 9 }, + }, + // PIMP XXX + { + slug: 'drilledxxx', + name: 'Drilled.XXX', + url: 'https://drilled.xxx', + tags: ['anal'], + network: 'pimpxxx', + }, + { + slug: 'cuckedxxx', + name: 'Cucked.XXX', + url: 'https://cucked.xxx', + tags: ['cuckold'], + network: 'pimpxxx', + }, + { + slug: 'familyxxx', + name: 'Family.XXX', + url: 'https://family.xxx', + tags: ['family'], + network: 'pimpxxx', + }, + { + slug: 'petitexxx', + name: 'Petite.XXX', + url: 'https://petite.xxx', + network: 'pimpxxx', + }, + { + slug: 'confessionsxxx', + name: 'Confessions.XXX', + url: 'https://confessions.xxx', + network: 'pimpxxx', + }, + { + slug: 'bcmxxx', + name: 'BCM.XXX', + url: 'https://bcm.xxx', + network: 'pimpxxx', + }, + // PORN PROS + { + name: 'Real Ex Girlfriends', + slug: 'realexgirlfriends', + alias: ['reg'], + url: 'https://pornpros.com/site/realexgirlfriends', + network: 'pornpros', + }, + { + name: '18 Years Old', + slug: 'eighteenyearsold', + alias: ['18yo'], + url: 'https://pornpros.com/site/18yearsold', + tags: ['teen'], + network: 'pornpros', + }, + { + name: 'Massage Creep', + slug: 'massagecreep', + alias: ['mc'], + url: 'https://pornpros.com/site/massagecreep', + tags: ['massage'], + network: 'pornpros', + }, + { + name: 'Deep Throat Love', + slug: 'deepthroatlove', + url: 'https://pornpros.com/site/deepthroatlove', + tags: ['blowjob', 'deepthroat'], + network: 'pornpros', + }, + { + name: 'Teen BFF', + slug: 'teenbff', + url: 'https://pornpros.com/site/teenbff', + tags: ['mff'], + network: 'pornpros', + }, + { + name: 'Shady P.I.', + slug: 'shadypi', + url: 'https://pornpros.com/site/shadypi', + network: 'pornpros', + }, + { + name: 'Cruelty Party', + slug: 'crueltyparty', + url: 'https://pornpros.com/site/crueltyparty', + network: 'pornpros', + }, + { + name: 'Disgraced 18', + slug: 'disgraced18', + url: 'https://pornpros.com/site/disgraced18', + network: 'pornpros', + }, + { + name: 'Cumshot Surprise', + slug: 'cumshotsurprise', + url: 'https://pornpros.com/site/cumshotsurprise', + network: 'pornpros', + }, + { + name: '40oz Bounce', + slug: 'fortyozbounce', + url: 'https://pornpros.com/site/40ozbounce', + network: 'pornpros', + }, + { + name: 'Jurassic Cock', + slug: 'jurassiccock', + url: 'https://pornpros.com/site/jurassiccock', + network: 'pornpros', + }, + { + name: 'Freaks Of Cock', + slug: 'freaksofcock', + url: 'https://pornpros.com/site/freaksofcock', + network: 'pornpros', + }, + { + name: 'Euro Humpers', + slug: 'eurohumpers', + url: 'https://pornpros.com/site/eurohumpers', + network: 'pornpros', + }, + { + name: 'Freaks Of Boobs', + slug: 'freaksofboobs', + url: 'https://pornpros.com/site/freaksofboobs', + network: 'pornpros', + }, + { + name: 'Cock Competition', + slug: 'cockcompetition', + url: 'https://pornpros.com/site/cockcompetition', + network: 'pornpros', + }, + { + name: 'Pimp Parade', + slug: 'pimpparade', + url: 'https://pornpros.com/site/pimpparade', + network: 'pornpros', + }, + { + name: 'MILF Humiliation', + slug: 'milfhumiliation', + url: 'https://milfhumiliation.com', + network: 'pornpros', + tags: ['milf'], + }, + { + name: 'Humiliated', + slug: 'humiliated', + url: 'https://humiliated.com', + network: 'pornpros', + }, + { + name: 'Flexible Positions', + slug: 'flexiblepositions', + url: 'https://flexiblepositions.com', + network: 'pornpros', + parameters: { + network: true, + }, + }, + { + name: 'Public Violations', + slug: 'publicviolations', + url: 'https://publicviolations.com', + network: 'pornpros', + parameters: { + network: true, + }, + }, + { + name: 'Amateur Violations', + slug: 'amateurviolations', + url: 'https://amateurviolations.com', + network: 'pornpros', + }, + { + name: 'Squirt Disgrace', + slug: 'squirtdisgrace', + url: 'https://squirtdisgrace.com', + network: 'pornpros', + }, + { + name: 'Cum Disgrace', + slug: 'cumdisgrace', + url: 'https://cumdisgrace.com', + network: 'pornpros', + }, + { + name: 'Webcam Hackers', + slug: 'webcamhackers', + url: 'https://webcamhackers.com', + network: 'pornpros', + }, + { + name: 'College Teens', + slug: 'collegeteens', + network: 'pornpros', + }, + // PRIVATE + { + slug: 'analintroductions', + name: 'Anal Introductions', + description: 'Private\'s Anal Introductions is all about ass. Watch these girls get their asses broken in by fat hard cocks! Hot double penetrations, gaping wide assholes and anal creampies are all standard in this exclusive site. Many of these girls have never had cock in their ass before, while others are real addicts and can only cum when being savagely sodomised. Watch which girls can take it... Private style.', + url: 'https://www.private.com/site/anal-introductions', + network: 'private', + }, + { + slug: 'iconfessfiles', + name: 'I Confess Files', + description: 'From the heart of the UK comes found footage exclusively provided to private.com which will shock and offend some viewers. Reality, perversion and unnatural lust come together perhaps as never before.', + url: 'https://www.private.com/site/i-confess-files', + network: 'private', + }, + { + slug: 'missionasspossible', + name: 'Mission: Ass Possible', + description: 'From the streets of Europe, Private\'s team of professionals find and exploit clueless young sluts, for some great sex and for your viewing pleasure. See what young hot chicks will do when their desire for easy fame or money makes them vulnerable to some of the craziest schemes and plots imaginable. Private\'s hung studs are on a mission for ass and hungry for fun. All part of the Private network of sites.', + url: 'https://www.private.com/site/mission-ass-possible', + network: 'private', + }, + { + slug: 'russianfakeagent', + name: 'Russian Fake Agent', + description: 'Direct from Russia, young naïve women pursue their dream of visas, Hollywood and fame. Eager to please and willing to do anything to get their break. Unfortunately, it’s a case of lies, sex and videotape as these gullible hotties perform for us. If these girls only knew the truth!', + url: 'https://www.private.com/site/russian-fake-agent', + network: 'private', + }, + { + slug: 'sexonthebeach', + name: 'Sex on the Beach', + description: 'Amazing locations and steamy sex in the sun, www.privatetropics.com is a celebration of tropical lust and sand covered sluts. From Private\'s exclusive line of scenes in exotic countries, watch what happens when our hot models go naked and native.', + url: 'https://www.private.com/site/sex-on-the-beach', + network: 'private', + }, + { + slug: 'tightandteen', + name: 'Tight and Teen', + description: 'Europe\'s number one teen offering and part of the Private network of sites, Tight and Teen takes you to the place that every father dreads, as 18+ teens discover just how much fun pleasing a hung stud can be. Fresh tight pussies, virgin anal initiations and teen face fuckings all brought to you exclusively by Europe\'s leading adult brand.', + url: 'https://www.private.com/site/tight-and-teen', + network: 'private', + }, + { + slug: 'blacksonsluts', + name: 'Blacks on Sluts', + description: 'See what happens when European women discover hung black studs looking to breed. Blacks on Sluts is 100% white slut and cheating wives versus huge black dicks giving them something they will never forget. Private puts its stamp on these women as our stallions stretch them to their limits.', + url: 'https://www.private.com/site/blacks-on-sluts', + network: 'private', + }, + { + slug: 'privateblack', + name: 'Private Black', + description: 'Private Black is number 1 for European Interracial Porn with exclusive interracial content in HD and Ultra 4K featuring the freshest young faces from Europe and the most popular European porn stars.', + url: 'https://www.privateblack.com', + network: 'private', + }, + { + slug: 'privatefetish', + name: 'Private Fetish', + description: 'www.privatefetish.com is here to give you that taste of dark desire that you secretly crave. Domination and Submission, Pleasure and Pain, are the drivers in this hardcore dungeon. What turns you on most? Being forced to beg for release from a sexy dominatrix or making that bitch next door beg for your cock? All part of Private\'s network of sites.', + url: 'https://www.private.com/site/private-fetish', + network: 'private', + }, + { + slug: 'privatemilfs', + name: 'Private MILFs', + description: 'Part of the awesome network of Private sites, Private MILFs is all about moms who are getting what they need when their limp dicked husbands can\'t perform. From their daughters\' stud boyfriends to their husbands\' hung black co-workers to their sons\' friends, no one is safe from their cravings for hard cock and salty cum.', + url: 'https://www.private.com/site/private-milfs', + network: 'private', + }, + { + slug: 'russianteenass', + name: 'Russian Teen Ass', + description: 'Many people say that Russian girls are the most beautiful in the world, and at www.russianteenass.com we show you why. These sexy Soviet newcomers are ready to work hard for their visa and their big shot at stardom. These barely 18+ girls know what they want and Private gives them an exclusive opportunity to get it. From Russia with lust, come see these girls in action!', + url: 'https://www.private.com/site/russian-teen-ass', + network: 'private', + }, + { + slug: 'privatestars', + name: 'Private Stars', + description: 'Welcome to Private Stars. The name speaks for itself as only top-model babes and perfect girls can join this select group of stars, all shot in HD. Part of the Private network of sites, Private Stars brings you a sneak peek into the life of seductive glamour girls who could have easily stepped from the catwalks of Paris or Milan and straight into a world of torrid sex.', + url: 'https://www.private.com/site/private-stars', + network: 'private', + }, + // PURE TABOO + { + name: 'Pure Taboo', + slug: 'puretaboo', + url: 'https://www.puretaboo.com', + description: 'PureTaboo.com is the ultimate site for family taboo porn, featuring submissive teens & virgins in rough sex videos in ultra 4k HD.', + network: 'puretaboo', + priority: 1, + parameters: { + independent: true, + mobile: 'https://m.dpfanatics.com/en/video', + }, + }, + { + name: 'Pretty Dirty', + slug: 'prettydirty', + alias: ['prdi'], + url: 'https://www.prettydirty.com', + network: 'puretaboo', + parameters: { + referer: 'https://www.puretaboo.com', + }, + }, + /* series, not sites, that appear on Pure Taboo itself { name: 'Under The Bed', slug: 'underthebed', @@ -4409,1789 +4416,1789 @@ const sites = [ network: 'puretaboo', }, */ - // REALITY KINGS - { - name: 'Look At Her Now', - url: 'https://www.lookathernow.com', - description: 'Look At Her Now brings you best HD reality porn videos every week. Check out these girls before and after they get some rough pounding.', - parameters: { native: true }, - // parameters: { siteId: 300 }, - slug: 'lookathernow', - network: 'realitykings', - }, - { - name: 'We Live Together', - slug: 'welivetogether', - alias: ['wlt'], - url: 'https://www.welivetogether.com', - description: "We are girls that love to eat pussy and We Live Together! Every week we go out on the streets, bars, parties, malls... wherever and we pick up the cutest lesbians and invite them to come over and party at our apartment. From our girl friends at college, to roommates, and friends of friends.. we're always looking for the hottest lesbian girls around! We Live Together has hundreds of lesbian videos for you to download right from Reality Kings... it's the sexiest lesbian porn anywhere guys and gals! :-) Come watch us eat pussy and work our dildo magic on gorgeous, sexy girls. We love to get together and get off in steamy hot threesome and foursome lesbian movies! We promise you're going to love our amazing collection of lesbian porn. Thanks for dropping in to the We Live Together Apartment, hope you enjoy your visit! Love xoxo Brittney, Taylor, Nicole & All the Girls", - parameters: { siteId: 3 }, - network: 'realitykings', - }, - { - name: 'Black GFs', - slug: 'blackgfs', - alias: ['bgfs'], - url: 'https://www.realitykings.com/scenes?site=47', - description: '', - parameters: null, - network: 'realitykings', - }, - { - name: 'Dare Dorm', - url: 'https://www.daredorm.com', - description: '', - parameters: { siteId: 48 }, - slug: 'daredorm', - network: 'realitykings', - }, - { - name: 'GF Revenge', - slug: 'gfrevenge', - alias: ['gfr'], - url: 'https://www.gfrevenge.com', - description: '', - parameters: { siteId: 49 }, - network: 'realitykings', - }, - { - name: 'Horny Birds', - url: 'https://www.realitykings.com/scenes?site=50', - description: '', - parameters: null, - slug: 'hornybirds', - network: 'realitykings', - }, - { - name: 'Crazy College GFs', - url: 'https://www.realitykings.com/scenes?site=51', - description: '', - parameters: null, - slug: 'crazycollegegfs', - network: 'realitykings', - }, - { - name: 'Crazy Asian GFs', - url: 'https://www.crazyasiangfs.com', - description: '', - parameters: { siteId: 52 }, - slug: 'crazyasiangfs', - network: 'realitykings', - }, - { - name: 'Teens Love Huge Cocks', - url: 'https://www.teenslovehugecocks.com', - alias: ['tlhc'], - description: "Teens Love Big Cocks is dedicated to providing you the hottest teens getting fucked by the biggest cocks! Every week Reality Kings introduces another teen to a big hot meat rod! When these girls see a big throbbing penis they can't resist shoving it in their hot teen mouths. These girl next door types are no slouches when it comes to oral sex! Watch them deepthroat & gag on a mouth full of cock before taking big hot loads all over their pretty faces. The fun doesn't stop there! These girls love getting their tight teen pussy & asses spread wide and pounded by massive dicks! These girls won't settle for less & there is no dick too large. Start downloading TeensLoveBigCock porn videos & HD quality pictures now and watch teen pussy get fucked like you've never seen before!", - parameters: { siteId: 42 }, - slug: 'teenslovehugecocks', - network: 'realitykings', - }, - { - name: 'Big Naturals', - url: 'https://www.bignaturals.com', - alias: ['bin'], - description: "If you think there is nothing like big natural breasts, Big Naturals welcomes you home. Reality Kings brings you nothing but the hottest amateur big tit women. We're talking about some seriously big boobs. Sexy women with big bouncy tits who love to get it on. These women don't hesitate to let their big natural tits get fucked and let those massive juggs bounce! Big Naturals has hundreds of high quality videos available for download. If into tits, this is the place to be. There's no plastic parts here, only big natural boobs! There's thousands of high resolution pics available to download as well. Check out any of our top rated scenes for the biggest, huge natural tits. Hooters, fun bags, juggs... whatever you want to call them Reality Kings and Big Naturals have the hottest big boobs you'll find anywhere. Sit back, relax, and watch the titties bounce... Reality Kings style!", - parameters: { siteId: 5 }, - slug: 'bignaturals', - network: 'realitykings', - }, - { - name: 'Money Talks', - slug: 'moneytalks', - alias: ['mot'], - url: 'https://www.moneytalks.com', - description: "Money Talks... bullshit walks. We all know the saying, but at Reality Kings we like to prove it! Just watch us approach everyday people on the street and ask them what they will do for some real American Greenbacks! Check out smokin' hot amateurs preform in porn videos or watch crazy college kids preform insane stunts on film... all in exchange for cold hard cash. People will do anything when Money Talks! Watch as we offer cash in exchange for one, AMAZING blow job! From crazy Spring Breakers to the girl next door, we find some amazing sluts and see just what they'll do for the loot--girls that give up the booty, for the booty! Arrr! Reality Kings has every high quality Money Talks episode available for download. We're talking about some seriously hot videos here. You won't find this crazy porn content anywhere else! Remember, Money Talks... bullshit walks!", - parameters: { siteId: 28 }, - network: 'realitykings', - }, - { - name: 'Moms Lick Teens', - url: 'https://www.momslickteens.com', - alias: ['momslickteens'], - description: 'Hot moms know how to fuck, especially when they have a lot of pent up energy. MomsLickTeens.com is where all the magic happens between lustful milf minxes and curious 18+ teen bombshells in HD porn videos. Mature horny women love to sample a fresh batch of pussy and ass whenever possible here at Reality Kings. They love teaching the carnal arts to eager younger women who crave a deeper understanding of the female body. Our bodacious mommies love exploring the anatomy of their fresh-faced lesbian lovers and engage in cunnilingus and anilingus within seconds. Naked women licking, sucking, scissoring, and toying their gaping pussy and assholes with a plethora of adult toys is absolutely riveting to watch. You’ll be aroused by RK girls of different ages rolling around together in sweaty sex scenes. Moms Lick Teens features limber tongues exploring the deepest recesses of female erogenous zones often eliciting projectile squirt orgasms. The phenomenon of female ejaculation occurs regularly in our premium erotica so get a load of it while blowing your own load to our buxom mommies today!', - parameters: { siteId: 43 }, - slug: 'momslickteens', - network: 'realitykings', - }, - { - name: 'RK Prime', - slug: 'rkprime', - alias: ['rkp'], - url: 'https://www.realitykings.com/scenes?site=45', - parameters: null, - network: 'realitykings', - }, - { - name: 'Milf Hunter', - url: 'https://www.milfhunter.com', - description: "Reality Kings presents MILF Hunter the ORIGINAL reality porn site dedicated to MILFs and mature sex content. If you don't know what a MILF is, allow us to explain... we're talking about sex starved, smokin' hot moms that are in need of a little attention--a MILF, a Mother I'd Like to Fuck! We've all seen these moms at the mall, the beach, and around town. Watch every week as the Hunter captures another hottie on film and gives them what they've been craving... some dick! These moms are seriously hot MILFs and they appear in the most incredible high quality pics and movies! We have hundreds of mature porn videos available for you to download. Or if you're looking for photos we have thousands of high resolution MILF porn pics directly from the MILF Hunter! Reality Kings brings you the best mature sex scenes around so why not join the MILF Hunter hunt down mature moms across America...", - parameters: { siteId: 2 }, - slug: 'milfhunter', - network: 'realitykings', - }, - { - name: 'Pure 18', - url: 'https://www.realitykings.com/scenes?site=31', - description: 'There\'s a lot of stuff out there that claims to be "pure", from spring water to gold chains, who knows what\'s actually legit$2 Reality Kings presents Pure 18, legit, 100% verified 18 year old sex scenes--no bullshit, only incredible 18 year old girls! These hot girls are the real deal, barely legal, smokin\' hot babes looking for some fun. Don\'t let their age fool you, these chicks know how to work a cock. Tight pussies and tight asses, the finest sex scenes around, that\'s what Pure 18 is all about! If you love watching amazing blow jobs, you\'re going to love this content. Download hundreds of high quality videos and pics featuring 100% verified 18 year old sex! These cuties are not shy about sex. Watch them take on monster cocks and love every minute of it. Pure 18, legit, verified, real 18 year old girls hungry for a cock meat sandwich!', - parameters: null, - slug: 'pure18', - network: 'realitykings', - }, - { - name: 'Bad Tow Truck', - url: 'https://www.realitykings.com/scenes?site=44', - description: 'Driving a tow truck is hard work. Especially when clients have a tough time paying! At BadTowTruck.com we alleviate that problem for sexy female damsels in distress by offering them different “payment” options. When big tit babes need a boost but are tight on cash, our drivers are more than happy to boost their tight asses up and give them a deep dicking. The chance to unleash a creamy internal cumshot should not be missed here at Reality Kings! Enjoy HD porn videos full of stranded sirens who are all too happy to get naked and oblige their rescuer with a gagging BJ. Anal riding is not far behind as our buxom RK divas love to get their bumpers shined. Bad Tow Truck is home to monster cocks getting gobbled up by very appreciative clients whose cars have broken down. They love swallowing every drop of jizz in the front or back seat of the tow truck. Anything goes here as our tantalizing teasers can contort their agile bodies into any number of sexual positions in tight spaces. They require maximal torque and horsepower in their erotic escapades so watch them get out of a jam with the help of a helpful towing guy, and then get jammed by him!', - parameters: null, - slug: 'badtowtruck', - network: 'realitykings', - }, - { - name: 'Cum Fiesta', - url: 'https://www.cumfiesta.com', - alias: ['cuf'], - description: '"Is this the fiesta $4 " Of course, welcome to the Cum Fiesta! Every week Reality Kings welcomes another hottie to the party... the dick sucking party! :-) When the girl (or girls!) arrive they show us the super secret password (watch a trailer to find out) and the party begins! As their clothes come off, these babes begin to show off their amazing oral skills. Amateur chicks taking the biggest facial cumshots, that\'s what Cum Fiesta is about! There are no pansy blow jobs here, these chicks give the best head around, and take huge cum shots to their face at the same time. Download hundreds of high quality videos and pics of semen swallowing hotties. Join the party that features amateurs, newbies, and even the girl next door! These babes love to suck cock and take incredible cum shots you\'ll find no where else! This is no siesta folks, it\'s a Cum Fiesta!', - parameters: { siteId: 10 }, - slug: 'cumfiesta', - network: 'realitykings', - }, - { - name: '8th Street Latinas', - url: 'https://www.8thstreetlatinas.com', - description: "Scientists say that the sun is what makes the temperature rise south of the equator, but we think its the women! If you haven't seen the chicas (women) from 8th Street Latinas, you're in for a spicy treat. From the famous Calle 8 (8th Street) in Miami, Reality Kings brings you the some incredibly hot latinas! We're talking Cubans, Dominicans, Panamanians, and other sexy latinas from South and Central America. These babes aim to please with their tanned bodies and deliciously round asses. Damn, we can't get enough! 8th Street Latinas has some caliente (hot) content for you to download, featuring the hottest latina sex scenes around. These are not tanned white chicks, these are REAL, hot latinas who know how to get your blood pumping. If you think you can handle the heat, grab your pair of shorts and flip-flops and let's head to Miami... 8th Street Latinas has some incredible latina porn for you!", - parameters: { siteId: 1 }, - slug: '8thstreetlatinas', - network: 'realitykings', - }, - { - name: "Mike's Apartment", - url: 'http://www.mikesapartment.com', - description: 'There\'s a room for rent in Mikes Apartment and Mike has found another hot chick to fill the vacancy! Join Mike on his search for roommates to help pay the bills. If these hot euro babes don\'t have the money, that\'s alright, Mike offers them the room in exchange for a few hours of masturbation and fun! And if the girl is traveling with a companion, thats not a problem... Mike just creates another steamy euro sex film for his "private" collection. Seriously, these babes are Europe\'s finest and Mike is your connoisseur of European booty! From their tight bodies, to their thick accents, these ladies know how to please and excite. Reality Kings offers hundreds of our incredible european porn movies and pics for you to download, and you don\'t have to travel to Moscow to get them. If you\'re looking for original, hot, European porn content, welcome to your new home: Mikes Apartment.', - parameters: { siteId: 25 }, - slug: 'mikesapartment', - network: 'realitykings', - }, - { - name: 'In the VIP', - url: 'http://www.inthevip.com', - description: "Ever wonder what happens In The VIP$3 Reality Kings takes you to the hottest night clubs in the country to show you exactly what goes on in the VIP room. When the club is packed, the music is pumpin', and the ladies are looking this HOT, you know it's going to be a damn good time! Grab a drink and step into the VIP room. Check out these gorgeous babes shaking their asses and flashing the camera. You will never see ladies like this in some whack ass bar, only in the most exclusive VIP rooms. As the party gets going, the clothes come off, and the panties drop! Watch some amazing free VIP porn movies, featuring these hotties having sex VIP style. We're talking about some down and dirty club sex featuring smokin' hot sluts. These chicks came for a good time and they've found it! Join the exclusive party In The VIP.", - parameters: { siteId: 22 }, - slug: 'inthevip', - network: 'realitykings', - }, - { - name: 'CFNM Secret', - slug: 'cfnmsecret', - alias: ['cfnms'], - url: 'https://www.realitykings.com/scenes?site=9', - description: "Shhh, keep your voice down! At Reality Kings we have a secret to share, the CFNM Secret! What's this secret all about$5 Clothed Females and Nude Males (CFNM)! Beautiful women dressed to impress and an unsuspecting male who is about to discover the secret for himself! These voyeurs are interested in every inch of the male body, touching and grabbing, they won't stop until they've had enough. Who wouldn't mind being these ladies play things$6 Gorgeous babes teasing and embarrassing men for their own fun and pleasure. Vulnerable guys being inspected and scrutinized in amazing high quality voyeur sex scenes. From CFNM handjobs to CFNM party scenes Reality Kings offers the hottest women enjoying the male body like never before. Browse our free CFNM videos below to download high quality pics and trailers. You're going to tell your friends about this secret, the CFNM Secret!", - parameters: null, - network: 'realitykings', - }, - { - name: 'Street BlowJobs', - url: 'https://www.realitykings.com/scenes?site=36', - description: "Street Blowjobs is one man's hunt for down on their luck ladies. Ladies who will blow your creamy wad, while they win a nice greedy wad of cash for their outstanding efforts. Horny honeys captured on hidden spy camera, giving amazing blowjobs for some good ol' American greenbacks. Can you imagine any of these smokin' hot babes giving blowjobs to you for just a little bit of moolah$11 Well we've got the content for you! Street Blowjobs has hundreds of hot blowjobs for you to download in high quality movies and pics. Watch these hotties use their magnificent dick sucking lips to get exactly what they want--your cock and your money! Reality Kings brings you a new episode every week, full of the best blowjobs and public blowjobs around. They say money can't buy happiness, but we beg to differ... money can chose your type of pleasure at Street Blowjobs.", - parameters: null, - slug: 'streetblowjobs', - network: 'realitykings', - }, - { - name: 'Hot Bush', - url: 'https://www.realitykings.com/scenes?site=21', - description: 'Forget bald vaginas, at Reality Kings we love a Hot Bush! Women with some grass on the field. These hairy beavers belong to some of the hottest women around. From brunette babes to red heads, every episode features a stunning natural beauty being worshiped for her beautiful bush! Browse some of our free hairy pussy videos below, and see for yourself if the curtains match the drapes. From the landing strip to the Bermuda triangle, these magnificent muff mounds are sure to please! Natural unshaved pussy movies and pics, thats what Hot Bush is all about. Grab your snorkel and get ready for some serious muff diving! Join the Reality Kings crew for some *very* Hot Bush.', - parameters: null, - slug: 'hotbush', - network: 'realitykings', - }, - { - name: 'Team Squirt', - url: 'https://www.realitykings.com/scenes?site=37', - description: "There's no denying it, at Reality Kings we love all kinds of pussy! Ask us what we really love however, and you'll get one answer: hot wet pussy! Team Squirt invites you to strap on your snorkel and fins, because we're going diving in some of the wettest pussy around! This is NOT pee ladies and gentlemen, this is real female ejaculation. Watch these beautiful ladies experience pleasure beyond belief as they try to control their squirting pussy on camera. Masturbation, fucking, whatever it takes, these babes will do anything for a squirting orgasm! Team Squirt has tons of high quality videos of girls squirting available for you to download right now. Be prepared, this is some serious female squirting content! From the girl, to the camera... everything is drenched when these super soakers take aim. These babes all pack a loaded, squirting pussy, and they know exactly how to use it! Grab your eye protection and join the team... Team Squirt.", - parameters: null, - slug: 'teamsquirt', - network: 'realitykings', - }, - { - name: 'Milf Next Door', - url: 'https://www.realitykings.com/scenes?site=26', - description: "We all love them, from the sexy mom at the grocery store, to the mature hottie down the block... we're talking about the MILF Next Door! There is nothing that these hot MILFs need more than a good pounding. If you don't know what a MILF is, allow us to explain... a Mother I'd Like to Fuck, a MILF! Watch as these sex starved sluts and their girlfriends search for a lucky dude to satisfy their craving for cock. MILF Next Door offers lesbian threesomes, amazing foursomes, and more mature sex movies featuring the hottest mature women! Start downloading some of this incredible content right now from our free pics and videos below. Every episode features another stunningly hot MILF finally getting the attention she deserves. If you love everyday mom's and can't wait to see these ladies get off, join Reality Kings and the MILF Next Door.", - parameters: null, - slug: 'milfnextdoor', - network: 'realitykings', - }, - { - name: 'Captain Stabbin', - url: 'https://www.captainstabbin.com', - description: "Hop aboard the S.S. Stabbin and join Captain Stabbin on his trip to analize the seven seas! What's better than a girl with a hot pussy$8 A girl with a hot pussy getting poked in the ass! Reality Kings invites you to the worlds greatest anal sex adventure. These babes don't need a boarding pass to climb aboard this ship, only a hot body and a gorgeous ass. Watch as the Captain sets course for the anal islands in search of the best anal sex scenes around! Download hundreds of incredible anal sex movies and pics in stunning high quality formats. Captain Stabbin brings you the very best booty content, from her first anal sex scene, to amazing boat sex scenes at sea, every episode is sure to please! These girls are ready for a stern spanking (pun intended)! Raise the main sail, set course, and join Captain Stabbin on his anal adventure! Arrr!", - parameters: { siteId: 8 }, - slug: 'captainstabbin', - network: 'realitykings', - }, - { - name: 'Big Tits Boss', - url: 'https://www.realitykings.com/scenes?site=6', - description: "Reality Kings presents Big Tits Boss! Have you been checking out that smokin' hot female executive around the office$10 Damn she's fine! She wears those short skirts and tight tops that make her huge tits pop out! Time to stop slackin' off fellas, because these ladies have been watching you closely. We're talking about the sexy women with the big tits at work. CEOs, Lawyers, CIOs, CFOs, these babes don't take any bullshit and they'll gladly use you like a toy whenever they please! Big Tits Boss has amazing high quality videos and pics available for download, featuring some very powerful women with awesome big natural tits. You won't mind being called into the office for a little discipline this time around! It's all business when these hotties are in the office... so fill out your TPS reports and be on your best behavior if you're looking for the promotion from the Big Tits Boss!", - parameters: null, - slug: 'bigtitsboss', - network: 'realitykings', - }, - { - name: 'Euro Sex Parties', - slug: 'eurosexparties', - alias: ['esp'], - url: 'https://www.realitykings.com/scenes?site=13', - description: "Pack your bags folks, we're headed to Europe! It's time to join two best friends as they travel across Europe and throw some amazing Euro Sex Parties. Forget about boring 1on1 sex scenes. Get ready for hardcore threesomes, foursomes, and fivesomes! Hot European porn directly from the source, just the way we like it. Euro babes with tight bodies taking on multiple cocks... what could be better$9 How about watching them eat pussy as well! Now that's a group sex party we'd love to attend. From hardcore group sex to hardcore gangbangs, this is the hottest content anywhere. Euro Sex Parties offers hundreds of European porn videos and pics to download in stunning high quality formats. Don't pack your bags yet, Reality Kings has tons of free movies and pics for you to download right here! Join us on our European vacation, and we'll throw a few Euro Sex Parties along the way.", - parameters: null, - network: 'realitykings', - }, - { - name: 'Dangerous Dongs', - url: 'https://www.realitykings.com/scenes?site=12', - description: "Reality Kings presents the Dangerous Dongs porn site, which bring you highlights from the featured huge cock and big dick porn on the entire network. If you enjoy watching sexy girls having fat cock stuffed deep into their tight pussies while they moan in pleasure, Dangerous Dongs has thousands of high resolution big dick porn pics and videos waiting for you inside. See tons of hot Latina, MILF, college, blond, teen and ebony babes taking big cock balls deep. You'll be able to download tons of big cock videos with hardcore sex scenes where cute girls with big tits and sexy, round asses take fat cock from every angle including doggy style and reverse cowgirl. So, for true fans of the big dick porn genre, look no further, we've gathered the best huge cock videos Reality Kings has to offer on the internet.", - parameters: null, - slug: 'dangerousdongs', - network: 'realitykings', - }, - { - name: 'Extreme Asses', - url: 'https://www.realitykings.com/scenes?site=14', - description: "Extreme Asses brings you a slew of big ass babes exclusively from Reality Kings. We're talking serious highlights from featured RK models like Jayden James big ass pictures and even some Jenny Hendrix ass sex videos. These sexy babes have nothing but big bouncy tits and porn ass that is perfect whether being viewed in doggy style action or riding cock in big ass videos. Watch tons of free ass porn trailers to get a taste of the kind of porn ass and ass sex that awaits you inside the Extreme Asses website. No matter what you like whether it be Latina, MILF, college, blond, teen or ebony big ass, we've got all the best extreme asses you can handle. This is big ass heaven, so be sure to check out this collection of big ass porn and ass sex gathered from Reality Kings best for your viewing pleasure.", - parameters: null, - slug: 'extremeasses', - network: 'realitykings', - }, - { - name: '40 Inch Plus', - url: 'https://www.realitykings.com/scenes?site=4', - description: "We have three words for you: deliciously round asses. Are you searching for ladies of the thicker variety$15 Beautiful women with hips measuring over 40 Inch Plus$16 Reality Kings presents the finest collection of booty around! Hot babes that love to have their big asses pinched and smacked. Grab 'em, squeeze 'em, bite 'em, these girls love to have their round asses played with. 40 Inch Plus is a tribute to ladies with a shape, there's no skinny chicks here! All of these women have a perfect ass and their tits are not too bad either! Download hundreds of movies and pics featuring women with big round asses. Check out our trailers and pics below to get a free sample of this incredible content. If you're looking for beautiful women with nice round asses look no further! Reality Kings and 40 Inch Plus are your source for gorgeous women with big asses.", - parameters: null, - slug: '40inchplus', - network: 'realitykings', - }, - { - name: 'Happy Tugs', - url: 'https://www.happytugs.com', - description: "Come on dudes, who doesn't like a happy ending$13 We've all seen those hole in the wall Asian massage parlors! Finally there is a site that celebrates the hand job. Asian beauties rubbing massage oil all over, what could be better$14 These babes know how to work out the kinks, seriously amazing rub 'n tug jobs. Happy Tugs captures hidden camera footage from inside one of the country's best sexual massage parlors. The dudes come in looking for a little rub down and, for a few dollars more, get a full servicing. It doesn't get any better than this, hand jobs and hot Asian babes. Check out our amazing happy ending videos, with babes rubbing their oil soaked breasts all over their favorite customers. Strip down, jump on the massage table and get your wallet out, Happy Tugs will ensure you get a very happy ending!", - parameters: { siteId: 19 }, - slug: 'happytugs', - network: 'realitykings', - }, - { - name: 'Reckless In Miami', - url: 'https://www.realitykings.com/scenes?site=303', - description: '', - parameters: null, - slug: 'recklessinmiami', - network: 'realitykings', - }, - { - name: 'HD Love', - url: 'https://www.realitykings.com/scenes?site=20', - description: 'Looking for incredibly hot porn videos in HD$12 Reality Kings showcases it all in hardcore erotica here at HDLove.com. Our premium adult content will satisfy your deepest carnal desires in stunning high-definition sex scenes. Feast your eyes on bodacious naked babes who love nothing more than to fuck on camera for you. Our hi-def movies capture every inch of their voluptuous bodies in vivid detail. Perfect round boobs and perky nipples are just the start. These jezebels proudly display their killer asses and dripping wet pussies before ravaging huge cocks like their lives depended on it. Whether you’re in the mood for horny 18+ teen nymphos or seasoned mature women the raunchiest scenes will keep you cumming back to HD Love. Our eager to please divas love to disrobe and spread as much love around as they can offering up deepthroat gagging blowjobs to anyone daring enough. They yearn for deep anal penetrations and are always up for a sweaty orgy so witness it all in crystal clear resolution. Catch every microscopic detail and blow your load repeatedly with the best that RK has in store!', - parameters: null, - slug: 'hdlove', - network: 'realitykings', - }, - { - name: 'Bikini Crashers', - url: 'https://www.realitykings.com/scenes?site=7', - description: "What's better than a babe in a scantily clad bikini$22 A party full of babes in scantily clad bikinis of course! Welcome to the Bikini Crashers! Reality Kings invites you to join our party with the hottest swimsuit models and bikini babes anywhere. We're talking about smokin' hot beauties throwing a naked pool party. Could it get any better than this$23 From perfectly round asses to amazing tan lines, these girls know how to party. We're not talking about your average swimsuit model either, these chicks are wild, crazy, and ready to get it on. Every party is loaded with 1on1, girl on girl, and group sex scenes. Gorgeous swimsuit girls getting it on by the pool, the beach, or anywhere they can show off their amazing bikinis! So grab a cold one, your shades, and kick back... you're invited to the Bikini Crashers party!", - parameters: null, - slug: 'bikinicrashers', - network: 'realitykings', - }, - { - name: 'Wives in Pantyhose', - url: 'https://www.realitykings.com/scenes?site=41', - description: 'Wives in Pantyhose features all kinds of real wives in sexy lingerie fingering their pussies with sex toys while they squeeze their big mature tits and moan. This Reality Kings network site has collected tons of pantyhose pics of hot wives and presented them to you for your viewing pleasure. No matter whether you prefer Latinas, MILFs, redheads, blondes or ebony babes, Wives in Pantyhose has all the sexiest nylon wives masturbating. There are even pantyhose lesbians playing with each other using dildos while they orgasm in smoking hot pantyhose videos. Wives in Pantyhose is easily one the best collection of real wives engaging in pantyhose porn ever put together on the net. So if you have a housewife pantyhose fetish, the the website Wives in Pantyhose is sure to deliver for you all the best models and porn the Reality Kings network has to offer.', - parameters: null, - slug: 'wivesinpantyhose', - network: 'realitykings', - }, - { - name: 'No Faces', - url: 'https://www.realitykings.com/scenes?site=30', - description: 'Isn’t it arousing to watch porn with a little mystery thrown in$19 That’s what Nofaces.com is about. The scrumptious porn stars at Reality Kings like to keep you guessing sometimes, so enjoy a wide array of HD porn videos where the faces of our horny minxes are not shown. A little sensual secrecy never hurt anyone so have some fun trying to figure out which titillating temptress is getting reamed in our torrid sex clips. Are her ample breast, perfect round ass, and wet pussy enough of a giveaway$20 What about her tattoos, piercings, or birth marks$21 Play the role of an X-rated detective and enjoy an endless sea of hardcore erotica with 18+ teen foxes and mature naked temptresses in graphic films depicting a covert cum sucker gobbling up monster cocks with glee. Our enigmatic nymphos relish in getting fucked in multiple holes simultaneously knowing that their identity is not revealed on camera. No Faces respects an RK girl’s desire to remain anonymous and only show more arousing parts of her luscious body.', - parameters: null, - slug: 'nofaces', - network: 'realitykings', - }, - { - name: 'Saturday Night Latinas', - url: 'https://www.realitykings.com/scenes?site=34', - description: "What's better than a Saturday Night out partying$18 Taking home a beautiful chick at the end of the night to fuck and have your way with! Reality Kings presents Saturday Night Latinas, gorgeous babes from the steamy night clubs and streets of Brazil. These hotties may have left the club, but the real party is about to begin! Real latina girls sucking and fucking after a night of partying! From deliciously round asses to amazing tan lines, these Brazilian bombshells are sure to please. Browse our videos below to download free latina porn movies and pictures. We have hundreds of latina sex scenes available for you to download. Grab your bags and get ready to head to Brazil, Reality Kings invites you to take home a Saturday Night Latina of your very own. Hot latina babes who love to party, join us today for a steamy Saturday Night out!", - parameters: null, - slug: 'saturdaynightlatinas', - network: 'realitykings', - }, - { - name: 'Extreme Naturals', - url: 'https://www.realitykings.com/scenes?site=15', - description: 'There are big natural breasts, then there are Extreme Naturals. On this site, we say, "Go big or go home!" That\'s why we only deliver massive naturals straight from the best Reality Kings has to offer. Extreme Naturals has painstakingly combed the RK network for the best giant naturals models and the hottest big naturals videos with the most hardcore XXX. These sexy babes have giant naturals that bounce while they ride cock and while they get stroked from behind doggy style in their perfect porn asses. For true fans of huge natural breasts, be sure to watch tons of free big naturals videos exclusively available as Extreme Naturals trailers on the website. Whether you like your giant naturals to be on Latinas, MILFs, college babes, blondes, teens or ebony babes, Extreme Naturals has the best collection of massive naturals straight from the vaults of Reality Kings.', - parameters: null, - slug: 'extremenaturals', - network: 'realitykings', - }, - { - name: 'Cum Girls', - url: 'https://www.realitykings.com/scenes?site=11', - description: "Reality Kings presents the Cum Girls porn site, which is dedicated solely to XXX cum shots and cum videos. If you like seeing hot girls with cum in their mouth, or face cum pictures, Cum Girls has thousands of high resolution cum porn pics and videos waiting for your viewing pleasure. There are smoking hot Latina, MILF, college, blond, teen and ebony babes with cum shots not only on their face, but also cum ass and cum tits pictures too. You'll be able to download tons of cum porn videos with hardcore sex scenes that all end with sticky and gooey cum in the mouth, the face, boobs, pussy or ass. Cum Girls has got cum porn and cum videos for true fans of the genre, and they all come straight to you from Reality Kings, so you know you're getting nothing less than the best on the internet.", - parameters: null, - slug: 'cumgirls', - network: 'realitykings', - }, - { - name: 'VIP Crew', - url: 'https://www.realitykings.com/scenes?site=40', - description: "Party animals rejoice! The VIP Crew is your guide to the hottest and wildest VIP parties in the world! We're not talking about ordinary house parties here, we're talking about the biggest, most badass sex parties around. When you combine loads of fun, some fine looking women, and a few lucky dudes you have the recipe for one amazing fucking party. Best of all, you're invited! From huge orgy sex parties to private sex parties, the VIP Crew brings in the hottest women--all ready to bare their VIP pussies for you. Babes that aren't afraid of a little pole dancing, foam dancing, or strip tease! These girls will do anything to join these wild sex parties and have a good time. Reality Kings has hundreds of high quality videos and pics available for you to download. So what the hell are you waiting for$17 Join the VIP Crew and get your freak on!", - parameters: null, - slug: 'vipcrew', - network: 'realitykings', - }, - { - name: 'Moms Bang Teens', - slug: 'momsbangteens', - alias: ['mbt'], - url: 'https://www.momsbangteens.com', - description: "Reality Kings presents the first website dedicated to hot moms who love to bang 18+ teens. Moms Bang Teens features the sexiest MILFs on the web, and these MILFs are all about fucking young guys on camera. If you remember lusting after one of your friend's hot moms back in grade school, then you know exactly what Moms Bang Teens is all about. Imagine if instead of just fantasizing about that sexy mother, you actually got to bang her. These are the same hot moms you see at your local supermarket and shopping at your neighborhood mall. Some of them are married and never get the attention they need. While others are just horny and sexy moms who never got tied down with a husband. Instead they like to go out and find hot young studs that know how to fuck them right. These are experienced and mature women who know what they want; young 18+ teens that can give them that rock hard cock.", - parameters: { siteId: 27 }, - network: 'realitykings', - }, - { - name: 'Sneaky Sex', - url: 'https://www.sneakysex.com', - description: 'Sneaky dirty sex! They are fucking and nobody can see, otherwise they will have a HUGE problem. When no one is watching, these horny MILFs and Teens are having sneaky sex!', - parameters: { siteId: 46 }, - slug: 'sneakysex', - network: 'realitykings', - }, - { - name: 'See My Wife', - url: 'https://www.realitykings.com/scenes?site=35', - description: 'Have you been spying on that hot couple next door$26 See My Wife invites you to view the private porn collection of horny amateurs everywhere! We\'re talking about 100% user submitted movies and pictures. Real women appearing in the hottest wife sex scenes around, that is what See My Wife is about. Our users have a chance to make 0 for pics and 00 for videos when they submit their homemade content. If you\'ve ever said "I wish I could bang my wife on film and get paid for it," look no further! Reality Kings considers every submission when we post new episodes. Check out some of our free pics and trailers below, this is one amazing collection of girlfriend and wife sex scenes. Every week we post a new episode crammed with four incredible babes showing off in front of the camera. No need to spy on the couple next door when you come See My Wife!', - parameters: null, - slug: 'seemywife', - network: 'realitykings', - }, - { - name: 'Girls of Naked', - url: 'https://www.realitykings.com/scenes?site=18', - description: 'Nothing is hotter than voluptuous minxes who love getting naked. Girlsofnaked.com is home to a bevy of bodacious beauties who are all about showing as much skin to whomever is willing to satisfy their sexual desires. Our 18+ pornstars are daring and always curious for new carnal adventures in HD porn videos. Reality Kings has compiled an incredible assortment of erotica with big boob naughty nymphos. Watch them squeeze their perky nipples before rubbing their ticklish clits in steamy scenes. Our deviant divas need their juicy pussies stuffed 24/7 by the biggest cocks in the adult biz and will stop at nothing to devour as much man meat as they can fit into every hungry orifice. Girls of Naked celebrate nudity and hardcore sex in all its glory. Fetishes, orgies, bukkake, anal creampies and much more are their favorite pastimes. RK has full-length premium porno movies bursting with our luscious babes bursting out of their clothes just for you!', - parameters: null, - slug: 'girlsofnaked', - network: 'realitykings', - }, - { - name: 'Lil Humpers', - url: 'https://lilhumpers.com', - description: '', - parameters: { siteId: 310 }, - slug: 'lilhumpers', - network: 'realitykings', - }, - { - name: 'Mike in Brazil', - url: 'https://www.realitykings.com/scenes?site=24', - description: "Are you ready for the never ending booty vacation$24 Join Mike In Brazil as he explores the wild, the exotic, and the gorgeous women of South America! If you have never been to Brazil, don't worry... Mike will give you a crash course on the most amazing ASSet of their native women. We're talking about deliciously tanned, round, thong clad Brazilian ass! These booties will not disappoint. Mike exports nothing but the finest, Grade A, Brazilian porn directly to your computer screen. Check out the hottest Brazilian ass around, wearing nothing but bikinis and thongs that are sure to get your blood pumping! These hotties spend hours working on their amazing tans to show off their bodies... look at those incredible tan lines! Mike In Brazil features some amazing hardcore sex, from anal to Brazilian facials, we're sure you're going to be planning a trip to Brazil soon. What are you waiting for$25 Join the never ending booty vacation with Mike In Brazil!", - parameters: null, - slug: 'mikeinbrazil', - network: 'realitykings', - }, - { - name: 'Real Orgasms', - url: 'https://www.realitykings.com/scenes?site=32', - description: "Real Orgasms features all kinds of sexy women playing with their pussies and stimulating their clits with sex toys and big dildos until they have real orgasms. This Reality Kings network site has collected tons of real orgasm videos and masturbation videos and concentrated them down to only the best real female orgasms that you will ever witness on the net. Whether you're really into Latinas, MILFs, college babes, blondes, teens or ebony babes, Real Orgasms has every kind of the most beautiful women masturbating. Watch as they play with themselves using sex toys and dildos while they moan, shake and their pussies convulse, as they have real orgasm on video for your pleasure. By far, this is the best collection of real orgasm porn ever put together on the net. Thanks to Reality Kings, Real Orgasms only delivers 100% real female orgasms and masturbation videos.", - parameters: null, - slug: 'realorgasms', - network: 'realitykings', - }, - { - name: 'Tranny Surprise', - url: 'https://www.trannysurprise.com', - description: 'If you’re in the mood for graphic tranny porn, look no further than TrannySurprise.com. A sexy shemale is a thing of beauty, often possessing a voracious appetite for sex. Reality Kings is home to some of the most incredible transsexual pornstars on the net. Watch them stroke their huge dicks and massage their voluminous ball sacks in our full-length HD videos. All these goddesses want to do is suck dick until it erupts in their wide open mouths. Cum swallowing is their specialty so enjoy our scenes full of creamy jizz loads overflowing onto their giant tits and firm stomachs. These nude RK shemales live to get rimjobs before getting drilled by gigantic dicks. Messy creampies are usually how their nights end so witness the torrid fuck marathons leading up to juicy orgasms. Tranny Surprise features sensual ladyboys that know just how to please anyone looking to take a walk on the wild side. Premium porno is what you deserve so eat it all up with our luscious, busty trannies. Long legs, tight asses, toned physiques, and a healthy dose of raw animal passion is what our “chicks with dicks” deliver in every one of our erotic films.', - parameters: { native: true }, - slug: 'trannysurprise', - network: 'realitykings', - }, - { - name: 'Flower Tucci', - url: 'https://www.realitykings.com/scenes?site=17', - description: 'Reality Kings presents Flower Tucci, and this is what she has to say: "My name is Flower, and I live, eat, breathe, sleep, and worship SEX! You have never met a girl like me! My ass is for worshiping. I can squirt over and over again when my pussy cums. I search out the biggest cocks and take them in my mouth, pussy, and ass! I milk those cocks until my pussy squirts everywhere. This site is dedicated to all my fantasies. Watch me search for the ultimate orgasm." Damn folks! I don\'t know about you, but this babe sounds perfect. Squirting pussy, amazing ass, gorgeous tits... the full package! If you\'re like us, you can\'t wait another moment to download these amazing videos of Flower Tucci squirting. Reality Kings is the one and only home to Flower Tucci pics and Flower Tucci movies--this content is absolutely incredible! Join us, Flower, and her friends in search of the ultimate orgasm.', - parameters: null, - slug: 'flowertucci', - network: 'realitykings', - }, - { - name: 'First Time Auditions', - url: 'https://www.realitykings.com/scenes?site=16', - description: "Forget about the next big music idol, we're looking for the next big porn star! Reality Kings presents First Time Auditions, featuring the hottest amateur chicks, searching for fame and fortune. These sluts will do anything to break into the business, from blowjobs to amateur sex scenes, these are their first porn auditions caught on film. Do you think they have what it takes$7 Download hundreds of amateur porn movies and pics, and you be the judge. First Time Auditions places ads in local and college newspapers seeking the hottest models around. When these babes arrive, we are never disappointed. They show off their perfect bodies and their many, amazing talents! These are the hottest amateur auditions around. Trying to get their careers started, these girls give the porn auditions of a lifetime! If you're ready to be the judge, to put these girls to the test, watch them on their First Time Auditions.", - parameters: null, - slug: 'firsttimeauditions', - network: 'realitykings', - }, - { - name: 'Top Shelf Pussy', - url: 'https://www.realitykings.com/scenes?site=38', - description: 'Top Shelf Pussy features nothing but the best pussy on the net. If pussy is like Johnny Walker, consider Top Shelf Pussy the Blue Label of the bunch. Whether you like shaved pussy, teen pussy, hairy pussy, wet pussy, mature pussy, black pussy or fat pussy, Top Shelf Pussy has got the hottest models and the best pussy videos on the net. Watch tons of free pussy trailers and see as these gorgeous girls play with their pussies using sex toys and dildos. Then see them take a deep stroking to their wet pussy while they moan, shake and their pussies convulse in some amazing pussy porn. No doubt, Top Shelf Pussy has got tons of pictures of sexy ladies spread eagle and more pussy videos than you could ever possibly watch. If you are a fan of the pussy porn genre, then Top Shelf Pussy is the site for you.', - parameters: null, - slug: 'topshelfpussy', - network: 'realitykings', - }, - { - name: 'Round and Brown', - url: 'https://www.roundandbrown.com', - alias: ['rab'], - description: 'Chocolate lovers out there, Reality Kings presents to you... Round And Brown, the porn site that caters to horny dudes who have a mighty craving for fine "sistah" booties. The ladies featured in these ebony porn movies are SIZZLING HOT, like a newly melted chocolate fondue! We\'re talking about some damn fine black booties! If it\'s Round And Brown, it gets the special lube treatment, no exceptions! Think you can handle this collection of premium ebony ass$1 There\'s no skinny white girls here, only gorgeous black beauties with deliciously round booties, featured in the best hardcore ebony sex videos around! Reality Kings is the only one who can bring you this amazing collection of black girl porn. If you love big round asses, gorgeous black babes, and amazing tits we have the videos and pics you\'re looking for. Warning: This chocolate may melt in your hand and your mouth... but who cares, if it\'s Round And Brown!', - parameters: { siteId: 33 }, - slug: 'roundandbrown', - network: 'realitykings', - }, - { - name: 'Monster Curves', - slug: 'monstercurves', - alias: ['mcu'], - url: 'https://www.realitykings.com/scenes?site=29', - description: "Forget about those toothpick size runway models, give us some ladies with curves-- Monster Curves! If you love your women round and juicy, ladies with some meat on their bones... then we have the content for you! We're talking about women with hips that don't quit. Incredibly round asses that will make your mouth water! Big booty girls with big round asses. Only people as obsessed as us could bring you this many pairs of perfects hips and asses! Download hundreds of movies and pics featuring gorgeous girls with amazing curves (we call them Monster Curves). Check out some of our free trailers below, these girls and their round butts and perfect hips are sure to wet your appetite! Every week, Reality Kings brings you nothing but the finest butts, the sexy round asses that jiggle when you grab 'em, the women with the Monster Curves!", - parameters: null, - network: 'realitykings', - }, - // SCORE - { - name: '18 Eighteen', - slug: '18eighteen', - url: 'https://www.18eighteen.com', - network: 'score', - parameters: { path: '/xxx-teen-videos' }, - }, - { - name: '40 Something Mag', - slug: '40somethingmag', - url: 'https://www.40somethingmag.com', - parameters: { path: '/xxx-mature-videos' }, - network: 'score', - }, - { - name: '50 Plus MILFs', - slug: '50plusmilfs', - url: 'https://www.50plusmilfs.com', - parameters: { path: '/xxx-milf-videos' }, - network: 'score', - }, - { - name: '60 Plus MILFs', - slug: '60plusmilfs', - url: 'https://www.60plusmilfs.com', - parameters: { path: '/xxx-granny-videos' }, - network: 'score', - }, - { - name: 'Ashley Sage Ellison', - slug: 'ashleysageellison', - url: 'https://www.bigboobbundle.com/ashleysageellison', - parameters: { path: '/videos', actors: ['Ashley Sage Ellison'] }, - network: 'score', - }, - { - name: 'Autumn Jade', - slug: 'autumnjade', - url: 'https://www.bigboobbundle.com/autumn-jade', - network: 'score', - parameters: { path: '/videos', actors: ['Autumn Jade'] }, - }, - { - name: 'Big Boob Bundle', - slug: 'bigboobbundle', - url: 'https://www.bigboobbundle.com', - network: 'score', - show: false, // all content appears to be on subsites - }, - { - name: 'Big Boobs POV', - slug: 'bigboobspov', - url: 'https://www.scorepass.com/bigboobspov', - network: 'score', - }, - { - name: 'Big Tit Angela White', - slug: 'bigtitangelawhite', - url: 'https://www.bigboobbundle.com/bigtitangelawhite', - parameters: { path: '/videos', actors: ['Angela White'] }, // no dates available - network: 'score', - }, - { - name: 'Big Tit Hitomi', - slug: 'bigtithitomi', - url: 'https://www.bigboobbundle.com/bigtithitomi', - parameters: { path: '/videos', actors: ['Hitomi'] }, - network: 'score', - }, - { - name: 'Big Tit Hooker', - slug: 'bigtithooker', - url: 'https://www.scorepass.com/bigtithooker', - network: 'score', - }, - { - name: 'Big Tit Terry Nova', - slug: 'bigtitterrynova', - url: 'https://www.bigboobbundle.com/bigtitterrynova', - parameters: { path: '/videos', actors: ['Terry Nova'] }, - network: 'score', - }, - { - name: 'Big Tit Venera', - slug: 'bigtitvenera', - url: 'https://www.bigboobbundle.com/bigtitvenera', - network: 'score', - }, - { - name: 'Black And Stacked', - slug: 'blackandstacked', - url: 'https://www.scorepass.com/blackandstacked', - network: 'score', - }, - { - name: 'Boned At Home', - slug: 'bonedathome', - url: 'https://www.scorepass.com/bonedathome', - network: 'score', - }, - { - name: 'Bootylicious Mag', - slug: 'bootyliciousmag', - url: 'https://www.bootyliciousmag.com', - network: 'score', - }, - { - name: 'Busty Angelique', - slug: 'bustyangelique', - url: 'https://www.bigboobbundle.com/bustyangelique', - network: 'score', - }, - { - name: 'Busty Arianna', - slug: 'bustyarianna', - url: 'https://www.bigboobbundle.com/bustyarianna', - network: 'score', - }, - { - name: 'Busty Danni Ashe', - slug: 'bustydanniashe', - url: 'https://www.bigboobbundle.com/bustydanniashe', - network: 'score', - }, - { - name: 'Busty Dusty Stash', - slug: 'bustydustystash', - url: 'https://www.bigboobbundle.com/bustydustystash', - network: 'score', - }, - { - name: 'Busty Ines Cudna', - slug: 'bustyinescudna', - url: 'https://www.bigboobbundle.com/bustyinescudna', - network: 'score', - }, - { - name: 'Busty Kelly Kay', - slug: 'bustykellykay', - url: 'https://www.bigboobbundle.com/bustykellykay', - network: 'score', - }, - { - name: 'Busty Kerry Marie', - slug: 'bustykerrymarie', - url: 'https://www.bigboobbundle.com/bustykerrymarie', - network: 'score', - }, - { - name: 'Busty Lorna Morgan', - slug: 'bustylornamorgan', - url: 'https://www.bigboobbundle.com/bustylornamorgan', - network: 'score', - }, - { - name: 'Busty Merilyn', - slug: 'bustymerilyn', - url: 'https://www.scorepass.com/bustymerilyn', - network: 'score', - }, - { - name: 'Busty Old Sluts', - slug: 'bustyoldsluts', - url: 'https://www.milfbundle.com/bustyoldsluts', - network: 'score', - }, - { - name: 'Busty Sammie Black', - slug: 'bustysammieblack', - url: 'https://www.bigboobbundle.com/bustysammieblack', - network: 'score', - }, - { - name: 'Cherry Brady', - slug: 'cherrybrady', - url: 'https://www.bigboobbundle.com/cherrybrady', - network: 'score', - }, - { - name: 'Chloes World', - slug: 'chloesworld', - url: 'https://www.scorepass.com/chloesworld', - network: 'score', - }, - { - name: 'Christy Marks', - slug: 'christymarks', - url: 'https://www.scorepass.com/christymarks', - network: 'score', - }, - { - name: 'Creampie for Granny', - slug: 'creampieforgranny', - url: 'https://www.milfbundle.com/creampieforgranny', - network: 'score', - }, - { - name: 'Crystal Gunns World', - slug: 'crystalgunnsworld', - url: 'https://www.bigboobbundle.com/crystalgunnsworld', - network: 'score', - }, - { - name: 'Daylene Rio', - slug: 'daylenerio', - url: 'https://www.bigboobbundle.com/daylenerio', - network: 'score', - }, - { - name: 'Desiraes World', - slug: 'desiraesworld', - url: 'https://www.bigboobbundle.com/desiraesworld', - network: 'score', - }, - { - name: 'Diane Poppos', - slug: 'dianepoppos', - url: 'https://www.bigboobbundle.com/dianepoppos', - network: 'score', - }, - { - name: 'Eva Notty Videos', - slug: 'evanottyvideos', - url: 'https://www.bigboobbundle.com/evanottyvideos', - network: 'score', - }, - { - name: 'Feed Her Fuck Her', - slug: 'feedherfuckher', - url: 'https://www.scorepass.com/feedherfuckher', - network: 'score', - }, - { - name: 'Flat And Fucked MILFs', - slug: 'flatandfuckedmilfs', - url: 'https://www.milfbundle.com/flatandfuckedmilfs', - network: 'score', - }, - { - name: 'Granny Gets A Facial', - slug: 'grannygetsafacial', - url: 'https://www.milfbundle.com/grannygetsafacial', - network: 'score', - }, - { - name: 'Granny Loves BBC', - slug: 'grannylovesbbc', - url: 'https://www.milfbundle.com/grannylovesbbc', - network: 'score', - }, - { - name: 'Granny Loves Young Cock', - slug: 'grannylovesyoungcock', - url: 'https://www.milfbundle.com/grannylovesyoungcock', - network: 'score', - }, - { - name: 'Home Alone MILFs', - slug: 'homealonemilfs', - url: 'https://www.milfbundle.com/homealonemilfs', - network: 'score', - }, - { - name: 'I Boned Your Mom', - slug: 'ibonedyourmom', - url: 'https://www.milfbundle.com/ibonedyourmom', - network: 'score', - }, - { - name: 'I Fucked the Boss', - slug: 'ifuckedtheboss', - url: 'https://www.milfbundle.com/ifuckedtheboss', - network: 'score', - }, - { - name: 'Jessica Turner', - slug: 'jessicaturner', - url: 'https://www.bigboobbundle.com/jessicaturner', - network: 'score', - }, - { - name: 'Joana Bliss', - slug: 'joanabliss', - url: 'https://www.bigboobbundle.com/joanabliss', - network: 'score', - }, - { - name: 'Julia Miles', - slug: 'juliamiles', - url: 'https://www.bigboobbundle.com/juliamiles', - network: 'score', - }, - { - name: 'Karina Hart', - slug: 'karinahart', - url: 'https://www.scorepass.com/karinahart', - network: 'score', - }, - { - name: 'Karla James', - slug: 'karlajames', - url: 'https://www.bigboobbundle.com/karlajames', - network: 'score', - }, - { - name: 'Leanne Crow Videos', - slug: 'leannecrowvideos', - url: 'https://www.bigboobbundle.com/leannecrowvideos', - network: 'score', - }, - { - name: 'Leg Sex', - slug: 'legsex', - url: 'https://www.legsex.com', - network: 'score', - }, - { - name: 'Linseys World', - slug: 'linseysworld', - url: 'https://www.scorepass.com/linseysworld', - network: 'score', - }, - { - name: 'Mega Tits Minka', - slug: 'megatitsminka', - url: 'https://www.bigboobbundle.com/megatitsminka', - network: 'score', - }, - { - name: 'Micky Bells', - slug: 'mickybells', - url: 'https://www.bigboobbundle.com/mickybells', - network: 'score', - }, - { - name: 'MILF Bundle', - slug: 'milfbundle', - url: 'https://www.milfbundle.com', - network: 'score', - show: false, - }, - { - name: 'Teaming Cock', - slug: 'milfthreesomes', - url: 'https://www.milfbundle.com/milfthreesomes', - network: 'score', - }, - { - name: 'MILF Tugs', - slug: 'milftugs', - url: 'https://www.milfbundle.com/milftugs', - network: 'score', - }, - { - name: 'Natalie Fiore', - slug: 'nataliefiore', - url: 'https://www.bigboobbundle.com/nataliefiore', - network: 'score', - }, - { - name: 'Naughty Footjobs', - slug: 'naughtyfootjobs', - url: 'https://www.scorepass.com/naughtyfootjobs', - network: 'score', - }, - { - name: 'Naughty Mag', - slug: 'naughtymag', - url: 'https://www.naughtymag.com', - network: 'score', - }, - { - name: 'Naughty Tugs', - slug: 'naughtytugs', - url: 'https://www.scorepass.com/naughtytugs', - network: 'score', - }, - { - name: 'Nicole Peters', - slug: 'nicolepeters', - url: 'https://www.bigboobbundle.com/nicolepeters', - network: 'score', - }, - { - name: 'Old Horny MILFs', - slug: 'oldhornymilfs', - url: 'https://www.milfbundle.com/oldhornymilfs', - network: 'score', - }, - { - name: 'Picking Up Pussy', - slug: 'pickinguppussy', - url: 'https://www.scorepass.com/pickinguppussy', - network: 'score', - }, - { - name: 'Porn Loser', - slug: 'pornloser', - url: 'https://www.scorepass.com/pornloser', - network: 'score', - }, - { - name: 'Porn Mega Load', - slug: 'pornmegaload', - url: 'https://www.pornmegaload.com', - network: 'score', - show: false, - }, - { - name: 'SaRennas World', - slug: 'sarennasworld', - url: 'https://www.bigboobbundle.com/sarennasworld', - network: 'score', - }, - { - name: 'Scoreland', - slug: 'scoreland', - url: 'https://www.scoreland.com', - network: 'score', - parameters: { path: '/big-boob-videos' }, - priority: 3, - }, - { - name: 'Scoreland2', - slug: 'scoreland2', - url: 'https://www.scoreland2.com', - network: 'score', - parameters: { path: '/big-boob-scenes' }, - priority: 1, - }, - { - name: 'Score Classics', - slug: 'scoreclassics', - url: 'https://www.scoreclassics.com', - network: 'score', - parameters: { path: '/classic-boob-videos' }, - priority: 1, - }, - { - name: 'Scoreland TV', - slug: 'scorelandtv', - url: 'https://www.scorepass.com/scorelandtv', - network: 'score', - priority: 1, - show: false, // appears to be streaming service for other sites - }, - { - name: 'ScoreTV', - slug: 'scoretv', - url: 'https://www.scoretv.tv', - network: 'score', - priority: 1, - show: false, // similar to or same as Scoreland TV - }, - { - name: 'Score Videos', - slug: 'scorevideos', - url: 'https://www.scorevideos.com', - network: 'score', - parameters: { path: '/porn-videos' }, - priority: 2, - }, - { - name: 'Sha Rizel Videos', - slug: 'sharizelvideos', - url: 'https://www.bigboobbundle.com/sharizelvideos', - network: 'score', - }, - { - name: 'Silver Sluts', - slug: 'silversluts', - url: 'https://www.milfbundle.com/silversluts', - network: 'score', - }, - { - name: 'Stacy Vandenberg Boobs', - slug: 'stacyvandenbergboobs', - url: 'https://www.bigboobbundle.com/stacyvandenbergboobs', - network: 'score', - }, - { - name: 'Susie Wildin', - slug: 'susiewildin', - url: 'https://www.bigboobbundle.com/susiewildin', - network: 'score', - }, - { - name: 'Tawny Peaks', - slug: 'tawnypeaks', - url: 'https://www.bigboobbundle.com/tawny-peaks', - network: 'score', - }, - { - name: 'Tiffany Towers', - slug: 'tiffanytowers', - url: 'https://www.bigboobbundle.com/tiffany-towers', - network: 'score', - }, - { - name: 'Tits And Tugs', - slug: 'titsandtugs', - url: 'https://www.scorepass.com/titsandtugs', - network: 'score', - }, - { - name: 'TNA Tryouts', - slug: 'tnatryouts', - url: 'https://www.scorepass.com/tnatryouts', - network: 'score', - }, - { - name: 'Valory Irene', - slug: 'valoryirene', - url: 'https://www.bigboobbundle.com/valoryirene', - network: 'score', - }, - { - name: 'XL Girls', - slug: 'xlgirls', - url: 'https://www.xlgirls.com', - network: 'score', - }, - { - name: 'Your Mom Loves Anal', - slug: 'yourmomlovesanal', - url: 'https://www.milfbundle.com/yourmomlovesanal', - network: 'score', - }, - { - name: 'Your Mom\'s Got Big Tits', - slug: 'yourmomsgotbigtits', - url: 'https://www.milfbundle.com/yourmomsgotbigtits', - network: 'score', - }, - { - name: 'Your Wife My Meat', - slug: 'yourwifemymeat', - url: 'https://www.milfbundle.com/yourwifemymeat', - network: 'score', - }, - // SEXY HUB - { - slug: 'danejones', - name: 'Dane Jones', - alias: ['dnj'], - url: 'https://www.danejones.com/', - parameters: { siteId: 290 }, - network: 'sexyhub', - }, - { - slug: 'lesbea', - name: 'Lesbea', - alias: ['lsb'], - url: 'https://www.lesbea.com', - parameters: { siteId: 291 }, - tags: ['lesbian'], - network: 'sexyhub', - }, - { - slug: 'massagerooms', - name: 'Massage Rooms', - alias: ['mrs'], - url: 'https://www.sexyhub.com/scenes?site=292', - tags: ['massage'], - network: 'sexyhub', - }, - { - slug: 'momxxx', - name: 'Mom XXX', - alias: ['mom'], - url: 'https://www.sexyhub.com/scenes?site=293', - tags: ['milf'], - network: 'sexyhub', - }, - { - slug: 'fitnessrooms', - name: 'Fitness Rooms', - alias: ['frs'], - url: 'https://www.sexyhub.com/scenes?site=294', - network: 'sexyhub', - }, - { - slug: 'girlfriends', - name: 'Girlfriends', - url: 'https://www.sexyhub.com/scenes?site=289', - tags: ['lesbian'], - network: 'sexyhub', - }, - // TEAM SKEET - { - slug: 'exxxtrasmall', - name: 'Exxxtra Small', - alias: ['ext'], - description: '', - url: 'https://www.exxxtrasmall.com', - parameters: { id: 'exs' }, - network: 'teamskeet', - }, - { - slug: 'teenpies', - name: 'Teen Pies', - description: '', - url: 'https://www.teenpies.com', - parameters: { id: 'tp' }, - network: 'teamskeet', - }, - { - slug: 'innocenthigh', - name: 'Innocent High', - alias: ['inh'], - description: '', - url: 'https://www.innocenthigh.com', - parameters: { id: 'ih' }, - network: 'teamskeet', - }, - { - slug: 'teencurves', - name: 'Teen Curves', - description: '', - url: 'https://www.teencurves.com', - parameters: { id: 'tc' }, - network: 'teamskeet', - }, - { - slug: 'cfnmteens', - name: 'CFNM Teens', - alias: ['cfnmt'], - url: 'https://www.cfnmteens.com', - parameters: { id: 'cfnm' }, - network: 'teamskeet', - }, - { - slug: 'teensloveanal', - name: 'Teens Love Anal', - alias: ['tla'], - url: 'https://www.teensloveanal.com', - tags: ['anal'], - parameters: { id: 'tla' }, - network: 'teamskeet', - }, - { - slug: 'mybabysittersclub', - name: 'My Babysitters Club', - description: '', - url: 'https://www.mybabysittersclub.com', - parameters: { id: 'bsc' }, - network: 'teamskeet', - }, - { - slug: 'shesnew', - name: 'She\'s New', - alias: ['ssn'], - url: 'https://www.shesnew.com', - parameters: { id: 'bsc' }, - network: 'teamskeet', - }, - { - slug: 'teensdoporn', - name: 'Teens Do Porn', - alias: ['tdp'], - url: 'https://www.teensdoporn.com', - parameters: { id: 'tdp' }, - network: 'teamskeet', - }, - { - slug: 'povlife', - name: 'POV Life', - description: '', - url: 'https://www.povlife.com', - parameters: { id: 'pov' }, - network: 'teamskeet', - }, - { - slug: 'therealworkout', - name: 'The Real Workout', - description: '', - url: 'https://www.therealworkout.com', - parameters: { id: 'trw' }, - network: 'teamskeet', - }, - { - slug: 'thisgirlsucks', - name: 'This Girl Sucks', - alias: ['tgs'], - description: '', - url: 'https://www.thisgirlsucks.com', - parameters: { id: 'tgs' }, - network: 'teamskeet', - }, - { - slug: 'teenslovemoney', - name: 'Teens Love Money', - alias: ['tlm'], - description: '', - url: 'https://www.teenslovemoney.com', - parameters: { id: 'tlm' }, - network: 'teamskeet', - }, - { - slug: 'oyeloca', - name: 'Oye Loca', - description: '', - url: 'https://www.oyeloca.com', - parameters: { id: 'ol' }, - network: 'teamskeet', - }, - { - slug: 'tittyattack', - name: 'Titty Attack', - description: '', - url: 'https://www.tittyattack.com', - parameters: { id: 'ta' }, - network: 'teamskeet', - }, - { - slug: 'teenyblack', - name: 'Teeny Black', - description: '', - url: 'https://www.teenyblack.com', - parameters: { id: 'tb' }, - network: 'teamskeet', - }, - { - slug: 'lusthd', - name: 'Lust HD', - description: '', - url: 'https://www.lusthd.com', - parameters: { id: 'lhd' }, - network: 'teamskeet', - }, - { - slug: 'rubateen', - name: 'Rub A Teen', - description: '', - url: 'https://www.rubateen.com', - parameters: { id: 'rat' }, - network: 'teamskeet', - }, - { - slug: 'herfreshmanyear', - name: 'Her Freshman Year', - description: '', - url: 'https://www.exxxtrasmall.com', - parameters: { id: 'hfy' }, - network: 'teamskeet', - }, - { - slug: 'selfdesire', - name: 'Self Desire', - description: '', - url: 'https://www.selfdesire.com', - parameters: { id: 'sd' }, - network: 'teamskeet', - }, - { - slug: 'solointerviews', - name: 'Solo Interviews', - description: '', - url: 'https://www.solointerviews.com', - parameters: { id: 'si' }, - network: 'teamskeet', - }, - { - slug: 'teamskeetextras', - name: 'Team Skeet Extras', - description: '', - url: 'https://www.teamskeetextras.com', - parameters: { id: 'tse' }, - network: 'teamskeet', - }, - { - slug: 'dyked', - name: 'Dyked', - description: '', - url: 'https://www.dyked.com', - parameters: { id: 'dyk' }, - network: 'teamskeet', - }, - { - slug: 'badmilfs', - name: 'Bad MILFs', - description: '', - url: 'https://www.badmilfs.com', - parameters: { id: 'bad' }, - network: 'teamskeet', - }, - { - slug: 'gingerpatch', - name: 'Ginger Patch', - description: '', - url: 'https://www.gingerpatch.com', - parameters: { id: 'gp' }, - network: 'teamskeet', - }, - { - slug: 'bracefaced', - name: 'Brace Faced', - description: '', - url: 'https://www.bracefaced.com', - parameters: { id: 'bfd' }, - network: 'teamskeet', - }, - { - slug: 'teenjoi', - name: 'Teen JOI', - description: '', - url: 'https://www.teenjoi.com', - parameters: { id: 'joi' }, - network: 'teamskeet', - }, - { - slug: 'stepsiblings', - name: 'Step Siblings', - alias: ['steps'], - url: 'https://www.stepsiblings.com', - parameters: { id: 'sss' }, - network: 'teamskeet', - }, - { - slug: 'submissived', - name: 'Submissived', - description: '', - url: 'https://www.submissived.com', - tags: ['bdsm'], - parameters: { scraper: 'A' }, - network: 'teamskeet', - }, - { - slug: 'familystrokes', - name: 'Family Strokes', - alias: ['fams'], - url: 'https://www.familystrokes.com', - parameters: { scraper: 'A' }, - tags: ['family'], - network: 'teamskeet', - }, - // TWISTYS - { - name: 'Twistys', - slug: 'twistys', - url: 'https://www.twistys.com/scenes?site=219', - description: 'Twistys.com is the #1 ranked babe site on the web! With over 10 years of photos and videos, updated daily with 3 HQ photo sets and 2 HD videos, Twistys also awards one hot babe a Treat of the Month title, complete with exclusive photo & video sets! Weekly updates!', - network: 'twistys', - priority: 1, - }, - { - name: 'When Girls Play', - slug: 'whengirlsplay', - alias: ['wgp'], - url: 'https://www.whengirlsplay.com', - description: 'Watch hot girls seducing other girls in steamy lesbian play. These sluts finger, use dildos, strap-ons and squirt their pink pussies in lesbian porn by WhenGirlsPlay.com. Get Access to the Hottest Lesbian videos on the web!', - parameters: { siteId: 227 }, - network: 'twistys', - priority: 1, - }, - { - name: 'Turning Twistys', - slug: 'turningtwistys', - url: 'https://www.twistys.com/scenes?site=302', - description: 'Where straight curious cuties explore their sexuality, and get seduced by sexy butch girls. These sneaky lesbians lure the innocent looking beauties away from their boyfriends and into their beds... Be careful or they might just steal your girl!', - network: 'twistys', - priority: 1, - }, - { - name: 'Mom Knows Best', - slug: 'momknowsbest', - url: 'https://www.momknowsbest.com', - description: 'The world’s tightest teens and most elegant MILFs get together at Mom Knows Best, where horny teen girls learn the ins and outs of lesbian sex from confident and beautiful older women. These MILFs know what they like and exactly how to get it, and lucky for them, these tasty teens are eager to learn, and always very eager to please!', - parameters: { siteId: 234 }, - network: 'twistys', - priority: 1, - }, - { - name: 'Twistys Hard', - slug: 'twistyshard', - alias: ['th'], - url: 'https://www.twistyshard.com', - description: 'Watch horny nymphos get stuffed with stiff, bulging cocks. Hot sluts eager to spread their legs, bend over on all fours, or mount a big rock-hard erection. They want their needs fulfilled, and love to show off how they do it. Get into Twistys Hard and see just how hard things can get!', - parameters: { siteId: 225 }, - network: 'twistys', - priority: 1, - }, - { - name: 'Feature Films', - slug: 'featurefilms', - url: 'https://www.twistys.com/scenes?site=233', - description: 'Prepare yourself for a night at the movies you\'ll never forget with Feature Films from Twistys. High-end cinematic productions featuring in-depth storylines and industry-leading visuals, erotic adventures with the most beautiful women in the world are now at your fingertips. Lesbian, Hardcore Bonus Updates!', - network: 'twistys', - }, - { - name: 'Nicole Graves', - slug: 'nicolegraves', - url: 'https://www.twistys.com/scenes?site=223', - description: 'NicoleGraves.com is the only official web site of Nicole Graves with 100% EXCLUSIVE content! Here you\'ll find Nicole Graves videos and photos of her shaved pussy and Nicole Graves fucking and giving a blowjob!', - network: 'twistys', - }, - { - name: 'Anette Dawn', - slug: 'anettedawn', - url: 'https://www.twistys.com/scenes?site=221', - description: 'Hey guys! Its me Anette Dawn, if you\'ve seen me on other sites, like Twistys I have been doing internet modeling for a while now. This is however my first and only official site. I recieved so many requests for more so I finally got this site together! I can\'t wait for you to join me inside!', - network: 'twistys', - }, - { - name: 'Twistys Teasers', - slug: 'twistysteasers', - url: 'https://www.twistys.com/scenes?site=232', - description: 'Twistys Teasers is a doorway to new exciting content, opened exclusively to you, our loyal members. See it here first while it’s fresh and hot, and be sure to let us know exactly how much you like it before you leave. Be tempted. Be tantalized. Be teased. Solo, Lesbian, Hardcore Bonus Updates!', - network: 'twistys', - }, - { - name: 'Euro Foxes', - slug: 'eurofoxes', - url: 'https://www.twistys.com/scenes?site=226', - description: 'EuroFoxes.com: the worlds Number One European Babe site! EuroFoxes is dedicated to bringing you the very best european babes!', - network: 'twistys', - }, - { - name: 'Blue Fantasies', - slug: 'bluefantasies', - url: 'https://www.twistys.com/scenes?site=220', - description: 'BlueFantasies.com prides itself on getting the most beautiful women in the world to show off their hot bodies, 100% exclusively for you.', - network: 'twistys', - }, - { - name: 'Busty Ones', - slug: 'bustyones', - url: 'https://www.twistys.com/scenes?site=229', - description: 'BustyOnes.com bringing you the most beautiful big breasts in the world! The hottest women alive showcasing their fantastic tits.', - network: 'twistys', - }, - // VIVID - { - slug: 'vividceleb', - name: 'Vivid Celeb', - url: 'https://www.vividceleb.com', - network: 'vivid', - parameters: { - referer: 'https://www.thebrats.com', - deep: 'https://www.thebrats.com/en/video', - scene: false, - lastNative: new Date('2018-03-25'), - }, - }, - { - slug: 'thebrats', - name: 'The Brats', - url: 'https://www.thebrats.com', - network: 'vivid', - }, - { - slug: 'wheretheboysarent', - name: 'Where The Boys Aren\'t', - url: 'https://www.wheretheboysarent.com', - network: 'vivid', - }, - { - slug: 'nineteen', - name: 'Nineteen', - url: 'http://www.nineteen.com', - network: 'vivid', - parameters: { - referer: 'https://www.thebrats.com', - deep: 'https://www.thebrats.com/en/video', - scene: false, - lastNative: new Date('2019-01-23'), - }, - }, - { - slug: 'nastystepfamily', - name: 'Nasty Step Family', - url: 'http://www.nastystepfamily.com', - network: 'vivid', - parameters: { - referer: 'https://www.thebrats.com', - deep: 'https://www.thebrats.com/en/video', - scene: false, - lastNative: new Date('2019-01-29'), - }, - }, - { - slug: 'girlswhofuckgirls', - name: 'Girls Who Fuck Girls', - url: 'http://www.girlswhofuckgirls.com', - network: 'vivid', - parameters: { - referer: 'https://www.thebrats.com', - deep: 'https://www.thebrats.com/en/video', - scene: false, - lastNative: new Date('2019-05-21'), - }, - }, - { - slug: 'petited', - name: 'Petited', - url: 'http://www.petited.com', - network: 'vivid', - parameters: { - referer: 'https://www.thebrats.com', - deep: 'https://www.thebrats.com/en/video', - scene: false, - lastNative: new Date('2019-01-28'), - }, - }, - { - slug: 'orgytrain', - name: 'Orgy Train', - url: 'http://www.orgytrain.com', - network: 'vivid', - parameters: { - referer: 'https://www.thebrats.com', - deep: 'https://www.thebrats.com/en/video', - scene: false, - lastNative: new Date('2019-01-09'), - }, - }, - { - slug: 'momisamilf', - name: 'Mom Is A MILF', - url: 'http://www.momisamilf.com', - network: 'vivid', - parameters: { - referer: 'https://www.thebrats.com', - deep: 'https://www.thebrats.com/en/video', - scene: false, - lastNative: new Date('2019-01-25'), - }, - }, - { - slug: 'blackwhitefuckfest', - name: 'Black White Fuck Fest', - url: 'http://www.blackwhitefuckfest.com', - network: 'vivid', - parameters: { - referer: 'https://www.thebrats.com', - deep: 'https://www.thebrats.com/en/video', - scene: false, - lastNative: new Date('2019-01-30'), - }, - }, - { - slug: '65inchhugeasses', - name: '65 Inch Huge Asses', - url: 'http://www.65inchhugeasses.com', - network: 'vivid', - parameters: { - referer: 'https://www.thebrats.com', - deep: 'https://www.thebrats.com/en/video', - scene: false, - lastNative: new Date('2019-05-18'), - }, - }, - { - slug: 'brandnewfaces', - name: 'Brand New Faces', - url: 'http://www.brandnewfaces.com', - network: 'vivid', - parameters: { - referer: 'https://www.thebrats.com', - deep: 'https://www.thebrats.com/en/video', - scene: false, - lastNative: new Date('2018-02-28'), - }, - }, - { - slug: 'vividclassic', - name: 'Vivid Classic', - url: 'http://www.vividclassic.com', - network: 'vivid', - parameters: { - referer: 'https://www.thebrats.com', - deep: 'https://www.thebrats.com/en/video', - scene: false, - lastNative: new Date('2016-06-29'), - }, - }, - // VIXEN - { - slug: 'vixen', - name: 'Vixen', - description: 'Vixen.com features the world’s finest cinematic adult films with 4K quality and high-end erotic photography.', - url: 'https://www.vixen.com', - network: 'vixen', - }, - { - slug: 'blacked', - name: 'Blacked', - description: 'Porn videos of beautiful girls in first time interracial porn videos. BLACKED has the hottest pornstars in HD sex videos.', - url: 'https://www.blacked.com', - tags: ['interracial', 'bbc'], - network: 'vixen', - }, - { - slug: 'tushy', - name: 'Tushy', - description: 'Watch the world\'s best HD Anal videos! Featuring beautiful, never before seen girls in first time anal. Exclusively on Tushy.com', - url: 'https://www.tushy.com', - tags: ['anal'], - network: 'vixen', - }, - { - slug: 'blackedraw', - name: 'Blacked Raw', - description: 'Experience real women in interracial sex videos. Passionate sex with beautiful pornstars. No photoshop just the highest quality porn. Everything you see is real.', - url: 'https://www.blackedraw.com', - tags: ['interracial', 'bbc'], - network: 'vixen', - }, - { - slug: 'tushyraw', - name: 'Tushy Raw', - description: 'Anal sex videos with beautiful models and pornstars being fucked in the ass. TUSHY RAW features famous pornstars in high quality anal porn videos.', - url: 'https://www.tushyraw.com', - tags: ['anal'], - network: 'vixen', - }, - { - slug: 'deeper', - name: 'Deeper', - description: 'Porn videos from DEEPER.com featuring Passionate sex, light kink and BDSM with plenty of erotic sex videos featuring beautiful models.', - url: 'https://www.deeper.com', - network: 'vixen', - }, - // VOGOV - { - slug: 'vogov', - name: 'VogoV', - url: 'https://www.vogov.com', - description: 'Top rated models. Graceful locations. Best gonzo scenes. 4K UHD 60 FPS. So, in general Vogov is a website that is worth visiting and exploring carefully. It gives a chance to spend a fantastic night with gorgeous girls ready to experiment and to full around with their lovers.', - network: 'vogov', - }, - // WHALE MEMBER - { - name: 'Cum 4K', - slug: 'cum4k', - url: 'https://cum4k.com', - tags: ['fake-cum', 'creampie', '4k'], - network: 'whalemember', - }, - { - name: 'Tiny 4K', - slug: 'tiny4k', - url: 'https://tiny4k.com', - tags: ['4k'], - network: 'whalemember', - }, - { - name: 'POVD', - slug: 'povd', - url: 'https://povd.com', - tags: ['pov'], - network: 'whalemember', - }, - { - name: 'Lubed', - slug: 'lubed', - url: 'https://lubed.com', - tags: ['oil'], - network: 'whalemember', - }, - { - name: 'Casting Couch X', - slug: 'castingcouchx', - alias: ['castingcouch x', 'castingcouch-x', 'casting couch-x'], - url: 'https://castingcouch-x.com', - network: 'whalemember', - }, - { - name: 'Passion HD', - slug: 'passionhd', - alias: ['phd', 'passion-hd'], - url: 'https://passion-hd.com', - network: 'whalemember', - }, - { - name: 'Nanny Spy', - slug: 'nannyspy', - url: 'https://nannyspy.com', - network: 'whalemember', - }, - { - name: 'Girl Cum', - slug: 'girlcum', - url: 'https://girlcum.com', - network: 'whalemember', - }, - { - name: 'Pure Mature', - slug: 'puremature', - url: 'https://puremature.com', - tags: ['milf'], - network: 'whalemember', - }, - { - name: 'Fantasy HD', - slug: 'fantasyhd', - alias: ['fhd'], - url: 'https://fantasyhd.com', - network: 'whalemember', - }, - { - name: 'Spy Fam', - slug: 'spyfam', - url: 'https://spyfam.com', - tags: ['family'], - network: 'whalemember', - }, - { - name: 'Holed', - slug: 'holed', - url: 'https://holed.com', - tags: ['anal'], - network: 'whalemember', - }, - { - name: 'BBC Pie', - slug: 'bbcpie', - url: 'https://bbcpie.com', - tags: ['bbc', 'interracial'], - network: 'whalemember', - }, - { - name: 'Wet VR', - slug: 'wetvr', - url: 'https://wetvr.com', - tags: ['virtual-reality'], - network: 'whalemember', - }, - { - name: 'Exotic 4K', - slug: 'exotic4k', - url: 'https://exotic4k.com', - tags: ['4k'], - network: 'whalemember', - }, - { - name: 'My Very First Time', - slug: 'myveryfirsttime', - alias: ['mvft'], - url: 'https://myveryfirsttime.com', - network: 'whalemember', - }, - { - name: 'Baeb', - slug: 'baeb', - alias: ['baebz'], - url: 'https://baeb.com', - network: 'whalemember', - }, - // WICKED - { - slug: 'wicked', - name: 'Wicked', - alias: ['wkp'], - url: 'https://www.wicked.com', - description: 'Welcome to the new Wicked.com! Watch over 25 years of Wicked Pictures\' brand of award-winning porn for couples and women in 4k HD movies & xxx videos', - parameters: { independent: true }, - network: 'wicked', - }, - // XEMPIRE - { - slug: 'hardx', - name: 'HardX', - description: "Welcome to HardX.com, home of exclusive hardcore gonzo porn and first time anal scenes, DP, blowbangs and gangbangs from today's hottest porn stars!", - url: 'https://www.hardx.com', - network: 'xempire', - }, - { - slug: 'eroticax', - name: 'EroticaX', - description: 'EroticaX.com features intimate scenes of passionate, erotic sex. Watch the sensual side of hardcore porn as your favorite pornstars have real, intense orgasms.', - url: 'https://www.eroticax.com', - network: 'xempire', - }, - { - slug: 'darkx', - name: 'DarkX', - description: 'Watch interracial BBC porn videos on DarkX.com, featuring the best pornstars taking big black cock in exclusive scenes. The best black on white porn inside!', - url: 'https://www.darkx.com', - tags: ['interracial'], - network: 'xempire', - }, - { - slug: 'allblackx', - name: 'AllBlackX', - description: 'AllBlackX.com features the hottest ebony pornstar beauties in hardcore black on black gonzo porn. From director Mason, watch 4k ultra HD videos inside', - url: 'https://www.allblackx.com', - network: 'xempire', - tags: ['ebony', 'bbc'], - }, - { - slug: 'lesbianx', - name: 'LesbianX', - description: "LesbianX.com features today's top pornstars in hardcore lesbian porn. Watch passionate & intense girl on girl sex videos, from erotic kissing to pussy licking.", - url: 'https://www.lesbianx.com', - tags: ['lesbian'], - network: 'xempire', - }, + // REALITY KINGS + { + name: 'Look At Her Now', + url: 'https://www.lookathernow.com', + description: 'Look At Her Now brings you best HD reality porn videos every week. Check out these girls before and after they get some rough pounding.', + parameters: { native: true }, + // parameters: { siteId: 300 }, + slug: 'lookathernow', + network: 'realitykings', + }, + { + name: 'We Live Together', + slug: 'welivetogether', + alias: ['wlt'], + url: 'https://www.welivetogether.com', + description: "We are girls that love to eat pussy and We Live Together! Every week we go out on the streets, bars, parties, malls... wherever and we pick up the cutest lesbians and invite them to come over and party at our apartment. From our girl friends at college, to roommates, and friends of friends.. we're always looking for the hottest lesbian girls around! We Live Together has hundreds of lesbian videos for you to download right from Reality Kings... it's the sexiest lesbian porn anywhere guys and gals! :-) Come watch us eat pussy and work our dildo magic on gorgeous, sexy girls. We love to get together and get off in steamy hot threesome and foursome lesbian movies! We promise you're going to love our amazing collection of lesbian porn. Thanks for dropping in to the We Live Together Apartment, hope you enjoy your visit! Love xoxo Brittney, Taylor, Nicole & All the Girls", + parameters: { siteId: 3 }, + network: 'realitykings', + }, + { + name: 'Black GFs', + slug: 'blackgfs', + alias: ['bgfs'], + url: 'https://www.realitykings.com/scenes?site=47', + description: '', + parameters: null, + network: 'realitykings', + }, + { + name: 'Dare Dorm', + url: 'https://www.daredorm.com', + description: '', + parameters: { siteId: 48 }, + slug: 'daredorm', + network: 'realitykings', + }, + { + name: 'GF Revenge', + slug: 'gfrevenge', + alias: ['gfr'], + url: 'https://www.gfrevenge.com', + description: '', + parameters: { siteId: 49 }, + network: 'realitykings', + }, + { + name: 'Horny Birds', + url: 'https://www.realitykings.com/scenes?site=50', + description: '', + parameters: null, + slug: 'hornybirds', + network: 'realitykings', + }, + { + name: 'Crazy College GFs', + url: 'https://www.realitykings.com/scenes?site=51', + description: '', + parameters: null, + slug: 'crazycollegegfs', + network: 'realitykings', + }, + { + name: 'Crazy Asian GFs', + url: 'https://www.crazyasiangfs.com', + description: '', + parameters: { siteId: 52 }, + slug: 'crazyasiangfs', + network: 'realitykings', + }, + { + name: 'Teens Love Huge Cocks', + url: 'https://www.teenslovehugecocks.com', + alias: ['tlhc'], + description: "Teens Love Big Cocks is dedicated to providing you the hottest teens getting fucked by the biggest cocks! Every week Reality Kings introduces another teen to a big hot meat rod! When these girls see a big throbbing penis they can't resist shoving it in their hot teen mouths. These girl next door types are no slouches when it comes to oral sex! Watch them deepthroat & gag on a mouth full of cock before taking big hot loads all over their pretty faces. The fun doesn't stop there! These girls love getting their tight teen pussy & asses spread wide and pounded by massive dicks! These girls won't settle for less & there is no dick too large. Start downloading TeensLoveBigCock porn videos & HD quality pictures now and watch teen pussy get fucked like you've never seen before!", + parameters: { siteId: 42 }, + slug: 'teenslovehugecocks', + network: 'realitykings', + }, + { + name: 'Big Naturals', + url: 'https://www.bignaturals.com', + alias: ['bin'], + description: "If you think there is nothing like big natural breasts, Big Naturals welcomes you home. Reality Kings brings you nothing but the hottest amateur big tit women. We're talking about some seriously big boobs. Sexy women with big bouncy tits who love to get it on. These women don't hesitate to let their big natural tits get fucked and let those massive juggs bounce! Big Naturals has hundreds of high quality videos available for download. If into tits, this is the place to be. There's no plastic parts here, only big natural boobs! There's thousands of high resolution pics available to download as well. Check out any of our top rated scenes for the biggest, huge natural tits. Hooters, fun bags, juggs... whatever you want to call them Reality Kings and Big Naturals have the hottest big boobs you'll find anywhere. Sit back, relax, and watch the titties bounce... Reality Kings style!", + parameters: { siteId: 5 }, + slug: 'bignaturals', + network: 'realitykings', + }, + { + name: 'Money Talks', + slug: 'moneytalks', + alias: ['mot'], + url: 'https://www.moneytalks.com', + description: "Money Talks... bullshit walks. We all know the saying, but at Reality Kings we like to prove it! Just watch us approach everyday people on the street and ask them what they will do for some real American Greenbacks! Check out smokin' hot amateurs preform in porn videos or watch crazy college kids preform insane stunts on film... all in exchange for cold hard cash. People will do anything when Money Talks! Watch as we offer cash in exchange for one, AMAZING blow job! From crazy Spring Breakers to the girl next door, we find some amazing sluts and see just what they'll do for the loot--girls that give up the booty, for the booty! Arrr! Reality Kings has every high quality Money Talks episode available for download. We're talking about some seriously hot videos here. You won't find this crazy porn content anywhere else! Remember, Money Talks... bullshit walks!", + parameters: { siteId: 28 }, + network: 'realitykings', + }, + { + name: 'Moms Lick Teens', + url: 'https://www.momslickteens.com', + alias: ['momslickteens'], + description: 'Hot moms know how to fuck, especially when they have a lot of pent up energy. MomsLickTeens.com is where all the magic happens between lustful milf minxes and curious 18+ teen bombshells in HD porn videos. Mature horny women love to sample a fresh batch of pussy and ass whenever possible here at Reality Kings. They love teaching the carnal arts to eager younger women who crave a deeper understanding of the female body. Our bodacious mommies love exploring the anatomy of their fresh-faced lesbian lovers and engage in cunnilingus and anilingus within seconds. Naked women licking, sucking, scissoring, and toying their gaping pussy and assholes with a plethora of adult toys is absolutely riveting to watch. You’ll be aroused by RK girls of different ages rolling around together in sweaty sex scenes. Moms Lick Teens features limber tongues exploring the deepest recesses of female erogenous zones often eliciting projectile squirt orgasms. The phenomenon of female ejaculation occurs regularly in our premium erotica so get a load of it while blowing your own load to our buxom mommies today!', + parameters: { siteId: 43 }, + slug: 'momslickteens', + network: 'realitykings', + }, + { + name: 'RK Prime', + slug: 'rkprime', + alias: ['rkp'], + url: 'https://www.realitykings.com/scenes?site=45', + parameters: null, + network: 'realitykings', + }, + { + name: 'Milf Hunter', + url: 'https://www.milfhunter.com', + description: "Reality Kings presents MILF Hunter the ORIGINAL reality porn site dedicated to MILFs and mature sex content. If you don't know what a MILF is, allow us to explain... we're talking about sex starved, smokin' hot moms that are in need of a little attention--a MILF, a Mother I'd Like to Fuck! We've all seen these moms at the mall, the beach, and around town. Watch every week as the Hunter captures another hottie on film and gives them what they've been craving... some dick! These moms are seriously hot MILFs and they appear in the most incredible high quality pics and movies! We have hundreds of mature porn videos available for you to download. Or if you're looking for photos we have thousands of high resolution MILF porn pics directly from the MILF Hunter! Reality Kings brings you the best mature sex scenes around so why not join the MILF Hunter hunt down mature moms across America...", + parameters: { siteId: 2 }, + slug: 'milfhunter', + network: 'realitykings', + }, + { + name: 'Pure 18', + url: 'https://www.realitykings.com/scenes?site=31', + description: 'There\'s a lot of stuff out there that claims to be "pure", from spring water to gold chains, who knows what\'s actually legit$2 Reality Kings presents Pure 18, legit, 100% verified 18 year old sex scenes--no bullshit, only incredible 18 year old girls! These hot girls are the real deal, barely legal, smokin\' hot babes looking for some fun. Don\'t let their age fool you, these chicks know how to work a cock. Tight pussies and tight asses, the finest sex scenes around, that\'s what Pure 18 is all about! If you love watching amazing blow jobs, you\'re going to love this content. Download hundreds of high quality videos and pics featuring 100% verified 18 year old sex! These cuties are not shy about sex. Watch them take on monster cocks and love every minute of it. Pure 18, legit, verified, real 18 year old girls hungry for a cock meat sandwich!', + parameters: null, + slug: 'pure18', + network: 'realitykings', + }, + { + name: 'Bad Tow Truck', + url: 'https://www.realitykings.com/scenes?site=44', + description: 'Driving a tow truck is hard work. Especially when clients have a tough time paying! At BadTowTruck.com we alleviate that problem for sexy female damsels in distress by offering them different “payment” options. When big tit babes need a boost but are tight on cash, our drivers are more than happy to boost their tight asses up and give them a deep dicking. The chance to unleash a creamy internal cumshot should not be missed here at Reality Kings! Enjoy HD porn videos full of stranded sirens who are all too happy to get naked and oblige their rescuer with a gagging BJ. Anal riding is not far behind as our buxom RK divas love to get their bumpers shined. Bad Tow Truck is home to monster cocks getting gobbled up by very appreciative clients whose cars have broken down. They love swallowing every drop of jizz in the front or back seat of the tow truck. Anything goes here as our tantalizing teasers can contort their agile bodies into any number of sexual positions in tight spaces. They require maximal torque and horsepower in their erotic escapades so watch them get out of a jam with the help of a helpful towing guy, and then get jammed by him!', + parameters: null, + slug: 'badtowtruck', + network: 'realitykings', + }, + { + name: 'Cum Fiesta', + url: 'https://www.cumfiesta.com', + alias: ['cuf'], + description: '"Is this the fiesta $4 " Of course, welcome to the Cum Fiesta! Every week Reality Kings welcomes another hottie to the party... the dick sucking party! :-) When the girl (or girls!) arrive they show us the super secret password (watch a trailer to find out) and the party begins! As their clothes come off, these babes begin to show off their amazing oral skills. Amateur chicks taking the biggest facial cumshots, that\'s what Cum Fiesta is about! There are no pansy blow jobs here, these chicks give the best head around, and take huge cum shots to their face at the same time. Download hundreds of high quality videos and pics of semen swallowing hotties. Join the party that features amateurs, newbies, and even the girl next door! These babes love to suck cock and take incredible cum shots you\'ll find no where else! This is no siesta folks, it\'s a Cum Fiesta!', + parameters: { siteId: 10 }, + slug: 'cumfiesta', + network: 'realitykings', + }, + { + name: '8th Street Latinas', + url: 'https://www.8thstreetlatinas.com', + description: "Scientists say that the sun is what makes the temperature rise south of the equator, but we think its the women! If you haven't seen the chicas (women) from 8th Street Latinas, you're in for a spicy treat. From the famous Calle 8 (8th Street) in Miami, Reality Kings brings you the some incredibly hot latinas! We're talking Cubans, Dominicans, Panamanians, and other sexy latinas from South and Central America. These babes aim to please with their tanned bodies and deliciously round asses. Damn, we can't get enough! 8th Street Latinas has some caliente (hot) content for you to download, featuring the hottest latina sex scenes around. These are not tanned white chicks, these are REAL, hot latinas who know how to get your blood pumping. If you think you can handle the heat, grab your pair of shorts and flip-flops and let's head to Miami... 8th Street Latinas has some incredible latina porn for you!", + parameters: { siteId: 1 }, + slug: '8thstreetlatinas', + network: 'realitykings', + }, + { + name: "Mike's Apartment", + url: 'http://www.mikesapartment.com', + description: 'There\'s a room for rent in Mikes Apartment and Mike has found another hot chick to fill the vacancy! Join Mike on his search for roommates to help pay the bills. If these hot euro babes don\'t have the money, that\'s alright, Mike offers them the room in exchange for a few hours of masturbation and fun! And if the girl is traveling with a companion, thats not a problem... Mike just creates another steamy euro sex film for his "private" collection. Seriously, these babes are Europe\'s finest and Mike is your connoisseur of European booty! From their tight bodies, to their thick accents, these ladies know how to please and excite. Reality Kings offers hundreds of our incredible european porn movies and pics for you to download, and you don\'t have to travel to Moscow to get them. If you\'re looking for original, hot, European porn content, welcome to your new home: Mikes Apartment.', + parameters: { siteId: 25 }, + slug: 'mikesapartment', + network: 'realitykings', + }, + { + name: 'In the VIP', + url: 'http://www.inthevip.com', + description: "Ever wonder what happens In The VIP$3 Reality Kings takes you to the hottest night clubs in the country to show you exactly what goes on in the VIP room. When the club is packed, the music is pumpin', and the ladies are looking this HOT, you know it's going to be a damn good time! Grab a drink and step into the VIP room. Check out these gorgeous babes shaking their asses and flashing the camera. You will never see ladies like this in some whack ass bar, only in the most exclusive VIP rooms. As the party gets going, the clothes come off, and the panties drop! Watch some amazing free VIP porn movies, featuring these hotties having sex VIP style. We're talking about some down and dirty club sex featuring smokin' hot sluts. These chicks came for a good time and they've found it! Join the exclusive party In The VIP.", + parameters: { siteId: 22 }, + slug: 'inthevip', + network: 'realitykings', + }, + { + name: 'CFNM Secret', + slug: 'cfnmsecret', + alias: ['cfnms'], + url: 'https://www.realitykings.com/scenes?site=9', + description: "Shhh, keep your voice down! At Reality Kings we have a secret to share, the CFNM Secret! What's this secret all about$5 Clothed Females and Nude Males (CFNM)! Beautiful women dressed to impress and an unsuspecting male who is about to discover the secret for himself! These voyeurs are interested in every inch of the male body, touching and grabbing, they won't stop until they've had enough. Who wouldn't mind being these ladies play things$6 Gorgeous babes teasing and embarrassing men for their own fun and pleasure. Vulnerable guys being inspected and scrutinized in amazing high quality voyeur sex scenes. From CFNM handjobs to CFNM party scenes Reality Kings offers the hottest women enjoying the male body like never before. Browse our free CFNM videos below to download high quality pics and trailers. You're going to tell your friends about this secret, the CFNM Secret!", + parameters: null, + network: 'realitykings', + }, + { + name: 'Street BlowJobs', + url: 'https://www.realitykings.com/scenes?site=36', + description: "Street Blowjobs is one man's hunt for down on their luck ladies. Ladies who will blow your creamy wad, while they win a nice greedy wad of cash for their outstanding efforts. Horny honeys captured on hidden spy camera, giving amazing blowjobs for some good ol' American greenbacks. Can you imagine any of these smokin' hot babes giving blowjobs to you for just a little bit of moolah$11 Well we've got the content for you! Street Blowjobs has hundreds of hot blowjobs for you to download in high quality movies and pics. Watch these hotties use their magnificent dick sucking lips to get exactly what they want--your cock and your money! Reality Kings brings you a new episode every week, full of the best blowjobs and public blowjobs around. They say money can't buy happiness, but we beg to differ... money can chose your type of pleasure at Street Blowjobs.", + parameters: null, + slug: 'streetblowjobs', + network: 'realitykings', + }, + { + name: 'Hot Bush', + url: 'https://www.realitykings.com/scenes?site=21', + description: 'Forget bald vaginas, at Reality Kings we love a Hot Bush! Women with some grass on the field. These hairy beavers belong to some of the hottest women around. From brunette babes to red heads, every episode features a stunning natural beauty being worshiped for her beautiful bush! Browse some of our free hairy pussy videos below, and see for yourself if the curtains match the drapes. From the landing strip to the Bermuda triangle, these magnificent muff mounds are sure to please! Natural unshaved pussy movies and pics, thats what Hot Bush is all about. Grab your snorkel and get ready for some serious muff diving! Join the Reality Kings crew for some *very* Hot Bush.', + parameters: null, + slug: 'hotbush', + network: 'realitykings', + }, + { + name: 'Team Squirt', + url: 'https://www.realitykings.com/scenes?site=37', + description: "There's no denying it, at Reality Kings we love all kinds of pussy! Ask us what we really love however, and you'll get one answer: hot wet pussy! Team Squirt invites you to strap on your snorkel and fins, because we're going diving in some of the wettest pussy around! This is NOT pee ladies and gentlemen, this is real female ejaculation. Watch these beautiful ladies experience pleasure beyond belief as they try to control their squirting pussy on camera. Masturbation, fucking, whatever it takes, these babes will do anything for a squirting orgasm! Team Squirt has tons of high quality videos of girls squirting available for you to download right now. Be prepared, this is some serious female squirting content! From the girl, to the camera... everything is drenched when these super soakers take aim. These babes all pack a loaded, squirting pussy, and they know exactly how to use it! Grab your eye protection and join the team... Team Squirt.", + parameters: null, + slug: 'teamsquirt', + network: 'realitykings', + }, + { + name: 'Milf Next Door', + url: 'https://www.realitykings.com/scenes?site=26', + description: "We all love them, from the sexy mom at the grocery store, to the mature hottie down the block... we're talking about the MILF Next Door! There is nothing that these hot MILFs need more than a good pounding. If you don't know what a MILF is, allow us to explain... a Mother I'd Like to Fuck, a MILF! Watch as these sex starved sluts and their girlfriends search for a lucky dude to satisfy their craving for cock. MILF Next Door offers lesbian threesomes, amazing foursomes, and more mature sex movies featuring the hottest mature women! Start downloading some of this incredible content right now from our free pics and videos below. Every episode features another stunningly hot MILF finally getting the attention she deserves. If you love everyday mom's and can't wait to see these ladies get off, join Reality Kings and the MILF Next Door.", + parameters: null, + slug: 'milfnextdoor', + network: 'realitykings', + }, + { + name: 'Captain Stabbin', + url: 'https://www.captainstabbin.com', + description: "Hop aboard the S.S. Stabbin and join Captain Stabbin on his trip to analize the seven seas! What's better than a girl with a hot pussy$8 A girl with a hot pussy getting poked in the ass! Reality Kings invites you to the worlds greatest anal sex adventure. These babes don't need a boarding pass to climb aboard this ship, only a hot body and a gorgeous ass. Watch as the Captain sets course for the anal islands in search of the best anal sex scenes around! Download hundreds of incredible anal sex movies and pics in stunning high quality formats. Captain Stabbin brings you the very best booty content, from her first anal sex scene, to amazing boat sex scenes at sea, every episode is sure to please! These girls are ready for a stern spanking (pun intended)! Raise the main sail, set course, and join Captain Stabbin on his anal adventure! Arrr!", + parameters: { siteId: 8 }, + slug: 'captainstabbin', + network: 'realitykings', + }, + { + name: 'Big Tits Boss', + url: 'https://www.realitykings.com/scenes?site=6', + description: "Reality Kings presents Big Tits Boss! Have you been checking out that smokin' hot female executive around the office$10 Damn she's fine! She wears those short skirts and tight tops that make her huge tits pop out! Time to stop slackin' off fellas, because these ladies have been watching you closely. We're talking about the sexy women with the big tits at work. CEOs, Lawyers, CIOs, CFOs, these babes don't take any bullshit and they'll gladly use you like a toy whenever they please! Big Tits Boss has amazing high quality videos and pics available for download, featuring some very powerful women with awesome big natural tits. You won't mind being called into the office for a little discipline this time around! It's all business when these hotties are in the office... so fill out your TPS reports and be on your best behavior if you're looking for the promotion from the Big Tits Boss!", + parameters: null, + slug: 'bigtitsboss', + network: 'realitykings', + }, + { + name: 'Euro Sex Parties', + slug: 'eurosexparties', + alias: ['esp'], + url: 'https://www.realitykings.com/scenes?site=13', + description: "Pack your bags folks, we're headed to Europe! It's time to join two best friends as they travel across Europe and throw some amazing Euro Sex Parties. Forget about boring 1on1 sex scenes. Get ready for hardcore threesomes, foursomes, and fivesomes! Hot European porn directly from the source, just the way we like it. Euro babes with tight bodies taking on multiple cocks... what could be better$9 How about watching them eat pussy as well! Now that's a group sex party we'd love to attend. From hardcore group sex to hardcore gangbangs, this is the hottest content anywhere. Euro Sex Parties offers hundreds of European porn videos and pics to download in stunning high quality formats. Don't pack your bags yet, Reality Kings has tons of free movies and pics for you to download right here! Join us on our European vacation, and we'll throw a few Euro Sex Parties along the way.", + parameters: null, + network: 'realitykings', + }, + { + name: 'Dangerous Dongs', + url: 'https://www.realitykings.com/scenes?site=12', + description: "Reality Kings presents the Dangerous Dongs porn site, which bring you highlights from the featured huge cock and big dick porn on the entire network. If you enjoy watching sexy girls having fat cock stuffed deep into their tight pussies while they moan in pleasure, Dangerous Dongs has thousands of high resolution big dick porn pics and videos waiting for you inside. See tons of hot Latina, MILF, college, blond, teen and ebony babes taking big cock balls deep. You'll be able to download tons of big cock videos with hardcore sex scenes where cute girls with big tits and sexy, round asses take fat cock from every angle including doggy style and reverse cowgirl. So, for true fans of the big dick porn genre, look no further, we've gathered the best huge cock videos Reality Kings has to offer on the internet.", + parameters: null, + slug: 'dangerousdongs', + network: 'realitykings', + }, + { + name: 'Extreme Asses', + url: 'https://www.realitykings.com/scenes?site=14', + description: "Extreme Asses brings you a slew of big ass babes exclusively from Reality Kings. We're talking serious highlights from featured RK models like Jayden James big ass pictures and even some Jenny Hendrix ass sex videos. These sexy babes have nothing but big bouncy tits and porn ass that is perfect whether being viewed in doggy style action or riding cock in big ass videos. Watch tons of free ass porn trailers to get a taste of the kind of porn ass and ass sex that awaits you inside the Extreme Asses website. No matter what you like whether it be Latina, MILF, college, blond, teen or ebony big ass, we've got all the best extreme asses you can handle. This is big ass heaven, so be sure to check out this collection of big ass porn and ass sex gathered from Reality Kings best for your viewing pleasure.", + parameters: null, + slug: 'extremeasses', + network: 'realitykings', + }, + { + name: '40 Inch Plus', + url: 'https://www.realitykings.com/scenes?site=4', + description: "We have three words for you: deliciously round asses. Are you searching for ladies of the thicker variety$15 Beautiful women with hips measuring over 40 Inch Plus$16 Reality Kings presents the finest collection of booty around! Hot babes that love to have their big asses pinched and smacked. Grab 'em, squeeze 'em, bite 'em, these girls love to have their round asses played with. 40 Inch Plus is a tribute to ladies with a shape, there's no skinny chicks here! All of these women have a perfect ass and their tits are not too bad either! Download hundreds of movies and pics featuring women with big round asses. Check out our trailers and pics below to get a free sample of this incredible content. If you're looking for beautiful women with nice round asses look no further! Reality Kings and 40 Inch Plus are your source for gorgeous women with big asses.", + parameters: null, + slug: '40inchplus', + network: 'realitykings', + }, + { + name: 'Happy Tugs', + url: 'https://www.happytugs.com', + description: "Come on dudes, who doesn't like a happy ending$13 We've all seen those hole in the wall Asian massage parlors! Finally there is a site that celebrates the hand job. Asian beauties rubbing massage oil all over, what could be better$14 These babes know how to work out the kinks, seriously amazing rub 'n tug jobs. Happy Tugs captures hidden camera footage from inside one of the country's best sexual massage parlors. The dudes come in looking for a little rub down and, for a few dollars more, get a full servicing. It doesn't get any better than this, hand jobs and hot Asian babes. Check out our amazing happy ending videos, with babes rubbing their oil soaked breasts all over their favorite customers. Strip down, jump on the massage table and get your wallet out, Happy Tugs will ensure you get a very happy ending!", + parameters: { siteId: 19 }, + slug: 'happytugs', + network: 'realitykings', + }, + { + name: 'Reckless In Miami', + url: 'https://www.realitykings.com/scenes?site=303', + description: '', + parameters: null, + slug: 'recklessinmiami', + network: 'realitykings', + }, + { + name: 'HD Love', + url: 'https://www.realitykings.com/scenes?site=20', + description: 'Looking for incredibly hot porn videos in HD$12 Reality Kings showcases it all in hardcore erotica here at HDLove.com. Our premium adult content will satisfy your deepest carnal desires in stunning high-definition sex scenes. Feast your eyes on bodacious naked babes who love nothing more than to fuck on camera for you. Our hi-def movies capture every inch of their voluptuous bodies in vivid detail. Perfect round boobs and perky nipples are just the start. These jezebels proudly display their killer asses and dripping wet pussies before ravaging huge cocks like their lives depended on it. Whether you’re in the mood for horny 18+ teen nymphos or seasoned mature women the raunchiest scenes will keep you cumming back to HD Love. Our eager to please divas love to disrobe and spread as much love around as they can offering up deepthroat gagging blowjobs to anyone daring enough. They yearn for deep anal penetrations and are always up for a sweaty orgy so witness it all in crystal clear resolution. Catch every microscopic detail and blow your load repeatedly with the best that RK has in store!', + parameters: null, + slug: 'hdlove', + network: 'realitykings', + }, + { + name: 'Bikini Crashers', + url: 'https://www.realitykings.com/scenes?site=7', + description: "What's better than a babe in a scantily clad bikini$22 A party full of babes in scantily clad bikinis of course! Welcome to the Bikini Crashers! Reality Kings invites you to join our party with the hottest swimsuit models and bikini babes anywhere. We're talking about smokin' hot beauties throwing a naked pool party. Could it get any better than this$23 From perfectly round asses to amazing tan lines, these girls know how to party. We're not talking about your average swimsuit model either, these chicks are wild, crazy, and ready to get it on. Every party is loaded with 1on1, girl on girl, and group sex scenes. Gorgeous swimsuit girls getting it on by the pool, the beach, or anywhere they can show off their amazing bikinis! So grab a cold one, your shades, and kick back... you're invited to the Bikini Crashers party!", + parameters: null, + slug: 'bikinicrashers', + network: 'realitykings', + }, + { + name: 'Wives in Pantyhose', + url: 'https://www.realitykings.com/scenes?site=41', + description: 'Wives in Pantyhose features all kinds of real wives in sexy lingerie fingering their pussies with sex toys while they squeeze their big mature tits and moan. This Reality Kings network site has collected tons of pantyhose pics of hot wives and presented them to you for your viewing pleasure. No matter whether you prefer Latinas, MILFs, redheads, blondes or ebony babes, Wives in Pantyhose has all the sexiest nylon wives masturbating. There are even pantyhose lesbians playing with each other using dildos while they orgasm in smoking hot pantyhose videos. Wives in Pantyhose is easily one the best collection of real wives engaging in pantyhose porn ever put together on the net. So if you have a housewife pantyhose fetish, the the website Wives in Pantyhose is sure to deliver for you all the best models and porn the Reality Kings network has to offer.', + parameters: null, + slug: 'wivesinpantyhose', + network: 'realitykings', + }, + { + name: 'No Faces', + url: 'https://www.realitykings.com/scenes?site=30', + description: 'Isn’t it arousing to watch porn with a little mystery thrown in$19 That’s what Nofaces.com is about. The scrumptious porn stars at Reality Kings like to keep you guessing sometimes, so enjoy a wide array of HD porn videos where the faces of our horny minxes are not shown. A little sensual secrecy never hurt anyone so have some fun trying to figure out which titillating temptress is getting reamed in our torrid sex clips. Are her ample breast, perfect round ass, and wet pussy enough of a giveaway$20 What about her tattoos, piercings, or birth marks$21 Play the role of an X-rated detective and enjoy an endless sea of hardcore erotica with 18+ teen foxes and mature naked temptresses in graphic films depicting a covert cum sucker gobbling up monster cocks with glee. Our enigmatic nymphos relish in getting fucked in multiple holes simultaneously knowing that their identity is not revealed on camera. No Faces respects an RK girl’s desire to remain anonymous and only show more arousing parts of her luscious body.', + parameters: null, + slug: 'nofaces', + network: 'realitykings', + }, + { + name: 'Saturday Night Latinas', + url: 'https://www.realitykings.com/scenes?site=34', + description: "What's better than a Saturday Night out partying$18 Taking home a beautiful chick at the end of the night to fuck and have your way with! Reality Kings presents Saturday Night Latinas, gorgeous babes from the steamy night clubs and streets of Brazil. These hotties may have left the club, but the real party is about to begin! Real latina girls sucking and fucking after a night of partying! From deliciously round asses to amazing tan lines, these Brazilian bombshells are sure to please. Browse our videos below to download free latina porn movies and pictures. We have hundreds of latina sex scenes available for you to download. Grab your bags and get ready to head to Brazil, Reality Kings invites you to take home a Saturday Night Latina of your very own. Hot latina babes who love to party, join us today for a steamy Saturday Night out!", + parameters: null, + slug: 'saturdaynightlatinas', + network: 'realitykings', + }, + { + name: 'Extreme Naturals', + url: 'https://www.realitykings.com/scenes?site=15', + description: 'There are big natural breasts, then there are Extreme Naturals. On this site, we say, "Go big or go home!" That\'s why we only deliver massive naturals straight from the best Reality Kings has to offer. Extreme Naturals has painstakingly combed the RK network for the best giant naturals models and the hottest big naturals videos with the most hardcore XXX. These sexy babes have giant naturals that bounce while they ride cock and while they get stroked from behind doggy style in their perfect porn asses. For true fans of huge natural breasts, be sure to watch tons of free big naturals videos exclusively available as Extreme Naturals trailers on the website. Whether you like your giant naturals to be on Latinas, MILFs, college babes, blondes, teens or ebony babes, Extreme Naturals has the best collection of massive naturals straight from the vaults of Reality Kings.', + parameters: null, + slug: 'extremenaturals', + network: 'realitykings', + }, + { + name: 'Cum Girls', + url: 'https://www.realitykings.com/scenes?site=11', + description: "Reality Kings presents the Cum Girls porn site, which is dedicated solely to XXX cum shots and cum videos. If you like seeing hot girls with cum in their mouth, or face cum pictures, Cum Girls has thousands of high resolution cum porn pics and videos waiting for your viewing pleasure. There are smoking hot Latina, MILF, college, blond, teen and ebony babes with cum shots not only on their face, but also cum ass and cum tits pictures too. You'll be able to download tons of cum porn videos with hardcore sex scenes that all end with sticky and gooey cum in the mouth, the face, boobs, pussy or ass. Cum Girls has got cum porn and cum videos for true fans of the genre, and they all come straight to you from Reality Kings, so you know you're getting nothing less than the best on the internet.", + parameters: null, + slug: 'cumgirls', + network: 'realitykings', + }, + { + name: 'VIP Crew', + url: 'https://www.realitykings.com/scenes?site=40', + description: "Party animals rejoice! The VIP Crew is your guide to the hottest and wildest VIP parties in the world! We're not talking about ordinary house parties here, we're talking about the biggest, most badass sex parties around. When you combine loads of fun, some fine looking women, and a few lucky dudes you have the recipe for one amazing fucking party. Best of all, you're invited! From huge orgy sex parties to private sex parties, the VIP Crew brings in the hottest women--all ready to bare their VIP pussies for you. Babes that aren't afraid of a little pole dancing, foam dancing, or strip tease! These girls will do anything to join these wild sex parties and have a good time. Reality Kings has hundreds of high quality videos and pics available for you to download. So what the hell are you waiting for$17 Join the VIP Crew and get your freak on!", + parameters: null, + slug: 'vipcrew', + network: 'realitykings', + }, + { + name: 'Moms Bang Teens', + slug: 'momsbangteens', + alias: ['mbt'], + url: 'https://www.momsbangteens.com', + description: "Reality Kings presents the first website dedicated to hot moms who love to bang 18+ teens. Moms Bang Teens features the sexiest MILFs on the web, and these MILFs are all about fucking young guys on camera. If you remember lusting after one of your friend's hot moms back in grade school, then you know exactly what Moms Bang Teens is all about. Imagine if instead of just fantasizing about that sexy mother, you actually got to bang her. These are the same hot moms you see at your local supermarket and shopping at your neighborhood mall. Some of them are married and never get the attention they need. While others are just horny and sexy moms who never got tied down with a husband. Instead they like to go out and find hot young studs that know how to fuck them right. These are experienced and mature women who know what they want; young 18+ teens that can give them that rock hard cock.", + parameters: { siteId: 27 }, + network: 'realitykings', + }, + { + name: 'Sneaky Sex', + url: 'https://www.sneakysex.com', + description: 'Sneaky dirty sex! They are fucking and nobody can see, otherwise they will have a HUGE problem. When no one is watching, these horny MILFs and Teens are having sneaky sex!', + parameters: { siteId: 46 }, + slug: 'sneakysex', + network: 'realitykings', + }, + { + name: 'See My Wife', + url: 'https://www.realitykings.com/scenes?site=35', + description: 'Have you been spying on that hot couple next door$26 See My Wife invites you to view the private porn collection of horny amateurs everywhere! We\'re talking about 100% user submitted movies and pictures. Real women appearing in the hottest wife sex scenes around, that is what See My Wife is about. Our users have a chance to make 0 for pics and 00 for videos when they submit their homemade content. If you\'ve ever said "I wish I could bang my wife on film and get paid for it," look no further! Reality Kings considers every submission when we post new episodes. Check out some of our free pics and trailers below, this is one amazing collection of girlfriend and wife sex scenes. Every week we post a new episode crammed with four incredible babes showing off in front of the camera. No need to spy on the couple next door when you come See My Wife!', + parameters: null, + slug: 'seemywife', + network: 'realitykings', + }, + { + name: 'Girls of Naked', + url: 'https://www.realitykings.com/scenes?site=18', + description: 'Nothing is hotter than voluptuous minxes who love getting naked. Girlsofnaked.com is home to a bevy of bodacious beauties who are all about showing as much skin to whomever is willing to satisfy their sexual desires. Our 18+ pornstars are daring and always curious for new carnal adventures in HD porn videos. Reality Kings has compiled an incredible assortment of erotica with big boob naughty nymphos. Watch them squeeze their perky nipples before rubbing their ticklish clits in steamy scenes. Our deviant divas need their juicy pussies stuffed 24/7 by the biggest cocks in the adult biz and will stop at nothing to devour as much man meat as they can fit into every hungry orifice. Girls of Naked celebrate nudity and hardcore sex in all its glory. Fetishes, orgies, bukkake, anal creampies and much more are their favorite pastimes. RK has full-length premium porno movies bursting with our luscious babes bursting out of their clothes just for you!', + parameters: null, + slug: 'girlsofnaked', + network: 'realitykings', + }, + { + name: 'Lil Humpers', + url: 'https://lilhumpers.com', + description: '', + parameters: { siteId: 310 }, + slug: 'lilhumpers', + network: 'realitykings', + }, + { + name: 'Mike in Brazil', + url: 'https://www.realitykings.com/scenes?site=24', + description: "Are you ready for the never ending booty vacation$24 Join Mike In Brazil as he explores the wild, the exotic, and the gorgeous women of South America! If you have never been to Brazil, don't worry... Mike will give you a crash course on the most amazing ASSet of their native women. We're talking about deliciously tanned, round, thong clad Brazilian ass! These booties will not disappoint. Mike exports nothing but the finest, Grade A, Brazilian porn directly to your computer screen. Check out the hottest Brazilian ass around, wearing nothing but bikinis and thongs that are sure to get your blood pumping! These hotties spend hours working on their amazing tans to show off their bodies... look at those incredible tan lines! Mike In Brazil features some amazing hardcore sex, from anal to Brazilian facials, we're sure you're going to be planning a trip to Brazil soon. What are you waiting for$25 Join the never ending booty vacation with Mike In Brazil!", + parameters: null, + slug: 'mikeinbrazil', + network: 'realitykings', + }, + { + name: 'Real Orgasms', + url: 'https://www.realitykings.com/scenes?site=32', + description: "Real Orgasms features all kinds of sexy women playing with their pussies and stimulating their clits with sex toys and big dildos until they have real orgasms. This Reality Kings network site has collected tons of real orgasm videos and masturbation videos and concentrated them down to only the best real female orgasms that you will ever witness on the net. Whether you're really into Latinas, MILFs, college babes, blondes, teens or ebony babes, Real Orgasms has every kind of the most beautiful women masturbating. Watch as they play with themselves using sex toys and dildos while they moan, shake and their pussies convulse, as they have real orgasm on video for your pleasure. By far, this is the best collection of real orgasm porn ever put together on the net. Thanks to Reality Kings, Real Orgasms only delivers 100% real female orgasms and masturbation videos.", + parameters: null, + slug: 'realorgasms', + network: 'realitykings', + }, + { + name: 'Tranny Surprise', + url: 'https://www.trannysurprise.com', + description: 'If you’re in the mood for graphic tranny porn, look no further than TrannySurprise.com. A sexy shemale is a thing of beauty, often possessing a voracious appetite for sex. Reality Kings is home to some of the most incredible transsexual pornstars on the net. Watch them stroke their huge dicks and massage their voluminous ball sacks in our full-length HD videos. All these goddesses want to do is suck dick until it erupts in their wide open mouths. Cum swallowing is their specialty so enjoy our scenes full of creamy jizz loads overflowing onto their giant tits and firm stomachs. These nude RK shemales live to get rimjobs before getting drilled by gigantic dicks. Messy creampies are usually how their nights end so witness the torrid fuck marathons leading up to juicy orgasms. Tranny Surprise features sensual ladyboys that know just how to please anyone looking to take a walk on the wild side. Premium porno is what you deserve so eat it all up with our luscious, busty trannies. Long legs, tight asses, toned physiques, and a healthy dose of raw animal passion is what our “chicks with dicks” deliver in every one of our erotic films.', + parameters: { native: true }, + slug: 'trannysurprise', + network: 'realitykings', + }, + { + name: 'Flower Tucci', + url: 'https://www.realitykings.com/scenes?site=17', + description: 'Reality Kings presents Flower Tucci, and this is what she has to say: "My name is Flower, and I live, eat, breathe, sleep, and worship SEX! You have never met a girl like me! My ass is for worshiping. I can squirt over and over again when my pussy cums. I search out the biggest cocks and take them in my mouth, pussy, and ass! I milk those cocks until my pussy squirts everywhere. This site is dedicated to all my fantasies. Watch me search for the ultimate orgasm." Damn folks! I don\'t know about you, but this babe sounds perfect. Squirting pussy, amazing ass, gorgeous tits... the full package! If you\'re like us, you can\'t wait another moment to download these amazing videos of Flower Tucci squirting. Reality Kings is the one and only home to Flower Tucci pics and Flower Tucci movies--this content is absolutely incredible! Join us, Flower, and her friends in search of the ultimate orgasm.', + parameters: null, + slug: 'flowertucci', + network: 'realitykings', + }, + { + name: 'First Time Auditions', + url: 'https://www.realitykings.com/scenes?site=16', + description: "Forget about the next big music idol, we're looking for the next big porn star! Reality Kings presents First Time Auditions, featuring the hottest amateur chicks, searching for fame and fortune. These sluts will do anything to break into the business, from blowjobs to amateur sex scenes, these are their first porn auditions caught on film. Do you think they have what it takes$7 Download hundreds of amateur porn movies and pics, and you be the judge. First Time Auditions places ads in local and college newspapers seeking the hottest models around. When these babes arrive, we are never disappointed. They show off their perfect bodies and their many, amazing talents! These are the hottest amateur auditions around. Trying to get their careers started, these girls give the porn auditions of a lifetime! If you're ready to be the judge, to put these girls to the test, watch them on their First Time Auditions.", + parameters: null, + slug: 'firsttimeauditions', + network: 'realitykings', + }, + { + name: 'Top Shelf Pussy', + url: 'https://www.realitykings.com/scenes?site=38', + description: 'Top Shelf Pussy features nothing but the best pussy on the net. If pussy is like Johnny Walker, consider Top Shelf Pussy the Blue Label of the bunch. Whether you like shaved pussy, teen pussy, hairy pussy, wet pussy, mature pussy, black pussy or fat pussy, Top Shelf Pussy has got the hottest models and the best pussy videos on the net. Watch tons of free pussy trailers and see as these gorgeous girls play with their pussies using sex toys and dildos. Then see them take a deep stroking to their wet pussy while they moan, shake and their pussies convulse in some amazing pussy porn. No doubt, Top Shelf Pussy has got tons of pictures of sexy ladies spread eagle and more pussy videos than you could ever possibly watch. If you are a fan of the pussy porn genre, then Top Shelf Pussy is the site for you.', + parameters: null, + slug: 'topshelfpussy', + network: 'realitykings', + }, + { + name: 'Round and Brown', + url: 'https://www.roundandbrown.com', + alias: ['rab'], + description: 'Chocolate lovers out there, Reality Kings presents to you... Round And Brown, the porn site that caters to horny dudes who have a mighty craving for fine "sistah" booties. The ladies featured in these ebony porn movies are SIZZLING HOT, like a newly melted chocolate fondue! We\'re talking about some damn fine black booties! If it\'s Round And Brown, it gets the special lube treatment, no exceptions! Think you can handle this collection of premium ebony ass$1 There\'s no skinny white girls here, only gorgeous black beauties with deliciously round booties, featured in the best hardcore ebony sex videos around! Reality Kings is the only one who can bring you this amazing collection of black girl porn. If you love big round asses, gorgeous black babes, and amazing tits we have the videos and pics you\'re looking for. Warning: This chocolate may melt in your hand and your mouth... but who cares, if it\'s Round And Brown!', + parameters: { siteId: 33 }, + slug: 'roundandbrown', + network: 'realitykings', + }, + { + name: 'Monster Curves', + slug: 'monstercurves', + alias: ['mcu'], + url: 'https://www.realitykings.com/scenes?site=29', + description: "Forget about those toothpick size runway models, give us some ladies with curves-- Monster Curves! If you love your women round and juicy, ladies with some meat on their bones... then we have the content for you! We're talking about women with hips that don't quit. Incredibly round asses that will make your mouth water! Big booty girls with big round asses. Only people as obsessed as us could bring you this many pairs of perfects hips and asses! Download hundreds of movies and pics featuring gorgeous girls with amazing curves (we call them Monster Curves). Check out some of our free trailers below, these girls and their round butts and perfect hips are sure to wet your appetite! Every week, Reality Kings brings you nothing but the finest butts, the sexy round asses that jiggle when you grab 'em, the women with the Monster Curves!", + parameters: null, + network: 'realitykings', + }, + // SCORE + { + name: '18 Eighteen', + slug: '18eighteen', + url: 'https://www.18eighteen.com', + network: 'score', + parameters: { path: '/xxx-teen-videos' }, + }, + { + name: '40 Something Mag', + slug: '40somethingmag', + url: 'https://www.40somethingmag.com', + parameters: { path: '/xxx-mature-videos' }, + network: 'score', + }, + { + name: '50 Plus MILFs', + slug: '50plusmilfs', + url: 'https://www.50plusmilfs.com', + parameters: { path: '/xxx-milf-videos' }, + network: 'score', + }, + { + name: '60 Plus MILFs', + slug: '60plusmilfs', + url: 'https://www.60plusmilfs.com', + parameters: { path: '/xxx-granny-videos' }, + network: 'score', + }, + { + name: 'Ashley Sage Ellison', + slug: 'ashleysageellison', + url: 'https://www.bigboobbundle.com/ashleysageellison', + parameters: { path: '/videos', actors: ['Ashley Sage Ellison'] }, + network: 'score', + }, + { + name: 'Autumn Jade', + slug: 'autumnjade', + url: 'https://www.bigboobbundle.com/autumn-jade', + network: 'score', + parameters: { path: '/videos', actors: ['Autumn Jade'] }, + }, + { + name: 'Big Boob Bundle', + slug: 'bigboobbundle', + url: 'https://www.bigboobbundle.com', + network: 'score', + show: false, // all content appears to be on subsites + }, + { + name: 'Big Boobs POV', + slug: 'bigboobspov', + url: 'https://www.scorepass.com/bigboobspov', + network: 'score', + }, + { + name: 'Big Tit Angela White', + slug: 'bigtitangelawhite', + url: 'https://www.bigboobbundle.com/bigtitangelawhite', + parameters: { path: '/videos', actors: ['Angela White'] }, // no dates available + network: 'score', + }, + { + name: 'Big Tit Hitomi', + slug: 'bigtithitomi', + url: 'https://www.bigboobbundle.com/bigtithitomi', + parameters: { path: '/videos', actors: ['Hitomi'] }, + network: 'score', + }, + { + name: 'Big Tit Hooker', + slug: 'bigtithooker', + url: 'https://www.scorepass.com/bigtithooker', + network: 'score', + }, + { + name: 'Big Tit Terry Nova', + slug: 'bigtitterrynova', + url: 'https://www.bigboobbundle.com/bigtitterrynova', + parameters: { path: '/videos', actors: ['Terry Nova'] }, + network: 'score', + }, + { + name: 'Big Tit Venera', + slug: 'bigtitvenera', + url: 'https://www.bigboobbundle.com/bigtitvenera', + network: 'score', + }, + { + name: 'Black And Stacked', + slug: 'blackandstacked', + url: 'https://www.scorepass.com/blackandstacked', + network: 'score', + }, + { + name: 'Boned At Home', + slug: 'bonedathome', + url: 'https://www.scorepass.com/bonedathome', + network: 'score', + }, + { + name: 'Bootylicious Mag', + slug: 'bootyliciousmag', + url: 'https://www.bootyliciousmag.com', + network: 'score', + }, + { + name: 'Busty Angelique', + slug: 'bustyangelique', + url: 'https://www.bigboobbundle.com/bustyangelique', + network: 'score', + }, + { + name: 'Busty Arianna', + slug: 'bustyarianna', + url: 'https://www.bigboobbundle.com/bustyarianna', + network: 'score', + }, + { + name: 'Busty Danni Ashe', + slug: 'bustydanniashe', + url: 'https://www.bigboobbundle.com/bustydanniashe', + network: 'score', + }, + { + name: 'Busty Dusty Stash', + slug: 'bustydustystash', + url: 'https://www.bigboobbundle.com/bustydustystash', + network: 'score', + }, + { + name: 'Busty Ines Cudna', + slug: 'bustyinescudna', + url: 'https://www.bigboobbundle.com/bustyinescudna', + network: 'score', + }, + { + name: 'Busty Kelly Kay', + slug: 'bustykellykay', + url: 'https://www.bigboobbundle.com/bustykellykay', + network: 'score', + }, + { + name: 'Busty Kerry Marie', + slug: 'bustykerrymarie', + url: 'https://www.bigboobbundle.com/bustykerrymarie', + network: 'score', + }, + { + name: 'Busty Lorna Morgan', + slug: 'bustylornamorgan', + url: 'https://www.bigboobbundle.com/bustylornamorgan', + network: 'score', + }, + { + name: 'Busty Merilyn', + slug: 'bustymerilyn', + url: 'https://www.scorepass.com/bustymerilyn', + network: 'score', + }, + { + name: 'Busty Old Sluts', + slug: 'bustyoldsluts', + url: 'https://www.milfbundle.com/bustyoldsluts', + network: 'score', + }, + { + name: 'Busty Sammie Black', + slug: 'bustysammieblack', + url: 'https://www.bigboobbundle.com/bustysammieblack', + network: 'score', + }, + { + name: 'Cherry Brady', + slug: 'cherrybrady', + url: 'https://www.bigboobbundle.com/cherrybrady', + network: 'score', + }, + { + name: 'Chloes World', + slug: 'chloesworld', + url: 'https://www.scorepass.com/chloesworld', + network: 'score', + }, + { + name: 'Christy Marks', + slug: 'christymarks', + url: 'https://www.scorepass.com/christymarks', + network: 'score', + }, + { + name: 'Creampie for Granny', + slug: 'creampieforgranny', + url: 'https://www.milfbundle.com/creampieforgranny', + network: 'score', + }, + { + name: 'Crystal Gunns World', + slug: 'crystalgunnsworld', + url: 'https://www.bigboobbundle.com/crystalgunnsworld', + network: 'score', + }, + { + name: 'Daylene Rio', + slug: 'daylenerio', + url: 'https://www.bigboobbundle.com/daylenerio', + network: 'score', + }, + { + name: 'Desiraes World', + slug: 'desiraesworld', + url: 'https://www.bigboobbundle.com/desiraesworld', + network: 'score', + }, + { + name: 'Diane Poppos', + slug: 'dianepoppos', + url: 'https://www.bigboobbundle.com/dianepoppos', + network: 'score', + }, + { + name: 'Eva Notty Videos', + slug: 'evanottyvideos', + url: 'https://www.bigboobbundle.com/evanottyvideos', + network: 'score', + }, + { + name: 'Feed Her Fuck Her', + slug: 'feedherfuckher', + url: 'https://www.scorepass.com/feedherfuckher', + network: 'score', + }, + { + name: 'Flat And Fucked MILFs', + slug: 'flatandfuckedmilfs', + url: 'https://www.milfbundle.com/flatandfuckedmilfs', + network: 'score', + }, + { + name: 'Granny Gets A Facial', + slug: 'grannygetsafacial', + url: 'https://www.milfbundle.com/grannygetsafacial', + network: 'score', + }, + { + name: 'Granny Loves BBC', + slug: 'grannylovesbbc', + url: 'https://www.milfbundle.com/grannylovesbbc', + network: 'score', + }, + { + name: 'Granny Loves Young Cock', + slug: 'grannylovesyoungcock', + url: 'https://www.milfbundle.com/grannylovesyoungcock', + network: 'score', + }, + { + name: 'Home Alone MILFs', + slug: 'homealonemilfs', + url: 'https://www.milfbundle.com/homealonemilfs', + network: 'score', + }, + { + name: 'I Boned Your Mom', + slug: 'ibonedyourmom', + url: 'https://www.milfbundle.com/ibonedyourmom', + network: 'score', + }, + { + name: 'I Fucked the Boss', + slug: 'ifuckedtheboss', + url: 'https://www.milfbundle.com/ifuckedtheboss', + network: 'score', + }, + { + name: 'Jessica Turner', + slug: 'jessicaturner', + url: 'https://www.bigboobbundle.com/jessicaturner', + network: 'score', + }, + { + name: 'Joana Bliss', + slug: 'joanabliss', + url: 'https://www.bigboobbundle.com/joanabliss', + network: 'score', + }, + { + name: 'Julia Miles', + slug: 'juliamiles', + url: 'https://www.bigboobbundle.com/juliamiles', + network: 'score', + }, + { + name: 'Karina Hart', + slug: 'karinahart', + url: 'https://www.scorepass.com/karinahart', + network: 'score', + }, + { + name: 'Karla James', + slug: 'karlajames', + url: 'https://www.bigboobbundle.com/karlajames', + network: 'score', + }, + { + name: 'Leanne Crow Videos', + slug: 'leannecrowvideos', + url: 'https://www.bigboobbundle.com/leannecrowvideos', + network: 'score', + }, + { + name: 'Leg Sex', + slug: 'legsex', + url: 'https://www.legsex.com', + network: 'score', + }, + { + name: 'Linseys World', + slug: 'linseysworld', + url: 'https://www.scorepass.com/linseysworld', + network: 'score', + }, + { + name: 'Mega Tits Minka', + slug: 'megatitsminka', + url: 'https://www.bigboobbundle.com/megatitsminka', + network: 'score', + }, + { + name: 'Micky Bells', + slug: 'mickybells', + url: 'https://www.bigboobbundle.com/mickybells', + network: 'score', + }, + { + name: 'MILF Bundle', + slug: 'milfbundle', + url: 'https://www.milfbundle.com', + network: 'score', + show: false, + }, + { + name: 'Teaming Cock', + slug: 'milfthreesomes', + url: 'https://www.milfbundle.com/milfthreesomes', + network: 'score', + }, + { + name: 'MILF Tugs', + slug: 'milftugs', + url: 'https://www.milfbundle.com/milftugs', + network: 'score', + }, + { + name: 'Natalie Fiore', + slug: 'nataliefiore', + url: 'https://www.bigboobbundle.com/nataliefiore', + network: 'score', + }, + { + name: 'Naughty Footjobs', + slug: 'naughtyfootjobs', + url: 'https://www.scorepass.com/naughtyfootjobs', + network: 'score', + }, + { + name: 'Naughty Mag', + slug: 'naughtymag', + url: 'https://www.naughtymag.com', + network: 'score', + }, + { + name: 'Naughty Tugs', + slug: 'naughtytugs', + url: 'https://www.scorepass.com/naughtytugs', + network: 'score', + }, + { + name: 'Nicole Peters', + slug: 'nicolepeters', + url: 'https://www.bigboobbundle.com/nicolepeters', + network: 'score', + }, + { + name: 'Old Horny MILFs', + slug: 'oldhornymilfs', + url: 'https://www.milfbundle.com/oldhornymilfs', + network: 'score', + }, + { + name: 'Picking Up Pussy', + slug: 'pickinguppussy', + url: 'https://www.scorepass.com/pickinguppussy', + network: 'score', + }, + { + name: 'Porn Loser', + slug: 'pornloser', + url: 'https://www.scorepass.com/pornloser', + network: 'score', + }, + { + name: 'Porn Mega Load', + slug: 'pornmegaload', + url: 'https://www.pornmegaload.com', + network: 'score', + show: false, + }, + { + name: 'SaRennas World', + slug: 'sarennasworld', + url: 'https://www.bigboobbundle.com/sarennasworld', + network: 'score', + }, + { + name: 'Scoreland', + slug: 'scoreland', + url: 'https://www.scoreland.com', + network: 'score', + parameters: { path: '/big-boob-videos' }, + priority: 3, + }, + { + name: 'Scoreland2', + slug: 'scoreland2', + url: 'https://www.scoreland2.com', + network: 'score', + parameters: { path: '/big-boob-scenes' }, + priority: 1, + }, + { + name: 'Score Classics', + slug: 'scoreclassics', + url: 'https://www.scoreclassics.com', + network: 'score', + parameters: { path: '/classic-boob-videos' }, + priority: 1, + }, + { + name: 'Scoreland TV', + slug: 'scorelandtv', + url: 'https://www.scorepass.com/scorelandtv', + network: 'score', + priority: 1, + show: false, // appears to be streaming service for other sites + }, + { + name: 'ScoreTV', + slug: 'scoretv', + url: 'https://www.scoretv.tv', + network: 'score', + priority: 1, + show: false, // similar to or same as Scoreland TV + }, + { + name: 'Score Videos', + slug: 'scorevideos', + url: 'https://www.scorevideos.com', + network: 'score', + parameters: { path: '/porn-videos' }, + priority: 2, + }, + { + name: 'Sha Rizel Videos', + slug: 'sharizelvideos', + url: 'https://www.bigboobbundle.com/sharizelvideos', + network: 'score', + }, + { + name: 'Silver Sluts', + slug: 'silversluts', + url: 'https://www.milfbundle.com/silversluts', + network: 'score', + }, + { + name: 'Stacy Vandenberg Boobs', + slug: 'stacyvandenbergboobs', + url: 'https://www.bigboobbundle.com/stacyvandenbergboobs', + network: 'score', + }, + { + name: 'Susie Wildin', + slug: 'susiewildin', + url: 'https://www.bigboobbundle.com/susiewildin', + network: 'score', + }, + { + name: 'Tawny Peaks', + slug: 'tawnypeaks', + url: 'https://www.bigboobbundle.com/tawny-peaks', + network: 'score', + }, + { + name: 'Tiffany Towers', + slug: 'tiffanytowers', + url: 'https://www.bigboobbundle.com/tiffany-towers', + network: 'score', + }, + { + name: 'Tits And Tugs', + slug: 'titsandtugs', + url: 'https://www.scorepass.com/titsandtugs', + network: 'score', + }, + { + name: 'TNA Tryouts', + slug: 'tnatryouts', + url: 'https://www.scorepass.com/tnatryouts', + network: 'score', + }, + { + name: 'Valory Irene', + slug: 'valoryirene', + url: 'https://www.bigboobbundle.com/valoryirene', + network: 'score', + }, + { + name: 'XL Girls', + slug: 'xlgirls', + url: 'https://www.xlgirls.com', + network: 'score', + }, + { + name: 'Your Mom Loves Anal', + slug: 'yourmomlovesanal', + url: 'https://www.milfbundle.com/yourmomlovesanal', + network: 'score', + }, + { + name: 'Your Mom\'s Got Big Tits', + slug: 'yourmomsgotbigtits', + url: 'https://www.milfbundle.com/yourmomsgotbigtits', + network: 'score', + }, + { + name: 'Your Wife My Meat', + slug: 'yourwifemymeat', + url: 'https://www.milfbundle.com/yourwifemymeat', + network: 'score', + }, + // SEXY HUB + { + slug: 'danejones', + name: 'Dane Jones', + alias: ['dnj'], + url: 'https://www.danejones.com/', + parameters: { siteId: 290 }, + network: 'sexyhub', + }, + { + slug: 'lesbea', + name: 'Lesbea', + alias: ['lsb'], + url: 'https://www.lesbea.com', + parameters: { siteId: 291 }, + tags: ['lesbian'], + network: 'sexyhub', + }, + { + slug: 'massagerooms', + name: 'Massage Rooms', + alias: ['mrs'], + url: 'https://www.sexyhub.com/scenes?site=292', + tags: ['massage'], + network: 'sexyhub', + }, + { + slug: 'momxxx', + name: 'Mom XXX', + alias: ['mom'], + url: 'https://www.sexyhub.com/scenes?site=293', + tags: ['milf'], + network: 'sexyhub', + }, + { + slug: 'fitnessrooms', + name: 'Fitness Rooms', + alias: ['frs'], + url: 'https://www.sexyhub.com/scenes?site=294', + network: 'sexyhub', + }, + { + slug: 'girlfriends', + name: 'Girlfriends', + url: 'https://www.sexyhub.com/scenes?site=289', + tags: ['lesbian'], + network: 'sexyhub', + }, + // TEAM SKEET + { + slug: 'exxxtrasmall', + name: 'Exxxtra Small', + alias: ['ext'], + description: '', + url: 'https://www.exxxtrasmall.com', + parameters: { id: 'exs' }, + network: 'teamskeet', + }, + { + slug: 'teenpies', + name: 'Teen Pies', + description: '', + url: 'https://www.teenpies.com', + parameters: { id: 'tp' }, + network: 'teamskeet', + }, + { + slug: 'innocenthigh', + name: 'Innocent High', + alias: ['inh'], + description: '', + url: 'https://www.innocenthigh.com', + parameters: { id: 'ih' }, + network: 'teamskeet', + }, + { + slug: 'teencurves', + name: 'Teen Curves', + description: '', + url: 'https://www.teencurves.com', + parameters: { id: 'tc' }, + network: 'teamskeet', + }, + { + slug: 'cfnmteens', + name: 'CFNM Teens', + alias: ['cfnmt'], + url: 'https://www.cfnmteens.com', + parameters: { id: 'cfnm' }, + network: 'teamskeet', + }, + { + slug: 'teensloveanal', + name: 'Teens Love Anal', + alias: ['tla'], + url: 'https://www.teensloveanal.com', + tags: ['anal'], + parameters: { id: 'tla' }, + network: 'teamskeet', + }, + { + slug: 'mybabysittersclub', + name: 'My Babysitters Club', + description: '', + url: 'https://www.mybabysittersclub.com', + parameters: { id: 'bsc' }, + network: 'teamskeet', + }, + { + slug: 'shesnew', + name: 'She\'s New', + alias: ['ssn'], + url: 'https://www.shesnew.com', + parameters: { id: 'bsc' }, + network: 'teamskeet', + }, + { + slug: 'teensdoporn', + name: 'Teens Do Porn', + alias: ['tdp'], + url: 'https://www.teensdoporn.com', + parameters: { id: 'tdp' }, + network: 'teamskeet', + }, + { + slug: 'povlife', + name: 'POV Life', + description: '', + url: 'https://www.povlife.com', + parameters: { id: 'pov' }, + network: 'teamskeet', + }, + { + slug: 'therealworkout', + name: 'The Real Workout', + description: '', + url: 'https://www.therealworkout.com', + parameters: { id: 'trw' }, + network: 'teamskeet', + }, + { + slug: 'thisgirlsucks', + name: 'This Girl Sucks', + alias: ['tgs'], + description: '', + url: 'https://www.thisgirlsucks.com', + parameters: { id: 'tgs' }, + network: 'teamskeet', + }, + { + slug: 'teenslovemoney', + name: 'Teens Love Money', + alias: ['tlm'], + description: '', + url: 'https://www.teenslovemoney.com', + parameters: { id: 'tlm' }, + network: 'teamskeet', + }, + { + slug: 'oyeloca', + name: 'Oye Loca', + description: '', + url: 'https://www.oyeloca.com', + parameters: { id: 'ol' }, + network: 'teamskeet', + }, + { + slug: 'tittyattack', + name: 'Titty Attack', + description: '', + url: 'https://www.tittyattack.com', + parameters: { id: 'ta' }, + network: 'teamskeet', + }, + { + slug: 'teenyblack', + name: 'Teeny Black', + description: '', + url: 'https://www.teenyblack.com', + parameters: { id: 'tb' }, + network: 'teamskeet', + }, + { + slug: 'lusthd', + name: 'Lust HD', + description: '', + url: 'https://www.lusthd.com', + parameters: { id: 'lhd' }, + network: 'teamskeet', + }, + { + slug: 'rubateen', + name: 'Rub A Teen', + description: '', + url: 'https://www.rubateen.com', + parameters: { id: 'rat' }, + network: 'teamskeet', + }, + { + slug: 'herfreshmanyear', + name: 'Her Freshman Year', + description: '', + url: 'https://www.exxxtrasmall.com', + parameters: { id: 'hfy' }, + network: 'teamskeet', + }, + { + slug: 'selfdesire', + name: 'Self Desire', + description: '', + url: 'https://www.selfdesire.com', + parameters: { id: 'sd' }, + network: 'teamskeet', + }, + { + slug: 'solointerviews', + name: 'Solo Interviews', + description: '', + url: 'https://www.solointerviews.com', + parameters: { id: 'si' }, + network: 'teamskeet', + }, + { + slug: 'teamskeetextras', + name: 'Team Skeet Extras', + description: '', + url: 'https://www.teamskeetextras.com', + parameters: { id: 'tse' }, + network: 'teamskeet', + }, + { + slug: 'dyked', + name: 'Dyked', + description: '', + url: 'https://www.dyked.com', + parameters: { id: 'dyk' }, + network: 'teamskeet', + }, + { + slug: 'badmilfs', + name: 'Bad MILFs', + description: '', + url: 'https://www.badmilfs.com', + parameters: { id: 'bad' }, + network: 'teamskeet', + }, + { + slug: 'gingerpatch', + name: 'Ginger Patch', + description: '', + url: 'https://www.gingerpatch.com', + parameters: { id: 'gp' }, + network: 'teamskeet', + }, + { + slug: 'bracefaced', + name: 'Brace Faced', + description: '', + url: 'https://www.bracefaced.com', + parameters: { id: 'bfd' }, + network: 'teamskeet', + }, + { + slug: 'teenjoi', + name: 'Teen JOI', + description: '', + url: 'https://www.teenjoi.com', + parameters: { id: 'joi' }, + network: 'teamskeet', + }, + { + slug: 'stepsiblings', + name: 'Step Siblings', + alias: ['steps'], + url: 'https://www.stepsiblings.com', + parameters: { id: 'sss' }, + network: 'teamskeet', + }, + { + slug: 'submissived', + name: 'Submissived', + description: '', + url: 'https://www.submissived.com', + tags: ['bdsm'], + parameters: { scraper: 'A' }, + network: 'teamskeet', + }, + { + slug: 'familystrokes', + name: 'Family Strokes', + alias: ['fams'], + url: 'https://www.familystrokes.com', + parameters: { scraper: 'A' }, + tags: ['family'], + network: 'teamskeet', + }, + // TWISTYS + { + name: 'Twistys', + slug: 'twistys', + url: 'https://www.twistys.com/scenes?site=219', + description: 'Twistys.com is the #1 ranked babe site on the web! With over 10 years of photos and videos, updated daily with 3 HQ photo sets and 2 HD videos, Twistys also awards one hot babe a Treat of the Month title, complete with exclusive photo & video sets! Weekly updates!', + network: 'twistys', + priority: 1, + }, + { + name: 'When Girls Play', + slug: 'whengirlsplay', + alias: ['wgp'], + url: 'https://www.whengirlsplay.com', + description: 'Watch hot girls seducing other girls in steamy lesbian play. These sluts finger, use dildos, strap-ons and squirt their pink pussies in lesbian porn by WhenGirlsPlay.com. Get Access to the Hottest Lesbian videos on the web!', + parameters: { siteId: 227 }, + network: 'twistys', + priority: 1, + }, + { + name: 'Turning Twistys', + slug: 'turningtwistys', + url: 'https://www.twistys.com/scenes?site=302', + description: 'Where straight curious cuties explore their sexuality, and get seduced by sexy butch girls. These sneaky lesbians lure the innocent looking beauties away from their boyfriends and into their beds... Be careful or they might just steal your girl!', + network: 'twistys', + priority: 1, + }, + { + name: 'Mom Knows Best', + slug: 'momknowsbest', + url: 'https://www.momknowsbest.com', + description: 'The world’s tightest teens and most elegant MILFs get together at Mom Knows Best, where horny teen girls learn the ins and outs of lesbian sex from confident and beautiful older women. These MILFs know what they like and exactly how to get it, and lucky for them, these tasty teens are eager to learn, and always very eager to please!', + parameters: { siteId: 234 }, + network: 'twistys', + priority: 1, + }, + { + name: 'Twistys Hard', + slug: 'twistyshard', + alias: ['th'], + url: 'https://www.twistyshard.com', + description: 'Watch horny nymphos get stuffed with stiff, bulging cocks. Hot sluts eager to spread their legs, bend over on all fours, or mount a big rock-hard erection. They want their needs fulfilled, and love to show off how they do it. Get into Twistys Hard and see just how hard things can get!', + parameters: { siteId: 225 }, + network: 'twistys', + priority: 1, + }, + { + name: 'Feature Films', + slug: 'featurefilms', + url: 'https://www.twistys.com/scenes?site=233', + description: 'Prepare yourself for a night at the movies you\'ll never forget with Feature Films from Twistys. High-end cinematic productions featuring in-depth storylines and industry-leading visuals, erotic adventures with the most beautiful women in the world are now at your fingertips. Lesbian, Hardcore Bonus Updates!', + network: 'twistys', + }, + { + name: 'Nicole Graves', + slug: 'nicolegraves', + url: 'https://www.twistys.com/scenes?site=223', + description: 'NicoleGraves.com is the only official web site of Nicole Graves with 100% EXCLUSIVE content! Here you\'ll find Nicole Graves videos and photos of her shaved pussy and Nicole Graves fucking and giving a blowjob!', + network: 'twistys', + }, + { + name: 'Anette Dawn', + slug: 'anettedawn', + url: 'https://www.twistys.com/scenes?site=221', + description: 'Hey guys! Its me Anette Dawn, if you\'ve seen me on other sites, like Twistys I have been doing internet modeling for a while now. This is however my first and only official site. I recieved so many requests for more so I finally got this site together! I can\'t wait for you to join me inside!', + network: 'twistys', + }, + { + name: 'Twistys Teasers', + slug: 'twistysteasers', + url: 'https://www.twistys.com/scenes?site=232', + description: 'Twistys Teasers is a doorway to new exciting content, opened exclusively to you, our loyal members. See it here first while it’s fresh and hot, and be sure to let us know exactly how much you like it before you leave. Be tempted. Be tantalized. Be teased. Solo, Lesbian, Hardcore Bonus Updates!', + network: 'twistys', + }, + { + name: 'Euro Foxes', + slug: 'eurofoxes', + url: 'https://www.twistys.com/scenes?site=226', + description: 'EuroFoxes.com: the worlds Number One European Babe site! EuroFoxes is dedicated to bringing you the very best european babes!', + network: 'twistys', + }, + { + name: 'Blue Fantasies', + slug: 'bluefantasies', + url: 'https://www.twistys.com/scenes?site=220', + description: 'BlueFantasies.com prides itself on getting the most beautiful women in the world to show off their hot bodies, 100% exclusively for you.', + network: 'twistys', + }, + { + name: 'Busty Ones', + slug: 'bustyones', + url: 'https://www.twistys.com/scenes?site=229', + description: 'BustyOnes.com bringing you the most beautiful big breasts in the world! The hottest women alive showcasing their fantastic tits.', + network: 'twistys', + }, + // VIVID + { + slug: 'vividceleb', + name: 'Vivid Celeb', + url: 'https://www.vividceleb.com', + network: 'vivid', + parameters: { + referer: 'https://www.thebrats.com', + deep: 'https://www.thebrats.com/en/video', + scene: false, + lastNative: new Date('2018-03-25'), + }, + }, + { + slug: 'thebrats', + name: 'The Brats', + url: 'https://www.thebrats.com', + network: 'vivid', + }, + { + slug: 'wheretheboysarent', + name: 'Where The Boys Aren\'t', + url: 'https://www.wheretheboysarent.com', + network: 'vivid', + }, + { + slug: 'nineteen', + name: 'Nineteen', + url: 'http://www.nineteen.com', + network: 'vivid', + parameters: { + referer: 'https://www.thebrats.com', + deep: 'https://www.thebrats.com/en/video', + scene: false, + lastNative: new Date('2019-01-23'), + }, + }, + { + slug: 'nastystepfamily', + name: 'Nasty Step Family', + url: 'http://www.nastystepfamily.com', + network: 'vivid', + parameters: { + referer: 'https://www.thebrats.com', + deep: 'https://www.thebrats.com/en/video', + scene: false, + lastNative: new Date('2019-01-29'), + }, + }, + { + slug: 'girlswhofuckgirls', + name: 'Girls Who Fuck Girls', + url: 'http://www.girlswhofuckgirls.com', + network: 'vivid', + parameters: { + referer: 'https://www.thebrats.com', + deep: 'https://www.thebrats.com/en/video', + scene: false, + lastNative: new Date('2019-05-21'), + }, + }, + { + slug: 'petited', + name: 'Petited', + url: 'http://www.petited.com', + network: 'vivid', + parameters: { + referer: 'https://www.thebrats.com', + deep: 'https://www.thebrats.com/en/video', + scene: false, + lastNative: new Date('2019-01-28'), + }, + }, + { + slug: 'orgytrain', + name: 'Orgy Train', + url: 'http://www.orgytrain.com', + network: 'vivid', + parameters: { + referer: 'https://www.thebrats.com', + deep: 'https://www.thebrats.com/en/video', + scene: false, + lastNative: new Date('2019-01-09'), + }, + }, + { + slug: 'momisamilf', + name: 'Mom Is A MILF', + url: 'http://www.momisamilf.com', + network: 'vivid', + parameters: { + referer: 'https://www.thebrats.com', + deep: 'https://www.thebrats.com/en/video', + scene: false, + lastNative: new Date('2019-01-25'), + }, + }, + { + slug: 'blackwhitefuckfest', + name: 'Black White Fuck Fest', + url: 'http://www.blackwhitefuckfest.com', + network: 'vivid', + parameters: { + referer: 'https://www.thebrats.com', + deep: 'https://www.thebrats.com/en/video', + scene: false, + lastNative: new Date('2019-01-30'), + }, + }, + { + slug: '65inchhugeasses', + name: '65 Inch Huge Asses', + url: 'http://www.65inchhugeasses.com', + network: 'vivid', + parameters: { + referer: 'https://www.thebrats.com', + deep: 'https://www.thebrats.com/en/video', + scene: false, + lastNative: new Date('2019-05-18'), + }, + }, + { + slug: 'brandnewfaces', + name: 'Brand New Faces', + url: 'http://www.brandnewfaces.com', + network: 'vivid', + parameters: { + referer: 'https://www.thebrats.com', + deep: 'https://www.thebrats.com/en/video', + scene: false, + lastNative: new Date('2018-02-28'), + }, + }, + { + slug: 'vividclassic', + name: 'Vivid Classic', + url: 'http://www.vividclassic.com', + network: 'vivid', + parameters: { + referer: 'https://www.thebrats.com', + deep: 'https://www.thebrats.com/en/video', + scene: false, + lastNative: new Date('2016-06-29'), + }, + }, + // VIXEN + { + slug: 'vixen', + name: 'Vixen', + description: 'Vixen.com features the world’s finest cinematic adult films with 4K quality and high-end erotic photography.', + url: 'https://www.vixen.com', + network: 'vixen', + }, + { + slug: 'blacked', + name: 'Blacked', + description: 'Porn videos of beautiful girls in first time interracial porn videos. BLACKED has the hottest pornstars in HD sex videos.', + url: 'https://www.blacked.com', + tags: ['interracial', 'bbc'], + network: 'vixen', + }, + { + slug: 'tushy', + name: 'Tushy', + description: 'Watch the world\'s best HD Anal videos! Featuring beautiful, never before seen girls in first time anal. Exclusively on Tushy.com', + url: 'https://www.tushy.com', + tags: ['anal'], + network: 'vixen', + }, + { + slug: 'blackedraw', + name: 'Blacked Raw', + description: 'Experience real women in interracial sex videos. Passionate sex with beautiful pornstars. No photoshop just the highest quality porn. Everything you see is real.', + url: 'https://www.blackedraw.com', + tags: ['interracial', 'bbc'], + network: 'vixen', + }, + { + slug: 'tushyraw', + name: 'Tushy Raw', + description: 'Anal sex videos with beautiful models and pornstars being fucked in the ass. TUSHY RAW features famous pornstars in high quality anal porn videos.', + url: 'https://www.tushyraw.com', + tags: ['anal'], + network: 'vixen', + }, + { + slug: 'deeper', + name: 'Deeper', + description: 'Porn videos from DEEPER.com featuring Passionate sex, light kink and BDSM with plenty of erotic sex videos featuring beautiful models.', + url: 'https://www.deeper.com', + network: 'vixen', + }, + // VOGOV + { + slug: 'vogov', + name: 'VogoV', + url: 'https://www.vogov.com', + description: 'Top rated models. Graceful locations. Best gonzo scenes. 4K UHD 60 FPS. So, in general Vogov is a website that is worth visiting and exploring carefully. It gives a chance to spend a fantastic night with gorgeous girls ready to experiment and to full around with their lovers.', + network: 'vogov', + }, + // WHALE MEMBER + { + name: 'Cum 4K', + slug: 'cum4k', + url: 'https://cum4k.com', + tags: ['fake-cum', 'creampie', '4k'], + network: 'whalemember', + }, + { + name: 'Tiny 4K', + slug: 'tiny4k', + url: 'https://tiny4k.com', + tags: ['4k'], + network: 'whalemember', + }, + { + name: 'POVD', + slug: 'povd', + url: 'https://povd.com', + tags: ['pov'], + network: 'whalemember', + }, + { + name: 'Lubed', + slug: 'lubed', + url: 'https://lubed.com', + tags: ['oil'], + network: 'whalemember', + }, + { + name: 'Casting Couch X', + slug: 'castingcouchx', + alias: ['castingcouch x', 'castingcouch-x', 'casting couch-x'], + url: 'https://castingcouch-x.com', + network: 'whalemember', + }, + { + name: 'Passion HD', + slug: 'passionhd', + alias: ['phd', 'passion-hd'], + url: 'https://passion-hd.com', + network: 'whalemember', + }, + { + name: 'Nanny Spy', + slug: 'nannyspy', + url: 'https://nannyspy.com', + network: 'whalemember', + }, + { + name: 'Girl Cum', + slug: 'girlcum', + url: 'https://girlcum.com', + network: 'whalemember', + }, + { + name: 'Pure Mature', + slug: 'puremature', + url: 'https://puremature.com', + tags: ['milf'], + network: 'whalemember', + }, + { + name: 'Fantasy HD', + slug: 'fantasyhd', + alias: ['fhd'], + url: 'https://fantasyhd.com', + network: 'whalemember', + }, + { + name: 'Spy Fam', + slug: 'spyfam', + url: 'https://spyfam.com', + tags: ['family'], + network: 'whalemember', + }, + { + name: 'Holed', + slug: 'holed', + url: 'https://holed.com', + tags: ['anal'], + network: 'whalemember', + }, + { + name: 'BBC Pie', + slug: 'bbcpie', + url: 'https://bbcpie.com', + tags: ['bbc', 'interracial'], + network: 'whalemember', + }, + { + name: 'Wet VR', + slug: 'wetvr', + url: 'https://wetvr.com', + tags: ['virtual-reality'], + network: 'whalemember', + }, + { + name: 'Exotic 4K', + slug: 'exotic4k', + url: 'https://exotic4k.com', + tags: ['4k'], + network: 'whalemember', + }, + { + name: 'My Very First Time', + slug: 'myveryfirsttime', + alias: ['mvft'], + url: 'https://myveryfirsttime.com', + network: 'whalemember', + }, + { + name: 'Baeb', + slug: 'baeb', + alias: ['baebz'], + url: 'https://baeb.com', + network: 'whalemember', + }, + // WICKED + { + slug: 'wicked', + name: 'Wicked', + alias: ['wkp'], + url: 'https://www.wicked.com', + description: 'Welcome to the new Wicked.com! Watch over 25 years of Wicked Pictures\' brand of award-winning porn for couples and women in 4k HD movies & xxx videos', + parameters: { independent: true }, + network: 'wicked', + }, + // XEMPIRE + { + slug: 'hardx', + name: 'HardX', + description: "Welcome to HardX.com, home of exclusive hardcore gonzo porn and first time anal scenes, DP, blowbangs and gangbangs from today's hottest porn stars!", + url: 'https://www.hardx.com', + network: 'xempire', + }, + { + slug: 'eroticax', + name: 'EroticaX', + description: 'EroticaX.com features intimate scenes of passionate, erotic sex. Watch the sensual side of hardcore porn as your favorite pornstars have real, intense orgasms.', + url: 'https://www.eroticax.com', + network: 'xempire', + }, + { + slug: 'darkx', + name: 'DarkX', + description: 'Watch interracial BBC porn videos on DarkX.com, featuring the best pornstars taking big black cock in exclusive scenes. The best black on white porn inside!', + url: 'https://www.darkx.com', + tags: ['interracial'], + network: 'xempire', + }, + { + slug: 'allblackx', + name: 'AllBlackX', + description: 'AllBlackX.com features the hottest ebony pornstar beauties in hardcore black on black gonzo porn. From director Mason, watch 4k ultra HD videos inside', + url: 'https://www.allblackx.com', + network: 'xempire', + tags: ['ebony', 'bbc'], + }, + { + slug: 'lesbianx', + name: 'LesbianX', + description: "LesbianX.com features today's top pornstars in hardcore lesbian porn. Watch passionate & intense girl on girl sex videos, from erotic kissing to pussy licking.", + url: 'https://www.lesbianx.com', + tags: ['lesbian'], + network: 'xempire', + }, ]; /* eslint-disable max-len */ exports.seed = knex => Promise.resolve() - .then(async () => { - const networks = await knex('networks').select('*'); - const networksMap = networks.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); + .then(async () => { + const networks = await knex('networks').select('*'); + const networksMap = networks.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); - const tags = await knex('tags').select('*').where('alias_for', null); - const tagsMap = tags.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); + const tags = await knex('tags').select('*').where('alias_for', null); + const tagsMap = tags.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); - const sitesWithNetworks = sites.map(site => ({ - slug: site.slug, - name: site.name, - alias: (site.alias || []).join(','), - description: site.description, - url: site.url, - parameters: site.parameters, - network_id: networksMap[site.network], - priority: site.priority, - show: site.show, - })); + const sitesWithNetworks = sites.map(site => ({ + slug: site.slug, + name: site.name, + alias: (site.alias || []).join(','), + description: site.description, + url: site.url, + parameters: site.parameters, + network_id: networksMap[site.network], + priority: site.priority, + show: site.show, + })); - const { inserted, updated } = await upsert('sites', sitesWithNetworks, 'slug', knex); - const sitesMap = [].concat(inserted, updated).reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); + const { inserted, updated } = await upsert('sites', sitesWithNetworks, 'slug', knex); + const sitesMap = [].concat(inserted, updated).reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); - const tagAssociations = sites.map(site => (site.tags - ? site.tags.map(tagSlug => ({ - site_id: sitesMap[site.slug], - tag_id: tagsMap[tagSlug], - inherit: true, - })) - : [] - )).flat(); + const tagAssociations = sites.map(site => (site.tags + ? site.tags.map(tagSlug => ({ + site_id: sitesMap[site.slug], + tag_id: tagsMap[tagSlug], + inherit: true, + })) + : [] + )).flat(); - return upsert('sites_tags', tagAssociations, ['site_id', 'tag_id'], knex); - }); + return upsert('sites_tags', tagAssociations, ['site_id', 'tag_id'], knex); + }); /* 'X-Art' => 'xart', diff --git a/seeds/04_media.js b/seeds/04_media.js index 852c0556..e38931e4 100644 --- a/seeds/04_media.js +++ b/seeds/04_media.js @@ -2,785 +2,786 @@ const nanoid = require('nanoid/non-secure'); const upsert = require('../src/utils/upsert'); const tagPosters = [ - ['airtight', 6, 'Remy Lacroix in "Ass Worship 14" for Jules Jordan'], - ['anal', 0, 'Adriana Chechik in "Manuel Creampies Their Asses 3" for Jules Jordan'], - ['anal-creampie', 0, 'Gina Valentina and Jane Wilde in "A Very Special Anniversary" for Tushy'], - ['ass-eating', 0, 'Kendra Sunderland and Ana Foxxx in "Kendra\'s Obsession, Part 3" for Blacked'], - ['asian', 0, 'Alina Li in "Slut Puppies 8" for Jules Jordan'], - ['ass-to-mouth', 'poster', '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'], - ['blonde', 0, 'Anikka Albrite and Lena Nicole or Cherie DeVille in the BTS of "New Zealand Holiday" for In The Crack'], - ['blowbang', 'poster', 'Marsha May in "Feeding Frenzy 12" for Jules Jordan'], - ['blowjob', 0, 'Adriana Chechik in "The Dinner Party" for Real Wife Stories (Brazzers)'], - ['brunette', 0, 'Nicole Black in GIO971 for LegalPorno'], - ['bukkake', 'poster', 'Mia Malkova in "Facialized 2" for HardX'], - ['caucasian', 0, 'Remy Lacroix for HardX'], - ['creampie', 'poster', 'ALina Lopez in "Making Yourself Unforgettable" for Blacked'], - ['cum-in-mouth', 1, 'Sarah Vandella in "Blow Bang Vandella" for HardX'], - ['da-tp', 0, 'Natasha Teen in LegalPorno SZ2164'], - ['deepthroat', 0, 'Chanel Grey in "Deepthroating Is Fun" for Throated'], - ['double-anal', 7, 'Adriana Chechik in "DP Masters 6" for Jules Jordan'], - ['double-blowjob', 1, 'Veronica Rodriguez and Penny Pax in "Fucking Older Guys 5" for Penthouse'], - ['double-dildo-blowjob', 0, 'Adriana Chechik and Vicki Chase in "Anal Savages 1" for Jules Jordan'], - ['double-penetration', 2, 'Megan Rain in "DP Masters 4" for Jules Jordan'], - ['double-vaginal', 'poster', 'Riley Reid in "Pizza That Ass" for Reid My Lips'], - ['dv-tp', 'poster', 'Juelz Ventura in "Gangbanged 5" for Elegant Angel'], - ['ebony', 1, 'Ana Foxxx in "DP Me 4" for HardX'], - ['facefucking', 2, 'Jynx Maze for Throated'], - ['facial', 0, 'Brooklyn Gray in "All About Ass 4" for Evil Angel'], - ['fake-boobs', 1, 'Lela Star in "Thick" for Jules Jordan'], - ['family', 0, 'Teanna Trump in "A Family Appear: Part One" for Brazzers'], - ['gangbang', 5, 'Carter Cruise\'s first gangbang in "Slut Puppies 9" for Jules Jordan'], - ['gaping', 1, 'Vina Sky in "Vina Sky Does Anal" for HardX'], - ['interracial', 0, 'Jaye Summers and Prince Yahshua in "Platinum Pussy 3" for Jules Jordan'], - ['latina', 'poster', 'Alexis Love for Penthouse'], - ['lesbian', 0, 'Jenna Sativa and Alina Lopez in "Opposites Attract" for Girl Girl'], - ['maid', 0, 'Whitney Wright in "Dredd Up Your Ass 2" for Jules Jordan'], - ['milf', 0, 'Olivia Austin in "Dredd 3" for Jules Jordan'], - ['mff', 0, 'Madison Ivy, Adriana Chechik and Keiran Lee in "Day With A Pornstar" for Brazzers'], - ['mfm', 5, 'Vina Sky in "Slut Puppies 15" for Jules Jordan'], - ['natural-boobs', 0, 'Autumn Falls in "Manuel Ferrara\'s Ripe 7" for Jules Jordan'], - ['nurse', 0, 'Sarah Vandella in "Cum For Nurse Sarah" for Brazzers'], - ['orgy', 1, 'Megan Rain (DP), Morgan Lee (anal), Jessa Rhodes, Melissa Moore and Kimmy Granger in "Orgy Masters 8" for Jules Jordan'], - ['pussy-eating', 0, 'Kali Roses licking Emily Willis\' pussy in "Peeping On My Neighbor" for Girl Girl'], - ['redhead', 0, 'Penny Pax in "The Submission of Emma Marx: Boundaries" for New Sensations'], - ['schoolgirl', 1, 'Eliza Ibarra for Brazzers'], - ['swallowing', 'poster'], - ['teen', 0, 'Eva Elfie in "Fresh New Talent" for Club Seventeen'], - ['tattoo', 'poster', 'Kali Roses in "Goes All In For Anal" for Hussie Pass'], - ['trainbang', 'poster', 'Kali Roses in "Passing Me Around" for Blacked'], - ['triple-anal', 'poster', 'Kristy Black in SZ1986 for LegalPorno'], + ['airtight', 6, 'Remy Lacroix in "Ass Worship 14" for Jules Jordan'], + ['anal', 0, 'Adriana Chechik in "Manuel Creampies Their Asses 3" for Jules Jordan'], + ['anal-creampie', 0, 'Gina Valentina and Jane Wilde in "A Very Special Anniversary" for Tushy'], + ['ass-eating', 0, 'Kendra Sunderland and Ana Foxxx in "Kendra\'s Obsession, Part 3" for Blacked'], + ['asian', 0, 'Alina Li in "Slut Puppies 8" for Jules Jordan'], + ['ass-to-mouth', 'poster', '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'], + ['blonde', 0, 'Anikka Albrite and Lena Nicole or Cherie DeVille in the BTS of "New Zealand Holiday" for In The Crack'], + ['blowbang', 'poster', 'Marsha May in "Feeding Frenzy 12" for Jules Jordan'], + ['blowjob', 0, 'Adriana Chechik in "The Dinner Party" for Real Wife Stories (Brazzers)'], + ['brunette', 0, 'Nicole Black in GIO971 for LegalPorno'], + ['bukkake', 0, 'Jaye Summers in "Facialized 5" for HardX'], + ['caucasian', 0, 'Remy Lacroix for HardX'], + ['creampie', 'poster', 'ALina Lopez in "Making Yourself Unforgettable" for Blacked'], + ['cum-in-mouth', 1, 'Sarah Vandella in "Blow Bang Vandella" for HardX'], + ['da-tp', 0, 'Natasha Teen in LegalPorno SZ2164'], + ['deepthroat', 0, 'Chanel Grey in "Deepthroating Is Fun" for Throated'], + ['double-anal', 7, 'Adriana Chechik in "DP Masters 6" for Jules Jordan'], + ['double-blowjob', 1, 'Veronica Rodriguez and Penny Pax in "Fucking Older Guys 5" for Penthouse'], + ['double-dildo-blowjob', 0, 'Adriana Chechik and Vicki Chase in "Anal Savages 1" for Jules Jordan'], + ['double-penetration', 2, 'Megan Rain in "DP Masters 4" for Jules Jordan'], + ['double-vaginal', 'poster', 'Riley Reid in "Pizza That Ass" for Reid My Lips'], + ['dv-tp', 'poster', 'Juelz Ventura in "Gangbanged 5" for Elegant Angel'], + ['ebony', 1, 'Ana Foxxx in "DP Me 4" for HardX'], + ['facefucking', 2, 'Jynx Maze for Throated'], + ['facial', 0, 'Brooklyn Gray in "All About Ass 4" for Evil Angel'], + ['fake-boobs', 1, 'Lela Star in "Thick" for Jules Jordan'], + ['family', 0, 'Teanna Trump in "A Family Appear: Part One" for Brazzers'], + ['gangbang', 5, 'Carter Cruise\'s first gangbang in "Slut Puppies 9" for Jules Jordan'], + ['gaping', 1, 'Vina Sky in "Vina Sky Does Anal" for HardX'], + ['interracial', 0, 'Jaye Summers and Prince Yahshua in "Platinum Pussy 3" for Jules Jordan'], + ['latina', 'poster', 'Alexis Love for Penthouse'], + ['lesbian', 0, 'Jenna Sativa and Alina Lopez in "Opposites Attract" for Girl Girl'], + ['maid', 0, 'Whitney Wright in "Dredd Up Your Ass 2" for Jules Jordan'], + ['milf', 0, 'Olivia Austin in "Dredd 3" for Jules Jordan'], + ['mff', 0, 'Madison Ivy, Adriana Chechik and Keiran Lee in "Day With A Pornstar" for Brazzers'], + ['mfm', 5, 'Vina Sky in "Slut Puppies 15" for Jules Jordan'], + ['natural-boobs', 0, 'Autumn Falls in "Manuel Ferrara\'s Ripe 7" for Jules Jordan'], + ['nurse', 0, 'Sarah Vandella in "Cum For Nurse Sarah" for Brazzers'], + ['orgy', 1, 'Megan Rain (DP), Morgan Lee (anal), Jessa Rhodes, Melissa Moore and Kimmy Granger in "Orgy Masters 8" for Jules Jordan'], + ['pussy-eating', 0, 'Kali Roses licking Emily Willis\' pussy in "Peeping On My Neighbor" for Girl Girl'], + ['redhead', 0, 'Penny Pax in "The Submission of Emma Marx: Boundaries" for New Sensations'], + ['schoolgirl', 1, 'Eliza Ibarra for Brazzers'], + ['swallowing', 'poster'], + ['teen', 0, 'Eva Elfie in "Fresh New Talent" for Club Seventeen'], + ['tattoo', 'poster', 'Kali Roses in "Goes All In For Anal" for Hussie Pass'], + ['trainbang', 'poster', 'Kali Roses in "Passing Me Around" for Blacked'], + ['triple-anal', 'poster', 'Kristy Black in SZ1986 for LegalPorno'], ] - .map(([slug, filename, comment], index) => ({ - id: nanoid(), - tagSlug: slug, - path: `tags/${slug}/${filename}.jpeg`, - thumbnail: `tags/${slug}/thumbs/${filename}.jpeg`, - lazy: `tags/${slug}/lazy/${filename}.jpeg`, - mime: 'image/jpeg', - index, - comment, - })); + .map(([slug, filename, comment], index) => ({ + id: nanoid(), + tagSlug: slug, + path: `tags/${slug}/${filename}.jpeg`, + thumbnail: `tags/${slug}/thumbs/${filename}.jpeg`, + lazy: `tags/${slug}/lazy/${filename}.jpeg`, + mime: 'image/jpeg', + index, + comment, + })); const tagPhotos = [ - ['airtight', 5, 'Chloe Amour in "DP Masters 4" for Jules Jordan'], - ['airtight', 1, 'Jynx Maze in "Pump My Ass Full of Cum 3" for Jules Jordan'], - ['airtight', 2, 'Dakota Skye in "Dakota Goes Nuts" for ArchAngel'], - ['airtight', 3, 'Anita Bellini in "Triple Dick Gangbang" for Hands On Hardcore (DDF Network)'], - ['asian', 'poster', 'Vina Sky in "Slut Puppies 15" for Jules Jordan'], - // ['asian', 1, 'Alina Li in "Oil Overload 11" for Jules Jordan'], - // ['anal', 'poster', 'Jynx Maze in "Anal Buffet 6" for Evil Angel'], - ['anal', 4, 'Lana Roy in "Anal In The Club" for 21Naturals'], - ['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'], - ['caucasian', 1, 'Sheena Shaw for Brazzers'], - ['da-tp', 1, 'Francys Belle in SZ1702 for LegalPorno'], - ['da-tp', 2, 'Angel Smalls in GIO408 for LegalPorno'], - ['da-tp', 3, 'Evelina Darling in GIO294'], - ['da-tp', 4, 'Ninel Mojado aka Mira Cuckold in GIO063 for LegalPorno'], - ['double-anal', 2, 'Lana Rhoades in "Lana Rhoades Unleashed" for HardX'], - ['double-anal', 6, 'Sheena Shaw in "Ass Worship 14" for Jules Jordan'], - ['double-anal', 5, 'Riley Reid in "The Gangbang of Riley Reid" for Jules Jordan'], - ['double-anal', 'poster', 'Haley Reed in "Young Hot Ass" for Evil Angel'], - ['double-anal', 0, 'Nicole Black doing double anal during a gangbang in GIO971 for LegalPorno'], - ['double-anal', 1, 'Ria Sunn in SZ1801 for LegalPorno'], - ['double-blowjob', 0, 'Kira Noir and Kali Roses for Brazzers'], - ['double-dildo-blowjob', 1, 'Aidra Fox and Reena Sky in "Reena\'s Got A Staring Problem" for Brazzers'], - ['double-dildo-dp', 0, 'u/LacyCrow "Sometimes you have to do it yourself"'], - ['double-penetration', 'poster', 'Mia Malkova in "DP Me 8" for HardX'], - ['double-penetration', 0, 'Zoey Monroe in "Slut Puppies 7" for Jules Jordan'], - ['double-penetration', 1, 'Jynx Maze in "Don\'t Make Me Beg 4" for Evil Angel'], - ['double-vaginal', 0, 'Aaliyah Hadid in "Squirting From Double Penetration With Anal" for Bang Bros'], - ['dv-tp', 1, 'Adriana Chechik in "Adriana\'s Triple Anal Penetration!"'], - ['dv-tp', 0, 'Luna Rival in LegalPorno SZ1490'], - ['facial', 1, 'Ella Knox in "Mr Saltys Adult Emporium Adventure 2" for Aziani'], - ['facial', 'poster', 'Jynx Maze'], - ['facefucking', 1, 'Carrie for Young Throats'], - // ['fake-boobs', 0, 'Marsha May in "Once You Go Black 7" for Jules Jordan'], - ['gangbang', 'poster', 'Kristen Scott in "Interracial Gangbang!" for Jules Jordan'], - ['gangbang', 0, '"4 On 1 Gangbangs" for Doghouse Digital'], - ['gangbang', 4, 'Marley Brinx in "The Gangbang of Marley Brinx" for Jules Jordan'], - ['gangbang', 1, 'Ginger Lynn in "Gangbang Mystique", a photoset shot by Suze Randall for Puritan No. 10, 1984. This photo pushed the boundaries of pornography at the time, as depicting a woman \'fully occupied\' was unheard of.'], - ['gaping', 'poster', 'Zoey Monroe in "Manuel DPs Them All 5" for Jules Jordan'], - ['gaping', 2, 'Alex Grey in "DP Masters 5" for Jules Jordan'], - ['latina', 0, 'Abby Lee Brazil for Bang Bros'], - // ['mfm', 0, 'Vina Sky in "Jules Jordan\'s Three Ways" for Jules Jordan'], - ['mfm', 1, 'Jynx Maze in "Don\'t Make Me Beg 4" for Evil Angel'], - ['orgy', 'poster', 'Zoey Mornoe (DP), Jillian Janson (sex), Frida Sante, Katerina Kay and Natasha Starr in "Orgy Masters 6" for Jules Jordan'], - ['trainbang', 0, 'Nicole Black in GIO971 for LegalPorno'], - ['triple-anal', 1, 'Natasha Teen in SZ2098 for LegalPorno'], - ['triple-anal', 2, 'Kira Thorn in GIO1018 for LegalPorno'], - ['cum-in-mouth', 'poster', 'Khloe Kapri'], + ['airtight', 5, 'Chloe Amour in "DP Masters 4" for Jules Jordan'], + ['airtight', 1, 'Jynx Maze in "Pump My Ass Full of Cum 3" for Jules Jordan'], + ['airtight', 2, 'Dakota Skye in "Dakota Goes Nuts" for ArchAngel'], + ['airtight', 3, 'Anita Bellini in "Triple Dick Gangbang" for Hands On Hardcore (DDF Network)'], + ['asian', 'poster', 'Vina Sky in "Slut Puppies 15" for Jules Jordan'], + // ['asian', 1, 'Alina Li in "Oil Overload 11" for Jules Jordan'], + // ['anal', 'poster', 'Jynx Maze in "Anal Buffet 6" for Evil Angel'], + ['anal', 4, 'Lana Roy in "Anal In The Club" for 21Naturals'], + ['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'], + ['bukkake', 'poster', 'Mia Malkova in "Facialized 2" for HardX'], + ['caucasian', 1, 'Sheena Shaw for Brazzers'], + ['da-tp', 1, 'Francys Belle in SZ1702 for LegalPorno'], + ['da-tp', 2, 'Angel Smalls in GIO408 for LegalPorno'], + ['da-tp', 3, 'Evelina Darling in GIO294'], + ['da-tp', 4, 'Ninel Mojado aka Mira Cuckold in GIO063 for LegalPorno'], + ['double-anal', 2, 'Lana Rhoades in "Lana Rhoades Unleashed" for HardX'], + ['double-anal', 6, 'Sheena Shaw in "Ass Worship 14" for Jules Jordan'], + ['double-anal', 5, 'Riley Reid in "The Gangbang of Riley Reid" for Jules Jordan'], + ['double-anal', 'poster', 'Haley Reed in "Young Hot Ass" for Evil Angel'], + ['double-anal', 0, 'Nicole Black doing double anal during a gangbang in GIO971 for LegalPorno'], + ['double-anal', 1, 'Ria Sunn in SZ1801 for LegalPorno'], + ['double-blowjob', 0, 'Kira Noir and Kali Roses for Brazzers'], + ['double-dildo-blowjob', 1, 'Aidra Fox and Reena Sky in "Reena\'s Got A Staring Problem" for Brazzers'], + ['double-dildo-dp', 0, 'u/LacyCrow "Sometimes you have to do it yourself"'], + ['double-penetration', 'poster', 'Mia Malkova in "DP Me 8" for HardX'], + ['double-penetration', 0, 'Zoey Monroe in "Slut Puppies 7" for Jules Jordan'], + ['double-penetration', 1, 'Jynx Maze in "Don\'t Make Me Beg 4" for Evil Angel'], + ['double-vaginal', 0, 'Aaliyah Hadid in "Squirting From Double Penetration With Anal" for Bang Bros'], + ['dv-tp', 1, 'Adriana Chechik in "Adriana\'s Triple Anal Penetration!"'], + ['dv-tp', 0, 'Luna Rival in LegalPorno SZ1490'], + ['facial', 1, 'Ella Knox in "Mr Saltys Adult Emporium Adventure 2" for Aziani'], + ['facial', 'poster', 'Jynx Maze'], + ['facefucking', 1, 'Carrie for Young Throats'], + // ['fake-boobs', 0, 'Marsha May in "Once You Go Black 7" for Jules Jordan'], + ['gangbang', 'poster', 'Kristen Scott in "Interracial Gangbang!" for Jules Jordan'], + ['gangbang', 0, '"4 On 1 Gangbangs" for Doghouse Digital'], + ['gangbang', 4, 'Marley Brinx in "The Gangbang of Marley Brinx" for Jules Jordan'], + ['gangbang', 1, 'Ginger Lynn in "Gangbang Mystique", a photoset shot by Suze Randall for Puritan No. 10, 1984. This photo pushed the boundaries of pornography at the time, as depicting a woman \'fully occupied\' was unheard of.'], + ['gaping', 'poster', 'Zoey Monroe in "Manuel DPs Them All 5" for Jules Jordan'], + ['gaping', 2, 'Alex Grey in "DP Masters 5" for Jules Jordan'], + ['latina', 0, 'Abby Lee Brazil for Bang Bros'], + // ['mfm', 0, 'Vina Sky in "Jules Jordan\'s Three Ways" for Jules Jordan'], + ['mfm', 1, 'Jynx Maze in "Don\'t Make Me Beg 4" for Evil Angel'], + ['orgy', 'poster', 'Zoey Mornoe (DP), Jillian Janson (sex), Frida Sante, Katerina Kay and Natasha Starr in "Orgy Masters 6" for Jules Jordan'], + ['trainbang', 0, 'Nicole Black in GIO971 for LegalPorno'], + ['triple-anal', 1, 'Natasha Teen in SZ2098 for LegalPorno'], + ['triple-anal', 2, 'Kira Thorn in GIO1018 for LegalPorno'], + ['cum-in-mouth', 'poster', 'Khloe Kapri'], ] - .map(([slug, fileIndex, comment], index) => ({ - id: nanoid(), - tagSlug: slug, - path: `tags/${slug}/${fileIndex}.jpeg`, - thumbnail: `tags/${slug}/thumbs/${fileIndex}.jpeg`, - lazy: `tags/${slug}/lazy/${fileIndex}.jpeg`, - mime: 'image/jpeg', - index, - comment, - })); + .map(([slug, fileIndex, comment], index) => ({ + id: nanoid(), + tagSlug: slug, + path: `tags/${slug}/${fileIndex}.jpeg`, + thumbnail: `tags/${slug}/thumbs/${fileIndex}.jpeg`, + lazy: `tags/${slug}/lazy/${fileIndex}.jpeg`, + mime: 'image/jpeg', + index, + comment, + })); const sfw = Object.entries({ - animals: [ - ['7WXfIIxVlNo', 'David Boca'], - ['aMBhrrveocw', 'Ivan Diaz'], - ['LTzbD8lj0kw', 'Lionel HESRY'], - ['t2I1PTZEx4E', 'Mohamed Nanabhai'], - ['0S1qMPpHbkE', 'Thomas Giotopoulos'], - ['UnLcVKt7d7I', 'Fernando @cferdo'], - ['reXwAuEdkVM', 'Ryan Al Bishri'], - ['Hq5N_iPRkOs', 'Axel Holen'], - ['GtXsMqOR3ik', 'Sophie Dale'], - ['FYJNmIQk7JA', 'Anastasiya Romanova'], - ['pQmu6_4VDaI', 'joel herzog'], - ['j0iiByCxGfA', 'Jordan Opel'], - ['67rtZAf_Jgw', 'Sean Thoman'], - ['2puB0ahKDag', 'K. Mitch Hodge'], - ['AM9ZtoUss68', 'Erica Nilsson'], - ['7r58deUCN4Q', 'Zuriela Benitez'], - ['PQJN_Po0O1U', 'Quaid Lagan'], - ['rCkstBJusv4', 'Martin Woortman'], - ['Tj4lLm49hhM', 'Saketh Upadhya'], - ['SjtR_cCEh0w', 'Patti Black'], - ['hmn3ZiTDep4', 'Portuguese Gravity'], - ['ZC3LibyQxZQ', 'Narges Pms'], - ['l-OYOwN2gpQ', 'Pablo Guerrero'], - ['E8LxrpUjW7Q', 'Tamara Bellis'], - ['xYVRzube0iM', 'Ricardo Braham'], - ['7AzUTGOBeJI', 'Adam Thomas'], - ['9Ha6VNNsKAA', 'Thomas Evans'], - ['GNvCMZzljr0', 'Hari Nandakumar'], - ['51u3WqwzXnQ', 'Grace Evans'], - ['X8Sck0T0y8s', 'Mélody P'], - ], - kittens: [ - ['fEK4jvgnApg', 'Kim Davies'], - ['FCx5h0erwnA', 'Lorraine Steriopol'], - ['RcGUiP9dphM', 'Kym Ellis'], - ['gAPXLS1LRVE', 'Olya Kuzovkina'], - ['JHf_O0inuHg', 'Prasad Panchakshari'], - ['CKsDMYPDgCs', 'Jacalyn Beales'], - ['mQquoOszMRM', 'Dimitri Houtteman'], - ['MNju0A6EeE0', 'Amy Baugess'], - ['emI7VUcvLi0', 'The Lucky Neko'], - ['Aq4e2-v7UuQ', 'Mario Peter'], - ['6vY_jbV12kQ', 'Kazuky Akayashi'], - ['1BfCps2-XjQ', 'Ramiz Dedaković'], - ['iNEXVlX-RLs', 'Lucija Ros'], - ['b8g-ywrrl5Y', 'Leighann Blackwood'], - ['Y3gjv5x38Wc', 'Agustin Fernandez'], - ['TYXh7h4QxX0', 'Leighann Blackwood'], - ['6EchiwVg7C4', 'Jesse Borovnica'], - ['fGh_mgAcKYY', 'Šárka Jonášová'], - ['V8o2n1GbMWc', 'Hunt Han'], - ['TEnrxLKakgI', 'Diver Zhang'], - ['XZuTLRfxwcU', 'Diana Parkhouse'], - ['rpvS2T2Tl0c', 'Luiza Braun'], - ['1S08ciB6Fy4', 'Екатерина Балабанова'], - ['iuBVMDnwaho', 'Shaun Meintjes'], - ['02kGh5lGsb8', 'Kym Ellis'], - ['8ELEgu78IbU', 'Kristin Wilson'], - ['TQ0XD_mGC8c', 'Kamal Bilal'], - ['zgvb8Knw_AU', 'Zoë Gayah Jonker'], - ['GGb-AyBZhjY', 'Brett Jordan'], - ['DyD9hx5lvpg', 'James Pond'], - ], - dogs: [ - ['f7-aL0xQd6A', 'Mark Galer'], - ['kOI1RlSGsfo', 'Mark Zamora'], - ['7c8z4KEvgds', 'William Daigneault'], - ['oWrZoAVOBS0', 'William Daigneault'], - ['xwpF_Hts7jA', 'Carolinie Cavalli'], - ['72mXoMwCoq4', 'Yuki Dog'], - ['7EKROB9iGFc', 'Yuki Dog'], - ['cX-KEISwDIw', 'Cole Keister'], - ['6v-lTFEoO6E', 'Camilo Fierro'], - ['1-sM8xqPFTM', 'Dimitri Bong'], - ['uTRtIcwkbGM', 'Lenin Estrada'], - ['dpn6K9e1vzY', 'Evan Clark'], - ['M8AKGLnbi90', 'Tatiana Rodriguez'], - ['sxGJv1SUlew', 'Jon Tyson'], - ['pT9TTuuzivQ', 'William Daigneault'], - ['KKNxCHLesGc', 'Julio Arcadio Santamaría Reyes'], - ['e_Du6fAT5dI', 'Thomas Fryatt'], - ['1IqHMmYaQJE', 'Mitchell Orr'], - ['BHh-jKrTIoU', 'NICOLAS TESSARI'], - ['l6GlfPH-8y4', 'Tatiana Rodriguez'], - ['d7TLLuiwm_c', 'Brett Jordan'], - ['I_PBuIa3Bm8', 'Karl Bewick'], - ['d2hWXEV8J-8', 'Harrison Kugler'], - ['AhKXmqa_7yU', 'Yuki Dog'], - ['aH79Nc7Npas', 'Irene Garcia'], - ['v3-zcCWMjgM', 'James Barker'], - ], - architecture: [ - ['jBanV-D3T-Q', 'Dimitry Anikin'], - ['VoQ35NRfZro', 'Dabbas'], - ['_u_wI4LaT7o', 'Alex Hudson'], - ['oSmn4cbhl8w', 'Jorge Gardner'], - ['c8GdokJMjWU', 'Mert Kahveci'], - ['nfPguKj20Ac', 'Ilya Panasenko'], - ['GGxUyCgfORg', 'photo_comments'], - ['oLUPaceKme0', 'Jack Schwartz'], - ['bo6oz4m4OXY', 'Vinicius Henrique'], - ['7HYbCXD2GSA', 'Theme Inn'], - ['6K4hh4VX3T0', 'SaiKrishna Saketh'], - ['lTxOZBNZ9yM', '[2Ni]'], - ['H3mL3kocOQ4', 'Artur Matosyan'], - ['ijxxeMO3c8E', 'Larry Teo'], - ['WMrd7-CjyF0', 'Anna Claire Schellenberg'], - ['FJIFiUCOTfc', 'Kirsten Drew'], - ['9daKXiWx5Eg', 'Anastasia Dulgier'], - ['47QjuZBn5dQ', 'Murugavel Oli'], - ['yjR2ne1gtAA', 'Marius'], - ['y9vO3FWDZb0', 'bckfwd'], - ['Ro6CB6x-VUg', 'Andreas NextVoyagePL'], - ['2td44mctvmI', 'Cameron Venti'], - ['M1uoNRrNrkE', 'Willian Justen de Vasconcellos'], - ['L-2jRW74fPY', 'Daryan Shamkhali'], - ['Qr5pi1_GlvY', 'Benno Klandt'], - ['twruXW0M2Mw', 'sk'], - ['B8vwUO2NM9Y', 'Stuart Frisby'], - ['p9jBrqMSU6Q', 'Han Leentvaar'], - ['4rGlazYAV3I', 'Dmitry Bayer'], - ], - flowers: [ - ['4QLKuXKAy7k', 'BEAUFIGEAU CELINE'], - ['DlYzHwAl32g', 'HISANARI KUNIMOTO'], - ['3TXuFNun-5Q', 'Erik Andres Reynoso'], - ['Rrhfeq9yeQ0', 'NeONBRAND'], - ['YaQdJyulJdU', 'Natasha V'], - ['wt4u1wNiT9I', 'Teo Zac'], - ['HG5RcKEawLA', 'Varshesh Joshi'], - ['VBtdWGCQ2yw', 'Vanessa Ochotorena'], - ['a-lIQzpvbHs', 'Thomas AE'], - ['D5RScffd8WU', 'NeONBRAND'], - ['mXQyEcINwa8', 'Thought Catalog'], - ['REczfcmwooE', 'Steve Harvey'], - ['7pGehyH7o64', 'Leonardo Wong'], - ['wvGQYtZ_c08', 'chuttersnap'], - ['jjqZ0gvHLYc', 'Suresh Purohit'], - ['9wQ-aGu0gBs', 'Joël de Vriend'], - ['8ANiHTtHbAQ', 'Nick Karvounis'], - ['Ehko8EbURbk', 'Chris Barbalis'], - ['Rlxfn__azLQ', 'Annie Spratt'], - ['q8I3Jeph4uU', 'Jake Dela Concepcion'], - ['wQjjYp8_a0Y', 'Henry Lorenzatto'], - ['GnwWHUXBfYI', 'Will Svec'], - ['pwFNVBlj5XU', 'Danijela Froki'], - ['RWz313DUECo', 'Laura Baker'], - ['lffwXgsqChg', 'Nils Schirmer'], - ['e1O6NCmhUt4', 'Brian McGowan'], - ], - food: [ - ['XPSXhLx143g', 'Wouter Meijering'], - ['VpxavZd4S-I', 'Mr Lemon'], - ['9vHOhKoNlNw', 'Edrece Stansberry'], - ['uScYRjZ2ol8', 'donald modeste'], - ['fCE-pTmFrPI', 'Valeriu Bondarenco'], - ['1ZTccDpF71k', 'Austin Paquette'], - ['Au-LzDMd_Cw', 'Stephanie McCabe'], - ['9cfHdC2Asak', 'cindy fernandez'], - ['4-rmvQRL2nY', 'Annie Spratt'], - ['8Yvwy4Kbd1g', 'Alex Kondratiev'], - ['4PtChDuxsDI', 'Dronile Hiraldo'], - ['Mi1SNlsyWAk', 'Louis Hansel @shotsoflouis'], - ['nn0whk6nzv4', 'Yulia Chinato'], - ['8JIIoSFtEbo', 'Massimo Virgilio'], - ['44eKcVXzFoc', 'Alonso Romero'], - ['JpbtAb-f3JA', 'Dollar Gill'], - ['Hvrm3efPYIA', 'Delaney Van'], - ['4E0dknSrQVU', 'Matteo Maretto'], - ['krNP2ESq-54', 'Joshua Bedford'], - ['DPNrBT1WCMs', 'Egor Lyfar'], - ['Yy-dHQP-Ax0', 'Markus Spiske'], - ['BKSntHf8oiU', 'Melissa Walker Horn'], - ['asaGSZEyltQ', 'Noora AlHammadi'], - ['U3hCd1S7FQ4', 'Louis Hansel @shotsoflouis'], - ['VOpJTnP6S9g', 'Liana Mikah'], - ['9TWavGempJc', 'Ashleigh Robertson'], - ['I2tgHl69Dco', 'Louis Hansel @shotsoflouis'], - ['fyQr1T3GE34', 'Petr Sevcovic'], - ['pGe5mc4Eip8', 'Daniel Park'], - ['gZsgKrNc8es', 'Dan Gold'], - ['XPSXhLx143g', 'Wouter Meijering'], - ['VpxavZd4S-I', 'Mr Lemon'], - ['9vHOhKoNlNw', 'Edrece Stansberry'], - ['uScYRjZ2ol8', 'donald modeste'], - ['fCE-pTmFrPI', 'Valeriu Bondarenco'], - ['1ZTccDpF71k', 'Austin Paquette'], - ['Au-LzDMd_Cw', 'Stephanie McCabe'], - ['9cfHdC2Asak', 'cindy fernandez'], - ['4-rmvQRL2nY', 'Annie Spratt'], - ['8Yvwy4Kbd1g', 'Alex Kondratiev'], - ['4PtChDuxsDI', 'Dronile Hiraldo'], - ['Mi1SNlsyWAk', 'Louis Hansel @shotsoflouis'], - ['nn0whk6nzv4', 'Yulia Chinato'], - ['8JIIoSFtEbo', 'Massimo Virgilio'], - ['44eKcVXzFoc', 'Alonso Romero'], - ['JpbtAb-f3JA', 'Dollar Gill'], - ['Hvrm3efPYIA', 'Delaney Van'], - ['4E0dknSrQVU', 'Matteo Maretto'], - ['krNP2ESq-54', 'Joshua Bedford'], - ['DPNrBT1WCMs', 'Egor Lyfar'], - ['Yy-dHQP-Ax0', 'Markus Spiske'], - ['BKSntHf8oiU', 'Melissa Walker Horn'], - ['asaGSZEyltQ', 'Noora AlHammadi'], - ['U3hCd1S7FQ4', 'Louis Hansel @shotsoflouis'], - ['VOpJTnP6S9g', 'Liana Mikah'], - ['9TWavGempJc', 'Ashleigh Robertson'], - ['I2tgHl69Dco', 'Louis Hansel @shotsoflouis'], - ['fyQr1T3GE34', 'Petr Sevcovic'], - ['pGe5mc4Eip8', 'Daniel Park'], - ['gZsgKrNc8es', 'Dan Gold'], - ], - candy: [ - ['51AhxwkYyHo', 'Viktor Forgacs'], - ['rS1GogPLVHk', 'Eaters Collective'], - ['9m6NQHyxk-s', 'Brooke Lark'], - ['DOmaKmeCp_8', 'Sarah Takforyan'], - ['aCPOKOb4qis', 'Jamie Street'], - ['On8Ov1TItnU', 'QooQee'], - ['KN7tpVQCmWA', 'Nick Fewings'], - ['9lmFbtJ2QzM', 'Tim Gouw'], - ['54hUU5pNSvo', 'Erol Ahmed'], - ['zYH4SubXCWY', 'Наталья Горох'], - ['wJHJY7PcDcg', 'Andrew Itaga'], - ['BnusUP5jydc', 'Marc Markstein'], - ['NI_fJ15rIfI', 'Szabo Viktor'], - ['LymVMRIUwPQ', 'Happy Films'], - ['mrNVnLEphdo', 'Greg Nunes'], - ['FKvoEKSV2LY', 'zhou yu'], - ['CKLF34baCTQ', 'Willian Justen de Vasconcellos'], - ['7uGCN9qshsY', 'Siora Photography'], - ['xBTnaTgleQE', 'Glen Carrie'], - ['sC_HExGwbhI', 'Erik Mclean'], - ['1gViVVlEaPc', 'Sérgio André'], - ['k9yY0XZTSnI', 'Fernando Hernandez'], - ['h1_R9-o9an0', 'emrecan arık'], - ['XB0ha-DSGoU', 'Laura Briedis'], - ['ONn4OfAnxZY', 'Monique Carrati'], - ['24p9dPeXdFA', 'Frederic Köberl'], - ['GL6J_sFYHLw', 'Yuiizaa September'], - ['LU_fCezP9-o', 'Amit Lahav'], - ['CSkAj_XqOVc', 'Mockaroon'], - ['xLvIcAYuuMQ', 'Luis Aguila'], - ], - fruit: [ - ['ZLc9yTIFzNk', 'Kelly Sikkema'], - ['DoxGtpAsdYY', 'Sanni Sahil'], - ['lyEkpuuIrg0', 'kaouther djouada'], - ['fDx4zHpnlOM', 'Nanxi wei'], - ['0AXNt5SdeXU', 'Sara Cervera'], - ['s05XKB6jK2c', 'Markus Spiske'], - ['bPMyJzKhCyA', 'Louis Hansel @shotsoflouis'], - ['4jeWN4puDrw', 'Carolyn Leber'], - ['8EScigZC6AU', 'MF Evelyn'], - ['1QbosWMxOx0', 'elCarito'], - ['NWA3s8r-1bc', 'Fli Hi'], - ['TfNUmbaIjj8', 'Sophie Dale'], - ['duskNBVv420', 'Louis Hansel @shotsoflouis'], - ['QYupKZjYDbw', 'Moritz Kindler'], - ['ozcHSq3XfVs', 'Callum Blacoe'], - ['ucY_-U1dM8U', 'Louis Hansel @shotsoflouis'], - ['4WLc_dWE-kc', 'Dmitry Mishin'], - ['BTqDasyX62E', 'Priscilla Du Preez'], - ['SD5dOSkhQdU', 'Alexander Mils'], - ['4pJekgmSmPM', 'Rinck Content Studio'], - ['0XGWys_GaFo', 'Toa Heftiba'], - ['uTZvsJsylYc', 'Louis Hansel @shotsoflouis'], - ['vAHaYh5s_Sc', 'RAPHAEL MAKSIAN'], - ['0N4A1c5tJSs', 'Miguel Vaz'], - ['VAhUq30sW0c', 'Cody Berg'], - ['qbO7Mlhq8PQ', 'JOSHUA COLEMAN'], - ['dLB32q_bRs0', 'Jason Leung'], - ['66itnKdGlC4', 'Fateme Azimi'], - ['_DYRZHbCIq0', 'Will Mcmahon'], - ], - landscapes: [ - ['FuaNmJPLAHg', 'MINSUN KIM'], - ['8flZ753v87Q', 'Ken Schlabach'], - ['6qdO_nFhYoI', 'Clay Banks'], - ['VMmPxFmfwfQ', 'Xiaopeng Ma'], - ['DAufF3R8B5Q', 'Sangga Rima Roman Selia'], - ['5U9n9gip7VY', 'Annie Spratt'], - ['Tot7FLHWotI', 'elCarito'], - ['vph4L_OjhuI', 'Marek Piwnicki'], - ['Nd8h38tRIlc', 'Kevin Horvat'], - ['nACRxCEAut8', 'ALEKSEY KUPRIKOV'], - ['PjyfZ-Ujut8', 'Denys Nevozhai'], - ['X6utHocVX8w', 'Ian Williams'], - ['cq6g1WssaJI', 'Tyler Casey'], - ['U1ad7OjdHx8', 'Dave Herring'], - ['si5673B4PIE', 'Annie Spratt'], - ['shv8H0Dv5iE', 'Aditya Chinchure'], - ['hjVrWFVyOqE', 'chuttersnap'], - ['_skrrHIcsEk', 'Edward Ma'], - ['GMNtcgo-KdA', 'Yoav Hornung'], - ['313dueuxHls', 'Jamie Street'], - ['SncOlKOY7NY', 'Ward Mercer'], - ['7R11NCmOY6k', 'Ivan Bandura'], - ['YFwOiSisSLM', 'Krzysztof Kotkowicz'], - ['gbO7zCOUJIg', 'Brian Kyed'], - ['5iwG4xlUgJ4', 'Jason Leung'], - ['G_PSim-dsvU', 'Alexandra Jitariuc'], - ['gVl6Hsi_pxo', 'Jean-Philippe Delberghe'], - ['p8h0_0pyW9k', 'Bence Balla-Schottner'], - ], - waterfalls: [ - ['Cwd0zYOIClY', 'Thomas Ensley'], - ['dGCHAo7mb2Q', 'Jamie Melville'], - ['bukitR21NO8', 'Jen Theodore'], - ['IKIY5bpd9eM', 'Bruno Kelzer'], - ['rrVm3b-uxkk', 'Jamie Melville'], - ['UegnUIW76gQ', 'Jamie Melville'], - ['d0cvao4fC6Q', 'Forest Simon'], - ['N8r2hH1siEY', 'Lester Hine'], - ['oRmeWoJx_nY', 'Michael Olsen'], - ['jVjwlfyJAkA', 'Tom Wheatley'], - ['ar6PYxBFgis', 'Gerald Berliner'], - ['9fpK7fPw6Is', 'Jamie Melville'], - ['jeXkw2HR1SU', 'Julia Caesar'], - ['Evs5MnlmUXY', 'Damian Kamp'], - ['phstcH4QKJc', 'Jakob Owens'], - ['7dmDlBfB9Vk', 'Spencer Watson'], - ['SHhaNnNR1xo', 'SaiKrishna Saketh'], - ['_6ghImrDiVU', 'Daniel H. Tong'], - ['gfaXzDmMY7M', 'RADIN AHMAD MUJAHID'], - ['rFbK1PP9LEA', 'Daniel Hernandez'], - ['s6Tv7b4SAoo', 'Miltiadis Fragkidis'], - ['ir9RUDjVpPo', 'Clay Banks'], - ['7emiteIwfuk', 'yunchuan luo'], - ['9tqrKo4B98I', 'Austin Farrington'], - ['dfazzUCjMro', 'oakie'], - ['4HEuHmA7WfM', 'Ketan Morris'], - ['eG1_rPekhTk', 'Jack B'], - ['_up7EDGdTqU', 'Hari Nandakumar'], - ['MhIPEJmVwaA', 'Karthik Chandran'], - ], - travel: [ - ['gC2Q_Tfub6c', 'James Lee'], - ['1Tcu61Qz7c0', 'Payas'], - ['eLMJ2x7s9ak', 'Alexandre Chambon'], - ['aNrRsB2wLDk', 'Ahmet Yalçınkaya'], - ['xu2WYJek5AI', 'Anastasia Petrova'], - ['xRoexKKv8fs', 'Robson Hatsukami Morgan'], - ['qdIer4A0afE', 'Camille Brodard'], - ['0lPZBa6-1J8', 'Torbjorn Sandbakk'], - ['CLm3pWXrS9Q', 'Tim Trad'], - ['D1fs2x11_jk', 'Raul Taciu'], - ['BZ3aE3ouAfc', 'Petr Sevcovic'], - ['_OUvt8kLf0s', 'GAWN AUSTRALIA'], - ['T_6yJJQ_-wA', 'Square Lab'], - ['1Z_mX3zzEBc', 'Andy Pearce'], - ['lWHJwoSZf7M', 'Joseph Costa'], - ['O6Euhw7NMbQ', 'Cosmic Timetraveler'], - ['bs1eqd6zSiU', 'Harley-Davidson'], - ['_qkuc1V9Gbg', 'Ken Cheung'], - ], - cars: [ - ['w5SZe8hoqlk', 'Rachel Lynette French'], - ['Nwk0ye_Y_As', 'Lex Valishvili'], - ['P9yqMy_9ZaY', 'Anastasia Dulgier'], - ['aGwBtbncMWo', 'Sandra Kaas'], - ['jV_QaRjbhWE', 'Clem Onojeghuo'], - ['SwmaJDvasuU', 'Gunnar Ridderström'], - ['BsJovWrQ7vE', 'Caroline'], - ['3t0Q-GYXE0U', 'Austin Park'], - ['tlCzxI2RQAc', 'Vinícius Henrique'], - ['fecFQcxsUok', 'Zachary Spears'], - ['upttrzCo8-U', 'takahiro taguchi'], - ['zoHustBfNxA', 'Lukas Werner'], - ['UZH69YA50qY', 'Austin Park'], - ['tJwY80NMkhk', 'Hanny Naibaho'], - ['esfNTaHsR1c', 'Willian Justen de Vasconcellos'], - ['Tas1kF-6aNA', 'Tyler Casey'], - ['7nrsVjvALnA', 'Denys Nevozhai'], - ['CRf3KYexpCA', 'Will Truettner'], - ['B8JpogxOnyw', 'Mike Von'], - ['mU5vnpJW_CQ', 'Luke Tanis'], - ['GaRPxo9Z86M', 'Court Cook'], - ['Orp-VAQ_gNA', 'Angello Lopez'], - ['q4UZ53rYYy0', 'Ali Moharami'], - ['XA2OEcvrrP8', 'Alexander Pidgeon'], - ['hXMv04v_py0', 'Patrick Schöpflin'], - ['G7sWGEF8pRc', 'Julian Hochgesang'], - ['PUnARRf-rE8', 'jean wimmerlin'], - ['UIk-rF4Df60', 'Igor Bumba'], - ['0dW-eQVL0WQ', 'Michael Heuser'], - ], - aviation: [ - ['bw1hXT_okL4', 'sayhitobel'], - ['ecOXN5jGtaU', 'Alexander Lobanov'], - ['rFujmwrNv1w', 'Abe Drzycimski'], - ['EKYsla2fER8', 'Paola Aguilar'], - ['1uiXp6fXd3w', 'Kevin Hackert'], - ['tVzGTraJ4T0', 'Samantha Gades'], - ['5h1-cHxJz1I', 'Nikli Nodin'], - ['u-1cYIua_aI', 'John Torcasio'], - ['l2OusPPMLxs', 'yvette Wynne'], - ['SUIvWFHBZas', 'Timothy Newman'], - ['Tvrnezn1N6g', 'Daniel Eledut'], - ['ZN0469D3v98', 'Jason Hafso'], - ['LNmKCmZ2pcI', 'Ricardo Resende'], - ['P_ExwnnPGyM', 'Tudose Alexandru'], - ['1vyWYdyUDGU', 'Ian Cumming'], - ['k102QVrpitQ', 'Jake Blucker'], - ['eB8-XtMtqZI', 'Miguel Ángel Sanz'], - ['1XDZavWyows', 'Samuel Sianipar'], - ['7WHD-pdECGU', 'Miguel Ángel Sanz'], - ['CN8HsCUCjUQ', 'Cameron Kitson'], - ['q_rNy9pRe78', 'Walter Walraven'], - ['a9SA6Zs1L9g', 'Tim Dennert'], - ['JW50PRr5UbI', 'Roland O'], - ['um_6nrOnPZ4', 'Pascal Meier'], - ['EpxT58kpBhc', 'Terence Burke'], - ['3fbjHj2k0vE', 'Dušan Smetana'], - ['9o4p3eCcRBE', 'Jeremy Bishop'], - ['sbfLwfoVX7E', 'Nour Betar'], - ['4VCJBAtlnNI', 'sippakorn yamkasikorn'], - ['LcbsrFbqwGk', 'Angel Barnes'], - ], - nature: [ - ['io0umElGQwU', 'Fabrizio Forte'], - ['lh2sQ_L3GaA', 'Daniel Lord'], - ['Gyr7o3BMsSc', 'Dave Francis'], - ['lRipDukRpd4', 'Kevin'], - ['GxymWkdnl4Y', 'Fernando Jorge'], - ['rWnw7JL0LPE', 'Rahul Gupta'], - ['YKuYS3ChMyw', 'Yuliya Kosolapova'], - ['ZsUjLYqbNz8', 'Pau Morfín'], - ['x2o3HX6RKG8', 'Dave'], - ['Few073pm6aQ', 'Dennis Bertuch'], - ['jP-wvLA7uyg', 'Gary Butterfield'], - ['euhs3wanL-I', 'Andrew Stapleton'], - ['UHt2TG_CHO0', 'Wolfgang Hasselmann'], - ['SAIb5NkbAaE', 'Alexis Gethin'], - ['tdNjdIW-_OY', 'Lachlan'], - ['0NtjFr0-DwM', 'Jane Carmona'], - ['fhg_nm1sufU', 'Pascal Debrunner'], - ['HJUDECvtxZE', 'jose alfonso sierra'], - ['8vS1CwHIEH8', 'Serge Sustavov'], - ['oTTnZcwQ2t4', 'Daniel Radford'], - ['TNhs9udas8Q', 'Migsar Navarro'], - ['zT9g3crTPcE', 'Devin L'], - ['hYuNG1zptM4', 'Roberto Gamito'], - ['DfOQRep1LKU', 'Wolfgang Rottmann'], - ['2sT_iJhIcEg', 'Anamika Pokharel'], - ['Zjcl2vaeGdo', 'Lucas Mitchell'], - ['b2T6zgFAW1E', 'Theme Inn'], - ['EnFQmcTtsjo', 'Yang Jing'], - ['PWI-CDVynhw', 'Thomas Galler'], - ['gTvLsDi-Uzo', 'Arun Raj'], - ], - trees: [ - ['aSCx7M1E4Vo', 'Markos Mant'], - ['3B9MYDjQ-EQ', 'Firasat Durrani'], - ['39oaRqfUacc', 'Nick'], - ['Y0NLa_KmEgU', 'Michael Ankes'], - ['i6nGVkbbUT4', 'Henry McIntosh'], - ['_WITb4g1ImY', 'Mark Basarab'], - ['prd5CXMsD68', 'JOHN TOWNER'], - ['avxSevmCKfw', 'Marta Pawlik'], - ['LZf0HMPg-3I', 'Max Brinton'], - ['Ovjx9FsXX9c', 'Andy Fitzsimon'], - ['YPtfMXRMUe0', 'Corey Agopian'], - ['h3y3XPXKoeY', 'Elke Karin Lugert'], - ['qeGC1zrsG8U', 'Tycho Atsma'], - ['dAkd3xaSDTA', 'Nick West'], - ['Prd-KB7CKo4', 'Austin Neill'], - ['iqu9ZTwTfVM', 'Clem Onojeghuo'], - ['klNpWLkgezo', 'Wil Stewart'], - ['Pm4U5IqI4dM', 'Lena Derevianko'], - ['5i664o1oY4c', 'Yannick Pulver'], - ['uoMFPm5_Xg8', 'Yoni Kozminsi'], - ['Qr2dZRNdf_M', 'Afifi Zulkifle'], - ['jxljuYqn8uU', 'Sebastian Pichler'], - ['H3mO3zNlvNU', 'Jonathan Knepper'], - ['pEb-Xf_qM0s', 'Martin Reisch'], - ['prSogOoFmkw', 'Braden Jarvis'], - ['y9csmronT3s', 'Alberto Restifo'], - ['R8R9H_xuvBY', 'Andrew Pons'], - ['fuT-m1yzUG4', 'Ales Krivec'], - ], - wildlife: [ - ['Tot7FLHWotI', 'elCarito'], - ['-I0EgcZdV0E', 'Maarten van den Heuvel'], - ['F21zt7_icpo', 'Tevin Trinh'], - ['gMxgp-MwCyQ', 'James Wainscoat'], - ['FFlb5Uj3vhc', 'Sigmund'], - ['hglSMjdh83M', 'Srinivasan Venkataraman'], - ['UgWcvfl4q5I', 'NOAA'], - ['PZ1nEPFNBJQ', 'Third Idea'], - ['f4yYs5P5GbA', 'rigel'], - ['e94T5ag-9x0', 'Jayden Brand'], - ['6Fcllk7ze_Q', 'Vivian Arcidiacono'], - ['O7G3II8E2Eo', 'Erik-Jan Leusink'], - ['-6UNL6Ghn_c', 'Silvio Kundt'], - ['EyZMGYn1Uj4', 'AGL Fotos'], - ['nxR7gvSokH8', 'Waldemar Brandt'], - ['sAGXVK6bNFc', 'Amar Yashlaha'], - ['8zLCXDWETEg', 'Clément ROY'], - ['4nPFQ2sUhUE', 'Justin Porter'], - ['mUNDTQrfnSk', 'Zahrin Lukman'], - ['VXcX0Joa09k', 'Max Rovensky'], - ['F_HycxA2lwc', 'Marthijn Brinks'], - ['ss01halnU4I', 'Dušan Smetana'], - ['lktWv61WoNI', 'Rory Lindholm'], - ['myeQ2RH1PX0', 'Alexander Ross'], - ['dhIAyAmfjz8', 'Third Idea'], - ['AGprQpF4STo', 'Jeff Lemond'], - ['P8pBJQVt4UA', 'Christer Gundersen'], - ['nt8Ek7sRgdA', 'Mikell Darling'], - ['YHv0BDThVOw', 'Nicholas Doherty'], - ], - interior: [ - ['9wGKENQ-qTI', 'Kunj Parekh'], - ['x3BCSWCAtrY', 'yann maignan'], - ['EF6z_6R94zQ', 'Orlova Maria'], - ['dC8NC2QBFyQ', 'Kari Shea'], - ['A_AuirVquQY', 'Abbie Bernet'], - ['IJf2v-StB4Y', 'yann maignan'], - ['frsN89Tf-1Q', 'Joshua Eckstein'], - ['s95oB2n9jng', 'Aaron Huber'], - ['n_vdmdtNh6M', 'Tiplada Mekvisan'], - ['GbiVL6t4T-o', 'gdtography'], - ['xx0oSB1YxRE', 'Joseph Albanese'], - ['McaNtoPEEcg', 'Brooke Cagle'], - ['s65VlkIYSDw', 'Robert Bye'], - ['pEaBEqXXk-M', 'Aw Creative'], - ['GaX7QTgs8pg', 'Waldemar Brandt'], - ['b75FBg97dU0', 'Bao Menglong'], - ['5i0GnoTTjSE', 'Philipp Berndt'], - ['buhmhprfo3g', 'Kara Eads'], - ['L7EwHkq1B2s', 'Kara Eads'], - ['gBdirnalxcQ', 'Nick Fewings'], - ['ILgaxpiQu0', 'Matteo Maretto'], - ['ydcMwcfY5E0', 'Clark Street Mercantile'], - ['zLT3VqWEgOQ', 'Sidekix Media'], - ['Yg0Rds6_TsY', 'Sergiu Cindea'], - ['gbS_fhrFo10', 'Bekah Russom'], - ['o3c-euNd_ZM', 'Alessandra Caretto'], - ['IEkMMvdZFc0', 'Nick Hillier'], - ['hnhE83RhAWg', 'Stefan Steinbauer'], - ], - statues: [ - ['ig8E7Mlrl7Y', 'Vidar Nordli-Mathisen'], - ['1ak3Z7ZmtQA', 'Ryan Lum'], - ['ntPF02wcTY', 'Gigi'], - ['5_i4OPeOAZU', 'Viktor Forgacs'], - ['iRON0g6iO0k', 'Alexandre Chambon'], - ['PhQ4CpXLEX4', 'Daniels Joffe'], - ['EfHqouvZU2Y', 'Bettina Otott Kovács'], - ['kaEhf0eZme8', 'Nils'], - ['4-4IDc21Gto', 'K. Mitch Hodge'], - ['Y8Xh7ZJFU5A', 'Vidar Nordli-Mathisen'], - ['fmawALmMLSA', 'Gabriel TRESCH'], - ['DUp4B6M0AMc', 'Sebastien'], - ['MFZUY4gqvA4', 'James Yarema'], - ['50vvwcNFFzU', 'Robert Anasch'], - ['TMRi8cD2umM', 'Frank Eiffert'], - ['wLx_WCkWvHg', 'Chris A. Tweten'], - ['Twoj21Av-so', 'Arthur Reeder'], - ['EiGDn8cwU4Y', 'Tessa Rampersad'], - ['fVY6UxZuECA', 'Christine Wehrmeier'], - ['uJdTBTJ9rbo', 'Christine Wehrmeier'], - ['4eEBFTBKx5E', 'Ralph Spandl'], - ['HtQRGemW_40', 'Ivan Bertona'], - ['uHBcinxOLhQ', 'K. Mitch Hodge'], - ['2TmsyZXMNTE', 'Emma Fabbri'], - ['9KkPloRgOUY', 'Matteo Maretto'], - ['KzPefInJW58', 'JOSHUA COLEMAN'], - ['szVTIkisN1M', 'David Siglin'], - ['iRzEPkYSETQ', 'Francois Olwage'], - ], - technology: [ - ['QpTCSHzhWuo', 'Joshua Hoehne'], - ['0lMpQaXfOCg', 'Barrett Ward'], - ['w33-zg-dNL4', 'Rami Al-zayat'], - ['MC5WbGo_bZM', 'Tom Pumford'], - ['iHJ7xouUyXs', 'Amith Nair'], - ['E3I9thV98kQ', 'Tatiana Lapina'], - ['JuUK7Er9nR4', 'Mohamed Boumaiza'], - ['Dei5oAC_wJc', 'Kenny Luo'], - ['ltwEbf_G9bs', 'Mario Caruso'], - ['gWdlDR4WpV4', 'Zarak Khan'], - ['0Um6Yr1cyx0', 'Antoine Beauvillain'], - ['_8S9nEmCZK0', 'Oliur'], - ['etFrnBJS1qc', 'NeONBRAND'], - ['ZMVtx_KJtOk', 'Thought Catalog'], - ['JNuKyKXLh8U', 'Noiseporn'], - ['5gzr-RM-rZM', 'Kenny Luo'], - ['eWaXmZsXKDs', 'Zane Lee'], - ['4qGbMEZb56c', 'Thomas William'], - ['hwqWxHoH2wk', 'Markus Spiske'], - ['vZJdYl5JVXY', 'Kaitlyn Baker'], - ['Lg8xTZjs6Lg', 'Marc Mueller'], - ['M5HQPjXrjlQ', 'Matt Hoffman'], - ['A-b37b-CrYE', 'Kenny Luo'], - ['Kj2SaNHG-hg', 'Christopher Burns'], - ['A1v0-iH3T5A', 'Patrick Hendry'], - ['iFBIdX54BOk', 'Keagan Henman'], - ], + animals: [ + ['7WXfIIxVlNo', 'David Boca'], + ['aMBhrrveocw', 'Ivan Diaz'], + ['LTzbD8lj0kw', 'Lionel HESRY'], + ['t2I1PTZEx4E', 'Mohamed Nanabhai'], + ['0S1qMPpHbkE', 'Thomas Giotopoulos'], + ['UnLcVKt7d7I', 'Fernando @cferdo'], + ['reXwAuEdkVM', 'Ryan Al Bishri'], + ['Hq5N_iPRkOs', 'Axel Holen'], + ['GtXsMqOR3ik', 'Sophie Dale'], + ['FYJNmIQk7JA', 'Anastasiya Romanova'], + ['pQmu6_4VDaI', 'joel herzog'], + ['j0iiByCxGfA', 'Jordan Opel'], + ['67rtZAf_Jgw', 'Sean Thoman'], + ['2puB0ahKDag', 'K. Mitch Hodge'], + ['AM9ZtoUss68', 'Erica Nilsson'], + ['7r58deUCN4Q', 'Zuriela Benitez'], + ['PQJN_Po0O1U', 'Quaid Lagan'], + ['rCkstBJusv4', 'Martin Woortman'], + ['Tj4lLm49hhM', 'Saketh Upadhya'], + ['SjtR_cCEh0w', 'Patti Black'], + ['hmn3ZiTDep4', 'Portuguese Gravity'], + ['ZC3LibyQxZQ', 'Narges Pms'], + ['l-OYOwN2gpQ', 'Pablo Guerrero'], + ['E8LxrpUjW7Q', 'Tamara Bellis'], + ['xYVRzube0iM', 'Ricardo Braham'], + ['7AzUTGOBeJI', 'Adam Thomas'], + ['9Ha6VNNsKAA', 'Thomas Evans'], + ['GNvCMZzljr0', 'Hari Nandakumar'], + ['51u3WqwzXnQ', 'Grace Evans'], + ['X8Sck0T0y8s', 'Mélody P'], + ], + kittens: [ + ['fEK4jvgnApg', 'Kim Davies'], + ['FCx5h0erwnA', 'Lorraine Steriopol'], + ['RcGUiP9dphM', 'Kym Ellis'], + ['gAPXLS1LRVE', 'Olya Kuzovkina'], + ['JHf_O0inuHg', 'Prasad Panchakshari'], + ['CKsDMYPDgCs', 'Jacalyn Beales'], + ['mQquoOszMRM', 'Dimitri Houtteman'], + ['MNju0A6EeE0', 'Amy Baugess'], + ['emI7VUcvLi0', 'The Lucky Neko'], + ['Aq4e2-v7UuQ', 'Mario Peter'], + ['6vY_jbV12kQ', 'Kazuky Akayashi'], + ['1BfCps2-XjQ', 'Ramiz Dedaković'], + ['iNEXVlX-RLs', 'Lucija Ros'], + ['b8g-ywrrl5Y', 'Leighann Blackwood'], + ['Y3gjv5x38Wc', 'Agustin Fernandez'], + ['TYXh7h4QxX0', 'Leighann Blackwood'], + ['6EchiwVg7C4', 'Jesse Borovnica'], + ['fGh_mgAcKYY', 'Šárka Jonášová'], + ['V8o2n1GbMWc', 'Hunt Han'], + ['TEnrxLKakgI', 'Diver Zhang'], + ['XZuTLRfxwcU', 'Diana Parkhouse'], + ['rpvS2T2Tl0c', 'Luiza Braun'], + ['1S08ciB6Fy4', 'Екатерина Балабанова'], + ['iuBVMDnwaho', 'Shaun Meintjes'], + ['02kGh5lGsb8', 'Kym Ellis'], + ['8ELEgu78IbU', 'Kristin Wilson'], + ['TQ0XD_mGC8c', 'Kamal Bilal'], + ['zgvb8Knw_AU', 'Zoë Gayah Jonker'], + ['GGb-AyBZhjY', 'Brett Jordan'], + ['DyD9hx5lvpg', 'James Pond'], + ], + dogs: [ + ['f7-aL0xQd6A', 'Mark Galer'], + ['kOI1RlSGsfo', 'Mark Zamora'], + ['7c8z4KEvgds', 'William Daigneault'], + ['oWrZoAVOBS0', 'William Daigneault'], + ['xwpF_Hts7jA', 'Carolinie Cavalli'], + ['72mXoMwCoq4', 'Yuki Dog'], + ['7EKROB9iGFc', 'Yuki Dog'], + ['cX-KEISwDIw', 'Cole Keister'], + ['6v-lTFEoO6E', 'Camilo Fierro'], + ['1-sM8xqPFTM', 'Dimitri Bong'], + ['uTRtIcwkbGM', 'Lenin Estrada'], + ['dpn6K9e1vzY', 'Evan Clark'], + ['M8AKGLnbi90', 'Tatiana Rodriguez'], + ['sxGJv1SUlew', 'Jon Tyson'], + ['pT9TTuuzivQ', 'William Daigneault'], + ['KKNxCHLesGc', 'Julio Arcadio Santamaría Reyes'], + ['e_Du6fAT5dI', 'Thomas Fryatt'], + ['1IqHMmYaQJE', 'Mitchell Orr'], + ['BHh-jKrTIoU', 'NICOLAS TESSARI'], + ['l6GlfPH-8y4', 'Tatiana Rodriguez'], + ['d7TLLuiwm_c', 'Brett Jordan'], + ['I_PBuIa3Bm8', 'Karl Bewick'], + ['d2hWXEV8J-8', 'Harrison Kugler'], + ['AhKXmqa_7yU', 'Yuki Dog'], + ['aH79Nc7Npas', 'Irene Garcia'], + ['v3-zcCWMjgM', 'James Barker'], + ], + architecture: [ + ['jBanV-D3T-Q', 'Dimitry Anikin'], + ['VoQ35NRfZro', 'Dabbas'], + ['_u_wI4LaT7o', 'Alex Hudson'], + ['oSmn4cbhl8w', 'Jorge Gardner'], + ['c8GdokJMjWU', 'Mert Kahveci'], + ['nfPguKj20Ac', 'Ilya Panasenko'], + ['GGxUyCgfORg', 'photo_comments'], + ['oLUPaceKme0', 'Jack Schwartz'], + ['bo6oz4m4OXY', 'Vinicius Henrique'], + ['7HYbCXD2GSA', 'Theme Inn'], + ['6K4hh4VX3T0', 'SaiKrishna Saketh'], + ['lTxOZBNZ9yM', '[2Ni]'], + ['H3mL3kocOQ4', 'Artur Matosyan'], + ['ijxxeMO3c8E', 'Larry Teo'], + ['WMrd7-CjyF0', 'Anna Claire Schellenberg'], + ['FJIFiUCOTfc', 'Kirsten Drew'], + ['9daKXiWx5Eg', 'Anastasia Dulgier'], + ['47QjuZBn5dQ', 'Murugavel Oli'], + ['yjR2ne1gtAA', 'Marius'], + ['y9vO3FWDZb0', 'bckfwd'], + ['Ro6CB6x-VUg', 'Andreas NextVoyagePL'], + ['2td44mctvmI', 'Cameron Venti'], + ['M1uoNRrNrkE', 'Willian Justen de Vasconcellos'], + ['L-2jRW74fPY', 'Daryan Shamkhali'], + ['Qr5pi1_GlvY', 'Benno Klandt'], + ['twruXW0M2Mw', 'sk'], + ['B8vwUO2NM9Y', 'Stuart Frisby'], + ['p9jBrqMSU6Q', 'Han Leentvaar'], + ['4rGlazYAV3I', 'Dmitry Bayer'], + ], + flowers: [ + ['4QLKuXKAy7k', 'BEAUFIGEAU CELINE'], + ['DlYzHwAl32g', 'HISANARI KUNIMOTO'], + ['3TXuFNun-5Q', 'Erik Andres Reynoso'], + ['Rrhfeq9yeQ0', 'NeONBRAND'], + ['YaQdJyulJdU', 'Natasha V'], + ['wt4u1wNiT9I', 'Teo Zac'], + ['HG5RcKEawLA', 'Varshesh Joshi'], + ['VBtdWGCQ2yw', 'Vanessa Ochotorena'], + ['a-lIQzpvbHs', 'Thomas AE'], + ['D5RScffd8WU', 'NeONBRAND'], + ['mXQyEcINwa8', 'Thought Catalog'], + ['REczfcmwooE', 'Steve Harvey'], + ['7pGehyH7o64', 'Leonardo Wong'], + ['wvGQYtZ_c08', 'chuttersnap'], + ['jjqZ0gvHLYc', 'Suresh Purohit'], + ['9wQ-aGu0gBs', 'Joël de Vriend'], + ['8ANiHTtHbAQ', 'Nick Karvounis'], + ['Ehko8EbURbk', 'Chris Barbalis'], + ['Rlxfn__azLQ', 'Annie Spratt'], + ['q8I3Jeph4uU', 'Jake Dela Concepcion'], + ['wQjjYp8_a0Y', 'Henry Lorenzatto'], + ['GnwWHUXBfYI', 'Will Svec'], + ['pwFNVBlj5XU', 'Danijela Froki'], + ['RWz313DUECo', 'Laura Baker'], + ['lffwXgsqChg', 'Nils Schirmer'], + ['e1O6NCmhUt4', 'Brian McGowan'], + ], + food: [ + ['XPSXhLx143g', 'Wouter Meijering'], + ['VpxavZd4S-I', 'Mr Lemon'], + ['9vHOhKoNlNw', 'Edrece Stansberry'], + ['uScYRjZ2ol8', 'donald modeste'], + ['fCE-pTmFrPI', 'Valeriu Bondarenco'], + ['1ZTccDpF71k', 'Austin Paquette'], + ['Au-LzDMd_Cw', 'Stephanie McCabe'], + ['9cfHdC2Asak', 'cindy fernandez'], + ['4-rmvQRL2nY', 'Annie Spratt'], + ['8Yvwy4Kbd1g', 'Alex Kondratiev'], + ['4PtChDuxsDI', 'Dronile Hiraldo'], + ['Mi1SNlsyWAk', 'Louis Hansel @shotsoflouis'], + ['nn0whk6nzv4', 'Yulia Chinato'], + ['8JIIoSFtEbo', 'Massimo Virgilio'], + ['44eKcVXzFoc', 'Alonso Romero'], + ['JpbtAb-f3JA', 'Dollar Gill'], + ['Hvrm3efPYIA', 'Delaney Van'], + ['4E0dknSrQVU', 'Matteo Maretto'], + ['krNP2ESq-54', 'Joshua Bedford'], + ['DPNrBT1WCMs', 'Egor Lyfar'], + ['Yy-dHQP-Ax0', 'Markus Spiske'], + ['BKSntHf8oiU', 'Melissa Walker Horn'], + ['asaGSZEyltQ', 'Noora AlHammadi'], + ['U3hCd1S7FQ4', 'Louis Hansel @shotsoflouis'], + ['VOpJTnP6S9g', 'Liana Mikah'], + ['9TWavGempJc', 'Ashleigh Robertson'], + ['I2tgHl69Dco', 'Louis Hansel @shotsoflouis'], + ['fyQr1T3GE34', 'Petr Sevcovic'], + ['pGe5mc4Eip8', 'Daniel Park'], + ['gZsgKrNc8es', 'Dan Gold'], + ['XPSXhLx143g', 'Wouter Meijering'], + ['VpxavZd4S-I', 'Mr Lemon'], + ['9vHOhKoNlNw', 'Edrece Stansberry'], + ['uScYRjZ2ol8', 'donald modeste'], + ['fCE-pTmFrPI', 'Valeriu Bondarenco'], + ['1ZTccDpF71k', 'Austin Paquette'], + ['Au-LzDMd_Cw', 'Stephanie McCabe'], + ['9cfHdC2Asak', 'cindy fernandez'], + ['4-rmvQRL2nY', 'Annie Spratt'], + ['8Yvwy4Kbd1g', 'Alex Kondratiev'], + ['4PtChDuxsDI', 'Dronile Hiraldo'], + ['Mi1SNlsyWAk', 'Louis Hansel @shotsoflouis'], + ['nn0whk6nzv4', 'Yulia Chinato'], + ['8JIIoSFtEbo', 'Massimo Virgilio'], + ['44eKcVXzFoc', 'Alonso Romero'], + ['JpbtAb-f3JA', 'Dollar Gill'], + ['Hvrm3efPYIA', 'Delaney Van'], + ['4E0dknSrQVU', 'Matteo Maretto'], + ['krNP2ESq-54', 'Joshua Bedford'], + ['DPNrBT1WCMs', 'Egor Lyfar'], + ['Yy-dHQP-Ax0', 'Markus Spiske'], + ['BKSntHf8oiU', 'Melissa Walker Horn'], + ['asaGSZEyltQ', 'Noora AlHammadi'], + ['U3hCd1S7FQ4', 'Louis Hansel @shotsoflouis'], + ['VOpJTnP6S9g', 'Liana Mikah'], + ['9TWavGempJc', 'Ashleigh Robertson'], + ['I2tgHl69Dco', 'Louis Hansel @shotsoflouis'], + ['fyQr1T3GE34', 'Petr Sevcovic'], + ['pGe5mc4Eip8', 'Daniel Park'], + ['gZsgKrNc8es', 'Dan Gold'], + ], + candy: [ + ['51AhxwkYyHo', 'Viktor Forgacs'], + ['rS1GogPLVHk', 'Eaters Collective'], + ['9m6NQHyxk-s', 'Brooke Lark'], + ['DOmaKmeCp_8', 'Sarah Takforyan'], + ['aCPOKOb4qis', 'Jamie Street'], + ['On8Ov1TItnU', 'QooQee'], + ['KN7tpVQCmWA', 'Nick Fewings'], + ['9lmFbtJ2QzM', 'Tim Gouw'], + ['54hUU5pNSvo', 'Erol Ahmed'], + ['zYH4SubXCWY', 'Наталья Горох'], + ['wJHJY7PcDcg', 'Andrew Itaga'], + ['BnusUP5jydc', 'Marc Markstein'], + ['NI_fJ15rIfI', 'Szabo Viktor'], + ['LymVMRIUwPQ', 'Happy Films'], + ['mrNVnLEphdo', 'Greg Nunes'], + ['FKvoEKSV2LY', 'zhou yu'], + ['CKLF34baCTQ', 'Willian Justen de Vasconcellos'], + ['7uGCN9qshsY', 'Siora Photography'], + ['xBTnaTgleQE', 'Glen Carrie'], + ['sC_HExGwbhI', 'Erik Mclean'], + ['1gViVVlEaPc', 'Sérgio André'], + ['k9yY0XZTSnI', 'Fernando Hernandez'], + ['h1_R9-o9an0', 'emrecan arık'], + ['XB0ha-DSGoU', 'Laura Briedis'], + ['ONn4OfAnxZY', 'Monique Carrati'], + ['24p9dPeXdFA', 'Frederic Köberl'], + ['GL6J_sFYHLw', 'Yuiizaa September'], + ['LU_fCezP9-o', 'Amit Lahav'], + ['CSkAj_XqOVc', 'Mockaroon'], + ['xLvIcAYuuMQ', 'Luis Aguila'], + ], + fruit: [ + ['ZLc9yTIFzNk', 'Kelly Sikkema'], + ['DoxGtpAsdYY', 'Sanni Sahil'], + ['lyEkpuuIrg0', 'kaouther djouada'], + ['fDx4zHpnlOM', 'Nanxi wei'], + ['0AXNt5SdeXU', 'Sara Cervera'], + ['s05XKB6jK2c', 'Markus Spiske'], + ['bPMyJzKhCyA', 'Louis Hansel @shotsoflouis'], + ['4jeWN4puDrw', 'Carolyn Leber'], + ['8EScigZC6AU', 'MF Evelyn'], + ['1QbosWMxOx0', 'elCarito'], + ['NWA3s8r-1bc', 'Fli Hi'], + ['TfNUmbaIjj8', 'Sophie Dale'], + ['duskNBVv420', 'Louis Hansel @shotsoflouis'], + ['QYupKZjYDbw', 'Moritz Kindler'], + ['ozcHSq3XfVs', 'Callum Blacoe'], + ['ucY_-U1dM8U', 'Louis Hansel @shotsoflouis'], + ['4WLc_dWE-kc', 'Dmitry Mishin'], + ['BTqDasyX62E', 'Priscilla Du Preez'], + ['SD5dOSkhQdU', 'Alexander Mils'], + ['4pJekgmSmPM', 'Rinck Content Studio'], + ['0XGWys_GaFo', 'Toa Heftiba'], + ['uTZvsJsylYc', 'Louis Hansel @shotsoflouis'], + ['vAHaYh5s_Sc', 'RAPHAEL MAKSIAN'], + ['0N4A1c5tJSs', 'Miguel Vaz'], + ['VAhUq30sW0c', 'Cody Berg'], + ['qbO7Mlhq8PQ', 'JOSHUA COLEMAN'], + ['dLB32q_bRs0', 'Jason Leung'], + ['66itnKdGlC4', 'Fateme Azimi'], + ['_DYRZHbCIq0', 'Will Mcmahon'], + ], + landscapes: [ + ['FuaNmJPLAHg', 'MINSUN KIM'], + ['8flZ753v87Q', 'Ken Schlabach'], + ['6qdO_nFhYoI', 'Clay Banks'], + ['VMmPxFmfwfQ', 'Xiaopeng Ma'], + ['DAufF3R8B5Q', 'Sangga Rima Roman Selia'], + ['5U9n9gip7VY', 'Annie Spratt'], + ['Tot7FLHWotI', 'elCarito'], + ['vph4L_OjhuI', 'Marek Piwnicki'], + ['Nd8h38tRIlc', 'Kevin Horvat'], + ['nACRxCEAut8', 'ALEKSEY KUPRIKOV'], + ['PjyfZ-Ujut8', 'Denys Nevozhai'], + ['X6utHocVX8w', 'Ian Williams'], + ['cq6g1WssaJI', 'Tyler Casey'], + ['U1ad7OjdHx8', 'Dave Herring'], + ['si5673B4PIE', 'Annie Spratt'], + ['shv8H0Dv5iE', 'Aditya Chinchure'], + ['hjVrWFVyOqE', 'chuttersnap'], + ['_skrrHIcsEk', 'Edward Ma'], + ['GMNtcgo-KdA', 'Yoav Hornung'], + ['313dueuxHls', 'Jamie Street'], + ['SncOlKOY7NY', 'Ward Mercer'], + ['7R11NCmOY6k', 'Ivan Bandura'], + ['YFwOiSisSLM', 'Krzysztof Kotkowicz'], + ['gbO7zCOUJIg', 'Brian Kyed'], + ['5iwG4xlUgJ4', 'Jason Leung'], + ['G_PSim-dsvU', 'Alexandra Jitariuc'], + ['gVl6Hsi_pxo', 'Jean-Philippe Delberghe'], + ['p8h0_0pyW9k', 'Bence Balla-Schottner'], + ], + waterfalls: [ + ['Cwd0zYOIClY', 'Thomas Ensley'], + ['dGCHAo7mb2Q', 'Jamie Melville'], + ['bukitR21NO8', 'Jen Theodore'], + ['IKIY5bpd9eM', 'Bruno Kelzer'], + ['rrVm3b-uxkk', 'Jamie Melville'], + ['UegnUIW76gQ', 'Jamie Melville'], + ['d0cvao4fC6Q', 'Forest Simon'], + ['N8r2hH1siEY', 'Lester Hine'], + ['oRmeWoJx_nY', 'Michael Olsen'], + ['jVjwlfyJAkA', 'Tom Wheatley'], + ['ar6PYxBFgis', 'Gerald Berliner'], + ['9fpK7fPw6Is', 'Jamie Melville'], + ['jeXkw2HR1SU', 'Julia Caesar'], + ['Evs5MnlmUXY', 'Damian Kamp'], + ['phstcH4QKJc', 'Jakob Owens'], + ['7dmDlBfB9Vk', 'Spencer Watson'], + ['SHhaNnNR1xo', 'SaiKrishna Saketh'], + ['_6ghImrDiVU', 'Daniel H. Tong'], + ['gfaXzDmMY7M', 'RADIN AHMAD MUJAHID'], + ['rFbK1PP9LEA', 'Daniel Hernandez'], + ['s6Tv7b4SAoo', 'Miltiadis Fragkidis'], + ['ir9RUDjVpPo', 'Clay Banks'], + ['7emiteIwfuk', 'yunchuan luo'], + ['9tqrKo4B98I', 'Austin Farrington'], + ['dfazzUCjMro', 'oakie'], + ['4HEuHmA7WfM', 'Ketan Morris'], + ['eG1_rPekhTk', 'Jack B'], + ['_up7EDGdTqU', 'Hari Nandakumar'], + ['MhIPEJmVwaA', 'Karthik Chandran'], + ], + travel: [ + ['gC2Q_Tfub6c', 'James Lee'], + ['1Tcu61Qz7c0', 'Payas'], + ['eLMJ2x7s9ak', 'Alexandre Chambon'], + ['aNrRsB2wLDk', 'Ahmet Yalçınkaya'], + ['xu2WYJek5AI', 'Anastasia Petrova'], + ['xRoexKKv8fs', 'Robson Hatsukami Morgan'], + ['qdIer4A0afE', 'Camille Brodard'], + ['0lPZBa6-1J8', 'Torbjorn Sandbakk'], + ['CLm3pWXrS9Q', 'Tim Trad'], + ['D1fs2x11_jk', 'Raul Taciu'], + ['BZ3aE3ouAfc', 'Petr Sevcovic'], + ['_OUvt8kLf0s', 'GAWN AUSTRALIA'], + ['T_6yJJQ_-wA', 'Square Lab'], + ['1Z_mX3zzEBc', 'Andy Pearce'], + ['lWHJwoSZf7M', 'Joseph Costa'], + ['O6Euhw7NMbQ', 'Cosmic Timetraveler'], + ['bs1eqd6zSiU', 'Harley-Davidson'], + ['_qkuc1V9Gbg', 'Ken Cheung'], + ], + cars: [ + ['w5SZe8hoqlk', 'Rachel Lynette French'], + ['Nwk0ye_Y_As', 'Lex Valishvili'], + ['P9yqMy_9ZaY', 'Anastasia Dulgier'], + ['aGwBtbncMWo', 'Sandra Kaas'], + ['jV_QaRjbhWE', 'Clem Onojeghuo'], + ['SwmaJDvasuU', 'Gunnar Ridderström'], + ['BsJovWrQ7vE', 'Caroline'], + ['3t0Q-GYXE0U', 'Austin Park'], + ['tlCzxI2RQAc', 'Vinícius Henrique'], + ['fecFQcxsUok', 'Zachary Spears'], + ['upttrzCo8-U', 'takahiro taguchi'], + ['zoHustBfNxA', 'Lukas Werner'], + ['UZH69YA50qY', 'Austin Park'], + ['tJwY80NMkhk', 'Hanny Naibaho'], + ['esfNTaHsR1c', 'Willian Justen de Vasconcellos'], + ['Tas1kF-6aNA', 'Tyler Casey'], + ['7nrsVjvALnA', 'Denys Nevozhai'], + ['CRf3KYexpCA', 'Will Truettner'], + ['B8JpogxOnyw', 'Mike Von'], + ['mU5vnpJW_CQ', 'Luke Tanis'], + ['GaRPxo9Z86M', 'Court Cook'], + ['Orp-VAQ_gNA', 'Angello Lopez'], + ['q4UZ53rYYy0', 'Ali Moharami'], + ['XA2OEcvrrP8', 'Alexander Pidgeon'], + ['hXMv04v_py0', 'Patrick Schöpflin'], + ['G7sWGEF8pRc', 'Julian Hochgesang'], + ['PUnARRf-rE8', 'jean wimmerlin'], + ['UIk-rF4Df60', 'Igor Bumba'], + ['0dW-eQVL0WQ', 'Michael Heuser'], + ], + aviation: [ + ['bw1hXT_okL4', 'sayhitobel'], + ['ecOXN5jGtaU', 'Alexander Lobanov'], + ['rFujmwrNv1w', 'Abe Drzycimski'], + ['EKYsla2fER8', 'Paola Aguilar'], + ['1uiXp6fXd3w', 'Kevin Hackert'], + ['tVzGTraJ4T0', 'Samantha Gades'], + ['5h1-cHxJz1I', 'Nikli Nodin'], + ['u-1cYIua_aI', 'John Torcasio'], + ['l2OusPPMLxs', 'yvette Wynne'], + ['SUIvWFHBZas', 'Timothy Newman'], + ['Tvrnezn1N6g', 'Daniel Eledut'], + ['ZN0469D3v98', 'Jason Hafso'], + ['LNmKCmZ2pcI', 'Ricardo Resende'], + ['P_ExwnnPGyM', 'Tudose Alexandru'], + ['1vyWYdyUDGU', 'Ian Cumming'], + ['k102QVrpitQ', 'Jake Blucker'], + ['eB8-XtMtqZI', 'Miguel Ángel Sanz'], + ['1XDZavWyows', 'Samuel Sianipar'], + ['7WHD-pdECGU', 'Miguel Ángel Sanz'], + ['CN8HsCUCjUQ', 'Cameron Kitson'], + ['q_rNy9pRe78', 'Walter Walraven'], + ['a9SA6Zs1L9g', 'Tim Dennert'], + ['JW50PRr5UbI', 'Roland O'], + ['um_6nrOnPZ4', 'Pascal Meier'], + ['EpxT58kpBhc', 'Terence Burke'], + ['3fbjHj2k0vE', 'Dušan Smetana'], + ['9o4p3eCcRBE', 'Jeremy Bishop'], + ['sbfLwfoVX7E', 'Nour Betar'], + ['4VCJBAtlnNI', 'sippakorn yamkasikorn'], + ['LcbsrFbqwGk', 'Angel Barnes'], + ], + nature: [ + ['io0umElGQwU', 'Fabrizio Forte'], + ['lh2sQ_L3GaA', 'Daniel Lord'], + ['Gyr7o3BMsSc', 'Dave Francis'], + ['lRipDukRpd4', 'Kevin'], + ['GxymWkdnl4Y', 'Fernando Jorge'], + ['rWnw7JL0LPE', 'Rahul Gupta'], + ['YKuYS3ChMyw', 'Yuliya Kosolapova'], + ['ZsUjLYqbNz8', 'Pau Morfín'], + ['x2o3HX6RKG8', 'Dave'], + ['Few073pm6aQ', 'Dennis Bertuch'], + ['jP-wvLA7uyg', 'Gary Butterfield'], + ['euhs3wanL-I', 'Andrew Stapleton'], + ['UHt2TG_CHO0', 'Wolfgang Hasselmann'], + ['SAIb5NkbAaE', 'Alexis Gethin'], + ['tdNjdIW-_OY', 'Lachlan'], + ['0NtjFr0-DwM', 'Jane Carmona'], + ['fhg_nm1sufU', 'Pascal Debrunner'], + ['HJUDECvtxZE', 'jose alfonso sierra'], + ['8vS1CwHIEH8', 'Serge Sustavov'], + ['oTTnZcwQ2t4', 'Daniel Radford'], + ['TNhs9udas8Q', 'Migsar Navarro'], + ['zT9g3crTPcE', 'Devin L'], + ['hYuNG1zptM4', 'Roberto Gamito'], + ['DfOQRep1LKU', 'Wolfgang Rottmann'], + ['2sT_iJhIcEg', 'Anamika Pokharel'], + ['Zjcl2vaeGdo', 'Lucas Mitchell'], + ['b2T6zgFAW1E', 'Theme Inn'], + ['EnFQmcTtsjo', 'Yang Jing'], + ['PWI-CDVynhw', 'Thomas Galler'], + ['gTvLsDi-Uzo', 'Arun Raj'], + ], + trees: [ + ['aSCx7M1E4Vo', 'Markos Mant'], + ['3B9MYDjQ-EQ', 'Firasat Durrani'], + ['39oaRqfUacc', 'Nick'], + ['Y0NLa_KmEgU', 'Michael Ankes'], + ['i6nGVkbbUT4', 'Henry McIntosh'], + ['_WITb4g1ImY', 'Mark Basarab'], + ['prd5CXMsD68', 'JOHN TOWNER'], + ['avxSevmCKfw', 'Marta Pawlik'], + ['LZf0HMPg-3I', 'Max Brinton'], + ['Ovjx9FsXX9c', 'Andy Fitzsimon'], + ['YPtfMXRMUe0', 'Corey Agopian'], + ['h3y3XPXKoeY', 'Elke Karin Lugert'], + ['qeGC1zrsG8U', 'Tycho Atsma'], + ['dAkd3xaSDTA', 'Nick West'], + ['Prd-KB7CKo4', 'Austin Neill'], + ['iqu9ZTwTfVM', 'Clem Onojeghuo'], + ['klNpWLkgezo', 'Wil Stewart'], + ['Pm4U5IqI4dM', 'Lena Derevianko'], + ['5i664o1oY4c', 'Yannick Pulver'], + ['uoMFPm5_Xg8', 'Yoni Kozminsi'], + ['Qr2dZRNdf_M', 'Afifi Zulkifle'], + ['jxljuYqn8uU', 'Sebastian Pichler'], + ['H3mO3zNlvNU', 'Jonathan Knepper'], + ['pEb-Xf_qM0s', 'Martin Reisch'], + ['prSogOoFmkw', 'Braden Jarvis'], + ['y9csmronT3s', 'Alberto Restifo'], + ['R8R9H_xuvBY', 'Andrew Pons'], + ['fuT-m1yzUG4', 'Ales Krivec'], + ], + wildlife: [ + ['Tot7FLHWotI', 'elCarito'], + ['-I0EgcZdV0E', 'Maarten van den Heuvel'], + ['F21zt7_icpo', 'Tevin Trinh'], + ['gMxgp-MwCyQ', 'James Wainscoat'], + ['FFlb5Uj3vhc', 'Sigmund'], + ['hglSMjdh83M', 'Srinivasan Venkataraman'], + ['UgWcvfl4q5I', 'NOAA'], + ['PZ1nEPFNBJQ', 'Third Idea'], + ['f4yYs5P5GbA', 'rigel'], + ['e94T5ag-9x0', 'Jayden Brand'], + ['6Fcllk7ze_Q', 'Vivian Arcidiacono'], + ['O7G3II8E2Eo', 'Erik-Jan Leusink'], + ['-6UNL6Ghn_c', 'Silvio Kundt'], + ['EyZMGYn1Uj4', 'AGL Fotos'], + ['nxR7gvSokH8', 'Waldemar Brandt'], + ['sAGXVK6bNFc', 'Amar Yashlaha'], + ['8zLCXDWETEg', 'Clément ROY'], + ['4nPFQ2sUhUE', 'Justin Porter'], + ['mUNDTQrfnSk', 'Zahrin Lukman'], + ['VXcX0Joa09k', 'Max Rovensky'], + ['F_HycxA2lwc', 'Marthijn Brinks'], + ['ss01halnU4I', 'Dušan Smetana'], + ['lktWv61WoNI', 'Rory Lindholm'], + ['myeQ2RH1PX0', 'Alexander Ross'], + ['dhIAyAmfjz8', 'Third Idea'], + ['AGprQpF4STo', 'Jeff Lemond'], + ['P8pBJQVt4UA', 'Christer Gundersen'], + ['nt8Ek7sRgdA', 'Mikell Darling'], + ['YHv0BDThVOw', 'Nicholas Doherty'], + ], + interior: [ + ['9wGKENQ-qTI', 'Kunj Parekh'], + ['x3BCSWCAtrY', 'yann maignan'], + ['EF6z_6R94zQ', 'Orlova Maria'], + ['dC8NC2QBFyQ', 'Kari Shea'], + ['A_AuirVquQY', 'Abbie Bernet'], + ['IJf2v-StB4Y', 'yann maignan'], + ['frsN89Tf-1Q', 'Joshua Eckstein'], + ['s95oB2n9jng', 'Aaron Huber'], + ['n_vdmdtNh6M', 'Tiplada Mekvisan'], + ['GbiVL6t4T-o', 'gdtography'], + ['xx0oSB1YxRE', 'Joseph Albanese'], + ['McaNtoPEEcg', 'Brooke Cagle'], + ['s65VlkIYSDw', 'Robert Bye'], + ['pEaBEqXXk-M', 'Aw Creative'], + ['GaX7QTgs8pg', 'Waldemar Brandt'], + ['b75FBg97dU0', 'Bao Menglong'], + ['5i0GnoTTjSE', 'Philipp Berndt'], + ['buhmhprfo3g', 'Kara Eads'], + ['L7EwHkq1B2s', 'Kara Eads'], + ['gBdirnalxcQ', 'Nick Fewings'], + ['ILgaxpiQu0', 'Matteo Maretto'], + ['ydcMwcfY5E0', 'Clark Street Mercantile'], + ['zLT3VqWEgOQ', 'Sidekix Media'], + ['Yg0Rds6_TsY', 'Sergiu Cindea'], + ['gbS_fhrFo10', 'Bekah Russom'], + ['o3c-euNd_ZM', 'Alessandra Caretto'], + ['IEkMMvdZFc0', 'Nick Hillier'], + ['hnhE83RhAWg', 'Stefan Steinbauer'], + ], + statues: [ + ['ig8E7Mlrl7Y', 'Vidar Nordli-Mathisen'], + ['1ak3Z7ZmtQA', 'Ryan Lum'], + ['ntPF02wcTY', 'Gigi'], + ['5_i4OPeOAZU', 'Viktor Forgacs'], + ['iRON0g6iO0k', 'Alexandre Chambon'], + ['PhQ4CpXLEX4', 'Daniels Joffe'], + ['EfHqouvZU2Y', 'Bettina Otott Kovács'], + ['kaEhf0eZme8', 'Nils'], + ['4-4IDc21Gto', 'K. Mitch Hodge'], + ['Y8Xh7ZJFU5A', 'Vidar Nordli-Mathisen'], + ['fmawALmMLSA', 'Gabriel TRESCH'], + ['DUp4B6M0AMc', 'Sebastien'], + ['MFZUY4gqvA4', 'James Yarema'], + ['50vvwcNFFzU', 'Robert Anasch'], + ['TMRi8cD2umM', 'Frank Eiffert'], + ['wLx_WCkWvHg', 'Chris A. Tweten'], + ['Twoj21Av-so', 'Arthur Reeder'], + ['EiGDn8cwU4Y', 'Tessa Rampersad'], + ['fVY6UxZuECA', 'Christine Wehrmeier'], + ['uJdTBTJ9rbo', 'Christine Wehrmeier'], + ['4eEBFTBKx5E', 'Ralph Spandl'], + ['HtQRGemW_40', 'Ivan Bertona'], + ['uHBcinxOLhQ', 'K. Mitch Hodge'], + ['2TmsyZXMNTE', 'Emma Fabbri'], + ['9KkPloRgOUY', 'Matteo Maretto'], + ['KzPefInJW58', 'JOSHUA COLEMAN'], + ['szVTIkisN1M', 'David Siglin'], + ['iRzEPkYSETQ', 'Francois Olwage'], + ], + technology: [ + ['QpTCSHzhWuo', 'Joshua Hoehne'], + ['0lMpQaXfOCg', 'Barrett Ward'], + ['w33-zg-dNL4', 'Rami Al-zayat'], + ['MC5WbGo_bZM', 'Tom Pumford'], + ['iHJ7xouUyXs', 'Amith Nair'], + ['E3I9thV98kQ', 'Tatiana Lapina'], + ['JuUK7Er9nR4', 'Mohamed Boumaiza'], + ['Dei5oAC_wJc', 'Kenny Luo'], + ['ltwEbf_G9bs', 'Mario Caruso'], + ['gWdlDR4WpV4', 'Zarak Khan'], + ['0Um6Yr1cyx0', 'Antoine Beauvillain'], + ['_8S9nEmCZK0', 'Oliur'], + ['etFrnBJS1qc', 'NeONBRAND'], + ['ZMVtx_KJtOk', 'Thought Catalog'], + ['JNuKyKXLh8U', 'Noiseporn'], + ['5gzr-RM-rZM', 'Kenny Luo'], + ['eWaXmZsXKDs', 'Zane Lee'], + ['4qGbMEZb56c', 'Thomas William'], + ['hwqWxHoH2wk', 'Markus Spiske'], + ['vZJdYl5JVXY', 'Kaitlyn Baker'], + ['Lg8xTZjs6Lg', 'Marc Mueller'], + ['M5HQPjXrjlQ', 'Matt Hoffman'], + ['A-b37b-CrYE', 'Kenny Luo'], + ['Kj2SaNHG-hg', 'Christopher Burns'], + ['A1v0-iH3T5A', 'Patrick Hendry'], + ['iFBIdX54BOk', 'Keagan Henman'], + ], }) - .map(([category, photos]) => photos.map(([photo, copyright], index) => ({ - id: nanoid(), - path: `sfw/${category}/${photo}.jpeg`, - thumbnail: `sfw/${category}/thumbs/${photo}.jpeg`, - lazy: `sfw/${category}/lazy/${photo}.jpeg`, - mime: 'image/jpeg', - sfw_media_id: null, - group: category, - index, - copyright, - comment: `Courtesy of ${copyright}`, - }))) - .flat(); + .map(([category, photos]) => photos.map(([photo, copyright], index) => ({ + id: nanoid(), + path: `sfw/${category}/${photo}.jpeg`, + thumbnail: `sfw/${category}/thumbs/${photo}.jpeg`, + lazy: `sfw/${category}/lazy/${photo}.jpeg`, + mime: 'image/jpeg', + sfw_media_id: null, + group: category, + index, + copyright, + comment: `Courtesy of ${copyright}`, + }))) + .flat(); /* eslint-disable max-len */ exports.seed = knex => Promise.resolve() - .then(async () => { - const { inserted } = await upsert('media', sfw, 'path'); + .then(async () => { + const { inserted } = await upsert('media', sfw, 'path'); - const sfwMediaIds = inserted.map(mediaEntry => ({ - id: nanoid(), - media_id: mediaEntry.id, - })); + const sfwMediaIds = inserted.map(mediaEntry => ({ + id: nanoid(), + media_id: mediaEntry.id, + })); - await upsert('media_sfw', sfwMediaIds, 'media_id'); - }) - .then(async () => { - const tagMedia = tagPosters.concat(tagPhotos); + await upsert('media_sfw', sfwMediaIds, 'media_id'); + }) + .then(async () => { + const tagMedia = tagPosters.concat(tagPhotos); - const tags = await knex('tags').whereIn('slug', tagMedia.map(item => item.tagSlug)); + const tags = await knex('tags').whereIn('slug', tagMedia.map(item => item.tagSlug)); - const { inserted, updated } = await upsert('media', tagMedia.map(({ - id, path, thumbnail, lazy, mime, index, comment, - }) => ({ - id, path, thumbnail, lazy, mime, index, comment, - })), 'path', knex); + const { inserted, updated } = await upsert('media', tagMedia.map(({ + id, path, thumbnail, lazy, mime, index, comment, + }) => ({ + id, path, thumbnail, lazy, mime, index, comment, + })), 'path', knex); - const tagIdsBySlug = tags.reduce((acc, tag) => ({ ...acc, [tag.slug]: tag.id }), {}); - const mediaIdsByPath = inserted.concat(updated).reduce((acc, item) => ({ ...acc, [item.path]: item.id }), {}); + const tagIdsBySlug = tags.reduce((acc, tag) => ({ ...acc, [tag.slug]: tag.id }), {}); + const mediaIdsByPath = inserted.concat(updated).reduce((acc, item) => ({ ...acc, [item.path]: item.id }), {}); - const tagPosterEntries = tagPosters.map(poster => ({ - tag_id: tagIdsBySlug[poster.tagSlug], - media_id: mediaIdsByPath[poster.path], - })); + const tagPosterEntries = tagPosters.map(poster => ({ + tag_id: tagIdsBySlug[poster.tagSlug], + media_id: mediaIdsByPath[poster.path], + })); - const tagPhotoEntries = tagPhotos.map(photo => ({ - tag_id: tagIdsBySlug[photo.tagSlug], - media_id: mediaIdsByPath[photo.path], - })); + const tagPhotoEntries = tagPhotos.map(photo => ({ + tag_id: tagIdsBySlug[photo.tagSlug], + media_id: mediaIdsByPath[photo.path], + })); - return Promise.all([ - upsert('tags_posters', tagPosterEntries, 'tag_id', knex), - upsert('tags_photos', tagPhotoEntries, ['tag_id', 'media_id'], knex), - ]); - }); + return Promise.all([ + upsert('tags_posters', tagPosterEntries, 'tag_id', knex), + upsert('tags_photos', tagPhotoEntries, ['tag_id', 'media_id'], knex), + ]); + }); diff --git a/src/.eslintrc b/src/.eslintrc index 686fcae7..cb1bf23d 100644 --- a/src/.eslintrc +++ b/src/.eslintrc @@ -6,10 +6,11 @@ }, "rules": { "strict": 0, + "indent": ["error", "tab"], + "no-tabs": "off", "no-unused-vars": ["error", {"argsIgnorePattern": "^_"}], "no-console": 0, "no-underscore-dangle": 0, - "indent": "off", "prefer-destructuring": "off", "template-curly-spacing": "off", "object-curly-newline": "off", diff --git a/src/actors-legacy.js b/src/actors-legacy.js index e93193c5..246e9f8b 100644 --- a/src/actors-legacy.js +++ b/src/actors-legacy.js @@ -18,522 +18,522 @@ const { curateSites } = require('./sites'); const { storeMedia, associateMedia } = require('./media'); async function curateActor(actor) { - const [aliases, avatar, photos, social] = await Promise.all([ - knex('actors').where({ alias_for: actor.id }), - knex('actors_avatars') - .where('actor_id', actor.id) - .join('media', 'media.id', 'actors_avatars.media_id') - .first(), - knex('actors_photos') - .where('actor_id', actor.id) - .join('media', 'media.id', 'actors_photos.media_id') - .orderBy('index'), - knex('actors_social') - .where('actor_id', actor.id) - .orderBy('platform', 'desc'), - ]); + const [aliases, avatar, photos, social] = await Promise.all([ + knex('actors').where({ alias_for: actor.id }), + knex('actors_avatars') + .where('actor_id', actor.id) + .join('media', 'media.id', 'actors_avatars.media_id') + .first(), + knex('actors_photos') + .where('actor_id', actor.id) + .join('media', 'media.id', 'actors_photos.media_id') + .orderBy('index'), + knex('actors_social') + .where('actor_id', actor.id) + .orderBy('platform', 'desc'), + ]); - const curatedActor = { - id: actor.id, - gender: actor.gender, - name: actor.name, - description: actor.description, - birthdate: actor.birthdate && new Date(actor.birthdate), - country: actor.country_alpha2, - origin: (actor.birth_city || actor.birth_state || actor.birth_country_alpha2) ? {} : null, - residence: (actor.residence_city || actor.residence_state || actor.residence_country_alpha2) ? {} : null, - ethnicity: actor.ethnicity, - height: actor.height, - weight: actor.weight, - bust: actor.bust, - waist: actor.waist, - hip: actor.hip, - naturalBoobs: actor.natural_boobs, - aliases: aliases.map(({ name }) => name), - slug: actor.slug, - avatar, - photos, - hasTattoos: actor.has_tattoos, - hasPiercings: actor.has_piercings, - tattoos: actor.tattoos, - piercings: actor.piercings, - social, - scrapedAt: actor.scraped_at, - }; + const curatedActor = { + id: actor.id, + gender: actor.gender, + name: actor.name, + description: actor.description, + birthdate: actor.birthdate && new Date(actor.birthdate), + country: actor.country_alpha2, + origin: (actor.birth_city || actor.birth_state || actor.birth_country_alpha2) ? {} : null, + residence: (actor.residence_city || actor.residence_state || actor.residence_country_alpha2) ? {} : null, + ethnicity: actor.ethnicity, + height: actor.height, + weight: actor.weight, + bust: actor.bust, + waist: actor.waist, + hip: actor.hip, + naturalBoobs: actor.natural_boobs, + aliases: aliases.map(({ name }) => name), + slug: actor.slug, + avatar, + photos, + hasTattoos: actor.has_tattoos, + hasPiercings: actor.has_piercings, + tattoos: actor.tattoos, + piercings: actor.piercings, + social, + scrapedAt: actor.scraped_at, + }; - if (curatedActor.birthdate) { - curatedActor.age = moment().diff(curatedActor.birthdate, 'years'); - } + if (curatedActor.birthdate) { + curatedActor.age = moment().diff(curatedActor.birthdate, 'years'); + } - if (actor.birth_city) curatedActor.origin.city = actor.birth_city; - if (actor.birth_state) curatedActor.origin.state = actor.birth_state; + if (actor.birth_city) curatedActor.origin.city = actor.birth_city; + if (actor.birth_state) curatedActor.origin.state = actor.birth_state; - if (actor.birth_country_alpha2) { - curatedActor.origin.country = { - alpha2: actor.birth_country_alpha2, - name: actor.birth_country_name, - alias: actor.birth_country_alias, - }; - } + if (actor.birth_country_alpha2) { + curatedActor.origin.country = { + alpha2: actor.birth_country_alpha2, + name: actor.birth_country_name, + alias: actor.birth_country_alias, + }; + } - if (actor.residence_city) curatedActor.residence.city = actor.residence_city; - if (actor.residence_state) curatedActor.residence.state = actor.residence_state; + if (actor.residence_city) curatedActor.residence.city = actor.residence_city; + if (actor.residence_state) curatedActor.residence.state = actor.residence_state; - if (actor.residence_country_alpha2) { - curatedActor.residence.country = { - alpha2: actor.residence_country_alpha2, - name: actor.residence_country_name, - alias: actor.residence_country_alias, - }; - } + if (actor.residence_country_alpha2) { + curatedActor.residence.country = { + alpha2: actor.residence_country_alpha2, + name: actor.residence_country_name, + alias: actor.residence_country_alias, + }; + } - return curatedActor; + return curatedActor; } function curateActors(releases) { - return Promise.all(releases.map(async release => curateActor(release))); + return Promise.all(releases.map(async release => curateActor(release))); } function curateActorEntry(actor, scraped, scrapeSuccess) { - const curatedActor = { - name: capitalize(actor.name), - slug: slugify(actor.name), - birthdate: actor.birthdate, - description: actor.description, - gender: actor.gender, - ethnicity: actor.ethnicity, - bust: actor.bust, - waist: actor.waist, - hip: actor.hip, - natural_boobs: actor.naturalBoobs, - height: actor.height, - weight: actor.weight, - hair: actor.hair, - eyes: actor.eyes, - has_tattoos: actor.hasTattoos, - has_piercings: actor.hasPiercings, - tattoos: actor.tattoos, - piercings: actor.piercings, - }; + const curatedActor = { + name: capitalize(actor.name), + slug: slugify(actor.name), + birthdate: actor.birthdate, + description: actor.description, + gender: actor.gender, + ethnicity: actor.ethnicity, + bust: actor.bust, + waist: actor.waist, + hip: actor.hip, + natural_boobs: actor.naturalBoobs, + height: actor.height, + weight: actor.weight, + hair: actor.hair, + eyes: actor.eyes, + has_tattoos: actor.hasTattoos, + has_piercings: actor.hasPiercings, + tattoos: actor.tattoos, + piercings: actor.piercings, + }; - if (actor.id) { - curatedActor.id = actor.id; - } + if (actor.id) { + curatedActor.id = actor.id; + } - if (actor.birthPlace) { - curatedActor.birth_city = actor.birthPlace.city; - curatedActor.birth_state = actor.birthPlace.state; - curatedActor.birth_country_alpha2 = actor.birthPlace.country; - } + if (actor.birthPlace) { + curatedActor.birth_city = actor.birthPlace.city; + curatedActor.birth_state = actor.birthPlace.state; + curatedActor.birth_country_alpha2 = actor.birthPlace.country; + } - if (actor.residencePlace) { - curatedActor.residence_city = actor.residencePlace.city; - curatedActor.residence_state = actor.residencePlace.state; - curatedActor.residence_country_alpha2 = actor.residencePlace.country; - } + if (actor.residencePlace) { + curatedActor.residence_city = actor.residencePlace.city; + curatedActor.residence_state = actor.residencePlace.state; + curatedActor.residence_country_alpha2 = actor.residencePlace.country; + } - if (scraped) { - curatedActor.scraped_at = new Date(); - curatedActor.scrape_success = scrapeSuccess; - } + if (scraped) { + curatedActor.scraped_at = new Date(); + curatedActor.scrape_success = scrapeSuccess; + } - return curatedActor; + return curatedActor; } function curateSocialEntry(url, actorId) { - const platforms = [ - // links supplied by PH often look like domain.com/domain.com/username - { - label: 'twitter', - pattern: 'http(s)\\://(*)twitter.com/:username(/)(?*)', - format: username => `https://www.twitter.com/${username}`, - }, - { - label: 'youtube', - pattern: 'http(s)\\://(*)youtube.com/channel/:username(?*)', - format: username => `https://www.youtube.com/channel/${username}`, - }, - { - label: 'instagram', - pattern: 'http(s)\\://(*)instagram.com/:username(/)(?*)', - format: username => `https://www.instagram.com/${username}`, - }, - { - label: 'snapchat', - pattern: 'http(s)\\://(*)snapchat.com/add/:username(/)(?*)', - format: username => `https://www.snapchat.com/add/${username}`, - }, - { - label: 'tumblr', - pattern: 'http(s)\\://:username.tumblr.com(*)', - format: username => `https://${username}.tumblr.com`, - }, - { - label: 'onlyfans', - pattern: 'http(s)\\://(*)onlyfans.com/:username(/)(?*)', - format: username => `https://www.onlyfans.com/${username}`, - }, - { - label: 'fancentro', - pattern: 'http(s)\\://(*)fancentro.com/:username(/)(?*)', - format: username => `https://www.fancentro.com/${username}`, - }, - { - label: 'modelhub', - pattern: 'http(s)\\://(*)modelhub.com/:username(/)(?*)', - format: username => `https://www.modelhub.com/${username}`, - }, - ]; + const platforms = [ + // links supplied by PH often look like domain.com/domain.com/username + { + label: 'twitter', + pattern: 'http(s)\\://(*)twitter.com/:username(/)(?*)', + format: username => `https://www.twitter.com/${username}`, + }, + { + label: 'youtube', + pattern: 'http(s)\\://(*)youtube.com/channel/:username(?*)', + format: username => `https://www.youtube.com/channel/${username}`, + }, + { + label: 'instagram', + pattern: 'http(s)\\://(*)instagram.com/:username(/)(?*)', + format: username => `https://www.instagram.com/${username}`, + }, + { + label: 'snapchat', + pattern: 'http(s)\\://(*)snapchat.com/add/:username(/)(?*)', + format: username => `https://www.snapchat.com/add/${username}`, + }, + { + label: 'tumblr', + pattern: 'http(s)\\://:username.tumblr.com(*)', + format: username => `https://${username}.tumblr.com`, + }, + { + label: 'onlyfans', + pattern: 'http(s)\\://(*)onlyfans.com/:username(/)(?*)', + format: username => `https://www.onlyfans.com/${username}`, + }, + { + label: 'fancentro', + pattern: 'http(s)\\://(*)fancentro.com/:username(/)(?*)', + format: username => `https://www.fancentro.com/${username}`, + }, + { + label: 'modelhub', + pattern: 'http(s)\\://(*)modelhub.com/:username(/)(?*)', + format: username => `https://www.modelhub.com/${username}`, + }, + ]; - const match = platforms.reduce((acc, platform) => { - if (acc) return acc; + const match = platforms.reduce((acc, platform) => { + if (acc) return acc; - const patternMatch = new UrlPattern(platform.pattern).match(url); + const patternMatch = new UrlPattern(platform.pattern).match(url); - if (patternMatch) { - return { - platform: platform.label, - original: url, - username: patternMatch.username, - url: platform.format ? platform.format(patternMatch.username) : url, - }; - } + if (patternMatch) { + return { + platform: platform.label, + original: url, + username: patternMatch.username, + url: platform.format ? platform.format(patternMatch.username) : url, + }; + } - return null; - }, null) || { url }; + return null; + }, null) || { url }; - return { - url: match.url, - platform: match.platform, - actor_id: actorId, - }; + return { + url: match.url, + platform: match.platform, + actor_id: actorId, + }; } async function curateSocialEntries(urls, actorId) { - if (!urls) { - return []; - } + if (!urls) { + return []; + } - const existingSocialLinks = await knex('actors_social').where('actor_id', actorId); + const existingSocialLinks = await knex('actors_social').where('actor_id', actorId); - return urls.reduce((acc, url) => { - const socialEntry = curateSocialEntry(url, actorId); + return urls.reduce((acc, url) => { + const socialEntry = curateSocialEntry(url, actorId); - if (acc.some(entry => socialEntry.url.toLowerCase() === entry.url.toLowerCase()) || existingSocialLinks.some(entry => socialEntry.url.toLowerCase() === entry.url.toLowerCase())) { - // prevent duplicates - return acc; - } + if (acc.some(entry => socialEntry.url.toLowerCase() === entry.url.toLowerCase()) || existingSocialLinks.some(entry => socialEntry.url.toLowerCase() === entry.url.toLowerCase())) { + // prevent duplicates + return acc; + } - return [...acc, socialEntry]; - }, []); + return [...acc, socialEntry]; + }, []); } async function fetchActors(queryObject, limit = 100) { - const releases = await knex('actors') - .select( - 'actors.*', - 'birth_countries.alpha2 as birth_country_alpha2', 'birth_countries.name as birth_country_name', 'birth_countries.alias as birth_country_alias', - 'residence_countries.alpha2 as residence_country_alpha2', 'residence_countries.name as residence_country_name', 'residence_countries.alias as residence_country_alias', - ) - .leftJoin('countries as birth_countries', 'actors.birth_country_alpha2', 'birth_countries.alpha2') - .leftJoin('countries as residence_countries', 'actors.residence_country_alpha2', 'residence_countries.alpha2') - .orderBy(['actors.name', 'actors.gender']) - .where(builder => whereOr(queryObject, 'actors', builder)) - .limit(limit); + const releases = await knex('actors') + .select( + 'actors.*', + 'birth_countries.alpha2 as birth_country_alpha2', 'birth_countries.name as birth_country_name', 'birth_countries.alias as birth_country_alias', + 'residence_countries.alpha2 as residence_country_alpha2', 'residence_countries.name as residence_country_name', 'residence_countries.alias as residence_country_alias', + ) + .leftJoin('countries as birth_countries', 'actors.birth_country_alpha2', 'birth_countries.alpha2') + .leftJoin('countries as residence_countries', 'actors.residence_country_alpha2', 'residence_countries.alpha2') + .orderBy(['actors.name', 'actors.gender']) + .where(builder => whereOr(queryObject, 'actors', builder)) + .limit(limit); - return curateActors(releases); + return curateActors(releases); } async function storeSocialLinks(urls, actorId) { - const curatedSocialEntries = await curateSocialEntries(urls, actorId); + const curatedSocialEntries = await curateSocialEntries(urls, actorId); - await knex('actors_social').insert(curatedSocialEntries); + await knex('actors_social').insert(curatedSocialEntries); } async function storeAvatars(avatars, actorId) { - if (!avatars || avatars.length === 0) { - return []; - } + if (!avatars || avatars.length === 0) { + return []; + } - const avatarsBySource = await storeMedia(avatars, 'actor', 'avatar'); - await associateMedia({ [actorId]: avatars }, avatarsBySource, 'actor', 'photo', 'avatar'); + const avatarsBySource = await storeMedia(avatars, 'actor', 'avatar'); + await associateMedia({ [actorId]: avatars }, avatarsBySource, 'actor', 'photo', 'avatar'); - return avatarsBySource; + return avatarsBySource; } async function storeActor(actor, scraped = false, scrapeSuccess = false) { - const curatedActor = curateActorEntry(actor, scraped, scrapeSuccess); + const curatedActor = curateActorEntry(actor, scraped, scrapeSuccess); - const [actorEntry] = await knex('actors') - .insert(curatedActor) - .returning('*'); + const [actorEntry] = await knex('actors') + .insert(curatedActor) + .returning('*'); - await storeSocialLinks(actor.social, actorEntry.id); + await storeSocialLinks(actor.social, actorEntry.id); - if (actor.avatars) { - await storeAvatars(actor.avatars, actorEntry.id); - } + if (actor.avatars) { + await storeAvatars(actor.avatars, actorEntry.id); + } - logger.info(`Added new entry for actor '${actor.name}'`); + logger.info(`Added new entry for actor '${actor.name}'`); - return actorEntry; + return actorEntry; } async function updateActor(actor, scraped = false, scrapeSuccess = false) { - const curatedActor = curateActorEntry(actor, scraped, scrapeSuccess); + const curatedActor = curateActorEntry(actor, scraped, scrapeSuccess); - const [actorEntry] = await knex('actors') - .where({ id: actor.id }) - .update(curatedActor) - .returning('*'); + const [actorEntry] = await knex('actors') + .where({ id: actor.id }) + .update(curatedActor) + .returning('*'); - await storeSocialLinks(actor.social, actor.id); + await storeSocialLinks(actor.social, actor.id); - logger.info(`Updated entry for actor '${actor.name}'`); + logger.info(`Updated entry for actor '${actor.name}'`); - return actorEntry; + return actorEntry; } async function mergeProfiles(profiles, actor) { - if (profiles.filter(Boolean).length === 0) { - return null; - } + if (profiles.filter(Boolean).length === 0) { + return null; + } - const mergedProfile = profiles.reduce((prevProfile, profile) => { - if (profile === null) { - return prevProfile; - } + const mergedProfile = profiles.reduce((prevProfile, profile) => { + if (profile === null) { + return prevProfile; + } - const accProfile = { - id: actor ? actor.id : null, - name: actor ? actor.name : (prevProfile.name || profile.name), - description: prevProfile.description || profile.description, - gender: prevProfile.gender || profile.gender, - birthdate: !prevProfile.birthdate || Number.isNaN(Number(prevProfile.birthdate)) ? profile.birthdate : prevProfile.birthdate, - birthPlace: prevProfile.birthPlace || profile.birthPlace, - residencePlace: prevProfile.residencePlace || profile.residencePlace, - nationality: prevProfile.nationality || profile.nationality, // used to derive country when not available - ethnicity: prevProfile.ethnicity || profile.ethnicity, - bust: prevProfile.bust || (/\d+\w+/.test(profile.bust) ? profile.bust : null), - waist: prevProfile.waist || profile.waist, - hip: prevProfile.hip || profile.hip, - naturalBoobs: prevProfile.naturalBoobs === undefined ? profile.naturalBoobs : prevProfile.naturalBoobs, - height: prevProfile.height || profile.height, - weight: prevProfile.weight || profile.weight, - hair: prevProfile.hair || profile.hair, - eyes: prevProfile.eyes || profile.eyes, - hasPiercings: prevProfile.hasPiercings === undefined ? profile.hasPiercings : prevProfile.hasPiercings, - hasTattoos: prevProfile.hasTattoos === undefined ? profile.hasTattoos : prevProfile.hasTattoos, - piercings: prevProfile.piercings || profile.piercings, - tattoos: prevProfile.tattoos || profile.tattoos, - social: prevProfile.social.concat(profile.social || []), - releases: prevProfile.releases.concat(profile.releases ? profile.releases : []), // don't flatten fallbacks - }; + const accProfile = { + id: actor ? actor.id : null, + name: actor ? actor.name : (prevProfile.name || profile.name), + description: prevProfile.description || profile.description, + gender: prevProfile.gender || profile.gender, + birthdate: !prevProfile.birthdate || Number.isNaN(Number(prevProfile.birthdate)) ? profile.birthdate : prevProfile.birthdate, + birthPlace: prevProfile.birthPlace || profile.birthPlace, + residencePlace: prevProfile.residencePlace || profile.residencePlace, + nationality: prevProfile.nationality || profile.nationality, // used to derive country when not available + ethnicity: prevProfile.ethnicity || profile.ethnicity, + bust: prevProfile.bust || (/\d+\w+/.test(profile.bust) ? profile.bust : null), + waist: prevProfile.waist || profile.waist, + hip: prevProfile.hip || profile.hip, + naturalBoobs: prevProfile.naturalBoobs === undefined ? profile.naturalBoobs : prevProfile.naturalBoobs, + height: prevProfile.height || profile.height, + weight: prevProfile.weight || profile.weight, + hair: prevProfile.hair || profile.hair, + eyes: prevProfile.eyes || profile.eyes, + hasPiercings: prevProfile.hasPiercings === undefined ? profile.hasPiercings : prevProfile.hasPiercings, + hasTattoos: prevProfile.hasTattoos === undefined ? profile.hasTattoos : prevProfile.hasTattoos, + piercings: prevProfile.piercings || profile.piercings, + tattoos: prevProfile.tattoos || profile.tattoos, + social: prevProfile.social.concat(profile.social || []), + releases: prevProfile.releases.concat(profile.releases ? profile.releases : []), // don't flatten fallbacks + }; - if (profile.avatar) { - const avatar = Array.isArray(profile.avatar) - ? profile.avatar.map(avatarX => ({ - src: avatarX.src || avatarX, - scraper: profile.scraper, - copyright: avatarX.copyright === undefined ? capitalize(profile.site?.name || profile.scraper) : profile.avatar.copyright, - })) - : { - src: profile.avatar.src || profile.avatar, - scraper: profile.scraper, - copyright: profile.avatar.copyright === undefined ? capitalize(profile.site?.name || profile.scraper) : profile.avatar.copyright, - }; + if (profile.avatar) { + const avatar = Array.isArray(profile.avatar) + ? profile.avatar.map(avatarX => ({ + src: avatarX.src || avatarX, + scraper: profile.scraper, + copyright: avatarX.copyright === undefined ? capitalize(profile.site?.name || profile.scraper) : profile.avatar.copyright, + })) + : { + src: profile.avatar.src || profile.avatar, + scraper: profile.scraper, + copyright: profile.avatar.copyright === undefined ? capitalize(profile.site?.name || profile.scraper) : profile.avatar.copyright, + }; - accProfile.avatars = prevProfile.avatars.concat([avatar]); // don't flatten fallbacks - } else { - accProfile.avatars = prevProfile.avatars; - } + accProfile.avatars = prevProfile.avatars.concat([avatar]); // don't flatten fallbacks + } else { + accProfile.avatars = prevProfile.avatars; + } - return accProfile; - }, { - social: [], - avatars: [], - releases: [], - }); + return accProfile; + }, { + social: [], + avatars: [], + releases: [], + }); - const [birthPlace, residencePlace] = await Promise.all([ - resolvePlace(mergedProfile.birthPlace), - resolvePlace(mergedProfile.residencePlace), - ]); + const [birthPlace, residencePlace] = await Promise.all([ + resolvePlace(mergedProfile.birthPlace), + resolvePlace(mergedProfile.residencePlace), + ]); - mergedProfile.birthPlace = birthPlace; - mergedProfile.residencePlace = residencePlace; + mergedProfile.birthPlace = birthPlace; + mergedProfile.residencePlace = residencePlace; - if (!mergedProfile.birthPlace && mergedProfile.nationality) { - const country = await knex('countries') - .where('nationality', 'ilike', `%${mergedProfile.nationality}%`) - .orderBy('priority', 'desc') - .first(); + if (!mergedProfile.birthPlace && mergedProfile.nationality) { + const country = await knex('countries') + .where('nationality', 'ilike', `%${mergedProfile.nationality}%`) + .orderBy('priority', 'desc') + .first(); - mergedProfile.birthPlace = { - country: country.alpha2, - }; - } + mergedProfile.birthPlace = { + country: country.alpha2, + }; + } - return mergedProfile; + return mergedProfile; } async function scrapeProfiles(sources, actorName, actorEntry, sitesBySlug) { - return Promise.map(sources, async (source) => { - // const [scraperSlug, scraper] = source; - const profileScrapers = [].concat(source).map(slug => ({ scraperSlug: slug, scraper: scrapers.actors[slug] })); + return Promise.map(sources, async (source) => { + // const [scraperSlug, scraper] = source; + const profileScrapers = [].concat(source).map(slug => ({ scraperSlug: slug, scraper: scrapers.actors[slug] })); - try { - return await profileScrapers.reduce(async (outcome, { scraper, scraperSlug }) => outcome.catch(async () => { - if (!scraper) { - logger.warn(`No profile profile scraper available for ${scraperSlug}`); - throw Object.assign(new Error(`No profile scraper available for ${scraperSlug}`)); - } + try { + return await profileScrapers.reduce(async (outcome, { scraper, scraperSlug }) => outcome.catch(async () => { + if (!scraper) { + logger.warn(`No profile profile scraper available for ${scraperSlug}`); + throw Object.assign(new Error(`No profile scraper available for ${scraperSlug}`)); + } - logger.verbose(`Searching '${actorName}' on ${scraperSlug}`); + logger.verbose(`Searching '${actorName}' on ${scraperSlug}`); - const site = sitesBySlug[scraperSlug] || null; - const profile = await scraper.fetchProfile(actorEntry ? actorEntry.name : actorName, scraperSlug, site, include); + const site = sitesBySlug[scraperSlug] || null; + const profile = await scraper.fetchProfile(actorEntry ? actorEntry.name : actorName, scraperSlug, site, include); - if (profile && typeof profile !== 'number') { - logger.verbose(`Found profile for '${actorName}' on ${scraperSlug}`); + if (profile && typeof profile !== 'number') { + logger.verbose(`Found profile for '${actorName}' on ${scraperSlug}`); - return { - ...profile, - name: actorName, - scraper: scraperSlug, - site, - releases: profile.releases?.map(release => (typeof release === 'string' - ? { url: release, site } - : { ...release, site: release.site || site } - )), - }; - } + return { + ...profile, + name: actorName, + scraper: scraperSlug, + site, + releases: profile.releases?.map(release => (typeof release === 'string' + ? { url: release, site } + : { ...release, site: release.site || site } + )), + }; + } - logger.verbose(`No profile for '${actorName}' available on ${scraperSlug}: ${profile}`); - throw Object.assign(new Error(`Profile for ${actorName} not available on ${scraperSlug}`), { warn: false }); - }), Promise.reject(new Error())); - } catch (error) { - if (error.warn !== false) { - logger.warn(`Error in scraper ${source}: ${error.message}`); - // logger.error(error.stack); - } - } + logger.verbose(`No profile for '${actorName}' available on ${scraperSlug}: ${profile}`); + throw Object.assign(new Error(`Profile for ${actorName} not available on ${scraperSlug}`), { warn: false }); + }), Promise.reject(new Error())); + } catch (error) { + if (error.warn !== false) { + logger.warn(`Error in scraper ${source}: ${error.message}`); + // logger.error(error.stack); + } + } - return null; - }); + return null; + }); } async function scrapeActors(actorNames) { - return Promise.map(actorNames || argv.actors, async (actorName) => { - try { - const actorSlug = slugify(actorName); - const actorEntry = await knex('actors').where({ slug: actorSlug }).first(); - const sources = argv.sources || config.profiles || Object.keys(scrapers.actors); + return Promise.map(actorNames || argv.actors, async (actorName) => { + try { + const actorSlug = slugify(actorName); + const actorEntry = await knex('actors').where({ slug: actorSlug }).first(); + const sources = argv.sources || config.profiles || Object.keys(scrapers.actors); - const finalSources = argv.withReleases ? sources.flat() : sources; // ignore race-to-success grouping when scenes are requested + const finalSources = argv.withReleases ? sources.flat() : sources; // ignore race-to-success grouping when scenes are requested - const [siteEntries, networkEntries] = await Promise.all([ - knex('sites') - .leftJoin('networks', 'sites.network_id', 'networks.id') - .select( - 'sites.*', - 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', 'networks.parameters as network_parameters', - ) - .whereIn('sites.slug', finalSources.flat()), - knex('networks').select('*').whereIn('slug', finalSources.flat()), - ]); + const [siteEntries, networkEntries] = await Promise.all([ + knex('sites') + .leftJoin('networks', 'sites.network_id', 'networks.id') + .select( + 'sites.*', + 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', 'networks.parameters as network_parameters', + ) + .whereIn('sites.slug', finalSources.flat()), + knex('networks').select('*').whereIn('slug', finalSources.flat()), + ]); - const sites = await curateSites(siteEntries, true); - const networks = networkEntries.map(network => ({ ...network, isFallback: true })); - const sitesBySlug = [].concat(networks, sites).reduce((acc, site) => ({ ...acc, [site.slug]: site }), {}); + const sites = await curateSites(siteEntries, true); + const networks = networkEntries.map(network => ({ ...network, isFallback: true })); + const sitesBySlug = [].concat(networks, sites).reduce((acc, site) => ({ ...acc, [site.slug]: site }), {}); - const profiles = await scrapeProfiles(sources, actorName, actorEntry, sitesBySlug); - const profile = await mergeProfiles(profiles, actorEntry); + const profiles = await scrapeProfiles(sources, actorName, actorEntry, sitesBySlug); + const profile = await mergeProfiles(profiles, actorEntry); - if (profile === null) { - logger.warn(`Could not find profile for actor '${actorName}'`); + if (profile === null) { + logger.warn(`Could not find profile for actor '${actorName}'`); - if (argv.save && !actorEntry) { - await storeActor({ name: actorName }, false, false); - } + if (argv.save && !actorEntry) { + await storeActor({ name: actorName }, false, false); + } - return null; - } + return null; + } - if (argv.inspect) { - console.log(profile); - logger.info(`Found ${profile.releases.length} releases for ${actorName}`); - } + if (argv.inspect) { + console.log(profile); + logger.info(`Found ${profile.releases.length} releases for ${actorName}`); + } - if (argv.save) { - if (actorEntry && profile) { - await Promise.all([ - updateActor(profile, true, true), - storeAvatars(profile.avatars, actorEntry.id), - ]); + if (argv.save) { + if (actorEntry && profile) { + await Promise.all([ + updateActor(profile, true, true), + storeAvatars(profile.avatars, actorEntry.id), + ]); - return profile; - } + return profile; + } - await storeActor(profile, true, true); - } + await storeActor(profile, true, true); + } - return profile; - } catch (error) { - console.log(error); - logger.warn(`${actorName}: ${error}`); + return profile; + } catch (error) { + console.log(error); + logger.warn(`${actorName}: ${error}`); - return null; - } - }, { - concurrency: 3, - }); + return null; + } + }, { + concurrency: 3, + }); } async function scrapeBasicActors() { - const basicActors = await knex('actors').where('scraped_at', null); + const basicActors = await knex('actors').where('scraped_at', null); - return scrapeActors(basicActors.map(actor => actor.name)); + return scrapeActors(basicActors.map(actor => actor.name)); } async function associateActors(mappedActors, releases) { - const [existingActorEntries, existingAssociationEntries] = await Promise.all([ - knex('actors') - .whereIn('name', Object.values(mappedActors).map(actor => actor.name)) - .orWhereIn('slug', Object.keys(mappedActors)), - knex('releases_actors').whereIn('release_id', releases.map(release => release.id)), - ]); + const [existingActorEntries, existingAssociationEntries] = await Promise.all([ + knex('actors') + .whereIn('name', Object.values(mappedActors).map(actor => actor.name)) + .orWhereIn('slug', Object.keys(mappedActors)), + knex('releases_actors').whereIn('release_id', releases.map(release => release.id)), + ]); - const associations = await Promise.map(Object.entries(mappedActors), async ([actorSlug, actor]) => { - try { - const actorEntry = existingActorEntries.find(actorX => actorX.slug === actorSlug) + const associations = await Promise.map(Object.entries(mappedActors), async ([actorSlug, actor]) => { + try { + const actorEntry = existingActorEntries.find(actorX => actorX.slug === actorSlug) || await storeActor(actor); - // if a scene - return Array.from(actor.releaseIds) - .map(releaseId => ({ - release_id: releaseId, - actor_id: actorEntry.id, - })) - .filter(association => !existingAssociationEntries - // remove associations already in database - .some(associationEntry => associationEntry.actor_id === association.actor_id + // if a scene + return Array.from(actor.releaseIds) + .map(releaseId => ({ + release_id: releaseId, + actor_id: actorEntry.id, + })) + .filter(association => !existingAssociationEntries + // remove associations already in database + .some(associationEntry => associationEntry.actor_id === association.actor_id && associationEntry.release_id === association.release_id)); - } catch (error) { - logger.error(actor.name, error); - return null; - } - }); + } catch (error) { + logger.error(actor.name, error); + return null; + } + }); - await knex('releases_actors').insert(associations.filter(association => association).flat()); + await knex('releases_actors').insert(associations.filter(association => association).flat()); - // basic actor scraping is failure prone, don't run together with actor association - // await scrapebasicactors(), + // basic actor scraping is failure prone, don't run together with actor association + // await scrapebasicactors(), } module.exports = { - associateActors, - fetchActors, - scrapeActors, - scrapeBasicActors, + associateActors, + fetchActors, + scrapeActors, + scrapeBasicActors, }; diff --git a/src/actors.js b/src/actors.js index 7ddc481b..5dcb7012 100644 --- a/src/actors.js +++ b/src/actors.js @@ -1,125 +1,156 @@ 'use strict'; +const config = require('config'); +const Promise = require('bluebird'); + // const logger = require('./logger')(__filename); const knex = require('./knex'); +const scrapers = require('./scrapers/scrapers'); + +const argv = require('./argv'); const slugify = require('./utils/slugify'); const capitalize = require('./utils/capitalize'); function toBaseActors(actorsOrNames, release) { - return actorsOrNames.map((actorOrName) => { - const name = capitalize(actorOrName.name || actorOrName); - const slug = slugify(name); + return actorsOrNames.map((actorOrName) => { + const name = capitalize(actorOrName.name || actorOrName); + const slug = slugify(name); - const baseActor = { - name, - slug, - network: release.site.network, - }; + const baseActor = { + name, + slug, + network: release?.site.network, + }; - if (actorOrName.name) { - return { - ...actorOrName, - ...baseActor, - }; - } + if (actorOrName.name) { + return { + ...actorOrName, + ...baseActor, + }; + } - return baseActor; - }); + return baseActor; + }); } function curateActorEntry(baseActor, batchId) { - return { - name: baseActor.name, - slug: baseActor.slug, - network_id: null, - batch_id: batchId, - }; + return { + name: baseActor.name, + slug: baseActor.slug, + network_id: null, + batch_id: batchId, + }; } function curateActorEntries(baseActors, batchId) { - return baseActors.map(baseActor => curateActorEntry(baseActor, batchId)); + return baseActors.map(baseActor => curateActorEntry(baseActor, batchId)); } -async function scrapeProfiles() { +async function scrapeActors(actorNames) { + const baseActors = toBaseActors(actorNames); + const sources = argv.sources || config.profiles || Object.keys(scrapers.actors); + const siteSlugs = sources.flat(); + + const [networks, sites, existingActorEntries] = await Promise.all([ + knex('networks').whereIn('slug', siteSlugs), + knex('sites').whereIn('slug', siteSlugs), + knex('actors') + .select(['id', 'name', 'slug']) + .whereIn('slug', baseActors.map(baseActor => baseActor.slug)) + .whereNull('network_id'), + ]); + + const existingActorEntriesBySlug = existingActorEntries.reduce((acc, actorEntry) => ({ ...acc, [actorEntry.slug]: actorEntry }), {}); + const networksBySlug = networks.reduce((acc, network) => ({ ...acc, [network.slug]: { ...network, isNetwork: true } }), {}); + const sitesBySlug = sites.reduce((acc, site) => ({ ...acc, [site.slug]: site }), {}); + + const newBaseActors = baseActors.filter(baseActor => !existingActorEntriesBySlug[baseActor.slug]); + + const [batchId] = newBaseActors.length > 0 ? await knex('batches').insert({ comment: null }).returning('id') : [null]; + const curatedActorEntries = batchId && curateActorEntries(newBaseActors, batchId); + const newActorEntries = batchId && await knex('actors').insert(curatedActorEntries).returning(['id', 'name', 'slug']); + + const actorEntries = existingActorEntries.concat(Array.isArray(newActorEntries) ? newActorEntries : []); + + console.log(actorEntries, newActorEntries, actorEntries); } async function getOrCreateActors(baseActors, batchId) { - const existingActors = await knex('actors') - .select('id', 'alias_for', 'name', 'slug', 'network_id') - .whereIn('slug', baseActors.map(baseActor => baseActor.slug)) - .whereNull('network_id') - .orWhereIn(['slug', 'network_id'], baseActors.map(baseActor => [baseActor.slug, baseActor.network.id])); + const existingActors = await knex('actors') + .select('id', 'alias_for', 'name', 'slug', 'network_id') + .whereIn('slug', baseActors.map(baseActor => baseActor.slug)) + .whereNull('network_id') + .orWhereIn(['slug', 'network_id'], baseActors.map(baseActor => [baseActor.slug, baseActor.network.id])); - // const existingActorSlugs = new Set(existingActors.map(actor => actor.slug)); - const existingActorSlugs = existingActors.reduce((acc, actor) => ({ - ...acc, - [actor.network_id]: { - ...acc[actor.network_id], - [actor.slug]: true, - }, - }), {}); + // const existingActorSlugs = new Set(existingActors.map(actor => actor.slug)); + const existingActorSlugs = existingActors.reduce((acc, actor) => ({ + ...acc, + [actor.network_id]: { + ...acc[actor.network_id], + [actor.slug]: true, + }, + }), {}); - const uniqueBaseActors = baseActors.filter(baseActor => !existingActorSlugs[baseActor.network.id]?.[baseActor.slug] && !existingActorSlugs.null?.[baseActor.slug]); + const uniqueBaseActors = baseActors.filter(baseActor => !existingActorSlugs[baseActor.network.id]?.[baseActor.slug] && !existingActorSlugs.null?.[baseActor.slug]); - const curatedActorEntries = curateActorEntries(uniqueBaseActors, batchId); - const newActors = await knex('actors').insert(curatedActorEntries, ['id', 'alias_for', 'name', 'slug', 'network_id']); + const curatedActorEntries = curateActorEntries(uniqueBaseActors, batchId); + const newActors = await knex('actors').insert(curatedActorEntries, ['id', 'alias_for', 'name', 'slug', 'network_id']); - if (Array.isArray(newActors)) { - return newActors.concat(existingActors); - } + if (Array.isArray(newActors)) { + return newActors.concat(existingActors); + } - return existingActors; + return existingActors; } async function associateActors(releases, batchId) { - const baseActorsByReleaseId = releases.reduce((acc, release) => { - if (release.actors) { - acc[release.id] = toBaseActors(release.actors, release); - } + const baseActorsByReleaseId = releases.reduce((acc, release) => { + if (release.actors) { + acc[release.id] = toBaseActors(release.actors, release); + } - return acc; - }, {}); + return acc; + }, {}); - const baseActors = Object.values(baseActorsByReleaseId).flat(); + const baseActors = Object.values(baseActorsByReleaseId).flat(); - if (baseActors.length === 0) { - return; - } + if (baseActors.length === 0) { + return; + } - const baseActorsBySlugAndNetworkId = baseActors.reduce((acc, baseActor) => ({ - ...acc, - [baseActor.slug]: { - ...acc[baseActor.slug], - [baseActor.network.id]: baseActor, - }, - }), {}); + const baseActorsBySlugAndNetworkId = baseActors.reduce((acc, baseActor) => ({ + ...acc, + [baseActor.slug]: { + ...acc[baseActor.slug], + [baseActor.network.id]: baseActor, + }, + }), {}); - const uniqueBaseActors = Object.values(baseActorsBySlugAndNetworkId).map(baseActorsByNetworkId => Object.values(baseActorsByNetworkId)).flat(); + const uniqueBaseActors = Object.values(baseActorsBySlugAndNetworkId).map(baseActorsByNetworkId => Object.values(baseActorsByNetworkId)).flat(); - const actors = await getOrCreateActors(uniqueBaseActors, batchId); - console.log(actors); - const actorIdsBySlugAndNetworkId = actors.reduce((acc, actor) => ({ - ...acc, - [actor.network_id]: { - ...acc[actor.network_id], - [actor.slug]: actor.alias_for || actor.id, - }, - }), {}); + const actors = await getOrCreateActors(uniqueBaseActors, batchId); - console.log(actorIdsBySlugAndNetworkId); + const actorIdsBySlugAndNetworkId = actors.reduce((acc, actor) => ({ + ...acc, + [actor.network_id]: { + ...acc[actor.network_id], + [actor.slug]: actor.alias_for || actor.id, + }, + }), {}); - const releaseActorAssociations = Object.entries(baseActorsByReleaseId) - .map(([releaseId, releaseActors]) => releaseActors - .map(releaseActor => ({ - release_id: releaseId, - actor_id: actorIdsBySlugAndNetworkId[releaseActor.network.id]?.[releaseActor.slug] || actorIdsBySlugAndNetworkId.null[releaseActor.slug], - }))) - .flat(); + const releaseActorAssociations = Object.entries(baseActorsByReleaseId) + .map(([releaseId, releaseActors]) => releaseActors + .map(releaseActor => ({ + release_id: releaseId, + actor_id: actorIdsBySlugAndNetworkId[releaseActor.network.id]?.[releaseActor.slug] || actorIdsBySlugAndNetworkId.null[releaseActor.slug], + }))) + .flat(); - await knex.raw(`${knex('releases_actors').insert(releaseActorAssociations).toString()} ON CONFLICT DO NOTHING;`); + await knex.raw(`${knex('releases_actors').insert(releaseActorAssociations).toString()} ON CONFLICT DO NOTHING;`); } module.exports = { - associateActors, + associateActors, + scrapeActors, }; diff --git a/src/app.js b/src/app.js index 21f6893f..11b55c64 100644 --- a/src/app.js +++ b/src/app.js @@ -7,39 +7,39 @@ const knex = require('./knex'); const fetchUpdates = require('./updates'); const { fetchScenes, fetchMovies } = require('./deep'); const { storeReleases, updateReleasesSearch } = require('./store-releases'); -const { scrapeActors } = require('./actors-legacy'); +const { scrapeActors } = require('./actors'); async function init() { - if (argv.server) { - await initServer(); - return; - } + if (argv.server) { + await initServer(); + return; + } - if (argv.updateSearch) { - await updateReleasesSearch(); - } + if (argv.updateSearch) { + await updateReleasesSearch(); + } - if (argv.actors) { - await scrapeActors(argv.actors); - } + if (argv.actors) { + await scrapeActors(argv.actors); + } - const updateBaseScenes = (argv.scrape || argv.sites || argv.networks) && await fetchUpdates(); + const updateBaseScenes = (argv.scrape || argv.sites || argv.networks) && await fetchUpdates(); - const deepScenes = argv.deep - ? await fetchScenes([...(argv.scenes || []), ...(updateBaseScenes || [])]) - : updateBaseScenes; + const deepScenes = argv.deep + ? await fetchScenes([...(argv.scenes || []), ...(updateBaseScenes || [])]) + : updateBaseScenes; - const sceneMovies = deepScenes && argv.sceneMovies && deepScenes.map(scene => scene.movie).filter(Boolean); - const deepMovies = await fetchMovies([...(argv.movies || []), ...(sceneMovies || [])]); + const sceneMovies = deepScenes && argv.sceneMovies && deepScenes.map(scene => scene.movie).filter(Boolean); + const deepMovies = await fetchMovies([...(argv.movies || []), ...(sceneMovies || [])]); - if (argv.save) { - await storeReleases([ - ...(deepScenes || []), - ...(deepMovies || []), - ]); - } + if (argv.save) { + await storeReleases([ + ...(deepScenes || []), + ...(deepMovies || []), + ]); + } - knex.destroy(); + knex.destroy(); } module.exports = init; diff --git a/src/argv.js b/src/argv.js index 995f2ad1..e8b85f65 100644 --- a/src/argv.js +++ b/src/argv.js @@ -4,188 +4,188 @@ const config = require('config'); const yargs = require('yargs'); const { argv } = yargs - .command('npm start') - .option('server', { - describe: 'Start web server', - type: 'boolean', - alias: 'web', - }) - .option('scrape', { - describe: 'Scrape sites and networks defined in configuration', - type: 'boolean', - }) - .option('networks', { - describe: 'Networks to scrape (overrides configuration)', - type: 'array', - alias: 'network', - }) - .option('sites', { - describe: 'Sites to scrape (overrides configuration)', - type: 'array', - alias: 'site', - }) - .option('actors', { - describe: 'Scrape actors by name or slug', - type: 'array', - alias: 'actor', - }) - .option('actor-scenes', { - describe: 'Fetch all scenes for an actor', - type: 'boolean', - alias: 'with-releases', - default: false, - }) - .option('movie-scenes', { - describe: 'Fetch all scenes for a movie', - type: 'boolean', - alias: 'with-releases', - default: false, - }) - .option('scene-movies', { - describe: 'Fetch movies for scenes', - type: 'boolean', - default: true, - }) - .option('profiles', { - describe: 'Scrape profiles for new actors after fetching scenes', - type: 'boolean', - alias: 'bios', - default: false, - }) - .option('scene', { - describe: 'Scrape scene info from URL', - type: 'array', - alias: 'scenes', - }) - .option('movie', { - describe: 'Scrape movie info from URL', - type: 'array', - alias: 'movies', - }) - .option('sources', { - describe: 'Use these scrapers for actor data', - type: 'array', - alias: 'source', - }) - .option('deep', { - describe: 'Fetch details for all releases', - type: 'boolean', - default: true, - }) - .option('latest', { - describe: 'Scrape latest releases if available', - type: 'boolean', - default: true, - }) - .option('upcoming', { - describe: 'Scrape upcoming releases if available', - type: 'boolean', - default: true, - }) - .option('redownload', { - describe: 'Don\'t ignore duplicates, update existing entries', - type: 'boolean', - alias: 'force', - }) - .option('after', { - describe: 'Don\'t fetch scenes older than', - type: 'string', - default: config.fetchAfter.join(' '), - }) - .option('last', { - describe: 'Get the latest x releases, no matter the date range', - type: 'number', - }) - .option('null-date-limit', { - describe: 'Limit amount of scenes when dates are missing.', - type: 'number', - default: config.nullDateLimit, - alias: 'limit', - }) - .option('page', { - describe: 'Page to start scraping at', - type: 'number', - default: 1, - }) - .option('save', { - describe: 'Save fetched releases to database', - type: 'boolean', - default: true, - }) - .option('media', { - describe: 'Include any release media', - type: 'boolean', - default: true, - }) - .option('media-limit', { - describe: 'Maximum amount of assets of each type per release', - type: 'number', - default: config.media.limit, - }) - .option('images', { - describe: 'Include any photos, posters or covers', - type: 'boolean', - default: true, - alias: 'pics', - }) - .option('videos', { - describe: 'Include any trailers or teasers', - type: 'boolean', - default: true, - }) - .option('posters', { - describe: 'Include release posters', - type: 'boolean', - default: true, - alias: 'poster', - }) - .option('covers', { - describe: 'Include release covers', - type: 'boolean', - default: true, - alias: 'cover', - }) - .option('photos', { - describe: 'Include release photos', - type: 'boolean', - default: true, - }) - .option('trailers', { - describe: 'Include release trailers', - type: 'boolean', - default: true, - alias: 'trailer', - }) - .option('teasers', { - describe: 'Include release teasers', - type: 'boolean', - default: true, - alias: 'teaser', - }) - .option('avatars', { - describe: 'Include actor avatars', - type: 'boolean', - default: true, - }) - .option('inspect', { - describe: 'Show data in console.', - type: 'boolean', - default: false, - }) - .option('level', { - describe: 'Log level', - type: 'string', - default: process.env.NODE_ENV === 'development' ? 'silly' : 'info', - }) - .option('debug', { - describe: 'Show error stack traces', - type: 'boolean', - default: process.env.NODE_ENV === 'development', - }) - .option('update-search', { - describe: 'Update search documents for all releases.', - type: 'boolean', - default: false, - }); + .command('npm start') + .option('server', { + describe: 'Start web server', + type: 'boolean', + alias: 'web', + }) + .option('scrape', { + describe: 'Scrape sites and networks defined in configuration', + type: 'boolean', + }) + .option('networks', { + describe: 'Networks to scrape (overrides configuration)', + type: 'array', + alias: 'network', + }) + .option('sites', { + describe: 'Sites to scrape (overrides configuration)', + type: 'array', + alias: 'site', + }) + .option('actors', { + describe: 'Scrape actors by name or slug', + type: 'array', + alias: 'actor', + }) + .option('actor-scenes', { + describe: 'Fetch all scenes for an actor', + type: 'boolean', + alias: 'with-releases', + default: false, + }) + .option('movie-scenes', { + describe: 'Fetch all scenes for a movie', + type: 'boolean', + alias: 'with-releases', + default: false, + }) + .option('scene-movies', { + describe: 'Fetch movies for scenes', + type: 'boolean', + default: true, + }) + .option('profiles', { + describe: 'Scrape profiles for new actors after fetching scenes', + type: 'boolean', + alias: 'bios', + default: false, + }) + .option('scene', { + describe: 'Scrape scene info from URL', + type: 'array', + alias: 'scenes', + }) + .option('movie', { + describe: 'Scrape movie info from URL', + type: 'array', + alias: 'movies', + }) + .option('sources', { + describe: 'Use these scrapers for actor data', + type: 'array', + alias: 'source', + }) + .option('deep', { + describe: 'Fetch details for all releases', + type: 'boolean', + default: true, + }) + .option('latest', { + describe: 'Scrape latest releases if available', + type: 'boolean', + default: true, + }) + .option('upcoming', { + describe: 'Scrape upcoming releases if available', + type: 'boolean', + default: true, + }) + .option('redownload', { + describe: 'Don\'t ignore duplicates, update existing entries', + type: 'boolean', + alias: 'force', + }) + .option('after', { + describe: 'Don\'t fetch scenes older than', + type: 'string', + default: config.fetchAfter.join(' '), + }) + .option('last', { + describe: 'Get the latest x releases, no matter the date range', + type: 'number', + }) + .option('null-date-limit', { + describe: 'Limit amount of scenes when dates are missing.', + type: 'number', + default: config.nullDateLimit, + alias: 'limit', + }) + .option('page', { + describe: 'Page to start scraping at', + type: 'number', + default: 1, + }) + .option('save', { + describe: 'Save fetched releases to database', + type: 'boolean', + default: true, + }) + .option('media', { + describe: 'Include any release media', + type: 'boolean', + default: true, + }) + .option('media-limit', { + describe: 'Maximum amount of assets of each type per release', + type: 'number', + default: config.media.limit, + }) + .option('images', { + describe: 'Include any photos, posters or covers', + type: 'boolean', + default: true, + alias: 'pics', + }) + .option('videos', { + describe: 'Include any trailers or teasers', + type: 'boolean', + default: true, + }) + .option('posters', { + describe: 'Include release posters', + type: 'boolean', + default: true, + alias: 'poster', + }) + .option('covers', { + describe: 'Include release covers', + type: 'boolean', + default: true, + alias: 'cover', + }) + .option('photos', { + describe: 'Include release photos', + type: 'boolean', + default: true, + }) + .option('trailers', { + describe: 'Include release trailers', + type: 'boolean', + default: true, + alias: 'trailer', + }) + .option('teasers', { + describe: 'Include release teasers', + type: 'boolean', + default: true, + alias: 'teaser', + }) + .option('avatars', { + describe: 'Include actor avatars', + type: 'boolean', + default: true, + }) + .option('inspect', { + describe: 'Show data in console.', + type: 'boolean', + default: false, + }) + .option('level', { + describe: 'Log level', + type: 'string', + default: process.env.NODE_ENV === 'development' ? 'silly' : 'info', + }) + .option('debug', { + describe: 'Show error stack traces', + type: 'boolean', + default: process.env.NODE_ENV === 'development', + }) + .option('update-search', { + describe: 'Update search documents for all releases.', + type: 'boolean', + default: false, + }); module.exports = argv; diff --git a/src/deep.js b/src/deep.js index fcfd84b3..c648e6cf 100644 --- a/src/deep.js +++ b/src/deep.js @@ -11,159 +11,160 @@ const { curateSites } = require('./sites'); const { curateNetworks } = require('./networks'); function urlToSiteSlug(url) { - try { - const slug = new URL(url) - .hostname - .match(/([\w-]+)\.\w+$/)?.[1]; + try { + const slug = new URL(url) + .hostname + .match(/([\w-]+)\.\w+$/)?.[1]; - return slug; - } catch (error) { - logger.warn(`Failed to derive site slug from '${url}': ${error.message}`); + return slug; + } catch (error) { + logger.warn(`Failed to derive site slug from '${url}': ${error.message}`); - return null; - } + return null; + } } async function findSites(baseReleases) { - const baseReleasesWithoutSite = baseReleases.filter(release => release.url && !release.site); + const baseReleasesWithoutSite = baseReleases.filter(release => release.url && !release.site); - const siteSlugs = Array.from(new Set( - baseReleasesWithoutSite - .map(baseRelease => urlToSiteSlug(baseRelease.url)) - .filter(Boolean), - )); + const siteSlugs = Array.from(new Set( + baseReleasesWithoutSite + .map(baseRelease => urlToSiteSlug(baseRelease.url)) + .filter(Boolean), + )); - const siteEntries = await knex('sites') - .leftJoin('networks', 'networks.id', 'sites.network_id') - .select('sites.*', 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.parameters as network_parameters', 'networks.description as network_description') - .whereIn('sites.slug', siteSlugs); + const siteEntries = await knex('sites') + .leftJoin('networks', 'networks.id', 'sites.network_id') + .select('sites.*', 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.parameters as network_parameters', 'networks.description as network_description') + .whereIn('sites.slug', siteSlugs); - const networkEntries = await knex('networks').whereIn('slug', siteSlugs); + const networkEntries = await knex('networks').whereIn('slug', siteSlugs); - const sites = await curateSites(siteEntries, true, false); - const networks = await curateNetworks(networkEntries, true, false, false); - const markedNetworks = networks.map(network => ({ ...network, isFallback: true })); + const sites = await curateSites(siteEntries, true, false); + const networks = await curateNetworks(networkEntries, true, false, false); + const markedNetworks = networks.map(network => ({ ...network, isNetwork: true })); - const sitesBySlug = [] - .concat(markedNetworks, sites) - .reduce((accSites, site) => ({ ...accSites, [site.slug]: site }), {}); + const sitesBySlug = [] + .concat(markedNetworks, sites) + .reduce((accSites, site) => ({ ...accSites, [site.slug]: site }), {}); - return sitesBySlug; + return sitesBySlug; } function toBaseReleases(baseReleasesOrUrls) { - return baseReleasesOrUrls - .map((baseReleaseOrUrl) => { - if (baseReleaseOrUrl.url) { - // base release with URL - return { - ...baseReleaseOrUrl, - deep: false, - }; - } + return baseReleasesOrUrls + .map((baseReleaseOrUrl) => { + if (baseReleaseOrUrl.url) { + // base release with URL + return { + ...baseReleaseOrUrl, + deep: false, + }; + } - if (/^http/.test(baseReleaseOrUrl)) { - // URL - return { - url: baseReleaseOrUrl, - deep: false, - }; - } + if (/^http/.test(baseReleaseOrUrl)) { + // URL + return { + url: baseReleaseOrUrl, + deep: false, + }; + } - if (typeof baseReleaseOrUrl === 'object' && !Array.isArray(baseReleaseOrUrl)) { - // base release without URL, prepare for passthrough - return { - ...baseReleaseOrUrl, - deep: false, - }; - } + if (typeof baseReleaseOrUrl === 'object' && !Array.isArray(baseReleaseOrUrl)) { + // base release without URL, prepare for passthrough + return { + ...baseReleaseOrUrl, + deep: false, + }; + } - logger.warn(`Malformed base release, discarding '${baseReleaseOrUrl}'`); - return null; - }) - .filter(Boolean); + logger.warn(`Malformed base release, discarding '${baseReleaseOrUrl}'`); + return null; + }) + .filter(Boolean); } async function scrapeRelease(baseRelease, sites, type = 'scene') { - const site = baseRelease.site || sites[urlToSiteSlug(baseRelease.url)]; + const site = baseRelease.site || sites[urlToSiteSlug(baseRelease.url)]; + const siteWithFallbackNetwork = site.isNetwork ? { ...site, network: site } : site; // make site.network available, even when site is network fallback - if (!site) { - logger.warn(`No site available for ${baseRelease.url}`); - return baseRelease; - } + if (!site) { + logger.warn(`No site available for ${baseRelease.url}`); + return baseRelease; + } - if ((!baseRelease.url && !baseRelease.path) || !argv.deep) { - return { - ...baseRelease, - site, - }; - } + if ((!baseRelease.url && !baseRelease.path) || !argv.deep) { + return { + ...baseRelease, + site, + }; + } - const scraper = scrapers.releases[site.slug] || scrapers.releases[site.network.slug]; + const scraper = scrapers.releases[site.slug] || scrapers.releases[site.network.slug]; - if (!scraper) { - logger.warn(`Could not find scraper for ${baseRelease.url}`); - return baseRelease; - } + if (!scraper) { + logger.warn(`Could not find scraper for ${baseRelease.url}`); + return baseRelease; + } - if ((type === 'scene' && !scraper.fetchScene) || (type === 'movie' && !scraper.fetchMovie)) { - logger.warn(`The '${site.name}'-scraper cannot fetch individual ${type}s`); - return baseRelease; - } + if ((type === 'scene' && !scraper.fetchScene) || (type === 'movie' && !scraper.fetchMovie)) { + logger.warn(`The '${site.name}'-scraper cannot fetch individual ${type}s`); + return baseRelease; + } - try { - logger.verbose(`Fetching ${type} ${baseRelease.url}`); + try { + logger.verbose(`Fetching ${type} ${baseRelease.url}`); - const scrapedRelease = type === 'scene' - ? await scraper.fetchScene(baseRelease.url, site, baseRelease, null, include) - : await scraper.fetchMovie(baseRelease.url, site, baseRelease, null, include); + const scrapedRelease = type === 'scene' + ? await scraper.fetchScene(baseRelease.url, siteWithFallbackNetwork, baseRelease, null, include) + : await scraper.fetchMovie(baseRelease.url, siteWithFallbackNetwork, baseRelease, null, include); - const mergedRelease = { - ...baseRelease, - ...scrapedRelease, - deep: !!scrapedRelease, - site, - }; + const mergedRelease = { + ...baseRelease, + ...scrapedRelease, + deep: !!scrapedRelease, + site, + }; - if (scrapedRelease && baseRelease?.tags) { - // accumulate all available tags - mergedRelease.tags = baseRelease.tags.concat(scrapedRelease.tags); - } + if (scrapedRelease && baseRelease?.tags) { + // accumulate all available tags + mergedRelease.tags = baseRelease.tags.concat(scrapedRelease.tags); + } - return mergedRelease; - } catch (error) { - logger.error(`Deep scrape failed for ${baseRelease.url}: ${error.message}`); - return baseRelease; - } + return mergedRelease; + } catch (error) { + logger.error(`Deep scrape failed for ${baseRelease.url}: ${error.message}`); + return baseRelease; + } } async function scrapeReleases(baseReleases, sites, type) { - return Promise.map( - baseReleases, - async baseRelease => scrapeRelease(baseRelease, sites, type), - { concurrency: 10 }, - ); + return Promise.map( + baseReleases, + async baseRelease => scrapeRelease(baseRelease, sites, type), + { concurrency: 10 }, + ); } async function fetchReleases(baseReleasesOrUrls, type = 'scene') { - const baseReleases = toBaseReleases(baseReleasesOrUrls); - const sites = await findSites(baseReleases); + const baseReleases = toBaseReleases(baseReleasesOrUrls); + const sites = await findSites(baseReleases); - const deepReleases = await scrapeReleases(baseReleases, sites, type); + const deepReleases = await scrapeReleases(baseReleases, sites, type); - return deepReleases; + return deepReleases; } async function fetchScenes(baseReleasesOrUrls) { - return fetchReleases(baseReleasesOrUrls, 'scene'); + return fetchReleases(baseReleasesOrUrls, 'scene'); } async function fetchMovies(baseReleasesOrUrls) { - return fetchReleases(baseReleasesOrUrls, 'movie'); + return fetchReleases(baseReleasesOrUrls, 'movie'); } module.exports = { - fetchReleases, - fetchScenes, - fetchMovies, + fetchReleases, + fetchScenes, + fetchMovies, }; diff --git a/src/knex.js b/src/knex.js index baebbfee..02240346 100644 --- a/src/knex.js +++ b/src/knex.js @@ -4,8 +4,8 @@ const config = require('config'); const knex = require('knex'); module.exports = knex({ - client: 'pg', - connection: config.database, - // performance overhead, don't use asyncStackTraces in production - asyncStackTraces: process.env.NODE_ENV === 'development', + client: 'pg', + connection: config.database, + // performance overhead, don't use asyncStackTraces in production + asyncStackTraces: process.env.NODE_ENV === 'development', }); diff --git a/src/logger.js b/src/logger.js index 7e78981c..6621e08b 100644 --- a/src/logger.js +++ b/src/logger.js @@ -9,31 +9,31 @@ require('winston-daily-rotate-file'); const args = require('./argv'); function logger(filepath) { - const root = filepath.match(/src\/|dist\//); - const filename = filepath.slice(root.index + root[0].length) - .replace(path.extname(filepath), ''); + const root = filepath.match(/src\/|dist\//); + const filename = filepath.slice(root.index + root[0].length) + .replace(path.extname(filepath), ''); - return winston.createLogger({ - format: winston.format.combine( - winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), - winston.format(info => (info instanceof Error - ? { ...info, message: info.stack } - : { ...info, message: typeof info.message === 'string' ? info.message : util.inspect(info.message) }))(), - winston.format.colorize(), - winston.format.printf(({ level, timestamp, label, message }) => `${timestamp} ${level} [${label || filename}] ${message}`), - ), - transports: [ - new winston.transports.Console({ - level: args.level, - timestamp: true, - }), - new winston.transports.DailyRotateFile({ - datePattern: 'YYYY-MM-DD', - filename: 'log/%DATE%.log', - level: 'silly', - }), - ], - }); + return winston.createLogger({ + format: winston.format.combine( + winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + winston.format(info => (info instanceof Error + ? { ...info, message: info.stack } + : { ...info, message: typeof info.message === 'string' ? info.message : util.inspect(info.message) }))(), + winston.format.colorize(), + winston.format.printf(({ level, timestamp, label, message }) => `${timestamp} ${level} [${label || filename}] ${message}`), + ), + transports: [ + new winston.transports.Console({ + level: args.level, + timestamp: true, + }), + new winston.transports.DailyRotateFile({ + datePattern: 'YYYY-MM-DD', + filename: 'log/%DATE%.log', + level: 'silly', + }), + ], + }); } module.exports = logger; diff --git a/src/media.js b/src/media.js index 87b61522..cf22e051 100644 --- a/src/media.js +++ b/src/media.js @@ -19,591 +19,591 @@ const http = require('./utils/http'); const { get } = require('./utils/qu'); function sampleMedias(medias, limit = config.media.limit, preferLast = true) { - // limit media sets, use extras as fallbacks - if (medias.length <= limit) { - return medias; - } + // limit media sets, use extras as fallbacks + if (medias.length <= limit) { + return medias; + } - const chunkSize = Math.floor(medias.length / limit); - const rest = medias.length - (limit * chunkSize); + const chunkSize = Math.floor(medias.length / limit); + const rest = medias.length - (limit * chunkSize); - const chunks = Array.from( - { length: limit }, - (value, index) => { - const start = (chunkSize * index) + Math.min(index, rest); + const chunks = Array.from( + { length: limit }, + (value, index) => { + const start = (chunkSize * index) + Math.min(index, rest); - return medias.slice( - start, - start + chunkSize + (index < rest ? 1 : 0), - ); - }, - ); + return medias.slice( + start, + start + chunkSize + (index < rest ? 1 : 0), + ); + }, + ); - // flip last chunk so the very last image (often the best cumshot) is tried first - const lastPreferredChunks = preferLast - ? chunks.slice(0, -1).concat(chunks.slice(-1).reverse()) - : chunks; + // flip last chunk so the very last image (often the best cumshot) is tried first + const lastPreferredChunks = preferLast + ? chunks.slice(0, -1).concat(chunks.slice(-1).reverse()) + : chunks; - const groupedMedias = lastPreferredChunks.map((chunk) => { - // merge chunked medias into single media with grouped fallback priorities, - // so the first sources of each media is preferred over all second sources, etc. - const sources = chunk - .reduce((accSources, media) => { - media.sources.forEach((source, index) => { - if (!accSources[index]) { - accSources.push([source]); - return; - } + const groupedMedias = lastPreferredChunks.map((chunk) => { + // merge chunked medias into single media with grouped fallback priorities, + // so the first sources of each media is preferred over all second sources, etc. + const sources = chunk + .reduce((accSources, media) => { + media.sources.forEach((source, index) => { + if (!accSources[index]) { + accSources.push([source]); + return; + } - accSources[index].push(source); - }); + accSources[index].push(source); + }); - return accSources; - }, []) - .flat(); + return accSources; + }, []) + .flat(); - return { - id: chunk[0].id, - role: chunk[0].role, - sources, - }; - }); + return { + id: chunk[0].id, + role: chunk[0].role, + sources, + }; + }); - return groupedMedias; + return groupedMedias; } function itemsByKey(items, key) { - return items.reduce((acc, item) => ({ ...acc, [item[key]]: item }), {}); + return items.reduce((acc, item) => ({ ...acc, [item[key]]: item }), {}); } function toBaseSource(rawSource) { - if (rawSource.src || (rawSource.extract && rawSource.url)) { - const baseSource = {}; + if (rawSource.src || (rawSource.extract && rawSource.url)) { + const baseSource = {}; - if (rawSource.src) baseSource.src = rawSource.src; - if (rawSource.quality) baseSource.quality = rawSource.quality; - if (rawSource.type) baseSource.type = rawSource.type; + if (rawSource.src) baseSource.src = rawSource.src; + if (rawSource.quality) baseSource.quality = rawSource.quality; + if (rawSource.type) baseSource.type = rawSource.type; - if (rawSource.url) baseSource.url = rawSource.url; - if (rawSource.extract) baseSource.extract = rawSource.extract; + if (rawSource.url) baseSource.url = rawSource.url; + if (rawSource.extract) baseSource.extract = rawSource.extract; - if (rawSource.referer) baseSource.referer = rawSource.referer; - if (rawSource.host) baseSource.host = rawSource.host; + if (rawSource.referer) baseSource.referer = rawSource.referer; + if (rawSource.host) baseSource.host = rawSource.host; - if (rawSource.copyright) baseSource.copyright = rawSource.copyright; - if (rawSource.comment) baseSource.comment = rawSource.comment; - if (rawSource.group) baseSource.group = rawSource.group; + if (rawSource.copyright) baseSource.copyright = rawSource.copyright; + if (rawSource.comment) baseSource.comment = rawSource.comment; + if (rawSource.group) baseSource.group = rawSource.group; - return baseSource; - } + return baseSource; + } - if (typeof rawSource === 'string') { - return { - src: rawSource, - }; - } + if (typeof rawSource === 'string') { + return { + src: rawSource, + }; + } - return null; + return null; } function baseSourceToBaseMedia(baseSource, role) { - if (Array.isArray(baseSource)) { - if (baseSource.length > 0) { - return { - id: nanoid(), - role, - sources: baseSource, - }; - } + if (Array.isArray(baseSource)) { + if (baseSource.length > 0) { + return { + id: nanoid(), + role, + sources: baseSource, + }; + } - return null; - } + return null; + } - if (baseSource) { - return { - id: nanoid(), - role, - sources: [baseSource], - }; - } + if (baseSource) { + return { + id: nanoid(), + role, + sources: [baseSource], + }; + } - return null; + return null; } function fallbackMediaToBaseMedia(rawMedia, role) { - const baseSources = rawMedia - .map(source => toBaseSource(source)) - .filter(Boolean); + const baseSources = rawMedia + .map(source => toBaseSource(source)) + .filter(Boolean); - return baseSourceToBaseMedia(baseSources, role); + return baseSourceToBaseMedia(baseSources, role); } function toBaseMedias(rawMedias, role) { - if (!rawMedias || rawMedias.length === 0) { - return []; - } + if (!rawMedias || rawMedias.length === 0) { + return []; + } - const baseMedias = rawMedias.map((rawMedia) => { - if (!rawMedia) { - return null; - } + const baseMedias = rawMedias.map((rawMedia) => { + if (!rawMedia) { + return null; + } - if (Array.isArray(rawMedia)) { - // fallback sources provided - return fallbackMediaToBaseMedia(rawMedia, role); - } + if (Array.isArray(rawMedia)) { + // fallback sources provided + return fallbackMediaToBaseMedia(rawMedia, role); + } - const baseSource = toBaseSource(rawMedia); + const baseSource = toBaseSource(rawMedia); - return baseSourceToBaseMedia(baseSource, role); - }).filter(Boolean); + return baseSourceToBaseMedia(baseSource, role); + }).filter(Boolean); - const sampledBaseMedias = sampleMedias(baseMedias); + const sampledBaseMedias = sampleMedias(baseMedias); - return sampledBaseMedias; + return sampledBaseMedias; } async function findSourceDuplicates(baseMedias) { - const sourceUrls = baseMedias - .map(baseMedia => baseMedia.sources.map(source => source.src)) - .flat() - .filter(Boolean); + const sourceUrls = baseMedias + .map(baseMedia => baseMedia.sources.map(source => source.src)) + .flat() + .filter(Boolean); - const extractUrls = baseMedias - .map(baseMedia => baseMedia.sources.map(source => source.url)) - .flat() - .filter(Boolean); + const extractUrls = baseMedias + .map(baseMedia => baseMedia.sources.map(source => source.url)) + .flat() + .filter(Boolean); - const [existingSourceMedia, existingExtractMedia] = await Promise.all([ - knex('media').whereIn('source', sourceUrls), - knex('media').whereIn('source_page', extractUrls), - ]); + const [existingSourceMedia, existingExtractMedia] = await Promise.all([ + knex('media').whereIn('source', sourceUrls), + knex('media').whereIn('source_page', extractUrls), + ]); - const existingSourceMediaByUrl = itemsByKey(existingSourceMedia, 'source'); - const existingExtractMediaByUrl = itemsByKey(existingExtractMedia, 'source_page'); + const existingSourceMediaByUrl = itemsByKey(existingSourceMedia, 'source'); + const existingExtractMediaByUrl = itemsByKey(existingExtractMedia, 'source_page'); - return [existingSourceMediaByUrl, existingExtractMediaByUrl]; + return [existingSourceMediaByUrl, existingExtractMediaByUrl]; } async function findHashDuplicates(medias) { - const hashes = medias.map(media => media.meta?.hash || media.entry?.hash).filter(Boolean); + const hashes = medias.map(media => media.meta?.hash || media.entry?.hash).filter(Boolean); - const existingHashMediaEntries = await knex('media').whereIn('hash', hashes); - const existingHashMediaEntriesByHash = itemsByKey(existingHashMediaEntries, 'hash'); + const existingHashMediaEntries = await knex('media').whereIn('hash', hashes); + const existingHashMediaEntriesByHash = itemsByKey(existingHashMediaEntries, 'hash'); - const uniqueHashMedias = medias.filter(media => !media.entry && !existingHashMediaEntriesByHash[media.meta?.hash]); + const uniqueHashMedias = medias.filter(media => !media.entry && !existingHashMediaEntriesByHash[media.meta?.hash]); - const { selfDuplicateMedias, selfUniqueMediasByHash } = uniqueHashMedias.reduce((acc, media) => { - if (!media.meta?.hash) { - return acc; - } + const { selfDuplicateMedias, selfUniqueMediasByHash } = uniqueHashMedias.reduce((acc, media) => { + if (!media.meta?.hash) { + return acc; + } - if (acc.selfUniqueMediasByHash[media.meta.hash]) { - acc.selfDuplicateMedias.push({ - ...media, - use: acc.selfUniqueMediasByHash[media.meta.hash].id, - }); + if (acc.selfUniqueMediasByHash[media.meta.hash]) { + acc.selfDuplicateMedias.push({ + ...media, + use: acc.selfUniqueMediasByHash[media.meta.hash].id, + }); - return acc; - } + return acc; + } - acc.selfUniqueMediasByHash[media.meta.hash] = media; + acc.selfUniqueMediasByHash[media.meta.hash] = media; - return acc; - }, { - selfDuplicateMedias: [], - selfUniqueMediasByHash: {}, - }); + return acc; + }, { + selfDuplicateMedias: [], + selfUniqueMediasByHash: {}, + }); - const selfUniqueHashMedias = Object.values(selfUniqueMediasByHash); + const selfUniqueHashMedias = Object.values(selfUniqueMediasByHash); - const existingHashMedias = medias - .filter(media => existingHashMediaEntriesByHash[media.entry?.hash || media.meta?.hash]) - .map(media => ({ - ...media, - entry: existingHashMediaEntriesByHash[media.entry?.hash || media.meta?.hash], - })) - .concat(selfDuplicateMedias); + const existingHashMedias = medias + .filter(media => existingHashMediaEntriesByHash[media.entry?.hash || media.meta?.hash]) + .map(media => ({ + ...media, + entry: existingHashMediaEntriesByHash[media.entry?.hash || media.meta?.hash], + })) + .concat(selfDuplicateMedias); - return [selfUniqueHashMedias, existingHashMedias]; + return [selfUniqueHashMedias, existingHashMedias]; } async function extractSource(baseSource, { existingExtractMediaByUrl }) { - if (typeof baseSource.extract !== 'function' || !baseSource.url) { - return baseSource; - } + if (typeof baseSource.extract !== 'function' || !baseSource.url) { + return baseSource; + } - const existingExtractMedia = existingExtractMediaByUrl[baseSource.url]; + const existingExtractMedia = existingExtractMediaByUrl[baseSource.url]; - if (existingExtractMedia) { - // media entry found by extract URL - return { - ...baseSource, - entry: existingExtractMedia, - }; - } + if (existingExtractMedia) { + // media entry found by extract URL + return { + ...baseSource, + entry: existingExtractMedia, + }; + } - const res = await get(baseSource.url); + const res = await get(baseSource.url); - if (res.ok) { - const src = await baseSource.extract(res.item); + if (res.ok) { + const src = await baseSource.extract(res.item); - return { - ...baseSource, - src, - }; - } + return { + ...baseSource, + src, + }; + } - throw new Error(`Could not extract source from ${baseSource.url}: ${res.status}`); + throw new Error(`Could not extract source from ${baseSource.url}: ${res.status}`); } async function storeImageFile(media, hashDir, hashSubDir, filename, filedir, filepath) { - const thumbdir = path.join(media.role, 'thumbs', hashDir, hashSubDir); - const thumbpath = path.join(thumbdir, filename); + const thumbdir = path.join(media.role, 'thumbs', hashDir, hashSubDir); + const thumbpath = path.join(thumbdir, filename); - const lazydir = path.join(media.role, 'lazy', hashDir, hashSubDir); - const lazypath = path.join(lazydir, filename); + const lazydir = path.join(media.role, 'lazy', hashDir, hashSubDir); + const lazypath = path.join(lazydir, filename); - await Promise.all([ - fsPromises.mkdir(path.join(config.media.path, filedir), { recursive: true }), - fsPromises.mkdir(path.join(config.media.path, thumbdir), { recursive: true }), - fsPromises.mkdir(path.join(config.media.path, lazydir), { recursive: true }), - ]); + await Promise.all([ + fsPromises.mkdir(path.join(config.media.path, filedir), { recursive: true }), + fsPromises.mkdir(path.join(config.media.path, thumbdir), { recursive: true }), + fsPromises.mkdir(path.join(config.media.path, lazydir), { recursive: true }), + ]); - const image = sharp(media.file.path); - const info = await image.metadata(); + const image = sharp(media.file.path); + const info = await image.metadata(); - // generate thumbnail and lazy - await Promise.all([ - image - .resize({ - height: config.media.thumbnailSize, - withoutEnlargement: true, - }) - .jpeg({ quality: config.media.thumbnailQuality }) - .toFile(path.join(config.media.path, thumbpath)), - image - .resize({ - height: config.media.lazySize, - withoutEnlargement: true, - }) - .jpeg({ quality: config.media.lazyQuality }) - .toFile(path.join(config.media.path, lazypath)), - ]); + // generate thumbnail and lazy + await Promise.all([ + image + .resize({ + height: config.media.thumbnailSize, + withoutEnlargement: true, + }) + .jpeg({ quality: config.media.thumbnailQuality }) + .toFile(path.join(config.media.path, thumbpath)), + image + .resize({ + height: config.media.lazySize, + withoutEnlargement: true, + }) + .jpeg({ quality: config.media.lazyQuality }) + .toFile(path.join(config.media.path, lazypath)), + ]); - if (media.meta.subtype === 'jpeg') { - // move temp file to permanent location - await fsPromises.rename(media.file.path, path.join(config.media.path, filepath)); - } else { - // convert to JPEG and write to permanent location - await sharp(media.file.path) - .jpeg() - .toFile(path.join(config.media.path, filepath)); + if (media.meta.subtype === 'jpeg') { + // move temp file to permanent location + await fsPromises.rename(media.file.path, path.join(config.media.path, filepath)); + } else { + // convert to JPEG and write to permanent location + await sharp(media.file.path) + .jpeg() + .toFile(path.join(config.media.path, filepath)); - // remove temp file - await fsPromises.unlink(media.file.path); - } + // remove temp file + await fsPromises.unlink(media.file.path); + } - logger.silly(`Stored thumbnail, lazy and permanent media file for ${media.id} from ${media.src} at ${filepath}`); + logger.silly(`Stored thumbnail, lazy and permanent media file for ${media.id} from ${media.src} at ${filepath}`); - return { - ...media, - file: { - path: filepath, - thumbnail: thumbpath, - lazy: lazypath, - }, - meta: { - ...media.meta, - width: info.width, - height: info.height, - }, - }; + return { + ...media, + file: { + path: filepath, + thumbnail: thumbpath, + lazy: lazypath, + }, + meta: { + ...media.meta, + width: info.width, + height: info.height, + }, + }; } async function storeFile(media) { - try { - const hashDir = media.meta.hash.slice(0, 2); - const hashSubDir = media.meta.hash.slice(2, 4); - const hashFilename = media.meta.hash.slice(4); + try { + const hashDir = media.meta.hash.slice(0, 2); + const hashSubDir = media.meta.hash.slice(2, 4); + const hashFilename = media.meta.hash.slice(4); - const filename = media.quality - ? `${hashFilename}_${media.quality}.${media.meta.extension}` - : `${hashFilename}.${media.meta.extension}`; + const filename = media.quality + ? `${hashFilename}_${media.quality}.${media.meta.extension}` + : `${hashFilename}.${media.meta.extension}`; - const filedir = path.join(media.role, hashDir, hashSubDir); - const filepath = path.join(filedir, filename); + const filedir = path.join(media.role, hashDir, hashSubDir); + const filepath = path.join(filedir, filename); - if (media.meta.type === 'image') { - return storeImageFile(media, hashDir, hashSubDir, filename, filedir, filepath); - } + if (media.meta.type === 'image') { + return storeImageFile(media, hashDir, hashSubDir, filename, filedir, filepath); + } - const [stat] = await Promise.all([ - fsPromises.stat(media.file.path), - fsPromises.mkdir(path.join(config.media.path, filedir), { recursive: true }), - ]); + const [stat] = await Promise.all([ + fsPromises.stat(media.file.path), + fsPromises.mkdir(path.join(config.media.path, filedir), { recursive: true }), + ]); - await fsPromises.rename(media.file.path, path.join(config.media.path, filepath)); + await fsPromises.rename(media.file.path, path.join(config.media.path, filepath)); - logger.silly(`Stored permanent media file for ${media.id} from ${media.src} at ${filepath}`); + logger.silly(`Stored permanent media file for ${media.id} from ${media.src} at ${filepath}`); - return { - ...media, - file: { - path: filepath, - }, - meta: { - ...media.meta, - size: stat.size, - }, - }; - } catch (error) { - logger.warn(`Failed to store ${media.src}: ${error.message}`); + return { + ...media, + file: { + path: filepath, + }, + meta: { + ...media.meta, + size: stat.size, + }, + }; + } catch (error) { + logger.warn(`Failed to store ${media.src}: ${error.message}`); - return null; - } + return null; + } } async function fetchSource(source, baseMedia) { - logger.silly(`Fetching media from ${source.src}`); - // attempts + logger.silly(`Fetching media from ${source.src}`); + // attempts - async function attempt(attempts = 1) { - try { - const tempFilePath = path.join(config.media.path, 'temp', `${baseMedia.id}`); + async function attempt(attempts = 1) { + try { + const tempFilePath = path.join(config.media.path, 'temp', `${baseMedia.id}`); - const hasher = new blake2.Hash('blake2b'); - hasher.setEncoding('hex'); + const hasher = new blake2.Hash('blake2b'); + hasher.setEncoding('hex'); - const tempFileTarget = fs.createWriteStream(tempFilePath); - const hashStream = new PassThrough(); - let size = 0; + const tempFileTarget = fs.createWriteStream(tempFilePath); + const hashStream = new PassThrough(); + let size = 0; - hashStream.on('data', (chunk) => { - size += chunk.length; - hasher.write(chunk); - }); + hashStream.on('data', (chunk) => { + size += chunk.length; + hasher.write(chunk); + }); - const res = await http.get(source.src, { - ...(source.referer && { referer: source.referer }), - ...(source.host && { host: source.host }), - }, { - stream: true, // sources are fetched in parallel, don't gobble up memory - transforms: [hashStream], - destination: tempFileTarget, - }); + const res = await http.get(source.src, { + ...(source.referer && { referer: source.referer }), + ...(source.host && { host: source.host }), + }, { + stream: true, // sources are fetched in parallel, don't gobble up memory + transforms: [hashStream], + destination: tempFileTarget, + }); - hasher.end(); + hasher.end(); - const hash = hasher.read(); - const { pathname } = new URL(source.src); - const mimetype = res.headers['content-type'] || mime.getType(pathname); - const [type, subtype] = mimetype.split('/'); - const extension = mime.getExtension(mimetype); + const hash = hasher.read(); + const { pathname } = new URL(source.src); + const mimetype = res.headers['content-type'] || mime.getType(pathname); + const [type, subtype] = mimetype.split('/'); + const extension = mime.getExtension(mimetype); - if (!res.ok) { - throw new Error(`Response ${res.status} not OK`); - } + if (!res.ok) { + throw new Error(`Response ${res.status} not OK`); + } - return { - ...source, - file: { - path: tempFilePath, - }, - meta: { - hash, - mimetype, - extension, - type, - subtype, - size, - }, - }; - } catch (error) { - logger.warn(`Failed attempt ${attempts}/3 to fetch ${source.src}: ${error.message}`); + return { + ...source, + file: { + path: tempFilePath, + }, + meta: { + hash, + mimetype, + extension, + type, + subtype, + size, + }, + }; + } catch (error) { + logger.warn(`Failed attempt ${attempts}/3 to fetch ${source.src}: ${error.message}`); - if (attempts < 3) { - await Promise.delay(1000); + if (attempts < 3) { + await Promise.delay(1000); - return attempt(attempts + 1); - } + return attempt(attempts + 1); + } - throw new Error(`Failed to fetch ${source.src}: ${error.message}`); - } - } + throw new Error(`Failed to fetch ${source.src}: ${error.message}`); + } + } - return attempt(1); + return attempt(1); } async function trySource(baseSource, existingMedias, baseMedia) { - // catch error and try the next source - const extractedSource = await extractSource(baseSource, existingMedias); - const existingSourceMedia = existingMedias.existingSourceMediaByUrl[extractedSource.src]; + // catch error and try the next source + const extractedSource = await extractSource(baseSource, existingMedias); + const existingSourceMedia = existingMedias.existingSourceMediaByUrl[extractedSource.src]; - if (!argv.force && extractedSource.entry) { - logger.silly(`Media page URL already in database, not extracting ${baseSource.url}`); + if (!argv.force && extractedSource.entry) { + logger.silly(`Media page URL already in database, not extracting ${baseSource.url}`); - // media entry found during extraction, don't fetch - return extractedSource; - } + // media entry found during extraction, don't fetch + return extractedSource; + } - if (!argv.force && existingSourceMedia) { - logger.silly(`Media source URL already in database, skipping ${baseSource.src}`); + if (!argv.force && existingSourceMedia) { + logger.silly(`Media source URL already in database, skipping ${baseSource.src}`); - // media entry found by source URL, don't fetch - return { - ...baseSource, - entry: existingSourceMedia, - }; - } + // media entry found by source URL, don't fetch + return { + ...baseSource, + entry: existingSourceMedia, + }; + } - return fetchSource(extractedSource, baseMedia); + return fetchSource(extractedSource, baseMedia); } async function fetchMedia(baseMedia, existingMedias) { - try { - const source = await baseMedia.sources.reduce( - // try each source until success - (result, baseSource, baseSourceIndex) => result.catch(async (error) => { - if (error.message) { - logger.warn(error.message); - } + try { + const source = await baseMedia.sources.reduce( + // try each source until success + (result, baseSource, baseSourceIndex) => result.catch(async (error) => { + if (error.message) { + logger.warn(error.message); + } - return trySource(baseSource, existingMedias, baseMedia, baseSourceIndex); - }), - Promise.reject(new Error()), - ); + return trySource(baseSource, existingMedias, baseMedia, baseSourceIndex); + }), + Promise.reject(new Error()), + ); - return { - ...baseMedia, - ...source, - }; - } catch (error) { - logger.warn(error.message); + return { + ...baseMedia, + ...source, + }; + } catch (error) { + logger.warn(error.message); - return baseMedia; - } + return baseMedia; + } } function curateMediaEntry(media, index) { - if (media.entry) { - return media; - } + if (media.entry) { + return media; + } - const curatedMediaEntry = { - id: media.id, - path: media.file.path, - thumbnail: media.file.thumbnail, - lazy: media.file.lazy, - index, - mime: media.meta.mimetype, - hash: media.meta.hash, - size: media.meta.size, - width: media.meta.width, - height: media.meta.height, - entropy: media.meta.entropy, - source: media.src, - source_page: media.url, - scraper: media.scraper, - copyright: media.copyright, - comment: media.comment, - }; + const curatedMediaEntry = { + id: media.id, + path: media.file.path, + thumbnail: media.file.thumbnail, + lazy: media.file.lazy, + index, + mime: media.meta.mimetype, + hash: media.meta.hash, + size: media.meta.size, + width: media.meta.width, + height: media.meta.height, + entropy: media.meta.entropy, + source: media.src, + source_page: media.url, + scraper: media.scraper, + copyright: media.copyright, + comment: media.comment, + }; - return { - ...media, - newEntry: true, - entry: curatedMediaEntry, - }; + return { + ...media, + newEntry: true, + entry: curatedMediaEntry, + }; } async function storeMedias(baseMedias) { - await fsPromises.mkdir(path.join(config.media.path, 'temp'), { recursive: true }); + await fsPromises.mkdir(path.join(config.media.path, 'temp'), { recursive: true }); - const [existingSourceMediaByUrl, existingExtractMediaByUrl] = await findSourceDuplicates(baseMedias); + const [existingSourceMediaByUrl, existingExtractMediaByUrl] = await findSourceDuplicates(baseMedias); - const fetchedMedias = await Promise.map( - baseMedias, - async baseMedia => fetchMedia(baseMedia, { existingSourceMediaByUrl, existingExtractMediaByUrl }), - ); + const fetchedMedias = await Promise.map( + baseMedias, + async baseMedia => fetchMedia(baseMedia, { existingSourceMediaByUrl, existingExtractMediaByUrl }), + ); - const [uniqueHashMedias, existingHashMedias] = await findHashDuplicates(fetchedMedias); + const [uniqueHashMedias, existingHashMedias] = await findHashDuplicates(fetchedMedias); - const savedMedias = await Promise.map( - uniqueHashMedias, - async baseMedia => storeFile(baseMedia), - ); + const savedMedias = await Promise.map( + uniqueHashMedias, + async baseMedia => storeFile(baseMedia), + ); - const newMediaWithEntries = savedMedias.map((media, index) => curateMediaEntry(media, index)); - const newMediaEntries = newMediaWithEntries.filter(media => media.newEntry).map(media => media.entry); + const newMediaWithEntries = savedMedias.map((media, index) => curateMediaEntry(media, index)); + const newMediaEntries = newMediaWithEntries.filter(media => media.newEntry).map(media => media.entry); - await knex('media').insert(newMediaEntries); + await knex('media').insert(newMediaEntries); - return [...newMediaWithEntries, ...existingHashMedias]; + return [...newMediaWithEntries, ...existingHashMedias]; } async function associateReleaseMedia(releases) { - if (!argv.media) { - return; - } + if (!argv.media) { + return; + } - const baseMediasByReleaseId = releases.reduce((acc, release) => ({ - ...acc, - [release.id]: [ - ...(argv.images && argv.poster ? toBaseMedias([release.poster], 'posters') : []), - ...(argv.images && argv.poster ? toBaseMedias(release.covers, 'covers') : []), - ...(argv.images && argv.photos ? toBaseMedias(release.photos, 'photos') : []), - ...(argv.videos && argv.trailer ? toBaseMedias([release.trailer], 'trailers') : []), - ...(argv.videos && argv.teaser ? toBaseMedias([release.teaser], 'teasers') : []), - ], - }), {}); + const baseMediasByReleaseId = releases.reduce((acc, release) => ({ + ...acc, + [release.id]: [ + ...(argv.images && argv.poster ? toBaseMedias([release.poster], 'posters') : []), + ...(argv.images && argv.poster ? toBaseMedias(release.covers, 'covers') : []), + ...(argv.images && argv.photos ? toBaseMedias(release.photos, 'photos') : []), + ...(argv.videos && argv.trailer ? toBaseMedias([release.trailer], 'trailers') : []), + ...(argv.videos && argv.teaser ? toBaseMedias([release.teaser], 'teasers') : []), + ], + }), {}); - const baseMediasByRole = Object.values(baseMediasByReleaseId) - .flat() - .filter(Boolean) - .reduce((acc, baseMedia) => { - if (!acc[baseMedia.role]) acc[baseMedia.role] = []; - acc[baseMedia.role].push(baseMedia); + const baseMediasByRole = Object.values(baseMediasByReleaseId) + .flat() + .filter(Boolean) + .reduce((acc, baseMedia) => { + if (!acc[baseMedia.role]) acc[baseMedia.role] = []; + acc[baseMedia.role].push(baseMedia); - return acc; - }, {}); + return acc; + }, {}); - await Promise.reduce(['posters', 'covers', 'photos', 'teasers', 'trailers'], async (chain, role) => { - // stage by role so posters are prioritized over photos and videos - await chain; + await Promise.reduce(['posters', 'covers', 'photos', 'teasers', 'trailers'], async (chain, role) => { + // stage by role so posters are prioritized over photos and videos + await chain; - const baseMedias = baseMediasByRole[role]; + const baseMedias = baseMediasByRole[role]; - if (!baseMedias) { - return; - } + if (!baseMedias) { + return; + } - const storedMedias = await storeMedias(baseMedias); - const storedMediasById = itemsByKey(storedMedias, 'id'); + const storedMedias = await storeMedias(baseMedias); + const storedMediasById = itemsByKey(storedMedias, 'id'); - const associations = Object - .entries(baseMediasByReleaseId) - .reduce((acc, [releaseId, releaseBaseMedias]) => { - releaseBaseMedias.forEach((baseMedia) => { - const media = storedMediasById[baseMedia.id]; + const associations = Object + .entries(baseMediasByReleaseId) + .reduce((acc, [releaseId, releaseBaseMedias]) => { + releaseBaseMedias.forEach((baseMedia) => { + const media = storedMediasById[baseMedia.id]; - if (media) { - acc.push({ - release_id: releaseId, - media_id: media.use || media.entry.id, - }); - } - }); + if (media) { + acc.push({ + release_id: releaseId, + media_id: media.use || media.entry.id, + }); + } + }); - return acc; - }, []) - .filter(Boolean); + return acc; + }, []) + .filter(Boolean); - if (associations.length > 0) { - await knex.raw(`${knex(`releases_${role}`).insert(associations)} ON CONFLICT DO NOTHING`); - } - }, Promise.resolve()); + if (associations.length > 0) { + await knex.raw(`${knex(`releases_${role}`).insert(associations)} ON CONFLICT DO NOTHING`); + } + }, Promise.resolve()); } module.exports = { - associateReleaseMedia, + associateReleaseMedia, }; diff --git a/src/networks.js b/src/networks.js index 983bfac2..549bd1a3 100644 --- a/src/networks.js +++ b/src/networks.js @@ -5,77 +5,77 @@ const whereOr = require('./utils/where-or'); const { fetchSites } = require('./sites'); async function curateNetwork(network, includeParameters = false, includeSites = true, includeStudios = false) { - const curatedNetwork = { - id: network.id, - name: network.name, - url: network.url, - description: network.description, - slug: network.slug, - parameters: includeParameters ? network.parameters : null, - }; + const curatedNetwork = { + id: network.id, + name: network.name, + url: network.url, + description: network.description, + slug: network.slug, + parameters: includeParameters ? network.parameters : null, + }; - if (includeSites) { - curatedNetwork.sites = await fetchSites({ network_id: network.id }); - } + if (includeSites) { + curatedNetwork.sites = await fetchSites({ network_id: network.id }); + } - if (includeStudios) { - const studios = await knex('studios').where({ network_id: network.id }); + if (includeStudios) { + const studios = await knex('studios').where({ network_id: network.id }); - curatedNetwork.studios = studios.map(studio => ({ - id: studio.id, - name: studio.name, - url: studio.url, - description: studio.description, - slug: studio.slug, - })); - } + curatedNetwork.studios = studios.map(studio => ({ + id: studio.id, + name: studio.name, + url: studio.url, + description: studio.description, + slug: studio.slug, + })); + } - return curatedNetwork; + return curatedNetwork; } function curateNetworks(releases) { - return Promise.all(releases.map(async release => curateNetwork(release))); + return Promise.all(releases.map(async release => curateNetwork(release))); } async function findNetworkByUrl(url) { - const { hostname } = new URL(url); - const domain = hostname.replace(/^www./, ''); + const { hostname } = new URL(url); + const domain = hostname.replace(/^www./, ''); - const network = await knex('networks') - .where('networks.url', 'like', `%${domain}`) - .orWhere('networks.url', url) - .first(); + const network = await knex('networks') + .where('networks.url', 'like', `%${domain}`) + .orWhere('networks.url', url) + .first(); - if (network) { - return curateNetwork(network, true); - } + if (network) { + return curateNetwork(network, true); + } - return null; + return null; } async function fetchNetworks(queryObject) { - const releases = await knex('networks') - .where(builder => whereOr(queryObject, 'networks', builder)) - .limit(100); + const releases = await knex('networks') + .where(builder => whereOr(queryObject, 'networks', builder)) + .limit(100); - return curateNetworks(releases); + return curateNetworks(releases); } async function fetchNetworksFromReleases() { - const releases = await knex('releases') - .select('site_id', '') - .leftJoin('sites', 'sites.id', 'releases.site_id') - .leftJoin('networks', 'networks.id', 'sites.network_id') - .groupBy('networks.id') - .limit(100); + const releases = await knex('releases') + .select('site_id', '') + .leftJoin('sites', 'sites.id', 'releases.site_id') + .leftJoin('networks', 'networks.id', 'sites.network_id') + .groupBy('networks.id') + .limit(100); - return curateNetworks(releases); + return curateNetworks(releases); } module.exports = { - curateNetwork, - curateNetworks, - fetchNetworks, - fetchNetworksFromReleases, - findNetworkByUrl, + curateNetwork, + curateNetworks, + fetchNetworks, + fetchNetworksFromReleases, + findNetworkByUrl, }; diff --git a/src/releases-legacy.js b/src/releases-legacy.js index 0b13c77c..f587f123 100644 --- a/src/releases-legacy.js +++ b/src/releases-legacy.js @@ -11,356 +11,356 @@ const whereOr = require('./utils/where-or'); const { associateTags } = require('./tags'); const { associateActors, scrapeBasicActors } = require('./actors'); const { - pluckItems, - storeMedia, - associateMedia, + pluckItems, + storeMedia, + associateMedia, } = require('./media'); const { fetchSites } = require('./sites'); const slugify = require('./utils/slugify'); const capitalize = require('./utils/capitalize'); function commonQuery(queryBuilder, { - filter = [], - after = new Date(0), // January 1970 - before = new Date(2 ** 44), // May 2109 - limit = 100, + filter = [], + after = new Date(0), // January 1970 + before = new Date(2 ** 44), // May 2109 + limit = 100, }) { - const finalFilter = [].concat(filter); // ensure filter is array + const finalFilter = [].concat(filter); // ensure filter is array - queryBuilder - .leftJoin('sites', 'releases.site_id', 'sites.id') - .leftJoin('studios', 'releases.studio_id', 'studios.id') - .leftJoin('networks', 'sites.network_id', 'networks.id') - .select( - 'releases.*', - 'sites.name as site_name', 'sites.slug as site_slug', 'sites.url as site_url', 'sites.network_id', 'sites.parameters as site_parameters', - 'studios.name as studio_name', 'sites.slug as site_slug', 'studios.url as studio_url', - 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', - ) - .whereNotExists((builder) => { - // apply tag filters - builder - .select('*') - .from('tags_associated') - .leftJoin('tags', 'tags_associated.tag_id', 'tags.id') - .whereIn('tags.slug', finalFilter) - .where('tags_associated.domain', 'releases') - .whereRaw('tags_associated.target_id = releases.id'); - }) - .andWhere('releases.date', '>', after) - .andWhere('releases.date', '<=', before) - .orderBy([{ column: 'date', order: 'desc' }, { column: 'created_at', order: 'desc' }]) - .limit(limit); + queryBuilder + .leftJoin('sites', 'releases.site_id', 'sites.id') + .leftJoin('studios', 'releases.studio_id', 'studios.id') + .leftJoin('networks', 'sites.network_id', 'networks.id') + .select( + 'releases.*', + 'sites.name as site_name', 'sites.slug as site_slug', 'sites.url as site_url', 'sites.network_id', 'sites.parameters as site_parameters', + 'studios.name as studio_name', 'sites.slug as site_slug', 'studios.url as studio_url', + 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', + ) + .whereNotExists((builder) => { + // apply tag filters + builder + .select('*') + .from('tags_associated') + .leftJoin('tags', 'tags_associated.tag_id', 'tags.id') + .whereIn('tags.slug', finalFilter) + .where('tags_associated.domain', 'releases') + .whereRaw('tags_associated.target_id = releases.id'); + }) + .andWhere('releases.date', '>', after) + .andWhere('releases.date', '<=', before) + .orderBy([{ column: 'date', order: 'desc' }, { column: 'created_at', order: 'desc' }]) + .limit(limit); } async function curateRelease(release) { - const [actors, tags, media] = await Promise.all([ - knex('actors_associated') - .select( - 'actors.id', 'actors.name', 'actors.gender', 'actors.slug', 'actors.birthdate', - 'birth_countries.alpha2 as birth_country_alpha2', 'birth_countries.name as birth_country_name', 'birth_countries.alias as birth_country_alias', - 'media.thumbnail as avatar', - ) - .where({ release_id: release.id }) - .leftJoin('actors', 'actors.id', 'actors_associated.actor_id') - .leftJoin('countries as birth_countries', 'actors.birth_country_alpha2', 'birth_countries.alpha2') - .leftJoin('media', (builder) => { - builder - .on('media.target_id', 'actors.id') - .andOnVal('media.domain', 'actors') - .andOnVal('media.index', '0'); - }) - .orderBy('actors.gender'), - knex('tags_associated') - .select('tags.name', 'tags.slug') - .where({ - domain: 'releases', - target_id: release.id, - }) - .leftJoin('tags', 'tags.id', 'tags_associated.tag_id') - .orderBy('tags.priority', 'desc'), - knex('media') - .where({ - target_id: release.id, - domain: 'releases', - }) - .orderBy(['role', 'index']), - ]); + const [actors, tags, media] = await Promise.all([ + knex('actors_associated') + .select( + 'actors.id', 'actors.name', 'actors.gender', 'actors.slug', 'actors.birthdate', + 'birth_countries.alpha2 as birth_country_alpha2', 'birth_countries.name as birth_country_name', 'birth_countries.alias as birth_country_alias', + 'media.thumbnail as avatar', + ) + .where({ release_id: release.id }) + .leftJoin('actors', 'actors.id', 'actors_associated.actor_id') + .leftJoin('countries as birth_countries', 'actors.birth_country_alpha2', 'birth_countries.alpha2') + .leftJoin('media', (builder) => { + builder + .on('media.target_id', 'actors.id') + .andOnVal('media.domain', 'actors') + .andOnVal('media.index', '0'); + }) + .orderBy('actors.gender'), + knex('tags_associated') + .select('tags.name', 'tags.slug') + .where({ + domain: 'releases', + target_id: release.id, + }) + .leftJoin('tags', 'tags.id', 'tags_associated.tag_id') + .orderBy('tags.priority', 'desc'), + knex('media') + .where({ + target_id: release.id, + domain: 'releases', + }) + .orderBy(['role', 'index']), + ]); - const curatedRelease = { - id: release.id, - type: release.type, - title: release.title, - date: release.date, - dateAdded: release.created_at, - description: release.description, - url: release.url, - shootId: release.shoot_id, - entryId: release.entry_id, - actors: actors.map(actor => ({ - id: actor.id, - slug: actor.slug, - name: actor.name, - gender: actor.gender, - birthdate: actor.birthdate, - age: moment().diff(actor.birthdate, 'years'), - ageThen: moment(release.date).diff(actor.birthdate, 'years'), - avatar: actor.avatar, - origin: actor.birth_country_alpha2 - ? { - country: { - name: actor.birth_country_alias, - alpha2: actor.birth_country_alpha2, - }, - } - : null, - })), - director: release.director, - tags, - duration: release.duration, - photos: media.filter(item => item.role === 'photo'), - poster: media.filter(item => item.role === 'poster')[0], - covers: media.filter(item => item.role === 'cover'), - trailer: media.filter(item => item.role === 'trailer')[0], - site: { - id: release.site_id, - name: release.site_name, - independent: !!release.site_parameters?.independent, - slug: release.site_slug, - url: release.site_url, - }, - studio: release.studio_id - ? { - id: release.studio_id, - name: release.studio_name, - slug: release.studio_slug, - url: release.studio_url, - } - : null, - network: { - id: release.network_id, - name: release.network_name, - description: release.network_description, - slug: release.network_slug, - url: release.network_url, - }, - }; + const curatedRelease = { + id: release.id, + type: release.type, + title: release.title, + date: release.date, + dateAdded: release.created_at, + description: release.description, + url: release.url, + shootId: release.shoot_id, + entryId: release.entry_id, + actors: actors.map(actor => ({ + id: actor.id, + slug: actor.slug, + name: actor.name, + gender: actor.gender, + birthdate: actor.birthdate, + age: moment().diff(actor.birthdate, 'years'), + ageThen: moment(release.date).diff(actor.birthdate, 'years'), + avatar: actor.avatar, + origin: actor.birth_country_alpha2 + ? { + country: { + name: actor.birth_country_alias, + alpha2: actor.birth_country_alpha2, + }, + } + : null, + })), + director: release.director, + tags, + duration: release.duration, + photos: media.filter(item => item.role === 'photo'), + poster: media.filter(item => item.role === 'poster')[0], + covers: media.filter(item => item.role === 'cover'), + trailer: media.filter(item => item.role === 'trailer')[0], + site: { + id: release.site_id, + name: release.site_name, + independent: !!release.site_parameters?.independent, + slug: release.site_slug, + url: release.site_url, + }, + studio: release.studio_id + ? { + id: release.studio_id, + name: release.studio_name, + slug: release.studio_slug, + url: release.studio_url, + } + : null, + network: { + id: release.network_id, + name: release.network_name, + description: release.network_description, + slug: release.network_slug, + url: release.network_url, + }, + }; - return curatedRelease; + return curatedRelease; } function curateReleases(releases) { - return Promise.all(releases.map(async release => curateRelease(release))); + return Promise.all(releases.map(async release => curateRelease(release))); } async function attachChannelSite(release) { - if (!release.site?.isFallback && !release.channel?.force) { - return release; - } + if (!release.site?.isFallback && !release.channel?.force) { + return release; + } - if (!release.channel) { - throw new Error(`Unable to derive channel site from generic URL: ${release.url}`); - } + if (!release.channel) { + throw new Error(`Unable to derive channel site from generic URL: ${release.url}`); + } - const [site] = await fetchSites({ - name: release.channel.name || release.channel, - slug: release.channel.slug || release.channel, - }); + const [site] = await fetchSites({ + name: release.channel.name || release.channel, + slug: release.channel.slug || release.channel, + }); - if (site) { - return { - ...release, - site, - }; - } + if (site) { + return { + ...release, + site, + }; + } - throw new Error(`Unable to match channel '${release.channel.slug || release.channel}' from generic URL: ${release.url}`); + throw new Error(`Unable to match channel '${release.channel.slug || release.channel}' from generic URL: ${release.url}`); } async function attachStudio(release) { - if (!release.studio) { - return release; - } + if (!release.studio) { + return release; + } - const studio = await knex('studios') - .where('name', release.studio) - .orWhere('slug', release.studio) - .orWhere('url', release.studio) - .first(); + const studio = await knex('studios') + .where('name', release.studio) + .orWhere('slug', release.studio) + .orWhere('url', release.studio) + .first(); - return { - ...release, - studio, - }; + return { + ...release, + studio, + }; } async function curateReleaseEntry(release, batchId, existingRelease) { - const slug = slugify(release.title, { - encode: true, - limit: config.titleSlugLength, - }); + const slug = slugify(release.title, { + encode: true, + limit: config.titleSlugLength, + }); - const curatedRelease = { - site_id: release.site.id, - studio_id: release.studio ? release.studio.id : null, - shoot_id: release.shootId || null, - entry_id: release.entryId || null, - type: release.type, - url: release.url, - title: release.title, - slug, - date: release.date, - description: release.description, - // director: release.director, - duration: release.duration, - // likes: release.rating && release.rating.likes, - // dislikes: release.rating && release.rating.dislikes, - // rating: release.rating && release.rating.stars && Math.floor(release.rating.stars), - deep: typeof release.deep === 'boolean' ? release.deep : false, - deep_url: release.deepUrl, - updated_batch_id: batchId, - ...(!existingRelease && { created_batch_id: batchId }), - }; + const curatedRelease = { + site_id: release.site.id, + studio_id: release.studio ? release.studio.id : null, + shoot_id: release.shootId || null, + entry_id: release.entryId || null, + type: release.type, + url: release.url, + title: release.title, + slug, + date: release.date, + description: release.description, + // director: release.director, + duration: release.duration, + // likes: release.rating && release.rating.likes, + // dislikes: release.rating && release.rating.dislikes, + // rating: release.rating && release.rating.stars && Math.floor(release.rating.stars), + deep: typeof release.deep === 'boolean' ? release.deep : false, + deep_url: release.deepUrl, + updated_batch_id: batchId, + ...(!existingRelease && { created_batch_id: batchId }), + }; - return curatedRelease; + return curatedRelease; } async function fetchReleases(queryObject = {}, options = {}) { - const releases = await knex('releases') - .modify(commonQuery, options) - .andWhere(builder => whereOr(queryObject, 'releases', builder)); + const releases = await knex('releases') + .modify(commonQuery, options) + .andWhere(builder => whereOr(queryObject, 'releases', builder)); - return curateReleases(releases); + return curateReleases(releases); } async function fetchSiteReleases(queryObject, options = {}) { - const releases = await knex('releases') - .modify(commonQuery, options) - .where(builder => whereOr(queryObject, 'sites', builder)); + const releases = await knex('releases') + .modify(commonQuery, options) + .where(builder => whereOr(queryObject, 'sites', builder)); - return curateReleases(releases); + return curateReleases(releases); } async function fetchNetworkReleases(queryObject, options = {}) { - const releases = await knex('releases') - .modify(commonQuery, options) - .where(builder => whereOr(queryObject, 'networks', builder)); + const releases = await knex('releases') + .modify(commonQuery, options) + .where(builder => whereOr(queryObject, 'networks', builder)); - return curateReleases(releases); + return curateReleases(releases); } async function fetchActorReleases(queryObject, options = {}) { - const releases = await knex('actors_associated') - .leftJoin('releases', 'actors_associated.release_id', 'releases.id') - .leftJoin('actors', 'actors_associated.actor_id', 'actors.id') - .select( - 'actors.name as actor_name', - ) - .modify(commonQuery, options) - .where(builder => whereOr(queryObject, 'actors', builder)); + const releases = await knex('actors_associated') + .leftJoin('releases', 'actors_associated.release_id', 'releases.id') + .leftJoin('actors', 'actors_associated.actor_id', 'actors.id') + .select( + 'actors.name as actor_name', + ) + .modify(commonQuery, options) + .where(builder => whereOr(queryObject, 'actors', builder)); - return curateReleases(releases); + return curateReleases(releases); } async function fetchTagReleases(queryObject, options = {}) { - const releases = await knex('tags_associated') - .leftJoin('releases', 'tags_associated.target_id', 'releases.id') - .leftJoin('tags', 'tags_associated.tag_id', 'tags.id') - .select( - 'tags.name as tag_name', - ) - .modify(commonQuery, options) - .where('tags_associated.domain', 'releases') - .where(builder => whereOr(queryObject, 'tags', builder)); + const releases = await knex('tags_associated') + .leftJoin('releases', 'tags_associated.target_id', 'releases.id') + .leftJoin('tags', 'tags_associated.tag_id', 'tags.id') + .select( + 'tags.name as tag_name', + ) + .modify(commonQuery, options) + .where('tags_associated.domain', 'releases') + .where(builder => whereOr(queryObject, 'tags', builder)); - return curateReleases(releases); + return curateReleases(releases); } function accumulateActors(releases) { - return releases.reduce((acc, release) => { - if (!Array.isArray(release.actors)) return acc; + return releases.reduce((acc, release) => { + if (!Array.isArray(release.actors)) return acc; - release.actors.forEach((actor) => { - const actorName = actor.name ? actor.name.trim() : actor.trim(); - const actorSlug = slugify(actorName); + release.actors.forEach((actor) => { + const actorName = actor.name ? actor.name.trim() : actor.trim(); + const actorSlug = slugify(actorName); - if (!actorSlug) return; + if (!actorSlug) return; - if (!acc[actorSlug]) { - acc[actorSlug] = { - name: actorName, - slug: actorSlug, - releaseIds: new Set(), - avatars: [], - }; - } + if (!acc[actorSlug]) { + acc[actorSlug] = { + name: actorName, + slug: actorSlug, + releaseIds: new Set(), + avatars: [], + }; + } - acc[actorSlug].releaseIds.add(release.id); + acc[actorSlug].releaseIds.add(release.id); - if (actor.name) acc[actorSlug] = { ...acc[actorSlug], ...actor }; // actor input contains profile info - if (actor.avatar) { - const avatar = Array.isArray(actor.avatar) - ? actor.avatar.map(avatarX => ({ - src: avatarX.src || avatarX, - copyright: avatarX.copyright === undefined ? capitalize(release.site?.network?.name) : avatarX.copyright, - })) - : { - src: actor.avatar.src || actor.avatar, - copyright: actor.avatar.copyright === undefined ? capitalize(release.site?.network?.name) : actor.avatar.copyright, - }; + if (actor.name) acc[actorSlug] = { ...acc[actorSlug], ...actor }; // actor input contains profile info + if (actor.avatar) { + const avatar = Array.isArray(actor.avatar) + ? actor.avatar.map(avatarX => ({ + src: avatarX.src || avatarX, + copyright: avatarX.copyright === undefined ? capitalize(release.site?.network?.name) : avatarX.copyright, + })) + : { + src: actor.avatar.src || actor.avatar, + copyright: actor.avatar.copyright === undefined ? capitalize(release.site?.network?.name) : actor.avatar.copyright, + }; - acc[actorSlug].avatars = acc[actorSlug].avatars.concat([avatar]); // don't flatten fallbacks - } - }); + acc[actorSlug].avatars = acc[actorSlug].avatars.concat([avatar]); // don't flatten fallbacks + } + }); - return acc; - }, {}); + return acc; + }, {}); } async function storeReleaseAssets(releases) { - if (!argv.media) { - return; - } + if (!argv.media) { + return; + } - const releasePostersById = releases.reduce((acc, release) => ({ ...acc, [release.id]: [release.poster] }), {}); - const releaseCoversById = releases.reduce((acc, release) => ({ ...acc, [release.id]: release.covers }), {}); - const releaseTrailersById = releases.reduce((acc, release) => ({ ...acc, [release.id]: [release.trailer] }), {}); - const releaseTeasersById = releases.reduce((acc, release) => ({ ...acc, [release.id]: [release.teaser] }), {}); - const releasePhotosById = releases.reduce((acc, release) => ({ - ...acc, - [release.id]: pluckItems(release.photos), - }), {}); + const releasePostersById = releases.reduce((acc, release) => ({ ...acc, [release.id]: [release.poster] }), {}); + const releaseCoversById = releases.reduce((acc, release) => ({ ...acc, [release.id]: release.covers }), {}); + const releaseTrailersById = releases.reduce((acc, release) => ({ ...acc, [release.id]: [release.trailer] }), {}); + const releaseTeasersById = releases.reduce((acc, release) => ({ ...acc, [release.id]: [release.teaser] }), {}); + const releasePhotosById = releases.reduce((acc, release) => ({ + ...acc, + [release.id]: pluckItems(release.photos), + }), {}); - if (argv.images && argv.posters) { - const posters = await storeMedia(Object.values(releasePostersById).flat(), 'release', 'poster'); - if (posters) await associateMedia(releasePostersById, posters, 'release', 'poster'); - } + if (argv.images && argv.posters) { + const posters = await storeMedia(Object.values(releasePostersById).flat(), 'release', 'poster'); + if (posters) await associateMedia(releasePostersById, posters, 'release', 'poster'); + } - if (argv.images && argv.covers) { - const covers = await storeMedia(Object.values(releaseCoversById).flat(), 'release', 'cover'); - if (covers) await associateMedia(releaseCoversById, covers, 'release', 'cover'); - } + if (argv.images && argv.covers) { + const covers = await storeMedia(Object.values(releaseCoversById).flat(), 'release', 'cover'); + if (covers) await associateMedia(releaseCoversById, covers, 'release', 'cover'); + } - if (argv.images && argv.photos) { - const photos = await storeMedia(Object.values(releasePhotosById).flat(), 'release', 'photo'); - if (photos) await associateMedia(releasePhotosById, photos, 'release', 'photo'); - } + if (argv.images && argv.photos) { + const photos = await storeMedia(Object.values(releasePhotosById).flat(), 'release', 'photo'); + if (photos) await associateMedia(releasePhotosById, photos, 'release', 'photo'); + } - if (argv.videos && argv.trailers) { - const trailers = await storeMedia(Object.values(releaseTrailersById).flat(), 'release', 'trailer'); - if (trailers) await associateMedia(releaseTrailersById, trailers, 'release', 'trailer'); - } + if (argv.videos && argv.trailers) { + const trailers = await storeMedia(Object.values(releaseTrailersById).flat(), 'release', 'trailer'); + if (trailers) await associateMedia(releaseTrailersById, trailers, 'release', 'trailer'); + } - if (argv.videos && argv.teasers) { - const teasers = await storeMedia(Object.values(releaseTeasersById).flat(), 'release', 'teaser'); - if (teasers) await associateMedia(releaseTeasersById, teasers, 'release', 'teaser'); - } + if (argv.videos && argv.teasers) { + const teasers = await storeMedia(Object.values(releaseTeasersById).flat(), 'release', 'teaser'); + if (teasers) await associateMedia(releaseTeasersById, teasers, 'release', 'teaser'); + } } async function updateReleasesSearch(releaseIds) { - logger.info(`Updating search documents for ${releaseIds ? releaseIds.length : 'all' } releases`); + logger.info(`Updating search documents for ${releaseIds ? releaseIds.length : 'all' } releases`); - const documents = await knex.raw(` + const documents = await knex.raw(` SELECT releases.id AS release_id, TO_TSVECTOR( @@ -391,117 +391,117 @@ async function updateReleasesSearch(releaseIds) { GROUP BY releases.id, sites.name, sites.slug, sites.alias, sites.url, networks.name, networks.slug, networks.url; `, releaseIds && [releaseIds]); - if (documents.rows?.length > 0) { - const query = knex('releases_search').insert(documents.rows).toString(); - await knex.raw(`${query} ON CONFLICT (release_id) DO UPDATE SET document = EXCLUDED.document`); - } + if (documents.rows?.length > 0) { + const query = knex('releases_search').insert(documents.rows).toString(); + await knex.raw(`${query} ON CONFLICT (release_id) DO UPDATE SET document = EXCLUDED.document`); + } } async function storeRelease(release, batchId) { - if (!release.site) { - throw new Error(`Missing site, unable to store "${release.title}" (${release.url})`); - } + if (!release.site) { + throw new Error(`Missing site, unable to store "${release.title}" (${release.url})`); + } - if (!release.entryId) { - logger.warn(`Missing entry ID, unable to store "${release.title}" (${release.url})`); - return null; - } + if (!release.entryId) { + logger.warn(`Missing entry ID, unable to store "${release.title}" (${release.url})`); + return null; + } - const existingRelease = await knex('releases') - .where({ - entry_id: release.entryId, - site_id: release.site.id, - }) - .first(); + const existingRelease = await knex('releases') + .where({ + entry_id: release.entryId, + site_id: release.site.id, + }) + .first(); - const curatedRelease = await curateReleaseEntry(release, batchId, existingRelease); + const curatedRelease = await curateReleaseEntry(release, batchId, existingRelease); - if (existingRelease && !argv.redownload) { - return existingRelease; - } + if (existingRelease && !argv.redownload) { + return existingRelease; + } - if (existingRelease && argv.redownload) { - const [updatedRelease] = await knex('releases') - .where('id', existingRelease.id) - .update({ - ...existingRelease, - ...curatedRelease, - }) - .returning('*'); + if (existingRelease && argv.redownload) { + const [updatedRelease] = await knex('releases') + .where('id', existingRelease.id) + .update({ + ...existingRelease, + ...curatedRelease, + }) + .returning('*'); - if (updatedRelease) { - await associateTags(release, updatedRelease.id); - logger.info(`Updated release "${release.title}" (${existingRelease.id}, ${release.site.name})`); - } + if (updatedRelease) { + await associateTags(release, updatedRelease.id); + logger.info(`Updated release "${release.title}" (${existingRelease.id}, ${release.site.name})`); + } - await associateTags(release, existingRelease.id); + await associateTags(release, existingRelease.id); - return existingRelease; - } + return existingRelease; + } - const [releaseEntry] = await knex('releases') - .insert(curatedRelease) - .returning('*'); + const [releaseEntry] = await knex('releases') + .insert(curatedRelease) + .returning('*'); - await associateTags(release, releaseEntry.id); + await associateTags(release, releaseEntry.id); - logger.info(`Stored release "${release.title}" (${releaseEntry.id}, ${release.site.name})`); + logger.info(`Stored release "${release.title}" (${releaseEntry.id}, ${release.site.name})`); - return releaseEntry; + return releaseEntry; } async function storeReleases(releases) { - const [batchId] = await knex('batches').insert({ comment: null }).returning('id'); + const [batchId] = await knex('batches').insert({ comment: null }).returning('id'); - const storedReleases = await Promise.map(releases, async (release) => { - try { - const releaseWithChannelSite = await attachChannelSite(release); - const releaseWithStudio = await attachStudio(releaseWithChannelSite); - const storedRelease = await storeRelease(releaseWithStudio, batchId); + const storedReleases = await Promise.map(releases, async (release) => { + try { + const releaseWithChannelSite = await attachChannelSite(release); + const releaseWithStudio = await attachStudio(releaseWithChannelSite); + const storedRelease = await storeRelease(releaseWithStudio, batchId); - return storedRelease && { - id: storedRelease.id, - slug: storedRelease.slug, - ...releaseWithChannelSite, - }; - } catch (error) { - logger.error(error); + return storedRelease && { + id: storedRelease.id, + slug: storedRelease.slug, + ...releaseWithChannelSite, + }; + } catch (error) { + logger.error(error); - return null; - } - }, { - concurrency: 10, - }).filter(Boolean); + return null; + } + }, { + concurrency: 10, + }).filter(Boolean); - logger.info(`Stored ${storedReleases.length} new releases`); + logger.info(`Stored ${storedReleases.length} new releases`); - const actors = accumulateActors(storedReleases); + const actors = accumulateActors(storedReleases); - await associateActors(actors, storedReleases); + await associateActors(actors, storedReleases); - await Promise.all([ - // actors need to be stored before generating search - updateReleasesSearch(storedReleases.map(release => release.id)), - storeReleaseAssets(storedReleases), - ]); + await Promise.all([ + // actors need to be stored before generating search + updateReleasesSearch(storedReleases.map(release => release.id)), + storeReleaseAssets(storedReleases), + ]); - if (argv.withProfiles && Object.keys(actors).length > 0) { - await scrapeBasicActors(); - } + if (argv.withProfiles && Object.keys(actors).length > 0) { + await scrapeBasicActors(); + } - return { - releases: storedReleases, - actors, - }; + return { + releases: storedReleases, + actors, + }; } module.exports = { - fetchReleases, - fetchActorReleases, - fetchSiteReleases, - fetchNetworkReleases, - fetchTagReleases, - storeRelease, - storeReleases, - updateReleasesSearch, + fetchReleases, + fetchActorReleases, + fetchSiteReleases, + fetchNetworkReleases, + fetchTagReleases, + storeRelease, + storeReleases, + updateReleasesSearch, }; diff --git a/src/releases.js b/src/releases.js index ecf59981..d5fe5089 100644 --- a/src/releases.js +++ b/src/releases.js @@ -3,18 +3,18 @@ const knex = require('./knex'); async function fetchReleases(limit = 100) { - const releases = await knex('releases').limit(limit); + const releases = await knex('releases').limit(limit); - return releases; + return releases; } async function searchReleases(query, limit = 100) { - const releases = await knex.raw('SELECT * FROM search_releases(?) LIMIT ?;', [query, limit]); + const releases = await knex.raw('SELECT * FROM search_releases(?) LIMIT ?;', [query, limit]); - return releases.rows; + return releases.rows; } module.exports = { - fetchReleases, - searchReleases, + fetchReleases, + searchReleases, }; diff --git a/src/scrape-releases.js b/src/scrape-releases.js deleted file mode 100644 index 82de59bb..00000000 --- a/src/scrape-releases.js +++ /dev/null @@ -1,199 +0,0 @@ -'use strict'; - -const config = require('config'); -const Promise = require('bluebird'); - -const logger = require('./logger')(__filename); -const argv = require('./argv'); -const include = require('./utils/argv-include')(argv); -const knex = require('./knex'); -const scrapers = require('./scrapers/scrapers'); -const { findSiteByUrl } = require('./sites'); -const { findNetworkByUrl } = require('./networks'); -const { storeReleases } = require('./releases'); - -async function findSite(url, release) { - if (release?.site) return release.site; - if (!url) return null; - - const site = await findSiteByUrl(url); - - if (site) { - return site; - } - - const network = await findNetworkByUrl(url); - - if (network) { - return { - ...network, - network, - isFallback: true, - }; - } - - return null; -} - -async function scrapeRelease(source, basicRelease = null, type = 'scene', beforeFetchLatest) { - // profile scraper may return either URLs or pre-scraped scenes - const sourceIsUrlOrEmpty = typeof source === 'string' || source === undefined; - const url = sourceIsUrlOrEmpty ? source : source?.url; - const release = sourceIsUrlOrEmpty ? basicRelease : source; - - const site = basicRelease?.site || await findSite(url, release); - - if (!site) { - throw new Error(`Could not find site for ${url} in database`); - } - - if (!argv.deep && release) { - return { - ...release, - site, - }; - } - - const scraper = scrapers.releases[site.slug] || scrapers.releases[site.network.slug]; - - if (!scraper) { - throw new Error(`Could not find scraper for ${url}`); - } - - if ((type === 'scene' && !scraper.fetchScene) || (type === 'movie' && !scraper.fetchMovie)) { - if (release) { - logger.warn(`The '${site.name}'-scraper cannot fetch individual ${type}s`); - return null; - } - - throw new Error(`The '${site.name}'-scraper cannot fetch individual ${type}s`); - } - - if (!release) { - logger.info(`Scraping release from ${url}`); - } - - const scrapedRelease = type === 'scene' - ? await scraper.fetchScene(url, site, release, beforeFetchLatest, include) - : await scraper.fetchMovie(url, site, release, beforeFetchLatest, include); - - return { - ...release, - ...scrapedRelease, - ...(scrapedRelease && release?.tags && { - tags: release.tags.concat(scrapedRelease.tags), - }), - site, - }; -} - -async function accumulateMovies(releases) { - if (!argv.withMovies) return []; - - const moviesByUrl = releases.reduce((acc, release) => { - if (!release.movie) return acc; - const movie = release.movie.url ? release.movie : { url: release.movie }; - - if (!acc[movie.url]) { - acc[movie.url] = { - ...movie, - type: 'movie', - sceneIds: [], - }; - } - - acc[movie.url].sceneIds = acc[movie.url].sceneIds.concat(release.id); - - return acc; - }, {}); - - const movies = await Promise.map(Object.values(moviesByUrl), async movie => scrapeRelease(movie, null, 'movie')); - const { releases: storedMovies } = await storeReleases(movies); - - const movieAssociations = storedMovies.reduce((acc, movie) => acc.concat(movie.sceneIds.map(sceneId => ({ - movie_id: movie.id, - scene_id: sceneId, - }))), []); - - await knex('releases_movies').insert(movieAssociations); - - // console.log(moviesByUrl); - return movies; -} - -async function scrapeReleases(sources, type = 'scene') { - const scrapedReleases = await Promise.map(sources, async source => scrapeRelease(source, null, type), { - concurrency: 5, - }).filter(Boolean); - - const curatedReleases = scrapedReleases.map(scrapedRelease => ({ ...scrapedRelease, type })); - - if ((argv.scene || argv.movie) && argv.inspect) { - // only show when fetching from URL - } - - if (argv.save) { - const { releases: storedReleases } = await storeReleases(curatedReleases); - - await accumulateMovies(storedReleases); - - if (storedReleases) { - logger.info(storedReleases.map(storedRelease => `\nhttp://${config.web.host}:${config.web.port}/scene/${storedRelease.id}/${storedRelease.slug}`).join('')); - } - - return storedReleases; - } - - return curatedReleases; -} - -async function scrapeScenes(sources) { - return scrapeReleases(sources, 'scene'); -} - -async function scrapeMovies(sources) { - return scrapeReleases(sources, 'movie'); -} - -async function deepFetchReleases(baseReleases, beforeFetchLatest) { - const deepReleases = await Promise.map(baseReleases, async (release) => { - if (release.url || (release.path && release.site)) { - try { - const fullRelease = await scrapeRelease(release.url, release, 'scene', beforeFetchLatest); - - if (fullRelease) { - return { - ...release, - ...fullRelease, - deep: true, - }; - } - - logger.warn(`Release scraper returned empty result for ${release.url}`); - - return release; - } catch (error) { - logger.error(`Failed to scrape ${release.url}: ${error}`); - - return { - ...release, - deep: false, - }; - } - } - - return release; - }, { - concurrency: 2, - }); - - return deepReleases; -} - -module.exports = { - deepFetchReleases, - scrapeMovies, - scrapeRelease, - scrapeReleases, - scrapeScenes, -}; diff --git a/src/scrape-sites.js b/src/scrape-sites.js deleted file mode 100644 index e980219e..00000000 --- a/src/scrape-sites.js +++ /dev/null @@ -1,184 +0,0 @@ -'use strict'; - -const Promise = require('bluebird'); -const moment = require('moment'); - -const argv = require('./argv'); -const include = require('./utils/argv-include')(argv); -const logger = require('./logger')(__filename); -const knex = require('./knex'); -const { fetchIncludedSites } = require('./sites'); -const scrapers = require('./scrapers/scrapers'); -const { deepFetchReleases } = require('./scrape-releases'); -const { storeReleases } = require('./releases'); - -function getAfterDate() { - if (/\d{2,4}-\d{2}-\d{2,4}/.test(argv.after)) { - // using date - return moment - .utc(argv.after, ['YYYY-MM-DD', 'DD-MM-YYYY']) - .toDate(); - } - - // using time distance (e.g. "1 month") - return moment - .utc() - .subtract(...argv.after.split(' ')) - .toDate(); -} - -async function findDuplicateReleaseIds(latestReleases, accReleases) { - const duplicateReleases = await knex('releases') - .whereIn('entry_id', latestReleases.map(({ entryId }) => entryId)); - - // include accumulated releases as duplicates to prevent an infinite - // loop when the next page contains the same releases as the previous - return new Set(duplicateReleases - .map(release => String(release.entry_id)) - .concat(accReleases.map(release => String(release.entryId)))); -} - -async function scrapeUniqueReleases(scraper, site, beforeFetchLatest, accSiteReleases, afterDate = getAfterDate(), accReleases = [], page = argv.page) { - if (!argv.latest || !scraper.fetchLatest) { - return []; - } - - const latestReleases = await scraper.fetchLatest(site, page, beforeFetchLatest, accSiteReleases, include); - - if (!Array.isArray(latestReleases)) { - logger.warn(`Scraper returned ${latestReleases || 'null'} when fetching latest from '${site.name}' on '${site.network.name}'`); - return accReleases; - } - - if (latestReleases.length === 0) { - return accReleases; - } - - const latestReleasesWithSite = latestReleases.map(release => ({ ...release, site })); - - const oldestReleaseOnPage = latestReleases.slice(-1)[0].date; - const duplicateReleaseIds = argv.redownload ? new Set() : await findDuplicateReleaseIds(latestReleases, accReleases); - - const uniqueReleases = latestReleasesWithSite - .filter(release => !duplicateReleaseIds.has(String(release.entryId)) // release is already in database - && (argv.last || !release.date || moment(release.date).isAfter(afterDate))); // release is older than specified date limit - - logger.verbose(`${site.name}: Scraped page ${page}, ${uniqueReleases.length} unique recent releases`); - - if ( - uniqueReleases.length > 0 - // && (oldestReleaseOnPage || page < argv.pages) - && ((oldestReleaseOnPage - ? moment(oldestReleaseOnPage).isAfter(afterDate) - : accReleases.length + uniqueReleases.length <= argv.nullDateLimit) - || (argv.last && accReleases.length + uniqueReleases.length < argv.last)) - ) { - // oldest release on page is newer that specified date range, or latest count has not yet been met, fetch next page - return scrapeUniqueReleases(scraper, site, beforeFetchLatest, accSiteReleases, afterDate, accReleases.concat(uniqueReleases), page + 1); - } - - if (argv.last && uniqueReleases.length >= argv.last) { - return accReleases.concat(uniqueReleases).slice(0, argv.last); - } - - if (oldestReleaseOnPage) { - return accReleases.concat(uniqueReleases); - } - - return accReleases.concat(uniqueReleases).slice(0, argv.nullDateLimit); -} - -async function scrapeUpcomingReleases(scraper, site, beforeFetchLatest) { - if (argv.upcoming && scraper.fetchUpcoming) { - const upcomingReleases = await scraper.fetchUpcoming(site, 1, beforeFetchLatest, include); - - return upcomingReleases - ? upcomingReleases.map(release => ({ ...release, site, upcoming: true })) - : []; - } - - return []; -} - -async function scrapeSiteReleases(scraper, site, accSiteReleases) { - const beforeFetchLatest = await scraper.beforeFetchLatest?.(site, accSiteReleases); - - const [newReleases, upcomingReleases] = await Promise.all([ - scrapeUniqueReleases(scraper, site, beforeFetchLatest, accSiteReleases), // fetch basic release info from scene overview - scrapeUpcomingReleases(scraper, site, beforeFetchLatest, accSiteReleases), // fetch basic release info from upcoming overview - ]); - - if (argv.upcoming) { - logger.info(`${site.name}: ${argv.latest ? `Found ${newReleases.length}` : 'Ignoring'} latest releases,${argv.upcoming ? ' ' : ' ignoring '}${upcomingReleases.length || '0'} upcoming releases`); - } - - const baseReleases = [...newReleases, ...upcomingReleases]; - - if (argv.deep) { - // follow URL for every release - return deepFetchReleases(baseReleases, beforeFetchLatest); - } - - return baseReleases; -} - -async function scrapeSite(site, network, accSiteReleases = []) { - if (site.parameters?.ignore) { - logger.warn(`Ignoring ${network.name}: ${site.name}`); - return []; - } - - const scraper = scrapers.releases[site.slug] || scrapers.releases[site.network.slug]; - - if (!scraper) { - logger.warn(`No scraper found for '${site.name}' (${site.slug})`); - return []; - } - - try { - const siteReleases = await scrapeSiteReleases(scraper, site, accSiteReleases); - - return siteReleases.map(release => ({ ...release, site })); - } catch (error) { - logger.error(`${site.name}: Failed to scrape releases: ${error.message}`); - - return []; - } -} - -async function scrapeSites() { - const networks = await fetchIncludedSites(); - - const scrapedNetworks = await Promise.map(networks, async (network) => { - if (network.parameters?.sequential) { - logger.info(`Scraping '${network.name}' sequentially`); - - return Promise.reduce(network.sites, async (acc, site) => { - const accSiteReleases = await acc; - const siteReleases = await scrapeSite(site, network, accSiteReleases); - - return accSiteReleases.concat(siteReleases); - }, Promise.resolve([])); - } - - return Promise.map(network.sites, async site => scrapeSite(site, network), { - concurrency: network.parameters?.concurrency || 2, - }); - }, - { - // 5 networks at a time - concurrency: 5, - }); - - const releases = scrapedNetworks.flat(2); - - if (argv.inspect) { - console.log(releases); - } - - if (argv.save) { - await storeReleases(releases); - } -} - -module.exports = scrapeSites; diff --git a/src/scrapers/21naturals.js b/src/scrapers/21naturals.js index 8d972832..780164a7 100644 --- a/src/scrapers/21naturals.js +++ b/src/scrapers/21naturals.js @@ -3,8 +3,8 @@ const { fetchApiLatest, fetchApiUpcoming, fetchScene, fetchApiProfile } = require('./gamma'); module.exports = { - fetchLatest: fetchApiLatest, - fetchProfile: fetchApiProfile, - fetchUpcoming: fetchApiUpcoming, - fetchScene, + fetchLatest: fetchApiLatest, + fetchProfile: fetchApiProfile, + fetchUpcoming: fetchApiUpcoming, + fetchScene, }; diff --git a/src/scrapers/21sextreme.js b/src/scrapers/21sextreme.js index 8d972832..780164a7 100644 --- a/src/scrapers/21sextreme.js +++ b/src/scrapers/21sextreme.js @@ -3,8 +3,8 @@ const { fetchApiLatest, fetchApiUpcoming, fetchScene, fetchApiProfile } = require('./gamma'); module.exports = { - fetchLatest: fetchApiLatest, - fetchProfile: fetchApiProfile, - fetchUpcoming: fetchApiUpcoming, - fetchScene, + fetchLatest: fetchApiLatest, + fetchProfile: fetchApiProfile, + fetchUpcoming: fetchApiUpcoming, + fetchScene, }; diff --git a/src/scrapers/21sextury.js b/src/scrapers/21sextury.js index 8d972832..780164a7 100644 --- a/src/scrapers/21sextury.js +++ b/src/scrapers/21sextury.js @@ -3,8 +3,8 @@ const { fetchApiLatest, fetchApiUpcoming, fetchScene, fetchApiProfile } = require('./gamma'); module.exports = { - fetchLatest: fetchApiLatest, - fetchProfile: fetchApiProfile, - fetchUpcoming: fetchApiUpcoming, - fetchScene, + fetchLatest: fetchApiLatest, + fetchProfile: fetchApiProfile, + fetchUpcoming: fetchApiUpcoming, + fetchScene, }; diff --git a/src/scrapers/adulttime.js b/src/scrapers/adulttime.js index 3870e7ed..5c1aa7f1 100644 --- a/src/scrapers/adulttime.js +++ b/src/scrapers/adulttime.js @@ -3,37 +3,37 @@ const { fetchApiLatest, fetchApiUpcoming, fetchScene, fetchApiProfile } = require('./gamma'); function curateRelease(release, site) { - if (['bubblegumdungeon', 'ladygonzo'].includes(site.slug)) { - return { - ...release, - title: release.title.split(/:|\|/)[1].trim(), - }; - } + if (['bubblegumdungeon', 'ladygonzo'].includes(site.slug)) { + return { + ...release, + title: release.title.split(/:|\|/)[1].trim(), + }; + } - return release; + return release; } async function networkFetchScene(url, site, release) { - const scene = await fetchScene(url, site, release); + const scene = await fetchScene(url, site, release); - return curateRelease(scene, site); + return curateRelease(scene, site); } async function fetchLatest(site, page = 1) { - const releases = await fetchApiLatest(site, page, false); + const releases = await fetchApiLatest(site, page, false); - return releases.map(release => curateRelease(release, site)); + return releases.map(release => curateRelease(release, site)); } async function fetchUpcoming(site, page = 1) { - const releases = await fetchApiUpcoming(site, page, false); + const releases = await fetchApiUpcoming(site, page, false); - return releases.map(release => curateRelease(release, site)); + return releases.map(release => curateRelease(release, site)); } module.exports = { - fetchLatest, - fetchProfile: fetchApiProfile, - fetchScene: networkFetchScene, - fetchUpcoming, + fetchLatest, + fetchProfile: fetchApiProfile, + fetchScene: networkFetchScene, + fetchUpcoming, }; diff --git a/src/scrapers/amateurallure.js b/src/scrapers/amateurallure.js index e86830c5..2609e030 100644 --- a/src/scrapers/amateurallure.js +++ b/src/scrapers/amateurallure.js @@ -3,47 +3,47 @@ const { fetchLatest, fetchScene } = require('./julesjordan'); function extractActors(scene) { - const release = scene; + const release = scene; - if (!scene.actors || scene.actors.length === 0) { - const introActorMatches = scene.title.match(/(?:presents|introduces|features|welcomes) (\w+ \w+)/i); - const introTwoActorMatches = scene.title.match(/(?:presents|introduces|features|welcomes) (?:(\w+)|(\w+ \w+)) and (\w+ \w+)/i); - const returnActorMatches = scene.title.match(/(?:(^\w+)|(\w+ \w+))(?:,| (?:return|visit|pov|give|suck|lick|milk|love|enjoy|service|is))/i); - const returnTwoActorMatches = scene.title.match(/(\w+ \w+) and (?:(\w+)|(\w+ \w+)) (?:return|visit|give|suck|lick|milk|love|enjoy|service|are)/i); + if (!scene.actors || scene.actors.length === 0) { + const introActorMatches = scene.title.match(/(?:presents|introduces|features|welcomes) (\w+ \w+)/i); + const introTwoActorMatches = scene.title.match(/(?:presents|introduces|features|welcomes) (?:(\w+)|(\w+ \w+)) and (\w+ \w+)/i); + const returnActorMatches = scene.title.match(/(?:(^\w+)|(\w+ \w+))(?:,| (?:return|visit|pov|give|suck|lick|milk|love|enjoy|service|is))/i); + const returnTwoActorMatches = scene.title.match(/(\w+ \w+) and (?:(\w+)|(\w+ \w+)) (?:return|visit|give|suck|lick|milk|love|enjoy|service|are)/i); - const rawActors = (introTwoActorMatches || introActorMatches || returnTwoActorMatches || returnActorMatches)?.slice(1); - const actors = rawActors?.filter((actor) => { - if (!actor) return false; - if (/swallow|\bcum|fuck|suck|give|giving|take|takes|taking|head|teen|babe|cute|beaut|naughty|teacher|nanny|adorable|brunette|blonde|bust|audition|from|\band\b|\bto\b/i.test(actor)) return false; + const rawActors = (introTwoActorMatches || introActorMatches || returnTwoActorMatches || returnActorMatches)?.slice(1); + const actors = rawActors?.filter((actor) => { + if (!actor) return false; + if (/swallow|\bcum|fuck|suck|give|giving|take|takes|taking|head|teen|babe|cute|beaut|naughty|teacher|nanny|adorable|brunette|blonde|bust|audition|from|\band\b|\bto\b/i.test(actor)) return false; - return true; - }); + return true; + }); - if (actors) { - release.actors = actors; - } - } + if (actors) { + release.actors = actors; + } + } - if (release.actors?.length > 1 || /threesome|threeway/.test(scene.title)) { - release.tags = scene.tags ? [...scene.tags, 'mff'] : ['mff']; - } + if (release.actors?.length > 1 || /threesome|threeway/.test(scene.title)) { + release.tags = scene.tags ? [...scene.tags, 'mff'] : ['mff']; + } - return release; + return release; } async function fetchLatestWrap(site, page = 1) { - const latest = await fetchLatest(site, page); + const latest = await fetchLatest(site, page); - return latest.map(scene => extractActors(scene)); + return latest.map(scene => extractActors(scene)); } async function fetchSceneWrap(url, site) { - const scene = await fetchScene(url, site); + const scene = await fetchScene(url, site); - return extractActors(scene); + return extractActors(scene); } module.exports = { - fetchLatest: fetchLatestWrap, - fetchScene: fetchSceneWrap, + fetchLatest: fetchLatestWrap, + fetchScene: fetchSceneWrap, }; diff --git a/src/scrapers/assylum.js b/src/scrapers/assylum.js index 178879e2..34e0f572 100644 --- a/src/scrapers/assylum.js +++ b/src/scrapers/assylum.js @@ -3,7 +3,7 @@ const { get, geta, ctxa } = require('../utils/q'); function extractActors(actorString) { - return actorString + return actorString ?.replace(/.*:|\(.*\)|\d+(-|\s)year(-|\s)old|nurses?|tangled/ig, '') // remove Patient:, (date) and other nonsense .split(/\band\b|\bvs\b|\/|,|&/ig) .map(actor => actor.trim()) @@ -12,120 +12,120 @@ function extractActors(actorString) { } function matchActors(actorString, models) { - return models - .filter(model => new RegExp(model.name, 'i') - .test(actorString)); + return models + .filter(model => new RegExp(model.name, 'i') + .test(actorString)); } function scrapeLatest(scenes, site, models) { - return scenes.map(({ qu }) => { - const release = {}; + return scenes.map(({ qu }) => { + const release = {}; - const pathname = qu.url('a.itemimg').slice(1); - [release.entryId] = pathname.split('/').slice(-1); - release.url = `${site.url}${pathname}`; + const pathname = qu.url('a.itemimg').slice(1); + [release.entryId] = pathname.split('/').slice(-1); + release.url = `${site.url}${pathname}`; - release.title = qu.q('.itemimg img', 'alt') || qu.q('h4 a', true); - release.description = qu.q('.mas_longdescription', true); - release.date = qu.date('.movie_info2', 'MM/DD/YY', /\d{2}\/\d{2}\/\d{2}/); + release.title = qu.q('.itemimg img', 'alt') || qu.q('h4 a', true); + release.description = qu.q('.mas_longdescription', true); + release.date = qu.date('.movie_info2', 'MM/DD/YY', /\d{2}\/\d{2}\/\d{2}/); - const actorString = qu.q('.mas_description', true); - const actors = matchActors(actorString, models); - if (actors.length > 0) release.actors = actors; - else release.actors = extractActors(actorString); + const actorString = qu.q('.mas_description', true); + const actors = matchActors(actorString, models); + if (actors.length > 0) release.actors = actors; + else release.actors = extractActors(actorString); - const posterPath = qu.img('.itemimg img'); - release.poster = `${site.url}/${posterPath}`; + const posterPath = qu.img('.itemimg img'); + release.poster = `${site.url}/${posterPath}`; - return release; - }); + return release; + }); } function scrapeScene({ html, qu }, url, site, models) { - const release = { url }; + const release = { url }; - [release.entryId] = url.split('/').slice(-1); - release.title = qu.q('.mas_title', true); - release.description = qu.q('.mas_longdescription', true); - release.date = qu.date('.mas_description', 'MMMM DD, YYYY', /\w+ \d{1,2}, \d{4}/); + [release.entryId] = url.split('/').slice(-1); + release.title = qu.q('.mas_title', true); + release.description = qu.q('.mas_longdescription', true); + release.date = qu.date('.mas_description', 'MMMM DD, YYYY', /\w+ \d{1,2}, \d{4}/); - const actorString = qu.q('.mas_description', true).replace(/\w+ \d{1,2}, \d{4}/, ''); - const actors = matchActors(actorString, models); - if (actors.length > 0) release.actors = actors; - else release.actors = extractActors(actorString); + const actorString = qu.q('.mas_description', true).replace(/\w+ \d{1,2}, \d{4}/, ''); + const actors = matchActors(actorString, models); + if (actors.length > 0) release.actors = actors; + else release.actors = extractActors(actorString); - release.tags = qu.all('.tags a', true); + release.tags = qu.all('.tags a', true); - release.photos = qu.imgs('.stills img').map(photoPath => `${site.url}/${photoPath}`); + release.photos = qu.imgs('.stills img').map(photoPath => `${site.url}/${photoPath}`); - const posterIndex = 'splash:'; - const poster = html.slice(html.indexOf('faceimages/', posterIndex), html.indexOf('.jpg', posterIndex) + 4); - if (poster) release.poster = `${site.url}/${poster}`; + const posterIndex = 'splash:'; + const poster = html.slice(html.indexOf('faceimages/', posterIndex), html.indexOf('.jpg', posterIndex) + 4); + if (poster) release.poster = `${site.url}/${poster}`; - const trailerIndex = html.indexOf('video/mp4'); - const trailer = html.slice(html.indexOf('/content', trailerIndex), html.indexOf('.mp4', trailerIndex) + 4); - if (trailer) release.trailer = { src: `${site.url}${trailer}` }; + const trailerIndex = html.indexOf('video/mp4'); + const trailer = html.slice(html.indexOf('/content', trailerIndex), html.indexOf('.mp4', trailerIndex) + 4); + if (trailer) release.trailer = { src: `${site.url}${trailer}` }; - return release; + return release; } function extractModels({ el }, site) { - const models = ctxa(el, '.item'); + const models = ctxa(el, '.item'); - return models.map(({ qu }) => { - const actor = { gender: 'female' }; + return models.map(({ qu }) => { + const actor = { gender: 'female' }; - const avatar = qu.q('.itemimg img'); - actor.avatar = `${site.url}/${avatar.src}`; - actor.name = avatar.alt - .split(':').slice(-1)[0] - .replace(/xtreme girl|nurse/ig, '') - .trim(); + const avatar = qu.q('.itemimg img'); + actor.avatar = `${site.url}/${avatar.src}`; + actor.name = avatar.alt + .split(':').slice(-1)[0] + .replace(/xtreme girl|nurse/ig, '') + .trim(); - const actorPath = qu.url('.itemimg'); - actor.url = `${site.url}${actorPath.slice(1)}`; + const actorPath = qu.url('.itemimg'); + actor.url = `${site.url}${actorPath.slice(1)}`; - return actor; - }); + return actor; + }); } async function fetchModels(site, page = 1, accModels = []) { - const url = `${site.url}/?models/${page}`; - const res = await get(url); + const url = `${site.url}/?models/${page}`; + const res = await get(url); - if (res.ok) { - const models = extractModels(res.item, site); - const nextPage = res.item.qa('.pagenumbers', true) - .map(pageX => Number(pageX)) - .filter(Boolean) // remove << and >> - .includes(page + 1); + if (res.ok) { + const models = extractModels(res.item, site); + const nextPage = res.item.qa('.pagenumbers', true) + .map(pageX => Number(pageX)) + .filter(Boolean) // remove << and >> + .includes(page + 1); - if (nextPage) { - return fetchModels(site, page + 1, accModels.concat(models)); - } + if (nextPage) { + return fetchModels(site, page + 1, accModels.concat(models)); + } - return accModels.concat(models, { name: 'Dr. Gray' }); - } + return accModels.concat(models, { name: 'Dr. Gray' }); + } - return []; + return []; } async function fetchLatest(site, page = 1, models) { - const url = `${site.url}/show.php?a=${site.parameters.a}_${page}`; - const res = await geta(url, '.item'); + const url = `${site.url}/show.php?a=${site.parameters.a}_${page}`; + const res = await geta(url, '.item'); - return res.ok ? scrapeLatest(res.items, site, models) : res.status; + return res.ok ? scrapeLatest(res.items, site, models) : res.status; } async function fetchScene(url, site, release, beforeFetchLatest) { - const models = beforeFetchLatest || await fetchModels(site); - const res = await get(url); + const models = beforeFetchLatest || await fetchModels(site); + const res = await get(url); - return res.ok ? scrapeScene(res.item, url, site, models) : res.status; + return res.ok ? scrapeScene(res.item, url, site, models) : res.status; } module.exports = { - fetchLatest, - fetchScene, - beforeFetchLatest: fetchModels, + fetchLatest, + fetchScene, + beforeFetchLatest: fetchModels, }; diff --git a/src/scrapers/aziani.js b/src/scrapers/aziani.js index efa46f05..0773ad23 100644 --- a/src/scrapers/aziani.js +++ b/src/scrapers/aziani.js @@ -5,141 +5,141 @@ const { get, getAll, initAll, extractDate } = require('../utils/qu'); const { feetInchesToCm } = require('../utils/convert'); function getFallbacks(source) { - return [ - source.replace('-1x.jpg', '-4x.jpg'), - source.replace('-1x.jpg', '-3x.jpg'), - source.replace('-1x.jpg', '-2x.jpg'), - source, - ]; + return [ + source.replace('-1x.jpg', '-4x.jpg'), + source.replace('-1x.jpg', '-3x.jpg'), + source.replace('-1x.jpg', '-2x.jpg'), + source, + ]; } function scrapeAll(scenes, site) { - return scenes.map(({ qu }) => { - const release = {}; + return scenes.map(({ qu }) => { + const release = {}; - release.entryId = qu.q('.stdimage', 'id', true).match(/set-target-(\d+)/)[1]; - release.url = qu.url('a'); + release.entryId = qu.q('.stdimage', 'id', true).match(/set-target-(\d+)/)[1]; + release.url = qu.url('a'); - release.title = qu.q('h5 a', true); - release.date = qu.date('.icon-calendar + strong', 'MM/DD/YYYY'); + release.title = qu.q('h5 a', true); + release.date = qu.date('.icon-calendar + strong', 'MM/DD/YYYY'); - release.actors = qu.q('h3', true).replace(/featuring:\s?/i, '').split(', '); + release.actors = qu.q('h3', true).replace(/featuring:\s?/i, '').split(', '); - const photoCount = qu.q('.stdimage', 'cnt'); - [release.poster, ...release.photos] = Array.from({ length: Number(photoCount) }, (value, index) => { - const source = qu.img('.stdimage', `src${index}_1x`, site.url); + const photoCount = qu.q('.stdimage', 'cnt'); + [release.poster, ...release.photos] = Array.from({ length: Number(photoCount) }, (value, index) => { + const source = qu.img('.stdimage', `src${index}_1x`, site.url); - return getFallbacks(source); - }); + return getFallbacks(source); + }); - return release; - }); + return release; + }); } function scrapeScene({ html, qu }, url) { - const release = { url }; + const release = { url }; - release.entryId = qu.q('.stdimage', 'id', true).match(/set-target-(\d+)/)[1]; + release.entryId = qu.q('.stdimage', 'id', true).match(/set-target-(\d+)/)[1]; - release.title = qu.q('h2', true); - release.description = qu.q('p', true); + release.title = qu.q('h2', true); + release.description = qu.q('p', true); - release.date = extractDate(html, 'MM/DD/YYYY', /\b\d{2}\/\d{2}\/\d{4}\b/); + release.date = extractDate(html, 'MM/DD/YYYY', /\b\d{2}\/\d{2}\/\d{4}\b/); - release.actors = qu.all('h5:not(.video_categories) a').map(actor => ({ - name: qu.q(actor, null, true), - url: qu.url(actor, null), - })); + release.actors = qu.all('h5:not(.video_categories) a').map(actor => ({ + name: qu.q(actor, null, true), + url: qu.url(actor, null), + })); - release.tags = qu.all('.video_categories a', true); + release.tags = qu.all('.video_categories a', true); - release.duration = qu.dur('.video_categories + p'); + release.duration = qu.dur('.video_categories + p'); - const poster = qu.img('a img'); + const poster = qu.img('a img'); - release.poster = getFallbacks(poster); - release.photos = qu.imgs('.featured-video img', 'src0_1x').map(source => getFallbacks(source)); + release.poster = getFallbacks(poster); + release.photos = qu.imgs('.featured-video img', 'src0_1x').map(source => getFallbacks(source)); - return release; + return release; } function scrapeProfile({ el, qu }) { - const profile = {}; + const profile = {}; - const bio = Array.from(qu.q('.widget-content').childNodes).reduce((acc, node, index, nodes) => { - const nextNode = nodes[index + 1]; + const bio = Array.from(qu.q('.widget-content').childNodes).reduce((acc, node, index, nodes) => { + const nextNode = nodes[index + 1]; - if (node.tagName === 'STRONG' && nextNode?.nodeType === 3) { - acc[slugify(node.textContent, '_')] = nextNode.textContent.trim(); - } + if (node.tagName === 'STRONG' && nextNode?.nodeType === 3) { + acc[slugify(node.textContent, '_')] = nextNode.textContent.trim(); + } - return acc; - }, {}); + return acc; + }, {}); - if (bio.ethnicity) profile.ethnicity = bio.ethnicity; - if (bio.age) profile.age = Number(bio.age); + if (bio.ethnicity) profile.ethnicity = bio.ethnicity; + if (bio.age) profile.age = Number(bio.age); - if (bio.height && /\d{3}/.test(bio.height)) profile.height = Number(bio.height.match(/\d+/)[0]); - if (bio.height && /\d[;']\d/.test(bio.height)) profile.height = feetInchesToCm(bio.height); + if (bio.height && /\d{3}/.test(bio.height)) profile.height = Number(bio.height.match(/\d+/)[0]); + if (bio.height && /\d[;']\d/.test(bio.height)) profile.height = feetInchesToCm(bio.height); - if (bio.measurements) { - const [bust, waist, hip] = bio.measurements.split('-'); + if (bio.measurements) { + const [bust, waist, hip] = bio.measurements.split('-'); - if (bust && /\d+[a-zA-Z]+/.test(bust)) profile.bust = bust; - if (waist) profile.waist = Number(waist); - if (hip) profile.hip = Number(hip); - } + if (bust && /\d+[a-zA-Z]+/.test(bust)) profile.bust = bust; + if (waist) profile.waist = Number(waist); + if (hip) profile.hip = Number(hip); + } - if (bio.bust_size && !profile.bust) profile.bust = bio.bust_size.toUpperCase(); + if (bio.bust_size && !profile.bust) profile.bust = bio.bust_size.toUpperCase(); - if (bio.birth_location) profile.birthPlace = bio.birth_location; - if (bio.status_married_or_single) profile.relationship = bio.status_married_or_single; + if (bio.birth_location) profile.birthPlace = bio.birth_location; + if (bio.status_married_or_single) profile.relationship = bio.status_married_or_single; - if (bio.eye_color) profile.eyes = bio.eye_color; + if (bio.eye_color) profile.eyes = bio.eye_color; - const avatar = qu.img('.tac img'); - profile.avatar = getFallbacks(avatar); + const avatar = qu.img('.tac img'); + profile.avatar = getFallbacks(avatar); - profile.releases = scrapeAll(initAll(el, '.featured-video')); + profile.releases = scrapeAll(initAll(el, '.featured-video')); - return profile; + return profile; } async function fetchLatest(site, page) { - const url = `${site.url}/tour/categories/movies_${page}_d.html`; - const res = await getAll(url, '.featured-video'); + const url = `${site.url}/tour/categories/movies_${page}_d.html`; + const res = await getAll(url, '.featured-video'); - if (res.ok) { - return scrapeAll(res.items, site); - } + if (res.ok) { + return scrapeAll(res.items, site); + } - return res.status; + return res.status; } async function fetchScene(url, site) { - const res = await get(url, '.page-content .row'); + const res = await get(url, '.page-content .row'); - if (res.ok) { - return scrapeScene(res.item, url, site); - } + if (res.ok) { + return scrapeScene(res.item, url, site); + } - return res.status; + return res.status; } async function fetchProfile(actorName, scraperSlug, site) { - const actorSlug = slugify(actorName, ''); - const url = `${site.url}/tour/models/${actorSlug}.html`; - const res = await get(url, '.page-content .row'); + const actorSlug = slugify(actorName, ''); + const url = `${site.url}/tour/models/${actorSlug}.html`; + const res = await get(url, '.page-content .row'); - if (res.ok) { - return scrapeProfile(res.item); - } + if (res.ok) { + return scrapeProfile(res.item); + } - return res.status; + return res.status; } module.exports = { - fetchLatest, - fetchProfile, - fetchScene, + fetchLatest, + fetchProfile, + fetchScene, }; diff --git a/src/scrapers/babes.js b/src/scrapers/babes.js index 01c4d3d9..68418c93 100644 --- a/src/scrapers/babes.js +++ b/src/scrapers/babes.js @@ -3,11 +3,11 @@ const { fetchScene, fetchLatest, fetchProfile } = require('./mindgeek'); async function networkFetchProfile(actorName) { - return fetchProfile(actorName, 'babes'); + return fetchProfile(actorName, 'babes'); } module.exports = { - fetchLatest, - fetchProfile: networkFetchProfile, - fetchScene, + fetchLatest, + fetchProfile: networkFetchProfile, + fetchScene, }; diff --git a/src/scrapers/bamvisions.js b/src/scrapers/bamvisions.js index 116073f0..658432db 100644 --- a/src/scrapers/bamvisions.js +++ b/src/scrapers/bamvisions.js @@ -6,144 +6,144 @@ const slugify = require('../utils/slugify'); const { feetInchesToCm } = require('../utils/convert'); function scrapeAll(scenes, site) { - return scenes.map(({ qu }) => { - const release = {}; + return scenes.map(({ qu }) => { + const release = {}; - release.title = qu.q('h3 a', true); - release.url = qu.url('h3 a'); + release.title = qu.q('h3 a', true); + release.url = qu.url('h3 a'); - release.date = qu.date('.item-meta li', 'MMMM D, YYYY', /\w+ \d{1,2}, \d{4}/); - release.duration = qu.dur('.item-meta li:nth-child(2)'); - release.description = qu.q('.description', true); + release.date = qu.date('.item-meta li', 'MMMM D, YYYY', /\w+ \d{1,2}, \d{4}/); + release.duration = qu.dur('.item-meta li:nth-child(2)'); + release.description = qu.q('.description', true); - release.actors = qu.all('a[href*="/models"]', true); - if (/bts/i.test(release.title)) release.tags = ['behind the scenes']; + release.actors = qu.all('a[href*="/models"]', true); + if (/bts/i.test(release.title)) release.tags = ['behind the scenes']; - [release.poster, ...release.photos] = qu.all('.item-thumbs img') - .map(source => [ - source.getAttribute('src0_3x'), - source.getAttribute('src0_2x'), - source.getAttribute('src0_1x'), - ] - .filter(Boolean) - .map(fallback => (/^http/.test(fallback) ? fallback : `${site.url}${fallback}`))); + [release.poster, ...release.photos] = qu.all('.item-thumbs img') + .map(source => [ + source.getAttribute('src0_3x'), + source.getAttribute('src0_2x'), + source.getAttribute('src0_1x'), + ] + .filter(Boolean) + .map(fallback => (/^http/.test(fallback) ? fallback : `${site.url}${fallback}`))); - release.entryId = `${formatDate(release.date, 'YYYY-MM-DD')}-${slugify(release.title)}`; + release.entryId = `${formatDate(release.date, 'YYYY-MM-DD')}-${slugify(release.title)}`; - return release; - }); + return release; + }); } function scrapeScene({ html, qu }, url, site) { - const release = { url }; + const release = { url }; - release.title = qu.q('.item-episode h4 a', true); - release.date = qu.date('.item-meta li', 'MMMM D, YYYY', /\w+ \d{1,2}, \d{4}/); - release.duration = qu.dur('.item-meta li:nth-child(2)'); - release.description = qu.q('.description', true); + release.title = qu.q('.item-episode h4 a', true); + release.date = qu.date('.item-meta li', 'MMMM D, YYYY', /\w+ \d{1,2}, \d{4}/); + release.duration = qu.dur('.item-meta li:nth-child(2)'); + release.description = qu.q('.description', true); - release.actors = qu.all('.item-episode a[href*="/models"]', true); - if (/bts/i.test(release.title)) release.tags = ['behind the scenes']; + release.actors = qu.all('.item-episode a[href*="/models"]', true); + if (/bts/i.test(release.title)) release.tags = ['behind the scenes']; - const posterPath = html.match(/poster="(.*.jpg)"/)?.[1]; - const trailerPath = html.match(/video src="(.*.mp4)"/)?.[1]; + const posterPath = html.match(/poster="(.*.jpg)"/)?.[1]; + const trailerPath = html.match(/video src="(.*.mp4)"/)?.[1]; - if (posterPath) { - const poster = /^http/.test(posterPath) ? posterPath : `${site.url}${posterPath}`; - release.poster = [ - poster.replace('-1x', '-3x'), - poster.replace('-1x', '-2x'), - poster, - ]; - } + if (posterPath) { + const poster = /^http/.test(posterPath) ? posterPath : `${site.url}${posterPath}`; + release.poster = [ + poster.replace('-1x', '-3x'), + poster.replace('-1x', '-2x'), + poster, + ]; + } - if (trailerPath) { - const trailer = /^http/.test(trailerPath) ? trailerPath : `${site.url}${trailerPath}`; - release.trailer = { src: trailer }; - } + if (trailerPath) { + const trailer = /^http/.test(trailerPath) ? trailerPath : `${site.url}${trailerPath}`; + release.trailer = { src: trailer }; + } - release.entryId = `${formatDate(release.date, 'YYYY-MM-DD')}-${slugify(release.title)}`; + release.entryId = `${formatDate(release.date, 'YYYY-MM-DD')}-${slugify(release.title)}`; - return release; + return release; } async function fetchActorReleases(actorId, site, page = 1, accScenes = []) { - const url = `${site.url}/sets.php?id=${actorId}&page=${page}`; - const res = await get(url); + const url = `${site.url}/sets.php?id=${actorId}&page=${page}`; + const res = await get(url); - if (!res.ok) return []; + if (!res.ok) return []; - const quReleases = initAll(res.item.el, '.item-episode'); - const releases = scrapeAll(quReleases, site); + const quReleases = initAll(res.item.el, '.item-episode'); + const releases = scrapeAll(quReleases, site); - const nextPage = res.item.qu.q(`a[href*="page=${page + 1}"]`); + const nextPage = res.item.qu.q(`a[href*="page=${page + 1}"]`); - if (nextPage) { - return fetchActorReleases(actorId, site, page + 1, accScenes.concat(releases)); - } + if (nextPage) { + return fetchActorReleases(actorId, site, page + 1, accScenes.concat(releases)); + } - return accScenes.concat(releases); + return accScenes.concat(releases); } async function scrapeProfile({ qu }, site, withScenes) { - const profile = {}; + const profile = {}; - const bio = qu.all('.stats li', true).reduce((acc, row) => { - const [key, value] = row.split(':'); - return { ...acc, [slugify(key, '_')]: value.trim() }; - }, {}); + const bio = qu.all('.stats li', true).reduce((acc, row) => { + const [key, value] = row.split(':'); + return { ...acc, [slugify(key, '_')]: value.trim() }; + }, {}); - if (bio.height) profile.height = feetInchesToCm(bio.height); - if (bio.measurements) { - const [bust, waist, hip] = bio.measurements.split('-'); + if (bio.height) profile.height = feetInchesToCm(bio.height); + if (bio.measurements) { + const [bust, waist, hip] = bio.measurements.split('-'); - if (bust) profile.bust = bust; - if (waist) profile.waist = Number(waist); - if (hip) profile.hip = Number(hip); - } + if (bust) profile.bust = bust; + if (waist) profile.waist = Number(waist); + if (hip) profile.hip = Number(hip); + } - profile.avatar = [ - qu.q('.profile-pic img', 'src0_3x'), - qu.q('.profile-pic img', 'src0_2x'), - qu.q('.profile-pic img', 'src0_1x'), - ].filter(Boolean).map(source => (/^http/.test(source) ? source : `${site.url}${source}`)); + profile.avatar = [ + qu.q('.profile-pic img', 'src0_3x'), + qu.q('.profile-pic img', 'src0_2x'), + qu.q('.profile-pic img', 'src0_1x'), + ].filter(Boolean).map(source => (/^http/.test(source) ? source : `${site.url}${source}`)); - if (withScenes) { - const actorId = qu.q('.profile-pic img', 'id')?.match(/set-target-(\d+)/)?.[1]; + if (withScenes) { + const actorId = qu.q('.profile-pic img', 'id')?.match(/set-target-(\d+)/)?.[1]; - if (actorId) { - profile.releases = await fetchActorReleases(actorId, site); - } - } + if (actorId) { + profile.releases = await fetchActorReleases(actorId, site); + } + } - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const url = `${site.url}/categories/movies/${page}/latest/`; - const res = await geta(url, '.item-episode'); + const url = `${site.url}/categories/movies/${page}/latest/`; + const res = await geta(url, '.item-episode'); - return res.ok ? scrapeAll(res.items, site) : res.status; + return res.ok ? scrapeAll(res.items, site) : res.status; } async function fetchScene(url, site) { - const res = await get(url); + const res = await get(url); - return res.ok ? scrapeScene(res.item, url, site) : res.status; + return res.ok ? scrapeScene(res.item, url, site) : res.status; } async function fetchProfile(actorName, scraperSlug, site, include) { - const actorSlugA = slugify(actorName, ''); - const actorSlugB = slugify(actorName); + const actorSlugA = slugify(actorName, ''); + const actorSlugB = slugify(actorName); - const resA = await get(`${site.url}/models/${actorSlugA}.html`); - const res = resA.ok ? resA : await get(`${site.url}/models/${actorSlugB}.html`); + const resA = await get(`${site.url}/models/${actorSlugA}.html`); + const res = resA.ok ? resA : await get(`${site.url}/models/${actorSlugB}.html`); - return res.ok ? scrapeProfile(res.item, site, include.scenes) : res.status; + return res.ok ? scrapeProfile(res.item, site, include.scenes) : res.status; } module.exports = { - fetchLatest, - fetchScene, - fetchProfile, + fetchLatest, + fetchScene, + fetchProfile, }; diff --git a/src/scrapers/bang.js b/src/scrapers/bang.js index c1c43fb6..b1a80029 100644 --- a/src/scrapers/bang.js +++ b/src/scrapers/bang.js @@ -8,99 +8,99 @@ const clusterId = '617fb597b659459bafe6472470d9073a'; const authKey = 'YmFuZy1yZWFkOktqVDN0RzJacmQ1TFNRazI='; const genderMap = { - M: 'male', - F: 'female', + M: 'male', + F: 'female', }; function getScreenUrl(item, scene) { - return `https://i.bang.com/screenshots/${scene.dvd.id}/movie/${scene.order}/${item.screenId}.jpg`; + return `https://i.bang.com/screenshots/${scene.dvd.id}/movie/${scene.order}/${item.screenId}.jpg`; } function encodeId(id) { - return Buffer - .from(id, 'hex') - .toString('base64') - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/=/g, ','); + return Buffer + .from(id, 'hex') + .toString('base64') + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, ','); } function decodeId(id) { - const restoredId = id - .replace(/-/g, '+') - .replace(/_/g, '/') - .replace(/,/g, '='); + const restoredId = id + .replace(/-/g, '+') + .replace(/_/g, '/') + .replace(/,/g, '='); - return Buffer - .from(restoredId, 'base64') - .toString('hex'); + return Buffer + .from(restoredId, 'base64') + .toString('hex'); } function scrapeScene(scene, site) { - const release = { - site, - entryId: scene.id, - title: scene.name, - description: scene.description, - tags: scene.genres.concat(scene.actions).map(genre => genre.name), - duration: scene.duration, - }; + const release = { + site, + entryId: scene.id, + title: scene.name, + description: scene.description, + tags: scene.genres.concat(scene.actions).map(genre => genre.name), + duration: scene.duration, + }; - const slug = slugify(release.title); - release.url = `https://www.bang.com/video/${encodeId(release.entryId)}/${slug}`; + const slug = slugify(release.title); + release.url = `https://www.bang.com/video/${encodeId(release.entryId)}/${slug}`; - const date = new Date(scene.releaseDate); - release.date = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())); + const date = new Date(scene.releaseDate); + release.date = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())); - release.actors = scene.actors.map(actor => ({ name: actor.name, gender: genderMap[actor.gender] })); + release.actors = scene.actors.map(actor => ({ name: actor.name, gender: genderMap[actor.gender] })); - if (scene.is4k) release.tags.push('4k'); - if (scene.gay) release.tags.push('gay'); + if (scene.is4k) release.tags.push('4k'); + if (scene.gay) release.tags.push('gay'); - const defaultPoster = scene.screenshots.find(photo => photo.default === true); - const photoset = scene.screenshots.filter(photo => photo.default === false); + const defaultPoster = scene.screenshots.find(photo => photo.default === true); + const photoset = scene.screenshots.filter(photo => photo.default === false); - const photos = defaultPoster ? photoset : photoset.slice(1); - const poster = defaultPoster || photoset[0]; + const photos = defaultPoster ? photoset : photoset.slice(1); + const poster = defaultPoster || photoset[0]; - release.poster = getScreenUrl(poster, scene); - release.photos = photos.map(photo => getScreenUrl(photo, scene)); + release.poster = getScreenUrl(poster, scene); + release.photos = photos.map(photo => getScreenUrl(photo, scene)); - release.trailer = { - src: `https://i.bang.com/v/${scene.dvd.id}/${scene.identifier}/preview.mp4`, - }; + release.trailer = { + src: `https://i.bang.com/v/${scene.dvd.id}/${scene.identifier}/preview.mp4`, + }; - release.channel = scene.series.name - .replace(/[! .]/g, '') - .replace('&', 'and'); + release.channel = scene.series.name + .replace(/[! .]/g, '') + .replace('&', 'and'); - return release; + return release; } function scrapeLatest(scenes, site) { - return scenes.map(({ _source: scene }) => scrapeScene(scene, site)); + return scenes.map(({ _source: scene }) => scrapeScene(scene, site)); } async function fetchLatest(site, page = 1) { - const res = await bhttp.post(`https://${clusterId}.us-east-1.aws.found.io/videos/video/_search`, { - size: 50, - from: (page - 1) * 50, - query: { - bool: { - must: [ - { - match: { - status: 'ok', - }, - }, - { - range: { - releaseDate: { - lte: 'now', - }, - }, - }, - /* + const res = await bhttp.post(`https://${clusterId}.us-east-1.aws.found.io/videos/video/_search`, { + size: 50, + from: (page - 1) * 50, + query: { + bool: { + must: [ + { + match: { + status: 'ok', + }, + }, + { + range: { + releaseDate: { + lte: 'now', + }, + }, + }, + /* * global fetch { nested: { @@ -122,66 +122,66 @@ async function fetchLatest(site, page = 1) { }, }, */ - { - nested: { - path: 'series', - query: { - bool: { - must: [ - { - match: { - 'series.id': { - operator: 'AND', - query: site.parameters.siteId, - }, - }, - }, - ], - }, - }, - }, - }, - ], - must_not: [ - { - match: { - type: 'trailer', - }, - }, - ], - }, - }, - sort: [ - { - releaseDate: { - order: 'desc', - }, - }, - ], - }, { - encodeJSON: true, - headers: { - Authorization: `Basic ${authKey}`, - }, - }); + { + nested: { + path: 'series', + query: { + bool: { + must: [ + { + match: { + 'series.id': { + operator: 'AND', + query: site.parameters.siteId, + }, + }, + }, + ], + }, + }, + }, + }, + ], + must_not: [ + { + match: { + type: 'trailer', + }, + }, + ], + }, + }, + sort: [ + { + releaseDate: { + order: 'desc', + }, + }, + ], + }, { + encodeJSON: true, + headers: { + Authorization: `Basic ${authKey}`, + }, + }); - return scrapeLatest(res.body.hits.hits, site); + return scrapeLatest(res.body.hits.hits, site); } async function fetchScene(url, site) { - const encodedId = new URL(url).pathname.split('/')[2]; - const entryId = decodeId(encodedId); + const encodedId = new URL(url).pathname.split('/')[2]; + const entryId = decodeId(encodedId); - const res = await bhttp.get(`https://${clusterId}.us-east-1.aws.found.io/videos/video/${entryId}`, { - headers: { - Authorization: `Basic ${authKey}`, - }, - }); + const res = await bhttp.get(`https://${clusterId}.us-east-1.aws.found.io/videos/video/${entryId}`, { + headers: { + Authorization: `Basic ${authKey}`, + }, + }); - return scrapeScene(res.body._source, site); // eslint-disable-line no-underscore-dangle + return scrapeScene(res.body._source, site); // eslint-disable-line no-underscore-dangle } module.exports = { - fetchLatest, - fetchScene, + fetchLatest, + fetchScene, }; diff --git a/src/scrapers/bangbros.js b/src/scrapers/bangbros.js index e4d15cb5..183219d2 100644 --- a/src/scrapers/bangbros.js +++ b/src/scrapers/bangbros.js @@ -10,44 +10,44 @@ const slugify = require('../utils/slugify'); const { ex } = require('../utils/q'); function scrape(html, site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const sceneElements = $('.echThumb').toArray(); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const sceneElements = $('.echThumb').toArray(); - return sceneElements.map((element) => { - const sceneLinkElement = $(element).find('.thmb_lnk'); - const title = sceneLinkElement.attr('title'); - const url = `https://bangbros.com${sceneLinkElement.attr('href')}`; - const shootId = sceneLinkElement.attr('id') && sceneLinkElement.attr('id').split('-')[1]; - const entryId = url.split('/')[3].slice(5); + return sceneElements.map((element) => { + const sceneLinkElement = $(element).find('.thmb_lnk'); + const title = sceneLinkElement.attr('title'); + const url = `https://bangbros.com${sceneLinkElement.attr('href')}`; + const shootId = sceneLinkElement.attr('id') && sceneLinkElement.attr('id').split('-')[1]; + const entryId = url.split('/')[3].slice(5); - const date = moment.utc($(element).find('.thmb_mr_2 span.faTxt').text(), 'MMM D, YYYY').toDate(); - const actors = $(element).find('.cast-wrapper a.cast').map((actorIndex, actorElement) => $(actorElement).text().trim()).toArray(); + const date = moment.utc($(element).find('.thmb_mr_2 span.faTxt').text(), 'MMM D, YYYY').toDate(); + const actors = $(element).find('.cast-wrapper a.cast').map((actorIndex, actorElement) => $(actorElement).text().trim()).toArray(); - const photoElement = $(element).find('.rollover-image'); - const poster = `https:${photoElement.attr('data-original')}`; + const photoElement = $(element).find('.rollover-image'); + const poster = `https:${photoElement.attr('data-original')}`; - const photosUrl = photoElement.attr('data-rollover-url'); - const photosMaxIndex = photoElement.attr('data-rollover-max-index'); - const photos = Array.from({ length: photosMaxIndex }, (val, index) => `https:${photosUrl}big${index + 1}.jpg`); + const photosUrl = photoElement.attr('data-rollover-url'); + const photosMaxIndex = photoElement.attr('data-rollover-max-index'); + const photos = Array.from({ length: photosMaxIndex }, (val, index) => `https:${photosUrl}big${index + 1}.jpg`); - const duration = moment.duration(`0:${$(element).find('.thmb_pic b.tTm').text()}`).asSeconds(); - const channel = $(element).find('a[href*="/websites"]').attr('href').split('/').slice(-1)[0]; + const duration = moment.duration(`0:${$(element).find('.thmb_pic b.tTm').text()}`).asSeconds(); + const channel = $(element).find('a[href*="/websites"]').attr('href').split('/').slice(-1)[0]; - return { - url, - entryId, - shootId, - title, - actors, - date, - duration, - poster, - photos, - rating: null, - site, - channel, - }; - }); + return { + url, + entryId, + shootId, + title, + actors, + date, + duration, + poster, + photos, + rating: null, + site, + channel, + }; + }); } /* no dates available, breaks database @@ -80,63 +80,63 @@ function scrapeUpcoming(html, site) { */ function scrapeScene(html, url, _site) { - const { qu } = ex(html, '.playerSection'); - const release = {}; + const { qu } = ex(html, '.playerSection'); + const release = {}; - [release.shootId] = qu.q('.vdoTags + .vdoCast', true).match(/\w+$/); - [release.entryId] = url.split('/')[3].match(/\d+$/); - release.title = qu.q('.ps-vdoHdd h1', true); - release.description = qu.q('.vdoDesc', true); + [release.shootId] = qu.q('.vdoTags + .vdoCast', true).match(/\w+$/); + [release.entryId] = url.split('/')[3].match(/\d+$/); + release.title = qu.q('.ps-vdoHdd h1', true); + release.description = qu.q('.vdoDesc', true); - release.actors = qu.all('a[href*="/model"]', true); - release.tags = qu.all('.vdoTags a', true); + release.actors = qu.all('a[href*="/model"]', true); + release.tags = qu.all('.vdoTags a', true); - release.stars = Number(qu.q('div[class*="like"]', true).match(/^\d+/)[0]) / 20; + release.stars = Number(qu.q('div[class*="like"]', true).match(/^\d+/)[0]) / 20; - const poster = qu.img('img#player-overlay-image'); - release.poster = [ - poster, - poster.replace('/big_trailer', '/members/450x340'), // load error fallback - ]; + const poster = qu.img('img#player-overlay-image'); + release.poster = [ + poster, + poster.replace('/big_trailer', '/members/450x340'), // load error fallback + ]; - release.trailer = { src: qu.trailer() }; + release.trailer = { src: qu.trailer() }; - // all scenes seem to have 12 album photos available, not always included on the page - const firstPhotoUrl = ex(html).qu.img('img[data-slider-index="1"]'); - release.photos = Array.from({ length: 12 }, (val, index) => firstPhotoUrl.replace(/big\d+/, `big${index + 1}`)); + // all scenes seem to have 12 album photos available, not always included on the page + const firstPhotoUrl = ex(html).qu.img('img[data-slider-index="1"]'); + release.photos = Array.from({ length: 12 }, (val, index) => firstPhotoUrl.replace(/big\d+/, `big${index + 1}`)); - const [channel] = qu.url('a[href*="/websites"]').match(/\w+$/); + const [channel] = qu.url('a[href*="/websites"]').match(/\w+$/); - if (channel === 'bangcasting') release.channel = 'bangbroscasting'; - if (channel === 'remaster') release.channel = 'bangbrosremastered'; - else release.channel = channel; + if (channel === 'bangcasting') release.channel = 'bangbroscasting'; + if (channel === 'remaster') release.channel = 'bangbrosremastered'; + else release.channel = channel; - return release; + return release; } function scrapeProfile(html) { - const { q } = ex(html); - const profile = {}; + const { q } = ex(html); + const profile = {}; - const avatar = q('.profilePic img', 'src'); - if (avatar) profile.avatar = `https:${avatar}`; + const avatar = q('.profilePic img', 'src'); + if (avatar) profile.avatar = `https:${avatar}`; - profile.releases = scrape(html); + profile.releases = scrape(html); - return profile; + return profile; } function scrapeProfileSearch(html, actorName) { - const { qu } = ex(html); - const actorLink = qu.url(`a[title="${actorName}" i][href*="model"]`); + const { qu } = ex(html); + const actorLink = qu.url(`a[title="${actorName}" i][href*="model"]`); - return actorLink ? `https://bangbros.com${actorLink}` : null; + return actorLink ? `https://bangbros.com${actorLink}` : null; } async function fetchLatest(site, page = 1) { - const res = await bhttp.get(`${site.url}/${page}`); + const res = await bhttp.get(`${site.url}/${page}`); - return scrape(res.body.toString(), site); + return scrape(res.body.toString(), site); } /* @@ -148,43 +148,43 @@ async function fetchUpcoming(site) { */ async function fetchScene(url, site, release) { - if (!release?.date) { - logger.warn(`Scraping Bang Bros scene from URL without release date: ${url}`); - } + if (!release?.date) { + logger.warn(`Scraping Bang Bros scene from URL without release date: ${url}`); + } - const { origin } = new URL(url); - const res = await bhttp.get(url); + const { origin } = new URL(url); + const res = await bhttp.get(url); - if (!/https?:\/\/(www.)?bangbros.com\/?$/.test(origin)) { - throw new Error('Cannot fetch from this URL. Please find the scene on https://bangbros.com and try again.'); - } + if (!/https?:\/\/(www.)?bangbros.com\/?$/.test(origin)) { + throw new Error('Cannot fetch from this URL. Please find the scene on https://bangbros.com and try again.'); + } - return scrapeScene(res.body.toString(), url, site); + return scrapeScene(res.body.toString(), url, site); } async function fetchProfile(actorName) { - const actorSlug = slugify(actorName); - const url = `https://bangbros.com/search/${actorSlug}`; - const res = await bhttp.get(url); + const actorSlug = slugify(actorName); + const url = `https://bangbros.com/search/${actorSlug}`; + const res = await bhttp.get(url); - if (res.statusCode === 200) { - const actorUrl = scrapeProfileSearch(res.body.toString(), actorName); + if (res.statusCode === 200) { + const actorUrl = scrapeProfileSearch(res.body.toString(), actorName); - if (actorUrl) { - const actorRes = await bhttp.get(actorUrl); + if (actorUrl) { + const actorRes = await bhttp.get(actorUrl); - if (actorRes.statusCode === 200) { - return scrapeProfile(actorRes.body.toString()); - } - } - } + if (actorRes.statusCode === 200) { + return scrapeProfile(actorRes.body.toString()); + } + } + } - return null; + return null; } module.exports = { - fetchLatest, - fetchScene, - fetchProfile, - // fetchUpcoming, no dates available + fetchLatest, + fetchScene, + fetchProfile, + // fetchUpcoming, no dates available }; diff --git a/src/scrapers/blowpass.js b/src/scrapers/blowpass.js index 72d3d3b5..683baa11 100644 --- a/src/scrapers/blowpass.js +++ b/src/scrapers/blowpass.js @@ -5,33 +5,33 @@ const { fetchScene, fetchLatest, fetchUpcoming, fetchProfile } = require('./gamma'); async function fetchSceneWrapper(url, site, baseRelease) { - const release = await fetchScene(url, site, baseRelease); + const release = await fetchScene(url, site, baseRelease); - if (site.isFallback && release.channel) { - const channelUrl = url.replace('blowpass.com', `${release.channel}.com`); + if (site.isNetwork && release.channel) { + const channelUrl = url.replace('blowpass.com', `${release.channel}.com`); - if (['onlyteenblowjobs', 'mommyblowsbest'].includes(release.channel)) { - release.url = channelUrl.replace(/video\/\w+\//, 'scene/'); - return release; - } + if (['onlyteenblowjobs', 'mommyblowsbest'].includes(release.channel)) { + release.url = channelUrl.replace(/video\/\w+\//, 'scene/'); + return release; + } - release.url = channelUrl.replace(/video\/\w+\//, 'video/'); - } + release.url = channelUrl.replace(/video\/\w+\//, 'video/'); + } - return release; + return release; } function getActorReleasesUrl(actorPath, page = 1) { - return `https://www.blowpass.com/en/videos/blowpass/latest/All-Categories/0${actorPath}/${page}`; + return `https://www.blowpass.com/en/videos/blowpass/latest/All-Categories/0${actorPath}/${page}`; } async function networkFetchProfile(actorName, scraperSlug, site, include) { - return fetchProfile(actorName, scraperSlug, null, getActorReleasesUrl, include); + return fetchProfile(actorName, scraperSlug, null, getActorReleasesUrl, include); } module.exports = { - fetchLatest, - fetchProfile: networkFetchProfile, - fetchUpcoming, - fetchScene: fetchSceneWrapper, + fetchLatest, + fetchProfile: networkFetchProfile, + fetchUpcoming, + fetchScene: fetchSceneWrapper, }; diff --git a/src/scrapers/boobpedia.js b/src/scrapers/boobpedia.js index a77e6866..9d34cbf9 100644 --- a/src/scrapers/boobpedia.js +++ b/src/scrapers/boobpedia.js @@ -5,90 +5,90 @@ const bhttp = require('bhttp'); const { ex } = require('../utils/q'); function scrapeProfile(html) { - const { qu } = ex(html); /* eslint-disable-line object-curly-newline */ - const profile = {}; + const { qu } = ex(html); /* eslint-disable-line object-curly-newline */ + const profile = {}; - const bio = qu.all('.infobox tr[valign="top"]') - .map(detail => qu.all(detail, 'td', true)) - .reduce((acc, [key, value]) => ({ ...acc, [key.slice(0, -1).replace(/[\s+|/]/g, '_')]: value }), {}); + const bio = qu.all('.infobox tr[valign="top"]') + .map(detail => qu.all(detail, 'td', true)) + .reduce((acc, [key, value]) => ({ ...acc, [key.slice(0, -1).replace(/[\s+|/]/g, '_')]: value }), {}); - /* unreliable, see: Syren De Mer + /* unreliable, see: Syren De Mer const catlinks = qa('#mw-normal-catlinks a', true); const isTrans = catlinks.some(link => link.match(/shemale|transgender/i)); profile.gender = isTrans ? 'transsexual' : 'female'; */ - profile.birthdate = qu.date('.bday', 'YYYY-MM-DD'); + profile.birthdate = qu.date('.bday', 'YYYY-MM-DD'); - profile.description = qu.q('#mw-content-text > p', true); + profile.description = qu.q('#mw-content-text > p', true); - if (bio.Born) profile.birthPlace = bio.Born.slice(bio.Born.lastIndexOf(')') + 1); - if (bio.Ethnicity) profile.ethnicity = bio.Ethnicity; + if (bio.Born) profile.birthPlace = bio.Born.slice(bio.Born.lastIndexOf(')') + 1); + if (bio.Ethnicity) profile.ethnicity = bio.Ethnicity; - if (bio.Measurements) { - const measurements = bio.Measurements - .match(/\d+(\w+)?-\d+-\d+/g) + if (bio.Measurements) { + const measurements = bio.Measurements + .match(/\d+(\w+)?-\d+-\d+/g) ?.slice(-1)[0] // allow for both '34C-25-36' and '86-64-94 cm / 34-25-37 in' .split('-'); - // account for measuemrents being just e.g. '32EE' - if (measurements) { - const [bust, waist, hip] = measurements; + // account for measuemrents being just e.g. '32EE' + if (measurements) { + const [bust, waist, hip] = measurements; - if (/[a-zA-Z]/.test(bust)) profile.bust = bust; // only use bust if cup size is included + if (/[a-zA-Z]/.test(bust)) profile.bust = bust; // only use bust if cup size is included - profile.waist = Number(waist); - profile.hip = Number(hip); - } + profile.waist = Number(waist); + profile.hip = Number(hip); + } - if (/^\d+\w+$/.test(bio.Measurements)) profile.bust = bio.Measurements; - } + if (/^\d+\w+$/.test(bio.Measurements)) profile.bust = bio.Measurements; + } - if (bio.Bra_cup_size) { - const bust = bio.Bra_cup_size.match(/^\d+\w+/); - if (bust) [profile.bust] = bust; - } + if (bio.Bra_cup_size) { + const bust = bio.Bra_cup_size.match(/^\d+\w+/); + if (bust) [profile.bust] = bust; + } - if (bio.Boobs === 'Enhanced') profile.naturalBoobs = false; - if (bio.Boobs === 'Natural') profile.naturalBoobs = true; + if (bio.Boobs === 'Enhanced') profile.naturalBoobs = false; + if (bio.Boobs === 'Natural') profile.naturalBoobs = true; - if (bio.Height) profile.height = Number(bio.Height.match(/\d+\.\d+/g).slice(-1)[0]) * 100; - if (bio.Weight) profile.weight = Number(bio.Weight.match(/\d+/g)[1]); + if (bio.Height) profile.height = Number(bio.Height.match(/\d+\.\d+/g).slice(-1)[0]) * 100; + if (bio.Weight) profile.weight = Number(bio.Weight.match(/\d+/g)[1]); - if (bio.Eye_color) profile.eyes = bio.Eye_color; - if (bio.Hair) [profile.hair] = bio.Hair.split(','); + if (bio.Eye_color) profile.eyes = bio.Eye_color; + if (bio.Hair) [profile.hair] = bio.Hair.split(','); - if (bio.Blood_group) profile.blood = bio.Blood_group; - if (bio.Also_known_as) profile.aliases = bio.Also_known_as.split(', '); + if (bio.Blood_group) profile.blood = bio.Blood_group; + if (bio.Also_known_as) profile.aliases = bio.Also_known_as.split(', '); - const avatarThumbPath = qu.img('.image img'); + const avatarThumbPath = qu.img('.image img'); - if (avatarThumbPath && !/NoImageAvailable/.test(avatarThumbPath)) { - const avatarPath = avatarThumbPath.slice(0, avatarThumbPath.lastIndexOf('/')).replace('thumb/', ''); + if (avatarThumbPath && !/NoImageAvailable/.test(avatarThumbPath)) { + const avatarPath = avatarThumbPath.slice(0, avatarThumbPath.lastIndexOf('/')).replace('thumb/', ''); - profile.avatar = { - src: `http://www.boobpedia.com${avatarPath}`, - copyright: null, - }; - } + profile.avatar = { + src: `http://www.boobpedia.com${avatarPath}`, + copyright: null, + }; + } - profile.social = qu.urls('.infobox a.external'); + profile.social = qu.urls('.infobox a.external'); - return profile; + return profile; } async function fetchProfile(actorName) { - const actorSlug = actorName.replace(/\s+/, '_'); - const res = await bhttp.get(`http://www.boobpedia.com/boobs/${actorSlug}`); + const actorSlug = actorName.replace(/\s+/, '_'); + const res = await bhttp.get(`http://www.boobpedia.com/boobs/${actorSlug}`); - if (res.statusCode === 200) { - return scrapeProfile(res.body.toString()); - } + if (res.statusCode === 200) { + return scrapeProfile(res.body.toString()); + } - return null; + return null; } module.exports = { - fetchProfile, + fetchProfile, }; diff --git a/src/scrapers/brazzers.js b/src/scrapers/brazzers.js index 36a24fbf..9aebfa24 100644 --- a/src/scrapers/brazzers.js +++ b/src/scrapers/brazzers.js @@ -11,216 +11,216 @@ const slugify = require('../utils/slugify'); const { heightToCm, lbsToKg } = require('../utils/convert'); const hairMap = { - Blonde: 'blonde', - Brunette: 'brown', - 'Black Hair': 'black', - Redhead: 'red', + Blonde: 'blonde', + Brunette: 'brown', + 'Black Hair': 'black', + Redhead: 'red', }; function scrapeAll(html, site, upcoming) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const sceneElements = $('.release-card.scene').toArray(); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const sceneElements = $('.release-card.scene').toArray(); - return sceneElements.reduce((acc, element) => { - const isUpcoming = $(element).find('.icon-upcoming.active').length === 1; + return sceneElements.reduce((acc, element) => { + const isUpcoming = $(element).find('.icon-upcoming.active').length === 1; - if ((upcoming && !isUpcoming) || (!upcoming && isUpcoming)) { - return acc; - } + if ((upcoming && !isUpcoming) || (!upcoming && isUpcoming)) { + return acc; + } - const sceneLinkElement = $(element).find('a'); + const sceneLinkElement = $(element).find('a'); - const url = `https://www.brazzers.com${sceneLinkElement.attr('href')}`; - const title = sceneLinkElement.attr('title'); - const entryId = url.split('/').slice(-3, -2)[0]; + const url = `https://www.brazzers.com${sceneLinkElement.attr('href')}`; + const title = sceneLinkElement.attr('title'); + const entryId = url.split('/').slice(-3, -2)[0]; - const date = moment.utc($(element).find('time').text(), 'MMMM DD, YYYY').toDate(); - const actors = $(element).find('.model-names a').map((actorIndex, actorElement) => $(actorElement).attr('title')).toArray(); + const date = moment.utc($(element).find('time').text(), 'MMMM DD, YYYY').toDate(); + const actors = $(element).find('.model-names a').map((actorIndex, actorElement) => $(actorElement).attr('title')).toArray(); - const likes = Number($(element).find('.label-rating .like-amount').text()); - const dislikes = Number($(element).find('.label-rating .dislike-amount').text()); + const likes = Number($(element).find('.label-rating .like-amount').text()); + const dislikes = Number($(element).find('.label-rating .dislike-amount').text()); - const poster = `https:${$(element).find('.card-main-img').attr('data-src')}`; - const photos = $(element).find('.card-overlay .image-under').map((photoIndex, photoElement) => `https:${$(photoElement).attr('data-src')}`).toArray(); + const poster = `https:${$(element).find('.card-main-img').attr('data-src')}`; + const photos = $(element).find('.card-overlay .image-under').map((photoIndex, photoElement) => `https:${$(photoElement).attr('data-src')}`).toArray(); - const channel = slugify($(element).find('.collection').attr('title'), ''); + const channel = slugify($(element).find('.collection').attr('title'), ''); - return acc.concat({ - url, - entryId, - title, - actors, - date, - poster, - photos, - rating: { - likes, - dislikes, - }, - channel, - site, - }); - }, []); + return acc.concat({ + url, + entryId, + title, + actors, + date, + poster, + photos, + rating: { + likes, + dislikes, + }, + channel, + site, + }); + }, []); } async function scrapeScene(html, url, _site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const release = {}; + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const release = {}; - const videoJson = $('script:contains("window.videoUiOptions")').html(); - const videoString = videoJson.slice(videoJson.indexOf('{"stream_info":'), videoJson.lastIndexOf('},') + 1); - const videoData = JSON.parse(videoString); + const videoJson = $('script:contains("window.videoUiOptions")').html(); + const videoString = videoJson.slice(videoJson.indexOf('{"stream_info":'), videoJson.lastIndexOf('},') + 1); + const videoData = JSON.parse(videoString); - [release.entryId] = url.split('/').slice(-3, -2); - release.title = $('.scene-title[itemprop="name"]').text(); + [release.entryId] = url.split('/').slice(-3, -2); + release.title = $('.scene-title[itemprop="name"]').text(); - release.description = $('#scene-description p[itemprop="description"]') - .contents() - .first() - .text() - .trim(); + release.description = $('#scene-description p[itemprop="description"]') + .contents() + .first() + .text() + .trim(); - release.date = moment.utc($('.more-scene-info .scene-date').text(), 'MMMM DD, YYYY').toDate(); - release.duration = Number($('.scene-length[itemprop="duration"]').attr('content').slice(1, -1)) * 60; + release.date = moment.utc($('.more-scene-info .scene-date').text(), 'MMMM DD, YYYY').toDate(); + release.duration = Number($('.scene-length[itemprop="duration"]').attr('content').slice(1, -1)) * 60; - const actorsFromCards = $('.featured-model .card-image a').map((actorIndex, actorElement) => { - const avatar = `https:${$(actorElement).find('img').attr('data-src')}`; + const actorsFromCards = $('.featured-model .card-image a').map((actorIndex, actorElement) => { + const avatar = `https:${$(actorElement).find('img').attr('data-src')}`; - return { - name: $(actorElement).attr('title'), - avatar: [avatar.replace('medium.jpg', 'large.jpg'), avatar], - }; - }).toArray(); + return { + name: $(actorElement).attr('title'), + avatar: [avatar.replace('medium.jpg', 'large.jpg'), avatar], + }; + }).toArray(); - release.actors = actorsFromCards || $('.related-model a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); + release.actors = actorsFromCards || $('.related-model a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); - release.likes = Number($('.label-rating .like').text()); - release.dislikes = Number($('.label-rating .dislike').text()); + release.likes = Number($('.label-rating .like').text()); + release.dislikes = Number($('.label-rating .dislike').text()); - const siteElement = $('.niche-site-logo'); - // const siteUrl = `https://www.brazzers.com${siteElement.attr('href').slice(0, -1)}`; - const siteName = siteElement.attr('title'); - release.channel = siteName.replace(/\s+/g, '').toLowerCase(); + const siteElement = $('.niche-site-logo'); + // const siteUrl = `https://www.brazzers.com${siteElement.attr('href').slice(0, -1)}`; + const siteName = siteElement.attr('title'); + release.channel = siteName.replace(/\s+/g, '').toLowerCase(); - release.tags = $('.tag-card-container a').map((tagIndex, tagElement) => $(tagElement).text()).toArray(); - release.photos = $('.carousel-thumb a').map((photoIndex, photoElement) => `https:${$(photoElement).attr('href')}`).toArray(); + release.tags = $('.tag-card-container a').map((tagIndex, tagElement) => $(tagElement).text()).toArray(); + release.photos = $('.carousel-thumb a').map((photoIndex, photoElement) => `https:${$(photoElement).attr('href')}`).toArray(); - const posterPath = videoData?.poster || $('meta[itemprop="thumbnailUrl"]').attr('content') || $('#trailer-player-container').attr('data-player-img'); - if (posterPath) release.poster = `https:${posterPath}`; + const posterPath = videoData?.poster || $('meta[itemprop="thumbnailUrl"]').attr('content') || $('#trailer-player-container').attr('data-player-img'); + if (posterPath) release.poster = `https:${posterPath}`; - if (videoData) { - release.trailer = Object.entries(videoData.stream_info.http.paths).map(([quality, path]) => ({ - src: `https:${path}`, - quality: Number(quality.match(/\d{3,}/)[0]), - })); - } + if (videoData) { + release.trailer = Object.entries(videoData.stream_info.http.paths).map(([quality, path]) => ({ + src: `https:${path}`, + quality: Number(quality.match(/\d{3,}/)[0]), + })); + } - return release; + return release; } function scrapeActorSearch(html, url, actorName) { - const { document } = new JSDOM(html).window; - const actorLink = document.querySelector(`a[title="${actorName}" i]`); + const { document } = new JSDOM(html).window; + const actorLink = document.querySelector(`a[title="${actorName}" i]`); - return actorLink ? actorLink.href : null; + return actorLink ? actorLink.href : null; } async function fetchActorReleases({ qu, html }, accReleases = []) { - const releases = scrapeAll(html); - const next = qu.url('.pagination .next a'); + const releases = scrapeAll(html); + const next = qu.url('.pagination .next a'); - if (next) { - const url = `https://www.brazzers.com${next}`; - const res = await get(url); + if (next) { + const url = `https://www.brazzers.com${next}`; + const res = await get(url); - if (res.ok) { - return fetchActorReleases(res.item, accReleases.concat(releases)); - } - } + if (res.ok) { + return fetchActorReleases(res.item, accReleases.concat(releases)); + } + } - return accReleases.concat(releases); + return accReleases.concat(releases); } async function scrapeProfile(html, url, actorName) { - const qProfile = ex(html); - const { q, qa } = qProfile; + const qProfile = ex(html); + const { q, qa } = qProfile; - const bioKeys = qa('.profile-spec-list label', true).map(key => key.replace(/\n+|\s{2,}/g, '').trim()); - const bioValues = qa('.profile-spec-list var', true).map(value => value.replace(/\n+|\s{2,}/g, '').trim()); + const bioKeys = qa('.profile-spec-list label', true).map(key => key.replace(/\n+|\s{2,}/g, '').trim()); + const bioValues = qa('.profile-spec-list var', true).map(value => value.replace(/\n+|\s{2,}/g, '').trim()); - const bio = bioKeys.reduce((acc, key, index) => ({ ...acc, [key]: bioValues[index] }), {}); + const bio = bioKeys.reduce((acc, key, index) => ({ ...acc, [key]: bioValues[index] }), {}); - const profile = { - name: actorName, - }; + const profile = { + name: actorName, + }; - profile.description = q('.model-profile-specs p', true); + profile.description = q('.model-profile-specs p', true); - if (bio.Ethnicity) profile.ethnicity = bio.Ethnicity; - if (bio.Measurements && bio.Measurements.match(/\d+[A-Z]+-\d+-\d+/)) [profile.bust, profile.waist, profile.hip] = bio.Measurements.split('-'); - if (bio['Date of Birth'] && bio['Date of Birth'] !== 'Unknown') profile.birthdate = moment.utc(bio['Date of Birth'], 'MMMM DD, YYYY').toDate(); - if (bio['Birth Location']) profile.birthPlace = bio['Birth Location']; - if (bio['Pussy Type']) profile.pussy = bio['Pussy Type'].split(',').slice(-1)[0].toLowerCase(); + if (bio.Ethnicity) profile.ethnicity = bio.Ethnicity; + if (bio.Measurements && bio.Measurements.match(/\d+[A-Z]+-\d+-\d+/)) [profile.bust, profile.waist, profile.hip] = bio.Measurements.split('-'); + if (bio['Date of Birth'] && bio['Date of Birth'] !== 'Unknown') profile.birthdate = moment.utc(bio['Date of Birth'], 'MMMM DD, YYYY').toDate(); + if (bio['Birth Location']) profile.birthPlace = bio['Birth Location']; + if (bio['Pussy Type']) profile.pussy = bio['Pussy Type'].split(',').slice(-1)[0].toLowerCase(); - if (bio.Height) profile.height = heightToCm(bio.Height); - if (bio.Weight) profile.weight = lbsToKg(bio.Weight.match(/\d+/)[0]); - if (bio['Hair Color']) profile.hair = hairMap[bio['Hair Color']] || bio['Hair Color'].toLowerCase(); + if (bio.Height) profile.height = heightToCm(bio.Height); + if (bio.Weight) profile.weight = lbsToKg(bio.Weight.match(/\d+/)[0]); + if (bio['Hair Color']) profile.hair = hairMap[bio['Hair Color']] || bio['Hair Color'].toLowerCase(); - if (bio['Tits Type'] && bio['Tits Type'].match('Natural')) profile.naturalBoobs = true; - if (bio['Tits Type'] && bio['Tits Type'].match('Enhanced')) profile.naturalBoobs = false; + if (bio['Tits Type'] && bio['Tits Type'].match('Natural')) profile.naturalBoobs = true; + if (bio['Tits Type'] && bio['Tits Type'].match('Enhanced')) profile.naturalBoobs = false; - if (bio['Body Art'] && bio['Body Art'].match('Tattoo')) profile.hasTattoos = true; - if (bio['Body Art'] && bio['Body Art'].match('Piercing')) profile.hasPiercings = true; + if (bio['Body Art'] && bio['Body Art'].match('Tattoo')) profile.hasTattoos = true; + if (bio['Body Art'] && bio['Body Art'].match('Piercing')) profile.hasPiercings = true; - const avatarEl = q('.big-pic-model-container img'); - if (avatarEl) profile.avatar = `https:${avatarEl.src}`; + const avatarEl = q('.big-pic-model-container img'); + if (avatarEl) profile.avatar = `https:${avatarEl.src}`; - profile.releases = await fetchActorReleases(qProfile); + profile.releases = await fetchActorReleases(qProfile); - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const res = await bhttp.get(`${site.url}/page/${page}/`); + const res = await bhttp.get(`${site.url}/page/${page}/`); - return scrapeAll(res.body.toString(), site, false); + return scrapeAll(res.body.toString(), site, false); } async function fetchUpcoming(site) { - const res = await bhttp.get(`${site.url}/`); + const res = await bhttp.get(`${site.url}/`); - return scrapeAll(res.body.toString(), site, true); + return scrapeAll(res.body.toString(), site, true); } async function fetchScene(url, site) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - return scrapeScene(res.body.toString(), url, site); + return scrapeScene(res.body.toString(), url, site); } async function fetchProfile(actorName) { - const searchUrl = 'https://brazzers.com/pornstars-search/'; - const searchRes = await bhttp.get(searchUrl, { - headers: { - Cookie: `textSearch=${encodeURIComponent(actorName)};`, - }, - }); + const searchUrl = 'https://brazzers.com/pornstars-search/'; + const searchRes = await bhttp.get(searchUrl, { + headers: { + Cookie: `textSearch=${encodeURIComponent(actorName)};`, + }, + }); - const actorLink = scrapeActorSearch(searchRes.body.toString(), searchUrl, actorName); + const actorLink = scrapeActorSearch(searchRes.body.toString(), searchUrl, actorName); - if (actorLink) { - const url = `https://brazzers.com${actorLink}`; - const res = await bhttp.get(url); + if (actorLink) { + const url = `https://brazzers.com${actorLink}`; + const res = await bhttp.get(url); - return scrapeProfile(res.body.toString(), url, actorName); - } + return scrapeProfile(res.body.toString(), url, actorName); + } - return null; + return null; } module.exports = { - fetchLatest, - fetchProfile, - fetchScene, - fetchUpcoming, + fetchLatest, + fetchProfile, + fetchScene, + fetchUpcoming, }; diff --git a/src/scrapers/burningangel.js b/src/scrapers/burningangel.js index c31fa6e8..562acaa6 100644 --- a/src/scrapers/burningangel.js +++ b/src/scrapers/burningangel.js @@ -3,8 +3,8 @@ const { fetchApiLatest, fetchApiUpcoming, fetchScene, fetchApiProfile } = require('./gamma'); module.exports = { - fetchLatest: fetchApiLatest, - fetchProfile: fetchApiProfile, - fetchScene, - fetchUpcoming: fetchApiUpcoming, + fetchLatest: fetchApiLatest, + fetchProfile: fetchApiProfile, + fetchScene, + fetchUpcoming: fetchApiUpcoming, }; diff --git a/src/scrapers/cherrypimps.js b/src/scrapers/cherrypimps.js index 7f573957..b65be552 100644 --- a/src/scrapers/cherrypimps.js +++ b/src/scrapers/cherrypimps.js @@ -4,139 +4,139 @@ const { get, geta, ctxa, ed } = require('../utils/q'); const slugify = require('../utils/slugify'); function scrapeAll(scenes, site) { - return scenes.map(({ qu }) => { - const url = qu.url('.text-thumb a'); - const { pathname } = new URL(url); - const channelUrl = qu.url('.badge'); + return scenes.map(({ qu }) => { + const url = qu.url('.text-thumb a'); + const { pathname } = new URL(url); + const channelUrl = qu.url('.badge'); - if (site?.parameters?.extract && qu.q('.badge', true) !== site.name) { - return null; - } + if (site?.parameters?.extract && qu.q('.badge', true) !== site.name) { + return null; + } - const release = {}; + const release = {}; - release.url = channelUrl ? `${channelUrl}${pathname}` : url; - release.entryId = pathname.match(/\/\d+/)[0].slice(1); - release.title = qu.q('.text-thumb a', true); + release.url = channelUrl ? `${channelUrl}${pathname}` : url; + release.entryId = pathname.match(/\/\d+/)[0].slice(1); + release.title = qu.q('.text-thumb a', true); - release.date = qu.date('.date', 'YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/); - release.duration = qu.dur('.date', /(\d{2}:)?\d{2}:\d{2}/); + release.date = qu.date('.date', 'YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/); + release.duration = qu.dur('.date', /(\d{2}:)?\d{2}:\d{2}/); - release.actors = qu.all('.category a', true); + release.actors = qu.all('.category a', true); - release.poster = qu.img('img.video_placeholder, .video-images img'); - release.teaser = { src: qu.trailer() }; + release.poster = qu.img('img.video_placeholder, .video-images img'); + release.teaser = { src: qu.trailer() }; - return release; - }).filter(Boolean); + return release; + }).filter(Boolean); } function scrapeScene({ q, qd, qa }, url, _site, baseRelease) { - const release = { url }; + const release = { url }; - const { pathname } = new URL(url); - release.entryId = pathname.match(/\/\d+/)[0].slice(1); + const { pathname } = new URL(url); + release.entryId = pathname.match(/\/\d+/)[0].slice(1); - release.title = q('.trailer-block_title', true); - release.description = q('.info-block:nth-child(3) .text', true); - release.date = qd('.info-block_data .text', 'MMMM D, YYYY', /\w+ \d{1,2}, \d{4}/); + release.title = q('.trailer-block_title', true); + release.description = q('.info-block:nth-child(3) .text', true); + release.date = qd('.info-block_data .text', 'MMMM D, YYYY', /\w+ \d{1,2}, \d{4}/); - const duration = baseRelease?.duration || Number(q('.info-block_data .text', true).match(/(\d+)\s+min/)?.[1]) * 60; - if (duration) release.duration = duration; + const duration = baseRelease?.duration || Number(q('.info-block_data .text', true).match(/(\d+)\s+min/)?.[1]) * 60; + if (duration) release.duration = duration; - release.actors = qa('.info-block_data a[href*="/models"]', true); - release.tags = qa('.info-block a[href*="/categories"]', true); + release.actors = qa('.info-block_data a[href*="/models"]', true); + release.tags = qa('.info-block a[href*="/categories"]', true); - const posterEl = q('.update_thumb'); - const poster = posterEl.getAttribute('src0_3x') || posterEl.getAttribute('src0_2x') || posterEl.dataset.src; + const posterEl = q('.update_thumb'); + const poster = posterEl.getAttribute('src0_3x') || posterEl.getAttribute('src0_2x') || posterEl.dataset.src; - if (poster && baseRelease?.poster) release.photos = [poster]; - else if (poster) release.poster = poster; + if (poster && baseRelease?.poster) release.photos = [poster]; + else if (poster) release.poster = poster; - return release; + return release; } function scrapeProfile({ q, qa, qtx }) { - const profile = {}; + const profile = {}; - const keys = qa('.model-descr_line:not(.model-descr_rait) p.text span', true); - const values = qa('.model-descr_line:not(.model-descr_rait) p.text').map(el => qtx(el)); - const bio = keys.reduce((acc, key, index) => ({ ...acc, [slugify(key, '_')]: values[index] }), {}); + const keys = qa('.model-descr_line:not(.model-descr_rait) p.text span', true); + const values = qa('.model-descr_line:not(.model-descr_rait) p.text').map(el => qtx(el)); + const bio = keys.reduce((acc, key, index) => ({ ...acc, [slugify(key, '_')]: values[index] }), {}); - if (bio.height) profile.height = Number(bio.height.match(/\((\d+)cm\)/)[1]); - if (bio.weight) profile.weight = Number(bio.weight.match(/\((\d+)kg\)/)[1]); - if (bio.race) profile.ethnicity = bio.race; + if (bio.height) profile.height = Number(bio.height.match(/\((\d+)cm\)/)[1]); + if (bio.weight) profile.weight = Number(bio.weight.match(/\((\d+)kg\)/)[1]); + if (bio.race) profile.ethnicity = bio.race; - if (bio.date_of_birth) profile.birthdate = ed(bio.date_of_birth, 'MMMM D, YYYY'); - if (bio.birthplace) profile.birthPlace = bio.birthplace; + if (bio.date_of_birth) profile.birthdate = ed(bio.date_of_birth, 'MMMM D, YYYY'); + if (bio.birthplace) profile.birthPlace = bio.birthplace; - if (bio.measurements) { - const [bust, waist, hip] = bio.measurements.split('-'); - if (!/\?/.test(bust)) profile.bust = bust; - if (!/\?/.test(waist)) profile.waist = waist; - if (!/\?/.test(hip)) profile.hip = hip; - } + if (bio.measurements) { + const [bust, waist, hip] = bio.measurements.split('-'); + if (!/\?/.test(bust)) profile.bust = bust; + if (!/\?/.test(waist)) profile.waist = waist; + if (!/\?/.test(hip)) profile.hip = hip; + } - if (bio.hair) profile.hair = bio.hair; - if (bio.eyes) profile.eyes = bio.eyes; + if (bio.hair) profile.hair = bio.hair; + if (bio.eyes) profile.eyes = bio.eyes; - if (/various/i.test(bio.tattoos)) profile.hasTattoos = true; - else if (/none/i.test(bio.tattoos)) profile.hasTattoos = false; - else if (bio.tattoos) { - profile.hasTattoos = true; - profile.tattoos = bio.tattoos; - } + if (/various/i.test(bio.tattoos)) profile.hasTattoos = true; + else if (/none/i.test(bio.tattoos)) profile.hasTattoos = false; + else if (bio.tattoos) { + profile.hasTattoos = true; + profile.tattoos = bio.tattoos; + } - if (/various/i.test(bio.piercings)) profile.hasPiercings = true; - else if (/none/i.test(bio.piercings)) profile.hasPiercings = false; - else if (bio.piercings) { - profile.hasPiercings = true; - profile.piercings = bio.piercings; - } + if (/various/i.test(bio.piercings)) profile.hasPiercings = true; + else if (/none/i.test(bio.piercings)) profile.hasPiercings = false; + else if (bio.piercings) { + profile.hasPiercings = true; + profile.piercings = bio.piercings; + } - if (bio.aliases) profile.aliases = bio.aliases.split(',').map(alias => alias.trim()); + if (bio.aliases) profile.aliases = bio.aliases.split(',').map(alias => alias.trim()); - const avatar = q('.model-img img'); - profile.avatar = avatar.getAttribute('src0_3x') || avatar.getAttribute('src0_2x') || avatar.dataset.src; + const avatar = q('.model-img img'); + profile.avatar = avatar.getAttribute('src0_3x') || avatar.getAttribute('src0_2x') || avatar.dataset.src; - const releases = qa('.video-thumb'); - profile.releases = scrapeAll(ctxa(releases)); + const releases = qa('.video-thumb'); + profile.releases = scrapeAll(ctxa(releases)); - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const url = site.parameters?.extract - ? `https://cherrypimps.com/categories/movies_${page}.html` - : `${site.url}/categories/movies_${page}.html`; - const res = await geta(url, 'div.video-thumb'); + const url = site.parameters?.extract + ? `https://cherrypimps.com/categories/movies_${page}.html` + : `${site.url}/categories/movies_${page}.html`; + const res = await geta(url, 'div.video-thumb'); - return res.ok ? scrapeAll(res.items, site) : res.status; + return res.ok ? scrapeAll(res.items, site) : res.status; } async function fetchScene(url, site, release) { - const res = await get(url); + const res = await get(url); - return res.ok ? scrapeScene(res.item, url, site, release) : res.status; + return res.ok ? scrapeScene(res.item, url, site, release) : res.status; } async function fetchProfile(actorName, scraperSlug) { - const actorSlug = slugify(actorName); - const actorSlug2 = slugify(actorName, ''); + const actorSlug = slugify(actorName); + const actorSlug2 = slugify(actorName, ''); - const [url, url2] = ['cherrypimps', 'wildoncam'].includes(scraperSlug) - ? [`https://${scraperSlug}.com/models/${actorSlug}.html`, `https://${scraperSlug}.com/models/${actorSlug2}.html`] - : [`https://${scraperSlug.replace('xxx', '')}.xxx/models/${actorSlug}.html`, `https://${scraperSlug.replace('xxx', '')}.xxx/models/${actorSlug2}.html`]; + const [url, url2] = ['cherrypimps', 'wildoncam'].includes(scraperSlug) + ? [`https://${scraperSlug}.com/models/${actorSlug}.html`, `https://${scraperSlug}.com/models/${actorSlug2}.html`] + : [`https://${scraperSlug.replace('xxx', '')}.xxx/models/${actorSlug}.html`, `https://${scraperSlug.replace('xxx', '')}.xxx/models/${actorSlug2}.html`]; - const res = await get(url); - if (res.ok) return scrapeProfile(res.item); + const res = await get(url); + if (res.ok) return scrapeProfile(res.item); - const res2 = await get(url2); - return res2.ok ? scrapeProfile(res2.item) : res2.status; + const res2 = await get(url2); + return res2.ok ? scrapeProfile(res2.item) : res2.status; } module.exports = { - fetchLatest, - fetchScene, - fetchProfile, + fetchLatest, + fetchScene, + fetchProfile, }; diff --git a/src/scrapers/ddfnetwork.js b/src/scrapers/ddfnetwork.js index ab70ccb9..00d64fac 100644 --- a/src/scrapers/ddfnetwork.js +++ b/src/scrapers/ddfnetwork.js @@ -7,182 +7,182 @@ const slugify = require('../utils/slugify'); /* eslint-disable newline-per-chained-call */ function scrapeAll(html, site, origin) { - return exa(html, '.card.m-1:not(.pornstar-card)').map(({ q, qa, qd }) => { - const release = {}; + return exa(html, '.card.m-1:not(.pornstar-card)').map(({ q, qa, qd }) => { + const release = {}; - release.title = q('a', 'title'); - release.url = `${site?.url || origin || 'https://ddfnetwork.com'}${q('a', 'href')}`; - [release.entryId] = release.url.split('/').slice(-1); + release.title = q('a', 'title'); + release.url = `${site?.url || origin || 'https://ddfnetwork.com'}${q('a', 'href')}`; + [release.entryId] = release.url.split('/').slice(-1); - release.date = qd('small[datetime]', 'YYYY-MM-DD HH:mm:ss', null, 'datetime'); - release.actors = qa('.card-subtitle a', true).filter(Boolean); + release.date = qd('small[datetime]', 'YYYY-MM-DD HH:mm:ss', null, 'datetime'); + release.actors = qa('.card-subtitle a', true).filter(Boolean); - const duration = parseInt(q('.card-info div:nth-child(2) .card-text', true), 10) * 60; - if (duration) release.duration = duration; + const duration = parseInt(q('.card-info div:nth-child(2) .card-text', true), 10) * 60; + if (duration) release.duration = duration; - release.poster = q('img').dataset.src; + release.poster = q('img').dataset.src; - return release; - }); + return release; + }); } async function scrapeScene(html, url, _site) { - const { qu } = ex(html); - const release = {}; + const { qu } = ex(html); + const release = {}; - [release.entryId] = url.split('/').slice(-1); + [release.entryId] = url.split('/').slice(-1); - release.title = qu.meta('itemprop=name'); - release.description = qu.q('.descr-box p', true); - release.date = qu.date('meta[itemprop=uploadDate]', 'YYYY-MM-DD', null, 'content') + release.title = qu.meta('itemprop=name'); + release.description = qu.q('.descr-box p', true); + release.date = qu.date('meta[itemprop=uploadDate]', 'YYYY-MM-DD', null, 'content') || qu.date('.title-border:nth-child(2) p', 'MM.DD.YYYY'); - release.actors = qu.all('.pornstar-card > a', 'title'); - release.tags = qu.all('.tags-tab .tags a', true); + release.actors = qu.all('.pornstar-card > a', 'title'); + release.tags = qu.all('.tags-tab .tags a', true); - release.duration = parseInt(qu.q('.icon-video-red + span', true), 10) * 60; - release.likes = Number(qu.q('.icon-like-red + span', true)); + release.duration = parseInt(qu.q('.icon-video-red + span', true), 10) * 60; + release.likes = Number(qu.q('.icon-like-red + span', true)); - release.poster = qu.poster(); - release.photos = qu.urls('.photo-slider-guest .card a'); + release.poster = qu.poster(); + release.photos = qu.urls('.photo-slider-guest .card a'); - release.trailer = qu.all('source[type="video/mp4"]').map(trailer => ({ - src: trailer.src, - quality: Number(trailer.attributes.res.value), - })); + release.trailer = qu.all('source[type="video/mp4"]').map(trailer => ({ + src: trailer.src, + quality: Number(trailer.attributes.res.value), + })); - return release; + return release; } async function fetchActorReleases(urls) { - // DDF Network and DDF Network Stream list all scenes, exclude - const sources = urls.filter(url => !/ddfnetwork/.test(url)); + // DDF Network and DDF Network Stream list all scenes, exclude + const sources = urls.filter(url => !/ddfnetwork/.test(url)); - const releases = await Promise.all(sources.map(async (url) => { - const { html } = await get(url); + const releases = await Promise.all(sources.map(async (url) => { + const { html } = await get(url); - return scrapeAll(html, null, new URL(url).origin); - })); + return scrapeAll(html, null, new URL(url).origin); + })); - // DDF cross-releases scenes between sites, filter duplicates by entryId - return Object.values(releases - .flat() - .sort((releaseA, releaseB) => releaseB.date - releaseA.date) // sort by date so earliest scene remains - .reduce((acc, release) => ({ ...acc, [release.entryId]: release }), {})); + // DDF cross-releases scenes between sites, filter duplicates by entryId + return Object.values(releases + .flat() + .sort((releaseA, releaseB) => releaseB.date - releaseA.date) // sort by date so earliest scene remains + .reduce((acc, release) => ({ ...acc, [release.entryId]: release }), {})); } async function scrapeProfile(html, _url, actorName) { - const { qu } = ex(html); + const { qu } = ex(html); - const keys = qu.all('.about-title', true).map(key => slugify(key, '_')); - const values = qu.all('.about-info').map((el) => { - if (el.children.length > 0) { - return Array.from(el.children, child => child.textContent.trim()).join(', '); - } + const keys = qu.all('.about-title', true).map(key => slugify(key, '_')); + const values = qu.all('.about-info').map((el) => { + if (el.children.length > 0) { + return Array.from(el.children, child => child.textContent.trim()).join(', '); + } - return el.textContent.trim(); - }); + return el.textContent.trim(); + }); - const bio = keys.reduce((acc, key, index) => { - if (values[index] === '-') return acc; + const bio = keys.reduce((acc, key, index) => { + if (values[index] === '-') return acc; - return { - ...acc, - [key]: values[index], - }; - }, {}); + return { + ...acc, + [key]: values[index], + }; + }, {}); - const profile = { - name: actorName, - }; + const profile = { + name: actorName, + }; - profile.description = qu.q('.description-box', true); - profile.birthdate = ed(bio.birthday, 'MMMM DD, YYYY'); + profile.description = qu.q('.description-box', true); + profile.birthdate = ed(bio.birthday, 'MMMM DD, YYYY'); - if (bio.nationality) profile.nationality = bio.nationality; + if (bio.nationality) profile.nationality = bio.nationality; - if (bio.bra_size) [profile.bust] = bio.bra_size.match(/\d+\w+/); - if (bio.waist) profile.waist = Number(bio.waist.match(/\d+/)[0]); - if (bio.hips) profile.hip = Number(bio.hips.match(/\d+/)[0]); + if (bio.bra_size) [profile.bust] = bio.bra_size.match(/\d+\w+/); + if (bio.waist) profile.waist = Number(bio.waist.match(/\d+/)[0]); + if (bio.hips) profile.hip = Number(bio.hips.match(/\d+/)[0]); - if (bio.height) profile.height = Number(bio.height.match(/\d{2,}/)[0]); + if (bio.height) profile.height = Number(bio.height.match(/\d{2,}/)[0]); - if (bio.tit_style && /Enhanced/.test(bio.tit_style)) profile.naturalBoobs = false; - if (bio.tit_style && /Natural/.test(bio.tit_style)) profile.naturalBoobs = true; + if (bio.tit_style && /Enhanced/.test(bio.tit_style)) profile.naturalBoobs = false; + if (bio.tit_style && /Natural/.test(bio.tit_style)) profile.naturalBoobs = true; - if (bio.body_art && /Tattoo/.test(bio.body_art)) profile.hasTattoos = true; - if (bio.body_art && /Piercing/.test(bio.body_art)) profile.hasPiercings = true; + if (bio.body_art && /Tattoo/.test(bio.body_art)) profile.hasTattoos = true; + if (bio.body_art && /Piercing/.test(bio.body_art)) profile.hasPiercings = true; - if (bio.hair_style) profile.hair = bio.hair_style.split(',')[0].trim().toLowerCase(); - if (bio.eye_color) profile.eyes = bio.eye_color.match(/\w+/)[0].toLowerCase(); + if (bio.hair_style) profile.hair = bio.hair_style.split(',')[0].trim().toLowerCase(); + if (bio.eye_color) profile.eyes = bio.eye_color.match(/\w+/)[0].toLowerCase(); - if (bio.shoe_size) profile.shoes = Number(bio.shoe_size.split('|')[1]); + if (bio.shoe_size) profile.shoes = Number(bio.shoe_size.split('|')[1]); - const avatarEl = qu.q('.pornstar-details .card-img-top'); - if (avatarEl && avatarEl.dataset.src.match('^//')) profile.avatar = `https:${avatarEl.dataset.src}`; + const avatarEl = qu.q('.pornstar-details .card-img-top'); + if (avatarEl && avatarEl.dataset.src.match('^//')) profile.avatar = `https:${avatarEl.dataset.src}`; - profile.releases = await fetchActorReleases(qu.urls('.find-me-tab li a')); + profile.releases = await fetchActorReleases(qu.urls('.find-me-tab li a')); - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const url = site.parameters?.native - ? `${site.url}/videos/search/latest/ever/allsite/-/${page}` - : `https://ddfnetwork.com/videos/search/latest/ever/${new URL(site.url).hostname}/-/${page}`; + const url = site.parameters?.native + ? `${site.url}/videos/search/latest/ever/allsite/-/${page}` + : `https://ddfnetwork.com/videos/search/latest/ever/${new URL(site.url).hostname}/-/${page}`; - const res = await bhttp.get(url); + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeAll(res.body.toString(), site); - } + if (res.statusCode === 200) { + return scrapeAll(res.body.toString(), site); + } - return res.statusCode; + return res.statusCode; } async function fetchScene(url, site) { - // DDF's main site moved to Porn World - // const res = await bhttp.get(`https://ddfnetwork.com${new URL(url).pathname}`); - const res = await bhttp.get(url); + // DDF's main site moved to Porn World + // const res = await bhttp.get(`https://ddfnetwork.com${new URL(url).pathname}`); + const res = await bhttp.get(url); - return scrapeScene(res.body.toString(), url, site); + return scrapeScene(res.body.toString(), url, site); } async function fetchProfile(actorName) { - const resSearch = await bhttp.post('https://ddfnetwork.com/search/ajax', - { - type: 'hints', - word: actorName, - }, - { - decodeJSON: true, - headers: { - 'x-requested-with': 'XMLHttpRequest', - }, - }); + const resSearch = await bhttp.post('https://ddfnetwork.com/search/ajax', + { + type: 'hints', + word: actorName, + }, + { + decodeJSON: true, + headers: { + 'x-requested-with': 'XMLHttpRequest', + }, + }); - if (resSearch.statusCode !== 200 || Array.isArray(resSearch.body.list)) { - return null; - } + if (resSearch.statusCode !== 200 || Array.isArray(resSearch.body.list)) { + return null; + } - if (!resSearch.body.list.pornstarsName || resSearch.body.list.pornstarsName.length === 0) { - return null; - } + if (!resSearch.body.list.pornstarsName || resSearch.body.list.pornstarsName.length === 0) { + return null; + } - const [actor] = resSearch.body.list.pornstarsName; - const url = `https://ddfnetwork.com${actor.href}`; + const [actor] = resSearch.body.list.pornstarsName; + const url = `https://ddfnetwork.com${actor.href}`; - const resActor = await bhttp.get(url); + const resActor = await bhttp.get(url); - if (resActor.statusCode !== 200) { - return null; - } + if (resActor.statusCode !== 200) { + return null; + } - return scrapeProfile(resActor.body.toString(), url, actorName); + return scrapeProfile(resActor.body.toString(), url, actorName); } module.exports = { - fetchLatest, - fetchProfile, - fetchScene, + fetchLatest, + fetchProfile, + fetchScene, }; diff --git a/src/scrapers/digitalplayground.js b/src/scrapers/digitalplayground.js index 8077d905..96d6a5d7 100644 --- a/src/scrapers/digitalplayground.js +++ b/src/scrapers/digitalplayground.js @@ -3,11 +3,11 @@ const { fetchScene, fetchLatest, fetchProfile } = require('./mindgeek'); async function networkFetchProfile(actorName) { - return fetchProfile(actorName, 'digitalplayground', 'modelprofile'); + return fetchProfile(actorName, 'digitalplayground', 'modelprofile'); } module.exports = { - fetchLatest, - fetchProfile: networkFetchProfile, - fetchScene, + fetchLatest, + fetchProfile: networkFetchProfile, + fetchScene, }; diff --git a/src/scrapers/dogfart.js b/src/scrapers/dogfart.js index 1d9252e4..603ec36b 100644 --- a/src/scrapers/dogfart.js +++ b/src/scrapers/dogfart.js @@ -7,136 +7,136 @@ const { JSDOM } = require('jsdom'); const moment = require('moment'); async function getPhotos(albumUrl) { - const res = await bhttp.get(albumUrl); - const html = res.body.toString(); - const { document } = new JSDOM(html).window; + const res = await bhttp.get(albumUrl); + const html = res.body.toString(); + const { document } = new JSDOM(html).window; - const lastPhotoPage = Array.from(document.querySelectorAll('.preview-image-container a')).slice(-1)[0].href; - const lastPhotoIndex = parseInt(lastPhotoPage.match(/\d+.jpg/)[0], 10); + const lastPhotoPage = Array.from(document.querySelectorAll('.preview-image-container a')).slice(-1)[0].href; + const lastPhotoIndex = parseInt(lastPhotoPage.match(/\d+.jpg/)[0], 10); - const photoUrls = Array.from({ length: lastPhotoIndex }, (value, index) => { - const pageUrl = `https://blacksonblondes.com${lastPhotoPage.replace(/\d+.jpg/, `${(index + 1).toString().padStart(3, '0')}.jpg`)}`; + const photoUrls = Array.from({ length: lastPhotoIndex }, (value, index) => { + const pageUrl = `https://blacksonblondes.com${lastPhotoPage.replace(/\d+.jpg/, `${(index + 1).toString().padStart(3, '0')}.jpg`)}`; - return { - url: pageUrl, - extract: ({ qu }) => qu.q('.scenes-module img', 'src'), - }; - }); + return { + url: pageUrl, + extract: ({ qu }) => qu.q('.scenes-module img', 'src'), + }; + }); - return photoUrls; + return photoUrls; } function scrapeLatest(html, site) { - const { document } = new JSDOM(html).window; - const sceneElements = Array.from(document.querySelectorAll('.recent-updates')); + const { document } = new JSDOM(html).window; + const sceneElements = Array.from(document.querySelectorAll('.recent-updates')); - return sceneElements.reduce((acc, element) => { - const siteUrl = element.querySelector('.help-block').textContent; + return sceneElements.reduce((acc, element) => { + const siteUrl = element.querySelector('.help-block').textContent; - if (`www.${siteUrl.toLowerCase()}` !== new URL(site.url).host) { - // different dogfart site - return acc; - } + if (`www.${siteUrl.toLowerCase()}` !== new URL(site.url).host) { + // different dogfart site + return acc; + } - const sceneLinkElement = element.querySelector('.thumbnail'); - const url = `https://dogfartnetwork.com${sceneLinkElement.href}`; - const { pathname } = new URL(url); - const entryId = `${site.slug}_${pathname.split('/')[4]}`; + const sceneLinkElement = element.querySelector('.thumbnail'); + const url = `https://dogfartnetwork.com${sceneLinkElement.href}`; + const { pathname } = new URL(url); + const entryId = `${site.slug}_${pathname.split('/')[4]}`; - const title = element.querySelector('.scene-title').textContent; - const actors = title.split(/[,&]|\band\b/).map(actor => actor.trim()); + const title = element.querySelector('.scene-title').textContent; + const actors = title.split(/[,&]|\band\b/).map(actor => actor.trim()); - const poster = `https:${element.querySelector('img').src}`; - const teaser = sceneLinkElement.dataset.preview_clip_url; + const poster = `https:${element.querySelector('img').src}`; + const teaser = sceneLinkElement.dataset.preview_clip_url; - return [ - ...acc, - { - url, - entryId, - title, - actors, - poster, - teaser: { - src: teaser, - }, - site, - }, - ]; - }, []); + return [ + ...acc, + { + url, + entryId, + title, + actors, + poster, + teaser: { + src: teaser, + }, + site, + }, + ]; + }, []); } async function scrapeScene(html, url, site) { - const { document } = new JSDOM(html).window; + const { document } = new JSDOM(html).window; - const title = document.querySelector('.description-title').textContent; - const actors = Array.from(document.querySelectorAll('.more-scenes a')).map(({ textContent }) => textContent); - const metaDescription = document.querySelector('meta[itemprop="description"]').content; - const description = metaDescription - ? metaDescription.content - : document.querySelector('.description') - .textContent - .replace(/[ \t\n]{2,}/g, ' ') - .replace('...read more', '') - .trim(); + const title = document.querySelector('.description-title').textContent; + const actors = Array.from(document.querySelectorAll('.more-scenes a')).map(({ textContent }) => textContent); + const metaDescription = document.querySelector('meta[itemprop="description"]').content; + const description = metaDescription + ? metaDescription.content + : document.querySelector('.description') + .textContent + .replace(/[ \t\n]{2,}/g, ' ') + .replace('...read more', '') + .trim(); - const channel = document.querySelector('.site-name').textContent.split('.')[0].toLowerCase(); - const { origin, pathname } = new URL(url); - const entryId = `${channel}_${pathname.split('/').slice(-2)[0]}`; + const channel = document.querySelector('.site-name').textContent.split('.')[0].toLowerCase(); + const { origin, pathname } = new URL(url); + const entryId = `${channel}_${pathname.split('/').slice(-2)[0]}`; - const date = new Date(document.querySelector('meta[itemprop="uploadDate"]').content); - const duration = moment - .duration(`00:${document - .querySelectorAll('.extra-info p')[1] - .textContent - .match(/\d+:\d+$/)[0]}`) - .asSeconds(); + const date = new Date(document.querySelector('meta[itemprop="uploadDate"]').content); + const duration = moment + .duration(`00:${document + .querySelectorAll('.extra-info p')[1] + .textContent + .match(/\d+:\d+$/)[0]}`) + .asSeconds(); - const trailerElement = document.querySelector('.html5-video'); - const poster = `https:${trailerElement.dataset.poster}`; - const { trailer } = trailerElement.dataset; + const trailerElement = document.querySelector('.html5-video'); + const poster = `https:${trailerElement.dataset.poster}`; + const { trailer } = trailerElement.dataset; - const lastPhotosUrl = Array.from(document.querySelectorAll('.pagination a')).slice(-1)[0].href; - const photos = await getPhotos(`${origin}${pathname}${lastPhotosUrl}`, site, url); + const lastPhotosUrl = Array.from(document.querySelectorAll('.pagination a')).slice(-1)[0].href; + const photos = await getPhotos(`${origin}${pathname}${lastPhotosUrl}`, site, url); - const stars = Math.floor(Number(document.querySelector('span[itemprop="average"]')?.textContent || document.querySelector('span[itemprop="ratingValue"]')?.textContent) / 2); - const tags = Array.from(document.querySelectorAll('.scene-details .categories a')).map(({ textContent }) => textContent); + const stars = Math.floor(Number(document.querySelector('span[itemprop="average"]')?.textContent || document.querySelector('span[itemprop="ratingValue"]')?.textContent) / 2); + const tags = Array.from(document.querySelectorAll('.scene-details .categories a')).map(({ textContent }) => textContent); - return { - entryId, - url: `${origin}${pathname}`, - title, - description, - actors, - date, - duration, - poster, - photos, - trailer: { - src: trailer, - }, - tags, - rating: { - stars, - }, - site, - channel, - }; + return { + entryId, + url: `${origin}${pathname}`, + title, + description, + actors, + date, + duration, + poster, + photos, + trailer: { + src: trailer, + }, + tags, + rating: { + stars, + }, + site, + channel, + }; } async function fetchLatest(site, page = 1) { - const res = await bhttp.get(`https://dogfartnetwork.com/tour/scenes/?p=${page}`); + const res = await bhttp.get(`https://dogfartnetwork.com/tour/scenes/?p=${page}`); - return scrapeLatest(res.body.toString(), site); + return scrapeLatest(res.body.toString(), site); } async function fetchScene(url, site) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - return scrapeScene(res.body.toString(), url, site); + return scrapeScene(res.body.toString(), url, site); } module.exports = { - fetchLatest, - fetchScene, + fetchLatest, + fetchScene, }; diff --git a/src/scrapers/evilangel.js b/src/scrapers/evilangel.js index c31fa6e8..562acaa6 100644 --- a/src/scrapers/evilangel.js +++ b/src/scrapers/evilangel.js @@ -3,8 +3,8 @@ const { fetchApiLatest, fetchApiUpcoming, fetchScene, fetchApiProfile } = require('./gamma'); module.exports = { - fetchLatest: fetchApiLatest, - fetchProfile: fetchApiProfile, - fetchScene, - fetchUpcoming: fetchApiUpcoming, + fetchLatest: fetchApiLatest, + fetchProfile: fetchApiProfile, + fetchScene, + fetchUpcoming: fetchApiUpcoming, }; diff --git a/src/scrapers/fakehub.js b/src/scrapers/fakehub.js index 5fa25f17..7f6d24a9 100644 --- a/src/scrapers/fakehub.js +++ b/src/scrapers/fakehub.js @@ -3,11 +3,11 @@ const { fetchScene, fetchLatest, fetchProfile } = require('./mindgeek'); async function networkFetchProfile(actorName) { - return fetchProfile(actorName, 'fakehub', 'modelprofile'); + return fetchProfile(actorName, 'fakehub', 'modelprofile'); } module.exports = { - fetchLatest, - fetchProfile: networkFetchProfile, - fetchScene, + fetchLatest, + fetchProfile: networkFetchProfile, + fetchScene, }; diff --git a/src/scrapers/famedigital.js b/src/scrapers/famedigital.js index 9698814d..ac6e5a74 100644 --- a/src/scrapers/famedigital.js +++ b/src/scrapers/famedigital.js @@ -1,115 +1,115 @@ 'use strict'; const { - fetchLatest, - fetchApiLatest, - fetchUpcoming, - fetchApiUpcoming, - fetchScene, - fetchProfile, - fetchApiProfile, - scrapeAll, + fetchLatest, + fetchApiLatest, + fetchUpcoming, + fetchApiUpcoming, + fetchScene, + fetchProfile, + fetchApiProfile, + scrapeAll, } = require('./gamma'); const { get } = require('../utils/q'); const slugify = require('../utils/slugify'); function extractLowArtActors(release) { - const actors = release.title - .replace(/solo/i, '') - .split(/,|\band\b/ig) - .map(actor => actor.trim()); + const actors = release.title + .replace(/solo/i, '') + .split(/,|\band\b/ig) + .map(actor => actor.trim()); - return { - ...release, - actors, - }; + return { + ...release, + actors, + }; } async function networkFetchLatest(site, page = 1) { - if (site.parameters?.api) return fetchApiLatest(site, page, false); + if (site.parameters?.api) return fetchApiLatest(site, page, false); - const releases = await fetchLatest(site, page); + const releases = await fetchLatest(site, page); - if (site.slug === 'lowartfilms') { - return releases.map(release => extractLowArtActors(release)); - } + if (site.slug === 'lowartfilms') { + return releases.map(release => extractLowArtActors(release)); + } - return releases; + return releases; } async function networkFetchScene(url, site) { - const release = await fetchScene(url, site); + const release = await fetchScene(url, site); - if (site.slug === 'lowartfilms') { - return extractLowArtActors(release); - } + if (site.slug === 'lowartfilms') { + return extractLowArtActors(release); + } - return release; + return release; } async function networkFetchUpcoming(site, page = 1) { - if (site.parameters?.api) return fetchApiUpcoming(site, page, true); + if (site.parameters?.api) return fetchApiUpcoming(site, page, true); - return fetchUpcoming(site, page); + return fetchUpcoming(site, page); } function getActorReleasesUrl(actorPath, page = 1) { - return `https://www.peternorth.com/en/videos/All-Categories/0${actorPath}/All-Dvds/0/latest/${page}`; + return `https://www.peternorth.com/en/videos/All-Categories/0${actorPath}/All-Dvds/0/latest/${page}`; } async function fetchClassicProfile(actorName, siteSlug) { - const actorSlug = slugify(actorName); + const actorSlug = slugify(actorName); - const url = `https://${siteSlug}.com/en/pornstars`; - const pornstarsRes = await get(url); + const url = `https://${siteSlug}.com/en/pornstars`; + const pornstarsRes = await get(url); - if (!pornstarsRes.ok) return null; + if (!pornstarsRes.ok) return null; - const actorPath = pornstarsRes.item.qa('option[value*="/pornstar"]') - .find(el => slugify(el.textContent) === actorSlug) + const actorPath = pornstarsRes.item.qa('option[value*="/pornstar"]') + .find(el => slugify(el.textContent) === actorSlug) ?.value; - if (actorPath) { - const actorUrl = `https://${siteSlug}.com${actorPath}`; - const res = await get(actorUrl); + if (actorPath) { + const actorUrl = `https://${siteSlug}.com${actorPath}`; + const res = await get(actorUrl); - if (res.ok) { - const releases = scrapeAll(res.item, null, `https://www.${siteSlug}.com`, false); + if (res.ok) { + const releases = scrapeAll(res.item, null, `https://www.${siteSlug}.com`, false); - return { releases }; - } - } + return { releases }; + } + } - return null; + return null; } async function networkFetchProfile(actorName, scraperSlug, site, include) { - // not all Fame Digital sites offer Gamma actors - const [devils, rocco, peter, silvia] = await Promise.all([ - fetchApiProfile(actorName, 'devilsfilm', true), - fetchApiProfile(actorName, 'roccosiffredi'), - include.scenes ? fetchProfile(actorName, 'peternorth', true, getActorReleasesUrl, include) : [], - include.scenes ? fetchClassicProfile(actorName, 'silviasaint') : [], - include.scenes ? fetchClassicProfile(actorName, 'silverstonedvd') : [], - ]); + // not all Fame Digital sites offer Gamma actors + const [devils, rocco, peter, silvia] = await Promise.all([ + fetchApiProfile(actorName, 'devilsfilm', true), + fetchApiProfile(actorName, 'roccosiffredi'), + include.scenes ? fetchProfile(actorName, 'peternorth', true, getActorReleasesUrl, include) : [], + include.scenes ? fetchClassicProfile(actorName, 'silviasaint') : [], + include.scenes ? fetchClassicProfile(actorName, 'silverstonedvd') : [], + ]); - if (devils || rocco || peter) { - const releases = [].concat(devils?.releases || [], rocco?.releases || [], peter?.releases || [], silvia?.releases || []); + if (devils || rocco || peter) { + const releases = [].concat(devils?.releases || [], rocco?.releases || [], peter?.releases || [], silvia?.releases || []); - return { - ...peter, - ...rocco, - ...devils, - releases, - }; - } + return { + ...peter, + ...rocco, + ...devils, + releases, + }; + } - return null; + return null; } module.exports = { - fetchLatest: networkFetchLatest, - fetchProfile: networkFetchProfile, - fetchScene: networkFetchScene, - fetchUpcoming: networkFetchUpcoming, + fetchLatest: networkFetchLatest, + fetchProfile: networkFetchProfile, + fetchScene: networkFetchScene, + fetchUpcoming: networkFetchUpcoming, }; diff --git a/src/scrapers/fantasymassage.js b/src/scrapers/fantasymassage.js index 448e1646..d0840db6 100644 --- a/src/scrapers/fantasymassage.js +++ b/src/scrapers/fantasymassage.js @@ -4,7 +4,7 @@ const { fetchLatest, fetchUpcoming, fetchScene } = require('./gamma'); module.exports = { - fetchLatest, - fetchScene, - fetchUpcoming, + fetchLatest, + fetchScene, + fetchUpcoming, }; diff --git a/src/scrapers/freeones.js b/src/scrapers/freeones.js index ecf8b7ef..ac0cfce2 100644 --- a/src/scrapers/freeones.js +++ b/src/scrapers/freeones.js @@ -5,89 +5,89 @@ const { JSDOM } = require('jsdom'); const moment = require('moment'); function scrapeProfile(html, actorName) { - const { document } = new JSDOM(html).window; - const profile = { name: actorName }; + const { document } = new JSDOM(html).window; + const profile = { name: actorName }; - const bio = Array.from(document.querySelectorAll('a[href^="/babes"]'), el => decodeURI(el.href)).reduce((acc, item) => { - const keyMatch = item.match(/\[\w+\]/); + const bio = Array.from(document.querySelectorAll('a[href^="/babes"]'), el => decodeURI(el.href)).reduce((acc, item) => { + const keyMatch = item.match(/\[\w+\]/); - if (keyMatch) { - const key = keyMatch[0].slice(1, -1); - const [, value] = item.split('='); + if (keyMatch) { + const key = keyMatch[0].slice(1, -1); + const [, value] = item.split('='); - // both hip and waist link to 'waist', assume biggest value is hip - if (key === 'waist' && acc.waist) { - if (acc.waist > value) { - acc.hip = acc.waist; - acc.waist = value; + // both hip and waist link to 'waist', assume biggest value is hip + if (key === 'waist' && acc.waist) { + if (acc.waist > value) { + acc.hip = acc.waist; + acc.waist = value; - return acc; - } + return acc; + } - acc.hip = value; + acc.hip = value; - return acc; - } + return acc; + } - acc[key] = value; - } + acc[key] = value; + } - return acc; - }, {}); + return acc; + }, {}); - if (bio.dateOfBirth) profile.birthdate = moment.utc(bio.dateOfBirth, 'YYYY-MM-DD').toDate(); + if (bio.dateOfBirth) profile.birthdate = moment.utc(bio.dateOfBirth, 'YYYY-MM-DD').toDate(); - if (profile.placeOfBirth || bio.country) profile.birthPlace = `${bio.placeOfBirth}, ${bio.country}`; - profile.eyes = bio.eyeColor; - profile.hair = bio.hairColor; - profile.ethnicity = bio.ethnicity; + if (profile.placeOfBirth || bio.country) profile.birthPlace = `${bio.placeOfBirth}, ${bio.country}`; + profile.eyes = bio.eyeColor; + profile.hair = bio.hairColor; + profile.ethnicity = bio.ethnicity; - profile.bust = bio.bra; - if (bio.waist) profile.waist = Number(bio.waist.split(',')[0]); - if (bio.hip) profile.hip = Number(bio.hip.split(',')[0]); + profile.bust = bio.bra; + if (bio.waist) profile.waist = Number(bio.waist.split(',')[0]); + if (bio.hip) profile.hip = Number(bio.hip.split(',')[0]); - if (bio.height) profile.height = Number(bio.height.split(',')[0]); - if (bio.weight) profile.weight = Number(bio.weight.split(',')[0]); + if (bio.height) profile.height = Number(bio.height.split(',')[0]); + if (bio.weight) profile.weight = Number(bio.weight.split(',')[0]); - profile.social = Array.from(document.querySelectorAll('.profile-meta-item a.social-icons'), el => el.href); + profile.social = Array.from(document.querySelectorAll('.profile-meta-item a.social-icons'), el => el.href); - const avatar = document.querySelector('.profile-image-large img').src; - if (!avatar.match('placeholder')) profile.avatar = { src: avatar, copyright: null }; + const avatar = document.querySelector('.profile-image-large img').src; + if (!avatar.match('placeholder')) profile.avatar = { src: avatar, copyright: null }; - return profile; + return profile; } function scrapeSearch(html) { - const { document } = new JSDOM(html).window; + const { document } = new JSDOM(html).window; - return document.querySelector('a.image-link')?.href || null; + return document.querySelector('a.image-link')?.href || null; } async function fetchProfile(actorName) { - const actorSlug = actorName.toLowerCase().replace(/\s+/g, '-'); + const actorSlug = actorName.toLowerCase().replace(/\s+/g, '-'); - const res = await bhttp.get(`https://freeones.nl/${actorSlug}/profile`); + const res = await bhttp.get(`https://freeones.nl/${actorSlug}/profile`); - if (res.statusCode === 200) { - return scrapeProfile(res.body.toString(), actorName); - } + if (res.statusCode === 200) { + return scrapeProfile(res.body.toString(), actorName); + } - const searchRes = await bhttp.get(`https://freeones.nl/babes?q=${actorName}`); - const actorPath = scrapeSearch(searchRes.body.toString()); + const searchRes = await bhttp.get(`https://freeones.nl/babes?q=${actorName}`); + const actorPath = scrapeSearch(searchRes.body.toString()); - if (actorPath) { - const actorRes = await bhttp.get(`https://freeones.nl${actorPath}/profile`); + if (actorPath) { + const actorRes = await bhttp.get(`https://freeones.nl${actorPath}/profile`); - if (actorRes.statusCode === 200) { - return scrapeProfile(actorRes.body.toString(), actorName); - } + if (actorRes.statusCode === 200) { + return scrapeProfile(actorRes.body.toString(), actorName); + } - return null; - } + return null; + } - return null; + return null; } module.exports = { - fetchProfile, + fetchProfile, }; diff --git a/src/scrapers/freeones_legacy.js b/src/scrapers/freeones_legacy.js index 7711a1c3..df1c9251 100644 --- a/src/scrapers/freeones_legacy.js +++ b/src/scrapers/freeones_legacy.js @@ -6,135 +6,135 @@ const { JSDOM } = require('jsdom'); const moment = require('moment'); async function scrapeProfileFrontpage(html, url, name) { - const { document } = new JSDOM(html).window; - const bioEl = document.querySelector('.dashboard-bio-list'); + const { document } = new JSDOM(html).window; + const bioEl = document.querySelector('.dashboard-bio-list'); - const bioUrl = `https:${document.querySelector('.seemore a').href}`; + const bioUrl = `https:${document.querySelector('.seemore a').href}`; - const keys = Array.from(bioEl.querySelectorAll('dt'), el => el.textContent.trim()); - const values = Array.from(bioEl.querySelectorAll('dd'), el => el.textContent.trim()); + const keys = Array.from(bioEl.querySelectorAll('dt'), el => el.textContent.trim()); + const values = Array.from(bioEl.querySelectorAll('dd'), el => el.textContent.trim()); - const bio = keys.reduce((acc, key, index) => ({ ...acc, [key]: values[index] }), {}); + const bio = keys.reduce((acc, key, index) => ({ ...acc, [key]: values[index] }), {}); - const profile = { - name, - gender: 'female', - }; + const profile = { + name, + gender: 'female', + }; - const birthdateString = bio['Date of Birth:']; - const measurementsString = bio['Measurements:']; + const birthdateString = bio['Date of Birth:']; + const measurementsString = bio['Measurements:']; - const birthCityString = bio['Place of Birth:']; - const birthCity = birthCityString !== undefined && birthCityString !== 'Unknown' && birthCityString !== 'Unknown (add)' && birthCityString; + const birthCityString = bio['Place of Birth:']; + const birthCity = birthCityString !== undefined && birthCityString !== 'Unknown' && birthCityString !== 'Unknown (add)' && birthCityString; - const birthCountryString = bio['Country of Origin:']; - const birthCountry = birthCountryString !== undefined && birthCountryString !== 'Unknown' && birthCountryString !== 'Unknown (add)' && birthCountryString; + const birthCountryString = bio['Country of Origin:']; + const birthCountry = birthCountryString !== undefined && birthCountryString !== 'Unknown' && birthCountryString !== 'Unknown (add)' && birthCountryString; - const piercingsString = bio['Piercings:']; - const tattoosString = bio['Tattoos:']; + const piercingsString = bio['Piercings:']; + const tattoosString = bio['Tattoos:']; - if (birthdateString && birthdateString !== 'Unknown (add)') profile.birthdate = moment.utc(birthdateString.slice(0, birthdateString.indexOf(' (')), 'MMMM D, YYYY').toDate(); - if (measurementsString) [profile.bust, profile.waist, profile.hip] = measurementsString.split('-').map(measurement => (measurement === '??' ? null : measurement)); + if (birthdateString && birthdateString !== 'Unknown (add)') profile.birthdate = moment.utc(birthdateString.slice(0, birthdateString.indexOf(' (')), 'MMMM D, YYYY').toDate(); + if (measurementsString) [profile.bust, profile.waist, profile.hip] = measurementsString.split('-').map(measurement => (measurement === '??' ? null : measurement)); - if (bio['Fake Boobs:']) profile.naturalBoobs = bio['Fake Boobs:'] === 'No'; - profile.birthPlace = `${birthCity || ''}${birthCity ? ', ' : ''}${birthCountry || ''}`; + if (bio['Fake Boobs:']) profile.naturalBoobs = bio['Fake Boobs:'] === 'No'; + profile.birthPlace = `${birthCity || ''}${birthCity ? ', ' : ''}${birthCountry || ''}`; - profile.hair = bio['Hair Color:'].toLowerCase(); - profile.eyes = bio['Eye Color:'].toLowerCase(); + profile.hair = bio['Hair Color:'].toLowerCase(); + profile.eyes = bio['Eye Color:'].toLowerCase(); - if (piercingsString) profile.hasPiercings = !!(piercingsString !== 'Unknown (add)' && piercingsString !== 'None'); - if (tattoosString) profile.hasTattoos = !!(tattoosString !== 'Unknown (add)' && tattoosString !== 'None'); + if (piercingsString) profile.hasPiercings = !!(piercingsString !== 'Unknown (add)' && piercingsString !== 'None'); + if (tattoosString) profile.hasTattoos = !!(tattoosString !== 'Unknown (add)' && tattoosString !== 'None'); - if (profile.hasPiercings && piercingsString !== 'various') profile.piercings = piercingsString; - if (profile.hasTattoos && tattoosString !== 'various') profile.tattoos = tattoosString; + if (profile.hasPiercings && piercingsString !== 'various') profile.piercings = piercingsString; + if (profile.hasTattoos && tattoosString !== 'various') profile.tattoos = tattoosString; - profile.social = Array.from(bioEl.querySelectorAll('.dashboard-socialmedia a'), el => el.href); + profile.social = Array.from(bioEl.querySelectorAll('.dashboard-socialmedia a'), el => el.href); - return { - profile, - url: bioUrl, - }; + return { + profile, + url: bioUrl, + }; } async function scrapeProfileBio(html, frontpageProfile, url, name) { - const { document } = new JSDOM(html).window; - const bioEl = document.querySelector('#biographyTable'); + const { document } = new JSDOM(html).window; + const bioEl = document.querySelector('#biographyTable'); - const keys = Array.from(bioEl.querySelectorAll('td:nth-child(1)'), el => el.textContent.trim()); - const values = Array.from(bioEl.querySelectorAll('td:nth-child(2)'), el => el.textContent.trim()); + const keys = Array.from(bioEl.querySelectorAll('td:nth-child(1)'), el => el.textContent.trim()); + const values = Array.from(bioEl.querySelectorAll('td:nth-child(2)'), el => el.textContent.trim()); - const bio = keys.reduce((acc, key, index) => ({ ...acc, [key]: values[index] }), {}); + const bio = keys.reduce((acc, key, index) => ({ ...acc, [key]: values[index] }), {}); - const profile = { - ...frontpageProfile, - name, - gender: 'female', - }; + const profile = { + ...frontpageProfile, + name, + gender: 'female', + }; - const birthdateString = bio['Date of Birth:']; - const measurementsString = bio['Measurements:']; + const birthdateString = bio['Date of Birth:']; + const measurementsString = bio['Measurements:']; - const birthCityString = bio['Place of Birth:']; - const birthCity = birthCityString !== undefined && birthCityString !== 'Unknown' && birthCityString !== 'Unknown (add)' && birthCityString; + const birthCityString = bio['Place of Birth:']; + const birthCity = birthCityString !== undefined && birthCityString !== 'Unknown' && birthCityString !== 'Unknown (add)' && birthCityString; - const birthCountryString = bio['Country of Origin:']; - const birthCountry = birthCountryString !== undefined && birthCountryString !== 'Unknown' && birthCountryString !== 'Unknown (add)' && birthCountryString; + const birthCountryString = bio['Country of Origin:']; + const birthCountry = birthCountryString !== undefined && birthCountryString !== 'Unknown' && birthCountryString !== 'Unknown (add)' && birthCountryString; - const piercingsString = bio['Piercings:']; - const tattoosString = bio['Tattoos:']; + const piercingsString = bio['Piercings:']; + const tattoosString = bio['Tattoos:']; - if (birthdateString && birthdateString !== 'Unknown') profile.birthdate = moment.utc(birthdateString.slice(0, birthdateString.indexOf(' (')), 'MMMM D, YYYY').toDate(); - if (measurementsString) [profile.bust, profile.waist, profile.hip] = measurementsString.split('-').map(measurement => (measurement === '??' ? null : measurement)); + if (birthdateString && birthdateString !== 'Unknown') profile.birthdate = moment.utc(birthdateString.slice(0, birthdateString.indexOf(' (')), 'MMMM D, YYYY').toDate(); + if (measurementsString) [profile.bust, profile.waist, profile.hip] = measurementsString.split('-').map(measurement => (measurement === '??' ? null : measurement)); - if (bio['Fake boobs']) profile.naturalBoobs = bio['Fake boobs:'] === 'No'; - profile.ethnicity = bio['Ethnicity:']; + if (bio['Fake boobs']) profile.naturalBoobs = bio['Fake boobs:'] === 'No'; + profile.ethnicity = bio['Ethnicity:']; - profile.birthPlace = `${birthCity || ''}${birthCity ? ', ' : ''}${birthCountry || ''}`; + profile.birthPlace = `${birthCity || ''}${birthCity ? ', ' : ''}${birthCountry || ''}`; - profile.hair = bio['Hair Color:'].toLowerCase(); - profile.eyes = bio['Eye Color:'].toLowerCase(); - profile.height = Number(bio['Height:'].match(/\d+/)[0]); - profile.weight = Number(bio['Weight:'].match(/\d+/)[0]); + profile.hair = bio['Hair Color:'].toLowerCase(); + profile.eyes = bio['Eye Color:'].toLowerCase(); + profile.height = Number(bio['Height:'].match(/\d+/)[0]); + profile.weight = Number(bio['Weight:'].match(/\d+/)[0]); - if (piercingsString) profile.hasPiercings = !!(piercingsString !== 'Unknown (add)' && piercingsString !== 'None'); - if (tattoosString) profile.hasTattoos = !!(tattoosString !== 'Unknown (add)' && tattoosString !== 'None'); + if (piercingsString) profile.hasPiercings = !!(piercingsString !== 'Unknown (add)' && piercingsString !== 'None'); + if (tattoosString) profile.hasTattoos = !!(tattoosString !== 'Unknown (add)' && tattoosString !== 'None'); - if (profile.hasPiercings && piercingsString !== 'various') profile.piercings = piercingsString; - if (profile.hasTattoos && tattoosString !== 'various') profile.tattoos = tattoosString; + if (profile.hasPiercings && piercingsString !== 'various') profile.piercings = piercingsString; + if (profile.hasTattoos && tattoosString !== 'various') profile.tattoos = tattoosString; - profile.social = Array.from(bioEl.querySelectorAll('#socialmedia a'), el => el.href); + profile.social = Array.from(bioEl.querySelectorAll('#socialmedia a'), el => el.href); - return profile; + return profile; } async function fetchProfile(actorName) { - const slug = actorName.replace(' ', '_'); - const frontpageUrl = `https://www.freeones.com/html/v_links/${slug}`; + const slug = actorName.replace(' ', '_'); + const frontpageUrl = `https://www.freeones.com/html/v_links/${slug}`; - const resFrontpage = await bhttp.get(frontpageUrl); + const resFrontpage = await bhttp.get(frontpageUrl); - if (resFrontpage.statusCode === 200) { - const { url, bio } = await scrapeProfileFrontpage(resFrontpage.body.toString(), frontpageUrl, actorName); - const resBio = await bhttp.get(url); + if (resFrontpage.statusCode === 200) { + const { url, bio } = await scrapeProfileFrontpage(resFrontpage.body.toString(), frontpageUrl, actorName); + const resBio = await bhttp.get(url); - return scrapeProfileBio(resBio.body.toString(), bio, url, actorName); - } + return scrapeProfileBio(resBio.body.toString(), bio, url, actorName); + } - // apparently some actors are appended 'Babe' as their surname... - const fallbackSlug = `${slug}_Babe`; - const fallbackUrl = `https://www.freeones.com/html/s_links/${fallbackSlug}`; - const resFallback = await bhttp.get(fallbackUrl); + // apparently some actors are appended 'Babe' as their surname... + const fallbackSlug = `${slug}_Babe`; + const fallbackUrl = `https://www.freeones.com/html/s_links/${fallbackSlug}`; + const resFallback = await bhttp.get(fallbackUrl); - if (resFallback.statusCode === 200) { - const { url, profile } = await scrapeProfileFrontpage(resFallback.body.toString(), fallbackUrl, actorName); - const resBio = await bhttp.get(url); + if (resFallback.statusCode === 200) { + const { url, profile } = await scrapeProfileFrontpage(resFallback.body.toString(), fallbackUrl, actorName); + const resBio = await bhttp.get(url); - return scrapeProfileBio(resBio.body.toString(), profile, url, actorName); - } + return scrapeProfileBio(resBio.body.toString(), profile, url, actorName); + } - return null; + return null; } module.exports = { - fetchProfile, + fetchProfile, }; diff --git a/src/scrapers/fullpornnetwork.js b/src/scrapers/fullpornnetwork.js index 2b8521b8..1347fe74 100644 --- a/src/scrapers/fullpornnetwork.js +++ b/src/scrapers/fullpornnetwork.js @@ -4,93 +4,93 @@ const { get, geta, ctxa } = require('../utils/q'); const slugify = require('../utils/slugify'); function scrapeAll(scenes) { - return scenes.map(({ el, qu }) => { - const release = {}; + return scenes.map(({ el, qu }) => { + const release = {}; - release.entryId = el.dataset.setid || qu.q('.update_thumb', 'id').match(/\w+-\w+-(\d+)-\d+/)[1]; - release.url = qu.url('.title'); + release.entryId = el.dataset.setid || qu.q('.update_thumb', 'id').match(/\w+-\w+-(\d+)-\d+/)[1]; + release.url = qu.url('.title'); - release.title = qu.q('.title', true); - release.description = qu.q('.title', 'title'); + release.title = qu.q('.title', true); + release.description = qu.q('.title', 'title'); - release.date = qu.date('.video-data > span:last-child', 'YYYY-MM-DD'); - release.duration = qu.dur('.video-data > span'); + release.date = qu.date('.video-data > span:last-child', 'YYYY-MM-DD'); + release.duration = qu.dur('.video-data > span'); - release.actors = qu.all('.update_models a', true); + release.actors = qu.all('.update_models a', true); - const poster = qu.q('.update_thumb', 'src0_1x'); - release.poster = [ - poster.replace('-1x', '-2x'), - poster, - ]; + const poster = qu.q('.update_thumb', 'src0_1x'); + release.poster = [ + poster.replace('-1x', '-2x'), + poster, + ]; - return release; - }); + return release; + }); } function scrapeScene({ q, qa, qd, qtx }, url, _site) { - const release = { url }; + const release = { url }; - release.entryId = q('#image_parent img', 'id').match(/\w+-\w+-(\d+)-\d+/)[1]; + release.entryId = q('#image_parent img', 'id').match(/\w+-\w+-(\d+)-\d+/)[1]; - release.title = q('.trailer_title', true); - release.description = qtx('.text p'); - release.date = qd('span[data-dateadded]', 'YYYY-MM-DD', null, 'data-dateadded'); + release.title = q('.trailer_title', true); + release.description = qtx('.text p'); + release.date = qd('span[data-dateadded]', 'YYYY-MM-DD', null, 'data-dateadded'); - release.actors = qa('.update_models a', true); - release.tags = qa('.video-info a[href*="/categories"]', true); + release.actors = qa('.update_models a', true); + release.tags = qa('.video-info a[href*="/categories"]', true); - const poster = q('#image_parent img', 'src0_1x'); - release.poster = [ - poster.replace('-1x', '-2x'), - poster, - ]; + const poster = q('#image_parent img', 'src0_1x'); + release.poster = [ + poster.replace('-1x', '-2x'), + poster, + ]; - return release; + return release; } function scrapeProfile({ el, q, qtx }) { - const profile = {}; + const profile = {}; - const description = qtx('.model-bio'); - if (description) profile.description = description; + const description = qtx('.model-bio'); + if (description) profile.description = description; - profile.avatar = [ - q('.model-image img', 'src0_2x'), - q('.model-image img', 'src0_1x'), - ]; + profile.avatar = [ + q('.model-image img', 'src0_2x'), + q('.model-image img', 'src0_1x'), + ]; - profile.releases = scrapeAll(ctxa(el, '.update')); + profile.releases = scrapeAll(ctxa(el, '.update')); - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const url = `${site.url}/categories/movies_${page}_d.html`; - const res = await geta(url, '.latest-updates .update'); + const url = `${site.url}/categories/movies_${page}_d.html`; + const res = await geta(url, '.latest-updates .update'); - return res.ok ? scrapeAll(res.items, site) : res.status; + return res.ok ? scrapeAll(res.items, site) : res.status; } async function fetchScene(url, site) { - const res = await get(url, '.content-wrapper'); + const res = await get(url, '.content-wrapper'); - return res.ok ? scrapeScene(res.item, url, site) : res.status; + return res.ok ? scrapeScene(res.item, url, site) : res.status; } async function fetchProfile(actorName, scraperSlug) { - const actorSlug = slugify(actorName, ''); - const url = scraperSlug === 'povperverts' - ? `https://povperverts.net/models/${actorSlug}.html` - : `https://${scraperSlug}.com/models/${actorSlug}.html`; + const actorSlug = slugify(actorName, ''); + const url = scraperSlug === 'povperverts' + ? `https://povperverts.net/models/${actorSlug}.html` + : `https://${scraperSlug}.com/models/${actorSlug}.html`; - const res = await get(url); + const res = await get(url); - return res.ok ? scrapeProfile(res.item, actorName) : res.status; + return res.ok ? scrapeProfile(res.item, actorName) : res.status; } module.exports = { - fetchLatest, - fetchScene, - fetchProfile, + fetchLatest, + fetchScene, + fetchProfile, }; diff --git a/src/scrapers/gamma.js b/src/scrapers/gamma.js index 26f25c4e..d6c1a6c0 100644 --- a/src/scrapers/gamma.js +++ b/src/scrapers/gamma.js @@ -12,618 +12,618 @@ const { ex, get } = require('../utils/q'); const slugify = require('../utils/slugify'); function getAlbumUrl(albumPath, site) { - if (site.parameters?.photos) { - return /^http/.test(site.parameters.photos) - ? `${site.parameters.photos}/${albumPath.split('/').slice(-2).join('/')}` - : `${site.url}${site.parameters.photos}/${albumPath.split('/').slice(-2).join('/')}`; - } + if (site.parameters?.photos) { + return /^http/.test(site.parameters.photos) + ? `${site.parameters.photos}/${albumPath.split('/').slice(-2).join('/')}` + : `${site.url}${site.parameters.photos}/${albumPath.split('/').slice(-2).join('/')}`; + } - if (site.url && site.parameters?.photos !== false) { - return `${site.url}${albumPath}`; - } + if (site.url && site.parameters?.photos !== false) { + return `${site.url}${albumPath}`; + } - return null; + return null; } async function fetchPhotos(url) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - return res.body.toString(); + return res.body.toString(); } function scrapePhotos(html, includeThumbnails = true) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); + const $ = cheerio.load(html, { normalizeWhitespace: true }); - return $('.preview .imgLink, .pgFooterThumb a').toArray().map((linkEl) => { - const url = $(linkEl).attr('href'); + return $('.preview .imgLink, .pgFooterThumb a').toArray().map((linkEl) => { + const url = $(linkEl).attr('href'); - if (/\/join|\/createaccount/.test(url)) { - // URL links to join page instead of full photo, extract thumbnail - // /createaccount is used by e.g. Tricky Spa native site - const src = $(linkEl).find('img').attr('src'); + if (/\/join|\/createaccount/.test(url)) { + // URL links to join page instead of full photo, extract thumbnail + // /createaccount is used by e.g. Tricky Spa native site + const src = $(linkEl).find('img').attr('src'); - if (/previews\//.test(src)) { - // resource often serves full photo at a modifier URL anyway, add as primary source - const highRes = src - .replace('previews/', '') - .replace('_tb.jpg', '.jpg'); + if (/previews\//.test(src)) { + // resource often serves full photo at a modifier URL anyway, add as primary source + const highRes = src + .replace('previews/', '') + .replace('_tb.jpg', '.jpg'); - // keep original thumbnail as fallback in case full photo is not available - return [highRes, src]; - } + // keep original thumbnail as fallback in case full photo is not available + return [highRes, src]; + } - if (!includeThumbnails) return null; + if (!includeThumbnails) return null; - return src; - } + return src; + } - // URL links to full photo - return url; - }).filter(Boolean); + // URL links to full photo + return url; + }).filter(Boolean); } async function getPhotos(albumPath, site, includeThumbnails = true) { - const albumUrl = getAlbumUrl(albumPath, site); + const albumUrl = getAlbumUrl(albumPath, site); - if (!albumUrl) { - return []; - } + if (!albumUrl) { + return []; + } - try { - const html = await fetchPhotos(albumUrl); - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const photos = scrapePhotos(html, includeThumbnails); + try { + const html = await fetchPhotos(albumUrl); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const photos = scrapePhotos(html, includeThumbnails); - const lastPage = $('.Gamma_Paginator a.last').attr('href')?.match(/\d+$/)[0]; + const lastPage = $('.Gamma_Paginator a.last').attr('href')?.match(/\d+$/)[0]; - if (lastPage) { - const otherPages = Array.from({ length: Number(lastPage) }, (_value, index) => index + 1).slice(1); + if (lastPage) { + const otherPages = Array.from({ length: Number(lastPage) }, (_value, index) => index + 1).slice(1); - const otherPhotos = await Promise.map(otherPages, async (page) => { - const pageUrl = `${albumUrl}/${page}`; - const pageHtml = await fetchPhotos(pageUrl); + const otherPhotos = await Promise.map(otherPages, async (page) => { + const pageUrl = `${albumUrl}/${page}`; + const pageHtml = await fetchPhotos(pageUrl); - return scrapePhotos(pageHtml, includeThumbnails); - }, { - concurrency: 2, - }); + return scrapePhotos(pageHtml, includeThumbnails); + }, { + concurrency: 2, + }); - return photos.concat(otherPhotos.flat()); - } + return photos.concat(otherPhotos.flat()); + } - return photos; - } catch (error) { - logger.warn(`Failed to fetch ${site.name} photos from ${albumUrl}: ${error.message}`); + return photos; + } catch (error) { + logger.warn(`Failed to fetch ${site.name} photos from ${albumUrl}: ${error.message}`); - return []; - } + return []; + } } async function scrapeApiReleases(json, site) { - return json.map((scene) => { - if (site.parameters?.extract && scene.sitename !== site.parameters.extract) { - return null; - } + return json.map((scene) => { + if (site.parameters?.extract && scene.sitename !== site.parameters.extract) { + return null; + } - const release = { - entryId: scene.clip_id, - title: scene.title, - description: scene.description, - duration: scene.length, - likes: scene.ratings_up, - dislikes: scene.ratings_down, - }; + const release = { + entryId: scene.clip_id, + title: scene.title, + description: scene.description, + duration: scene.length, + likes: scene.ratings_up, + dislikes: scene.ratings_down, + }; - release.path = `/${scene.url_title}/${release.entryId}`; + release.path = `/${scene.url_title}/${release.entryId}`; - if (site.parameters?.scene) release.url = `${site.parameters.scene}${release.path}`; - else if (site.url && site.parameters?.scene !== false) release.url = `${site.url}/en/video${release.path}`; + if (site.parameters?.scene) release.url = `${site.parameters.scene}${release.path}`; + else if (site.url && site.parameters?.scene !== false) release.url = `${site.url}/en/video${release.path}`; - release.date = moment.utc(scene.release_date, 'YYYY-MM-DD').toDate(); - release.actors = scene.actors.map(actor => ({ name: actor.name, gender: actor.gender })); - release.director = scene.directors[0]?.name || null; + release.date = moment.utc(scene.release_date, 'YYYY-MM-DD').toDate(); + release.actors = scene.actors.map(actor => ({ name: actor.name, gender: actor.gender })); + release.director = scene.directors[0]?.name || null; - release.tags = scene.master_categories - .concat(scene.categories?.map(category => category.name)) - .filter(Boolean); // some categories don't have a name + release.tags = scene.master_categories + .concat(scene.categories?.map(category => category.name)) + .filter(Boolean); // some categories don't have a name - const posterPath = scene.pictures.resized || (scene.pictures.nsfw?.top && Object.values(scene.pictures.nsfw.top)[0]); + const posterPath = scene.pictures.resized || (scene.pictures.nsfw?.top && Object.values(scene.pictures.nsfw.top)[0]); - if (posterPath) { - release.poster = [ - `https://images-evilangel.gammacdn.com/movies${posterPath}`, - `https://transform.gammacdn.com/movies${posterPath}`, - ]; - } + if (posterPath) { + release.poster = [ + `https://images-evilangel.gammacdn.com/movies${posterPath}`, + `https://transform.gammacdn.com/movies${posterPath}`, + ]; + } - // release.movie = `${site.url}/en/movie/${scene.url_movie_title}/${scene.movie_id}`; + // release.movie = `${site.url}/en/movie/${scene.url_movie_title}/${scene.movie_id}`; - return release; - }).filter(Boolean); + return release; + }).filter(Boolean); } function scrapeAll(html, site, networkUrl, hasTeaser = true) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const scenesElements = $('li[data-itemtype=scene], div[data-itemtype=scenes]').toArray(); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const scenesElements = $('li[data-itemtype=scene], div[data-itemtype=scenes]').toArray(); - return scenesElements.map((element) => { - const release = {}; + return scenesElements.map((element) => { + const release = {}; - const sceneLinkElement = $(element).find('.sceneTitle a, .tlcTitle a'); + const sceneLinkElement = $(element).find('.sceneTitle a, .tlcTitle a'); - if (site) release.url = `${networkUrl ? site.network.url : site.url}${sceneLinkElement.attr('href')}`; - else release.url = `${networkUrl}${sceneLinkElement.attr('href')}`; + if (site) release.url = `${networkUrl ? site.network.url : site.url}${sceneLinkElement.attr('href')}`; + else release.url = `${networkUrl}${sceneLinkElement.attr('href')}`; - release.title = sceneLinkElement.attr('title'); - release.entryId = $(element).attr('data-itemid'); + release.title = sceneLinkElement.attr('title'); + release.entryId = $(element).attr('data-itemid'); - const dateEl = $(element).find('.sceneDate, .tlcSpecsDate .tlcDetailsValue').text() || null; - if (dateEl) { - release.date = moment - .utc(dateEl, ['MM-DD-YYYY', 'YYYY-MM-DD']) - .toDate(); - } + const dateEl = $(element).find('.sceneDate, .tlcSpecsDate .tlcDetailsValue').text() || null; + if (dateEl) { + release.date = moment + .utc(dateEl, ['MM-DD-YYYY', 'YYYY-MM-DD']) + .toDate(); + } - release.actors = $(element).find('.sceneActors a, .tlcActors a') - .map((actorIndex, actorElement) => $(actorElement).attr('title')) - .toArray(); + release.actors = $(element).find('.sceneActors a, .tlcActors a') + .map((actorIndex, actorElement) => $(actorElement).attr('title')) + .toArray(); - [release.likes, release.dislikes] = $(element).find('.value') - .toArray() - .map(value => Number($(value).text())); + [release.likes, release.dislikes] = $(element).find('.value') + .toArray() + .map(value => Number($(value).text())); - const posterEl = $(element).find('.imgLink img, .tlcImageItem'); - if (posterEl) release.poster = posterEl.attr('data-original') || posterEl.attr('src'); + const posterEl = $(element).find('.imgLink img, .tlcImageItem'); + if (posterEl) release.poster = posterEl.attr('data-original') || posterEl.attr('src'); - if (hasTeaser) { - release.teaser = [ - { src: `https://videothumb.gammacdn.com/600x339/${release.entryId}.mp4` }, - { src: `https://videothumb.gammacdn.com/307x224/${release.entryId}.mp4` }, - ]; - } + if (hasTeaser) { + release.teaser = [ + { src: `https://videothumb.gammacdn.com/600x339/${release.entryId}.mp4` }, + { src: `https://videothumb.gammacdn.com/307x224/${release.entryId}.mp4` }, + ]; + } - return release; - }); + return release; + }); } async function scrapeScene(html, url, site, baseRelease, mobileHtml) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const m$ = mobileHtml && cheerio.load(mobileHtml, { normalizeWhitespace: true }); - const release = { $, url }; + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const m$ = mobileHtml && cheerio.load(mobileHtml, { normalizeWhitespace: true }); + const release = { $, url }; - const json = $('script[type="application/ld+json"]').html(); - const videoJson = $('script:contains("window.ScenePlayerOptions")').html(); + const json = $('script[type="application/ld+json"]').html(); + const videoJson = $('script:contains("window.ScenePlayerOptions")').html(); - const [data, data2] = json ? JSON.parse(json) : []; - const videoData = videoJson && JSON.parse(videoJson.slice(videoJson.indexOf('{'), videoJson.indexOf('};') + 1)); + const [data, data2] = json ? JSON.parse(json) : []; + const videoData = videoJson && JSON.parse(videoJson.slice(videoJson.indexOf('{'), videoJson.indexOf('};') + 1)); - release.entryId = (baseRelease?.path || new URL(url).pathname).match(/\/(\d{2,})(\/|$)/)?.[1]; - release.title = videoData?.playerOptions?.sceneInfos.sceneTitle || data?.name; + release.entryId = (baseRelease?.path || new URL(url).pathname).match(/\/(\d{2,})(\/|$)/)?.[1]; + release.title = videoData?.playerOptions?.sceneInfos.sceneTitle || data?.name; - // date in data object is not the release date of the scene, but the date the entry was added; only use as fallback - const dateString = $('.updatedDate').first().text().trim(); - const dateMatch = dateString.match(/\d{2,4}[-/]\d{2}[-/]\d{2,4}/)?.[0]; + // date in data object is not the release date of the scene, but the date the entry was added; only use as fallback + const dateString = $('.updatedDate').first().text().trim(); + const dateMatch = dateString.match(/\d{2,4}[-/]\d{2}[-/]\d{2,4}/)?.[0]; - if (dateMatch) release.date = moment.utc(dateMatch, ['MM-DD-YYYY', 'YYYY-MM-DD']).toDate(); - else if (data?.dateCreated) release.date = moment.utc(data.dateCreated, 'YYYY-MM-DD').toDate(); - else release.date = videoData.playerOptions.sceneInfos.sceneReleaseDate; + if (dateMatch) release.date = moment.utc(dateMatch, ['MM-DD-YYYY', 'YYYY-MM-DD']).toDate(); + else if (data?.dateCreated) release.date = moment.utc(data.dateCreated, 'YYYY-MM-DD').toDate(); + else release.date = videoData.playerOptions.sceneInfos.sceneReleaseDate; - if (data) { - release.description = data.description; - if (data.director?.[0]?.name) release.director = data.director[0].name; - else if (data2?.director?.[0]?.name) release.director = data2.director[0].name; + if (data) { + release.description = data.description; + if (data.director?.[0]?.name) release.director = data.director[0].name; + else if (data2?.director?.[0]?.name) release.director = data2.director[0].name; - const stars = (data.aggregateRating.ratingValue / data.aggregateRating.bestRating) * 5; - if (stars) release.rating = { stars }; + const stars = (data.aggregateRating.ratingValue / data.aggregateRating.bestRating) * 5; + if (stars) release.rating = { stars }; - release.duration = moment.duration(data.duration.slice(2)).asSeconds(); - } + release.duration = moment.duration(data.duration.slice(2)).asSeconds(); + } - const actors = data?.actor || data2?.actor; + const actors = data?.actor || data2?.actor; - if (actors) { - release.actors = actors.map(actor => ({ - name: actor.name, - gender: actor.gender, - })); - } + if (actors) { + release.actors = actors.map(actor => ({ + name: actor.name, + gender: actor.gender, + })); + } - const hasTrans = release.actors?.some(actor => actor.gender === 'shemale'); - const rawTags = data?.keywords?.split(', ') || data2?.keywords?.split(', ') || []; - release.tags = hasTrans ? [...rawTags, 'transsexual'] : rawTags; + const hasTrans = release.actors?.some(actor => actor.gender === 'shemale'); + const rawTags = data?.keywords?.split(', ') || data2?.keywords?.split(', ') || []; + release.tags = hasTrans ? [...rawTags, 'transsexual'] : rawTags; - const channel = data?.productionCompany?.name || $('.studioLink a, .siteLink a').attr('title')?.trim() || $('.siteNameSpan').text()?.trim().toLowerCase().replace('.com', ''); - if (channel) release.channel = slugify(channel, ''); + const channel = data?.productionCompany?.name || $('.studioLink a, .siteLink a').attr('title')?.trim() || $('.siteNameSpan').text()?.trim().toLowerCase().replace('.com', ''); + if (channel) release.channel = slugify(channel, ''); - if (videoData.picPreview && new URL(videoData.picPreview).pathname.length > 1) release.poster = videoData.picPreview; // sometimes links to just https://images02-fame.gammacdn.com/ + if (videoData.picPreview && new URL(videoData.picPreview).pathname.length > 1) release.poster = videoData.picPreview; // sometimes links to just https://images02-fame.gammacdn.com/ - const photoLink = $('.picturesItem a').attr('href'); - const mobilePhotos = m$ ? m$('.preview-displayer a img').map((photoIndex, photoEl) => $(photoEl).attr('src')).toArray() : []; + const photoLink = $('.picturesItem a').attr('href'); + const mobilePhotos = m$ ? m$('.preview-displayer a img').map((photoIndex, photoEl) => $(photoEl).attr('src')).toArray() : []; - if (photoLink) { - const photos = await getPhotos(photoLink, site, mobilePhotos.length < 3); // only get thumbnails when less than 3 mobile photos are available + if (photoLink) { + const photos = await getPhotos(photoLink, site, mobilePhotos.length < 3); // only get thumbnails when less than 3 mobile photos are available - if (photos.length < 7) release.photos = [...photos, ...mobilePhotos]; // probably only teaser photos available, supplement with mobile album - else release.photos = photos; - } else { - release.photos = mobilePhotos; - } + if (photos.length < 7) release.photos = [...photos, ...mobilePhotos]; // probably only teaser photos available, supplement with mobile album + else release.photos = photos; + } else { + release.photos = mobilePhotos; + } - const trailer = `${videoData.playerOptions.host}${videoData.url}`; - release.trailer = [ - { - src: trailer.replace('hd', 'sm'), - quality: 240, - }, - { - src: trailer.replace('hd', 'med'), - quality: 360, - }, - { - src: trailer.replace('hd', 'big'), - quality: 480, - }, - { - // probably 540p - src: trailer, - quality: parseInt(videoData.sizeOnLoad, 10), - }, - { - src: trailer.replace('hd', '720p'), - quality: 720, - }, - { - src: trailer.replace('hd', '1080p'), - quality: 1080, - }, - { - src: trailer.replace('hd', '4k'), - quality: 2160, - }, - ]; + const trailer = `${videoData.playerOptions.host}${videoData.url}`; + release.trailer = [ + { + src: trailer.replace('hd', 'sm'), + quality: 240, + }, + { + src: trailer.replace('hd', 'med'), + quality: 360, + }, + { + src: trailer.replace('hd', 'big'), + quality: 480, + }, + { + // probably 540p + src: trailer, + quality: parseInt(videoData.sizeOnLoad, 10), + }, + { + src: trailer.replace('hd', '720p'), + quality: 720, + }, + { + src: trailer.replace('hd', '1080p'), + quality: 1080, + }, + { + src: trailer.replace('hd', '4k'), + quality: 2160, + }, + ]; - return release; + return release; } function scrapeActorSearch(html, url, actorName) { - const { document } = new JSDOM(html).window; - const actorLink = document.querySelector(`a[title="${actorName}" i]`); + const { document } = new JSDOM(html).window; + const actorLink = document.querySelector(`a[title="${actorName}" i]`); - return actorLink ? actorLink.href : null; + return actorLink ? actorLink.href : null; } async function fetchActorReleases(profileUrl, getActorReleasesUrl, page = 1, accReleases = []) { - const { origin, pathname } = new URL(profileUrl); - const profilePath = `/${pathname.split('/').slice(-2).join('/')}`; + const { origin, pathname } = new URL(profileUrl); + const profilePath = `/${pathname.split('/').slice(-2).join('/')}`; - const url = getActorReleasesUrl(profilePath, page); - const res = await get(url); + const url = getActorReleasesUrl(profilePath, page); + const res = await get(url); - if (!res.ok) return []; + if (!res.ok) return []; - const releases = scrapeAll(res.html, null, origin); - const nextPage = res.item.qu.url('.Gamma_Paginator a.next'); + const releases = scrapeAll(res.html, null, origin); + const nextPage = res.item.qu.url('.Gamma_Paginator a.next'); - if (nextPage) { - return fetchActorReleases(profileUrl, getActorReleasesUrl, page + 1, accReleases.concat(releases)); - } + if (nextPage) { + return fetchActorReleases(profileUrl, getActorReleasesUrl, page + 1, accReleases.concat(releases)); + } - return accReleases.concat(releases); + return accReleases.concat(releases); } async function scrapeProfile(html, url, actorName, _siteSlug, getActorReleasesUrl, withReleases) { - const { q } = ex(html); + const { q } = ex(html); - const avatar = q('img.actorPicture'); - const hair = q('.actorProfile .attribute_hair_color', true); - const height = q('.actorProfile .attribute_height', true); - const weight = q('.actorProfile .attribute_weight', true); - const alias = q('.actorProfile .attribute_alternate_names', true); - const nationality = q('.actorProfile .attribute_home', true); + const avatar = q('img.actorPicture'); + const hair = q('.actorProfile .attribute_hair_color', true); + const height = q('.actorProfile .attribute_height', true); + const weight = q('.actorProfile .attribute_weight', true); + const alias = q('.actorProfile .attribute_alternate_names', true); + const nationality = q('.actorProfile .attribute_home', true); - const profile = { - name: actorName, - }; + const profile = { + name: actorName, + }; - if (avatar) { - // larger sizes usually available, provide fallbacks - const avatars = [ - avatar.src.replace(/\d+x\d+/, '500x750'), - avatar.src.replace(/\d+x\d+/, '240x360'), - avatar.src.replace(/\d+x\d+/, '200x300'), - avatar.src, - ]; + if (avatar) { + // larger sizes usually available, provide fallbacks + const avatars = [ + avatar.src.replace(/\d+x\d+/, '500x750'), + avatar.src.replace(/\d+x\d+/, '240x360'), + avatar.src.replace(/\d+x\d+/, '200x300'), + avatar.src, + ]; - profile.avatar = avatars; - } + profile.avatar = avatars; + } - profile.description = q('.actorBio p:not(.bioTitle)', true); + profile.description = q('.actorBio p:not(.bioTitle)', true); - if (hair) profile.hair = hair.split(':')[1].trim(); - if (height) profile.height = Number(height.match(/\d+/)[0]); - if (weight) profile.weight = Number(weight.match(/\d+/)[0]); - if (alias) profile.aliases = alias.split(':')[1].trim().split(', '); - if (nationality) profile.nationality = nationality.split(':')[1].trim(); + if (hair) profile.hair = hair.split(':')[1].trim(); + if (height) profile.height = Number(height.match(/\d+/)[0]); + if (weight) profile.weight = Number(weight.match(/\d+/)[0]); + if (alias) profile.aliases = alias.split(':')[1].trim().split(', '); + if (nationality) profile.nationality = nationality.split(':')[1].trim(); - if (getActorReleasesUrl && withReleases) { - profile.releases = await fetchActorReleases(url, getActorReleasesUrl); - } + if (getActorReleasesUrl && withReleases) { + profile.releases = await fetchActorReleases(url, getActorReleasesUrl); + } - return profile; + return profile; } function scrapeApiProfile(data, releases, siteSlug) { - const profile = {}; + const profile = {}; - if (data.male === 1) profile.gender = 'male'; - if (data.female === 1) profile.gender = 'female'; - if (data.shemale === 1 || data.trans === 1) profile.gender = 'transsexual'; + if (data.male === 1) profile.gender = 'male'; + if (data.female === 1) profile.gender = 'female'; + if (data.shemale === 1 || data.trans === 1) profile.gender = 'transsexual'; - if (data.description) profile.description = data.description.trim(); + if (data.description) profile.description = data.description.trim(); - if (data.attributes.ethnicity) profile.ethnicity = data.attributes.ethnicity; - if (data.attributes.eye_color) profile.eyes = data.attributes.eye_color; - if (data.attributes.hair_color) profile.hair = data.attributes.hair_color; + if (data.attributes.ethnicity) profile.ethnicity = data.attributes.ethnicity; + if (data.attributes.eye_color) profile.eyes = data.attributes.eye_color; + if (data.attributes.hair_color) profile.hair = data.attributes.hair_color; - const avatarPath = Object.values(data.pictures).reverse()[0]; - if (avatarPath) profile.avatar = `https://images01-evilangel.gammacdn.com/actors${avatarPath}`; + const avatarPath = Object.values(data.pictures).reverse()[0]; + if (avatarPath) profile.avatar = `https://images01-evilangel.gammacdn.com/actors${avatarPath}`; - profile.releases = releases.map(release => `https://${siteSlug}.com/en/video/${release.url_title}/${release.clip_id}`); + profile.releases = releases.map(release => `https://${siteSlug}.com/en/video/${release.url_title}/${release.clip_id}`); - return profile; + return profile; } function getApiUrl(appId, apiKey) { - const userAgent = 'Algolia for vanilla JavaScript (lite) 3.27.0;instantsearch.js 2.7.4;JS Helper 2.26.0'; + const userAgent = 'Algolia for vanilla JavaScript (lite) 3.27.0;instantsearch.js 2.7.4;JS Helper 2.26.0'; - const apiUrl = `https://${appId.toLowerCase()}-dsn.algolia.net/1/indexes/*/queries?x-algolia-agent=${userAgent}&x-algolia-application-id=${appId}&x-algolia-api-key=${apiKey}`; + const apiUrl = `https://${appId.toLowerCase()}-dsn.algolia.net/1/indexes/*/queries?x-algolia-agent=${userAgent}&x-algolia-application-id=${appId}&x-algolia-api-key=${apiKey}`; - return { - appId, - apiKey, - userAgent, - apiUrl, - }; + return { + appId, + apiKey, + userAgent, + apiUrl, + }; } async function fetchApiCredentials(referer, site) { - if (site?.parameters?.appId && site?.parameters?.apiKey) { - return getApiUrl(site.parameters.appId, site.parameters.apiKey); - } + if (site?.parameters?.appId && site?.parameters?.apiKey) { + return getApiUrl(site.parameters.appId, site.parameters.apiKey); + } - const res = await bhttp.get(referer); - const body = res.body.toString(); + const res = await bhttp.get(referer); + const body = res.body.toString(); - const apiLine = body.split('\n').find(bodyLine => bodyLine.match('apiKey')); + const apiLine = body.split('\n').find(bodyLine => bodyLine.match('apiKey')); - if (!apiLine) { - throw new Error(`No Gamma API key found for ${referer}`); - } + if (!apiLine) { + throw new Error(`No Gamma API key found for ${referer}`); + } - const apiSerial = apiLine.slice(apiLine.indexOf('{'), apiLine.indexOf('};') + 1); - const apiData = JSON.parse(apiSerial); + const apiSerial = apiLine.slice(apiLine.indexOf('{'), apiLine.indexOf('};') + 1); + const apiData = JSON.parse(apiSerial); - const { applicationID: appId, apiKey } = apiData.api.algolia; + const { applicationID: appId, apiKey } = apiData.api.algolia; - return getApiUrl(appId, apiKey); + return getApiUrl(appId, apiKey); } async function fetchApiLatest(site, page = 1, preData, include, upcoming = false) { - const referer = site.parameters?.referer || `${site.parameters?.networkReferer ? site.network.url : site.url}/en/videos`; - const { apiUrl } = await fetchApiCredentials(referer, site); + const referer = site.parameters?.referer || `${site.parameters?.networkReferer ? site.network.url : site.url}/en/videos`; + const { apiUrl } = await fetchApiCredentials(referer, site); - const res = await bhttp.post(apiUrl, { - requests: [ - { - indexName: 'all_scenes', - params: `query=&hitsPerPage=36&maxValuesPerFacet=100&page=${page - 1}&facetFilters=[["lesbian:"],["bisex:"],["shemale:"],["upcoming:${upcoming ? 1 : 0}"]]&filters=sitename:${site.slug} OR channels.id:${site.slug}`, - }, - ], - }, { - headers: { - Referer: referer, - }, - encodeJSON: true, - }); + const res = await bhttp.post(apiUrl, { + requests: [ + { + indexName: 'all_scenes', + params: `query=&hitsPerPage=36&maxValuesPerFacet=100&page=${page - 1}&facetFilters=[["lesbian:"],["bisex:"],["shemale:"],["upcoming:${upcoming ? 1 : 0}"]]&filters=sitename:${site.slug} OR channels.id:${site.slug}`, + }, + ], + }, { + headers: { + Referer: referer, + }, + encodeJSON: true, + }); - if (res.statusCode === 200 && res.body.results?.[0]?.hits) { - return scrapeApiReleases(res.body.results[0].hits, site); - } + if (res.statusCode === 200 && res.body.results?.[0]?.hits) { + return scrapeApiReleases(res.body.results[0].hits, site); + } - return []; + return []; } async function fetchApiUpcoming(site, page = 1, preData, include) { - return fetchApiLatest(site, page, preData, include, true); + return fetchApiLatest(site, page, preData, include, true); } function getLatestUrl(site, page) { - if (site.parameters?.latest) { - if (/^http/.test(site.parameters.latest)) { - return /%d/.test(site.parameters.latest) - ? util.format(site.parameters.latest, page) - : `${site.parameters.latest}${page}`; - } + if (site.parameters?.latest) { + if (/^http/.test(site.parameters.latest)) { + return /%d/.test(site.parameters.latest) + ? util.format(site.parameters.latest, page) + : `${site.parameters.latest}${page}`; + } - return /%d/.test(site.parameters.latest) - ? util.format(`${site.url}${site.parameters.latest}`, page) - : `${site.url}${site.parameters.latest}${page}`; - } + return /%d/.test(site.parameters.latest) + ? util.format(`${site.url}${site.parameters.latest}`, page) + : `${site.url}${site.parameters.latest}${page}`; + } - return `${site.url}/en/videos/AllCategories/0/${page}`; + return `${site.url}/en/videos/AllCategories/0/${page}`; } function getUpcomingUrl(site) { - if (site.parameters?.upcoming) { - return /^http/.test(site.parameters.upcoming) - ? `${site.parameters.upcoming}` - : `${site.url}${site.parameters.upcoming}`; - } + if (site.parameters?.upcoming) { + return /^http/.test(site.parameters.upcoming) + ? `${site.parameters.upcoming}` + : `${site.url}${site.parameters.upcoming}`; + } - return `${site.url}/en/videos/AllCategories/0/1/upcoming`; + return `${site.url}/en/videos/AllCategories/0/1/upcoming`; } async function fetchLatest(site, page = 1) { - const url = getLatestUrl(site, page); - const res = await bhttp.get(url); + const url = getLatestUrl(site, page); + const res = await bhttp.get(url); - return scrapeAll(res.body.toString(), site); + return scrapeAll(res.body.toString(), site); } async function fetchUpcoming(site) { - const url = getUpcomingUrl(site); - const res = await bhttp.get(url); + const url = getUpcomingUrl(site); + const res = await bhttp.get(url); - return scrapeAll(res.body.toString(), site, null, false); + return scrapeAll(res.body.toString(), site, null, false); } function getDeepUrl(url, site, baseRelease, mobile) { - const filter = new Set(['en', 'video', 'scene', site.slug, site.network.slug]); - const pathname = baseRelease?.path || new URL(url).pathname - .split('/') - .filter(component => !filter.has(component)) - .join('/'); // reduce to scene ID and title slug + const filter = new Set(['en', 'video', 'scene', site.slug, site.network.slug]); + const pathname = baseRelease?.path || new URL(url).pathname + .split('/') + .filter(component => !filter.has(component)) + .join('/'); // reduce to scene ID and title slug - const sceneId = baseRelease?.entryId || pathname.match(/\/(\d+)\//)?.[1]; + const sceneId = baseRelease?.entryId || pathname.match(/\/(\d+)\//)?.[1]; - if (mobile && /%d/.test(mobile)) { - return util.format(mobile, sceneId); - } + if (mobile && /%d/.test(mobile)) { + return util.format(mobile, sceneId); + } - if (mobile && sceneId) { - return `${mobile}${pathname}`; - } + if (mobile && sceneId) { + return `${mobile}${pathname}`; + } - if (site.parameters?.deep) { - return `${site.parameters.deep}${pathname}`; - } + if (site.parameters?.deep) { + return `${site.parameters.deep}${pathname}`; + } - return url; + return url; } async function fetchScene(url, site, baseRelease) { - if (site.parameters?.deep === false) { - return baseRelease; - } + if (site.parameters?.deep === false) { + return baseRelease; + } - const deepUrl = getDeepUrl(url, site, baseRelease); - const mobileUrl = getDeepUrl(url, site, baseRelease, site.parameters?.mobile || site.network.parameters?.mobile); + const deepUrl = getDeepUrl(url, site, baseRelease); + const mobileUrl = getDeepUrl(url, site, baseRelease, site.parameters?.mobile || site.network.parameters?.mobile); - if (deepUrl) { - const [res, mobileRes] = await Promise.all([ - bhttp.get(deepUrl), - mobileUrl && bhttp.get(mobileUrl, { - headers: { - // don't redirect to main site - 'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Mobile Safari/537.36', - }, - }), - ]); + if (deepUrl) { + const [res, mobileRes] = await Promise.all([ + bhttp.get(deepUrl), + mobileUrl && bhttp.get(mobileUrl, { + headers: { + // don't redirect to main site + 'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Mobile Safari/537.36', + }, + }), + ]); - if (res.statusCode === 200) { - const mobileBody = mobileRes?.statusCode === 200 ? mobileRes.body.toString() : null; - const scene = await scrapeScene(res.body.toString(), url, site, baseRelease, mobileBody); - return { ...scene, deepUrl }; - } - } + if (res.statusCode === 200) { + const mobileBody = mobileRes?.statusCode === 200 ? mobileRes.body.toString() : null; + const scene = await scrapeScene(res.body.toString(), url, site, baseRelease, mobileBody); + return { ...scene, deepUrl }; + } + } - return null; + return null; } async function fetchActorScenes(actorName, apiUrl, siteSlug) { - const res = await bhttp.post(apiUrl, { - requests: [ - { - indexName: 'all_scenes', - params: `query=&filters=sitename:${siteSlug}&hitsPerPage=36&maxValuesPerFacet=100&page=0&facetFilters=[["lesbian:"],["bisex:"],["shemale:"],["actors.name:${actorName}"]]`, - }, - ], - }, { - headers: { - Referer: `https://www.${siteSlug}.com/en/videos`, - }, - encodeJSON: true, - }); + const res = await bhttp.post(apiUrl, { + requests: [ + { + indexName: 'all_scenes', + params: `query=&filters=sitename:${siteSlug}&hitsPerPage=36&maxValuesPerFacet=100&page=0&facetFilters=[["lesbian:"],["bisex:"],["shemale:"],["actors.name:${actorName}"]]`, + }, + ], + }, { + headers: { + Referer: `https://www.${siteSlug}.com/en/videos`, + }, + encodeJSON: true, + }); - if (res.statusCode === 200 && res.body.results[0].hits.length > 0) { - return res.body.results[0].hits; - } + if (res.statusCode === 200 && res.body.results[0].hits.length > 0) { + return res.body.results[0].hits; + } - return []; + return []; } async function fetchProfile(actorName, siteSlug, altSearchUrl, getActorReleasesUrl, include) { - const actorSlug = actorName.toLowerCase().replace(/\s+/, '+'); - const searchUrl = altSearchUrl - ? `https://www.${siteSlug}.com/en/search/${actorSlug}/1/actor` - : `https://www.${siteSlug}.com/en/search/${siteSlug}/actor/${actorSlug}`; - const searchRes = await bhttp.get(searchUrl); + const actorSlug = actorName.toLowerCase().replace(/\s+/, '+'); + const searchUrl = altSearchUrl + ? `https://www.${siteSlug}.com/en/search/${actorSlug}/1/actor` + : `https://www.${siteSlug}.com/en/search/${siteSlug}/actor/${actorSlug}`; + const searchRes = await bhttp.get(searchUrl); - if (searchRes.statusCode !== 200) { - return null; - } + if (searchRes.statusCode !== 200) { + return null; + } - const actorUrl = scrapeActorSearch(searchRes.body.toString(), searchUrl, actorName); + const actorUrl = scrapeActorSearch(searchRes.body.toString(), searchUrl, actorName); - if (actorUrl) { - const url = `https://${siteSlug}.com${actorUrl}`; - const actorRes = await bhttp.get(url); + if (actorUrl) { + const url = `https://${siteSlug}.com${actorUrl}`; + const actorRes = await bhttp.get(url); - if (actorRes.statusCode !== 200) { - return null; - } + if (actorRes.statusCode !== 200) { + return null; + } - return scrapeProfile(actorRes.body.toString(), url, actorName, siteSlug, getActorReleasesUrl, include.scenes); - } + return scrapeProfile(actorRes.body.toString(), url, actorName, siteSlug, getActorReleasesUrl, include.scenes); + } - return null; + return null; } async function fetchApiProfile(actorName, siteSlug) { - const actorSlug = encodeURI(actorName); - const referer = `https://www.${siteSlug}.com/en/search`; + const actorSlug = encodeURI(actorName); + const referer = `https://www.${siteSlug}.com/en/search`; - const { apiUrl } = await fetchApiCredentials(referer); + const { apiUrl } = await fetchApiCredentials(referer); - const res = await bhttp.post(apiUrl, { - requests: [ - { - indexName: 'all_actors', - params: `query=${actorSlug}`, - }, - ], - }, { - headers: { - Referer: referer, - }, - encodeJSON: true, - }); + const res = await bhttp.post(apiUrl, { + requests: [ + { + indexName: 'all_actors', + params: `query=${actorSlug}`, + }, + ], + }, { + headers: { + Referer: referer, + }, + encodeJSON: true, + }); - if (res.statusCode === 200 && res.body.results[0].hits.length > 0) { - const actorData = res.body.results[0].hits.find(actor => slugify(actor.name) === slugify(actorName)); + if (res.statusCode === 200 && res.body.results[0].hits.length > 0) { + const actorData = res.body.results[0].hits.find(actor => slugify(actor.name) === slugify(actorName)); - if (actorData) { - const actorScenes = await fetchActorScenes(actorData.name, apiUrl, siteSlug); + if (actorData) { + const actorScenes = await fetchActorScenes(actorData.name, apiUrl, siteSlug); - return scrapeApiProfile(actorData, actorScenes, siteSlug); - } - } + return scrapeApiProfile(actorData, actorScenes, siteSlug); + } + } - return null; + return null; } module.exports = { - fetchApiLatest, - fetchApiProfile, - fetchApiUpcoming, - fetchLatest, - fetchProfile, - fetchScene, - fetchUpcoming, - getPhotos, - scrapeApiProfile, - scrapeApiReleases, - scrapeProfile, - scrapeAll, - scrapeScene, + fetchApiLatest, + fetchApiProfile, + fetchApiUpcoming, + fetchLatest, + fetchProfile, + fetchScene, + fetchUpcoming, + getPhotos, + scrapeApiProfile, + scrapeApiReleases, + scrapeProfile, + scrapeAll, + scrapeScene, }; diff --git a/src/scrapers/girlsway.js b/src/scrapers/girlsway.js index 151bb845..518aabe4 100644 --- a/src/scrapers/girlsway.js +++ b/src/scrapers/girlsway.js @@ -4,7 +4,7 @@ const { fetchApiLatest, fetchApiUpcoming, fetchScene } = require('./gamma'); module.exports = { - fetchLatest: fetchApiLatest, - fetchScene, - fetchUpcoming: fetchApiUpcoming, + fetchLatest: fetchApiLatest, + fetchScene, + fetchUpcoming: fetchApiUpcoming, }; diff --git a/src/scrapers/hush.js b/src/scrapers/hush.js index 7cfa8c46..28983710 100644 --- a/src/scrapers/hush.js +++ b/src/scrapers/hush.js @@ -8,404 +8,403 @@ const slugify = require('../utils/slugify'); const { feetInchesToCm } = require('../utils/convert'); async function getChannelRegExp(site) { - if (!['hushpass', 'interracialpass'].includes(site.network.slug)) return null; + if (!['hushpass', 'interracialpass'].includes(site.network.slug)) return null; - const sites = await knex('sites').where('network_id', site.network.id); + const sites = await knex('sites').where('network_id', site.network.id); - return new RegExp(sites.map(channel => channel.parameters?.match || channel.name).join('|'), 'i'); + return new RegExp(sites.map(channel => channel.parameters?.match || channel.name).join('|'), 'i'); } function deriveEntryId(release) { - if (release.date && release.title) { - return `${slugify(fd(release.date, 'YYYY-MM-DD'))}-${slugify(release.title)}`; - } + if (release.date && release.title) { + return `${slugify(fd(release.date, 'YYYY-MM-DD'))}-${slugify(release.title)}`; + } - return null; + return null; } function extractPoster(posterPath, site, baseRelease) { - if (posterPath && !/400.jpg/.test(posterPath)) { - const poster = `${site.parameters?.media || site.url}${posterPath}`; - const posterSources = [ - poster, - // upscaled - poster.replace('-1x', '-2x'), - poster.replace('-1x', '-3x'), - ]; + if (posterPath && !/400.jpg/.test(posterPath)) { + const poster = `${site.parameters?.media || site.url}${posterPath}`; + const posterSources = [ + poster, + // upscaled + poster.replace('-1x', '-2x'), + poster.replace('-1x', '-3x'), + ]; - if (baseRelease?.poster) { - return [posterSources, [baseRelease.poster]]; - } + if (baseRelease?.poster) { + return [posterSources, [baseRelease.poster]]; + } - return [posterSources, []]; - } + return [posterSources, []]; + } - return [baseRelease?.poster || null, []]; + return [baseRelease?.poster || null, []]; } function getImageWithFallbacks(q, selector, site, el) { - const sources = el - ? [ - q(el, selector, 'src0_3x'), - q(el, selector, 'src0_2x'), - q(el, selector, 'src0_1x'), - ] - : [ - q(selector, 'src0_3x'), - q(selector, 'src0_2x'), - q(selector, 'src0_1x'), - ]; + const sources = el + ? [ + q(el, selector, 'src0_3x'), + q(el, selector, 'src0_2x'), + q(el, selector, 'src0_1x'), + ] + : [ + q(selector, 'src0_3x'), + q(selector, 'src0_2x'), + q(selector, 'src0_1x'), + ]; - return sources.filter(Boolean).map(src => `${site.parameters?.media || site.url}${src}`); + return sources.filter(Boolean).map(src => `${site.parameters?.media || site.url}${src}`); } function scrapeAll(scenes, site) { - return scenes.map(({ qu }) => { - const release = {}; + return scenes.map(({ qu }) => { + const release = {}; - release.title = qu.q('h3 a', 'title') || qu.q('h3 a', true); - release.url = qu.url('h3 a'); + release.title = qu.q('h3 a', 'title') || qu.q('h3 a', true); + release.url = qu.url('h3 a'); - release.date = qu.date('.modeldata p', 'YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/); - release.duration = qu.dur('.modeldata p'); + release.date = qu.date('.modeldata p', 'YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/); + release.duration = qu.dur('.modeldata p'); - if (/bts|behind the scenes/i.test(release.title)) release.tags = ['behind the scenes']; + if (/bts|behind the scenes/i.test(release.title)) release.tags = ['behind the scenes']; - release.poster = getImageWithFallbacks(qu.q, '.modelimg img', site); + release.poster = getImageWithFallbacks(qu.q, '.modelimg img', site); - // release.entryId = q('.modelimg img', 'id').match(/set-target-(\d+)/)[1]; - release.entryId = deriveEntryId(release); + // release.entryId = q('.modelimg img', 'id').match(/set-target-(\d+)/)[1]; + release.entryId = deriveEntryId(release); - return release; - }); + return release; + }); } function scrapeAllT1(scenes, site, accSiteReleases) { - return scenes.map(({ qu }) => { - const release = {}; + return scenes.map(({ qu }) => { + const release = {}; - release.title = qu.q('h4 a', 'title') || qu.q('h4 a', true); - release.url = qu.url('h4 a'); + release.title = qu.q('h4 a', 'title') || qu.q('h4 a', true); + release.url = qu.url('h4 a'); - release.date = qu.date('.more-info-div', 'MMM D, YYYY'); - release.duration = qu.dur('.more-info-div'); + release.date = qu.date('.more-info-div', 'MMM D, YYYY'); + release.duration = qu.dur('.more-info-div'); - if (/bts|behind the scenes/i.test(release.title)) release.tags = ['behind the scenes']; + if (/bts|behind the scenes/i.test(release.title)) release.tags = ['behind the scenes']; - const posterPath = qu.q('.img-div img', 'src0_1x') || qu.img('img.video_placeholder'); + const posterPath = qu.q('.img-div img', 'src0_1x') || qu.img('img.video_placeholder'); - if (posterPath) { - const poster = /^http/.test(posterPath) ? posterPath : `${site.parameters?.media || site.url}${posterPath}`; + if (posterPath) { + const poster = /^http/.test(posterPath) ? posterPath : `${site.parameters?.media || site.url}${posterPath}`; - release.poster = [ - poster.replace('-1x', '-3x'), - poster.replace('-1x', '-2x'), - poster, - ]; - } + release.poster = [ + poster.replace('-1x', '-3x'), + poster.replace('-1x', '-2x'), + poster, + ]; + } - // release.entryId = q('.img-div img', 'id')?.match(/set-target-(\d+)/)[1]; - release.entryId = deriveEntryId(release); + // release.entryId = q('.img-div img', 'id')?.match(/set-target-(\d+)/)[1]; + release.entryId = deriveEntryId(release); - if (site.parameters?.accFilter && accSiteReleases?.map(accRelease => accRelease.entryId).includes(release.entryId)) { - // filter out releases that were already scraped from a categorized site - return null; - } + if (site.parameters?.accFilter && accSiteReleases?.map(accRelease => accRelease.entryId).includes(release.entryId)) { + // filter out releases that were already scraped from a categorized site + return null; + } - return release; - }).filter(Boolean); + return release; + }).filter(Boolean); } function scrapeAllTour(scenes) { - return scenes.map(({ qu }) => { - const release = {}; + return scenes.map(({ qu }) => { + const release = {}; - release.title = qu.q('h4 a', true); - release.url = qu.url('a'); - release.date = qu.date('.tour_update_models + span', 'YYYY-MM-DD'); + release.title = qu.q('h4 a', true); + release.url = qu.url('a'); + release.date = qu.date('.tour_update_models + span', 'YYYY-MM-DD'); - release.actors = qu.all('.tour_update_models a', true); + release.actors = qu.all('.tour_update_models a', true); - release.poster = qu.img('a img'); + release.poster = qu.img('a img'); - release.entryId = deriveEntryId(release); + release.entryId = deriveEntryId(release); - return release; - }); + return release; + }); } function scrapeScene({ html, qu }, site, url, baseRelease) { - const release = { url }; + const release = { url }; - release.title = qu.q('.centerwrap h2', true); - release.description = qu.q('.videocontent p', true); + release.title = qu.q('.centerwrap h2', true); + release.description = qu.q('.videocontent p', true); - release.date = qu.date('.videodetails .date', 'MM/DD/YYYY'); - release.duration = qu.dur('.videodetails .date'); + release.date = qu.date('.videodetails .date', 'MM/DD/YYYY'); + release.duration = qu.dur('.videodetails .date'); - release.actors = qu.all('.modelname a', true); + release.actors = qu.all('.modelname a', true); - const posterPath = html.match(/poster="([\w-/.]+)"/)?.[1]; - [release.poster, release.photos] = extractPoster(posterPath, site, baseRelease); + const posterPath = html.match(/poster="([\w-/.]+)"/)?.[1]; + [release.poster, release.photos] = extractPoster(posterPath, site, baseRelease); - const trailerPath = html.match(/\/trailers\/.*.mp4/); - if (trailerPath) release.trailer = { src: `${site.parameters?.media || site.url}${trailerPath}` }; + const trailerPath = html.match(/\/trailers\/.*.mp4/); + if (trailerPath) release.trailer = { src: `${site.parameters?.media || site.url}${trailerPath}` }; - const stars = qu.q('.modelrates + p', true).match(/\d.\d/)?.[0]; - if (stars) release.stars = Number(stars); + const stars = qu.q('.modelrates + p', true).match(/\d.\d/)?.[0]; + if (stars) release.stars = Number(stars); - // release.entryId = html.match(/set-target-(\d+)/)[1]; - release.entryId = deriveEntryId(release); + // release.entryId = html.match(/set-target-(\d+)/)[1]; + release.entryId = deriveEntryId(release); - return release; + return release; } function scrapeSceneT1({ html, qu }, site, url, baseRelease, channelRegExp) { - const release = { url }; + const release = { url }; - release.title = qu.q('.trailer-section-head .section-title', true); - release.description = qu.text('.row .update-info-block'); + release.title = qu.q('.trailer-section-head .section-title', true); + release.description = qu.text('.row .update-info-block'); - release.date = qu.date('.update-info-row', 'MMM D, YYYY', /\w+ \d{1,2}, \d{4}/); - release.duration = qu.dur('.update-info-row:nth-child(2)'); + release.date = qu.date('.update-info-row', 'MMM D, YYYY', /\w+ \d{1,2}, \d{4}/); + release.duration = qu.dur('.update-info-row:nth-child(2)'); - release.actors = qu.all('.models-list-thumbs a').map(el => ({ - name: qu.q(el, 'span', true), - avatar: getImageWithFallbacks(qu.q, 'img', site, el), - })); + release.actors = qu.all('.models-list-thumbs a').map(el => ({ + name: qu.q(el, 'span', true), + avatar: getImageWithFallbacks(qu.q, 'img', site, el), + })); - release.tags = qu.all('.tags a', true); + release.tags = qu.all('.tags a', true); - // const posterPath = html.match(/poster="(.*\.jpg)/)?.[1]; - const posterPath = qu.q('.player-thumb img', 'src0_1x'); - [release.poster, release.photos] = extractPoster(posterPath, site, baseRelease); + // const posterPath = html.match(/poster="(.*\.jpg)/)?.[1]; + const posterPath = qu.q('.player-thumb img', 'src0_1x'); + [release.poster, release.photos] = extractPoster(posterPath, site, baseRelease); - const trailer = html.match(/ channelRegExp.test(tag)); + if (channelRegExp) { + const channel = release.tags.find(tag => channelRegExp.test(tag)); - if (channel) { - release.channel = { - force: true, - slug: slugify(channel, ''), - }; - } - } + if (channel) { + release.channel = { + force: true, + slug: slugify(channel, ''), + }; + } + } - // release.entryId = q('.player-thumb img', 'id')?.match(/set-target-(\d+)/)[1]; - release.entryId = deriveEntryId(release); + // release.entryId = q('.player-thumb img', 'id')?.match(/set-target-(\d+)/)[1]; + release.entryId = deriveEntryId(release); - return release; + return release; } function scrapeSceneTour({ html, qu }, site, url) { - const release = {}; + const release = {}; - if (url) release.url = url; - release.title = qu.q('.update_title, .video-title', true); - release.description = qu.q('.latest_update_description, .video-summary', true); + if (url) release.url = url; + release.title = qu.q('.update_title, .video-title', true); + release.description = qu.q('.latest_update_description, .video-summary', true); - const date = qu.date('.availdate, .update_date', 'YYYY-MM-DD'); - if (date) release.date = date; + const date = qu.date('.availdate, .update_date', 'YYYY-MM-DD'); + if (date) release.date = date; - release.actors = qu.all('.update_block_info .tour_update_models a, .video-model .tour_update_models a', true); - release.tags = qu.all('.update_tags a, .tour_update_tags a', true); + release.actors = qu.all('.update_block_info .tour_update_models a, .video-model .tour_update_models a', true); + release.tags = qu.all('.update_tags a, .tour_update_tags a', true); - const [photo, poster, ...photos] = qu.imgs('.update_image img:not(.play_icon_overlay)'); - if (poster || photo) release.poster = poster || photo; - if ((photo && poster) || photos) release.photos = poster ? [photo, ...photos] : photos; // don't use first photo when already used as fallback poster + const [photo, poster, ...photos] = qu.imgs('.update_image img:not(.play_icon_overlay)'); + if (poster || photo) release.poster = poster || photo; + if ((photo && poster) || photos) release.photos = poster ? [photo, ...photos] : photos; // don't use first photo when already used as fallback poster - if (release.date) release.entryId = deriveEntryId(release); + if (release.date) release.entryId = deriveEntryId(release); - const trailerCode = qu.q('.update_image a', 'onclick'); - const trailerPath = trailerCode?.match(/tload\('(.*)'\)/)?.[1] || html.match(/\/trailer\/.*\.mp4/)?.[0]; - if (trailerPath && /^http/.test(trailerPath)) release.trailer = { src: trailerPath }; - else if (trailerPath) release.trailer = { src: `${site.parameters?.media || site.url}${trailerPath}` }; + const trailerCode = qu.q('.update_image a', 'onclick'); + const trailerPath = trailerCode?.match(/tload\('(.*)'\)/)?.[1] || html.match(/\/trailer\/.*\.mp4/)?.[0]; + if (trailerPath && /^http/.test(trailerPath)) release.trailer = { src: trailerPath }; + else if (trailerPath) release.trailer = { src: `${site.parameters?.media || site.url}${trailerPath}` }; - return release; + return release; } function scrapeProfile({ el, qu }, site) { - const profile = {}; + const profile = {}; - const bio = qu.texts('.stats p').reduce((acc, info) => { - const [key, value] = info.split(':'); + const bio = qu.texts('.stats p').reduce((acc, info) => { + const [key, value] = info.split(':'); - return { - ...acc, - [slugify(key, '_')]: value.trim(), - }; - }, {}); + return { + ...acc, + [slugify(key, '_')]: value.trim(), + }; + }, {}); - if (bio.measurements) { - const [bust, waist, hip] = bio.measurements.split('-'); + if (bio.measurements) { + const [bust, waist, hip] = bio.measurements.split('-'); - if (bust) profile.bust = bust; - if (waist) profile.waist = Number(waist); - if (hip) profile.hip = Number(hip); - } + if (bust) profile.bust = bust; + if (waist) profile.waist = Number(waist); + if (hip) profile.hip = Number(hip); + } - if (bio.age) profile.age = Number(bio.age); - if (bio.height) profile.height = feetInchesToCm(bio.height); + if (bio.age) profile.age = Number(bio.age); + if (bio.height) profile.height = feetInchesToCm(bio.height); - profile.avatar = getImageWithFallbacks(qu.q, '.profileimg img', site); + profile.avatar = getImageWithFallbacks(qu.q, '.profileimg img', site); - const qReleases = ctxa(el, '.modelFeatures .modelfeature'); - profile.releases = scrapeAll(qReleases, site); + const qReleases = ctxa(el, '.modelFeatures .modelfeature'); + profile.releases = scrapeAll(qReleases, site); - return profile; + return profile; } function scrapeProfileT1({ el, qu }, site) { - const profile = {}; + const profile = {}; - const bio = qu.all('.detail-div + .detail-div p, .detail-div p', true).reduce((acc, info) => { - const [key, value] = info.split(':'); + const bio = qu.all('.detail-div + .detail-div p, .detail-div p', true).reduce((acc, info) => { + const [key, value] = info.split(':'); - if (!value) return acc; + if (!value) return acc; - return { - ...acc, - [slugify(key, '_')]: value.trim(), - }; - }, {}); + return { + ...acc, + [slugify(key, '_')]: value.trim(), + }; + }, {}); - if (bio.measurements) { - const [bust, waist, hip] = bio.measurements.split('-'); + if (bio.measurements) { + const [bust, waist, hip] = bio.measurements.split('-'); - if (bust) profile.bust = bust; - if (waist) profile.waist = Number(waist); - if (hip) profile.hip = Number(hip); - } + if (bust) profile.bust = bust; + if (waist) profile.waist = Number(waist); + if (hip) profile.hip = Number(hip); + } - if (bio.fun_fact) profile.description = bio.fun_fact; - if (bio.age) profile.age = Number(bio.age); + if (bio.fun_fact) profile.description = bio.fun_fact; + if (bio.age) profile.age = Number(bio.age); - const heightMetric = bio.height?.match(/(\d{3})(\b|c)/); - const heightImperial = bio.height?.match(/\d{1}(\.\d)?/g); - if (heightMetric) profile.height = Number(heightMetric[1]); - if (heightImperial) profile.height = feetInchesToCm(Number(heightImperial[0]), Number(heightImperial[1])); + const heightMetric = bio.height?.match(/(\d{3})(\b|c)/); + const heightImperial = bio.height?.match(/\d{1}(\.\d)?/g); + if (heightMetric) profile.height = Number(heightMetric[1]); + if (heightImperial) profile.height = feetInchesToCm(Number(heightImperial[0]), Number(heightImperial[1])); - profile.avatar = getImageWithFallbacks(qu.q, '.img-div img', site); + profile.avatar = getImageWithFallbacks(qu.q, '.img-div img', site); - const qReleases = ctxa(el, '.item-video'); - profile.releases = scrapeAllT1(qReleases, site); + const qReleases = ctxa(el, '.item-video'); + profile.releases = scrapeAllT1(qReleases, site); - return profile; + return profile; } function scrapeProfileTour({ el, qu }, site) { - const profile = {}; + const profile = {}; - const bio = qu.texts('.model_bio').reduce((acc, info) => { - const [key, value] = info.split(':'); + const bio = qu.texts('.model_bio').reduce((acc, info) => { + const [key, value] = info.split(':'); - return { - ...acc, - [slugify(key, '_')]: value.trim(), - }; - }, {}); + return { + ...acc, + [slugify(key, '_')]: value.trim(), + }; + }, {}); - if (bio.date_of_birth) profile.birthdate = ed(bio.date_of_birth, 'MMMM D, YYYY'); - if (bio.birthplace) profile.birthPlace = bio.birthplace; - if (bio.fun_fact) profile.description = bio.fun_fact; + if (bio.date_of_birth) profile.birthdate = ed(bio.date_of_birth, 'MMMM D, YYYY'); + if (bio.birthplace) profile.birthPlace = bio.birthplace; + if (bio.fun_fact) profile.description = bio.fun_fact; - if (bio.ethnicity) profile.ethnicity = bio.ethnicity; + if (bio.ethnicity) profile.ethnicity = bio.ethnicity; - if (bio.height) profile.height = Number(bio.height.match(/^\d{2,3}/)?.[0]); - if (bio.weight) profile.weight = Number(bio.weight.match(/^\d{2,3}/)?.[0]); + if (bio.height) profile.height = Number(bio.height.match(/^\d{2,3}/)?.[0]); + if (bio.weight) profile.weight = Number(bio.weight.match(/^\d{2,3}/)?.[0]); - if (bio.measurements) { - const [bust, waist, hip] = bio.measurements.split('-'); + if (bio.measurements) { + const [bust, waist, hip] = bio.measurements.split('-'); - if (bust) profile.bust = bust; - if (waist) profile.waist = Number(waist); - if (hip) profile.hip = Number(hip); - } + if (bust) profile.bust = bust; + if (waist) profile.waist = Number(waist); + if (hip) profile.hip = Number(hip); + } - if (bio.natural_breasts && /yes/i.test(bio.natural_breasts)) profile.naturalBoobs = true; - if (bio.natural_breasts && /no/i.test(bio.natural_breasts)) profile.naturalBoobs = false; + if (bio.natural_breasts && /yes/i.test(bio.natural_breasts)) profile.naturalBoobs = true; + if (bio.natural_breasts && /no/i.test(bio.natural_breasts)) profile.naturalBoobs = false; - if (bio.tattoos && /yes/i.test(bio.tattoos)) profile.hasTattoos = true; - if (bio.tattoos && /no/i.test(bio.tattoos)) profile.hasTattoos = false; - if (bio.piercings && /yes/i.test(bio.piercings)) profile.hasPiercings = true; - if (bio.piercings && /no/i.test(bio.piercings)) profile.hasPiercings = false; + if (bio.tattoos && /yes/i.test(bio.tattoos)) profile.hasTattoos = true; + if (bio.tattoos && /no/i.test(bio.tattoos)) profile.hasTattoos = false; + if (bio.piercings && /yes/i.test(bio.piercings)) profile.hasPiercings = true; + if (bio.piercings && /no/i.test(bio.piercings)) profile.hasPiercings = false; - if (bio.aliases) profile.aliases = bio.aliases.split(',').map(alias => alias.trim()); + if (bio.aliases) profile.aliases = bio.aliases.split(',').map(alias => alias.trim()); - profile.avatar = getImageWithFallbacks(qu.q, '.model_picture img', site); + profile.avatar = getImageWithFallbacks(qu.q, '.model_picture img', site); - const qReleases = ctxa(el, '.update_block'); - profile.releases = qReleases.map((qRelease) => { - const url = qRelease.qu.url('.update_image a[href]'); - const release = scrapeSceneTour(qRelease, site); + const qReleases = ctxa(el, '.update_block'); + profile.releases = qReleases.map((qRelease) => { + const url = qRelease.qu.url('.update_image a[href]'); + const release = scrapeSceneTour(qRelease, site); - if (!/\/(signup|join)/i.test(url)) release.url = url; - release.entryId = deriveEntryId(release); - release.site = site; + if (!/\/(signup|join)/i.test(url)) release.url = url; + release.entryId = deriveEntryId(release); + release.site = site; - return release; - }); + return release; + }); - return profile; + return profile; } async function fetchLatest(site, page = 1, _beforeFetchLatest, accSiteReleases) { - const url = (site.parameters?.latest && util.format(site.parameters.latest, page)) + const url = (site.parameters?.latest && util.format(site.parameters.latest, page)) || (site.parameters?.t1 && `${site.url}/t1/categories/movies_${page}_d.html`) || `${site.url}/categories/movies_${page}_d.html`; - const res = await geta(url, '.modelfeature, .item-video, .updateItem'); + const res = await geta(url, '.modelfeature, .item-video, .updateItem'); - if (!res.ok) return res.status; - if (site.parameters?.t1) return scrapeAllT1(res.items, site, accSiteReleases); - if (site.parameters?.tour) return scrapeAllTour(res.items, site, accSiteReleases); + if (!res.ok) return res.status; + if (site.parameters?.t1) return scrapeAllT1(res.items, site, accSiteReleases); + if (site.parameters?.tour) return scrapeAllTour(res.items, site, accSiteReleases); - return scrapeAll(res.items, site, accSiteReleases); + return scrapeAll(res.items, site, accSiteReleases); } async function fetchScene(url, site, baseRelease, beforeFetchLatest) { - const channelRegExp = beforeFetchLatest || await getChannelRegExp(site); - const res = await get(url); + const channelRegExp = beforeFetchLatest || await getChannelRegExp(site); + const res = await get(url); - if (!res.ok) return res.status; - if (site.parameters?.t1) return scrapeSceneT1(res.item, site, url, baseRelease, channelRegExp); - if (site.parameters?.tour) return scrapeSceneTour(res.item, site, url, baseRelease); + if (!res.ok) return res.status; + if (site.parameters?.t1) return scrapeSceneT1(res.item, site, url, baseRelease, channelRegExp); + if (site.parameters?.tour) return scrapeSceneTour(res.item, site, url, baseRelease); - return scrapeScene(res.item, site, url, baseRelease); + return scrapeScene(res.item, site, url, baseRelease); } async function fetchProfile(actorName, scraperSlug, site) { - const actorSlugA = slugify(actorName, ''); - const actorSlugB = slugify(actorName); + const actorSlugA = slugify(actorName, ''); + const actorSlugB = slugify(actorName); - const t1 = site.parameters?.t1 ? 't1/' : ''; + const t1 = site.parameters?.t1 ? 't1/' : ''; - const res1 = site.parameters?.profile - ? await get(util.format(site.parameters.profile, actorSlugA)) - : await get(`${site.url}/${t1}models/${actorSlugA}.html`); + const res1 = site.parameters?.profile + ? await get(util.format(site.parameters.profile, actorSlugA)) + : await get(`${site.url}/${t1}models/${actorSlugA}.html`); - const res = (res1.ok && res1) - || (site.parameters?.profile - ? await get(util.format(site.parameters.profile, actorSlugB)) - : await get(`${site.url}/${t1}models/${actorSlugB}.html`)); + const res = (res1.ok && res1) + || (site.parameters?.profile && await get(util.format(site.parameters.profile, actorSlugB))) + || await get(`${site.url}/${t1}models/${actorSlugB}.html`); - if (!res.ok) return res.status; - if (site.parameters?.t1) return scrapeProfileT1(res.item, site); - if (site.parameters?.tour) return scrapeProfileTour(res.item, site); + if (!res.ok) return res.status; + if (site.parameters?.t1) return scrapeProfileT1(res.item, site); + if (site.parameters?.tour) return scrapeProfileTour(res.item, site); - return scrapeProfile(res.item, site); + return scrapeProfile(res.item, site); } module.exports = { - beforeFetchLatest: getChannelRegExp, - fetchLatest, - fetchScene, - fetchProfile, + beforeFetchLatest: getChannelRegExp, + fetchLatest, + fetchScene, + fetchProfile, }; diff --git a/src/scrapers/iconmale.js b/src/scrapers/iconmale.js index a41736a9..dea8600e 100644 --- a/src/scrapers/iconmale.js +++ b/src/scrapers/iconmale.js @@ -3,9 +3,9 @@ const { fetchProfile } = require('./mindgeek'); async function networkFetchProfile(actorName) { - return fetchProfile(actorName, 'iconmale'); + return fetchProfile(actorName, 'iconmale'); } module.exports = { - fetchProfile: networkFetchProfile, + fetchProfile: networkFetchProfile, }; diff --git a/src/scrapers/insex.js b/src/scrapers/insex.js index 713e206a..4aec50bc 100644 --- a/src/scrapers/insex.js +++ b/src/scrapers/insex.js @@ -4,104 +4,104 @@ const bhttp = require('bhttp'); const { get, exa, ed } = require('../utils/q'); function scrapeLatest(html, site) { - const scenes = site.slug === 'paintoy' - ? exa(html, '#articleTable table[cellspacing="2"]') - : exa(html, 'body > table'); + const scenes = site.slug === 'paintoy' + ? exa(html, '#articleTable table[cellspacing="2"]') + : exa(html, 'body > table'); - return scenes.map(({ qu }) => { - // if (q('.articleTitleText')) return scrapeFirstLatest(ctx(el), site); - const release = {}; + return scenes.map(({ qu }) => { + // if (q('.articleTitleText')) return scrapeFirstLatest(ctx(el), site); + const release = {}; - const titleEl = qu.q('.galleryTitleText, .articleTitleText'); - const [title, ...actors] = titleEl.textContent.split('|'); - const date = qu.date('.articlePostDateText td', 'MMM D, YYYY'); + const titleEl = qu.q('.galleryTitleText, .articleTitleText'); + const [title, ...actors] = titleEl.textContent.split('|'); + const date = qu.date('.articlePostDateText td', 'MMM D, YYYY'); - const url = qu.url(titleEl, 'a'); - [release.entryId] = url.split('/').slice(-2); - release.url = `${site.url}${url}`; + const url = qu.url(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, not applicable in brief mode - release.title = title.slice(title.indexOf(':') + 1).trim(); - release.date = ed(title.slice(0, title.indexOf(':')), 'MMM D, YYYY'); - } + if (date) { + release.title = title.trim(); + release.date = date; + } else { + // title should contain date instead, not applicable in brief mode + release.title = title.slice(title.indexOf(':') + 1).trim(); + release.date = ed(title.slice(0, title.indexOf(':')), 'MMM D, YYYY'); + } - release.actors = actors.map(actor => actor.trim()); + release.actors = actors.map(actor => actor.trim()); - const description = qu.q('.articleCopyText', true); - if (description) release.description = description.slice(0, description.lastIndexOf('(')); + const description = qu.q('.articleCopyText', true); + if (description) release.description = description.slice(0, description.lastIndexOf('(')); - const duration = qu.dur('.articleCopyText a:nth-child(2)'); - if (duration) release.duration = duration; + const duration = qu.dur('.articleCopyText a:nth-child(2)'); + if (duration) release.duration = duration; - release.likes = parseInt(qu.q('.articlePostDateText td:nth-child(3)', true), 10); + release.likes = parseInt(qu.q('.articlePostDateText td:nth-child(3)', true), 10); - const cover = qu.img('a img'); - release.covers = [[ - cover.replace('_thumbnail', ''), - cover, - ]]; + const cover = qu.img('a img'); + release.covers = [[ + cover.replace('_thumbnail', ''), + cover, + ]]; - return release; - }); + return release; + }); } function scrapeScene({ qu }, site) { - const release = {}; + const release = {}; - const titleEl = qu.q('.articleTitleText'); - const [title, ...actors] = titleEl.textContent.split('|'); + const titleEl = qu.q('.articleTitleText'); + const [title, ...actors] = titleEl.textContent.split('|'); - const url = qu.url(titleEl, 'a'); - [release.entryId] = url.split('/').slice(-2); - release.url = `${site.url}${url}`; + const url = qu.url(titleEl, 'a'); + [release.entryId] = url.split('/').slice(-2); + release.url = `${site.url}${url}`; - release.title = title.trim(); - release.description = qu.q('.articleCopyText', true); + release.title = title.trim(); + release.description = qu.q('.articleCopyText', true); - release.actors = actors.map(actor => actor.trim()); - release.date = qu.date('.articlePostDateText', 'MMMM D, YYYY'); - release.duration = qu.dur('.articlePostDateText a:nth-child(2)'); + release.actors = actors.map(actor => actor.trim()); + release.date = qu.date('.articlePostDateText', 'MMMM D, YYYY'); + release.duration = qu.dur('.articlePostDateText a:nth-child(2)'); - const [cover, ...photos] = qu.imgs('img[src*="images"]'); - release.covers = [cover]; - release.photos = photos; + const [cover, ...photos] = qu.imgs('img[src*="images"]'); + release.covers = [cover]; + release.photos = photos; - release.poster = qu.poster(); + release.poster = qu.poster(); - const trailer = qu.trailer(); - if (trailer) release.trailer = { src: trailer }; + const trailer = qu.trailer(); + if (trailer) release.trailer = { src: trailer }; - return release; + return release; } async function fetchLatest(site, page = 1) { - const url = site.slug === 'paintoy' // paintoy's site is partially broken, use front page - ? `${site.url}/corporal/punishment/gallery.php?type=brief&page=${page}` - : `${site.url}/scripts/switch_tour.php?type=brief&page=${page}`; + const url = site.slug === 'paintoy' // paintoy's site is partially broken, use front page + ? `${site.url}/corporal/punishment/gallery.php?type=brief&page=${page}` + : `${site.url}/scripts/switch_tour.php?type=brief&page=${page}`; - const res = await bhttp.get(url, { - type: 'brief', - page, - }); + const res = await bhttp.get(url, { + type: 'brief', + page, + }); - if (res.statusCode === 200) { - return scrapeLatest(site.slug === 'paintoy' ? res.body.toString() : res.body.html, site); - } + if (res.statusCode === 200) { + return scrapeLatest(site.slug === 'paintoy' ? res.body.toString() : res.body.html, site); + } - return null; + return null; } async function fetchScene(url, site) { - const res = await get(url); + const res = await get(url); - return res.ok ? scrapeScene(res.item, site) : res.status; + return res.ok ? scrapeScene(res.item, site) : res.status; } module.exports = { - fetchLatest, - fetchScene, + fetchLatest, + fetchScene, }; diff --git a/src/scrapers/jayrock.js b/src/scrapers/jayrock.js index d3dba53b..d492927e 100644 --- a/src/scrapers/jayrock.js +++ b/src/scrapers/jayrock.js @@ -9,116 +9,116 @@ const slugify = require('../utils/slugify'); const { fetchApiLatest, fetchScene } = require('./gamma'); async function fetchToken(site) { - const res = await bhttp.get(site.url); - const html = res.body.toString(); + const res = await bhttp.get(site.url); + const html = res.body.toString(); - const time = html.match(/"aet":\d+/)[0].split(':')[1]; - const ah = html.match(/"ah":"[\w-]+"/)[0].split(':')[1].slice(1, -1); - const token = ah.split('').reverse().join(''); + const time = html.match(/"aet":\d+/)[0].split(':')[1]; + const ah = html.match(/"ah":"[\w-]+"/)[0].split(':')[1].slice(1, -1); + const token = ah.split('').reverse().join(''); - return { time, token }; + return { time, token }; } async function fetchActors(entryId, site, { token, time }) { - const url = `${site.url}/sapi/${token}/${time}/model.getModelContent?_method=model.getModelContent&tz=1&fields[0]=modelId.stageName&fields[1]=_last&fields[2]=modelId.upsellLink&fields[3]=modelId.upsellText&limit=25&transitParameters[contentId]=${entryId}`; - const res = await bhttp.get(url); + const url = `${site.url}/sapi/${token}/${time}/model.getModelContent?_method=model.getModelContent&tz=1&fields[0]=modelId.stageName&fields[1]=_last&fields[2]=modelId.upsellLink&fields[3]=modelId.upsellText&limit=25&transitParameters[contentId]=${entryId}`; + const res = await bhttp.get(url); - if (res.statusCode === 200 && res.body.status === true) { - return Object.values(res.body.response.collection).map(actor => Object.values(actor.modelId.collection)[0].stageName); - } + if (res.statusCode === 200 && res.body.status === true) { + return Object.values(res.body.response.collection).map(actor => Object.values(actor.modelId.collection)[0].stageName); + } - return []; + return []; } async function fetchTrailerLocation(entryId, site) { - const url = `${site.url}/api/download/${entryId}/hd1080/stream`; + const url = `${site.url}/api/download/${entryId}/hd1080/stream`; - try { - const res = await bhttp.get(url, { - followRedirects: false, - }); + try { + const res = await bhttp.get(url, { + followRedirects: false, + }); - if (res.statusCode === 302) { - return res.headers.location; - } - } catch (error) { - logger.warn(`${site.name}: Unable to fetch trailer at '${url}': ${error.message}`); - } + if (res.statusCode === 302) { + return res.headers.location; + } + } catch (error) { + logger.warn(`${site.name}: Unable to fetch trailer at '${url}': ${error.message}`); + } - return null; + return null; } async function scrapeScene(scene, site, tokens) { - const release = { - entryId: scene.id, - title: scene.title, - duration: scene.length, - site, - meta: { - tokens, // attach tokens to reduce number of requests required for deep fetching - }, - }; + const release = { + entryId: scene.id, + title: scene.title, + duration: scene.length, + site, + meta: { + tokens, // attach tokens to reduce number of requests required for deep fetching + }, + }; - release.url = `${site.url}/scene/${release.entryId}/${slugify(release.title, { encode: true })}`; - release.date = new Date(scene.sites.collection[scene.id].publishDate); - release.poster = scene._resources.primary[0].url; + release.url = `${site.url}/scene/${release.entryId}/${slugify(release.title, { encode: true })}`; + release.date = new Date(scene.sites.collection[scene.id].publishDate); + release.poster = scene._resources.primary[0].url; - if (scene.tags) release.tags = Object.values(scene.tags.collection).map(tag => tag.alias); - if (scene._resources.base) release.photos = scene._resources.base.map(resource => resource.url); + if (scene.tags) release.tags = Object.values(scene.tags.collection).map(tag => tag.alias); + if (scene._resources.base) release.photos = scene._resources.base.map(resource => resource.url); - const [actors, trailer] = await Promise.all([ - fetchActors(release.entryId, site, tokens), - fetchTrailerLocation(release.entryId, site), - ]); + const [actors, trailer] = await Promise.all([ + fetchActors(release.entryId, site, tokens), + fetchTrailerLocation(release.entryId, site), + ]); - release.actors = actors; - if (trailer) release.trailer = { src: trailer, quality: 1080 }; + release.actors = actors; + if (trailer) release.trailer = { src: trailer, quality: 1080 }; - return release; + return release; } function scrapeLatest(scenes, site, tokens) { - return Promise.map(scenes, async scene => scrapeScene(scene, site, tokens), { concurrency: 10 }); + return Promise.map(scenes, async scene => scrapeScene(scene, site, tokens), { concurrency: 10 }); } async function fetchLatest(site, page = 1) { - if (site.parameters?.useGamma) { - return fetchApiLatest(site, page); - } + if (site.parameters?.useGamma) { + return fetchApiLatest(site, page); + } - const { time, token } = await fetchToken(site); + const { time, token } = await fetchToken(site); - // transParameters[v1] includes _resources, [v2] includes photos, [preset] is mandatory - const url = `${site.url}/sapi/${token}/${time}/content.load?limit=50&offset=${(page - 1) * 50}&transitParameters[v1]=OhUOlmasXD&transitParameters[v2]=OhUOlmasXD&transitParameters[preset]=videos`; - const res = await bhttp.get(url); + // transParameters[v1] includes _resources, [v2] includes photos, [preset] is mandatory + const url = `${site.url}/sapi/${token}/${time}/content.load?limit=50&offset=${(page - 1) * 50}&transitParameters[v1]=OhUOlmasXD&transitParameters[v2]=OhUOlmasXD&transitParameters[preset]=videos`; + const res = await bhttp.get(url); - if (res.statusCode === 200 && res.body.status) { - return scrapeLatest(res.body.response.collection, site, { time, token }); - } + if (res.statusCode === 200 && res.body.status) { + return scrapeLatest(res.body.response.collection, site, { time, token }); + } - return null; + return null; } async function fetchNetworkScene(url, site, release) { - if (site.parameters?.useGamma) { - return fetchScene(url, site, release); - } + if (site.parameters?.useGamma) { + return fetchScene(url, site, release); + } - const { time, token } = release?.meta.tokens || await fetchToken(site); // use attached tokens when deep fetching - const { pathname } = new URL(url); - const entryId = pathname.split('/')[2]; + const { time, token } = release?.meta.tokens || await fetchToken(site); // use attached tokens when deep fetching + const { pathname } = new URL(url); + const entryId = pathname.split('/')[2]; - const apiUrl = `${site.url}/sapi/${token}/${time}/content.load?filter[id][fields][0]=id&filter[id][values][0]=${entryId}&transitParameters[v1]=ykYa8ALmUD&transitParameters[preset]=scene`; - const res = await bhttp.get(apiUrl); + const apiUrl = `${site.url}/sapi/${token}/${time}/content.load?filter[id][fields][0]=id&filter[id][values][0]=${entryId}&transitParameters[v1]=ykYa8ALmUD&transitParameters[preset]=scene`; + const res = await bhttp.get(apiUrl); - if (res.statusCode === 200 && res.body.status) { - return scrapeScene(res.body.response.collection[0], site, { time, token }); - } + if (res.statusCode === 200 && res.body.status) { + return scrapeScene(res.body.response.collection[0], site, { time, token }); + } - return null; + return null; } module.exports = { - fetchLatest, - fetchScene: fetchNetworkScene, + fetchLatest, + fetchScene: fetchNetworkScene, }; diff --git a/src/scrapers/jesseloadsmonsterfacials.js b/src/scrapers/jesseloadsmonsterfacials.js index 622f9df1..19a49155 100644 --- a/src/scrapers/jesseloadsmonsterfacials.js +++ b/src/scrapers/jesseloadsmonsterfacials.js @@ -3,83 +3,83 @@ const { get, initAll } = require('../utils/qu'); function scrapeLatest(scenes, dates, site) { - return scenes.map(({ qu }, index) => { - const release = {}; + return scenes.map(({ qu }, index) => { + const release = {}; - const path = qu.url('a'); - release.url = `${site.url}/visitors/${path}`; - release.entryId = path.match(/videos\/([a-zA-Z0-9]+)(?:_hd)?_trailer/)?.[1]; + const path = qu.url('a'); + release.url = `${site.url}/visitors/${path}`; + release.entryId = path.match(/videos\/([a-zA-Z0-9]+)(?:_hd)?_trailer/)?.[1]; - if (dates && dates[index]) { - release.date = dates[index].qu.date(null, 'MM/DD/YYYY'); - } + if (dates && dates[index]) { + release.date = dates[index].qu.date(null, 'MM/DD/YYYY'); + } - release.description = qu.q('tbody tr:nth-child(3) font', true); + release.description = qu.q('tbody tr:nth-child(3) font', true); - const infoLine = qu.q('font[color="#663366"]', true); - if (infoLine) release.duration = Number(infoLine.match(/(\d+) min/)[1]) * 60; + const infoLine = qu.q('font[color="#663366"]', true); + if (infoLine) release.duration = Number(infoLine.match(/(\d+) min/)[1]) * 60; - const poster = qu.img('img[src*="photos/"][width="400"]'); - release.poster = `${site.url}/visitors/${poster}`; - release.photos = qu.imgs('img[src*="photos/"]:not([width="400"])').map(source => `${site.url}/visitors/${source}`); + const poster = qu.img('img[src*="photos/"][width="400"]'); + release.poster = `${site.url}/visitors/${poster}`; + release.photos = qu.imgs('img[src*="photos/"]:not([width="400"])').map(source => `${site.url}/visitors/${source}`); - return release; - }); + return release; + }); } function scrapeScene({ qu }, url, site) { - const release = { url }; + const release = { url }; - const { pathname } = new URL(url); - release.entryId = pathname.match(/videos\/(\w+)_hd_trailer/)[1]; + const { pathname } = new URL(url); + release.entryId = pathname.match(/videos\/(\w+)_hd_trailer/)[1]; - const actor = qu.q('font[color="#990033"] strong', true); - release.actors = [actor]; + const actor = qu.q('font[color="#990033"] strong', true); + release.actors = [actor]; - const hdTrailer = qu.url('a[href*="hd_trailer.mp4"]'); - const sdTrailer = qu.url('a[href*="hd_trailer_mobile.mp4"]'); + const hdTrailer = qu.url('a[href*="hd_trailer.mp4"]'); + const sdTrailer = qu.url('a[href*="hd_trailer_mobile.mp4"]'); - release.trailer = [ - { - src: `${site.url}/visitors/videos/${hdTrailer}`, - quality: 1080, - }, - { - src: `${site.url}/visitors/videos/${sdTrailer}`, - quality: 270, - }, - ]; + release.trailer = [ + { + src: `${site.url}/visitors/videos/${hdTrailer}`, + quality: 1080, + }, + { + src: `${site.url}/visitors/videos/${sdTrailer}`, + quality: 270, + }, + ]; - return release; + return release; } async function fetchLatest(site, page = 1) { - const url = `https://jesseloadsmonsterfacials.com/visitors/tour_${page.toString().padStart(2, '0')}.html`; - const res = await get(url); + const url = `https://jesseloadsmonsterfacials.com/visitors/tour_${page.toString().padStart(2, '0')}.html`; + const res = await get(url); - if (!res.ok) { - return res.status; - } + if (!res.ok) { + return res.status; + } - const { el } = res.item; + const { el } = res.item; - const scenes = initAll(el, 'table[width="880"]'); - const dates = initAll(el, 'font[color="#000000"] strong:not(:empty)'); + const scenes = initAll(el, 'table[width="880"]'); + const dates = initAll(el, 'font[color="#000000"] strong:not(:empty)'); - return scrapeLatest(scenes, dates, site); + return scrapeLatest(scenes, dates, site); } async function fetchScene(url, site) { - const res = await get(url); + const res = await get(url); - if (res.ok) { - return scrapeScene(res.item, url, site); - } + if (res.ok) { + return scrapeScene(res.item, url, site); + } - return res.status; + return res.status; } module.exports = { - fetchLatest, - fetchScene, + fetchLatest, + fetchScene, }; diff --git a/src/scrapers/julesjordan.js b/src/scrapers/julesjordan.js index da44f051..3b83daa2 100644 --- a/src/scrapers/julesjordan.js +++ b/src/scrapers/julesjordan.js @@ -13,406 +13,406 @@ const { heightToCm } = require('../utils/convert'); const slugify = require('../utils/slugify'); async function fetchPhotos(url) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - return res.body.toString(); + return res.body.toString(); } function scrapePhotos(html, type) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); + const $ = cheerio.load(html, { normalizeWhitespace: true }); - const photos = $('.photo_gallery_thumbnail_wrapper .thumbs') - .toArray() - .map((photoElement) => { - const src = $(photoElement).attr('src'); + const photos = $('.photo_gallery_thumbnail_wrapper .thumbs') + .toArray() + .map((photoElement) => { + const src = $(photoElement).attr('src'); - // high res often available in alternative directories, but not always, provide original as fallback - if (type === 'caps') { - return [ - src.replace('capthumbs/', 'caps/'), - src, - ]; - } + // high res often available in alternative directories, but not always, provide original as fallback + if (type === 'caps') { + return [ + src.replace('capthumbs/', 'caps/'), + src, + ]; + } - return [ - src.replace('thumbs/', 'photos/'), - src.replace('thumbs/', '1600watermarked/'), - src.replace('thumbs/', '1280watermarked/'), - src.replace('thumbs/', '1024watermarked/'), - src, - ]; - }); + return [ + src.replace('thumbs/', 'photos/'), + src.replace('thumbs/', '1600watermarked/'), + src.replace('thumbs/', '1280watermarked/'), + src.replace('thumbs/', '1024watermarked/'), + src, + ]; + }); - return photos; + return photos; } async function getPhotosLegacy(entryId, site, type = 'highres', page = 1) { - const albumUrl = `${site.url}/trial/gallery.php?id=${entryId}&type=${type}&page=${page}`; + const albumUrl = `${site.url}/trial/gallery.php?id=${entryId}&type=${type}&page=${page}`; - logger.warn(`Jules Jordan is using legacy photo scraper for ${albumUrl} (page ${page})`); + logger.warn(`Jules Jordan is using legacy photo scraper for ${albumUrl} (page ${page})`); - const html = await fetchPhotos(albumUrl); - const $ = cheerio.load(html, { normalizeWhitespace: true }); + const html = await fetchPhotos(albumUrl); + const $ = cheerio.load(html, { normalizeWhitespace: true }); - // don't add first URL to pages to prevent unnecessary duplicate request - const photos = scrapePhotos(html, type); - const pages = Array.from(new Set($('.page_numbers a').toArray().map(el => $(el).attr('href')))); + // don't add first URL to pages to prevent unnecessary duplicate request + const photos = scrapePhotos(html, type); + const pages = Array.from(new Set($('.page_numbers a').toArray().map(el => $(el).attr('href')))); - const otherPhotos = pages - ? await Promise.map(pages, async (pageX) => { - const pageUrl = `https://www.julesjordan.com/trial/${pageX}`; - const pageHtml = await fetchPhotos(pageUrl); + const otherPhotos = pages + ? await Promise.map(pages, async (pageX) => { + const pageUrl = `https://www.julesjordan.com/trial/${pageX}`; + const pageHtml = await fetchPhotos(pageUrl); - return scrapePhotos(pageHtml, type); - }, { - concurrency: 2, - }) - : []; + return scrapePhotos(pageHtml, type); + }, { + concurrency: 2, + }) + : []; - const allPhotos = photos.concat(otherPhotos.flat()); + const allPhotos = photos.concat(otherPhotos.flat()); - if (allPhotos.length === 0 && type === 'highres') { - // photos not available, try for screencaps instead - return getPhotosLegacy(entryId, site, 'caps', 1); - } + if (allPhotos.length === 0 && type === 'highres') { + // photos not available, try for screencaps instead + return getPhotosLegacy(entryId, site, 'caps', 1); + } - return allPhotos; + return allPhotos; } async function getPhotos(entryId, site, type = 'highres', page = 1) { - const albumUrl = `${site.parameters?.photos || `${site.url}/gallery.php`}?id=${entryId}&type=${type}&page=${page}`; + const albumUrl = `${site.parameters?.photos || `${site.url}/gallery.php`}?id=${entryId}&type=${type}&page=${page}`; - const res = await bhttp.get(albumUrl); - const html = res.body.toString(); + const res = await bhttp.get(albumUrl); + const html = res.body.toString(); - const sourceLines = html.split(/\n/).filter(line => line.match(/ptx\["\w+"\]/)); - const sources = sourceLines.reduce((acc, sourceLine) => { - const quality = sourceLine.match(/\["\w+"\]/)[0].slice(2, -2); - const sourceStart = sourceLine.match(/\/trial|\/tour|\/content/); + const sourceLines = html.split(/\n/).filter(line => line.match(/ptx\["\w+"\]/)); + const sources = sourceLines.reduce((acc, sourceLine) => { + const quality = sourceLine.match(/\["\w+"\]/)[0].slice(2, -2); + const sourceStart = sourceLine.match(/\/trial|\/tour|\/content/); - if (!sourceStart) return acc; - const source = sourceLine.slice(sourceStart.index, sourceLine.indexOf('.jpg') + 4); + if (!sourceStart) return acc; + const source = sourceLine.slice(sourceStart.index, sourceLine.indexOf('.jpg') + 4); - if (!source) return acc; - if (!acc[quality]) acc[quality] = []; + if (!source) return acc; + if (!acc[quality]) acc[quality] = []; - acc[quality].push(`${site.url}${source}`); + acc[quality].push(`${site.url}${source}`); - return acc; - }, {}); + return acc; + }, {}); - if (type === 'highres') { - if (sources['1600'] && sources['1600'].length > 0) return sources['1600']; - if (sources['1280'] && sources['1280'].length > 0) return sources['1280']; - if (sources['1024'] && sources['1024'].length > 0) return sources['1024']; - if (sources.Thumbs && sources.Thumbs.length > 0) return sources.Thumbs; + if (type === 'highres') { + if (sources['1600'] && sources['1600'].length > 0) return sources['1600']; + if (sources['1280'] && sources['1280'].length > 0) return sources['1280']; + if (sources['1024'] && sources['1024'].length > 0) return sources['1024']; + if (sources.Thumbs && sources.Thumbs.length > 0) return sources.Thumbs; - // no photos available, try for screencaps instead - return getPhotos(entryId, site, 'caps', 1); - } + // no photos available, try for screencaps instead + return getPhotos(entryId, site, 'caps', 1); + } - if (sources.jpg && sources.jpg.length > 0) return sources.jpg; - if (sources['Video Cap Thumbs'] && sources['Video Cap Thumbs'].length > 0) return sources['Video Cap Thumbs']; + if (sources.jpg && sources.jpg.length > 0) return sources.jpg; + if (sources['Video Cap Thumbs'] && sources['Video Cap Thumbs'].length > 0) return sources['Video Cap Thumbs']; - // no screencaps available either, try legacy scraper just in case - return getPhotosLegacy(entryId, site, 'highres', 1); + // no screencaps available either, try legacy scraper just in case + return getPhotosLegacy(entryId, site, 'highres', 1); } function getEntryId(html) { - const entryId = html.match(/showtagform\((\d+)\)/); + const entryId = html.match(/showtagform\((\d+)\)/); - if (entryId) { - return entryId[1]; - } + if (entryId) { + return entryId[1]; + } - const setIdIndex = html.indexOf('setid:"'); + const setIdIndex = html.indexOf('setid:"'); - if (setIdIndex) { - return html.slice(setIdIndex, html.indexOf(',', setIdIndex)).match(/\d+/)[0]; - } + if (setIdIndex) { + return html.slice(setIdIndex, html.indexOf(',', setIdIndex)).match(/\d+/)[0]; + } - return null; + return null; } function scrapeAll(scenes, site) { - return scenes.map(({ el, qu }) => { - const release = {}; + return scenes.map(({ el, qu }) => { + const release = {}; - release.entryId = el.dataset.setid || qu.q('.rating_box')?.dataset.id; + release.entryId = el.dataset.setid || qu.q('.rating_box')?.dataset.id; - release.url = qu.url('.update_title, .dvd_info > a, a ~ a'); - release.title = qu.q('.update_title, .dvd_info > a, a ~ a', true); - release.date = qu.date('.update_date', 'MM/DD/YYYY'); + release.url = qu.url('.update_title, .dvd_info > a, a ~ a'); + release.title = qu.q('.update_title, .dvd_info > a, a ~ a', true); + release.date = qu.date('.update_date', 'MM/DD/YYYY'); - release.actors = qu.all('.update_models a', true); + release.actors = qu.all('.update_models a', true); - const dvdPhotos = qu.imgs('.dvd_preview_thumb'); - const photoCount = Number(qu.q('a img.thumbs', 'cnt')) || 1; + const dvdPhotos = qu.imgs('.dvd_preview_thumb'); + const photoCount = Number(qu.q('a img.thumbs', 'cnt')) || 1; - [release.poster, ...release.photos] = dvdPhotos.length - ? dvdPhotos - : Array.from({ length: photoCount }).map((value, index) => { - const src = qu.img('a img.thumbs', `src${index}_1x`) || qu.img('a img.thumbs', `src${index}`) || qu.img('a img.thumbs'); + [release.poster, ...release.photos] = dvdPhotos.length + ? dvdPhotos + : Array.from({ length: photoCount }).map((value, index) => { + const src = qu.img('a img.thumbs', `src${index}_1x`) || qu.img('a img.thumbs', `src${index}`) || qu.img('a img.thumbs'); - return src ? { - src: /^http/.test(src) ? src : `${site.url}${src}`, - referer: site.url, - } : null; - }).filter(Boolean); + return src ? { + src: /^http/.test(src) ? src : `${site.url}${src}`, + referer: site.url, + } : null; + }).filter(Boolean); - const teaserScript = qu.html('script'); - if (teaserScript) { - const src = teaserScript.slice(teaserScript.indexOf('http'), teaserScript.indexOf('.mp4') + 4); - if (src) release.teaser = { src }; - } + const teaserScript = qu.html('script'); + if (teaserScript) { + const src = teaserScript.slice(teaserScript.indexOf('http'), teaserScript.indexOf('.mp4') + 4); + if (src) release.teaser = { src }; + } - return release; - }); + return release; + }); } function scrapeUpcoming(html, site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const scenesElements = $('#coming_soon_carousel').find('.table').toArray(); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const scenesElements = $('#coming_soon_carousel').find('.table').toArray(); - return scenesElements.map((element) => { - const entryId = $(element).find('.upcoming_updates_thumb').attr('id').match(/\d+/)[0]; + return scenesElements.map((element) => { + const entryId = $(element).find('.upcoming_updates_thumb').attr('id').match(/\d+/)[0]; - const details = $(element).find('.update_details_comingsoon') - .eq(1) - .children() - .remove(); + const details = $(element).find('.update_details_comingsoon') + .eq(1) + .children() + .remove(); - const title = details - .end() - .text() - .trim(); + const title = details + .end() + .text() + .trim(); - const actors = details - .text() - .trim() - .split(', '); + const actors = details + .text() + .trim() + .split(', '); - const date = moment - .utc($(element).find('.update_date_comingsoon').text().slice(7), 'MM/DD/YYYY') - .toDate(); + const date = moment + .utc($(element).find('.update_date_comingsoon').text().slice(7), 'MM/DD/YYYY') + .toDate(); - const photoElement = $(element).find('a img.thumbs'); - const posterPath = photoElement.attr('src'); - const poster = posterPath.match(/^http/) ? posterPath : `${site.url}${posterPath}`; + const photoElement = $(element).find('a img.thumbs'); + const posterPath = photoElement.attr('src'); + const poster = posterPath.match(/^http/) ? posterPath : `${site.url}${posterPath}`; - const videoClass = $(element).find('.update_thumbnail div').attr('class'); - const videoScript = $(element).find(`script:contains(${videoClass})`).html(); - const teaser = videoScript.slice(videoScript.indexOf('https://'), videoScript.indexOf('.mp4') + 4); + const videoClass = $(element).find('.update_thumbnail div').attr('class'); + const videoScript = $(element).find(`script:contains(${videoClass})`).html(); + const teaser = videoScript.slice(videoScript.indexOf('https://'), videoScript.indexOf('.mp4') + 4); - return { - url: null, - entryId, - title, - date, - actors, - poster, - teaser: { - src: teaser, - }, - rating: null, - site, - }; - }); + return { + url: null, + entryId, + title, + date, + actors, + poster, + teaser: { + src: teaser, + }, + rating: null, + site, + }; + }); } async function scrapeScene({ html, qu }, url, site, include) { - const release = { url, site }; + const release = { url, site }; - release.entryId = getEntryId(html); - release.title = qu.q('.title_bar_hilite', true); - release.description = qu.q('.update_description', true); + release.entryId = getEntryId(html); + release.title = qu.q('.title_bar_hilite', true); + release.description = qu.q('.update_description', true); - release.date = qu.date('.update_date', 'MM/DD/YYYY', null, 'innerHTML'); + release.date = qu.date('.update_date', 'MM/DD/YYYY', null, 'innerHTML'); - release.actors = qu.all('.backgroundcolor_info > .update_models a, .item .update_models a', true); - release.tags = qu.all('.update_tags a', true); + release.actors = qu.all('.backgroundcolor_info > .update_models a, .item .update_models a', true); + release.tags = qu.all('.update_tags a', true); - const posterPath = html.match(/useimage = "(.*)"/)?.[1]; + const posterPath = html.match(/useimage = "(.*)"/)?.[1]; - if (posterPath) { - const poster = /^http/.test(posterPath) ? posterPath : `${site.url}${posterPath}`; + if (posterPath) { + const poster = /^http/.test(posterPath) ? posterPath : `${site.url}${posterPath}`; - if (poster) { - release.poster = { - src: poster, - referer: site.url, - }; - } - } + if (poster) { + release.poster = { + src: poster, + referer: site.url, + }; + } + } - if (include.trailer && site.slug !== 'manuelferrara') { - const trailerLines = html.split('\n').filter(line => /movie\["trailer\w*"\]\[/i.test(line)); + if (include.trailer && site.slug !== 'manuelferrara') { + const trailerLines = html.split('\n').filter(line => /movie\["trailer\w*"\]\[/i.test(line)); - if (trailerLines.length) { - release.trailer = trailerLines.map((trailerLine) => { - const src = trailerLine.match(/path:"([\w:/.&=?%]+)"/)?.[1]; - const quality = trailerLine.match(/movie_height:'(\d+)/)?.[1]; + if (trailerLines.length) { + release.trailer = trailerLines.map((trailerLine) => { + const src = trailerLine.match(/path:"([\w:/.&=?%]+)"/)?.[1]; + const quality = trailerLine.match(/movie_height:'(\d+)/)?.[1]; - return src && { - src: /^http/.test(src) ? src : `${site.url}${src}`, - quality: quality && Number(quality.replace('558', '540')), - }; - }).filter(Boolean); - } - } + return src && { + src: /^http/.test(src) ? src : `${site.url}${src}`, + quality: quality && Number(quality.replace('558', '540')), + }; + }).filter(Boolean); + } + } - if (include.photos) release.photos = await getPhotos(release.entryId, site); + if (include.photos) release.photos = await getPhotos(release.entryId, site); - if (qu.exists('.update_dvds a')) { - release.movie = { - url: qu.url('.update_dvds a'), - title: qu.q('.update_dvds a', true), - }; - } + if (qu.exists('.update_dvds a')) { + release.movie = { + url: qu.url('.update_dvds a'), + title: qu.q('.update_dvds a', true), + }; + } - const stars = Number(qu.q('.avg_rating', true)?.replace(/[\s|Avg Rating:]/g, '')); - if (stars) release.stars = stars; + const stars = Number(qu.q('.avg_rating', true)?.replace(/[\s|Avg Rating:]/g, '')); + if (stars) release.stars = stars; - return release; + return release; } function scrapeMovie({ el, qu }, url, site) { - const movie = { url, site }; + const movie = { url, site }; - movie.entryId = qu.q('.dvd_details_overview .rating_box').dataset.id; - movie.title = qu.q('.title_bar span', true); - movie.covers = qu.urls('#dvd-cover-flip > a'); - movie.channel = slugify(qu.q('.update_date a', true), ''); + movie.entryId = qu.q('.dvd_details_overview .rating_box').dataset.id; + movie.title = qu.q('.title_bar span', true); + movie.covers = qu.urls('#dvd-cover-flip > a'); + movie.channel = slugify(qu.q('.update_date a', true), ''); - // movie.releases = Array.from(document.querySelectorAll('.cell.dvd_info > a'), el => el.href); - const sceneQus = ctxa(el, '.dvd_details'); - const scenes = scrapeAll(sceneQus, site); + // movie.releases = Array.from(document.querySelectorAll('.cell.dvd_info > a'), el => el.href); + const sceneQus = ctxa(el, '.dvd_details'); + const scenes = scrapeAll(sceneQus, site); - const curatedScenes = scenes + const curatedScenes = scenes ?.map(scene => ({ ...scene, movie })) .sort((sceneA, sceneB) => sceneA.date - sceneB.date); - movie.date = curatedScenes?.[0].date; + movie.date = curatedScenes?.[0].date; - return { - ...movie, - ...(curatedScenes && { scenes: curatedScenes }), - }; + return { + ...movie, + ...(curatedScenes && { scenes: curatedScenes }), + }; } function scrapeProfile(html, url, actorName) { - const { document } = new JSDOM(html).window; + const { document } = new JSDOM(html).window; - const bio = document.querySelector('.model_bio').textContent; - const avatarEl = document.querySelector('.model_bio_pic img'); + const bio = document.querySelector('.model_bio').textContent; + const avatarEl = document.querySelector('.model_bio_pic img'); - const profile = { - name: actorName, - }; + const profile = { + name: actorName, + }; - const heightString = bio.match(/\d+ feet \d+ inches/); - const ageString = bio.match(/Age:\s*(\d{2})/); - const birthDateString = bio.match(/Age:\s*(\w+ \d{1,2}, \d{4})/); - const measurementsString = bio.match(/\w+-\d+-\d+/); + const heightString = bio.match(/\d+ feet \d+ inches/); + const ageString = bio.match(/Age:\s*(\d{2})/); + const birthDateString = bio.match(/Age:\s*(\w+ \d{1,2}, \d{4})/); + const measurementsString = bio.match(/\w+-\d+-\d+/); - if (birthDateString) profile.birthdate = parseDate(birthDateString[1], 'MMMM D, YYYY'); - if (ageString) profile.age = Number(ageString[1]); + if (birthDateString) profile.birthdate = parseDate(birthDateString[1], 'MMMM D, YYYY'); + if (ageString) profile.age = Number(ageString[1]); - if (heightString) profile.height = heightToCm(heightString[0]); + if (heightString) profile.height = heightToCm(heightString[0]); - if (measurementsString) { - const [bust, waist, hip] = measurementsString[0].split('-'); + if (measurementsString) { + const [bust, waist, hip] = measurementsString[0].split('-'); - if (bust) profile.bust = bust; - if (waist) profile.waist = Number(waist); - if (hip) profile.hip = Number(hip); - } + if (bust) profile.bust = bust; + if (waist) profile.waist = Number(waist); + if (hip) profile.hip = Number(hip); + } - if (avatarEl) { - const avatarSources = [ - avatarEl.getAttribute('src0_3x'), - avatarEl.getAttribute('src0_2x'), - avatarEl.getAttribute('src0_1x'), - avatarEl.getAttribute('src0'), - avatarEl.getAttribute('src'), - ].filter(Boolean); + if (avatarEl) { + const avatarSources = [ + avatarEl.getAttribute('src0_3x'), + avatarEl.getAttribute('src0_2x'), + avatarEl.getAttribute('src0_1x'), + avatarEl.getAttribute('src0'), + avatarEl.getAttribute('src'), + ].filter(Boolean); - if (avatarSources.length) profile.avatar = avatarSources; - } + if (avatarSources.length) profile.avatar = avatarSources; + } - profile.releases = Array.from(document.querySelectorAll('.category_listing_block .update_details > a:first-child'), el => el.href); + profile.releases = Array.from(document.querySelectorAll('.category_listing_block .update_details > a:first-child'), el => el.href); - console.log(profile); + console.log(profile); - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const url = site.parameters?.latest - ? util.format(site.parameters.latest, page) - : `${site.url}/trial/categories/movies_${page}_d.html`; + const url = site.parameters?.latest + ? util.format(site.parameters.latest, page) + : `${site.url}/trial/categories/movies_${page}_d.html`; - // const res = await bhttp.get(url); - const res = await geta(url, '.update_details'); + // const res = await bhttp.get(url); + const res = await geta(url, '.update_details'); - return res.ok ? scrapeAll(res.items, site) : res.status; + return res.ok ? scrapeAll(res.items, site) : res.status; } async function fetchUpcoming(site) { - if (site.parameters?.upcoming === false) return null; + if (site.parameters?.upcoming === false) return null; - const url = site.parameters?.upcoming ? util.format(site.parameters.upcoming) : `${site.url}/trial/index.php`; - const res = await bhttp.get(url); + const url = site.parameters?.upcoming ? util.format(site.parameters.upcoming) : `${site.url}/trial/index.php`; + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeUpcoming(res.body.toString(), site); - } + if (res.statusCode === 200) { + return scrapeUpcoming(res.body.toString(), site); + } - return res.statusCode; + return res.statusCode; } async function fetchScene(url, site, baseRelease, preflight, include) { - const res = await get(url); + const res = await get(url); - return res.ok ? scrapeScene(res.item, url, site, include) : res.status; + return res.ok ? scrapeScene(res.item, url, site, include) : res.status; } async function fetchMovie(url, site) { - const res = await get(url); + const res = await get(url); - return res.ok ? scrapeMovie(res.item, url, site) : res.status; + return res.ok ? scrapeMovie(res.item, url, site) : res.status; } async function fetchProfile(actorName) { - const actorSlugA = slugify(actorName, '-'); - const actorSlugB = slugify(actorName, ''); + const actorSlugA = slugify(actorName, '-'); + const actorSlugB = slugify(actorName, ''); - const urlA = `https://julesjordan.com/trial/models/${actorSlugA}.html`; - const urlB = `https://julesjordan.com/trial/models/${actorSlugB}.html`; + const urlA = `https://julesjordan.com/trial/models/${actorSlugA}.html`; + const urlB = `https://julesjordan.com/trial/models/${actorSlugB}.html`; - const resA = await bhttp.get(urlA); + const resA = await bhttp.get(urlA); - if (resA.statusCode === 200) { - const profile = scrapeProfile(resA.body.toString(), urlA, actorName); + if (resA.statusCode === 200) { + const profile = scrapeProfile(resA.body.toString(), urlA, actorName); - return profile; - } + return profile; + } - const resB = await bhttp.get(urlB); + const resB = await bhttp.get(urlB); - if (resB.statusCode === 200) { - const profile = scrapeProfile(resB.body.toString(), urlB, actorName); + if (resB.statusCode === 200) { + const profile = scrapeProfile(resB.body.toString(), urlB, actorName); - return profile; - } + return profile; + } - return null; + return null; } module.exports = { - fetchLatest, - fetchMovie, - fetchProfile, - fetchUpcoming, - fetchScene, + fetchLatest, + fetchMovie, + fetchProfile, + fetchUpcoming, + fetchScene, }; diff --git a/src/scrapers/kellymadison.js b/src/scrapers/kellymadison.js index 50c99aca..9b29be65 100644 --- a/src/scrapers/kellymadison.js +++ b/src/scrapers/kellymadison.js @@ -7,184 +7,184 @@ const moment = require('moment'); const { feetInchesToCm } = require('../utils/convert'); const siteMapByKey = { - PF: 'pornfidelity', - TF: 'teenfidelity', - KM: 'kellymadison', + PF: 'pornfidelity', + TF: 'teenfidelity', + KM: 'kellymadison', }; const siteMapBySlug = Object.entries(siteMapByKey).reduce((acc, [key, value]) => ({ ...acc, [value]: key }), {}); function extractTextNode(parentEl) { - return Array.from(parentEl).reduce((acc, el) => (el.nodeType === 3 ? `${acc}${el.textContent.trim()}` : acc), ''); + return Array.from(parentEl).reduce((acc, el) => (el.nodeType === 3 ? `${acc}${el.textContent.trim()}` : acc), ''); } function scrapeLatest(html, site) { - const { document } = new JSDOM(html).window; + const { document } = new JSDOM(html).window; - return Array.from(document.querySelectorAll('.episode'), (scene) => { - const release = { site }; + return Array.from(document.querySelectorAll('.episode'), (scene) => { + const release = { site }; - release.shootId = scene.querySelector('.card-meta .text-right').textContent.trim(); + release.shootId = scene.querySelector('.card-meta .text-right').textContent.trim(); - const siteId = release.shootId.match(/\w{2}/)[0]; - const siteSlug = siteMapByKey[siteId]; + const siteId = release.shootId.match(/\w{2}/)[0]; + const siteSlug = siteMapByKey[siteId]; - if (site.slug !== siteSlug) { - // using generic network overview, scene is not from the site we want - return null; - } + if (site.slug !== siteSlug) { + // using generic network overview, scene is not from the site we want + return null; + } - const durationEl = scene.querySelector('.content a'); + const durationEl = scene.querySelector('.content a'); - [release.entryId] = durationEl.href.match(/\d+$/); - release.url = `${site.url}/episodes/${release.entryId}`; + [release.entryId] = durationEl.href.match(/\d+$/); + release.url = `${site.url}/episodes/${release.entryId}`; - release.title = scene.querySelector('h5 a').textContent.trim(); + release.title = scene.querySelector('h5 a').textContent.trim(); - const dateEl = scene.querySelector('.card-meta .text-left').childNodes; - const dateString = extractTextNode(dateEl); + const dateEl = scene.querySelector('.card-meta .text-left').childNodes; + const dateString = extractTextNode(dateEl); - release.date = moment.utc(dateString, ['MMM D', 'MMM D, YYYY']).toDate(); - release.actors = Array.from(scene.querySelectorAll('.models a'), el => el.textContent); + release.date = moment.utc(dateString, ['MMM D', 'MMM D, YYYY']).toDate(); + release.actors = Array.from(scene.querySelectorAll('.models a'), el => el.textContent); - const durationString = durationEl.textContent.match(/\d+ min/); - if (durationString) release.duration = Number(durationString[0].match(/\d+/)[0]) * 60; + const durationString = durationEl.textContent.match(/\d+ min/); + if (durationString) release.duration = Number(durationString[0].match(/\d+/)[0]) * 60; - release.poster = scene.querySelector('.card-img-top').dataset.src; - release.teaser = { - src: scene.querySelector('video').src, - }; + release.poster = scene.querySelector('.card-img-top').dataset.src; + release.teaser = { + src: scene.querySelector('video').src, + }; - return release; - }).filter(scene => scene); + return release; + }).filter(scene => scene); } function scrapeScene(html, url, site, baseRelease) { - const { document } = new JSDOM(html).window; - const release = { url, site }; + const { document } = new JSDOM(html).window; + const release = { url, site }; - const titleEl = document.querySelector('.card-header.row h4').childNodes; - const titleString = extractTextNode(titleEl); + const titleEl = document.querySelector('.card-header.row h4').childNodes; + const titleString = extractTextNode(titleEl); - if (!baseRelease) [release.entryId] = url.match(/\d+/); + if (!baseRelease) [release.entryId] = url.match(/\d+/); - release.title = titleString - .replace('Trailer: ', '') - .replace(/- \w+ #\d+$/, '') - .trim(); + release.title = titleString + .replace('Trailer: ', '') + .replace(/- \w+ #\d+$/, '') + .trim(); - release.channel = titleString.match(/\w+ #\d+$/)[0].match(/\w+/)[0].toLowerCase(); + release.channel = titleString.match(/\w+ #\d+$/)[0].match(/\w+/)[0].toLowerCase(); - const episode = titleString.match(/#\d+$/)[0]; - const siteKey = siteMapBySlug[release.channel]; + const episode = titleString.match(/#\d+$/)[0]; + const siteKey = siteMapBySlug[release.channel]; - release.shootId = `${siteKey} ${episode}`; - release.description = document.querySelector('p.card-text').textContent.trim(); + release.shootId = `${siteKey} ${episode}`; + release.description = document.querySelector('p.card-text').textContent.trim(); - const dateEl = document.querySelector('.card-body h4.card-title:nth-child(3)').childNodes; - const dateString = extractTextNode(dateEl); + const dateEl = document.querySelector('.card-body h4.card-title:nth-child(3)').childNodes; + const dateString = extractTextNode(dateEl); - release.date = moment.utc(dateString, 'YYYY-MM-DD').toDate(); - release.actors = Array.from(document.querySelectorAll('.card-body h4.card-title:nth-child(4) a'), el => el.textContent); + release.date = moment.utc(dateString, 'YYYY-MM-DD').toDate(); + release.actors = Array.from(document.querySelectorAll('.card-body h4.card-title:nth-child(4) a'), el => el.textContent); - const durationRaw = document.querySelector('.card-body h4.card-title:nth-child(1)').textContent; - const durationString = durationRaw.match(/\d+:\d+/)[0]; + const durationRaw = document.querySelector('.card-body h4.card-title:nth-child(1)').textContent; + const durationString = durationRaw.match(/\d+:\d+/)[0]; - release.duration = moment.duration(`00:${durationString}`).asSeconds(); + release.duration = moment.duration(`00:${durationString}`).asSeconds(); - const trailerStart = document.body.innerHTML.indexOf('player.updateSrc'); - const trailerString = document.body.innerHTML.slice(trailerStart, document.body.innerHTML.indexOf(');', trailerStart)); + const trailerStart = document.body.innerHTML.indexOf('player.updateSrc'); + const trailerString = document.body.innerHTML.slice(trailerStart, document.body.innerHTML.indexOf(');', trailerStart)); - const trailers = trailerString.match(/https:\/\/.*.mp4/g); - const resolutions = trailerString.match(/res: '\d+'/g).map((res) => { - const resolution = Number(res.match(/\d+/)[0]); + const trailers = trailerString.match(/https:\/\/.*.mp4/g); + const resolutions = trailerString.match(/res: '\d+'/g).map((res) => { + const resolution = Number(res.match(/\d+/)[0]); - return resolution === 4000 ? 2160 : resolution; // 4k is not 4000 pixels high - }); + return resolution === 4000 ? 2160 : resolution; // 4k is not 4000 pixels high + }); - release.trailer = trailers.map((trailer, index) => ({ - src: trailer, - quality: resolutions[index], - })); + release.trailer = trailers.map((trailer, index) => ({ + src: trailer, + quality: resolutions[index], + })); - const posterPrefix = html.indexOf('poster:'); - const poster = html.slice(html.indexOf('http', posterPrefix), html.indexOf('.jpg', posterPrefix) + 4); + const posterPrefix = html.indexOf('poster:'); + const poster = html.slice(html.indexOf('http', posterPrefix), html.indexOf('.jpg', posterPrefix) + 4); - if (baseRelease?.poster) release.photos = [poster]; - else release.poster = poster; + if (baseRelease?.poster) release.photos = [poster]; + else release.poster = poster; - return release; + return release; } function scrapeProfile(html, actorName) { - const { document } = new JSDOM(html).window; - const profile = { name: actorName }; + const { document } = new JSDOM(html).window; + const profile = { name: actorName }; - const bioKeys = Array.from(document.querySelectorAll('table.table td:nth-child(1)'), el => el.textContent.slice(0, -1)); - const bioValues = Array.from(document.querySelectorAll('table.table td:nth-child(2)'), el => el.textContent); - const bio = bioKeys.reduce((acc, key, index) => ({ ...acc, [key]: bioValues[index] }), {}); + const bioKeys = Array.from(document.querySelectorAll('table.table td:nth-child(1)'), el => el.textContent.slice(0, -1)); + const bioValues = Array.from(document.querySelectorAll('table.table td:nth-child(2)'), el => el.textContent); + const bio = bioKeys.reduce((acc, key, index) => ({ ...acc, [key]: bioValues[index] }), {}); - if (bio.Measurements) [profile.bust, profile.waist, profile.hip] = bio.Measurements.split('-'); - if (bio.Birthplace) profile.birthPlace = bio.Birthplace; + if (bio.Measurements) [profile.bust, profile.waist, profile.hip] = bio.Measurements.split('-'); + if (bio.Birthplace) profile.birthPlace = bio.Birthplace; - if (bio.Height) { - const [feet, inches] = bio.Height.match(/\d+/g); - profile.height = feetInchesToCm(feet, inches); - } + if (bio.Height) { + const [feet, inches] = bio.Height.match(/\d+/g); + profile.height = feetInchesToCm(feet, inches); + } - if (bio.Ethnicity) profile.ethnicity = bio.Ethnicity; + if (bio.Ethnicity) profile.ethnicity = bio.Ethnicity; - const avatarEl = Array.from(document.querySelectorAll('img')).find(photo => photo.src.match('model')); + const avatarEl = Array.from(document.querySelectorAll('img')).find(photo => photo.src.match('model')); - if (avatarEl) profile.avatar = avatarEl.src; + if (avatarEl) profile.avatar = avatarEl.src; - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const url = `https://kellymadison.com/episodes/search?page=${page}`; // TLS issues with teenfidelity.com, same overview on all sites - const res = await bhttp.get(url, { - headers: { - 'X-Requested-With': 'XMLHttpRequest', - }, - }); + const url = `https://kellymadison.com/episodes/search?page=${page}`; // TLS issues with teenfidelity.com, same overview on all sites + const res = await bhttp.get(url, { + headers: { + 'X-Requested-With': 'XMLHttpRequest', + }, + }); - if (res.statusCode === 200 && res.body.status === 'success') { - return scrapeLatest(res.body.html, site); - } + if (res.statusCode === 200 && res.body.status === 'success') { + return scrapeLatest(res.body.html, site); + } - return null; + return null; } async function fetchScene(url, site, baseRelease) { - const { pathname } = new URL(url); + const { pathname } = new URL(url); - const res = await bhttp.get(`https://www.kellymadison.com${pathname}`, { - headers: { - 'X-Requested-With': 'XMLHttpRequest', - }, - }); + const res = await bhttp.get(`https://www.kellymadison.com${pathname}`, { + headers: { + 'X-Requested-With': 'XMLHttpRequest', + }, + }); - return scrapeScene(res.body.toString(), url, site, baseRelease); + return scrapeScene(res.body.toString(), url, site, baseRelease); } async function fetchProfile(actorName) { - const actorSlug = actorName.toLowerCase().replace(/\s+/g, '-'); - const res = await bhttp.get(`https://www.kellymadison.com/models/${actorSlug}`, { - headers: { - 'X-Requested-With': 'XMLHttpRequest', - }, - }); + const actorSlug = actorName.toLowerCase().replace(/\s+/g, '-'); + const res = await bhttp.get(`https://www.kellymadison.com/models/${actorSlug}`, { + headers: { + 'X-Requested-With': 'XMLHttpRequest', + }, + }); - if (res.statusCode === 200) { - return scrapeProfile(res.body.toString(), actorName); - } + if (res.statusCode === 200) { + return scrapeProfile(res.body.toString(), actorName); + } - return null; + return null; } module.exports = { - fetchLatest, - fetchProfile, - fetchScene, + fetchLatest, + fetchProfile, + fetchScene, }; diff --git a/src/scrapers/kink.js b/src/scrapers/kink.js index 5603322e..35ed5f5b 100644 --- a/src/scrapers/kink.js +++ b/src/scrapers/kink.js @@ -5,116 +5,116 @@ const cheerio = require('cheerio'); const moment = require('moment'); function scrapeLatest(html, site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const sceneElements = $('.shoot-list .shoot').toArray(); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const sceneElements = $('.shoot-list .shoot').toArray(); - return sceneElements.map((element) => { - const sceneLinkElement = $(element).find('.shoot-thumb-title a'); - const href = sceneLinkElement.attr('href'); - const url = `https://kink.com${href}`; - const shootId = href.split('/')[2]; - const title = sceneLinkElement.text().trim(); + return sceneElements.map((element) => { + const sceneLinkElement = $(element).find('.shoot-thumb-title a'); + const href = sceneLinkElement.attr('href'); + const url = `https://kink.com${href}`; + const shootId = href.split('/')[2]; + const title = sceneLinkElement.text().trim(); - const poster = $(element).find('.adimage').attr('src'); - const photos = $(element).find('.rollover .roll-image').map((photoIndex, photoElement) => $(photoElement).attr('data-imagesrc')).toArray(); + const poster = $(element).find('.adimage').attr('src'); + const photos = $(element).find('.rollover .roll-image').map((photoIndex, photoElement) => $(photoElement).attr('data-imagesrc')).toArray(); - const date = moment.utc($(element).find('.date').text(), 'MMM DD, YYYY').toDate(); - const actors = $(element).find('.shoot-thumb-models a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); - const stars = $(element).find('.average-rating').attr('data-rating') / 10; + const date = moment.utc($(element).find('.date').text(), 'MMM DD, YYYY').toDate(); + const actors = $(element).find('.shoot-thumb-models a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); + const stars = $(element).find('.average-rating').attr('data-rating') / 10; - const timestamp = $(element).find('.video span').text(); - const timestampComponents = timestamp.split(':'); // fix mixed hh:mm:ss and mm:ss format - const duration = moment.duration(timestampComponents.length > 2 ? timestamp : `0:${timestamp}`).asSeconds(); + const timestamp = $(element).find('.video span').text(); + const timestampComponents = timestamp.split(':'); // fix mixed hh:mm:ss and mm:ss format + const duration = moment.duration(timestampComponents.length > 2 ? timestamp : `0:${timestamp}`).asSeconds(); - return { - url, - shootId, - entryId: shootId, - title, - actors, - date, - photos, - poster, - rating: { - stars, - }, - duration, - site, - }; - }); + return { + url, + shootId, + entryId: shootId, + title, + actors, + date, + photos, + poster, + rating: { + stars, + }, + duration, + site, + }; + }); } async function scrapeScene(html, url, shootId, ratingRes, site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); + const $ = cheerio.load(html, { normalizeWhitespace: true }); - // const title = $('h1.shoot-title').text().replace(/\ue800/, ''); // fallback, special character is 'like'-heart - const title = $('h1.shoot-title span.favorite-button').attr('data-title'); - const actorsRaw = $('.shoot-info p.starring'); + // const title = $('h1.shoot-title').text().replace(/\ue800/, ''); // fallback, special character is 'like'-heart + const title = $('h1.shoot-title span.favorite-button').attr('data-title'); + const actorsRaw = $('.shoot-info p.starring'); - const photos = $('.gallery .thumb img').map((photoIndex, photoElement) => $(photoElement).attr('data-image-file')).toArray(); - const trailerVideo = $('.player span[data-type="trailer-src"]').attr('data-url'); - const trailerPoster = $('.player video#kink-player').attr('poster'); + const photos = $('.gallery .thumb img').map((photoIndex, photoElement) => $(photoElement).attr('data-image-file')).toArray(); + const trailerVideo = $('.player span[data-type="trailer-src"]').attr('data-url'); + const trailerPoster = $('.player video#kink-player').attr('poster'); - const date = moment.utc($(actorsRaw) - .prev() - .text() - .trim() - .replace('Date: ', ''), - 'MMMM DD, YYYY') - .toDate(); + const date = moment.utc($(actorsRaw) + .prev() + .text() + .trim() + .replace('Date: ', ''), + 'MMMM DD, YYYY') + .toDate(); - const actors = $(actorsRaw).find('span.names a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); - const description = $('.shoot-info .description').text().trim(); + const actors = $(actorsRaw).find('span.names a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); + const description = $('.shoot-info .description').text().trim(); - const { average: stars } = ratingRes.body; + const { average: stars } = ratingRes.body; - const siteName = $('.shoot-logo a').attr('href').split('/')[2]; - const siteSlug = siteName.replace(/\s+/g, '').toLowerCase(); + const siteName = $('.shoot-logo a').attr('href').split('/')[2]; + const siteSlug = siteName.replace(/\s+/g, '').toLowerCase(); - const tags = $('.tag-list > a[href*="/tag"]').map((tagIndex, tagElement) => $(tagElement).text()).toArray(); - const channel = siteSlug; + const tags = $('.tag-list > a[href*="/tag"]').map((tagIndex, tagElement) => $(tagElement).text()).toArray(); + const channel = siteSlug; - return { - url, - shootId, - entryId: shootId, - title, - date, - actors, - description, - photos, - poster: trailerPoster, - trailer: { - src: trailerVideo, - quality: 480, - }, - rating: { - stars, - }, - tags, - site, - channel, - }; + return { + url, + shootId, + entryId: shootId, + title, + date, + actors, + description, + photos, + poster: trailerPoster, + trailer: { + src: trailerVideo, + quality: 480, + }, + rating: { + stars, + }, + tags, + site, + channel, + }; } async function fetchLatest(site, page = 1) { - const res = await bhttp.get(`${site.url}/latest/page/${page}`); + const res = await bhttp.get(`${site.url}/latest/page/${page}`); - return scrapeLatest(res.body.toString(), site); + return scrapeLatest(res.body.toString(), site); } async function fetchScene(url, site) { - const shootId = new URL(url).pathname.split('/')[2]; + const shootId = new URL(url).pathname.split('/')[2]; - const [res, ratingRes] = await Promise.all([ - bhttp.get(url), - bhttp.get(`https://kink.com/api/ratings/${shootId}`), - ]); + const [res, ratingRes] = await Promise.all([ + bhttp.get(url), + bhttp.get(`https://kink.com/api/ratings/${shootId}`), + ]); - return scrapeScene(res.body.toString(), url, shootId, ratingRes, site); + return scrapeScene(res.body.toString(), url, shootId, ratingRes, site); } module.exports = { - fetchLatest, - fetchScene, + fetchLatest, + fetchScene, }; diff --git a/src/scrapers/legalporno.js b/src/scrapers/legalporno.js index bd253a17..eafe2481 100644 --- a/src/scrapers/legalporno.js +++ b/src/scrapers/legalporno.js @@ -8,201 +8,201 @@ const moment = require('moment'); const slugify = require('../utils/slugify'); function extractTitle(originalTitle) { - const titleComponents = originalTitle.split(' '); - const sceneIdMatch = titleComponents.slice(-1)[0].match(/(AB|AF|GP|SZ|IV|GIO|RS|TW|MA|FM|SAL|NR|AA|GL|BZ|FS|KS|OT)\d+/); // detect studio prefixes - const shootId = sceneIdMatch ? sceneIdMatch[0] : null; - const title = sceneIdMatch ? titleComponents.slice(0, -1).join(' ') : originalTitle; + const titleComponents = originalTitle.split(' '); + const sceneIdMatch = titleComponents.slice(-1)[0].match(/(AB|AF|GP|SZ|IV|GIO|RS|TW|MA|FM|SAL|NR|AA|GL|BZ|FS|KS|OT)\d+/); // detect studio prefixes + const shootId = sceneIdMatch ? sceneIdMatch[0] : null; + const title = sceneIdMatch ? titleComponents.slice(0, -1).join(' ') : originalTitle; - return { shootId, title }; + return { shootId, title }; } function getPoster(posterElement, sceneId) { - const posterStyle = posterElement.attr('style'); + const posterStyle = posterElement.attr('style'); - if (posterStyle) { - return posterStyle.slice(posterStyle.indexOf('(') + 1, -1); - } + if (posterStyle) { + return posterStyle.slice(posterStyle.indexOf('(') + 1, -1); + } - const posterRange = posterElement.attr('data-casting'); - const posterRangeData = posterRange ? JSON.parse(posterRange) : null; - const posterTimeRange = posterRangeData[Math.floor(Math.random() * posterRangeData.length)]; + const posterRange = posterElement.attr('data-casting'); + const posterRangeData = posterRange ? JSON.parse(posterRange) : null; + const posterTimeRange = posterRangeData[Math.floor(Math.random() * posterRangeData.length)]; - if (!posterTimeRange) { - return null; - } + if (!posterTimeRange) { + return null; + } - if (typeof posterTimeRange === 'number') { - // poster time is already a single time value - return `https://legalporno.com/casting/${sceneId}/${posterTimeRange}`; - } + if (typeof posterTimeRange === 'number') { + // poster time is already a single time value + return `https://legalporno.com/casting/${sceneId}/${posterTimeRange}`; + } - const [max, min] = posterTimeRange.split('-'); - const posterTime = Math.floor(Math.random() * (Number(max) - Number(min) + 1) + Number(min)); + const [max, min] = posterTimeRange.split('-'); + const posterTime = Math.floor(Math.random() * (Number(max) - Number(min) + 1) + Number(min)); - return `https://legalporno.com/casting/${sceneId}/${posterTime}`; + return `https://legalporno.com/casting/${sceneId}/${posterTime}`; } function scrapeLatest(html, site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const scenesElements = $('.thumbnails > div').toArray(); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const scenesElements = $('.thumbnails > div').toArray(); - return scenesElements.map((element) => { - const sceneLinkElement = $(element).find('.thumbnail-title a'); - const url = sceneLinkElement.attr('href'); + return scenesElements.map((element) => { + const sceneLinkElement = $(element).find('.thumbnail-title a'); + const url = sceneLinkElement.attr('href'); - const originalTitle = sceneLinkElement.text().trim(); // title attribute breaks when they use \\ escaping - const { shootId, title } = extractTitle(originalTitle); - const entryId = new URL(url).pathname.split('/')[2]; + const originalTitle = sceneLinkElement.text().trim(); // title attribute breaks when they use \\ escaping + const { shootId, title } = extractTitle(originalTitle); + const entryId = new URL(url).pathname.split('/')[2]; - const date = moment.utc($(element).attr('release'), 'YYYY/MM/DD').toDate(); + const date = moment.utc($(element).attr('release'), 'YYYY/MM/DD').toDate(); - const sceneId = $(element).attr('data-content'); - const posterElement = $(element).find('.thumbnail-avatar'); + const sceneId = $(element).attr('data-content'); + const posterElement = $(element).find('.thumbnail-avatar'); - const poster = getPoster(posterElement, sceneId); + const poster = getPoster(posterElement, sceneId); - return { - url, - shootId, - entryId, - title, - date, - poster, - site, - }; - }); + return { + url, + shootId, + entryId, + title, + date, + poster, + site, + }; + }); } async function scrapeScene(html, url, site, useGallery) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const playerObject = $('script:contains("new WatchPage")').html(); - const playerData = playerObject && playerObject.slice(playerObject.indexOf('{"swf":'), playerObject.lastIndexOf('},') + 1); - const data = playerData && JSON.parse(playerData); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const playerObject = $('script:contains("new WatchPage")').html(); + const playerData = playerObject && playerObject.slice(playerObject.indexOf('{"swf":'), playerObject.lastIndexOf('},') + 1); + const data = playerData && JSON.parse(playerData); - const release = { url }; + const release = { url }; - const originalTitle = $('h1.watchpage-title').text().trim(); - const { shootId, title } = extractTitle(originalTitle); + const originalTitle = $('h1.watchpage-title').text().trim(); + const { shootId, title } = extractTitle(originalTitle); - release.shootId = shootId; - release.entryId = new URL(url).pathname.split('/')[2]; + release.shootId = shootId; + release.entryId = new URL(url).pathname.split('/')[2]; - release.title = title; - release.date = moment.utc($('span[title="Release date"] a').text(), 'YYYY-MM-DD').toDate(); + release.title = title; + release.date = moment.utc($('span[title="Release date"] a').text(), 'YYYY-MM-DD').toDate(); - const [actorsElement, tagsElement, descriptionElement] = $('.scene-description__row').toArray(); + const [actorsElement, tagsElement, descriptionElement] = $('.scene-description__row').toArray(); - release.description = $('meta[name="description"]')?.attr('content')?.trim() + release.description = $('meta[name="description"]')?.attr('content')?.trim() || (descriptionElement && $(descriptionElement).find('dd').text().trim()); - release.actors = $(actorsElement) - .find('a[href*="com/model"]') - .map((actorIndex, actorElement) => $(actorElement).text()).toArray(); + release.actors = $(actorsElement) + .find('a[href*="com/model"]') + .map((actorIndex, actorElement) => $(actorElement).text()).toArray(); - release.duration = moment.duration($('span[title="Runtime"]').text().trim()).asSeconds(); - release.tags = $(tagsElement).find('a').map((tagIndex, tagElement) => $(tagElement).text()).toArray(); + release.duration = moment.duration($('span[title="Runtime"]').text().trim()).asSeconds(); + release.tags = $(tagsElement).find('a').map((tagIndex, tagElement) => $(tagElement).text()).toArray(); - const photos = useGallery - ? $('.gallery a img').map((photoIndex, photoElement) => $(photoElement).attr('src')).toArray() - : $('.screenshots img').map((photoIndex, photoElement) => $(photoElement).attr('src')).toArray(); + const photos = useGallery + ? $('.gallery a img').map((photoIndex, photoElement) => $(photoElement).attr('src')).toArray() + : $('.screenshots img').map((photoIndex, photoElement) => $(photoElement).attr('src')).toArray(); - release.photos = photos.map((source) => { - // source without parameters sometimes serves larger preview photo - const { origin, pathname } = new URL(source); + release.photos = photos.map((source) => { + // source without parameters sometimes serves larger preview photo + const { origin, pathname } = new URL(source); - return `${origin}${pathname}`; + return `${origin}${pathname}`; - /* disable thumbnail as fallback, usually enough high res photos available + /* disable thumbnail as fallback, usually enough high res photos available return [ `${origin}${pathname}`, source, ]; */ - }); + }); - const posterStyle = $('#player').attr('style'); - const poster = posterStyle.slice(posterStyle.indexOf('(') + 1, -1); + const posterStyle = $('#player').attr('style'); + const poster = posterStyle.slice(posterStyle.indexOf('(') + 1, -1); - release.poster = poster || release.photos.slice(Math.floor(release.photos.length / 3) * -1); // poster unavailable, try last 1/3rd of high res photos as fallback + release.poster = poster || release.photos.slice(Math.floor(release.photos.length / 3) * -1); // poster unavailable, try last 1/3rd of high res photos as fallback - if (data) { - const qualityMap = { - web: 240, - vga: 480, - hd: 720, - '1080p': 1080, - }; + if (data) { + const qualityMap = { + web: 240, + vga: 480, + hd: 720, + '1080p': 1080, + }; - release.trailer = data.clip.qualities.map(trailer => ({ - src: trailer.src, - type: trailer.type, - quality: qualityMap[trailer.quality] || trailer.quality, - })); - } + release.trailer = data.clip.qualities.map(trailer => ({ + src: trailer.src, + type: trailer.type, + quality: qualityMap[trailer.quality] || trailer.quality, + })); + } - const studioName = $('.watchpage-studioname').first().text().trim(); - release.studio = slugify(studioName, ''); + const studioName = $('.watchpage-studioname').first().text().trim(); + release.studio = slugify(studioName, ''); - return release; + return release; } async function scrapeProfile(html, _url, actorName) { - const { document } = new JSDOM(html).window; + const { document } = new JSDOM(html).window; - const profile = { - name: actorName, - }; + const profile = { + name: actorName, + }; - const avatarEl = document.querySelector('.model--avatar img[src^="http"]'); - const entries = Array.from(document.querySelectorAll('.model--description tr'), el => el.textContent.replace(/\n/g, '').split(':')); + const avatarEl = document.querySelector('.model--avatar img[src^="http"]'); + const entries = Array.from(document.querySelectorAll('.model--description tr'), el => el.textContent.replace(/\n/g, '').split(':')); - const bio = entries - .filter(entry => entry.length === 2) // ignore entries without ':' (About section, see Blanche Bradburry) - .reduce((acc, [key, value]) => ({ ...acc, [key.trim()]: value.trim() }), {}); + const bio = entries + .filter(entry => entry.length === 2) // ignore entries without ':' (About section, see Blanche Bradburry) + .reduce((acc, [key, value]) => ({ ...acc, [key.trim()]: value.trim() }), {}); - profile.birthPlace = bio.Nationality; + profile.birthPlace = bio.Nationality; - if (bio.Age) profile.age = bio.Age; - if (avatarEl) profile.avatar = avatarEl.src; + if (bio.Age) profile.age = bio.Age; + if (avatarEl) profile.avatar = avatarEl.src; - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const res = await bhttp.get(`${site.url}/new-videos/${page}`); + const res = await bhttp.get(`${site.url}/new-videos/${page}`); - return scrapeLatest(res.body.toString(), site); + return scrapeLatest(res.body.toString(), site); } async function fetchScene(url, site) { - const useGallery = true; + const useGallery = true; - // TODO: fall back on screenshots when gallery is not available - const res = useGallery - ? await bhttp.get(`${url}/gallery#gallery`) - : await bhttp.get(`${url}/screenshots#screenshots`); + // TODO: fall back on screenshots when gallery is not available + const res = useGallery + ? await bhttp.get(`${url}/gallery#gallery`) + : await bhttp.get(`${url}/screenshots#screenshots`); - return scrapeScene(res.body.toString(), url, site, useGallery); + return scrapeScene(res.body.toString(), url, site, useGallery); } async function fetchProfile(actorName) { - const res = await bhttp.get(`https://www.legalporno.com/api/autocomplete/search?q=${actorName.replace(' ', '+')}`); - const data = res.body; + const res = await bhttp.get(`https://www.legalporno.com/api/autocomplete/search?q=${actorName.replace(' ', '+')}`); + const data = res.body; - const result = data.terms.find(item => item.type === 'model'); + const result = data.terms.find(item => item.type === 'model'); - if (result) { - const bioRes = await bhttp.get(result.url); - const html = bioRes.body.toString(); + if (result) { + const bioRes = await bhttp.get(result.url); + const html = bioRes.body.toString(); - return scrapeProfile(html, result.url, actorName); - } + return scrapeProfile(html, result.url, actorName); + } - return null; + return null; } module.exports = { - fetchLatest, - fetchProfile, - fetchScene, + fetchLatest, + fetchProfile, + fetchScene, }; diff --git a/src/scrapers/men.js b/src/scrapers/men.js index 4ae2fd3c..304e59ad 100644 --- a/src/scrapers/men.js +++ b/src/scrapers/men.js @@ -3,11 +3,11 @@ const { fetchScene, fetchLatest, fetchProfile } = require('./mindgeek'); async function networkFetchProfile(actorName) { - return fetchProfile(actorName, 'men', 'modelprofile'); + return fetchProfile(actorName, 'men', 'modelprofile'); } module.exports = { - fetchLatest, - fetchScene, - fetchProfile: networkFetchProfile, + fetchLatest, + fetchScene, + fetchProfile: networkFetchProfile, }; diff --git a/src/scrapers/metrohd.js b/src/scrapers/metrohd.js index 4fa60928..78c2b30b 100644 --- a/src/scrapers/metrohd.js +++ b/src/scrapers/metrohd.js @@ -3,11 +3,11 @@ const { fetchScene, fetchLatest, fetchProfile } = require('./mindgeek'); async function networkFetchProfile(actorName) { - return fetchProfile(actorName, 'devianthardcore'); + return fetchProfile(actorName, 'devianthardcore'); } module.exports = { - fetchLatest, - fetchProfile: networkFetchProfile, - fetchScene, + fetchLatest, + fetchProfile: networkFetchProfile, + fetchScene, }; diff --git a/src/scrapers/mikeadriano.js b/src/scrapers/mikeadriano.js index af703cf1..341c048b 100644 --- a/src/scrapers/mikeadriano.js +++ b/src/scrapers/mikeadriano.js @@ -8,232 +8,232 @@ const moment = require('moment'); const { get } = require('../utils/http'); const descriptionTags = { - 'anal cream pie': 'anal creampie', - 'ass to mouth': 'ass to mouth', - 'cream pie in her ass': 'anal creampie', - 'eats ass': 'ass eating', - facial: 'facial', - gaped: 'gaping', - gapes: 'gaping', - gape: 'gaping', - 'rectal cream pie': 'anal creampie', - rimming: 'ass eating', + 'anal cream pie': 'anal creampie', + 'ass to mouth': 'ass to mouth', + 'cream pie in her ass': 'anal creampie', + 'eats ass': 'ass eating', + facial: 'facial', + gaped: 'gaping', + gapes: 'gaping', + gape: 'gaping', + 'rectal cream pie': 'anal creampie', + rimming: 'ass eating', }; function deriveTagsFromDescription(description) { - const matches = (description || '').toLowerCase().match(new RegExp(Object.keys(descriptionTags).join('|'), 'g')); + const matches = (description || '').toLowerCase().match(new RegExp(Object.keys(descriptionTags).join('|'), 'g')); - return matches - ? matches.map(match => descriptionTags[match]) - : []; + return matches + ? matches.map(match => descriptionTags[match]) + : []; } async function scrapeLatestA(html, site) { - const { document } = new JSDOM(html).window; - const sceneElements = document.querySelectorAll('.content-item-large, .content-item'); + const { document } = new JSDOM(html).window; + const sceneElements = document.querySelectorAll('.content-item-large, .content-item'); - return Promise.all(Array.from(sceneElements, async (element) => { - const $ = cheerio.load(element.innerHTML, { normalizeWhitespace: true }); + return Promise.all(Array.from(sceneElements, async (element) => { + const $ = cheerio.load(element.innerHTML, { normalizeWhitespace: true }); - const titleElement = element.querySelector('h3.title a'); - const title = titleElement.textContent; - const url = titleElement.href; - const entryId = url.split('/').slice(-2)[0]; + const titleElement = element.querySelector('h3.title a'); + const title = titleElement.textContent; + const url = titleElement.href; + const entryId = url.split('/').slice(-2)[0]; - const descriptionElement = element.querySelector('.desc'); - const description = descriptionElement && descriptionElement.textContent.trim(); - const date = moment(element.querySelector('.date, time').textContent, 'Do MMM YYYY').toDate(); + const descriptionElement = element.querySelector('.desc'); + const description = descriptionElement && descriptionElement.textContent.trim(); + const date = moment(element.querySelector('.date, time').textContent, 'Do MMM YYYY').toDate(); - const actors = Array.from(element.querySelectorAll('h4.models a'), actorElement => actorElement.textContent); + const actors = Array.from(element.querySelectorAll('h4.models a'), actorElement => actorElement.textContent); - const durationString = element.querySelector('.total-time').textContent.trim(); - // timestamp is sometimes 00:00, sometimes 0:00:00 - const duration = durationString.split(':').length === 3 - ? moment.duration(durationString).asSeconds() - : moment.duration(`00:${durationString}`).asSeconds(); + const durationString = element.querySelector('.total-time').textContent.trim(); + // timestamp is sometimes 00:00, sometimes 0:00:00 + const duration = durationString.split(':').length === 3 + ? moment.duration(durationString).asSeconds() + : moment.duration(`00:${durationString}`).asSeconds(); - const ratingElement = element.querySelector('.rating'); - const stars = ratingElement && ratingElement.dataset.rating; + const ratingElement = element.querySelector('.rating'); + const stars = ratingElement && ratingElement.dataset.rating; - const [poster, ...primaryPhotos] = Array.from(element.querySelectorAll('img'), imageElement => imageElement.src); - const secondaryPhotos = $('.thumb-top, .thumb-bottom') - .map((photoIndex, photoElement) => $(photoElement).css()['background-image']) - .toArray() - .map(photoUrl => photoUrl.slice(photoUrl.indexOf('http'), photoUrl.indexOf('.jpg') + 4)); + const [poster, ...primaryPhotos] = Array.from(element.querySelectorAll('img'), imageElement => imageElement.src); + const secondaryPhotos = $('.thumb-top, .thumb-bottom') + .map((photoIndex, photoElement) => $(photoElement).css()['background-image']) + .toArray() + .map(photoUrl => photoUrl.slice(photoUrl.indexOf('http'), photoUrl.indexOf('.jpg') + 4)); - const photos = [...primaryPhotos, ...secondaryPhotos]; - const tags = deriveTagsFromDescription(description); + const photos = [...primaryPhotos, ...secondaryPhotos]; + const tags = deriveTagsFromDescription(description); - const scene = { - url, - entryId, - title, - description, - actors, - director: 'Mike Adriano', - date, - duration, - tags, - poster, - photos, - rating: { - stars, - }, - site, - }; + const scene = { + url, + entryId, + title, + description, + actors, + director: 'Mike Adriano', + date, + duration, + tags, + poster, + photos, + rating: { + stars, + }, + site, + }; - return scene; - })); + return scene; + })); } async function scrapeLatestB(html) { - const { document } = new JSDOM(html).window; - const sceneElements = document.querySelectorAll('.content-border'); + const { document } = new JSDOM(html).window; + const sceneElements = document.querySelectorAll('.content-border'); - return Promise.all(Array.from(sceneElements, async (element) => { - const $ = cheerio.load(element.innerHTML, { normalizeWhitespace: true }); - const release = { - director: 'Mike Adriano', - }; + return Promise.all(Array.from(sceneElements, async (element) => { + const $ = cheerio.load(element.innerHTML, { normalizeWhitespace: true }); + const release = { + director: 'Mike Adriano', + }; - const titleElement = element.querySelector('.content-title-wrap a'); - release.title = titleElement.title || titleElement.textContent.trim(); - release.url = titleElement.href; - release.entryId = release.url.split('/').slice(-2)[0]; + const titleElement = element.querySelector('.content-title-wrap a'); + release.title = titleElement.title || titleElement.textContent.trim(); + release.url = titleElement.href; + release.entryId = release.url.split('/').slice(-2)[0]; - release.description = element.querySelector('.content-description').textContent.trim(); - release.date = (moment(element.querySelector('.mobile-date').textContent, 'MM/DD/YYYY') + release.description = element.querySelector('.content-description').textContent.trim(); + release.date = (moment(element.querySelector('.mobile-date').textContent, 'MM/DD/YYYY') || moment(element.querySelector('.date').textContent, 'Do MMM YYYY')).toDate(); - release.actors = Array.from(element.querySelectorAll('.content-models a'), actorElement => actorElement.textContent); + release.actors = Array.from(element.querySelectorAll('.content-models a'), actorElement => actorElement.textContent); - const durationString = element.querySelector('.total-time').textContent.trim(); - // timestamp is somethines 00:00, sometimes 0:00:00 - release.duration = durationString.split(':').length === 3 - ? moment.duration(durationString).asSeconds() - : moment.duration(`00:${durationString}`).asSeconds(); + const durationString = element.querySelector('.total-time').textContent.trim(); + // timestamp is somethines 00:00, sometimes 0:00:00 + release.duration = durationString.split(':').length === 3 + ? moment.duration(durationString).asSeconds() + : moment.duration(`00:${durationString}`).asSeconds(); - const [poster, ...primaryPhotos] = Array.from(element.querySelectorAll('a img'), imageElement => imageElement.src); - const secondaryPhotos = $('.thumb-mouseover') - .map((photoIndex, photoElement) => $(photoElement).css()['background-image']) - .toArray() - .map(photoUrl => photoUrl.slice(photoUrl.indexOf('http'), photoUrl.indexOf('.jpg') + 4)); + const [poster, ...primaryPhotos] = Array.from(element.querySelectorAll('a img'), imageElement => imageElement.src); + const secondaryPhotos = $('.thumb-mouseover') + .map((photoIndex, photoElement) => $(photoElement).css()['background-image']) + .toArray() + .map(photoUrl => photoUrl.slice(photoUrl.indexOf('http'), photoUrl.indexOf('.jpg') + 4)); - release.poster = poster; - release.photos = [...primaryPhotos, ...secondaryPhotos]; + release.poster = poster; + release.photos = [...primaryPhotos, ...secondaryPhotos]; - release.tags = deriveTagsFromDescription(release.description); - return release; - })); + release.tags = deriveTagsFromDescription(release.description); + return release; + })); } async function scrapeSceneA(html, url) { - const { document } = new JSDOM(html).window; - const element = document.querySelector('.content-page-info'); - const release = { - url, - director: 'Mike Adriano', - }; + const { document } = new JSDOM(html).window; + const element = document.querySelector('.content-page-info'); + const release = { + url, + director: 'Mike Adriano', + }; - release.entryId = url.split('/').slice(-2)[0]; - release.title = element.querySelector('.title').textContent.trim(); - release.description = element.querySelector('.desc').textContent.trim(); - release.date = moment(element.querySelector('.post-date').textContent.trim(), 'Do MMM YYYY').toDate(); + release.entryId = url.split('/').slice(-2)[0]; + release.title = element.querySelector('.title').textContent.trim(); + release.description = element.querySelector('.desc').textContent.trim(); + release.date = moment(element.querySelector('.post-date').textContent.trim(), 'Do MMM YYYY').toDate(); - release.actors = Array.from(element.querySelectorAll('.models a'), actorElement => actorElement.textContent); + release.actors = Array.from(element.querySelectorAll('.models a'), actorElement => actorElement.textContent); - const durationString = element.querySelector('.total-time').textContent.trim(); - // timestamp is sometimes 00:00, sometimes 0:00:00 - release.duration = durationString.split(':').length === 3 - ? moment.duration(durationString).asSeconds() - : moment.duration(`00:${durationString}`).asSeconds(); + const durationString = element.querySelector('.total-time').textContent.trim(); + // timestamp is sometimes 00:00, sometimes 0:00:00 + release.duration = durationString.split(':').length === 3 + ? moment.duration(durationString).asSeconds() + : moment.duration(`00:${durationString}`).asSeconds(); - const { poster } = document.querySelector('.content-page-header video'); - const { src, type } = document.querySelector('.content-page-header source'); + const { poster } = document.querySelector('.content-page-header video'); + const { src, type } = document.querySelector('.content-page-header source'); - release.poster = poster; - release.trailer = { src, type }; + release.poster = poster; + release.trailer = { src, type }; - release.tags = deriveTagsFromDescription(release.description); + release.tags = deriveTagsFromDescription(release.description); - return release; + return release; } async function scrapeSceneB(html, url, site) { - const { document } = new JSDOM(html).window; - const element = document.querySelector('.content-page-info'); + const { document } = new JSDOM(html).window; + const element = document.querySelector('.content-page-info'); - const entryId = url.split('/').slice(-2)[0]; - const title = element.querySelector('.title').textContent.trim(); - const description = element.querySelector('.desc').textContent.trim(); - const date = moment(element.querySelector('.date').textContent.trim(), 'Do MMM YYYY').toDate(); + const entryId = url.split('/').slice(-2)[0]; + const title = element.querySelector('.title').textContent.trim(); + const description = element.querySelector('.desc').textContent.trim(); + const date = moment(element.querySelector('.date').textContent.trim(), 'Do MMM YYYY').toDate(); - const actors = Array.from(element.querySelectorAll('.models a'), actorElement => actorElement.textContent); + const actors = Array.from(element.querySelectorAll('.models a'), actorElement => actorElement.textContent); - const durationString = element.querySelector('.total-time').textContent.trim(); - // timestamp is somethines 00:00, sometimes 0:00:00 - const duration = durationString.split(':').length === 3 - ? moment.duration(durationString).asSeconds() - : moment.duration(`00:${durationString}`).asSeconds(); + const durationString = element.querySelector('.total-time').textContent.trim(); + // timestamp is somethines 00:00, sometimes 0:00:00 + const duration = durationString.split(':').length === 3 + ? moment.duration(durationString).asSeconds() + : moment.duration(`00:${durationString}`).asSeconds(); - const { poster } = document.querySelector('.content-page-header-inner video'); - const { src, type } = document.querySelector('.content-page-header-inner source'); + const { poster } = document.querySelector('.content-page-header-inner video'); + const { src, type } = document.querySelector('.content-page-header-inner source'); - const tags = deriveTagsFromDescription(description); + const tags = deriveTagsFromDescription(description); - const scene = { - url, - entryId, - title, - description, - actors, - director: 'Mike Adriano', - date, - duration, - tags, - poster, - trailer: { - src, - type, - }, - site, - }; + const scene = { + url, + entryId, + title, + description, + actors, + director: 'Mike Adriano', + date, + duration, + tags, + poster, + trailer: { + src, + type, + }, + site, + }; - return scene; + return scene; } async function fetchLatest(site, page = 1) { - const { host } = new URL(site.url); - const url = `https://tour.${host}/videos?page=${page}`; + const { host } = new URL(site.url); + const url = `https://tour.${host}/videos?page=${page}`; - const res = await get(url); + const res = await get(url); - if (res.code === 200) { - if (host === 'trueanal.com' || host === 'swallowed.com') { - return scrapeLatestA(res.html, site); - } + if (res.code === 200) { + if (host === 'trueanal.com' || host === 'swallowed.com') { + return scrapeLatestA(res.html, site); + } - return scrapeLatestB(res.html, site); - } + return scrapeLatestB(res.html, site); + } - return res.code; + return res.code; } async function fetchScene(url, site) { - const { host } = new URL(site.url); - const res = await get(url); + const { host } = new URL(site.url); + const res = await get(url); - if (res.code === 200) { - if (host === 'trueanal.com' || host === 'swallowed.com') { - return scrapeSceneA(res.body.toString(), url, site); - } + if (res.code === 200) { + if (host === 'trueanal.com' || host === 'swallowed.com') { + return scrapeSceneA(res.body.toString(), url, site); + } - return scrapeSceneB(res.body.toString(), url, site); - } + return scrapeSceneB(res.body.toString(), url, site); + } - return res.code; + return res.code; } module.exports = { - fetchLatest, - fetchScene, + fetchLatest, + fetchScene, }; diff --git a/src/scrapers/milehighmedia.js b/src/scrapers/milehighmedia.js index 3f7b0a39..a77460a0 100644 --- a/src/scrapers/milehighmedia.js +++ b/src/scrapers/milehighmedia.js @@ -3,11 +3,11 @@ const { fetchScene, fetchLatest, fetchProfile } = require('./mindgeek'); async function networkFetchProfile(actorName) { - return fetchProfile(actorName, 'milehighmedia'); + return fetchProfile(actorName, 'milehighmedia'); } module.exports = { - fetchLatest, - fetchProfile: networkFetchProfile, - fetchScene, + fetchLatest, + fetchProfile: networkFetchProfile, + fetchScene, }; diff --git a/src/scrapers/mindgeek.js b/src/scrapers/mindgeek.js index 1d74499e..ea5c5b19 100644 --- a/src/scrapers/mindgeek.js +++ b/src/scrapers/mindgeek.js @@ -13,257 +13,257 @@ const { inchesToCm, lbsToKg } = require('../utils/convert'); const { cookieToData } = require('../utils/cookies'); function getThumbs(scene) { - if (scene.images.poster) { - return scene.images.poster.map(image => image.xl.url); - } + if (scene.images.poster) { + return scene.images.poster.map(image => image.xl.url); + } - if (scene.images.card_main_rect) { - return scene.images.card_main_rect - .concat(scene.images.card_secondary_rect || []) - .map(image => image.xl.url.replace('.thumb', '')); - } + if (scene.images.card_main_rect) { + return scene.images.card_main_rect + .concat(scene.images.card_secondary_rect || []) + .map(image => image.xl.url.replace('.thumb', '')); + } - return []; + return []; } function scrapeLatestX(data, site) { - if (site.parameters?.extract === true && data.collections.length > 0) { - // release should not belong to any channel - return null; - } + if (site.parameters?.extract === true && data.collections.length > 0) { + // release should not belong to any channel + return null; + } - if (typeof site.parameters?.extract === 'string' && !data.collections.some(collection => collection.shortName === site.parameters.extract)) { - // release should belong to specific channel - return null; - } + if (typeof site.parameters?.extract === 'string' && !data.collections.some(collection => collection.shortName === site.parameters.extract)) { + // release should belong to specific channel + return null; + } - const release = { - entryId: data.id, - title: data.title, - description: data.description, - }; + const release = { + entryId: data.id, + title: data.title, + description: data.description, + }; - const hostname = site.parameters?.native ? site.url : site.network.url; + const hostname = site.parameters?.native ? site.url : site.network.url; - release.url = `${hostname}/scene/${release.entryId}/`; - release.date = new Date(data.dateReleased); - release.actors = data.actors.map(actor => ({ name: actor.name, gender: actor.gender })); + release.url = `${hostname}/scene/${release.entryId}/`; + release.date = new Date(data.dateReleased); + release.actors = data.actors.map(actor => ({ name: actor.name, gender: actor.gender })); - release.tags = data.tags.map(tag => tag.name); + release.tags = data.tags.map(tag => tag.name); - release.duration = data.videos.mediabook?.length; - [release.poster, ...release.photos] = getThumbs(data); + release.duration = data.videos.mediabook?.length; + [release.poster, ...release.photos] = getThumbs(data); - const teaserSources = data.videos.mediabook?.files; + const teaserSources = data.videos.mediabook?.files; - if (teaserSources) { - release.teaser = Object.values(teaserSources).map(teaser => ({ - src: teaser.urls.view, - quality: parseInt(teaser.format, 10), - })); - } + if (teaserSources) { + release.teaser = Object.values(teaserSources).map(teaser => ({ + src: teaser.urls.view, + quality: parseInt(teaser.format, 10), + })); + } - return release; + return release; } async function scrapeLatest(items, site) { - const latestReleases = await Promise.all(items.map(async data => scrapeLatestX(data, site))); + const latestReleases = await Promise.all(items.map(async data => scrapeLatestX(data, site))); - return latestReleases.filter(Boolean); + return latestReleases.filter(Boolean); } function scrapeScene(data, url, _site, networkName) { - const release = {}; + const release = {}; - const { id: entryId, title, description } = data; + const { id: entryId, title, description } = data; - release.entryId = data.id; - release.title = title; - release.description = description; + release.entryId = data.id; + release.title = title; + release.description = description; - release.date = new Date(data.dateReleased); - release.actors = data.actors.map(actor => ({ name: actor.name, gender: actor.gender })); + release.date = new Date(data.dateReleased); + release.actors = data.actors.map(actor => ({ name: actor.name, gender: actor.gender })); - release.tags = data.tags.map(tag => tag.name); + release.tags = data.tags.map(tag => tag.name); - [release.poster, ...release.photos] = getThumbs(data); + [release.poster, ...release.photos] = getThumbs(data); - const teaserSources = data.videos.mediabook?.files; + const teaserSources = data.videos.mediabook?.files; - if (teaserSources) { - release.teaser = Object.values(teaserSources).map(teaser => ({ - src: teaser.urls.view, - quality: parseInt(teaser.format, 10), - })); - } + if (teaserSources) { + release.teaser = Object.values(teaserSources).map(teaser => ({ + src: teaser.urls.view, + quality: parseInt(teaser.format, 10), + })); + } - const siteName = data.collections[0]?.name || data.brand; - release.channel = slugify(siteName, ''); + const siteName = data.collections[0]?.name || data.brand; + release.channel = slugify(siteName, ''); - release.url = url || `https://www.${networkName || data.brand}.com/scene/${entryId}/`; + release.url = url || `https://www.${networkName || data.brand}.com/scene/${entryId}/`; - return release; + return release; } function getUrl(site) { - const { search } = new URL(site.url); + const { search } = new URL(site.url); - if (search.match(/\?site=\d+/)) { - return site.url; - } + if (search.match(/\?site=\d+/)) { + return site.url; + } - if (site.parameters?.native) { - return `${site.url}/scenes`; - } + if (site.parameters?.native) { + return `${site.url}/scenes`; + } - if (site.parameters?.extract) { - return `${site.url}/scenes`; - } + if (site.parameters?.extract) { + return `${site.url}/scenes`; + } - if (site.parameters?.siteId) { - return `${site.network.url}/scenes?site=${site.parameters.siteId}`; - } + if (site.parameters?.siteId) { + return `${site.network.url}/scenes?site=${site.parameters.siteId}`; + } - throw new Error(`Mind Geek site '${site.name}' (${site.url}) not supported`); + throw new Error(`Mind Geek site '${site.name}' (${site.url}) not supported`); } async function getSession(url) { - const cookieJar = new CookieJar(); - const session = bhttp.session({ cookieJar }); + const cookieJar = new CookieJar(); + const session = bhttp.session({ cookieJar }); - await session.get(url); + await session.get(url); - const cookieString = await cookieJar.getCookieStringAsync(url); - const { instance_token: instanceToken } = cookieToData(cookieString); + const cookieString = await cookieJar.getCookieStringAsync(url); + const { instance_token: instanceToken } = cookieToData(cookieString); - return { session, instanceToken }; + return { session, instanceToken }; } function scrapeProfile(data, html, releases = [], networkName) { - const { qa, qd } = ex(html); + const { qa, qd } = ex(html); - const profile = { - description: data.bio, - aliases: data.aliases, - }; + const profile = { + description: data.bio, + aliases: data.aliases, + }; - const [bust, waist, hip] = data.measurements.split('-'); + const [bust, waist, hip] = data.measurements.split('-'); - profile.gender = data.gender === 'other' ? 'transsexual' : data.gender; + profile.gender = data.gender === 'other' ? 'transsexual' : data.gender; - if (profile.gender === 'female') { - if (bust) profile.bust = bust.toUpperCase(); - if (waist) profile.waist = waist; - if (hip) profile.hip = hip; - } + if (profile.gender === 'female') { + if (bust) profile.bust = bust.toUpperCase(); + if (waist) profile.waist = waist; + if (hip) profile.hip = hip; + } - if (data.birthPlace) profile.birthPlace = data.birthPlace; - if (data.height) profile.height = inchesToCm(data.height); - if (data.weight) profile.weight = lbsToKg(data.weight); + if (data.birthPlace) profile.birthPlace = data.birthPlace; + if (data.height) profile.height = inchesToCm(data.height); + if (data.weight) profile.weight = lbsToKg(data.weight); - if (data.images.card_main_rect?.[0]) { - profile.avatar = data.images.card_main_rect[0].xl?.url + if (data.images.card_main_rect?.[0]) { + profile.avatar = data.images.card_main_rect[0].xl?.url || data.images.card_main_rect[0].lg?.url || data.images.card_main_rect[0].md?.url || data.images.card_main_rect[0].sm?.url || data.images.card_main_rect[0].xs?.url; - } + } - const birthdate = qa('li').find(el => /Date of Birth/.test(el.textContent)); - if (birthdate) profile.birthdate = qd(birthdate, 'span', 'MMMM Do, YYYY'); + const birthdate = qa('li').find(el => /Date of Birth/.test(el.textContent)); + if (birthdate) profile.birthdate = qd(birthdate, 'span', 'MMMM Do, YYYY'); - profile.releases = releases.map(release => scrapeScene(release, null, null, networkName)); + profile.releases = releases.map(release => scrapeScene(release, null, null, networkName)); - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const url = getUrl(site); - const { search } = new URL(url); - const siteId = new URLSearchParams(search).get('site'); + const url = getUrl(site); + const { search } = new URL(url); + const siteId = new URLSearchParams(search).get('site'); - const { session, instanceToken } = await getSession(url); + const { session, instanceToken } = await getSession(url); - const beforeDate = moment().add('1', 'day').format('YYYY-MM-DD'); - const limit = 10; - const apiUrl = site.parameters?.native || site.parameters?.extract - ? `https://site-api.project1service.com/v2/releases?dateReleased=<${beforeDate}&limit=${limit}&offset=${limit * (page - 1)}&orderBy=-dateReleased&type=scene` - : `https://site-api.project1service.com/v2/releases?collectionId=${siteId}&dateReleased=<${beforeDate}&limit=${limit}&offset=${limit * (page - 1)}&orderBy=-dateReleased&type=scene`; + const beforeDate = moment().add('1', 'day').format('YYYY-MM-DD'); + const limit = 10; + const apiUrl = site.parameters?.native || site.parameters?.extract + ? `https://site-api.project1service.com/v2/releases?dateReleased=<${beforeDate}&limit=${limit}&offset=${limit * (page - 1)}&orderBy=-dateReleased&type=scene` + : `https://site-api.project1service.com/v2/releases?collectionId=${siteId}&dateReleased=<${beforeDate}&limit=${limit}&offset=${limit * (page - 1)}&orderBy=-dateReleased&type=scene`; - const res = await session.get(apiUrl, { - headers: { - Instance: instanceToken, - Origin: site.url, - Referer: url, - }, - }); + const res = await session.get(apiUrl, { + headers: { + Instance: instanceToken, + Origin: site.url, + Referer: url, + }, + }); - if (res.statusCode === 200 && res.body.result) { - return scrapeLatest(res.body.result, site); - } + if (res.statusCode === 200 && res.body.result) { + return scrapeLatest(res.body.result, site); + } - return null; + return null; } async function fetchScene(url, site) { - const entryId = url.match(/\d+/)[0]; - const { session, instanceToken } = await getSession(url); + const entryId = url.match(/\d+/)[0]; + const { session, instanceToken } = await getSession(url); - const res = await session.get(`https://site-api.project1service.com/v2/releases/${entryId}`, { - headers: { - Instance: instanceToken, - }, - }); + const res = await session.get(`https://site-api.project1service.com/v2/releases/${entryId}`, { + headers: { + Instance: instanceToken, + }, + }); - if (res.statusCode === 200 && res.body.result) { - return scrapeScene(res.body.result, url, site); - } + if (res.statusCode === 200 && res.body.result) { + return scrapeScene(res.body.result, url, site); + } - return null; + return null; } async function fetchProfile(actorName, networkName, actorPath = 'model') { - const url = `https://www.${networkName}.com`; - const { session, instanceToken } = await getSession(url); + const url = `https://www.${networkName}.com`; + const { session, instanceToken } = await getSession(url); - const res = await session.get(`https://site-api.project1service.com/v1/actors/?search=${encodeURI(actorName)}`, { - headers: { - Instance: instanceToken, - }, - }); + const res = await session.get(`https://site-api.project1service.com/v1/actors/?search=${encodeURI(actorName)}`, { + headers: { + Instance: instanceToken, + }, + }); - if (res.statusCode === 200) { - const actorData = res.body.result.find(actor => actor.name.toLowerCase() === actorName.toLowerCase()); + if (res.statusCode === 200) { + const actorData = res.body.result.find(actor => actor.name.toLowerCase() === actorName.toLowerCase()); - if (actorData) { - const actorUrl = `https://www.${networkName}.com/${actorPath}/${actorData.id}/`; - const actorReleasesUrl = `https://site-api.project1service.com/v2/releases?actorId=${actorData.id}&limit=100&offset=0&orderBy=-dateReleased&type=scene`; + if (actorData) { + const actorUrl = `https://www.${networkName}.com/${actorPath}/${actorData.id}/`; + const actorReleasesUrl = `https://site-api.project1service.com/v2/releases?actorId=${actorData.id}&limit=100&offset=0&orderBy=-dateReleased&type=scene`; - const [actorRes, actorReleasesRes] = await Promise.all([ - bhttp.get(actorUrl), - session.get(actorReleasesUrl, { - headers: { - Instance: instanceToken, - }, - }), - ]); + const [actorRes, actorReleasesRes] = await Promise.all([ + bhttp.get(actorUrl), + session.get(actorReleasesUrl, { + headers: { + Instance: instanceToken, + }, + }), + ]); - if (actorRes.statusCode === 200 && actorReleasesRes.statusCode === 200 && actorReleasesRes.body.result) { - return scrapeProfile(actorData, actorRes.body.toString(), actorReleasesRes.body.result, networkName); - } + if (actorRes.statusCode === 200 && actorReleasesRes.statusCode === 200 && actorReleasesRes.body.result) { + return scrapeProfile(actorData, actorRes.body.toString(), actorReleasesRes.body.result, networkName); + } - if (actorRes.statusCode === 200) { - return scrapeProfile(actorData, actorRes.body.toString(), null, networkName); - } - } - } + if (actorRes.statusCode === 200) { + return scrapeProfile(actorData, actorRes.body.toString(), null, networkName); + } + } + } - return null; + return null; } module.exports = { - scrapeLatestX, - fetchLatest, - fetchScene, - fetchProfile, + scrapeLatestX, + fetchLatest, + fetchScene, + fetchProfile, }; diff --git a/src/scrapers/mofos.js b/src/scrapers/mofos.js index 274c6cda..83cfd1a7 100644 --- a/src/scrapers/mofos.js +++ b/src/scrapers/mofos.js @@ -3,11 +3,11 @@ const { fetchScene, fetchLatest, fetchProfile } = require('./mindgeek'); async function networkFetchProfile(actorName) { - return fetchProfile(actorName, 'mofos'); + return fetchProfile(actorName, 'mofos'); } module.exports = { - fetchLatest, - fetchScene, - fetchProfile: networkFetchProfile, + fetchLatest, + fetchScene, + fetchProfile: networkFetchProfile, }; diff --git a/src/scrapers/naughtyamerica.js b/src/scrapers/naughtyamerica.js index 95911c52..d16ab706 100644 --- a/src/scrapers/naughtyamerica.js +++ b/src/scrapers/naughtyamerica.js @@ -9,149 +9,149 @@ const slugify = require('../utils/slugify'); const { ex, get } = require('../utils/q'); function titleExtractor(pathname) { - const components = pathname.split('/')[2].split('-'); - const entryId = components.slice(-1)[0]; + const components = pathname.split('/')[2].split('-'); + const entryId = components.slice(-1)[0]; - const title = components.slice(0, -1).reduce((accTitle, word, index) => `${accTitle}${index > 0 ? ' ' : ''}${word.slice(0, 1).toUpperCase()}${word.slice(1)}`, ''); + const title = components.slice(0, -1).reduce((accTitle, word, index) => `${accTitle}${index > 0 ? ' ' : ''}${word.slice(0, 1).toUpperCase()}${word.slice(1)}`, ''); - return { title, entryId }; + return { title, entryId }; } function scrapeLatest(html, site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const sceneElements = $('.site-list .scene-item').toArray(); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const sceneElements = $('.site-list .scene-item').toArray(); - return sceneElements.map((item) => { - const element = $(item); + return sceneElements.map((item) => { + const element = $(item); - const sceneLinkElement = element.find('a').first(); - const { protocol, hostname, pathname } = new URL(sceneLinkElement.attr('href')); - const url = `${protocol}//${hostname}${pathname}`; - const { title, entryId } = titleExtractor(pathname); + const sceneLinkElement = element.find('a').first(); + const { protocol, hostname, pathname } = new URL(sceneLinkElement.attr('href')); + const url = `${protocol}//${hostname}${pathname}`; + const { title, entryId } = titleExtractor(pathname); - const date = moment.utc(element.find('.entry-date').text(), 'MMM D, YYYY').toDate(); - const actors = element.find('.contain-actors a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); + const date = moment.utc(element.find('.entry-date').text(), 'MMM D, YYYY').toDate(); + const actors = element.find('.contain-actors a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); - const duration = Number(element.find('.scene-runtime').text().slice(0, -4)) * 60; + const duration = Number(element.find('.scene-runtime').text().slice(0, -4)) * 60; - const posterString = sceneLinkElement.find('img[data-srcset]').attr('data-srcset') || sceneLinkElement.find('img[data-src]').attr('data-src'); - const poster = `https:${posterString.match(/[\w/.]+$/)[0]}`; + const posterString = sceneLinkElement.find('img[data-srcset]').attr('data-srcset') || sceneLinkElement.find('img[data-src]').attr('data-src'); + const poster = `https:${posterString.match(/[\w/.]+$/)[0]}`; - return { - url, - entryId, - title, - actors, - date, - duration, - poster, - rating: null, - site, - }; - }); + return { + url, + entryId, + title, + actors, + date, + duration, + poster, + rating: null, + site, + }; + }); } function scrapeScene(html, url, site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const sceneElement = $('.scene-info'); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const sceneElement = $('.scene-info'); - const { protocol, hostname, pathname } = new URL(url); - const originalUrl = `${protocol}//${hostname}${pathname}`; + const { protocol, hostname, pathname } = new URL(url); + const originalUrl = `${protocol}//${hostname}${pathname}`; - const entryId = originalUrl.split('-').slice(-1)[0]; - const title = sceneElement.find('h1.scene-title.grey-text').text(); - const description = sceneElement.find('.synopsis').contents().slice(2).text().replace(/[\s\n]+/g, ' ').trim(); + const entryId = originalUrl.split('-').slice(-1)[0]; + const title = sceneElement.find('h1.scene-title.grey-text').text(); + const description = sceneElement.find('.synopsis').contents().slice(2).text().replace(/[\s\n]+/g, ' ').trim(); - const date = moment.utc(sceneElement.find('span.entry-date').text(), 'MMM D, YYYY').toDate(); - const actors = $('a.scene-title.grey-text.link').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); + const date = moment.utc(sceneElement.find('span.entry-date').text(), 'MMM D, YYYY').toDate(); + const actors = $('a.scene-title.grey-text.link').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); - const duration = Number(sceneElement.find('.duration-ratings .duration').text().slice(10, -4)) * 60; + const duration = Number(sceneElement.find('.duration-ratings .duration').text().slice(10, -4)) * 60; - const poster = `https:${$('video, dl8-video').attr('poster')}`; - const photos = $('.contain-scene-images.desktop-only a').map((index, el) => `https:${$(el).attr('href')}`).toArray(); + const poster = `https:${$('video, dl8-video').attr('poster')}`; + const photos = $('.contain-scene-images.desktop-only a').map((index, el) => `https:${$(el).attr('href')}`).toArray(); - const trailerEl = $('source'); - const trailerSrc = trailerEl.attr('src'); - const trailerType = trailerEl.attr('type'); + const trailerEl = $('source'); + const trailerSrc = trailerEl.attr('src'); + const trailerType = trailerEl.attr('type'); - const siteName = sceneElement.find('a.site-title').text(); - const channel = siteName.replace(/[\s']+/g, '').toLowerCase(); + const siteName = sceneElement.find('a.site-title').text(); + const channel = siteName.replace(/[\s']+/g, '').toLowerCase(); - const tags = $('.categories a.cat-tag').map((tagIndex, tagElement) => $(tagElement).text()).toArray(); + const tags = $('.categories a.cat-tag').map((tagIndex, tagElement) => $(tagElement).text()).toArray(); - return { - url, - entryId, - title, - description, - actors, - date, - duration, - tags, - photos, - poster, - trailer: { - src: trailerSrc, - type: trailerType, - }, - rating: null, - site, - channel, - }; + return { + url, + entryId, + title, + description, + actors, + date, + duration, + tags, + photos, + poster, + trailer: { + src: trailerSrc, + type: trailerType, + }, + rating: null, + site, + channel, + }; } async function fetchActorReleases(url) { - const res = await get(url); + const res = await get(url); - return res.ok - ? res.item.qu.urls('.contain-block:not(.live-scenes) .scene-item > a:first-child') // live scenes repeat on all pages - : []; + return res.ok + ? res.item.qu.urls('.contain-block:not(.live-scenes) .scene-item > a:first-child') // live scenes repeat on all pages + : []; } async function scrapeProfile(html) { - const { qu } = ex(html); - const profile = {}; + const { qu } = ex(html); + const profile = {}; - profile.description = qu.q('.bio_about_text', true); + profile.description = qu.q('.bio_about_text', true); - const avatar = qu.q('img.performer-pic', 'src'); - if (avatar) profile.avatar = `https:${avatar}`; + const avatar = qu.q('img.performer-pic', 'src'); + if (avatar) profile.avatar = `https:${avatar}`; - const releases = qu.urls('.scene-item > a:first-child'); - const otherPages = qu.urls('.pagination a:not([rel=next]):not([rel=prev])'); - const olderReleases = await Promise.all(otherPages.map(async page => fetchActorReleases(page))); + const releases = qu.urls('.scene-item > a:first-child'); + const otherPages = qu.urls('.pagination a:not([rel=next]):not([rel=prev])'); + const olderReleases = await Promise.all(otherPages.map(async page => fetchActorReleases(page))); - profile.releases = releases.concat(olderReleases.flat()); + profile.releases = releases.concat(olderReleases.flat()); - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const res = await bhttp.get(`${site.url}?page=${page}`); + const res = await bhttp.get(`${site.url}?page=${page}`); - return scrapeLatest(res.body.toString(), site); + return scrapeLatest(res.body.toString(), site); } async function fetchScene(url, site) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - return scrapeScene(res.body.toString(), url, site); + return scrapeScene(res.body.toString(), url, site); } async function fetchProfile(actorName) { - const actorSlug = slugify(actorName); + const actorSlug = slugify(actorName); - const res = await bhttp.get(`https://www.naughtyamerica.com/pornstar/${actorSlug}`); + const res = await bhttp.get(`https://www.naughtyamerica.com/pornstar/${actorSlug}`); - if (res.statusCode === 200) { - return scrapeProfile(res.body.toString()); - } + if (res.statusCode === 200) { + return scrapeProfile(res.body.toString()); + } - return null; + return null; } module.exports = { - fetchLatest, - fetchScene, - fetchProfile, + fetchLatest, + fetchScene, + fetchProfile, }; diff --git a/src/scrapers/newsensations.js b/src/scrapers/newsensations.js index 6f2b9a7b..05ad3a72 100644 --- a/src/scrapers/newsensations.js +++ b/src/scrapers/newsensations.js @@ -3,75 +3,75 @@ const { geta, ed } = require('../utils/q'); function scrapeBlockLatest(scenes) { - return scenes.map(({ html, qu }) => { - const release = {}; + return scenes.map(({ html, qu }) => { + const release = {}; - const entryId = qu.q('div[class*="videothumb"]', 'class').match(/videothumb_(\d+)/) + const entryId = qu.q('div[class*="videothumb"]', 'class').match(/videothumb_(\d+)/) || qu.q('div[id*="videothumb"]', 'id').match(/videothumb_(\d+)/); - release.entryId = entryId[1]; + release.entryId = entryId[1]; - release.title = qu.q('h4 a', true); - release.url = qu.url('h4 a'); - release.date = ed(html, 'MM/DD/YYYY', /\d{2}\/\d{2}\/\d{4}/); + release.title = qu.q('h4 a', true); + release.url = qu.url('h4 a'); + release.date = ed(html, 'MM/DD/YYYY', /\d{2}\/\d{2}\/\d{4}/); - release.actors = qu.all('.tour_update_models a', true); + release.actors = qu.all('.tour_update_models a', true); - release.poster = qu.q('div img').dataset.src; - release.photos = [qu.q('div img', 'src0_4x') || qu.q('div img', 'src0_3x') || qu.q('div img', 'src0_2x')]; + release.poster = qu.q('div img').dataset.src; + release.photos = [qu.q('div img', 'src0_4x') || qu.q('div img', 'src0_3x') || qu.q('div img', 'src0_2x')]; - release.teaser = qu.video(); + release.teaser = qu.video(); - return release; - }); + return release; + }); } function scrapeClassicLatest(scenes) { - return scenes.map(({ el, qu }) => { - const release = {}; + return scenes.map(({ el, qu }) => { + const release = {}; - release.entryId = el.dataset.setid; - release.url = qu.url('a'); + release.entryId = el.dataset.setid; + release.url = qu.url('a'); - release.title = qu.q('.update_title_small', true) || qu.q('a:nth-child(2)', true); + release.title = qu.q('.update_title_small', true) || qu.q('a:nth-child(2)', true); - const description = qu.q('a', 'title'); - if (description) release.description = description; + const description = qu.q('a', 'title'); + if (description) release.description = description; - const date = qu.date('.date_small, .update_date', 'MM/DD/YYYY'); - if (date) release.date = date; + const date = qu.date('.date_small, .update_date', 'MM/DD/YYYY'); + if (date) release.date = date; - const durationLine = qu.q('.update_counts', true); - if (durationLine) release.duration = Number(durationLine.match(/(\d+) min/i)[1]) * 60; + const durationLine = qu.q('.update_counts', true); + if (durationLine) release.duration = Number(durationLine.match(/(\d+) min/i)[1]) * 60; - const actors = qu.all('.update_models a', true); - release.actors = actors.length > 0 ? actors : qu.q('.update_models', true).split(/,\s*/); + const actors = qu.all('.update_models a', true); + release.actors = actors.length > 0 ? actors : qu.q('.update_models', true).split(/,\s*/); - const photoCount = qu.q('.update_thumb', 'cnt'); - [release.poster, ...release.photos] = Array.from({ length: photoCount }) - .map((value, index) => qu.q('.update_thumb', `src${index}_3x`) + const photoCount = qu.q('.update_thumb', 'cnt'); + [release.poster, ...release.photos] = Array.from({ length: photoCount }) + .map((value, index) => qu.q('.update_thumb', `src${index}_3x`) || qu.q('.update_thumb', `src${index}_2x`) || qu.q('.update_thumb', `src${index}_1x`)); - return release; - }); + return release; + }); } async function fetchLatest(site, page = 1) { - if (!site.parameters) { - return null; - } + if (!site.parameters) { + return null; + } - const url = `${site.url}/tour_${site.parameters.siteId}/categories/movies_${page}_d.html`; - const res = await geta(url, '.updatesBlock .movieBlock, .updatesBlock .videoBlock, .latest_updates_block .update_details, .category_listing_block .update_details'); + const url = `${site.url}/tour_${site.parameters.siteId}/categories/movies_${page}_d.html`; + const res = await geta(url, '.updatesBlock .movieBlock, .updatesBlock .videoBlock, .latest_updates_block .update_details, .category_listing_block .update_details'); - if (res.ok && site.parameters.block) { - return scrapeBlockLatest(res.items, site); - } + if (res.ok && site.parameters.block) { + return scrapeBlockLatest(res.items, site); + } - return res.ok ? scrapeClassicLatest(res.items, site) : res.status; + return res.ok ? scrapeClassicLatest(res.items, site) : res.status; } module.exports = { - fetchLatest, + fetchLatest, }; diff --git a/src/scrapers/nubiles.js b/src/scrapers/nubiles.js index a2c4a77c..a084ef8f 100644 --- a/src/scrapers/nubiles.js +++ b/src/scrapers/nubiles.js @@ -5,161 +5,161 @@ const slugify = require('../utils/slugify'); const { heightToCm } = require('../utils/convert'); const slugUrlMap = { - nubiles: 'https://www.nubiles.net', - nubilesporn: 'https://www.nubiles-porn.com', + nubiles: 'https://www.nubiles.net', + nubilesporn: 'https://www.nubiles-porn.com', }; async function getPhotos(albumUrl) { - const res = await geta(albumUrl, '.photo-thumb'); + const res = await geta(albumUrl, '.photo-thumb'); - return res.ok - ? res.items.map(({ q }) => q('source').srcset) - : []; + return res.ok + ? res.items.map(({ q }) => q('source').srcset) + : []; } function scrapeAll(scenes, site, origin) { - return scenes.map(({ qu }) => { - const release = {}; + return scenes.map(({ qu }) => { + const release = {}; - release.title = qu.q('.title a', true); + release.title = qu.q('.title a', true); - const url = qu.url('.title a').split('?')[0]; - const channelUrl = qu.url('.site-link'); + const url = qu.url('.title a').split('?')[0]; + const channelUrl = qu.url('.site-link'); - if (/^http/.test(url)) { - const { pathname } = new URL(url); - release.entryId = pathname.split('/')[3]; + if (/^http/.test(url)) { + const { pathname } = new URL(url); + release.entryId = pathname.split('/')[3]; - if (channelUrl) release.url = `${channelUrl}${pathname}`; - else release.url = url; - } else if (!/\/join/.test(url)) { - release.entryId = url.split('/')[3]; + if (channelUrl) release.url = `${channelUrl}${pathname}`; + else release.url = url; + } else if (!/\/join/.test(url)) { + release.entryId = url.split('/')[3]; - if (channelUrl) release.url = `${channelUrl}${url}`; - else if (site?.url) release.url = `${site.url}${url}`; - else if (origin) release.url = `${origin}${url}`; - } else { - release.entryId = qu.q('a img', 'tube_tour_thumb_id'); - } + if (channelUrl) release.url = `${channelUrl}${url}`; + else if (site?.url) release.url = `${site.url}${url}`; + else if (origin) release.url = `${origin}${url}`; + } else { + release.entryId = qu.q('a img', 'tube_tour_thumb_id'); + } - release.date = qu.date('.date', 'MMM D, YYYY'); - release.actors = qu.all('.models a.model', true); + release.date = qu.date('.date', 'MMM D, YYYY'); + release.actors = qu.all('.models a.model', true); - const poster = qu.q('img').dataset.original; - release.poster = [ - poster.replace('_640', '_1280'), - poster, - ]; + const poster = qu.q('img').dataset.original; + release.poster = [ + poster.replace('_640', '_1280'), + poster, + ]; - release.stars = Number(qu.q('.rating', true)); - release.likes = Number(qu.q('.likes', true)); + release.stars = Number(qu.q('.rating', true)); + release.likes = Number(qu.q('.likes', true)); - return release; - }); + return release; + }); } async function scrapeScene({ qu }, url, site) { - const release = {}; + const release = {}; - const { origin, pathname } = new URL(url); - release.url = `${origin}${pathname}`; + const { origin, pathname } = new URL(url); + release.url = `${origin}${pathname}`; - release.entryId = new URL(url).pathname.split('/')[3]; - release.title = qu.q('.content-pane-title h2', true); - release.description = qu.q('.content-pane-column div', true); + release.entryId = new URL(url).pathname.split('/')[3]; + release.title = qu.q('.content-pane-title h2', true); + release.description = qu.q('.content-pane-column div', true); - release.date = qu.q('.date', 'MMM D, YYYY'); + release.date = qu.q('.date', 'MMM D, YYYY'); - release.actors = qu.all('.content-pane-performers .model', true); - release.tags = qu.all('.categories a', true); + release.actors = qu.all('.content-pane-performers .model', true); + release.tags = qu.all('.categories a', true); - release.poster = qu.poster() || qu.img('.fake-video-player img'); - release.trailer = qu.all('source').map(source => ({ - src: source.src, - quality: Number(source.getAttribute('res')), - })); + release.poster = qu.poster() || qu.img('.fake-video-player img'); + release.trailer = qu.all('source').map(source => ({ + src: source.src, + quality: Number(source.getAttribute('res')), + })); - release.stars = Number(qu.q('.score', true)); - release.likes = Number(qu.q('#likecount', true)); + release.stars = Number(qu.q('.score', true)); + release.likes = Number(qu.q('#likecount', true)); - const albumLink = qu.url('.content-pane-related-links a[href*="gallery"]'); - if (albumLink) release.photos = await getPhotos(`${site.url}${albumLink}`); + const albumLink = qu.url('.content-pane-related-links a[href*="gallery"]'); + if (albumLink) release.photos = await getPhotos(`${site.url}${albumLink}`); - return release; + return release; } function scrapeProfile({ qu }, _actorName, origin) { - const profile = {}; + const profile = {}; - const keys = qu.all('.model-profile h5', true); - const values = qu.all('.model-profile h5 + p', true); + const keys = qu.all('.model-profile h5', true); + const values = qu.all('.model-profile h5 + p', true); - const bio = keys.reduce((acc, key, index) => ({ ...acc, [slugify(key, '_')]: values[index] }), {}); + const bio = keys.reduce((acc, key, index) => ({ ...acc, [slugify(key, '_')]: values[index] }), {}); - profile.age = Number(bio.age); - profile.description = qu.q('.model-bio', true); + profile.age = Number(bio.age); + profile.description = qu.q('.model-bio', true); - profile.residencePlace = bio.location; + profile.residencePlace = bio.location; - profile.height = heightToCm(bio.height); - [profile.bust, profile.waist, profile.hip] = bio.figure.split('-').map(v => Number(v) || v); + profile.height = heightToCm(bio.height); + [profile.bust, profile.waist, profile.hip] = bio.figure.split('-').map(v => Number(v) || v); - profile.avatar = qu.img('.model-profile img'); + profile.avatar = qu.img('.model-profile img'); - const releases = qu.all('.content-grid-item').filter(el => /video\//.test(qu.url(el, '.img-wrapper a'))); // filter out photos - profile.releases = scrapeAll(ctxa(releases), null, origin); + const releases = qu.all('.content-grid-item').filter(el => /video\//.test(qu.url(el, '.img-wrapper a'))); // filter out photos + profile.releases = scrapeAll(ctxa(releases), null, origin); - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const url = `${site.url}/video/gallery/${(page - 1) * 12}`; - const res = await geta(url, '.content-grid-item'); + const url = `${site.url}/video/gallery/${(page - 1) * 12}`; + const res = await geta(url, '.content-grid-item'); - return res.ok ? scrapeAll(res.items, site) : res.status; + return res.ok ? scrapeAll(res.items, site) : res.status; } async function fetchUpcoming(site) { - if (site.parameters?.upcoming) { - const url = `${site.url}/video/upcoming`; - const res = await geta(url, '.content-grid-item'); + if (site.parameters?.upcoming) { + const url = `${site.url}/video/upcoming`; + const res = await geta(url, '.content-grid-item'); - return res.ok ? scrapeAll(res.items, site) : res.status; - } + return res.ok ? scrapeAll(res.items, site) : res.status; + } - return []; + return []; } async function fetchScene(url, site) { - const res = await get(url); + const res = await get(url); - return res.ok ? scrapeScene(res.item, url, site) : res.status; + return res.ok ? scrapeScene(res.item, url, site) : res.status; } async function fetchProfile(actorName, siteSlug) { - const firstLetter = actorName.charAt(0).toLowerCase(); - const origin = slugUrlMap[siteSlug] || `https://www.${siteSlug}.com`; + const firstLetter = actorName.charAt(0).toLowerCase(); + const origin = slugUrlMap[siteSlug] || `https://www.${siteSlug}.com`; - const url = `${origin}/model/alpha/${firstLetter}`; - const resModels = await get(url); + const url = `${origin}/model/alpha/${firstLetter}`; + const resModels = await get(url); - if (!resModels.ok) return resModels.status; + if (!resModels.ok) return resModels.status; - const modelPath = resModels.item.qu.all('.content-grid-item a.title').find(el => slugify(el.textContent) === slugify(actorName)); + const modelPath = resModels.item.qu.all('.content-grid-item a.title').find(el => slugify(el.textContent) === slugify(actorName)); - if (modelPath) { - const modelUrl = `${origin}${modelPath}`; - const resModel = await get(modelUrl); + if (modelPath) { + const modelUrl = `${origin}${modelPath}`; + const resModel = await get(modelUrl); - return resModel.ok ? scrapeProfile(resModel.item, actorName, origin) : resModel.status; - } + return resModel.ok ? scrapeProfile(resModel.item, actorName, origin) : resModel.status; + } - return null; + return null; } module.exports = { - fetchLatest, - fetchUpcoming, - fetchScene, - fetchProfile, + fetchLatest, + fetchUpcoming, + fetchScene, + fetchProfile, }; diff --git a/src/scrapers/perfectgonzo.js b/src/scrapers/perfectgonzo.js index b70cf824..7247416e 100644 --- a/src/scrapers/perfectgonzo.js +++ b/src/scrapers/perfectgonzo.js @@ -7,143 +7,143 @@ const knex = require('../knex'); const { ex, ctxa } = require('../utils/q'); async function getSiteSlugs() { - return knex('sites') - .pluck('sites.slug') - .join('networks', 'networks.id', 'sites.network_id') - .where('networks.slug', 'perfectgonzo'); + return knex('sites') + .pluck('sites.slug') + .join('networks', 'networks.id', 'sites.network_id') + .where('networks.slug', 'perfectgonzo'); } function getHash(identifier) { - const hash = blake2.createHash('blake2b', { digestLength: 8 }); + const hash = blake2.createHash('blake2b', { digestLength: 8 }); - hash.update(Buffer.from(identifier)); + hash.update(Buffer.from(identifier)); - return hash.digest('hex'); + return hash.digest('hex'); } function extractMaleModelsFromTags(tagContainer) { - if (!tagContainer) { - return []; - } + if (!tagContainer) { + return []; + } - const tagEls = Array.from(tagContainer.childNodes, node => ({ type: node.nodeType, text: node.textContent.trim() })).filter(node => node.text.length > 0); - const modelLabelIndex = tagEls.findIndex(node => node.text === 'Male Models'); + const tagEls = Array.from(tagContainer.childNodes, node => ({ type: node.nodeType, text: node.textContent.trim() })).filter(node => node.text.length > 0); + const modelLabelIndex = tagEls.findIndex(node => node.text === 'Male Models'); - if (modelLabelIndex > -1) { - const nextLabelIndex = tagEls.findIndex((node, index) => index > modelLabelIndex && node.type === 3); - const maleModels = tagEls.slice(modelLabelIndex + 1, nextLabelIndex); + if (modelLabelIndex > -1) { + const nextLabelIndex = tagEls.findIndex((node, index) => index > modelLabelIndex && node.type === 3); + const maleModels = tagEls.slice(modelLabelIndex + 1, nextLabelIndex); - return maleModels.map(model => model.text); - } + return maleModels.map(model => model.text); + } - return []; + return []; } async function extractChannelFromPhoto(photo, metaSiteSlugs) { - const siteSlugs = metaSiteSlugs || await getSiteSlugs(); - const channelMatch = photo.match(new RegExp(siteSlugs.join('|'))); + const siteSlugs = metaSiteSlugs || await getSiteSlugs(); + const channelMatch = photo.match(new RegExp(siteSlugs.join('|'))); - if (channelMatch) { - return channelMatch[0]; - } + if (channelMatch) { + return channelMatch[0]; + } - return null; + return null; } async function scrapeLatest(html, site) { - const siteSlugs = await getSiteSlugs(); - const { element } = ex(html); + const siteSlugs = await getSiteSlugs(); + const { element } = ex(html); - return ctxa(element, '#content-main .itemm').map(({ - q, qa, qlength, qdate, qimages, - }) => { - const release = { - site, - meta: { - siteSlugs, - }, - }; + return ctxa(element, '#content-main .itemm').map(({ + q, qa, qlength, qdate, qimages, + }) => { + const release = { + site, + meta: { + siteSlugs, + }, + }; - const sceneLink = q('a'); + const sceneLink = q('a'); - release.title = sceneLink.title; - release.url = `${site.url}${sceneLink.href}`; - release.date = qdate('.nm-date', 'MM/DD/YYYY'); + release.title = sceneLink.title; + release.url = `${site.url}${sceneLink.href}`; + release.date = qdate('.nm-date', 'MM/DD/YYYY'); - const slug = new URL(release.url).pathname.split('/')[2]; - release.entryId = getHash(`${site.slug}${slug}${release.date.toISOString()}`); + const slug = new URL(release.url).pathname.split('/')[2]; + release.entryId = getHash(`${site.slug}${slug}${release.date.toISOString()}`); - release.actors = release.title.split('&').map(actor => actor.trim()); + release.actors = release.title.split('&').map(actor => actor.trim()); - [release.poster, ...release.photos] = qimages('.bloc-link img'); + [release.poster, ...release.photos] = qimages('.bloc-link img'); - release.tags = qa('.dropdown ul a', true).slice(1); - release.duration = qlength('.dropdown p:first-child'); + release.tags = qa('.dropdown ul a', true).slice(1); + release.duration = qlength('.dropdown p:first-child'); - return release; - }); + return release; + }); } async function scrapeScene(html, site, url, metaSiteSlugs) { - const { - q, qa, qlength, qdate, qposter, qtrailer, - } = ex(html); + const { + q, qa, qlength, qdate, qposter, qtrailer, + } = ex(html); - const release = { url, site }; + const release = { url, site }; - release.title = q('#movie-header h2', true); - release.date = qdate('#movie-header div span', 'MMMM DD, YYYY', /\w+ \d{1,2}, \d{4}/); + release.title = q('#movie-header h2', true); + release.date = qdate('#movie-header div span', 'MMMM DD, YYYY', /\w+ \d{1,2}, \d{4}/); - release.description = q('.container .mg-md', true); - release.duration = qlength('#video-ribbon .container > div > span:nth-child(3)'); + release.description = q('.container .mg-md', true); + release.duration = qlength('#video-ribbon .container > div > span:nth-child(3)'); - release.actors = qa('#video-info a', true).concat(extractMaleModelsFromTags(q('.tag-container'))); - release.tags = qa('.tag-container a', true); + release.actors = qa('#video-info a', true).concat(extractMaleModelsFromTags(q('.tag-container'))); + release.tags = qa('.tag-container a', true); - const uhd = q('#video-ribbon .container > div > span:nth-child(2)', true); - if (/4K/.test(uhd)) release.tags = release.tags.concat('4k'); + const uhd = q('#video-ribbon .container > div > span:nth-child(2)', true); + if (/4K/.test(uhd)) release.tags = release.tags.concat('4k'); - release.photos = qa('.bxslider_pics img').map(el => el.dataset.original || el.src); - release.poster = qposter(); + release.photos = qa('.bxslider_pics img').map(el => el.dataset.original || el.src); + release.poster = qposter(); - const trailer = qtrailer(); - if (trailer) release.trailer = { src: trailer }; + const trailer = qtrailer(); + if (trailer) release.trailer = { src: trailer }; - if (release.photos.length > 0) release.channel = await extractChannelFromPhoto(release.photos[0], metaSiteSlugs); + if (release.photos.length > 0) release.channel = await extractChannelFromPhoto(release.photos[0], metaSiteSlugs); - if (release.channel) { - const { pathname } = new URL(url); - release.url = `https://${release.channel}.com${pathname}`; + if (release.channel) { + const { pathname } = new URL(url); + release.url = `https://${release.channel}.com${pathname}`; - const slug = pathname.split('/')[2]; - release.entryId = getHash(`${release.channel}${slug}${release.date.toISOString()}`); - } + const slug = pathname.split('/')[2]; + release.entryId = getHash(`${release.channel}${slug}${release.date.toISOString()}`); + } - return release; + return release; } async function fetchLatest(site, page = 1) { - const url = `${site.url}/movies/page-${page}`; - const res = await bhttp.get(url); + const url = `${site.url}/movies/page-${page}`; + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeLatest(res.body.toString(), site); - } + if (res.statusCode === 200) { + return scrapeLatest(res.body.toString(), site); + } - return []; + return []; } async function fetchScene(url, site, release) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeScene(res.body.toString(), site, url, release?.meta.siteSlugs); - } + if (res.statusCode === 200) { + return scrapeScene(res.body.toString(), site, url, release?.meta.siteSlugs); + } - return []; + return []; } module.exports = { - fetchLatest, - fetchScene, + fetchLatest, + fetchScene, }; diff --git a/src/scrapers/pervcity.js b/src/scrapers/pervcity.js index 17f34fb6..3500ff86 100644 --- a/src/scrapers/pervcity.js +++ b/src/scrapers/pervcity.js @@ -6,135 +6,135 @@ const { JSDOM } = require('jsdom'); const moment = require('moment'); async function getTrailer(entryId) { - const trailerRes = await bhttp.post('https://www.pervcity.com/gettoken.php', { - setId: entryId, - }); + const trailerRes = await bhttp.post('https://www.pervcity.com/gettoken.php', { + setId: entryId, + }); - if (trailerRes.statusCode === 200) { - return { - poster: trailerRes.body.TrailerImg, - trailer: trailerRes.body.TrailerPath || trailerRes.body.Trailerfallback, - }; - } + if (trailerRes.statusCode === 200) { + return { + poster: trailerRes.body.TrailerImg, + trailer: trailerRes.body.TrailerPath || trailerRes.body.Trailerfallback, + }; + } - return null; + return null; } function scrapeLatestScene(html, site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); + const $ = cheerio.load(html, { normalizeWhitespace: true }); - const entryId = $('li').attr('id'); - const sceneLinkElement = $('#scene_title_border a'); - const url = `${site.url}/${sceneLinkElement.attr('href')}`; - const title = sceneLinkElement.attr('title').replace(/\u00E2\u0080\u0099/g, '\''); // replace weird apostrophes + const entryId = $('li').attr('id'); + const sceneLinkElement = $('#scene_title_border a'); + const url = `${site.url}/${sceneLinkElement.attr('href')}`; + const title = sceneLinkElement.attr('title').replace(/\u00E2\u0080\u0099/g, '\''); // replace weird apostrophes - const actors = $('.home_model_name a').toArray().map(element => $(element).text().replace(/,[\u0020\u00A0\u202F]/, '')); // replace weird commas - const date = moment.utc($('.add_date').text(), 'DD-MM-YYYY').toDate(); + const actors = $('.home_model_name a').toArray().map(element => $(element).text().replace(/,[\u0020\u00A0\u202F]/, '')); // replace weird commas + const date = moment.utc($('.add_date').text(), 'DD-MM-YYYY').toDate(); - const poster = $('a:nth-child(2) > img').attr('src'); - const photos = $('.sample-picker img').map((index, element) => $(element).attr('src').replace('tourpics', 'trailer')).toArray(); + const poster = $('a:nth-child(2) > img').attr('src'); + const photos = $('.sample-picker img').map((index, element) => $(element).attr('src').replace('tourpics', 'trailer')).toArray(); - const stars = $('img[src*="/star.png"]') - .toArray() - .map(element => $(element).attr('src')) - .length || 0; + const stars = $('img[src*="/star.png"]') + .toArray() + .map(element => $(element).attr('src')) + .length || 0; - return { - url, - entryId, - title, - actors, - date, - poster, - photos, - rating: { - stars, - }, - site, - }; + return { + url, + entryId, + title, + actors, + date, + poster, + photos, + rating: { + stars, + }, + site, + }; } async function scrapeScene(html, url, site) { - const { document } = new JSDOM(html).window; + const { document } = new JSDOM(html).window; - const release = { url, site }; + const release = { url, site }; - release.entryId = document.querySelector('input#set_ID').value; + release.entryId = document.querySelector('input#set_ID').value; - release.title = document.querySelector('title').textContent; - release.description = document.querySelector('.player_data').textContent.trim(); + release.title = document.querySelector('title').textContent; + release.description = document.querySelector('.player_data').textContent.trim(); - const durationString = document.querySelector('.tag_lineR div:nth-child(2) span').textContent; - const [minutes, seconds] = durationString.match(/\d+/g); + const durationString = document.querySelector('.tag_lineR div:nth-child(2) span').textContent; + const [minutes, seconds] = durationString.match(/\d+/g); - release.duration = Number(minutes) * 60 + Number(seconds); - release.tags = document.querySelector('meta[name="keywords"]').content.split(','); + release.duration = Number(minutes) * 60 + Number(seconds); + release.tags = document.querySelector('meta[name="keywords"]').content.split(','); - const { poster, trailer } = await getTrailer(release.entryId); + const { poster, trailer } = await getTrailer(release.entryId); - release.poster = poster; - release.trailer = { src: trailer }; + release.poster = poster; + release.trailer = { src: trailer }; - return release; + return release; } function scrapeFallbackLanding(html) { - const { document } = new JSDOM(html).window; + const { document } = new JSDOM(html).window; - return document.querySelector('input#set_ID').value; + return document.querySelector('input#set_ID').value; } async function scrapeFallbackScene(html, entryId, url, site) { - const { document } = new JSDOM(html).window; - const release = { url, entryId, site }; + const { document } = new JSDOM(html).window; + const release = { url, entryId, site }; - release.title = document.querySelector('.popup_data_set_head label').textContent; - release.description = document.querySelector('.popup_data_set_des p').textContent.trim(); - release.date = moment.utc(document.querySelector('.popup_left_top div span').textContent, 'MM-DD-YYYY').toDate(); - release.actors = Array.from(document.querySelectorAll('.popup_data_set_models a'), el => el.textContent); + release.title = document.querySelector('.popup_data_set_head label').textContent; + release.description = document.querySelector('.popup_data_set_des p').textContent.trim(); + release.date = moment.utc(document.querySelector('.popup_left_top div span').textContent, 'MM-DD-YYYY').toDate(); + release.actors = Array.from(document.querySelectorAll('.popup_data_set_models a'), el => el.textContent); - const { poster, trailer } = await getTrailer(release.entryId); + const { poster, trailer } = await getTrailer(release.entryId); - release.poster = poster; - release.trailer = { src: trailer }; + release.poster = poster; + release.trailer = { src: trailer }; - release.channel = document.querySelector('.popup_left_top div img').alt; + release.channel = document.querySelector('.popup_left_top div img').alt; - return release; + return release; } async function fetchLatest(site, page = 1) { - const res = page === 1 - ? await bhttp.get(`${site.url}/final_latestupdateview.php?limitstart=${(page - 1) * 9}&limitend=9&websiteid=0&deviceview=browser&tourId=${site.parameters.tourId}`) - : await bhttp.get(`${site.url}/final_load_latestupdate_grid_view.php?limitstart=0&limitend=${(page - 1) * 8 + 1}&websiteid=0&deviceview=browser&tourId=${site.parameters.tourId}`); - const elements = JSON.parse(res.body.toString()); + const res = page === 1 + ? await bhttp.get(`${site.url}/final_latestupdateview.php?limitstart=${(page - 1) * 9}&limitend=9&websiteid=0&deviceview=browser&tourId=${site.parameters.tourId}`) + : await bhttp.get(`${site.url}/final_load_latestupdate_grid_view.php?limitstart=0&limitend=${(page - 1) * 8 + 1}&websiteid=0&deviceview=browser&tourId=${site.parameters.tourId}`); + const elements = JSON.parse(res.body.toString()); - const latest = Object.values(elements.total_arr).map(html => scrapeLatestScene(html, site)); // total_arr is a key-value object for final_load_latestupdate_grid_view.php + const latest = Object.values(elements.total_arr).map(html => scrapeLatestScene(html, site)); // total_arr is a key-value object for final_load_latestupdate_grid_view.php - return latest; + return latest; } async function fetchScene(url, site) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - if (res.statusCode === 200) { - if (site.isFallback) { - const entryId = scrapeFallbackLanding(res.body.toString(), url); + if (res.statusCode === 200) { + if (site.isNetwork) { + const entryId = scrapeFallbackLanding(res.body.toString(), url); - const fallbackRes = await bhttp.post('https://www.pervcity.com/set_popupvideo.php', { - setId: entryId, - }); + const fallbackRes = await bhttp.post('https://www.pervcity.com/set_popupvideo.php', { + setId: entryId, + }); - return scrapeFallbackScene(fallbackRes.body.toString(), entryId, url, site); - } + return scrapeFallbackScene(fallbackRes.body.toString(), entryId, url, site); + } - return scrapeScene(res.body.toString(), url, site); - } + return scrapeScene(res.body.toString(), url, site); + } - return null; + return null; } module.exports = { - fetchLatest, - fetchScene, + fetchLatest, + fetchScene, }; diff --git a/src/scrapers/pornhub.js b/src/scrapers/pornhub.js index 4ec7861f..a546fc98 100644 --- a/src/scrapers/pornhub.js +++ b/src/scrapers/pornhub.js @@ -5,56 +5,56 @@ const { JSDOM } = require('jsdom'); const moment = require('moment'); const ethnicityMap = { - White: 'Caucasian', + White: 'Caucasian', }; const hairMap = { - Brunette: 'brown', + Brunette: 'brown', }; async function scrapeProfile(html, _url, actorName) { - const { document } = new JSDOM(html).window; + const { document } = new JSDOM(html).window; - const entries = Array.from(document.querySelectorAll('.infoPiece'), el => el.textContent.replace(/\n|\t/g, '').split(':')); - const bio = entries.reduce((acc, [key, value]) => (key ? { ...acc, [key.trim()]: value.trim() } : acc), {}); + const entries = Array.from(document.querySelectorAll('.infoPiece'), el => el.textContent.replace(/\n|\t/g, '').split(':')); + const bio = entries.reduce((acc, [key, value]) => (key ? { ...acc, [key.trim()]: value.trim() } : acc), {}); - const profile = { - name: actorName, - }; + const profile = { + name: actorName, + }; - const descriptionString = document.querySelector('div[itemprop="description"]') || document.querySelector('.longBio'); - const avatarEl = document.querySelector('#getAvatar') || document.querySelector('.thumbImage img'); + const descriptionString = document.querySelector('div[itemprop="description"]') || document.querySelector('.longBio'); + const avatarEl = document.querySelector('#getAvatar') || document.querySelector('.thumbImage img'); - if (bio.Gender) profile.gender = bio.Gender.toLowerCase(); - if (bio.ethnicity) profile.ethnicity = ethnicityMap[bio.Ethnicity] || bio.Ethnicity; + if (bio.Gender) profile.gender = bio.Gender.toLowerCase(); + if (bio.ethnicity) profile.ethnicity = ethnicityMap[bio.Ethnicity] || bio.Ethnicity; - if (descriptionString) profile.description = descriptionString.textContent; + if (descriptionString) profile.description = descriptionString.textContent; - if (bio.Birthday && !/-0001/.test(bio.Birthday)) profile.birthdate = moment.utc(bio.Birthday, 'MMM D, YYYY').toDate(); // birthyear sometimes -0001, see Spencer Bradley as of january 2020 - if (bio.Born) profile.birthdate = moment.utc(bio.Born, 'YYYY-MM-DD').toDate(); + if (bio.Birthday && !/-0001/.test(bio.Birthday)) profile.birthdate = moment.utc(bio.Birthday, 'MMM D, YYYY').toDate(); // birthyear sometimes -0001, see Spencer Bradley as of january 2020 + if (bio.Born) profile.birthdate = moment.utc(bio.Born, 'YYYY-MM-DD').toDate(); - profile.birthPlace = bio['Birth Place'] || bio.Birthplace; - profile.residencePlace = bio['City and Country']; + profile.birthPlace = bio['Birth Place'] || bio.Birthplace; + profile.residencePlace = bio['City and Country']; - if (bio.Measurements && bio.Measurements !== '--') [profile.bust, profile.waist, profile.hip] = bio.Measurements.split('-'); - if (bio['Fake Boobs']) profile.naturalBoobs = bio['Fake Boobs'] === 'No'; + if (bio.Measurements && bio.Measurements !== '--') [profile.bust, profile.waist, profile.hip] = bio.Measurements.split('-'); + if (bio['Fake Boobs']) profile.naturalBoobs = bio['Fake Boobs'] === 'No'; - if (bio.Height) profile.height = Number(bio.Height.match(/\(\d+/)[0].slice(1)); - if (bio.Weight) profile.weight = Number(bio.Weight.match(/\(\d+/)[0].slice(1)); - if (bio['Hair Color']) profile.hair = hairMap[bio['Hair Color']] || bio['Hair Color'].toLowerCase(); - if (bio.Piercings) profile.hasPiercings = bio.Piercings === 'Yes'; - if (bio.Tattoos) profile.hasTattoos = bio.Tattoos === 'Yes'; + if (bio.Height) profile.height = Number(bio.Height.match(/\(\d+/)[0].slice(1)); + if (bio.Weight) profile.weight = Number(bio.Weight.match(/\(\d+/)[0].slice(1)); + if (bio['Hair Color']) profile.hair = hairMap[bio['Hair Color']] || bio['Hair Color'].toLowerCase(); + if (bio.Piercings) profile.hasPiercings = bio.Piercings === 'Yes'; + if (bio.Tattoos) profile.hasTattoos = bio.Tattoos === 'Yes'; - if (avatarEl && !/default\//.test(avatarEl.src)) profile.avatar = avatarEl.src; - profile.social = Array.from(document.querySelectorAll('.socialList a'), el => el.href).filter(link => link !== 'https://www.twitter.com/'); // PH links to Twitter itself for some reason + if (avatarEl && !/default\//.test(avatarEl.src)) profile.avatar = avatarEl.src; + profile.social = Array.from(document.querySelectorAll('.socialList a'), el => el.href).filter(link => link !== 'https://www.twitter.com/'); // PH links to Twitter itself for some reason - return profile; + return profile; } async function fetchProfile(actorName) { - const actorSlug = actorName.toLowerCase().replace(/\s+/g, '-'); + const actorSlug = actorName.toLowerCase().replace(/\s+/g, '-'); - /* Model pages are not reliably associated with actual porn stars + /* Model pages are not reliably associated with actual porn stars const modelUrl = `https://pornhub.com/model/${actorSlug}`; const pornstarUrl = `https://pornhub.com/pornstar/${actorSlug}`; @@ -74,12 +74,12 @@ async function fetchProfile(actorName) { } */ - const pornstarUrl = `https://pornhub.com/pornstar/${actorSlug}`; - const pornstarRes = await bhttp.get(pornstarUrl); + const pornstarUrl = `https://pornhub.com/pornstar/${actorSlug}`; + const pornstarRes = await bhttp.get(pornstarUrl); - return scrapeProfile(pornstarRes.body.toString(), pornstarUrl, actorName); + return scrapeProfile(pornstarRes.body.toString(), pornstarUrl, actorName); } module.exports = { - fetchProfile, + fetchProfile, }; diff --git a/src/scrapers/private.js b/src/scrapers/private.js index aa8e7bd0..b6944376 100644 --- a/src/scrapers/private.js +++ b/src/scrapers/private.js @@ -9,193 +9,193 @@ const { get, geta } = require('../utils/q'); const slugify = require('../utils/slugify'); async function getPhotos(entryId, site) { - const { hostname } = new URL(site.url); + const { hostname } = new URL(site.url); - const res = await bhttp.get(`https://${hostname}/gallery.php?type=highres&id=${entryId}`); - const html = res.body.toString(); + const res = await bhttp.get(`https://${hostname}/gallery.php?type=highres&id=${entryId}`); + const html = res.body.toString(); - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const photos = $('a.fakethumb').map((photoIndex, photoElement) => $(photoElement).attr('data-src') || $(photoElement).attr('href')).toArray(); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const photos = $('a.fakethumb').map((photoIndex, photoElement) => $(photoElement).attr('data-src') || $(photoElement).attr('href')).toArray(); - return photos; + return photos; } function scrapeLatest(html, site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const sceneElements = $('.content-wrapper .scene').toArray(); + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const sceneElements = $('.content-wrapper .scene').toArray(); - return sceneElements.map((element) => { - const sceneLinkElement = $(element).find('h3 a'); - const thumbnailElement = $(element).find('a img'); + return sceneElements.map((element) => { + const sceneLinkElement = $(element).find('h3 a'); + const thumbnailElement = $(element).find('a img'); - const url = sceneLinkElement.attr('href'); - // const title = sceneLinkElement.text(); - const entryId = url.split('/').slice(-1)[0]; + const url = sceneLinkElement.attr('href'); + // const title = sceneLinkElement.text(); + const entryId = url.split('/').slice(-1)[0]; - const titleText = thumbnailElement.attr('alt'); - const title = titleText.slice(titleText.indexOf(':') + 1).trim(); + const titleText = thumbnailElement.attr('alt'); + const title = titleText.slice(titleText.indexOf(':') + 1).trim(); - const date = moment.utc($(element).find('.scene-date'), ['MM/DD/YYYY', 'YYYY-MM-DD']).toDate(); + const date = moment.utc($(element).find('.scene-date'), ['MM/DD/YYYY', 'YYYY-MM-DD']).toDate(); - const actors = $(element).find('.scene-models a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); - const likes = Number($(element).find('.scene-votes').text()); + const actors = $(element).find('.scene-models a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); + const likes = Number($(element).find('.scene-votes').text()); - const photoCount = Number(thumbnailElement.attr('thumbs_num')); - const poster = thumbnailElement.attr('src'); - const photos = Array.from({ length: photoCount }, (val, index) => thumbnailElement.attr(`src${index + 1}`)); + const photoCount = Number(thumbnailElement.attr('thumbs_num')); + const poster = thumbnailElement.attr('src'); + const photos = Array.from({ length: photoCount }, (val, index) => thumbnailElement.attr(`src${index + 1}`)); - const scene = { - url, - entryId, - title, - actors, - date, - poster, - photos, - rating: { - likes, - }, - site, - }; + const scene = { + url, + entryId, + title, + actors, + date, + poster, + photos, + rating: { + likes, + }, + site, + }; - return scene; - }); + return scene; + }); } async function scrapeScene(html, url, site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); - const release = { url }; + const $ = cheerio.load(html, { normalizeWhitespace: true }); + const release = { url }; - [release.entryId] = url.split('/').slice(-1); - release.title = $('.video-wrapper meta[itemprop="name"]').attr('content'); - release.description = $('.video-wrapper meta[itemprop="description"]').attr('content'); + [release.entryId] = url.split('/').slice(-1); + release.title = $('.video-wrapper meta[itemprop="name"]').attr('content'); + release.description = $('.video-wrapper meta[itemprop="description"]').attr('content'); - release.date = moment.utc($('.video-wrapper meta[itemprop="uploadDate"]').attr('content'), 'MM/DD/YYYY').toDate(); - release.actors = $('.content-wrapper .scene-models-list a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); + release.date = moment.utc($('.video-wrapper meta[itemprop="uploadDate"]').attr('content'), 'MM/DD/YYYY').toDate(); + release.actors = $('.content-wrapper .scene-models-list a').map((actorIndex, actorElement) => $(actorElement).text()).toArray(); - const timestamp = $('.video-wrapper meta[itemprop="duration"]').attr('content'); + const timestamp = $('.video-wrapper meta[itemprop="duration"]').attr('content'); - if (timestamp) { - const [minutes, seconds] = timestamp.match(/\d+/g); - release.duration = Number(minutes) * 60 + Number(seconds); - } + if (timestamp) { + const [minutes, seconds] = timestamp.match(/\d+/g); + release.duration = Number(minutes) * 60 + Number(seconds); + } - release.tags = $('.content-desc .scene-tags a').map((tagIndex, tagElement) => $(tagElement).text()).toArray(); - release.likes = Number($('.content-desc #social-actions #likes').text()); + release.tags = $('.content-desc .scene-tags a').map((tagIndex, tagElement) => $(tagElement).text()).toArray(); + release.likes = Number($('.content-desc #social-actions #likes').text()); - const posterScript = $('script:contains(poster)').html(); - const posterLink = posterScript?.slice(posterScript.indexOf('https://'), posterScript.indexOf('.jpg') + 4); - release.poster = $('meta[property="og:image"]').attr('content') || posterLink || $('#trailer_player_finished img').attr('src'); + const posterScript = $('script:contains(poster)').html(); + const posterLink = posterScript?.slice(posterScript.indexOf('https://'), posterScript.indexOf('.jpg') + 4); + release.poster = $('meta[property="og:image"]').attr('content') || posterLink || $('#trailer_player_finished img').attr('src'); - const trailer = $('meta[property="og:video"]').attr('content') || $('#videojs-trailer source').attr('src'); + const trailer = $('meta[property="og:video"]').attr('content') || $('#videojs-trailer source').attr('src'); - if (trailer) release.trailer = { src: trailer }; + if (trailer) release.trailer = { src: trailer }; - release.photos = await getPhotos(release.entryId, site); - release.movie = $('a[data-track="FULL MOVIE"]').attr('href'); + release.photos = await getPhotos(release.entryId, site); + release.movie = $('a[data-track="FULL MOVIE"]').attr('href'); - const siteElement = $('.content-wrapper .logos-sites a'); - if (siteElement) release.channel = slugify(siteElement.text(), ''); + const siteElement = $('.content-wrapper .logos-sites a'); + if (siteElement) release.channel = slugify(siteElement.text(), ''); - return release; + return release; } function scrapeProfile({ html, q, qa, qtx }) { - const profile = {}; + const profile = {}; - const bio = qa('.model-facts li:not(.model-facts-long)', true).reduce((acc, fact) => { - const [key, value] = fact.split(':'); - const trimmedValue = value.trim(); + const bio = qa('.model-facts li:not(.model-facts-long)', true).reduce((acc, fact) => { + const [key, value] = fact.split(':'); + const trimmedValue = value.trim(); - if (trimmedValue.length === 0 || trimmedValue === '-') return acc; - return { ...acc, [slugify(key, '_')]: trimmedValue }; - }, {}); + if (trimmedValue.length === 0 || trimmedValue === '-') return acc; + return { ...acc, [slugify(key, '_')]: trimmedValue }; + }, {}); - const description = q('.model-facts-long', true); - if (description) profile.description = description; + const description = q('.model-facts-long', true); + if (description) profile.description = description; - const aliases = qtx('.aka')?.split(/,\s*/); - if (aliases) profile.aliases = aliases; + const aliases = qtx('.aka')?.split(/,\s*/); + if (aliases) profile.aliases = aliases; - if (bio.birth_place) profile.birthPlace = bio.birth_place; - if (bio.nationality) profile.nationality = bio.nationality; + if (bio.birth_place) profile.birthPlace = bio.birth_place; + if (bio.nationality) profile.nationality = bio.nationality; - if (bio.measurements) { - const [bust, waist, hip] = bio.measurements.split('-'); + if (bio.measurements) { + const [bust, waist, hip] = bio.measurements.split('-'); - if (bust) profile.bust = bust; - if (waist) profile.waist = Number(waist); - if (hip) profile.hip = Number(hip); - } + if (bust) profile.bust = bust; + if (waist) profile.waist = Number(waist); + if (hip) profile.hip = Number(hip); + } - if (bio.weight) profile.weight = Number(bio.weight.match(/^\d+/)[0]); - if (bio.height) profile.height = Number(bio.height.match(/^\d+/)[0]); + if (bio.weight) profile.weight = Number(bio.weight.match(/^\d+/)[0]); + if (bio.height) profile.height = Number(bio.height.match(/^\d+/)[0]); - if (bio.hair_color) profile.hair = bio.hair_color; - if (bio.eye_color) profile.eye = bio.eye_color; + if (bio.hair_color) profile.hair = bio.hair_color; + if (bio.eye_color) profile.eye = bio.eye_color; - if (bio.tattoos) { - profile.hasTattoos = true; - profile.tattoos = bio.tattoos; - } + if (bio.tattoos) { + profile.hasTattoos = true; + profile.tattoos = bio.tattoos; + } - if (bio.tattoos) { - profile.hasTattoos = true; - profile.tattoos = bio.tattoos; - } + if (bio.tattoos) { + profile.hasTattoos = true; + profile.tattoos = bio.tattoos; + } - if (bio.piercings) { - profile.hasPiercings = true; - profile.piercings = bio.piercings; - } + if (bio.piercings) { + profile.hasPiercings = true; + profile.piercings = bio.piercings; + } - profile.avatar = q('.img-pornstar img').dataset.src; - profile.releases = scrapeLatest(html); + profile.avatar = q('.img-pornstar img').dataset.src; + profile.releases = scrapeLatest(html); - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const { hostname } = new URL(site.url); + const { hostname } = new URL(site.url); - if (hostname.match('private.com')) { - const res = await bhttp.get(`${site.url}/${page}/`); + if (hostname.match('private.com')) { + const res = await bhttp.get(`${site.url}/${page}/`); - return scrapeLatest(res.body.toString(), site); - } + return scrapeLatest(res.body.toString(), site); + } - const res = await bhttp.get(`${site.url}/scenes/${page}/`); + const res = await bhttp.get(`${site.url}/scenes/${page}/`); - return scrapeLatest(res.body.toString(), site); + return scrapeLatest(res.body.toString(), site); } async function fetchScene(url, site) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - return scrapeScene(res.body.toString(), url, site); + return scrapeScene(res.body.toString(), url, site); } async function fetchProfile(actorName) { - const actorSearchSlug = slugify(actorName, '+'); - const url = `https://www.private.com/search.php?query=${actorSearchSlug}`; - const modelRes = await geta(url, '.model h3 a'); + const actorSearchSlug = slugify(actorName, '+'); + const url = `https://www.private.com/search.php?query=${actorSearchSlug}`; + const modelRes = await geta(url, '.model h3 a'); - if (modelRes.ok) { - const actorSlug = slugify(actorName); - const model = modelRes.items.find(({ text }) => slugify(text) === actorSlug); + if (modelRes.ok) { + const actorSlug = slugify(actorName); + const model = modelRes.items.find(({ text }) => slugify(text) === actorSlug); - if (model) { - const res = await get(model.el.href); + if (model) { + const res = await get(model.el.href); - return res.ok ? scrapeProfile(res.item) : res.status; - } - } + return res.ok ? scrapeProfile(res.item) : res.status; + } + } - return null; + return null; } module.exports = { - fetchLatest, - fetchScene, - fetchProfile, + fetchLatest, + fetchScene, + fetchProfile, }; diff --git a/src/scrapers/puretaboo.js b/src/scrapers/puretaboo.js index ede8da8d..9ae001f9 100644 --- a/src/scrapers/puretaboo.js +++ b/src/scrapers/puretaboo.js @@ -3,7 +3,7 @@ const { fetchApiLatest, fetchApiUpcoming, fetchScene } = require('./gamma'); module.exports = { - fetchLatest: fetchApiLatest, - fetchScene, - fetchUpcoming: fetchApiUpcoming, + fetchLatest: fetchApiLatest, + fetchScene, + fetchUpcoming: fetchApiUpcoming, }; diff --git a/src/scrapers/realitykings.js b/src/scrapers/realitykings.js index 0b98badc..fe40d895 100644 --- a/src/scrapers/realitykings.js +++ b/src/scrapers/realitykings.js @@ -4,49 +4,49 @@ const bhttp = require('bhttp'); const cheerio = require('cheerio'); const { - scrapeLatestX, - fetchLatest, - fetchScene, - fetchProfile, + scrapeLatestX, + fetchLatest, + fetchScene, + fetchProfile, } = require('./mindgeek'); function scrapeLatestClassic(html, site) { - const $ = cheerio.load(html, { normalizeWhitespace: true }); + const $ = cheerio.load(html, { normalizeWhitespace: true }); - const stateTag = $('script:contains("initialState")').html(); - const prefix = 'initialState = {'; - const prefixIndex = stateTag.indexOf('initialState = {'); - const suffix = '};'; - const stateString = stateTag.slice(prefixIndex + prefix.length - 1, stateTag.indexOf('};', prefixIndex) + suffix.length - 1); - const data = JSON.parse(stateString); + const stateTag = $('script:contains("initialState")').html(); + const prefix = 'initialState = {'; + const prefixIndex = stateTag.indexOf('initialState = {'); + const suffix = '};'; + const stateString = stateTag.slice(prefixIndex + prefix.length - 1, stateTag.indexOf('};', prefixIndex) + suffix.length - 1); + const data = JSON.parse(stateString); - return Object.values(data.entities.releases).map(scene => scrapeLatestX(scene, site)); + return Object.values(data.entities.releases).map(scene => scrapeLatestX(scene, site)); } async function fetchClassic(site, page) { - const res = await bhttp.get(`${site.url}/scenes?page=${page}`); + const res = await bhttp.get(`${site.url}/scenes?page=${page}`); - if (res.statusCode === 200) { - return scrapeLatestClassic(res.body.toString(), site); - } + if (res.statusCode === 200) { + return scrapeLatestClassic(res.body.toString(), site); + } - return null; + return null; } async function fetchLatestWrap(site, page = 1) { - if (site.parameters?.classic) { - return fetchClassic(site, page); - } + if (site.parameters?.classic) { + return fetchClassic(site, page); + } - return fetchLatest(site, page); + return fetchLatest(site, page); } async function networkFetchProfile(actorName) { - return fetchProfile(actorName, 'realitykings'); + return fetchProfile(actorName, 'realitykings'); } module.exports = { - fetchLatest: fetchLatestWrap, - fetchProfile: networkFetchProfile, - fetchScene, + fetchLatest: fetchLatestWrap, + fetchProfile: networkFetchProfile, + fetchScene, }; diff --git a/src/scrapers/score.js b/src/scrapers/score.js index 509254a3..79c85022 100644 --- a/src/scrapers/score.js +++ b/src/scrapers/score.js @@ -7,255 +7,255 @@ const slugify = require('../utils/slugify'); const { heightToCm, lbsToKg } = require('../utils/convert'); function scrapePhotos(html) { - const { qis } = ex(html, '#photos-page'); - const photos = qis('img'); + const { qis } = ex(html, '#photos-page'); + const photos = qis('img'); - return photos.map(photo => [ - photo - .replace('x_800', 'x_xl') - .replace('_tn', ''), - photo, - ]); + return photos.map(photo => [ + photo + .replace('x_800', 'x_xl') + .replace('_tn', ''), + photo, + ]); } async function fetchPhotos(url) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapePhotos(res.body.toString(), url); - } + if (res.statusCode === 200) { + return scrapePhotos(res.body.toString(), url); + } - return []; + return []; } function scrapeAll(html, site) { - return exa(html, '.container .video, .container-fluid .video').map(({ q, qa, qd, ql }) => { - const release = {}; + return exa(html, '.container .video, .container-fluid .video').map(({ q, qa, qd, ql }) => { + const release = {}; - release.title = q('.title, .i-title', true); + release.title = q('.title, .i-title', true); - const linkEl = q('a'); - const url = new URL(linkEl.href); - release.url = `${url.origin}${url.pathname}`; + const linkEl = q('a'); + const url = new URL(linkEl.href); + release.url = `${url.origin}${url.pathname}`; - // this is a photo album, not a scene (used for profiles) - if (/photos\//.test(url)) return null; + // this is a photo album, not a scene (used for profiles) + if (/photos\//.test(url)) return null; - [release.entryId] = url.pathname.split('/').slice(-2); + [release.entryId] = url.pathname.split('/').slice(-2); - release.date = qd('.i-date', 'MMM DD', /\w+ \d{1,2}$/) + release.date = qd('.i-date', 'MMM DD', /\w+ \d{1,2}$/) || qd('.dt-box', 'MMM.DD YYYY'); - release.actors = site?.parameters?.actors || qa('.model, .i-model', true); - release.duration = ql('.i-amount, .amount'); + release.actors = site?.parameters?.actors || qa('.model, .i-model', true); + release.duration = ql('.i-amount, .amount'); - const posterEl = q('.item-img img'); + const posterEl = q('.item-img img'); - if (posterEl) { - release.poster = `https:${posterEl.src}`; - } + if (posterEl) { + release.poster = `https:${posterEl.src}`; + } - if (posterEl?.dataset.gifPreview) { - release.teaser = { - src: `https:${posterEl.dataset.gifPreview}`, - }; - } + if (posterEl?.dataset.gifPreview) { + release.teaser = { + src: `https:${posterEl.dataset.gifPreview}`, + }; + } - return release; - }).filter(Boolean); + return release; + }).filter(Boolean); } async function scrapeScene(html, url, site) { - const { qu } = ex(html, '#videos-page, #content'); - const release = {}; + const { qu } = ex(html, '#videos-page, #content'); + const release = {}; - [release.entryId] = new URL(url).pathname.split('/').slice(-2); + [release.entryId] = new URL(url).pathname.split('/').slice(-2); - release.title = qu.q('h2.text-uppercase, h2.title, #breadcrumb-top + h1', true) + release.title = qu.q('h2.text-uppercase, h2.title, #breadcrumb-top + h1', true) || qu.q('h1.m-title', true)?.split(/»|\//).slice(-1)[0].trim(); - release.description = qu.text('.p-desc, .desc'); + release.description = qu.text('.p-desc, .desc'); - release.actors = qu.all('.value a[href*=models], .value a[href*=performer], .value a[href*=teen-babes]', true); + release.actors = qu.all('.value a[href*=models], .value a[href*=performer], .value a[href*=teen-babes]', true); - if (release.actors.length === 0) { - const actorEl = qu.all('.stat').find(stat => /Featuring/.test(stat.textContent)); - const actorString = qu.text(actorEl); + if (release.actors.length === 0) { + const actorEl = qu.all('.stat').find(stat => /Featuring/.test(stat.textContent)); + const actorString = qu.text(actorEl); - release.actors = actorString?.split(/,\band\b|,/g).map(actor => actor.trim()) || []; - } + release.actors = actorString?.split(/,\band\b|,/g).map(actor => actor.trim()) || []; + } - if (release.actors.length === 0 && site.parameters?.actors) release.actors = site.parameters.actors; + if (release.actors.length === 0 && site.parameters?.actors) release.actors = site.parameters.actors; - release.tags = qu.all('a[href*=tag]', true); + release.tags = qu.all('a[href*=tag]', true); - const dateEl = qu.all('.value').find(el => /\w+ \d+\w+, \d{4}/.test(el.textContent)); - release.date = qu.date(dateEl, null, 'MMMM Do, YYYY') + const dateEl = qu.all('.value').find(el => /\w+ \d+\w+, \d{4}/.test(el.textContent)); + release.date = qu.date(dateEl, null, 'MMMM Do, YYYY') || qu.date('.date', 'MMMM Do, YYYY', /\w+ \d{1,2}\w+, \d{4}/) || qu.date('.info .holder', 'MM/DD/YYYY', /\d{2}\/\d{2}\/\d{4}/); - const durationEl = qu.all('value').find(el => /\d{1,3}:\d{2}/.test(el.textContent)); - release.duration = qu.dur(durationEl); + const durationEl = qu.all('value').find(el => /\d{1,3}:\d{2}/.test(el.textContent)); + release.duration = qu.dur(durationEl); - release.poster = qu.poster('video') || qu.img('.flowplayer img') || qu.img('img'); // _800.jpg is larger than _xl.jpg in landscape - const photosUrl = qu.url('.stat a[href*=photos]'); + release.poster = qu.poster('video') || qu.img('.flowplayer img') || qu.img('img'); // _800.jpg is larger than _xl.jpg in landscape + const photosUrl = qu.url('.stat a[href*=photos]'); - if (photosUrl) { - release.photos = await fetchPhotos(photosUrl); - } else { - release.photos = qu.imgs('img[src*=ThumbNails], .p-photos .tn img').map(photo => [ - photo.replace('_tn', ''), - photo, - ]); - } + if (photosUrl) { + release.photos = await fetchPhotos(photosUrl); + } else { + release.photos = qu.imgs('img[src*=ThumbNails], .p-photos .tn img').map(photo => [ + photo.replace('_tn', ''), + photo, + ]); + } - const trailers = qu.all('a[href*=Trailers]'); + const trailers = qu.all('a[href*=Trailers]'); - if (trailers) { - release.trailer = trailers.map((trailer) => { - const src = `https:${trailer.href}`; - const format = trailer.textContent.trim().match(/^\w+/)[0].toLowerCase(); - const quality = parseInt(trailer.textContent.trim().match(/\d+([a-zA-Z]+)?$/)[0], 10); + if (trailers) { + release.trailer = trailers.map((trailer) => { + const src = `https:${trailer.href}`; + const format = trailer.textContent.trim().match(/^\w+/)[0].toLowerCase(); + const quality = parseInt(trailer.textContent.trim().match(/\d+([a-zA-Z]+)?$/)[0], 10); - return format === 'mp4' ? { src, quality } : null; - }).filter(Boolean); - } + return format === 'mp4' ? { src, quality } : null; + }).filter(Boolean); + } - const stars = qu.q('.rate-box').dataset.score; - if (stars) release.rating = { stars }; + const stars = qu.q('.rate-box').dataset.score; + if (stars) release.rating = { stars }; - return release; + return release; } function scrapeModels(html, actorName) { - const { qa } = ex(html); - const model = qa('.model a').find(link => link.title === actorName); + const { qa } = ex(html); + const model = qa('.model a').find(link => link.title === actorName); - return model?.href || null; + return model?.href || null; } async function fetchActorReleases(url, accReleases = []) { - const res = await get(url); + const res = await get(url); - if (res.ok) { - const releases = accReleases.concat(scrapeAll(res.item.document.body.outerHTML)); - const nextPage = res.item.qu.url('.next-pg'); + if (res.ok) { + const releases = accReleases.concat(scrapeAll(res.item.document.body.outerHTML)); + const nextPage = res.item.qu.url('.next-pg'); - if (nextPage && new URL(nextPage).searchParams.has('page')) { // last page has 'next' button linking to join page - return fetchActorReleases(nextPage, releases); - } + if (nextPage && new URL(nextPage).searchParams.has('page')) { // last page has 'next' button linking to join page + return fetchActorReleases(nextPage, releases); + } - return releases; - } + return releases; + } - return null; + return null; } async function scrapeProfile(html, actorUrl, withReleases) { - const { q, qa, qi } = ex(html, '#model-page'); - const profile = { gender: 'female' }; + const { q, qa, qi } = ex(html, '#model-page'); + const profile = { gender: 'female' }; - const bio = qa('.stat').reduce((acc, el) => { - const prop = q(el, '.label', true).slice(0, -1); - const key = slugify(prop, '_'); - const value = q(el, '.value', true); + const bio = qa('.stat').reduce((acc, el) => { + const prop = q(el, '.label', true).slice(0, -1); + const key = slugify(prop, '_'); + const value = q(el, '.value', true); - return { - ...acc, - [key]: value, - }; - }, {}); + return { + ...acc, + [key]: value, + }; + }, {}); - if (bio.location) profile.residencePlace = bio.location.replace('Czech Repulic', 'Czech Republic'); // see Laura Lion + if (bio.location) profile.residencePlace = bio.location.replace('Czech Repulic', 'Czech Republic'); // see Laura Lion - if (bio.birthday) { - const birthMonth = bio.birthday.match(/^\w+/)[0].toLowerCase(); - const [birthDay] = bio.birthday.match(/\d+/); + if (bio.birthday) { + const birthMonth = bio.birthday.match(/^\w+/)[0].toLowerCase(); + const [birthDay] = bio.birthday.match(/\d+/); - profile.birthday = [birthMonth, birthDay]; // currently unused, not to be confused with birthdate - } + profile.birthday = [birthMonth, birthDay]; // currently unused, not to be confused with birthdate + } - if (bio.ethnicity) profile.ethnicity = bio.ethnicity; - if (bio.hair_color) profile.hair = bio.hair_color; + if (bio.ethnicity) profile.ethnicity = bio.ethnicity; + if (bio.hair_color) profile.hair = bio.hair_color; - if (bio.height) profile.height = heightToCm(bio.height); - if (bio.weight) profile.weight = lbsToKg(bio.weight); + if (bio.height) profile.height = heightToCm(bio.height); + if (bio.weight) profile.weight = lbsToKg(bio.weight); - if (bio.bra_size) profile.bust = bio.bra_size; - if (bio.measurements) [, profile.waist, profile.hip] = bio.measurements.split('-'); + if (bio.bra_size) profile.bust = bio.bra_size; + if (bio.measurements) [, profile.waist, profile.hip] = bio.measurements.split('-'); - if (bio.occupation) profile.occupation = bio.occupation; + if (bio.occupation) profile.occupation = bio.occupation; - const avatar = qi('img'); - if (avatar) profile.avatar = avatar; + const avatar = qi('img'); + if (avatar) profile.avatar = avatar; - if (withReleases) { - const { origin, pathname } = new URL(actorUrl); - profile.releases = await fetchActorReleases(`${origin}${pathname}/scenes?page=1`); - } + if (withReleases) { + const { origin, pathname } = new URL(actorUrl); + profile.releases = await fetchActorReleases(`${origin}${pathname}/scenes?page=1`); + } - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const latestPath = site.parameters?.path || '/big-boob-videos'; - const url = `${site.url}${latestPath}?page=${page}`; - const res = await bhttp.get(url); + const latestPath = site.parameters?.path || '/big-boob-videos'; + const url = `${site.url}${latestPath}?page=${page}`; + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeAll(res.body.toString(), site); - } + if (res.statusCode === 200) { + return scrapeAll(res.body.toString(), site); + } - return res.statusCode; + return res.statusCode; } async function fetchScene(url, site) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeScene(res.body.toString(), url, site); - } + if (res.statusCode === 200) { + return scrapeScene(res.body.toString(), url, site); + } - return null; + return null; } async function fetchProfile(actorName, scraperSlug, site, include, page = 1, source = 0) { - const letter = actorName.charAt(0).toUpperCase(); + const letter = actorName.charAt(0).toUpperCase(); - const sources = [ - `https://www.scoreland.com/big-boob-models/browse/${letter}/?page=${page}`, - `https://www.50plusmilfs.com/xxx-milf-models/browse/${letter}/?page=${page}`, - ]; + const sources = [ + `https://www.scoreland.com/big-boob-models/browse/${letter}/?page=${page}`, + `https://www.50plusmilfs.com/xxx-milf-models/browse/${letter}/?page=${page}`, + ]; - const url = sources[source]; + const url = sources[source]; - const res = await bhttp.get(url, { - followRedirects: false, - }); + const res = await bhttp.get(url, { + followRedirects: false, + }); - if (res.statusCode === 200) { - const actorUrl = scrapeModels(res.body.toString(), actorName); + if (res.statusCode === 200) { + const actorUrl = scrapeModels(res.body.toString(), actorName); - if (actorUrl) { - const actorRes = await bhttp.get(actorUrl); + if (actorUrl) { + const actorRes = await bhttp.get(actorUrl); - if (actorRes.statusCode === 200) { - return scrapeProfile(actorRes.body.toString(), actorUrl, include.scenes); - } + if (actorRes.statusCode === 200) { + return scrapeProfile(actorRes.body.toString(), actorUrl, include.scenes); + } - return null; - } + return null; + } - return fetchProfile(actorName, scraperSlug, site, include, page + 1, source); - } + return fetchProfile(actorName, scraperSlug, site, include, page + 1, source); + } - if (sources[source + 1]) { - return fetchProfile(actorName, scraperSlug, site, include, 1, source + 1); - } + if (sources[source + 1]) { + return fetchProfile(actorName, scraperSlug, site, include, 1, source + 1); + } - return null; + return null; } module.exports = { - fetchLatest, - fetchScene, - fetchProfile, + fetchLatest, + fetchScene, + fetchProfile, }; diff --git a/src/scrapers/scrapers.js b/src/scrapers/scrapers.js index 2408826a..41f99972 100644 --- a/src/scrapers/scrapers.js +++ b/src/scrapers/scrapers.js @@ -65,143 +65,143 @@ const freeones = require('./freeones'); // const freeoneslegacy = require('./freeones_legacy'); module.exports = { - releases: { - '21naturals': naturals, - '21sextreme': sextreme, - '21sextury': sextury, - adulttime, - amateurallure, - assylum, - aziani, - babes, - bamvisions, - bang, - bangbros, - blowpass, - brazzers, - burningangel, - cherrypimps, - ddfnetwork, - digitalplayground, - dogfart, - dogfartnetwork: dogfart, - evilangel, - fakehub, - famedigital, - fantasymassage, - fullpornnetwork, - girlsway, - girlgirl: julesjordan, - hussiepass: hush, - hushpass: hush, - insex, - interracialpass: hush, - jayrock, - jesseloadsmonsterfacials, - julesjordan, - kellymadison, - kink, - legalporno, - men, - metrohd, - mikeadriano, - milehighmedia, - mindgeek, - mofos, - naughtyamerica, - newsensations, - nubiles, - perfectgonzo, - pervcity, - pimpxxx: cherrypimps, - pornpros: whalemember, - private: privateNetwork, - puretaboo, - realitykings, - score, - sexyhub: mindgeek, - swallowsalon: julesjordan, - teamskeet, - twistys, - vivid, - vixen, - vogov, - whalemember, - wicked, - xempire, - }, - actors: { - '21sextury': sextury, - analbbc: fullpornnetwork, - analized: fullpornnetwork, - analviolation: fullpornnetwork, - anilos: nubiles, - aziani, - babes, - baddaddypov: fullpornnetwork, - bamvisions, - bangbros, - blacked: vixen, - blackedraw: vixen, - blowpass, - boobpedia, - brattysis: nubiles, - brazzers, - burningangel, - cherrypimps, - ddfnetwork, - deeper: vixen, - deeplush: nubiles, - digitalplayground, - dtfsluts: fullpornnetwork, - evilangel, - eyeontheguy: hush, - fakehub, - famedigital, - freeones, - gangbangcreampie: aziani, - girlfaction: fullpornnetwork, - gloryholesecrets: aziani, - hergape: fullpornnetwork, - homemadeanalwhores: fullpornnetwork, - hotcrazymess: nubiles, - hushpass: hush, - hussiepass: hush, - iconmale, - interracialpass: hush, - interracialpovs: hush, - jamesdeen: fullpornnetwork, - julesjordan, - kellymadison, - legalporno, - men, - metrohd, - milehighmedia, - mofos, - mugfucked: fullpornnetwork, - naughtyamerica, - nfbusty: nubiles, - nubilefilms: nubiles, - nubiles, - nubilesporn: nubiles, - onlyprince: fullpornnetwork, - pervertgallery: fullpornnetwork, - pimpxxx: cherrypimps, - pornhub, - povperverts: fullpornnetwork, - povpornstars: hush, - private: privateNetwork, - realitykings, - score, - seehimfuck: hush, - sexyhub: mindgeek, - thatsitcomshow: nubiles, - transangels, - tushy: vixen, - tushyraw: vixen, - twistys, - vixen, - wicked, - xempire, - }, + releases: { + '21naturals': naturals, + '21sextreme': sextreme, + '21sextury': sextury, + adulttime, + amateurallure, + assylum, + aziani, + babes, + bamvisions, + bang, + bangbros, + blowpass, + brazzers, + burningangel, + cherrypimps, + ddfnetwork, + digitalplayground, + dogfart, + dogfartnetwork: dogfart, + evilangel, + fakehub, + famedigital, + fantasymassage, + fullpornnetwork, + girlsway, + girlgirl: julesjordan, + hussiepass: hush, + hushpass: hush, + insex, + interracialpass: hush, + jayrock, + jesseloadsmonsterfacials, + julesjordan, + kellymadison, + kink, + legalporno, + men, + metrohd, + mikeadriano, + milehighmedia, + mindgeek, + mofos, + naughtyamerica, + newsensations, + nubiles, + perfectgonzo, + pervcity, + pimpxxx: cherrypimps, + pornpros: whalemember, + private: privateNetwork, + puretaboo, + realitykings, + score, + sexyhub: mindgeek, + swallowsalon: julesjordan, + teamskeet, + twistys, + vivid, + vixen, + vogov, + whalemember, + wicked, + xempire, + }, + actors: { + '21sextury': sextury, + analbbc: fullpornnetwork, + analized: fullpornnetwork, + analviolation: fullpornnetwork, + anilos: nubiles, + aziani, + babes, + baddaddypov: fullpornnetwork, + bamvisions, + bangbros, + blacked: vixen, + blackedraw: vixen, + blowpass, + boobpedia, + brattysis: nubiles, + brazzers, + burningangel, + cherrypimps, + ddfnetwork, + deeper: vixen, + deeplush: nubiles, + digitalplayground, + dtfsluts: fullpornnetwork, + evilangel, + eyeontheguy: hush, + fakehub, + famedigital, + freeones, + gangbangcreampie: aziani, + girlfaction: fullpornnetwork, + gloryholesecrets: aziani, + hergape: fullpornnetwork, + homemadeanalwhores: fullpornnetwork, + hotcrazymess: nubiles, + hushpass: hush, + hussiepass: hush, + iconmale, + interracialpass: hush, + interracialpovs: hush, + jamesdeen: fullpornnetwork, + julesjordan, + kellymadison, + legalporno, + men, + metrohd, + milehighmedia, + mofos, + mugfucked: fullpornnetwork, + naughtyamerica, + nfbusty: nubiles, + nubilefilms: nubiles, + nubiles, + nubilesporn: nubiles, + onlyprince: fullpornnetwork, + pervertgallery: fullpornnetwork, + pimpxxx: cherrypimps, + pornhub, + povperverts: fullpornnetwork, + povpornstars: hush, + private: privateNetwork, + realitykings, + score, + seehimfuck: hush, + sexyhub: mindgeek, + thatsitcomshow: nubiles, + transangels, + tushy: vixen, + tushyraw: vixen, + twistys, + vixen, + wicked, + xempire, + }, }; diff --git a/src/scrapers/teamskeet.js b/src/scrapers/teamskeet.js index 6e17590c..3aefc7da 100644 --- a/src/scrapers/teamskeet.js +++ b/src/scrapers/teamskeet.js @@ -5,176 +5,176 @@ const { JSDOM } = require('jsdom'); const moment = require('moment'); function extractTitle(pathname) { - return pathname - .split('/') - .slice(-2)[0] - .split('_') - .map(seg => `${seg.charAt(0).toUpperCase()}${seg.slice(1)}`) - .join(' '); + return pathname + .split('/') + .slice(-2)[0] + .split('_') + .map(seg => `${seg.charAt(0).toUpperCase()}${seg.slice(1)}`) + .join(' '); } function extractActors(str) { - return str - .split(/,|\band\b/ig) - .filter(actor => !/\.{3}/.test(actor)) - .map(actor => actor.trim()) - .filter(actor => actor.length > 0); + return str + .split(/,|\band\b/ig) + .filter(actor => !/\.{3}/.test(actor)) + .map(actor => actor.trim()) + .filter(actor => actor.length > 0); } function scrapeLatest(html, site) { - const { document } = new JSDOM(html).window; + const { document } = new JSDOM(html).window; - const scenes = Array.from(document.querySelectorAll('#updatesList li.grey, #updatesList li.white')); + const scenes = Array.from(document.querySelectorAll('#updatesList li.grey, #updatesList li.white')); - return scenes.map((scene) => { - const release = { site }; + return scenes.map((scene) => { + const release = { site }; - const link = scene.querySelector('.info a'); - const poster = scene.querySelector('img'); - const { pathname } = new URL(link); + const link = scene.querySelector('.info a'); + const poster = scene.querySelector('img'); + const { pathname } = new URL(link); - [release.entryId] = poster.id.match(/\d+/); + [release.entryId] = poster.id.match(/\d+/); - release.url = `https://www.teamskeet.com${pathname}`; - release.title = extractTitle(pathname); + release.url = `https://www.teamskeet.com${pathname}`; + release.title = extractTitle(pathname); - release.date = moment.utc(scene.querySelector('strong').textContent, 'MM/DD/YYYY').toDate(); + release.date = moment.utc(scene.querySelector('strong').textContent, 'MM/DD/YYYY').toDate(); - const photos = Array.from({ length: 5 }, (_value, index) => poster.dataset.original.replace(/\d+.jpg/, `${String(index + 1).padStart(2, '0')}.jpg`)); - [release.poster] = photos; - release.photos = photos.slice(1); + const photos = Array.from({ length: 5 }, (_value, index) => poster.dataset.original.replace(/\d+.jpg/, `${String(index + 1).padStart(2, '0')}.jpg`)); + [release.poster] = photos; + release.photos = photos.slice(1); - const actors = scene.querySelector('div span[rel="test"]').textContent; - release.actors = extractActors(actors); + const actors = scene.querySelector('div span[rel="test"]').textContent; + release.actors = extractActors(actors); - return release; - }); + return release; + }); } function scrapeScene(html, site, url) { - const { document } = new JSDOM(html).window; - const release = { site }; + const { document } = new JSDOM(html).window; + const release = { site }; - release.entryId = document.querySelector('#story-and-tags .scene_rater').attributes.rel.value; - release.description = document.querySelector('#story-and-tags td:nth-child(2) div').textContent; - const [actors, title, channel] = document.querySelector('title').textContent.split('|').map(item => item.trim()); + release.entryId = document.querySelector('#story-and-tags .scene_rater').attributes.rel.value; + release.description = document.querySelector('#story-and-tags td:nth-child(2) div').textContent; + const [actors, title, channel] = document.querySelector('title').textContent.split('|').map(item => item.trim()); - release.url = url; - release.title = title; - release.actors = extractActors(actors); - release.channel = channel.toLowerCase(); - release.tags = Array.from(document.querySelectorAll('#story-and-tags tr:nth-child(2) a'), el => el.rel); + release.url = url; + release.title = title; + release.actors = extractActors(actors); + release.channel = channel.toLowerCase(); + release.tags = Array.from(document.querySelectorAll('#story-and-tags tr:nth-child(2) a'), el => el.rel); - const date = document.querySelector('h3 ~ div:nth-child(4), h3 ~ div div.gray:not(.scene_rater)').textContent.split(':')[1].trim(); - release.date = moment.utc(date, 'MMMM Do, YYYY').toDate(); + const date = document.querySelector('h3 ~ div:nth-child(4), h3 ~ div div.gray:not(.scene_rater)').textContent.split(':')[1].trim(); + release.date = moment.utc(date, 'MMMM Do, YYYY').toDate(); - const { poster } = document.querySelector('video'); - if (poster && !/gen/.test(poster)) release.poster = [poster.replace('low', 'hi'), poster]; + const { poster } = document.querySelector('video'); + if (poster && !/gen/.test(poster)) release.poster = [poster.replace('low', 'hi'), poster]; - const siteId = document.querySelector('#story-and-tags img').src.match(/\w+.jpg/)[0].replace('.jpg', ''); - const actorsSlug = document.querySelector('h3 a').href.split('/').slice(-2)[0]; + const siteId = document.querySelector('#story-and-tags img').src.match(/\w+.jpg/)[0].replace('.jpg', ''); + const actorsSlug = document.querySelector('h3 a').href.split('/').slice(-2)[0]; - release.photos = Array.from({ length: 5 }, (value, index) => `https://images.psmcdn.net/teamskeet/${siteId}/${actorsSlug}/shared/scenes/new/${String(index + 1).padStart(2, '0')}.jpg`); + release.photos = Array.from({ length: 5 }, (value, index) => `https://images.psmcdn.net/teamskeet/${siteId}/${actorsSlug}/shared/scenes/new/${String(index + 1).padStart(2, '0')}.jpg`); - const trailer = document.querySelector('div.right.gray a').href; - if (trailer) release.trailer = { src: trailer }; + const trailer = document.querySelector('div.right.gray a').href; + if (trailer) release.trailer = { src: trailer }; - return release; + return release; } function scrapeSceneA(html, site, sceneX, url) { - const scene = sceneX || new JSDOM(html).window.document; - const release = { site }; + const scene = sceneX || new JSDOM(html).window.document; + const release = { site }; - release.description = scene.querySelector('.scene-story').textContent.replace('...read more', '...').trim(); + release.description = scene.querySelector('.scene-story').textContent.replace('...read more', '...').trim(); - release.date = moment.utc(scene.querySelector('.scene-date').textContent, 'MM/DD/YYYY').toDate(); - release.actors = Array.from(scene.querySelectorAll('.starring span'), el => extractActors(el.textContent)).flat(); + release.date = moment.utc(scene.querySelector('.scene-date').textContent, 'MM/DD/YYYY').toDate(); + release.actors = Array.from(scene.querySelectorAll('.starring span'), el => extractActors(el.textContent)).flat(); - const durationString = scene.querySelector('.time').textContent.trim(); - const duration = ['00'].concat(durationString.split(':')).slice(-3).join(':'); // ensure hh:mm:ss - release.duration = moment.duration(duration).asSeconds(); + const durationString = scene.querySelector('.time').textContent.trim(); + const duration = ['00'].concat(durationString.split(':')).slice(-3).join(':'); // ensure hh:mm:ss + release.duration = moment.duration(duration).asSeconds(); - if (sceneX) { - const titleEl = scene.querySelector(':scope > a'); + if (sceneX) { + const titleEl = scene.querySelector(':scope > a'); - release.url = titleEl.href; - release.entryId = titleEl.id; - release.title = titleEl.title; + release.url = titleEl.href; + release.entryId = titleEl.id; + release.title = titleEl.title; - const [poster, ...photos] = Array.from(scene.querySelectorAll('.scene img'), el => el.src); - release.poster = [poster.replace('bio_big', 'video'), poster]; - release.photos = photos; - } + const [poster, ...photos] = Array.from(scene.querySelectorAll('.scene img'), el => el.src); + release.poster = [poster.replace('bio_big', 'video'), poster]; + release.photos = photos; + } - if (!sceneX) { - release.title = scene.querySelector('.title span').textContent; - release.url = url; + if (!sceneX) { + release.title = scene.querySelector('.title span').textContent; + release.url = url; - release.poster = scene.querySelector('video').poster; - release.photos = [release.poster.replace('video', 'bio_small'), release.poster.replace('video', 'bio_small2')]; - } + release.poster = scene.querySelector('video').poster; + release.photos = [release.poster.replace('video', 'bio_small'), release.poster.replace('video', 'bio_small2')]; + } - const [, entryIdA, entryIdB] = new URL(release.url).pathname.split('/'); - release.entryId = entryIdA === 'scenes' ? entryIdB : entryIdA; + const [, entryIdA, entryIdB] = new URL(release.url).pathname.split('/'); + release.entryId = entryIdA === 'scenes' ? entryIdB : entryIdA; - return release; + return release; } function scrapeLatestA(html, site) { - const { document } = new JSDOM(html).window; + const { document } = new JSDOM(html).window; - const scenes = Array.from(document.querySelectorAll('.scenewrapper')); + const scenes = Array.from(document.querySelectorAll('.scenewrapper')); - return scenes.map(scene => scrapeSceneA(null, site, scene)); + return scenes.map(scene => scrapeSceneA(null, site, scene)); } async function fetchLatestTeamSkeet(site, page = 1) { - const url = `https://www.teamskeet.com/t1/updates/load?fltrs[site]=${site.parameters.id}&page=${page}&view=newest&fltrs[time]=ALL&order=DESC`; - const res = await bhttp.get(url); + const url = `https://www.teamskeet.com/t1/updates/load?fltrs[site]=${site.parameters.id}&page=${page}&view=newest&fltrs[time]=ALL&order=DESC`; + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeLatest(res.body.toString(), site); - } + if (res.statusCode === 200) { + return scrapeLatest(res.body.toString(), site); + } - return null; + return null; } async function fetchLatestA(site) { - const url = `${site.url}/scenes`; - const res = await bhttp.get(url); + const url = `${site.url}/scenes`; + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeLatestA(res.body.toString(), site); - } + if (res.statusCode === 200) { + return scrapeLatestA(res.body.toString(), site); + } - return null; + return null; } async function fetchLatest(site, page = 1) { - if (site.parameters.id) { - return fetchLatestTeamSkeet(site, page); - } + if (site.parameters.id) { + return fetchLatestTeamSkeet(site, page); + } - if (site.parameters.scraper === 'A') { - return fetchLatestA(site, page); - } + if (site.parameters.scraper === 'A') { + return fetchLatestA(site, page); + } - return null; + return null; } async function fetchScene(url, site) { - const session = bhttp.session(); // resolve redirects - const res = await session.get(url); + const session = bhttp.session(); // resolve redirects + const res = await session.get(url); - if (site.parameters?.scraper === 'A') { - return scrapeSceneA(res.body.toString(), site, null, url); - } + if (site.parameters?.scraper === 'A') { + return scrapeSceneA(res.body.toString(), site, null, url); + } - return scrapeScene(res.body.toString(), site, url); + return scrapeScene(res.body.toString(), site, url); } module.exports = { - fetchLatest, - fetchScene, + fetchLatest, + fetchScene, }; diff --git a/src/scrapers/transangels.js b/src/scrapers/transangels.js index 600d4ced..560dfccd 100644 --- a/src/scrapers/transangels.js +++ b/src/scrapers/transangels.js @@ -3,9 +3,9 @@ const { fetchProfile } = require('./mindgeek'); async function networkFetchProfile(actorName) { - return fetchProfile(actorName, 'transangels'); + return fetchProfile(actorName, 'transangels'); } module.exports = { - fetchProfile: networkFetchProfile, + fetchProfile: networkFetchProfile, }; diff --git a/src/scrapers/twistys.js b/src/scrapers/twistys.js index 2207736a..e753ac8c 100644 --- a/src/scrapers/twistys.js +++ b/src/scrapers/twistys.js @@ -3,11 +3,11 @@ const { fetchScene, fetchLatest, fetchProfile } = require('./mindgeek'); async function networkFetchProfile(actorName) { - return fetchProfile(actorName, 'twistys'); + return fetchProfile(actorName, 'twistys'); } module.exports = { - fetchLatest, - fetchProfile: networkFetchProfile, - fetchScene, + fetchLatest, + fetchProfile: networkFetchProfile, + fetchScene, }; diff --git a/src/scrapers/vivid.js b/src/scrapers/vivid.js index 45086283..275cc59b 100644 --- a/src/scrapers/vivid.js +++ b/src/scrapers/vivid.js @@ -8,128 +8,128 @@ const { fetchApiLatest, fetchApiUpcoming, fetchScene, fetchApiProfile } = requir const slugify = require('../utils/slugify'); function scrapeLatestNative(scenes, site) { - return scenes.map((scene) => { - const release = {}; + return scenes.map((scene) => { + const release = {}; - release.entryId = scene.id; - release.url = `${site.url}${scene.url}`; + release.entryId = scene.id; + release.url = `${site.url}${scene.url}`; - release.title = scene.name; - release.date = ed(scene.release_date, 'YYYY-MM-DD'); - release.duration = parseInt(scene.runtime, 10) * 60; + release.title = scene.name; + release.date = ed(scene.release_date, 'YYYY-MM-DD'); + release.duration = parseInt(scene.runtime, 10) * 60; - release.actors = scene.cast?.map(actor => ({ - name: actor.stagename, - gender: actor.gender.toLowerCase(), - avatar: actor.placard, - })) || []; + release.actors = scene.cast?.map(actor => ({ + name: actor.stagename, + gender: actor.gender.toLowerCase(), + avatar: actor.placard, + })) || []; - release.stars = Number(scene.rating); - release.poster = scene.placard_800 || scene.placard; + release.stars = Number(scene.rating); + release.poster = scene.placard_800 || scene.placard; - return release; - }); + return release; + }); } function scrapeSceneNative({ html, q, qa }, url, _site) { - const release = { url }; + const release = { url }; - release.entryId = new URL(url).pathname.split('/')[2]; // eslint-disable-line prefer-destructuring + release.entryId = new URL(url).pathname.split('/')[2]; // eslint-disable-line prefer-destructuring - release.title = q('.scene-h2-heading', true); - release.description = q('.indie-model-p', true); + release.title = q('.scene-h2-heading', true); + release.description = q('.indie-model-p', true); - const dateString = qa('h5').find(el => /Released/.test(el.textContent)).textContent; - release.date = ed(dateString, 'MMM DD, YYYY', /\w+ \d{1,2}, \d{4}/); + const dateString = qa('h5').find(el => /Released/.test(el.textContent)).textContent; + release.date = ed(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); + const duration = qa('h5').find(el => /Runtime/.test(el.textContent)).textContent; + const [hours, minutes] = duration.match(/\d+/g); - if (minutes) release.duration = (hours * 3600) + (minutes * 60); - else release.duration = hours * 60; // scene shorter that 1hr, hour match are minutes + if (minutes) release.duration = (hours * 3600) + (minutes * 60); + else release.duration = hours * 60; // scene shorter that 1hr, hour match are minutes - release.actors = qa('h4 a[href*="/stars"], h4 a[href*="/celebs"]', true); - release.tags = qa('h5 a[href*="/categories"]', true); + release.actors = qa('h4 a[href*="/stars"], h4 a[href*="/celebs"]', true); + release.tags = qa('h5 a[href*="/categories"]', true); - const [poster, trailer] = html.match(/https:\/\/content.vivid.com(.*)(.jpg|.mp4)/g); - release.poster = poster; + const [poster, trailer] = html.match(/https:\/\/content.vivid.com(.*)(.jpg|.mp4)/g); + release.poster = poster; - if (trailer) { - release.trailer = { - src: trailer, - }; - } + if (trailer) { + release.trailer = { + src: trailer, + }; + } - const channel = q('h5 a[href*="/sites"]', true); - if (channel) release.channel = channel.replace(/\.\w+/, ''); + const channel = q('h5 a[href*="/sites"]', true); + if (channel) release.channel = channel.replace(/\.\w+/, ''); - return release; + return release; } async function fetchLatestNative(site, page = 1) { - if (site.parameters?.useGamma) { - return fetchApiLatest(site, page); - } + if (site.parameters?.useGamma) { + return fetchApiLatest(site, page); + } - const apiUrl = `${site.url}/videos/api/?limit=50&offset=${(page - 1) * 50}&sort=datedesc`; - const res = await bhttp.get(apiUrl, { - decodeJSON: true, - }); + const apiUrl = `${site.url}/videos/api/?limit=50&offset=${(page - 1) * 50}&sort=datedesc`; + const res = await bhttp.get(apiUrl, { + decodeJSON: true, + }); - if (res.statusCode === 200 && res.body.code === 200) { - return scrapeLatestNative(res.body.responseData, site); - } + if (res.statusCode === 200 && res.body.code === 200) { + return scrapeLatestNative(res.body.responseData, site); + } - return null; + return null; } async function fetchUpcomingNative(site) { - if (site.parameters?.useGamma) { - return fetchApiUpcoming(site); - } + if (site.parameters?.useGamma) { + return fetchApiUpcoming(site); + } - return null; + return null; } async function fetchSceneNative(url, site, release) { - if (site.parameters?.useGamma) { - return fetchScene(url, site, release); - } + if (site.parameters?.useGamma) { + return fetchScene(url, site, release); + } - const res = await get(url); + const res = await get(url); - return res.ok ? scrapeSceneNative(res.item, url, site) : res.status; + return res.ok ? scrapeSceneNative(res.item, url, site) : res.status; } async function fetchSceneWrapper(url, site, release) { - const scene = await fetchScene(url, site, release); + const scene = await fetchScene(url, site, release); - if (scene.date - new Date(site.parameters?.lastNative) <= 0) { - // scene is probably still available on Vivid site, use search API to get URL and original date - const searchUrl = `${site.url}/videos/api/?limit=10&sort=datedesc&search=${encodeURI(scene.title)}`; - const searchRes = await bhttp.get(searchUrl, { - decodeJSON: true, - }); + if (scene.date - new Date(site.parameters?.lastNative) <= 0) { + // scene is probably still available on Vivid site, use search API to get URL and original date + const searchUrl = `${site.url}/videos/api/?limit=10&sort=datedesc&search=${encodeURI(scene.title)}`; + const searchRes = await bhttp.get(searchUrl, { + decodeJSON: true, + }); - if (searchRes.statusCode === 200 && searchRes.body.code === 200) { - const sceneMatch = searchRes.body.responseData.find(item => slugify(item.name) === slugify(scene.title)); + if (searchRes.statusCode === 200 && searchRes.body.code === 200) { + const sceneMatch = searchRes.body.responseData.find(item => slugify(item.name) === slugify(scene.title)); - if (sceneMatch) { - return { - ...scene, - url: `${site.url}${sceneMatch.url}`, - date: ed(sceneMatch.release_date, 'YYYY-MM-DD'), - }; - } - } - } + if (sceneMatch) { + return { + ...scene, + url: `${site.url}${sceneMatch.url}`, + date: ed(sceneMatch.release_date, 'YYYY-MM-DD'), + }; + } + } + } - return scene; + return scene; } module.exports = { - fetchLatest: fetchApiLatest, - fetchProfile: fetchApiProfile, - fetchUpcoming: fetchApiUpcoming, - fetchScene: fetchSceneWrapper, + fetchLatest: fetchApiLatest, + fetchProfile: fetchApiProfile, + fetchUpcoming: fetchApiUpcoming, + fetchScene: fetchSceneWrapper, }; diff --git a/src/scrapers/vixen.js b/src/scrapers/vixen.js index ddbd8bc1..cef49b38 100644 --- a/src/scrapers/vixen.js +++ b/src/scrapers/vixen.js @@ -8,246 +8,246 @@ const { get, post } = require('../utils/http'); const slugify = require('../utils/slugify'); const genderMap = { - F: 'female', - M: 'male', - T: 'transsexual', // not yet observed + F: 'female', + M: 'male', + T: 'transsexual', // not yet observed }; function getPosterFallbacks(poster) { - return poster - .filter(image => /landscape/i.test(image.name)) - .sort((imageA, imageB) => imageB.height - imageA.height) - .map((image) => { - const sources = [image.src, image.highdpi?.['2x'], image.highdpi?.['3x']]; - // high DPI images for full HD source are huge, only prefer for smaller fallback sources - return image.height === 1080 ? sources : sources.reverse(); - }) - .flat(); + return poster + .filter(image => /landscape/i.test(image.name)) + .sort((imageA, imageB) => imageB.height - imageA.height) + .map((image) => { + const sources = [image.src, image.highdpi?.['2x'], image.highdpi?.['3x']]; + // high DPI images for full HD source are huge, only prefer for smaller fallback sources + return image.height === 1080 ? sources : sources.reverse(); + }) + .flat(); } function getTeaserFallbacks(teaser) { - return teaser - .filter(video => /landscape/i.test(video.name)) - .map(video => ({ - src: video.src, - type: video.type, - quality: Number(String(video.height).replace('353', '360')), - })); + return teaser + .filter(video => /landscape/i.test(video.name)) + .map(video => ({ + src: video.src, + type: video.type, + quality: Number(String(video.height).replace('353', '360')), + })); } function getAvatarFallbacks(avatar) { - return avatar - .sort((imageA, imageB) => imageB.height - imageA.height) - .map(image => [image.highdpi?.['3x'], image.highdpi?.['2x'], image.src]) - .flat(); + return avatar + .sort((imageA, imageB) => imageB.height - imageA.height) + .map(image => [image.highdpi?.['3x'], image.highdpi?.['2x'], image.src]) + .flat(); } async function getTrailer(scene, site, url) { - const qualities = [360, 480, 720, 1080, 2160]; + const qualities = [360, 480, 720, 1080, 2160]; - const tokenRes = await post(`${site.url}/api/__record_tknreq`, { - file: scene.previewVideoUrl1080P, - sizes: qualities.join('+'), - type: 'trailer', - }, { referer: url }); + const tokenRes = await post(`${site.url}/api/__record_tknreq`, { + file: scene.previewVideoUrl1080P, + sizes: qualities.join('+'), + type: 'trailer', + }, { referer: url }); - if (!tokenRes.ok) { - return null; - } + if (!tokenRes.ok) { + return null; + } - const trailerUrl = `${site.url}/api${tokenRes.body.data.url}`; - const trailersRes = await post(trailerUrl, null, { referer: url }); + const trailerUrl = `${site.url}/api${tokenRes.body.data.url}`; + const trailersRes = await post(trailerUrl, null, { referer: url }); - if (trailersRes.ok) { - return qualities.map(quality => (trailersRes.body[quality] ? { - src: trailersRes.body[quality].token, - quality, - } : null)).filter(Boolean); - } + if (trailersRes.ok) { + return qualities.map(quality => (trailersRes.body[quality] ? { + src: trailersRes.body[quality].token, + quality, + } : null)).filter(Boolean); + } - return null; + return null; } function scrapeAll(scenes, site, origin) { - return scenes.map((scene) => { - const release = {}; + return scenes.map((scene) => { + const release = {}; - release.title = scene.title; + release.title = scene.title; - release.entryId = String(scene.newId); - release.url = `${site?.url || origin}${scene.targetUrl}`; + release.entryId = String(scene.newId); + release.url = `${site?.url || origin}${scene.targetUrl}`; - release.date = moment.utc(scene.releaseDate).toDate(); - release.shootDate = moment.utc(scene.shootDate).toDate(); + release.date = moment.utc(scene.releaseDate).toDate(); + release.shootDate = moment.utc(scene.shootDate).toDate(); - release.actors = scene.models; - release.stars = Number(scene.textRating) / 2; + release.actors = scene.models; + release.stars = Number(scene.textRating) / 2; - release.poster = getPosterFallbacks(scene.images.poster); - release.teaser = getTeaserFallbacks(scene.previews.poster); + release.poster = getPosterFallbacks(scene.images.poster); + release.teaser = getTeaserFallbacks(scene.previews.poster); - return release; - }); + return release; + }); } function scrapeUpcoming(scene, site) { - if (!scene || scene.isPreReleasePeriod) return null; + if (!scene || scene.isPreReleasePeriod) return null; - const release = {}; + const release = {}; - release.title = scene.targetUrl - .slice(1) - .split('-') - .map(component => `${component.charAt(0).toUpperCase()}${component.slice(1)}`) - .join(' '); + release.title = scene.targetUrl + .slice(1) + .split('-') + .map(component => `${component.charAt(0).toUpperCase()}${component.slice(1)}`) + .join(' '); - release.url = `${site.url}${scene.targetUrl}`; + release.url = `${site.url}${scene.targetUrl}`; - release.date = moment.utc(scene.releaseDate).toDate(); - release.shootDate = moment.utc(scene.shootDate).toDate(); + release.date = moment.utc(scene.releaseDate).toDate(); + release.shootDate = moment.utc(scene.shootDate).toDate(); - release.actors = scene.models; + release.actors = scene.models; - release.poster = getPosterFallbacks(scene.images.poster); - release.teaser = getTeaserFallbacks(scene.previews.poster); + release.poster = getPosterFallbacks(scene.images.poster); + release.teaser = getTeaserFallbacks(scene.previews.poster); - release.entryId = (release.poster[0] || release.teaser[0])?.match(/\/(\d+)/)?.[1]; + release.entryId = (release.poster[0] || release.teaser[0])?.match(/\/(\d+)/)?.[1]; - return [release]; + return [release]; } async function scrapeScene(data, url, site, baseRelease) { - const scene = data.video; + const scene = data.video; - const release = { - url, - title: scene.title, - description: scene.description, - actors: scene.models, - director: scene.directorNames, - duration: scene.runLength, - stars: scene.totalRateVal, - tags: scene.tags, - }; + const release = { + url, + title: scene.title, + description: scene.description, + actors: scene.models, + director: scene.directorNames, + duration: scene.runLength, + stars: scene.totalRateVal, + tags: scene.tags, + }; - release.entryId = scene.newId; + release.entryId = scene.newId; - release.date = moment.utc(scene.releaseDate).toDate(); - release.shootDate = moment.utc(scene.shootDate).toDate(); + release.date = moment.utc(scene.releaseDate).toDate(); + release.shootDate = moment.utc(scene.shootDate).toDate(); - release.actors = baseRelease?.actors || scene.models; + release.actors = baseRelease?.actors || scene.models; - release.poster = getPosterFallbacks(scene.images.poster); - release.photos = data.pictureset.map(photo => photo.main[0].src); + release.poster = getPosterFallbacks(scene.images.poster); + release.photos = data.pictureset.map(photo => photo.main[0].src); - release.teaser = getTeaserFallbacks(scene.previews.poster); + release.teaser = getTeaserFallbacks(scene.previews.poster); - const trailer = await getTrailer(scene, site, url); - if (trailer) release.trailer = trailer; + const trailer = await getTrailer(scene, site, url); + if (trailer) release.trailer = trailer; - return release; + return release; } async function fetchActorReleases(pages, model, origin) { - const releasesPerPage = await Promise.map(pages, async (page) => { - const url = `${origin}/api${model.targetUrl}?page=${page}`; - const res = await get(url); + const releasesPerPage = await Promise.map(pages, async (page) => { + const url = `${origin}/api${model.targetUrl}?page=${page}`; + const res = await get(url); - if (res.code === 200) { - return scrapeAll(res.body.data.videos.videos, null, origin); - } + if (res.code === 200) { + return scrapeAll(res.body.data.videos.videos, null, origin); + } - return []; - }, { concurrency: 3 }); + return []; + }, { concurrency: 3 }); - return releasesPerPage.flat(); + return releasesPerPage.flat(); } async function scrapeProfile(data, origin, withReleases) { - const model = data.model; - const profile = {}; + const model = data.model; + const profile = {}; - profile.birthdate = new Date(model.dateOfBirth); - profile.gender = genderMap[model.sex]; + profile.birthdate = new Date(model.dateOfBirth); + profile.gender = genderMap[model.sex]; - profile.hair = model.hairColour; - profile.nationality = model.nationality; + profile.hair = model.hairColour; + profile.nationality = model.nationality; - if (model.biography.trim().length > 0) profile.description = model.biography; + if (model.biography.trim().length > 0) profile.description = model.biography; - if (model.cupSize && model.bustMeasurment) profile.bust = `${model.bustMeasurment}${model.cupSize}`; - if (model.waistMeasurment) profile.waist = model.waistMeasurment; - if (model.hipMeasurment) profile.hip = model.hipMeasurment; + if (model.cupSize && model.bustMeasurment) profile.bust = `${model.bustMeasurment}${model.cupSize}`; + if (model.waistMeasurment) profile.waist = model.waistMeasurment; + if (model.hipMeasurment) profile.hip = model.hipMeasurment; - profile.avatar = getAvatarFallbacks(model.images.listing); - profile.poster = getAvatarFallbacks(model.images.profile); - profile.banner = getAvatarFallbacks(model.images.poster); + profile.avatar = getAvatarFallbacks(model.images.listing); + profile.poster = getAvatarFallbacks(model.images.profile); + profile.banner = getAvatarFallbacks(model.images.poster); - const releases = scrapeAll(data.videos.videos, null, origin); + const releases = scrapeAll(data.videos.videos, null, origin); - if (withReleases) { - const pageCount = Math.ceil(data.videos.count / 6); - const otherReleases = await fetchActorReleases((Array.from({ length: pageCount - 1 }, (value, index) => index + 2)), model, origin); + if (withReleases) { + const pageCount = Math.ceil(data.videos.count / 6); + const otherReleases = await fetchActorReleases((Array.from({ length: pageCount - 1 }, (value, index) => index + 2)), model, origin); - profile.releases = [...releases, ...otherReleases]; - } else { - profile.releases = releases; - } + profile.releases = [...releases, ...otherReleases]; + } else { + profile.releases = releases; + } - return profile; + return profile; } async function fetchLatest(site, page = 1) { - const url = `${site.url}/api/videos?page=${page}`; - const res = await get(url); + const url = `${site.url}/api/videos?page=${page}`; + const res = await get(url); - if (res.code === 200) { - return scrapeAll(res.body.data.videos, site); - } + if (res.code === 200) { + return scrapeAll(res.body.data.videos, site); + } - return res.code; + return res.code; } async function fetchUpcoming(site) { - const apiUrl = `${site.url}/api`; - const res = await get(apiUrl); + const apiUrl = `${site.url}/api`; + const res = await get(apiUrl); - if (res.code === 200) { - return scrapeUpcoming(res.body.data.nextScene, site); - } + if (res.code === 200) { + return scrapeUpcoming(res.body.data.nextScene, site); + } - return res.code; + return res.code; } async function fetchScene(url, site, baseRelease) { - const { origin, pathname } = new URL(url); - const apiUrl = `${origin}/api${pathname}`; + const { origin, pathname } = new URL(url); + const apiUrl = `${origin}/api${pathname}`; - const res = await get(apiUrl); + const res = await get(apiUrl); - if (res.code === 200) { - return scrapeScene(res.body.data, url, site, baseRelease); - } + if (res.code === 200) { + return scrapeScene(res.body.data, url, site, baseRelease); + } - return res.code; + return res.code; } async function fetchProfile(actorName, scraperSlug, site, include) { - const origin = `https://www.${scraperSlug}.com`; - const actorSlug = slugify(actorName); - const url = `${origin}/api/${actorSlug}`; - const res = await get(url); + const origin = `https://www.${scraperSlug}.com`; + const actorSlug = slugify(actorName); + const url = `${origin}/api/${actorSlug}`; + const res = await get(url); - if (res.code === 200) { - return scrapeProfile(res.body.data, origin, include.scenes); - } + if (res.code === 200) { + return scrapeProfile(res.body.data, origin, include.scenes); + } - return null; + return null; } module.exports = { - fetchLatest, - fetchUpcoming, - fetchScene, - fetchProfile, + fetchLatest, + fetchUpcoming, + fetchScene, + fetchProfile, }; diff --git a/src/scrapers/vogov.js b/src/scrapers/vogov.js index 91e8a07b..8ba4d0c5 100644 --- a/src/scrapers/vogov.js +++ b/src/scrapers/vogov.js @@ -5,199 +5,199 @@ const { ex, ctxa } = require('../utils/q'); // const slugify = require('../utils/slugify'); function getLicenseCode(html) { - const licensePrefix = 'license_code: \''; - const licenseStart = html.indexOf(licensePrefix); - const licenseCode = html.slice(licenseStart + licensePrefix.length, html.indexOf('\'', licenseStart + licensePrefix.length)); + const licensePrefix = 'license_code: \''; + const licenseStart = html.indexOf(licensePrefix); + const licenseCode = html.slice(licenseStart + licensePrefix.length, html.indexOf('\'', licenseStart + licensePrefix.length)); - const c = '16px'; - let f; - let g; - let h; - let i; - let j; - let k; - let l; - let m; - let n; + const c = '16px'; + let f; + let g; + let h; + let i; + let j; + let k; + let l; + let m; + let n; - for (f = '', g = 1; g < licenseCode.length; g += 1) { - f += parseInt(licenseCode[g], 10) ? parseInt(licenseCode[g], 10) : 1; - } + for (f = '', g = 1; g < licenseCode.length; g += 1) { + f += parseInt(licenseCode[g], 10) ? parseInt(licenseCode[g], 10) : 1; + } - for (j = parseInt(f.length / 2, 10), - k = parseInt(f.substring(0, j + 1), 10), - l = parseInt(f.substring(j), 10), - g = l - k, - g < 0 && (g = -g), - f = g, - g = k - l, - g < 0 && (g = -g), - f += g, - f *= 2, - f = String(f), - i = (parseInt(c, 10) / 2) + 2, - m = '', - g = 0; g < j + 1; g += 1) { - for (h = 1; h <= 4; h += 1) { - n = parseInt(licenseCode[g + h], 10) + parseInt(f[g], 10); + for (j = parseInt(f.length / 2, 10), + k = parseInt(f.substring(0, j + 1), 10), + l = parseInt(f.substring(j), 10), + g = l - k, + g < 0 && (g = -g), + f = g, + g = k - l, + g < 0 && (g = -g), + f += g, + f *= 2, + f = String(f), + i = (parseInt(c, 10) / 2) + 2, + m = '', + g = 0; g < j + 1; g += 1) { + for (h = 1; h <= 4; h += 1) { + n = parseInt(licenseCode[g + h], 10) + parseInt(f[g], 10); - if (n >= i) n -= i; - m += n; - } - } + if (n >= i) n -= i; + m += n; + } + } - return m; + return m; } function decodeTrailerUrl(html, encodedTrailerUrl) { - const licenseCode = getLicenseCode(html); - const i = licenseCode; + const licenseCode = getLicenseCode(html); + const i = licenseCode; - let j; - let k; - let l; - let m; - let n; - let o; + let j; + let k; + let l; + let m; + let n; + let o; - const d = '16px'; - const g = encodedTrailerUrl.split('/').slice(2); + const d = '16px'; + const g = encodedTrailerUrl.split('/').slice(2); - let h = g[5].substring(0, 2 * parseInt(d, 10)); + let h = g[5].substring(0, 2 * parseInt(d, 10)); - for (j = h, k = h.length - 1; k >= 0; k -= 1) { - for (l = k, m = k; m < i.length; m += 1) { - l += parseInt(i[m], 10); - } + for (j = h, k = h.length - 1; k >= 0; k -= 1) { + for (l = k, m = k; m < i.length; m += 1) { + l += parseInt(i[m], 10); + } - for (; l >= h.length;) { - l -= h.length; - } + for (; l >= h.length;) { + l -= h.length; + } - for (n = '', o = 0; o < h.length; o += 1) { - if (o === k) { - n += h[l]; - } else { - n += (o === l ? h[k] : h[o]); - } - } + for (n = '', o = 0; o < h.length; o += 1) { + if (o === k) { + n += h[l]; + } else { + n += (o === l ? h[k] : h[o]); + } + } - h = n; - } + h = n; + } - g[5] = g[5].replace(j, h); - const trailer = g.join('/'); + g[5] = g[5].replace(j, h); + const trailer = g.join('/'); - return trailer; + return trailer; } function scrapeLatest(html) { - const { document } = ex(html); + const { document } = ex(html); - return ctxa(document, '.video-post').map(({ q, qa, qd }) => { - const release = {}; + return ctxa(document, '.video-post').map(({ q, qa, qd }) => { + const release = {}; - // release.entryId = slugify(release.title); - release.entryId = q('.ico-fav-0').dataset.favVideoId; + // release.entryId = slugify(release.title); + release.entryId = q('.ico-fav-0').dataset.favVideoId; - const titleEl = q('.video-title-title'); - release.title = titleEl.title; - release.url = titleEl.href; + const titleEl = q('.video-title-title'); + release.title = titleEl.title; + release.url = titleEl.href; - release.date = qd('.video-data em', 'MMM DD, YYYY'); - release.actors = qa('.video-model-list a', true); + release.date = qd('.video-data em', 'MMM DD, YYYY'); + release.actors = qa('.video-model-list a', true); - const posterData = q('img.thumb').dataset; - release.poster = posterData.src; - release.trailer = posterData.preview; + const posterData = q('img.thumb').dataset; + release.poster = posterData.src; + release.trailer = posterData.preview; - return release; - }); + return release; + }); } function scrapeScene(html, url) { - const { qu } = ex(html); - const release = { url }; + const { qu } = ex(html); + const release = { url }; - // release.entryId = slugify(release.title); - [release.entryId] = qu.q('link[rel="canonical"]').href.match(/\d+/); + // release.entryId = slugify(release.title); + [release.entryId] = qu.q('link[rel="canonical"]').href.match(/\d+/); - release.title = qu.meta('meta[property="og:title"]') || qu.q('.video-page-header h1', true); - release.description = qu.meta('meta[property="og:description"]') || qu.q('.info-video-description', true); + release.title = qu.meta('meta[property="og:title"]') || qu.q('.video-page-header h1', true); + release.description = qu.meta('meta[property="og:description"]') || qu.q('.info-video-description', true); - release.date = qu.date('.info-video-details li:first-child span', 'MMM DD, YYYY'); - release.duration = qu.dur('.info-video-details li:nth-child(2) span'); + release.date = qu.date('.info-video-details li:first-child span', 'MMM DD, YYYY'); + release.duration = qu.dur('.info-video-details li:nth-child(2) span'); - release.actors = qu.all('.info-video-models a', true); - release.tags = qu.all('.info-video-category a', true); + release.actors = qu.all('.info-video-models a', true); + release.tags = qu.all('.info-video-category a', true); - release.photos = qu.urls('.swiper-wrapper .swiper-slide a').map(source => source.replace('.jpg/', '.jpg')); - release.poster = qu.meta('meta[property="og:image"'); + release.photos = qu.urls('.swiper-wrapper .swiper-slide a').map(source => source.replace('.jpg/', '.jpg')); + release.poster = qu.meta('meta[property="og:image"'); - if (!release.poster) { - const previewStart = html.indexOf('preview_url'); - release.poster = html.slice(html.indexOf('http', previewStart), html.indexOf('.jpg', previewStart) + 4); - } + if (!release.poster) { + const previewStart = html.indexOf('preview_url'); + release.poster = html.slice(html.indexOf('http', previewStart), html.indexOf('.jpg', previewStart) + 4); + } - const varsPrefix = 'flashvars = {'; - const varsStart = html.indexOf(varsPrefix); - const varsString = html.slice(varsStart + varsPrefix.length, html.indexOf('};', varsStart)); + const varsPrefix = 'flashvars = {'; + const varsStart = html.indexOf(varsPrefix); + const varsString = html.slice(varsStart + varsPrefix.length, html.indexOf('};', varsStart)); - const vars = varsString.split(',').reduce((acc, item) => { - const [prop, value] = item.split(': '); - acc[prop.trim()] = value.trim().replace(/'/g, ''); + const vars = varsString.split(',').reduce((acc, item) => { + const [prop, value] = item.split(': '); + acc[prop.trim()] = value.trim().replace(/'/g, ''); - return acc; - }, {}); + return acc; + }, {}); - release.trailer = [ - { - src: decodeTrailerUrl(html, vars.video_url), - quality: parseInt(vars.video_url_text, 10), - }, - { - src: decodeTrailerUrl(html, vars.video_alt_url), - quality: parseInt(vars.video_alt_url_text, 10), - }, - { - src: decodeTrailerUrl(html, vars.video_alt_url2), - quality: parseInt(vars.video_alt_url2_text, 10), - }, - { - src: decodeTrailerUrl(html, vars.video_alt_url3), - quality: parseInt(vars.video_alt_url3_text, 10), - }, - { - src: decodeTrailerUrl(html, vars.video_alt_url4), - quality: parseInt(vars.video_alt_url4_text, 10), - }, - ]; + release.trailer = [ + { + src: decodeTrailerUrl(html, vars.video_url), + quality: parseInt(vars.video_url_text, 10), + }, + { + src: decodeTrailerUrl(html, vars.video_alt_url), + quality: parseInt(vars.video_alt_url_text, 10), + }, + { + src: decodeTrailerUrl(html, vars.video_alt_url2), + quality: parseInt(vars.video_alt_url2_text, 10), + }, + { + src: decodeTrailerUrl(html, vars.video_alt_url3), + quality: parseInt(vars.video_alt_url3_text, 10), + }, + { + src: decodeTrailerUrl(html, vars.video_alt_url4), + quality: parseInt(vars.video_alt_url4_text, 10), + }, + ]; - return release; + return release; } async function fetchLatest(site, page = 1) { - const url = `https://vogov.com/latest-videos/?sort_by=post_date&from=${page}`; - const res = await bhttp.get(url); + const url = `https://vogov.com/latest-videos/?sort_by=post_date&from=${page}`; + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeLatest(res.body.toString(), site); - } + if (res.statusCode === 200) { + return scrapeLatest(res.body.toString(), site); + } - return null; + return null; } async function fetchScene(url) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeScene(res.body.toString(), url); - } + if (res.statusCode === 200) { + return scrapeScene(res.body.toString(), url); + } - return null; + return null; } module.exports = { - fetchLatest, - fetchScene, + fetchLatest, + fetchScene, }; diff --git a/src/scrapers/whalemember.js b/src/scrapers/whalemember.js index 3f9bdb07..05ff936a 100644 --- a/src/scrapers/whalemember.js +++ b/src/scrapers/whalemember.js @@ -5,86 +5,86 @@ const { JSDOM } = require('jsdom'); const moment = require('moment'); function scrapeLatest(html, site) { - const { document } = new JSDOM(html).window; - const { origin } = new URL(site.url); + const { document } = new JSDOM(html).window; + const { origin } = new URL(site.url); - const videos = Array.from(document.querySelectorAll('.video-releases-list')).slice(-1)[0]; + const videos = Array.from(document.querySelectorAll('.video-releases-list')).slice(-1)[0]; - return Array.from(videos.querySelectorAll('.card'), (scene) => { - const release = { site }; + return Array.from(videos.querySelectorAll('.card'), (scene) => { + const release = { site }; - release.url = `${origin}${scene.querySelector(':scope > a').href}`; - release.entryId = scene.dataset.videoId; - release.title = scene.querySelector('.card-title').textContent; - release.date = moment.utc(scene.dataset.date, 'MMMM DD, YYYY').toDate(); - release.actors = Array.from(scene.querySelectorAll('.actors a'), el => el.textContent); + release.url = `${origin}${scene.querySelector(':scope > a').href}`; + release.entryId = scene.dataset.videoId; + release.title = scene.querySelector('.card-title').textContent; + release.date = moment.utc(scene.dataset.date, 'MMMM DD, YYYY').toDate(); + release.actors = Array.from(scene.querySelectorAll('.actors a'), el => el.textContent); - release.poster = `https:${scene.querySelector('.single-image').src}`; - release.photos = Array.from(scene.querySelectorAll('.rollover-thumbs img'), el => `https:${el.dataset.src}`); + release.poster = `https:${scene.querySelector('.single-image').src}`; + release.photos = Array.from(scene.querySelectorAll('.rollover-thumbs img'), el => `https:${el.dataset.src}`); - const trailerEl = scene.querySelector('source'); - if (trailerEl) release.trailer = { src: trailerEl.dataset.src }; + const trailerEl = scene.querySelector('source'); + if (trailerEl) release.trailer = { src: trailerEl.dataset.src }; - return release; - }); + return release; + }); } function scrapeScene(html, site, url) { - const { document } = new JSDOM(html).window; - const release = { site }; + const { document } = new JSDOM(html).window; + const release = { site }; - const scene = document.querySelector('#t2019-2col'); + const scene = document.querySelector('#t2019-2col'); - release.url = url; - release.title = scene.querySelector('.t2019-stitle').textContent.trim(); - release.description = scene.querySelector('#t2019-description').textContent.trim(); - release.actors = Array.from(scene.querySelectorAll('#t2019-models a'), el => el.textContent); + release.url = url; + release.title = scene.querySelector('.t2019-stitle').textContent.trim(); + release.description = scene.querySelector('#t2019-description').textContent.trim(); + release.actors = Array.from(scene.querySelectorAll('#t2019-models a'), el => el.textContent); - const durationEls = Array.from(scene.querySelectorAll('#t2019-stime span')); + const durationEls = Array.from(scene.querySelectorAll('#t2019-stime span')); - if (durationEls.length > 1) { - release.date = moment.utc(durationEls[0].textContent, 'MMMM DD, YYYY').toDate(); - release.duration = Number(durationEls[1].textContent.match(/\d+/)[0]) * 60; - } else { - release.duration = Number(durationEls[0].textContent.match(/\d+/)[0]) * 60; - } + if (durationEls.length > 1) { + release.date = moment.utc(durationEls[0].textContent, 'MMMM DD, YYYY').toDate(); + release.duration = Number(durationEls[1].textContent.match(/\d+/)[0]) * 60; + } else { + release.duration = Number(durationEls[0].textContent.match(/\d+/)[0]) * 60; + } - release.photos = Array.from(scene.querySelectorAll('#t2019-main .t2019-thumbs img'), el => `https:${el.src}`); + release.photos = Array.from(scene.querySelectorAll('#t2019-main .t2019-thumbs img'), el => `https:${el.src}`); - const posterEl = scene.querySelector('#no-player-image'); - const videoEl = scene.querySelector('video'); + const posterEl = scene.querySelector('#no-player-image'); + const videoEl = scene.querySelector('video'); - if (posterEl) release.poster = `https:${posterEl.src}`; - else if (videoEl) release.poster = `https:${videoEl.poster}`; + if (posterEl) release.poster = `https:${posterEl.src}`; + else if (videoEl) release.poster = `https:${videoEl.poster}`; - const trailerEl = scene.querySelector('#t2019-video source'); - if (trailerEl) release.trailer = { src: trailerEl.src }; + const trailerEl = scene.querySelector('#t2019-video source'); + if (trailerEl) release.trailer = { src: trailerEl.src }; - return release; + return release; } async function fetchLatest(site, page = 1) { - const url = `${site.url}?page=${page}`; - const res = await bhttp.get(url); + const url = `${site.url}?page=${page}`; + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeLatest(res.body.toString(), site); - } + if (res.statusCode === 200) { + return scrapeLatest(res.body.toString(), site); + } - return []; + return []; } async function fetchScene(url, site) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - if (res.statusCode === 200) { - return scrapeScene(res.body.toString(), site, url); - } + if (res.statusCode === 200) { + return scrapeScene(res.body.toString(), site, url); + } - return null; + return null; } module.exports = { - fetchLatest, - fetchScene, + fetchLatest, + fetchScene, }; diff --git a/src/scrapers/wicked.js b/src/scrapers/wicked.js index c31fa6e8..562acaa6 100644 --- a/src/scrapers/wicked.js +++ b/src/scrapers/wicked.js @@ -3,8 +3,8 @@ const { fetchApiLatest, fetchApiUpcoming, fetchScene, fetchApiProfile } = require('./gamma'); module.exports = { - fetchLatest: fetchApiLatest, - fetchProfile: fetchApiProfile, - fetchScene, - fetchUpcoming: fetchApiUpcoming, + fetchLatest: fetchApiLatest, + fetchProfile: fetchApiProfile, + fetchScene, + fetchUpcoming: fetchApiUpcoming, }; diff --git a/src/scrapers/xempire.js b/src/scrapers/xempire.js index bbcf744a..f970ba6d 100644 --- a/src/scrapers/xempire.js +++ b/src/scrapers/xempire.js @@ -5,31 +5,31 @@ const bhttp = require('bhttp'); const { fetchLatest, fetchUpcoming, scrapeScene, fetchProfile } = require('./gamma'); async function fetchScene(url, site) { - const res = await bhttp.get(url); + const res = await bhttp.get(url); - const release = await scrapeScene(res.body.toString(), url, site); + const release = await scrapeScene(res.body.toString(), url, site); - const siteDomain = release.$('meta[name="twitter:domain"]').attr('content') || 'allblackx.com'; // only AllBlackX has no twitter domain, no other useful hints available - const siteSlug = siteDomain && siteDomain.split('.')[0].toLowerCase(); - // const siteUrl = siteDomain && `https://www.${siteDomain}`; + const siteDomain = release.$('meta[name="twitter:domain"]').attr('content') || 'allblackx.com'; // only AllBlackX has no twitter domain, no other useful hints available + const siteSlug = siteDomain && siteDomain.split('.')[0].toLowerCase(); + // const siteUrl = siteDomain && `https://www.${siteDomain}`; - release.channel = siteSlug; - release.director = 'Mason'; + release.channel = siteSlug; + release.director = 'Mason'; - return release; + return release; } function getActorReleasesUrl(actorPath, page = 1) { - return `https://www.xempire.com/en/videos/xempire/latest/${page}/All-Categories/0${actorPath}`; + return `https://www.xempire.com/en/videos/xempire/latest/${page}/All-Categories/0${actorPath}`; } async function networkFetchProfile(actorName, scraperSlug, site, include) { - return fetchProfile(actorName, scraperSlug, null, getActorReleasesUrl, include); + return fetchProfile(actorName, scraperSlug, null, getActorReleasesUrl, include); } module.exports = { - fetchLatest, - fetchProfile: networkFetchProfile, - fetchUpcoming, - fetchScene, + fetchLatest, + fetchProfile: networkFetchProfile, + fetchUpcoming, + fetchScene, }; diff --git a/src/sites.js b/src/sites.js index 1c9a591f..0d0995c9 100644 --- a/src/sites.js +++ b/src/sites.js @@ -8,189 +8,189 @@ const knex = require('./knex'); const whereOr = require('./utils/where-or'); async function curateSite(site, includeParameters = false, includeTags = true) { - const curatedSite = { - id: site.id, - name: site.name, - url: site.url, - description: site.description, - slug: site.slug, - independent: !!site.parameters && site.parameters.independent, - parameters: includeParameters ? site.parameters : null, - network: { - id: site.network_id, - name: site.network_name, - description: site.network_description, - slug: site.network_slug, - url: site.network_url, - parameters: includeParameters ? site.network_parameters : null, - }, - }; + const curatedSite = { + id: site.id, + name: site.name, + url: site.url, + description: site.description, + slug: site.slug, + independent: !!site.parameters && site.parameters.independent, + parameters: includeParameters ? site.parameters : null, + network: { + id: site.network_id, + name: site.network_name, + description: site.network_description, + slug: site.network_slug, + url: site.network_url, + parameters: includeParameters ? site.network_parameters : null, + }, + }; - if (includeTags) { - curatedSite.tags = await knex('sites_tags') - .select('tags.*', 'sites_tags.inherit') - .where('site_id', site.id) - .join('tags', 'tags.id', 'sites_tags.tag_id'); - } + if (includeTags) { + curatedSite.tags = await knex('sites_tags') + .select('tags.*', 'sites_tags.inherit') + .where('site_id', site.id) + .join('tags', 'tags.id', 'sites_tags.tag_id'); + } - return curatedSite; + return curatedSite; } async function curateSites(sites, includeParameters) { - return Promise.all(sites.map(async site => curateSite(site, includeParameters))); + return Promise.all(sites.map(async site => curateSite(site, includeParameters))); } function destructConfigNetworks(networks = []) { - return networks.reduce((acc, network) => { - if (Array.isArray(network)) { - // network specifies sites - return { - ...acc, - sites: [...acc.sites, ...network[1]], - }; - } + return networks.reduce((acc, network) => { + if (Array.isArray(network)) { + // network specifies sites + return { + ...acc, + sites: [...acc.sites, ...network[1]], + }; + } - return { - ...acc, - networks: [...acc.networks, network], - }; - }, { - networks: [], - sites: [], - }); + return { + ...acc, + networks: [...acc.networks, network], + }; + }, { + networks: [], + sites: [], + }); } async function findSiteByUrl(url) { - const { origin, hostname, pathname } = new URL(url); - // const domain = hostname.replace(/www.|tour./, ''); - const dirUrl = `${origin}${pathname.split('/').slice(0, 2).join('/')}`; // allow for sites on URI directory + const { origin, hostname, pathname } = new URL(url); + // const domain = hostname.replace(/www.|tour./, ''); + const dirUrl = `${origin}${pathname.split('/').slice(0, 2).join('/')}`; // allow for sites on URI directory - const site = await knex('sites') - .leftJoin('networks', 'sites.network_id', 'networks.id') - .select( - 'sites.*', - 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', 'networks.parameters as network_parameters', - ) - .where('sites.url', url) - .orWhere('sites.url', origin) - .orWhere('sites.url', origin.replace(/www\.|tour\./, '')) - .orWhere('sites.url', `https://www.${hostname}`) - .orWhere('sites.url', `http://www.${hostname}`) - .orWhere('sites.url', dirUrl) - // .orWhere('sites.url', 'like', `%${domain}`) - .first(); + const site = await knex('sites') + .leftJoin('networks', 'sites.network_id', 'networks.id') + .select( + 'sites.*', + 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', 'networks.parameters as network_parameters', + ) + .where('sites.url', url) + .orWhere('sites.url', origin) + .orWhere('sites.url', origin.replace(/www\.|tour\./, '')) + .orWhere('sites.url', `https://www.${hostname}`) + .orWhere('sites.url', `http://www.${hostname}`) + .orWhere('sites.url', dirUrl) + // .orWhere('sites.url', 'like', `%${domain}`) + .first(); - if (site) { - const curatedSite = curateSite(site, true, false); + if (site) { + const curatedSite = curateSite(site, true, false); - return curatedSite; - } + return curatedSite; + } - return null; + return null; } function sitesByNetwork(sites) { - const networks = sites.reduce((acc, site) => { - if (acc[site.network.slug]) { - acc[site.network.slug].sites = acc[site.network.slug].sites.concat(site); + const networks = sites.reduce((acc, site) => { + if (acc[site.network.slug]) { + acc[site.network.slug].sites = acc[site.network.slug].sites.concat(site); - return acc; - } + return acc; + } - acc[site.network.slug] = { - ...site.network, - sites: [site], - }; + acc[site.network.slug] = { + ...site.network, + sites: [site], + }; - return acc; - }, {}); + return acc; + }, {}); - return Object.values(networks); + return Object.values(networks); } async function fetchSitesFromArgv() { - const rawSites = await knex('sites') - .select( - 'sites.*', - 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', 'networks.parameters as network_parameters', - ) - .whereIn('sites.slug', argv.sites || []) - .orWhereIn('networks.slug', argv.networks || []) - .leftJoin('networks', 'sites.network_id', 'networks.id'); + const rawSites = await knex('sites') + .select( + 'sites.*', + 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', 'networks.parameters as network_parameters', + ) + .whereIn('sites.slug', argv.sites || []) + .orWhereIn('networks.slug', argv.networks || []) + .leftJoin('networks', 'sites.network_id', 'networks.id'); - const curatedSites = await curateSites(rawSites, true); - logger.info(`Found ${curatedSites.length} sites in database`); + const curatedSites = await curateSites(rawSites, true); + logger.info(`Found ${curatedSites.length} sites in database`); - return sitesByNetwork(curatedSites); + return sitesByNetwork(curatedSites); } async function fetchSitesFromConfig() { - const included = destructConfigNetworks(config.include); - const excluded = destructConfigNetworks(config.exclude); + const included = destructConfigNetworks(config.include); + const excluded = destructConfigNetworks(config.exclude); - const rawSites = await knex('sites') - .select( - 'sites.*', - 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', 'networks.parameters as network_parameters', - ) - .leftJoin('networks', 'sites.network_id', 'networks.id') - .where((builder) => { - if (config.include) { - builder - .whereIn('sites.slug', included.sites) - .orWhereIn('networks.slug', included.networks); - } - }) - .whereNot((builder) => { - builder - .whereIn('sites.slug', excluded.sites) - .orWhereIn('networks.slug', excluded.networks); - }); + const rawSites = await knex('sites') + .select( + 'sites.*', + 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', 'networks.parameters as network_parameters', + ) + .leftJoin('networks', 'sites.network_id', 'networks.id') + .where((builder) => { + if (config.include) { + builder + .whereIn('sites.slug', included.sites) + .orWhereIn('networks.slug', included.networks); + } + }) + .whereNot((builder) => { + builder + .whereIn('sites.slug', excluded.sites) + .orWhereIn('networks.slug', excluded.networks); + }); - const curatedSites = await curateSites(rawSites, true); - logger.info(`Found ${curatedSites.length} sites in database`); + const curatedSites = await curateSites(rawSites, true); + logger.info(`Found ${curatedSites.length} sites in database`); - return sitesByNetwork(curatedSites); + return sitesByNetwork(curatedSites); } async function fetchIncludedSites() { - if (argv.networks || argv.sites) { - return fetchSitesFromArgv(); - } + if (argv.networks || argv.sites) { + return fetchSitesFromArgv(); + } - return fetchSitesFromConfig(); + return fetchSitesFromConfig(); } async function fetchSites(queryObject) { - const sites = await knex('sites') - .where(builder => whereOr(queryObject, 'sites', builder)) - .select( - 'sites.*', - 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', 'networks.parameters as network_parameters', - ) - .leftJoin('networks', 'sites.network_id', 'networks.id') - .limit(100); + const sites = await knex('sites') + .where(builder => whereOr(queryObject, 'sites', builder)) + .select( + 'sites.*', + 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.description as network_description', 'networks.parameters as network_parameters', + ) + .leftJoin('networks', 'sites.network_id', 'networks.id') + .limit(100); - return curateSites(sites); + return curateSites(sites); } async function fetchSitesFromReleases() { - const sites = await knex('releases') - .select('site_id', '') - .leftJoin('sites', 'sites.id', 'releases.site_id') - .groupBy('sites.id') - .limit(100); + const sites = await knex('releases') + .select('site_id', '') + .leftJoin('sites', 'sites.id', 'releases.site_id') + .groupBy('sites.id') + .limit(100); - return curateSites(sites); + return curateSites(sites); } module.exports = { - curateSite, - curateSites, - fetchIncludedSites, - fetchSites, - fetchSitesFromConfig, - fetchSitesFromArgv, - fetchSitesFromReleases, - findSiteByUrl, + curateSite, + curateSites, + fetchIncludedSites, + fetchSites, + fetchSitesFromConfig, + fetchSitesFromArgv, + fetchSitesFromReleases, + findSiteByUrl, }; diff --git a/src/store-releases.js b/src/store-releases.js index 738a9803..1d38425a 100644 --- a/src/store-releases.js +++ b/src/store-releases.js @@ -11,155 +11,164 @@ const { curateSite } = require('./sites'); const { associateReleaseMedia } = require('./media'); function curateReleaseEntry(release, batchId, existingRelease) { - const slug = slugify(release.title || release.actors?.join('-') || null, '-', { - encode: true, - limit: config.titleSlugLength, - }); + const slug = slugify(release.title || release.actors?.join('-') || null, '-', { + encode: true, + limit: config.titleSlugLength, + }); - const curatedRelease = { - title: release.title, - entry_id: release.entryId || null, - site_id: release.site.id, - shoot_id: release.shootId || null, - studio_id: release.studio?.id || null, - url: release.url, - date: release.date, - slug, - description: release.description, - duration: release.duration, - type: release.type, - // director: release.director, - // likes: release.rating && release.rating.likes, - // dislikes: release.rating && release.rating.dislikes, - // rating: release.rating && release.rating.stars && Math.floor(release.rating.stars), - deep: typeof release.deep === 'boolean' ? release.deep : false, - deep_url: release.deepUrl, - updated_batch_id: batchId, - }; + const curatedRelease = { + title: release.title, + entry_id: release.entryId || null, + site_id: release.site?.id, + network_id: release.site ? null : release.network?.id, // prefer site ID if available + shoot_id: release.shootId || null, + studio_id: release.studio?.id || null, + url: release.url, + date: release.date, + slug, + description: release.description, + duration: release.duration, + type: release.type, + // director: release.director, + // likes: release.rating && release.rating.likes, + // dislikes: release.rating && release.rating.dislikes, + // rating: release.rating && release.rating.stars && Math.floor(release.rating.stars), + deep: typeof release.deep === 'boolean' ? release.deep : false, + deep_url: release.deepUrl, + updated_batch_id: batchId, + }; - if (!existingRelease && !release.id) { - curatedRelease.created_batch_id = batchId; - } + if (!existingRelease && !release.id) { + curatedRelease.created_batch_id = batchId; + } - return curatedRelease; + return curatedRelease; } async function attachChannelSites(releases) { - const releasesWithoutSite = releases.filter(release => release.channel && (!release.site || release.site.isFallback)); + const releasesWithoutSite = releases.filter(release => release.channel && (!release.site || release.site.isNetwork)); - const channelSites = await knex('sites') - .leftJoin('networks', 'networks.id', 'sites.network_id') - .select('sites.*', 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.parameters as network_parameters', 'networks.description as network_description') - .whereIn('sites.slug', releasesWithoutSite.map(release => release.channel)); + const channelSites = await knex('sites') + .leftJoin('networks', 'networks.id', 'sites.network_id') + .select('sites.*', 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', 'networks.parameters as network_parameters', 'networks.description as network_description') + .whereIn('sites.slug', releasesWithoutSite.map(release => release.channel)); - const channelSitesBySlug = channelSites.reduce((acc, site) => ({ ...acc, [site.slug]: site }), {}); + const channelSitesBySlug = channelSites.reduce((acc, site) => ({ ...acc, [site.slug]: site }), {}); - const releasesWithChannelSite = await Promise.all(releases - .map(async (release) => { - if (release.site && !release.site.isFallback) { - return release; - } + const releasesWithChannelSite = await Promise.all(releases + .map(async (release) => { + if (release.site && !release.site.isNetwork) { + return release; + } - if (release.channel && channelSitesBySlug[release.channel]) { - const curatedSite = await curateSite(channelSitesBySlug[release.channel]); + if (release.channel && channelSitesBySlug[release.channel]) { + const curatedSite = await curateSite(channelSitesBySlug[release.channel]); - return { - ...release, - site: curatedSite, - }; - } + return { + ...release, + site: curatedSite, + }; + } - logger.error(`Unable to match channel '${release.channel?.slug || release.channel}' from generic URL ${release.url}`); + if (release.site && release.site.isNetwork) { + return { + ...release, + site: null, + network: release.site, + }; + } - return null; - })); + logger.error(`Unable to match channel '${release.channel?.slug || release.channel}' from generic URL ${release.url}`); - return releasesWithChannelSite.filter(Boolean); + return null; + })); + + return releasesWithChannelSite.filter(Boolean); } async function attachStudios(releases) { - const studioSlugs = releases.map(release => release.studio).filter(Boolean); + const studioSlugs = releases.map(release => release.studio).filter(Boolean); - const studios = await knex('studios').whereIn('slug', studioSlugs); - const studioBySlug = studios.reduce((acc, studio) => ({ ...acc, [studio.slug]: studio }), {}); + const studios = await knex('studios').whereIn('slug', studioSlugs); + const studioBySlug = studios.reduce((acc, studio) => ({ ...acc, [studio.slug]: studio }), {}); - const releasesWithStudio = releases.map((release) => { - if (release.studio && studioBySlug[release.studio]) { - return { - ...release, - studio: studioBySlug[release.studio], - }; - } + const releasesWithStudio = releases.map((release) => { + if (release.studio && studioBySlug[release.studio]) { + return { + ...release, + studio: studioBySlug[release.studio], + }; + } - if (release.studio) { - logger.warn(`Unable to match studio '${release.studio}' for ${release.url}`); - } + if (release.studio) { + logger.warn(`Unable to match studio '${release.studio}' for ${release.url}`); + } - return release; - }); + return release; + }); - return releasesWithStudio; + return releasesWithStudio; } function attachReleaseIds(releases, storedReleases) { - const storedReleaseIdsBySiteIdAndEntryId = storedReleases.reduce((acc, release) => { - if (!acc[release.site_id]) acc[release.site_id] = {}; - acc[release.site_id][release.entry_id] = release.id; + const storedReleaseIdsBySiteIdAndEntryId = storedReleases.reduce((acc, release) => { + if (!acc[release.site_id]) acc[release.site_id] = {}; + acc[release.site_id][release.entry_id] = release.id; - return acc; - }, {}); + return acc; + }, {}); - const releasesWithId = releases.map(release => ({ - ...release, - id: storedReleaseIdsBySiteIdAndEntryId[release.site.id][release.entryId], - })); + const releasesWithId = releases.map(release => ({ + ...release, + id: storedReleaseIdsBySiteIdAndEntryId[release.site.id][release.entryId], + })); - return releasesWithId; + return releasesWithId; } function filterInternalDuplicateReleases(releases) { - const releasesBySiteIdAndEntryId = releases.reduce((acc, release) => { - if (!acc[release.site.id]) { - acc[release.site.id] = {}; - } + const releasesBySiteIdAndEntryId = releases.reduce((acc, release) => { + if (!acc[release.site.id]) { + acc[release.site.id] = {}; + } - acc[release.site.id][release.entryId] = release; + acc[release.site.id][release.entryId] = release; - return acc; - }, {}); + return acc; + }, {}); - return Object.values(releasesBySiteIdAndEntryId) - .map(siteReleases => Object.values(siteReleases)) - .flat(); + return Object.values(releasesBySiteIdAndEntryId) + .map(siteReleases => Object.values(siteReleases)) + .flat(); } async function filterDuplicateReleases(releases) { - const internalUniqueReleases = filterInternalDuplicateReleases(releases); + const internalUniqueReleases = filterInternalDuplicateReleases(releases); - const duplicateReleaseEntries = await knex('releases') - .whereIn(['entry_id', 'site_id'], internalUniqueReleases.map(release => [release.entryId, release.site.id])); + const duplicateReleaseEntries = await knex('releases') + .whereIn(['entry_id', 'site_id'], internalUniqueReleases.map(release => [release.entryId, release.site.id])); - const duplicateReleasesBySiteIdAndEntryId = duplicateReleaseEntries.reduce((acc, release) => { - if (!acc[release.site_id]) acc[release.site_id] = {}; - acc[release.site_id][release.entry_id] = true; + const duplicateReleasesBySiteIdAndEntryId = duplicateReleaseEntries.reduce((acc, release) => { + if (!acc[release.site_id]) acc[release.site_id] = {}; + acc[release.site_id][release.entry_id] = true; - return acc; - }, {}); + return acc; + }, {}); - const duplicateReleases = internalUniqueReleases.filter(release => duplicateReleasesBySiteIdAndEntryId[release.site.id]?.[release.entryId]); - const uniqueReleases = internalUniqueReleases.filter(release => !duplicateReleasesBySiteIdAndEntryId[release.site.id]?.[release.entryId]); + const duplicateReleases = internalUniqueReleases.filter(release => duplicateReleasesBySiteIdAndEntryId[release.site.id]?.[release.entryId]); + const uniqueReleases = internalUniqueReleases.filter(release => !duplicateReleasesBySiteIdAndEntryId[release.site.id]?.[release.entryId]); - return { - uniqueReleases, - duplicateReleases, - duplicateReleaseEntries, - }; + return { + uniqueReleases, + duplicateReleases, + duplicateReleaseEntries, + }; } async function updateReleasesSearch(releaseIds) { - logger.info(`Updating search documents for ${releaseIds ? releaseIds.length : 'all' } releases`); + logger.info(`Updating search documents for ${releaseIds ? releaseIds.length : 'all' } releases`); - const documents = await knex.raw(` + const documents = await knex.raw(` SELECT releases.id AS release_id, TO_TSVECTOR( @@ -190,45 +199,49 @@ async function updateReleasesSearch(releaseIds) { GROUP BY releases.id, sites.name, sites.slug, sites.alias, sites.url, networks.name, networks.slug, networks.url; `, releaseIds && [releaseIds]); - if (documents.rows?.length > 0) { - const query = knex('releases_search').insert(documents.rows).toString(); - await knex.raw(`${query} ON CONFLICT (release_id) DO UPDATE SET document = EXCLUDED.document`); - } + if (documents.rows?.length > 0) { + const query = knex('releases_search').insert(documents.rows).toString(); + await knex.raw(`${query} ON CONFLICT (release_id) DO UPDATE SET document = EXCLUDED.document`); + } } async function storeReleases(releases) { - const [batchId] = await knex('batches').insert({ comment: null }).returning('id'); + if (releases.length === 0) { + return []; + } - const releasesWithSites = await attachChannelSites(releases); - const releasesWithStudios = await attachStudios(releasesWithSites); + const [batchId] = await knex('batches').insert({ comment: null }).returning('id'); - // uniqueness is site ID + entry ID, filter uniques after adding sites - const { uniqueReleases, duplicateReleases, duplicateReleaseEntries } = await filterDuplicateReleases(releasesWithStudios); + const releasesWithSites = await attachChannelSites(releases); + const releasesWithStudios = await attachStudios(releasesWithSites); - const curatedNewReleaseEntries = uniqueReleases.map(release => curateReleaseEntry(release, batchId)); + // uniqueness is site ID + entry ID, filter uniques after adding sites + const { uniqueReleases, duplicateReleases, duplicateReleaseEntries } = await filterDuplicateReleases(releasesWithStudios); - const storedReleases = await knex('releases').insert(curatedNewReleaseEntries).returning('*'); - // TODO: update duplicate releases + const curatedNewReleaseEntries = uniqueReleases.map(release => curateReleaseEntry(release, batchId)); - const storedReleaseEntries = Array.isArray(storedReleases) ? storedReleases : []; - const releasesWithId = attachReleaseIds([].concat(uniqueReleases, duplicateReleases), [].concat(storedReleaseEntries, duplicateReleaseEntries)); + const storedReleases = await knex('releases').insert(curatedNewReleaseEntries).returning('*'); + // TODO: update duplicate releases - await Promise.all([ - associateActors(releasesWithId, batchId), - associateReleaseTags(releasesWithId), - ]); + const storedReleaseEntries = Array.isArray(storedReleases) ? storedReleases : []; + const releasesWithId = attachReleaseIds([].concat(uniqueReleases, duplicateReleases), [].concat(storedReleaseEntries, duplicateReleaseEntries)); - // media is more error-prone, associate separately - await associateReleaseMedia(releasesWithId); + await Promise.all([ + associateActors(releasesWithId, batchId), + associateReleaseTags(releasesWithId), + ]); - logger.info(`Stored ${storedReleaseEntries.length} releases`); + // media is more error-prone, associate separately + await associateReleaseMedia(releasesWithId); - await updateReleasesSearch(releasesWithId.map(release => release.id)); + logger.info(`Stored ${storedReleaseEntries.length} releases`); - return releasesWithId; + await updateReleasesSearch(releasesWithId.map(release => release.id)); + + return releasesWithId; } module.exports = { - storeReleases, - updateReleasesSearch, + storeReleases, + updateReleasesSearch, }; diff --git a/src/tags-legacy.js b/src/tags-legacy.js index c6f97442..1ea74b11 100644 --- a/src/tags-legacy.js +++ b/src/tags-legacy.js @@ -5,106 +5,106 @@ const knex = require('./knex'); const whereOr = require('./utils/where-or'); async function curateTag(tag) { - const [aliases, media] = await Promise.all([ - knex('tags').where({ alias_for: tag.id }), - knex('media') - .where('domain', 'tags') - .andWhere('target_id', tag.id) - .orderBy('index'), - ]); + const [aliases, media] = await Promise.all([ + knex('tags').where({ alias_for: tag.id }), + knex('media') + .where('domain', 'tags') + .andWhere('target_id', tag.id) + .orderBy('index'), + ]); - return { - id: tag.id, - name: tag.name, - slug: tag.slug, - description: tag.description, - poster: media.find(photo => photo.role === 'poster'), - photos: media.filter(photo => photo.role === 'photo'), - group: { - id: tag.group_id, - name: tag.group_name, - description: tag.group_description, - slug: tag.group_slug, - }, - aliases: aliases.map(({ name }) => name), - }; + return { + id: tag.id, + name: tag.name, + slug: tag.slug, + description: tag.description, + poster: media.find(photo => photo.role === 'poster'), + photos: media.filter(photo => photo.role === 'photo'), + group: { + id: tag.group_id, + name: tag.group_name, + description: tag.group_description, + slug: tag.group_slug, + }, + aliases: aliases.map(({ name }) => name), + }; } function curateTags(tags) { - return Promise.all(tags.map(async tag => curateTag(tag))); + return Promise.all(tags.map(async tag => curateTag(tag))); } async function matchTags(rawTags) { - const filteredTags = rawTags.filter(Boolean); + const filteredTags = rawTags.filter(Boolean); - const tags = filteredTags - .concat(filteredTags.map(tag => tag.toLowerCase())) - .concat(filteredTags.map(tag => tag.toUpperCase())); + const tags = filteredTags + .concat(filteredTags.map(tag => tag.toLowerCase())) + .concat(filteredTags.map(tag => tag.toUpperCase())); - const tagEntries = await knex('tags') - .pluck('aliases.id') - .whereIn('tags.name', tags) - .leftJoin('tags as aliases', function join() { - this - .on('tags.alias_for', 'aliases.id') - .orOn('tags.id', 'aliases.id'); - }) - .where(function where() { - this - .whereNull('tags.alias_for') - .orWhereNull('aliases.alias_for'); - }) - .groupBy('aliases.id'); + const tagEntries = await knex('tags') + .pluck('aliases.id') + .whereIn('tags.name', tags) + .leftJoin('tags as aliases', function join() { + this + .on('tags.alias_for', 'aliases.id') + .orOn('tags.id', 'aliases.id'); + }) + .where(function where() { + this + .whereNull('tags.alias_for') + .orWhereNull('aliases.alias_for'); + }) + .groupBy('aliases.id'); - return tagEntries; + return tagEntries; } async function associateTags(release, releaseId) { - const siteTags = release.site?.tags?.filter(tag => tag.inherit === true).map(tag => tag.id) || []; + const siteTags = release.site?.tags?.filter(tag => tag.inherit === true).map(tag => tag.id) || []; - const rawReleaseTags = release.tags?.filter(Boolean) || []; - const releaseTags = rawReleaseTags.some(tag => typeof tag === 'string') - ? await matchTags(release.tags) // scraper returned raw tags - : rawReleaseTags; // tags already matched by (outdated) scraper + const rawReleaseTags = release.tags?.filter(Boolean) || []; + const releaseTags = rawReleaseTags.some(tag => typeof tag === 'string') + ? await matchTags(release.tags) // scraper returned raw tags + : rawReleaseTags; // tags already matched by (outdated) scraper - const tags = Array.from(new Set(releaseTags.concat(siteTags))); + const tags = Array.from(new Set(releaseTags.concat(siteTags))); - if (tags.length === 0) { - logger.info(`No tags available for (${release.site.name}, ${releaseId}) "${release.title}"`); - return; - } + if (tags.length === 0) { + logger.info(`No tags available for (${release.site.name}, ${releaseId}) "${release.title}"`); + return; + } - const associationEntries = await knex('releases_tags') - .where('release_id', releaseId) - .whereIn('tag_id', tags); + const associationEntries = await knex('releases_tags') + .where('release_id', releaseId) + .whereIn('tag_id', tags); - const existingAssociations = new Set(associationEntries.map(association => association.tag_id)); - const newAssociations = tags.filter(tagId => !existingAssociations.has(tagId)); + const existingAssociations = new Set(associationEntries.map(association => association.tag_id)); + const newAssociations = tags.filter(tagId => !existingAssociations.has(tagId)); - await knex('releases_tags').insert(newAssociations.map(tagId => ({ - tag_id: tagId, - release_id: releaseId, - }))); + await knex('releases_tags').insert(newAssociations.map(tagId => ({ + tag_id: tagId, + release_id: releaseId, + }))); } async function fetchTags(queryObject, groupsQueryObject, limit = 100) { - const tags = await knex('tags') - .where(builder => whereOr(queryObject, 'tags', builder)) - .orWhere(builder => whereOr(groupsQueryObject, 'tags_groups', builder)) - .andWhere({ 'tags.alias_for': null }) - .select( - 'tags.*', - 'tags_groups.id as group_id', 'tags_groups.name as group_name', 'tags_groups.slug as group_slug', 'tags_groups.description as groups_description', - ) - .leftJoin('tags_groups', 'tags.group_id', 'tags_groups.id') - .orderBy('name') - .limit(limit); + const tags = await knex('tags') + .where(builder => whereOr(queryObject, 'tags', builder)) + .orWhere(builder => whereOr(groupsQueryObject, 'tags_groups', builder)) + .andWhere({ 'tags.alias_for': null }) + .select( + 'tags.*', + 'tags_groups.id as group_id', 'tags_groups.name as group_name', 'tags_groups.slug as group_slug', 'tags_groups.description as groups_description', + ) + .leftJoin('tags_groups', 'tags.group_id', 'tags_groups.id') + .orderBy('name') + .limit(limit); - return curateTags(tags); + return curateTags(tags); } module.exports = { - associateTags, - fetchTags, - matchTags, + associateTags, + fetchTags, + matchTags, }; diff --git a/src/tags.js b/src/tags.js index 78333fdb..beba770e 100644 --- a/src/tags.js +++ b/src/tags.js @@ -4,104 +4,104 @@ const knex = require('./knex'); const slugify = require('./utils/slugify'); async function matchReleaseTags(releases) { - const rawTags = releases - .map(release => release.tags).flat() - .filter(Boolean); + const rawTags = releases + .map(release => release.tags).flat() + .filter(Boolean); - const casedTags = [...new Set( - rawTags - .concat(rawTags.map(tag => tag.toLowerCase())) - .concat(rawTags.map(tag => tag.toUpperCase())), - )]; + const casedTags = [...new Set( + rawTags + .concat(rawTags.map(tag => tag.toLowerCase())) + .concat(rawTags.map(tag => tag.toUpperCase())), + )]; - const tagEntries = await knex('tags') - .select('tags.id', 'tags.name', 'tags.alias_for') - .whereIn('tags.name', casedTags); + const tagEntries = await knex('tags') + .select('tags.id', 'tags.name', 'tags.alias_for') + .whereIn('tags.name', casedTags); - const tagIdsBySlug = tagEntries - .reduce((acc, tag) => ({ - ...acc, - [slugify(tag.name)]: tag.alias_for || tag.id, - }), {}); + const tagIdsBySlug = tagEntries + .reduce((acc, tag) => ({ + ...acc, + [slugify(tag.name)]: tag.alias_for || tag.id, + }), {}); - return tagIdsBySlug; + return tagIdsBySlug; } async function getSiteTags(releases) { - const siteIds = releases.map(release => release.site.id); - const siteTags = await knex('sites_tags').whereIn('site_id', siteIds); + const siteIds = releases.map(release => release.site.id); + const siteTags = await knex('sites_tags').whereIn('site_id', siteIds); - const siteTagIdsBySiteId = siteTags.reduce((acc, siteTag) => { - if (!acc[siteTag.site_id]) { - acc[siteTag.site_id] = []; - } + const siteTagIdsBySiteId = siteTags.reduce((acc, siteTag) => { + if (!acc[siteTag.site_id]) { + acc[siteTag.site_id] = []; + } - acc[siteTag.site_id].push(siteTag.tag_id); + acc[siteTag.site_id].push(siteTag.tag_id); - return acc; - }, {}); + return acc; + }, {}); - return siteTagIdsBySiteId; + return siteTagIdsBySiteId; } function buildReleaseTagAssociations(releases, tagIdsBySlug, siteTagIdsBySiteId) { - const tagAssociations = releases - .map((release) => { - const siteTagIds = siteTagIdsBySiteId[release.site.id]; - const releaseTags = release.tags || []; + const tagAssociations = releases + .map((release) => { + const siteTagIds = siteTagIdsBySiteId[release.site.id]; + const releaseTags = release.tags || []; - const releaseTagIds = releaseTags.every(tag => typeof tag === 'number') - ? releaseTags // obsolete scraper returned pre-matched tags - : releaseTags.map(tag => tagIdsBySlug[slugify(tag)]); + const releaseTagIds = releaseTags.every(tag => typeof tag === 'number') + ? releaseTags // obsolete scraper returned pre-matched tags + : releaseTags.map(tag => tagIdsBySlug[slugify(tag)]); - const tags = [...new Set( - // filter duplicates and empties - releaseTagIds - .concat(siteTagIds) - .filter(Boolean), - )] - .map(tagId => ({ - release_id: release.id, - tag_id: tagId, - })); + const tags = [...new Set( + // filter duplicates and empties + releaseTagIds + .concat(siteTagIds) + .filter(Boolean), + )] + .map(tagId => ({ + release_id: release.id, + tag_id: tagId, + })); - return tags; - }) - .flat(); + return tags; + }) + .flat(); - return tagAssociations; + return tagAssociations; } async function filterUniqueAssociations(tagAssociations) { - const duplicateAssociations = await knex('releases_tags') - .whereIn(['release_id', 'tag_id'], tagAssociations.map(association => [association.release_id, association.tag_id])); + const duplicateAssociations = await knex('releases_tags') + .whereIn(['release_id', 'tag_id'], tagAssociations.map(association => [association.release_id, association.tag_id])); - const duplicateAssociationsByReleaseIdAndTagId = duplicateAssociations.reduce((acc, association) => { - if (!acc[association.release_id]) { - acc[association.release_id] = {}; - } + const duplicateAssociationsByReleaseIdAndTagId = duplicateAssociations.reduce((acc, association) => { + if (!acc[association.release_id]) { + acc[association.release_id] = {}; + } - acc[association.release_id][association.tag_id] = true; + acc[association.release_id][association.tag_id] = true; - return acc; - }, {}); + return acc; + }, {}); - const uniqueAssociations = tagAssociations - .filter(association => !duplicateAssociationsByReleaseIdAndTagId[association.release_id]?.[association.tag_id]); + const uniqueAssociations = tagAssociations + .filter(association => !duplicateAssociationsByReleaseIdAndTagId[association.release_id]?.[association.tag_id]); - return uniqueAssociations; + return uniqueAssociations; } async function associateReleaseTags(releases) { - const tagIdsBySlug = await matchReleaseTags(releases); - const siteTagIdsBySiteId = await getSiteTags(releases); + const tagIdsBySlug = await matchReleaseTags(releases); + const siteTagIdsBySiteId = await getSiteTags(releases); - const tagAssociations = buildReleaseTagAssociations(releases, tagIdsBySlug, siteTagIdsBySiteId); - const uniqueAssociations = await filterUniqueAssociations(tagAssociations); + const tagAssociations = buildReleaseTagAssociations(releases, tagIdsBySlug, siteTagIdsBySiteId); + const uniqueAssociations = await filterUniqueAssociations(tagAssociations); - await knex('releases_tags').insert(uniqueAssociations); + await knex('releases_tags').insert(uniqueAssociations); } module.exports = { - associateReleaseTags, + associateReleaseTags, }; diff --git a/src/updates.js b/src/updates.js index 240f5d31..fe1007ed 100644 --- a/src/updates.js +++ b/src/updates.js @@ -11,228 +11,228 @@ const scrapers = require('./scrapers/scrapers'); const { fetchSitesFromArgv, fetchSitesFromConfig } = require('./sites'); const afterDate = (() => { - if (/\d{2,4}-\d{2}-\d{2,4}/.test(argv.after)) { - // using date - return moment - .utc(argv.after, ['YYYY-MM-DD', 'DD-MM-YYYY']) - .toDate(); - } + if (/\d{2,4}-\d{2}-\d{2,4}/.test(argv.after)) { + // using date + return moment + .utc(argv.after, ['YYYY-MM-DD', 'DD-MM-YYYY']) + .toDate(); + } - // using time distance (e.g. "1 month") - return moment - .utc() - .subtract(...argv.after.split(' ')) - .toDate(); + // using time distance (e.g. "1 month") + return moment + .utc() + .subtract(...argv.after.split(' ')) + .toDate(); })(); async function filterUniqueReleases(latestReleases, accReleases) { - const latestReleaseIdentifiers = latestReleases - .map(release => [release.site.id, release.entryId]); + const latestReleaseIdentifiers = latestReleases + .map(release => [release.site.id, release.entryId]); - const duplicateReleases = await knex('releases') - .whereIn(['site_id', 'entry_id'], latestReleaseIdentifiers); + const duplicateReleases = await knex('releases') + .whereIn(['site_id', 'entry_id'], latestReleaseIdentifiers); - // add entry IDs of accumulated releases to prevent an infinite scrape loop - // when one page contains the same release as the previous - const duplicateReleasesSiteIdAndEntryIds = duplicateReleases - .concat(accReleases) - .reduce((acc, release) => { - const siteId = release.site_id || release.site.id; - const entryId = release.entry_id || release.entryId; + // add entry IDs of accumulated releases to prevent an infinite scrape loop + // when one page contains the same release as the previous + const duplicateReleasesSiteIdAndEntryIds = duplicateReleases + .concat(accReleases) + .reduce((acc, release) => { + const siteId = release.site_id || release.site.id; + const entryId = release.entry_id || release.entryId; - if (!acc[siteId]) acc[siteId] = {}; - acc[siteId][entryId] = true; + if (!acc[siteId]) acc[siteId] = {}; + acc[siteId][entryId] = true; - return acc; - }, {}); + return acc; + }, {}); - const uniqueReleases = latestReleases - .filter(release => !duplicateReleasesSiteIdAndEntryIds[release.site.id]?.[release.entryId]); + const uniqueReleases = latestReleases + .filter(release => !duplicateReleasesSiteIdAndEntryIds[release.site.id]?.[release.entryId]); - return uniqueReleases; + return uniqueReleases; } function needNextPage(uniqueReleases, pageAccReleases) { - if (uniqueReleases.length === 0) { - return false; - } + if (uniqueReleases.length === 0) { + return false; + } - if (argv.last && pageAccReleases.length < argv.last) { - // request for last N releases not yet satisfied - return true; - } + if (argv.last && pageAccReleases.length < argv.last) { + // request for last N releases not yet satisfied + return true; + } - if (uniqueReleases.every(release => !!release.date)) { - const oldestReleaseOnPage = uniqueReleases - .sort((releaseA, releaseB) => releaseB.date - releaseA.date) - .slice(-1)[0]; + if (uniqueReleases.every(release => !!release.date)) { + const oldestReleaseOnPage = uniqueReleases + .sort((releaseA, releaseB) => releaseB.date - releaseA.date) + .slice(-1)[0]; - if (moment(oldestReleaseOnPage.date).isAfter(afterDate)) { - // oldest release on page is newer than the specified date cut-off - return true; - } - } + if (moment(oldestReleaseOnPage.date).isAfter(afterDate)) { + // oldest release on page is newer than the specified date cut-off + return true; + } + } - // dates missing, and limit for scenes without dates not yet reached - return pageAccReleases.length <= argv.nullDateLimit; + // dates missing, and limit for scenes without dates not yet reached + return pageAccReleases.length <= argv.nullDateLimit; } async function scrapeReleases(scraper, site, preData, upcoming = false) { - const scrapePage = async (page = 1, accReleases = []) => { - const latestReleases = upcoming - ? await scraper.fetchUpcoming(site, page, preData, include) - : await scraper.fetchLatest(site, page, preData, include); + const scrapePage = async (page = 1, accReleases = []) => { + const latestReleases = upcoming + ? await scraper.fetchUpcoming(site, page, preData, include) + : await scraper.fetchLatest(site, page, preData, include); - if (!Array.isArray(latestReleases)) { - // scraper is unable to fetch the releases and returned a HTTP code or null - logger.warn(`Scraper returned ${latestReleases} when fetching latest from '${site.name}' (${site.network.name})`); - return accReleases; - } + if (!Array.isArray(latestReleases)) { + // scraper is unable to fetch the releases and returned a HTTP code or null + logger.warn(`Scraper returned ${latestReleases} when fetching latest from '${site.name}' (${site.network.name})`); + return accReleases; + } - if (latestReleases.length === 0) { - // scraper successfully requested releases, but found none - return accReleases; - } + if (latestReleases.length === 0) { + // scraper successfully requested releases, but found none + return accReleases; + } - const latestReleasesWithSite = latestReleases.map(release => ({ ...release, site: release.site || site })); // attach site release is assigned to when stored + const latestReleasesWithSite = latestReleases.map(release => ({ ...release, site: release.site || site })); // attach site release is assigned to when stored - const uniqueReleases = argv.redownload - ? latestReleasesWithSite - : await filterUniqueReleases(latestReleasesWithSite, accReleases); + const uniqueReleases = argv.redownload + ? latestReleasesWithSite + : await filterUniqueReleases(latestReleasesWithSite, accReleases); - const pageAccReleases = accReleases.concat(uniqueReleases); + const pageAccReleases = accReleases.concat(uniqueReleases); - logger.verbose(`Scraped '${site.name}' (${site.network.name}) ${upcoming ? 'upcoming' : 'latest'} page ${page}, found ${uniqueReleases.length} unique updates`); + logger.verbose(`Scraped '${site.name}' (${site.network.name}) ${upcoming ? 'upcoming' : 'latest'} page ${page}, found ${uniqueReleases.length} unique updates`); - if (needNextPage(uniqueReleases, pageAccReleases)) { - return scrapePage(page + 1, pageAccReleases); - } + if (needNextPage(uniqueReleases, pageAccReleases)) { + return scrapePage(page + 1, pageAccReleases); + } - return pageAccReleases; - }; + return pageAccReleases; + }; - const rawReleases = await scrapePage(argv.page || 1, []); - const releases = upcoming - ? rawReleases.map(rawRelease => ({ ...rawRelease, upcoming: true })) - : rawReleases; + const rawReleases = await scrapePage(argv.page || 1, []); + const releases = upcoming + ? rawReleases.map(rawRelease => ({ ...rawRelease, upcoming: true })) + : rawReleases; - if (argv.last) { - return releases.slice(0, argv.last); - } + if (argv.last) { + return releases.slice(0, argv.last); + } - if (releases.every(release => release.date)) { - return releases.filter(release => moment(release.date).isAfter(afterDate)); - } + if (releases.every(release => release.date)) { + return releases.filter(release => moment(release.date).isAfter(afterDate)); + } - return releases.slice(0, argv.nullDateLimit); + return releases.slice(0, argv.nullDateLimit); } async function scrapeLatestReleases(scraper, site, preData) { - if (!scraper.fetchLatest) { - return []; - } + if (!scraper.fetchLatest) { + return []; + } - try { - return await scrapeReleases(scraper, site, preData, false); - } catch (error) { - logger.warn(`Failed to scrape latest updates for '${site.slug}' (${site.network.slug}): ${error.message}`); - } + try { + return await scrapeReleases(scraper, site, preData, false); + } catch (error) { + logger.warn(`Failed to scrape latest updates for '${site.slug}' (${site.network.slug}): ${error.message}`); + } - return []; + return []; } async function scrapeUpcomingReleases(scraper, site, preData) { - if (!scraper.fetchUpcoming) { - return []; - } + if (!scraper.fetchUpcoming) { + return []; + } - try { - return await scrapeReleases(scraper, site, preData, true); - } catch (error) { - logger.warn(`Failed to scrape upcoming updates for '${site.slug}' (${site.network.slug}): ${error.message}`); - } + try { + return await scrapeReleases(scraper, site, preData, true); + } catch (error) { + logger.warn(`Failed to scrape upcoming updates for '${site.slug}' (${site.network.slug}): ${error.message}`); + } - return []; + return []; } async function scrapeSiteReleases(scraper, site, preData) { - const [latestReleases, upcomingReleases] = await Promise.all([ - argv.latest - ? scrapeLatestReleases(scraper, site, preData) - : [], - argv.upcoming - ? scrapeUpcomingReleases(scraper, site, preData) - : [], - ]); + const [latestReleases, upcomingReleases] = await Promise.all([ + argv.latest + ? scrapeLatestReleases(scraper, site, preData) + : [], + argv.upcoming + ? scrapeUpcomingReleases(scraper, site, preData) + : [], + ]); - logger.info(`Fetching ${latestReleases.length} latest and ${upcomingReleases.length} upcoming updates for '${site.name}' (${site.network.name})`); + logger.info(`Fetching ${latestReleases.length} latest and ${upcomingReleases.length} upcoming updates for '${site.name}' (${site.network.name})`); - return [...latestReleases, ...upcomingReleases]; + return [...latestReleases, ...upcomingReleases]; } async function scrapeSite(site, accSiteReleases) { - const scraper = scrapers.releases[site.slug] + const scraper = scrapers.releases[site.slug] || scrapers.releases[site.network.slug] || scrapers.releases[site.network.parent?.slug]; - if (!scraper) { - logger.warn(`No scraper found for '${site.name}' (${site.network.name})`); - return []; - } + if (!scraper) { + logger.warn(`No scraper found for '${site.name}' (${site.network.name})`); + return []; + } - try { - const beforeFetchLatest = await scraper.beforeFetchLatest?.(site); + try { + const beforeFetchLatest = await scraper.beforeFetchLatest?.(site); - const siteReleases = await scrapeSiteReleases(scraper, site, { - accSiteReleases, - beforeFetchLatest, - }); + const siteReleases = await scrapeSiteReleases(scraper, site, { + accSiteReleases, + beforeFetchLatest, + }); - return siteReleases.map(release => ({ ...release, site })); - } catch (error) { - logger.error(`Failed to scrape releases from ${site.name} using ${scraper.slug}: ${error.message}`); + return siteReleases.map(release => ({ ...release, site })); + } catch (error) { + logger.error(`Failed to scrape releases from ${site.name} using ${scraper.slug}: ${error.message}`); - return []; - } + return []; + } } async function scrapeNetworkSequential(network) { - return Promise.reduce( - network.sites, - async (chain, site) => { - const accSiteReleases = await chain; - const siteReleases = await scrapeSite(site, network, accSiteReleases); + return Promise.reduce( + network.sites, + async (chain, site) => { + const accSiteReleases = await chain; + const siteReleases = await scrapeSite(site, network, accSiteReleases); - return accSiteReleases.concat(siteReleases); - }, - Promise.resolve([]), - ); + return accSiteReleases.concat(siteReleases); + }, + Promise.resolve([]), + ); } async function scrapeNetworkParallel(network) { - return Promise.map( - network.sites, - async site => scrapeSite(site, network), - { concurrency: 3 }, - ); + return Promise.map( + network.sites, + async site => scrapeSite(site, network), + { concurrency: 3 }, + ); } async function fetchUpdates() { - const includedNetworks = argv.sites || argv.networks - ? await fetchSitesFromArgv() - : await fetchSitesFromConfig(); + const includedNetworks = argv.sites || argv.networks + ? await fetchSitesFromArgv() + : await fetchSitesFromConfig(); - const scrapedNetworks = await Promise.map( - includedNetworks, - async network => (network.parameters?.sequential - ? scrapeNetworkSequential(network) - : scrapeNetworkParallel(network)), - { concurrency: 5 }, - ); + const scrapedNetworks = await Promise.map( + includedNetworks, + async network => (network.parameters?.sequential + ? scrapeNetworkSequential(network) + : scrapeNetworkParallel(network)), + { concurrency: 5 }, + ); - const releases = scrapedNetworks.flat(2); + const releases = scrapedNetworks.flat(2); - return releases; + return releases; } module.exports = fetchUpdates; diff --git a/src/utils/argv-include.js b/src/utils/argv-include.js index 65935932..a955bec6 100644 --- a/src/utils/argv-include.js +++ b/src/utils/argv-include.js @@ -1,20 +1,20 @@ 'use strict'; function include(argv) { - return { - covers: argv.media && argv.covers, - media: argv.media, - photos: argv.media && argv.photos, - poster: argv.media && argv.posters, - posters: argv.media && argv.posters, - releases: argv.withReleases, - scenes: argv.withReleases, - teaser: argv.media && argv.videos && argv.teasers, - teasers: argv.media && argv.videos && argv.teasers, - trailer: argv.media && argv.videos && argv.trailers, - trailers: argv.media && argv.videos && argv.trailers, - videos: argv.videos, - }; + return { + covers: argv.media && argv.covers, + media: argv.media, + photos: argv.media && argv.photos, + poster: argv.media && argv.posters, + posters: argv.media && argv.posters, + releases: argv.withReleases, + scenes: argv.withReleases, + teaser: argv.media && argv.videos && argv.teasers, + teasers: argv.media && argv.videos && argv.teasers, + trailer: argv.media && argv.videos && argv.trailers, + trailers: argv.media && argv.videos && argv.trailers, + videos: argv.videos, + }; } module.exports = include; diff --git a/src/utils/buffer.js b/src/utils/buffer.js index bdaa84cd..67af934f 100644 --- a/src/utils/buffer.js +++ b/src/utils/buffer.js @@ -13,106 +13,106 @@ const file = 'https://speed.hetzner.de/100MB.bin'; // const file = 'https://speed.hetzner.de/10GB.bin'; function getMemoryUsage() { - return process.memoryUsage().rss / (10 ** 6); + return process.memoryUsage().rss / (10 ** 6); } const stats = { - peakMemoryUsage: getMemoryUsage(), - done: false, - downloads: {}, + peakMemoryUsage: getMemoryUsage(), + done: false, + downloads: {}, }; function render() { - const downloads = Object.entries(stats.downloads); + const downloads = Object.entries(stats.downloads); - process.stdout.clearScreenDown(); + process.stdout.clearScreenDown(); - process.stdout.write(`peak memory: ${stats.peakMemoryUsage.toFixed(2)} MB\n`); + process.stdout.write(`peak memory: ${stats.peakMemoryUsage.toFixed(2)} MB\n`); - downloads.forEach(([download, progress]) => { - process.stdout.write(`${download}: ${progress}${typeof progress === 'string' ? '' : '%'}\n`); - }); + downloads.forEach(([download, progress]) => { + process.stdout.write(`${download}: ${progress}${typeof progress === 'string' ? '' : '%'}\n`); + }); - process.stdout.moveCursor(0, -(downloads.length + 1)); - process.stdout.cursorTo(0); + process.stdout.moveCursor(0, -(downloads.length + 1)); + process.stdout.cursorTo(0); - if (downloads.length === 0 || !downloads.every(([_label, download]) => typeof download === 'string')) { - setTimeout(() => render(), 1000); - return; - } + if (downloads.length === 0 || !downloads.every(([_label, download]) => typeof download === 'string')) { + setTimeout(() => render(), 1000); + return; + } - process.stdout.moveCursor(0, downloads.length + 1); + process.stdout.moveCursor(0, downloads.length + 1); } function setProgress(label, completedBytes, totalBytes, hash) { - const memory = getMemoryUsage(); + const memory = getMemoryUsage(); - stats.peakMemoryUsage = Math.max(memory, stats.peakMemoryUsage); - stats.downloads[label] = hash || Math.round((completedBytes / totalBytes) * 100); + stats.peakMemoryUsage = Math.max(memory, stats.peakMemoryUsage); + stats.downloads[label] = hash || Math.round((completedBytes / totalBytes) * 100); } async function buffered(label) { - const hash = new blake2.Hash('blake2b'); + const hash = new blake2.Hash('blake2b'); - const imageRes = await bhttp.get(file, { - onDownloadProgress(completedBytes, totalBytes) { - setProgress(label, completedBytes, totalBytes); - }, - }); + const imageRes = await bhttp.get(file, { + onDownloadProgress(completedBytes, totalBytes) { + setProgress(label, completedBytes, totalBytes); + }, + }); - hash.update(imageRes.body); - setProgress(label, null, null, hash.digest('hex')); + hash.update(imageRes.body); + setProgress(label, null, null, hash.digest('hex')); - await fsPromises.writeFile(`/mnt/stor/Pictures/traxxx/temp/buffered-${label}.bin`, imageRes.body); + await fsPromises.writeFile(`/mnt/stor/Pictures/traxxx/temp/buffered-${label}.bin`, imageRes.body); } async function streamed(label) { - const hash = new blake2.Hash('blake2b'); - hash.setEncoding('hex'); + const hash = new blake2.Hash('blake2b'); + hash.setEncoding('hex'); - const hashStream = new PassThrough(); - const targetStream = fs.createWriteStream(`/mnt/stor/Pictures/traxxx/temp/streamed-${label}.bin`); + const hashStream = new PassThrough(); + const targetStream = fs.createWriteStream(`/mnt/stor/Pictures/traxxx/temp/streamed-${label}.bin`); - const imageRes = await bhttp.get(file, { - stream: true, - }); + const imageRes = await bhttp.get(file, { + stream: true, + }); - const stream = imageRes - .pipe(hashStream) - .pipe(targetStream); + const stream = imageRes + .pipe(hashStream) + .pipe(targetStream); - imageRes.on('progress', (completedBytes, totalBytes) => { - setProgress(label, completedBytes, totalBytes); - }); + imageRes.on('progress', (completedBytes, totalBytes) => { + setProgress(label, completedBytes, totalBytes); + }); - hashStream.on('data', (chunk) => { - hash.write(chunk); - }); + hashStream.on('data', (chunk) => { + hash.write(chunk); + }); - stream.on('finish', () => { - hash.end(); - setProgress(label, null, null, hash.read()); - }); + stream.on('finish', () => { + hash.end(); + setProgress(label, null, null, hash.read()); + }); } async function init() { - const n = argv.n || 1; + const n = argv.n || 1; - if (argv._.includes('stream')) { - console.log('using streams'); - render(); + if (argv._.includes('stream')) { + console.log('using streams'); + render(); - await Promise.map(Array.from({ length: n }), async (value, index) => streamed(index + 1)); + await Promise.map(Array.from({ length: n }), async (value, index) => streamed(index + 1)); - return; - } + return; + } - if (argv._.includes('buffer')) { - console.log('using buffers'); - render(); + if (argv._.includes('buffer')) { + console.log('using buffers'); + render(); - await Promise.map(Array.from({ length: n }), async (value, index) => buffered(index + 1)); - } + await Promise.map(Array.from({ length: n }), async (value, index) => buffered(index + 1)); + } } init(); diff --git a/src/utils/capitalize.js b/src/utils/capitalize.js index c51655cf..11eda33e 100644 --- a/src/utils/capitalize.js +++ b/src/utils/capitalize.js @@ -1,16 +1,16 @@ 'use strict'; function capitalize(string, trim = true) { - if (!string) { - return ''; - } + if (!string) { + return ''; + } - const capitalized = string - .split(/\s+/) - .map(component => `${component.charAt(0).toUpperCase()}${component.slice(1)}`) - .join(' '); + const capitalized = string + .split(/\s+/) + .map(component => `${component.charAt(0).toUpperCase()}${component.slice(1)}`) + .join(' '); - return trim ? capitalized.trim() : capitalized; + return trim ? capitalized.trim() : capitalized; } module.exports = capitalize; diff --git a/src/utils/chunk.js b/src/utils/chunk.js index 32f1d923..9ed28b1d 100644 --- a/src/utils/chunk.js +++ b/src/utils/chunk.js @@ -1,8 +1,8 @@ 'use strict'; function chunk(array, chunkSize) { - return Array.from({ length: Math.ceil(array.length / chunkSize) }) - .map((value, index) => array.slice(index * chunkSize, (index * chunkSize) + chunkSize)); + return Array.from({ length: Math.ceil(array.length / chunkSize) }) + .map((value, index) => array.slice(index * chunkSize, (index * chunkSize) + chunkSize)); } module.exports = chunk; diff --git a/src/utils/convert.js b/src/utils/convert.js index d8ec9ee4..d90dd5c3 100644 --- a/src/utils/convert.js +++ b/src/utils/convert.js @@ -1,48 +1,48 @@ 'use strict'; function inchesToCm(inches) { - return Math.round(Number(inches) * 2.54); + return Math.round(Number(inches) * 2.54); } function feetInchesToCm(feet, inches) { - if (typeof feet === 'string' && !inches) { - const [feetPart, inchesPart] = feet.match(/\d+/g); - return feetInchesToCm(feetPart, inchesPart); - } + if (typeof feet === 'string' && !inches) { + const [feetPart, inchesPart] = feet.match(/\d+/g); + return feetInchesToCm(feetPart, inchesPart); + } - return Math.round((Number(feet) * 30.48) + (Number(inches) * 2.54)); + return Math.round((Number(feet) * 30.48) + (Number(inches) * 2.54)); } function cmToFeetInches(centimeters) { - const feet = Math.floor(centimeters / 30.48); - const inches = Math.round((centimeters / 2.54) % (feet * 12)); + const feet = Math.floor(centimeters / 30.48); + const inches = Math.round((centimeters / 2.54) % (feet * 12)); - return { feet, inches }; + return { feet, inches }; } function heightToCm(height) { - const [feet, inches] = height.match(/\d+/g); + const [feet, inches] = height.match(/\d+/g); - return feetInchesToCm(feet, inches); + return feetInchesToCm(feet, inches); } function lbsToKg(lbs) { - const pounds = lbs.toString().match(/\d+/)[0]; + const pounds = lbs.toString().match(/\d+/)[0]; - return Math.round(Number(pounds) * 0.453592); + return Math.round(Number(pounds) * 0.453592); } function kgToLbs(kgs) { - const kilos = kgs.toString().match(/\d+/)[0]; + const kilos = kgs.toString().match(/\d+/)[0]; - return Math.round(Number(kilos) / 0.453592); + return Math.round(Number(kilos) / 0.453592); } module.exports = { - cmToFeetInches, - feetInchesToCm, - heightToCm, - inchesToCm, - lbsToKg, - kgToLbs, + cmToFeetInches, + feetInchesToCm, + heightToCm, + inchesToCm, + lbsToKg, + kgToLbs, }; diff --git a/src/utils/cookies.js b/src/utils/cookies.js index 9fad54fd..d14e28e6 100644 --- a/src/utils/cookies.js +++ b/src/utils/cookies.js @@ -1,16 +1,16 @@ 'use strict'; function cookieToData(cookieString) { - return cookieString.split('; ').reduce((acc, cookie) => { - const [key, value] = cookie.split('='); + return cookieString.split('; ').reduce((acc, cookie) => { + const [key, value] = cookie.split('='); - return { - ...acc, - [key]: value, - }; - }, {}); + return { + ...acc, + [key]: value, + }; + }, {}); } module.exports = { - cookieToData, + cookieToData, }; diff --git a/src/utils/escape-html.js b/src/utils/escape-html.js index 845f3864..0b66ffaa 100644 --- a/src/utils/escape-html.js +++ b/src/utils/escape-html.js @@ -1,10 +1,10 @@ function escapeHtml(text) { - return text - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); } module.exports = escapeHtml; diff --git a/src/utils/http.js b/src/utils/http.js index 47afd59e..226de71b 100644 --- a/src/utils/http.js +++ b/src/utils/http.js @@ -11,107 +11,107 @@ const pipeline = util.promisify(stream.pipeline); const logger = require('../logger')(__filename); const defaultHeaders = { - 'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1', + 'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1', }; const defaultOptions = { - responseTimeout: 30000, + responseTimeout: 30000, }; const proxyAgent = tunnel.httpsOverHttp({ - proxy: { - host: config.proxy.host, - port: config.proxy.port, - }, + proxy: { + host: config.proxy.host, + port: config.proxy.port, + }, }); function useProxy(url) { - if (!config.proxy.enable) { - return false; - } + if (!config.proxy.enable) { + return false; + } - const { hostname } = new URL(url); - return config.proxy.hostnames.includes(hostname); + const { hostname } = new URL(url); + return config.proxy.hostnames.includes(hostname); } const queue = taskQueue(); queue.on('concurrencyReached:http', () => { - logger.silly('Queueing requests'); + logger.silly('Queueing requests'); }); queue.define('http', async ({ - url, - method = 'GET', - body, - headers = {}, - options = {}, + url, + method = 'GET', + body, + headers = {}, + options = {}, }) => { - if (body) { - logger.silly(`${method.toUpperCase()} ${url} with ${JSON.stringify(body)}`); - } else { - logger.silly(`${method.toUpperCase()} ${url}`); - } + if (body) { + logger.silly(`${method.toUpperCase()} ${url} with ${JSON.stringify(body)}`); + } else { + logger.silly(`${method.toUpperCase()} ${url}`); + } - const reqOptions = { - headers: { - ...(options.defaultHeaders !== false && defaultHeaders), - ...headers, - }, - ...defaultOptions, - ...options, - ...(options.timeout && { responseTimeout: options.timeout }), - }; + const reqOptions = { + headers: { + ...(options.defaultHeaders !== false && defaultHeaders), + ...headers, + }, + ...defaultOptions, + ...options, + ...(options.timeout && { responseTimeout: options.timeout }), + }; - if (useProxy(url)) { - reqOptions.agent = proxyAgent; - } + if (useProxy(url)) { + reqOptions.agent = proxyAgent; + } - const res = ['POST', 'PUT', 'PATCH'].includes(method.toUpperCase()) - ? await bhttp[method.toLowerCase()](url, body, reqOptions) - : await bhttp[method.toLowerCase()](url, reqOptions); + const res = ['POST', 'PUT', 'PATCH'].includes(method.toUpperCase()) + ? await bhttp[method.toLowerCase()](url, body, reqOptions) + : await bhttp[method.toLowerCase()](url, reqOptions); - if (options.stream && options.destination) { - await pipeline(res, ...(options.transforms || []), options.destination); - } + if (options.stream && options.destination) { + await pipeline(res, ...(options.transforms || []), options.destination); + } - const html = Buffer.isBuffer(res.body) ? res.body.toString() : null; - const json = Buffer.isBuffer(res.body) ? null : res.body; + const html = Buffer.isBuffer(res.body) ? res.body.toString() : null; + const json = Buffer.isBuffer(res.body) ? null : res.body; - return { - ...res, - originalRes: res, - html, - json, - pipe: res.pipe, - ok: res.statusCode >= 200 && res.statusCode <= 299, - code: res.statusCode, - status: res.statusCode, - }; + return { + ...res, + originalRes: res, + html, + json, + pipe: res.pipe, + ok: res.statusCode >= 200 && res.statusCode <= 299, + code: res.statusCode, + status: res.statusCode, + }; }, { - concurrency: 20, + concurrency: 20, }); async function get(url, headers, options) { - return queue.push('http', { - method: 'GET', - url, - headers, - options, - }); + return queue.push('http', { + method: 'GET', + url, + headers, + options, + }); } async function post(url, body, headers, options) { - return queue.push('http', { - method: 'POST', - url, - body, - headers, - options, - }); + return queue.push('http', { + method: 'POST', + url, + body, + headers, + options, + }); } module.exports = { - get, - post, + get, + post, }; diff --git a/src/utils/img.js b/src/utils/img.js index d574a617..59552b5e 100644 --- a/src/utils/img.js +++ b/src/utils/img.js @@ -7,12 +7,12 @@ const { argv } = require('yargs'); const url = argv.url || 'http://localhost:5000/media/actors/tommy-pistol/1580341442712.jpeg'; async function scan() { - console.log(url); + console.log(url); - const res = await bhttp.get(url); - const stats = await sharp(res.body).stats(); + const res = await bhttp.get(url); + const stats = await sharp(res.body).stats(); - console.log(stats); + console.log(stats); } scan(); diff --git a/src/utils/list.js b/src/utils/list.js index d847cef1..fa7fc55f 100644 --- a/src/utils/list.js +++ b/src/utils/list.js @@ -4,33 +4,33 @@ const Promise = require('bluebird'); const knex = require('../knex'); async function listSites() { - const [networks, allSites] = await Promise.all([ - knex('networks').orderBy('name'), - knex('sites').orderBy('name'), - ]); + const [networks, allSites] = await Promise.all([ + knex('networks').orderBy('name'), + knex('sites').orderBy('name'), + ]); - await Promise.each(networks, async (network) => { - console.log(`* **${network.name}**`); + await Promise.each(networks, async (network) => { + console.log(`* **${network.name}**`); - const sites = await knex('sites') - .where({ network_id: network.id }) - .orderBy('name'); + const sites = await knex('sites') + .where({ network_id: network.id }) + .orderBy('name'); - if (sites.length === 1 && sites[0].name === network.name) { - return; - } + if (sites.length === 1 && sites[0].name === network.name) { + return; + } - sites.forEach((site) => { - const rkSpecial = network.id === 'realitykings' + sites.forEach((site) => { + const rkSpecial = network.id === 'realitykings' && (new URL(site.url).hostname === 'www.realitykings.com' || (site.parameters?.altLayout)) - ? '\\*' : ''; // Reality Kings alt layout sites do not support scene fetch by URL + ? '\\*' : ''; // Reality Kings alt layout sites do not support scene fetch by URL - console.log(` * ${site.name}${rkSpecial}`); - }); - }); + console.log(` * ${site.name}${rkSpecial}`); + }); + }); - console.log(`${networks.length} networks with ${allSites.length} sites total`); + console.log(`${networks.length} networks with ${allSites.length} sites total`); } listSites(); diff --git a/src/utils/media.js b/src/utils/media.js index 2b73cc23..4a3fb7e7 100644 --- a/src/utils/media.js +++ b/src/utils/media.js @@ -12,99 +12,99 @@ const { PassThrough } = require('stream'); const http = require('./http'); function getMemoryUsage() { - return process.memoryUsage().rss / (10 ** 6); + return process.memoryUsage().rss / (10 ** 6); } let peakMemoryUsage = getMemoryUsage(); async function fetchSource(link) { - const id = nanoid(); + const id = nanoid(); - const hasher = new blake2.Hash('blake2b'); - hasher.setEncoding('hex'); + const hasher = new blake2.Hash('blake2b'); + hasher.setEncoding('hex'); - const tempFilePath = `/home/niels/Pictures/thumbs/temp/${id}.jpeg`; - const tempFileStream = fs.createWriteStream(tempFilePath); - const hashStream = new PassThrough(); + const tempFilePath = `/home/niels/Pictures/thumbs/temp/${id}.jpeg`; + const tempFileStream = fs.createWriteStream(tempFilePath); + const hashStream = new PassThrough(); - hashStream.on('data', chunk => hasher.write(chunk)); + hashStream.on('data', chunk => hasher.write(chunk)); - try { - const res = await http.get(link, null, { - stream: true, - transforms: [hashStream], - destination: tempFileStream, - timeout: 5000, - }); + try { + const res = await http.get(link, null, { + stream: true, + transforms: [hashStream], + destination: tempFileStream, + timeout: 5000, + }); - if (!res.ok) { - throw new Error(res.status); - } + if (!res.ok) { + throw new Error(res.status); + } - hasher.end(); - const hash = hasher.read(); + hasher.end(); + const hash = hasher.read(); - const memoryUsage = getMemoryUsage(); - peakMemoryUsage = Math.max(memoryUsage, peakMemoryUsage); + const memoryUsage = getMemoryUsage(); + peakMemoryUsage = Math.max(memoryUsage, peakMemoryUsage); - console.log(`Stored ${tempFilePath}, memory usage: ${memoryUsage.toFixed(2)} MB`); + console.log(`Stored ${tempFilePath}, memory usage: ${memoryUsage.toFixed(2)} MB`); - return { - id, - path: tempFilePath, - hash, - }; - } catch (error) { - await fsPromises.unlink(tempFilePath); + return { + id, + path: tempFilePath, + hash, + }; + } catch (error) { + await fsPromises.unlink(tempFilePath); - throw error; - } + throw error; + } } async function init() { - const linksFile = await fsPromises.readFile('/home/niels/Pictures/photos', 'utf8'); - const links = linksFile.split('\n').filter(Boolean); + const linksFile = await fsPromises.readFile('/home/niels/Pictures/photos', 'utf8'); + const links = linksFile.split('\n').filter(Boolean); - await fsPromises.mkdir('/home/niels/Pictures/thumbs/temp', { recursive: true }); + await fsPromises.mkdir('/home/niels/Pictures/thumbs/temp', { recursive: true }); - console.time('thumbs'); + console.time('thumbs'); - const files = await Promise.map(links, async (link) => { - try { - return await fetchSource(link); - } catch (error) { - console.log(`Failed to fetch ${link}: ${error.message}`); - return null; - } - }); + const files = await Promise.map(links, async (link) => { + try { + return await fetchSource(link); + } catch (error) { + console.log(`Failed to fetch ${link}: ${error.message}`); + return null; + } + }); - await Promise.map(files.filter(Boolean), async (file) => { - const image = sharp(file.path).jpeg(); + await Promise.map(files.filter(Boolean), async (file) => { + const image = sharp(file.path).jpeg(); - const [{ width, height }, { size }] = await Promise.all([ - image.metadata(), - fsPromises.stat(file.path), - ]); + const [{ width, height }, { size }] = await Promise.all([ + image.metadata(), + fsPromises.stat(file.path), + ]); - await Promise.all([ - image - .toFile(`/home/niels/Pictures/thumbs/${file.hash}.jpeg`), - image - .resize({ - height: config.media.thumbnailSize, - withoutEnlargement: true, - }) - .toFile(`/home/niels/Pictures/thumbs/${file.hash}_thumb.jpeg`), - ]); + await Promise.all([ + image + .toFile(`/home/niels/Pictures/thumbs/${file.hash}.jpeg`), + image + .resize({ + height: config.media.thumbnailSize, + withoutEnlargement: true, + }) + .toFile(`/home/niels/Pictures/thumbs/${file.hash}_thumb.jpeg`), + ]); - const memoryUsage = getMemoryUsage(); - peakMemoryUsage = Math.max(memoryUsage, peakMemoryUsage); + const memoryUsage = getMemoryUsage(); + peakMemoryUsage = Math.max(memoryUsage, peakMemoryUsage); - console.log(`Resized ${file.id} (${width}, ${height}, ${size}), memory usage: ${memoryUsage.toFixed(2)} MB`); - }, { concurrency: 10 }); + console.log(`Resized ${file.id} (${width}, ${height}, ${size}), memory usage: ${memoryUsage.toFixed(2)} MB`); + }, { concurrency: 10 }); - console.log(`Peak memory usage: ${peakMemoryUsage.toFixed(2)} MB`); - console.timeEnd('thumbs'); + console.log(`Peak memory usage: ${peakMemoryUsage.toFixed(2)} MB`); + console.timeEnd('thumbs'); } init(); diff --git a/src/utils/mofos.js b/src/utils/mofos.js index c086cc51..6324c73c 100644 --- a/src/utils/mofos.js +++ b/src/utils/mofos.js @@ -6,16 +6,16 @@ const bhttp = require('bhttp'); const knex = require('../knex'); async function run() { - const network = await knex('networks').where('slug', 'mofos').first(); - const sites = await knex('sites').where('network_id', network.id); + const network = await knex('networks').where('slug', 'mofos').first(); + const sites = await knex('sites').where('network_id', network.id); - await Promise.map(sites, async (site) => { - const res = await bhttp.get(site.url); + await Promise.map(sites, async (site) => { + const res = await bhttp.get(site.url); - console.log(site.url, res.statusCode); - }, { - concurrency: 5, - }); + console.log(site.url, res.statusCode); + }, { + concurrency: 5, + }); } run(); diff --git a/src/utils/pick-random.js b/src/utils/pick-random.js index 729d5c51..1e8fb853 100644 --- a/src/utils/pick-random.js +++ b/src/utils/pick-random.js @@ -1,5 +1,5 @@ function pickRandom(array) { - return array[Math.floor(Math.random() * array.length)]; + return array[Math.floor(Math.random() * array.length)]; } module.exports = pickRandom; diff --git a/src/utils/posters.js b/src/utils/posters.js index d30d6d95..541aec0f 100644 --- a/src/utils/posters.js +++ b/src/utils/posters.js @@ -9,32 +9,32 @@ const argv = require('../argv'); const knex = require('../knex'); async function init() { - const posters = await knex('actors') - .select('actors.name as actor_name', 'releases.title', 'releases.date', 'media.path', 'media.index', 'sites.name as site_name', 'networks.name as network_name') - .whereIn('actors.name', (argv.actors || []).concat(argv._)) - .join('releases_actors', 'releases_actors.actor_id', 'actors.id') - .join('releases', 'releases_actors.release_id', 'releases.id') - .join('sites', 'sites.id', 'releases.site_id') - .join('networks', 'networks.id', 'sites.network_id') - .join('releases_posters', 'releases_posters.release_id', 'releases.id') - .join('media', 'releases_posters.media_id', 'media.id'); - // .join('releases_photos', 'releases_photos.release_id', 'releases.id') - // .join('media', 'releases_photos.media_id', 'media.id'); + const posters = await knex('actors') + .select('actors.name as actor_name', 'releases.title', 'releases.date', 'media.path', 'media.index', 'sites.name as site_name', 'networks.name as network_name') + .whereIn('actors.name', (argv.actors || []).concat(argv._)) + .join('releases_actors', 'releases_actors.actor_id', 'actors.id') + .join('releases', 'releases_actors.release_id', 'releases.id') + .join('sites', 'sites.id', 'releases.site_id') + .join('networks', 'networks.id', 'sites.network_id') + .join('releases_posters', 'releases_posters.release_id', 'releases.id') + .join('media', 'releases_posters.media_id', 'media.id'); + // .join('releases_photos', 'releases_photos.release_id', 'releases.id') + // .join('media', 'releases_photos.media_id', 'media.id'); - await Promise.all(posters.map(async (poster) => { - const source = path.join(config.media.path, poster.path); + await Promise.all(posters.map(async (poster) => { + const source = path.join(config.media.path, poster.path); - const directory = path.join(config.media.path, 'extracted', poster.actor_name); - const target = path.join(directory, `${poster.actor_name} - ${poster.network_name}: ${poster.site_name} - ${poster.title.replace(/[/.]/g, '_')} (${moment.utc(poster.date).format('YYYY-MM-DD')})-${poster.index}.jpeg`); - await fs.mkdir(path.join(directory), { recursive: true }); + const directory = path.join(config.media.path, 'extracted', poster.actor_name); + const target = path.join(directory, `${poster.actor_name} - ${poster.network_name}: ${poster.site_name} - ${poster.title.replace(/[/.]/g, '_')} (${moment.utc(poster.date).format('YYYY-MM-DD')})-${poster.index}.jpeg`); + await fs.mkdir(path.join(directory), { recursive: true }); - const file = await fs.readFile(source); - await fs.writeFile(target, file); + const file = await fs.readFile(source); + await fs.writeFile(target, file); - return file; - })); + return file; + })); - knex.destroy(); + knex.destroy(); } init(); diff --git a/src/utils/qu.js b/src/utils/qu.js index 31e76bea..7d97be2c 100644 --- a/src/utils/qu.js +++ b/src/utils/qu.js @@ -5,341 +5,341 @@ const moment = require('moment'); const http = require('./http'); function trim(str) { - if (!str) return null; - return str.trim().replace(/\s+/g, ' '); + if (!str) return null; + return str.trim().replace(/\s+/g, ' '); } function extractDate(dateString, format, match) { - if (match) { - const dateStamp = trim(dateString).match(match); + if (match) { + const dateStamp = trim(dateString).match(match); - if (dateStamp) { - const dateValue = moment.utc(dateStamp[0], format); + if (dateStamp) { + const dateValue = moment.utc(dateStamp[0], format); - return dateValue.isValid() ? dateValue.toDate() : null; - } - return null; - } + return dateValue.isValid() ? dateValue.toDate() : null; + } + return null; + } - const dateValue = moment.utc(trim(dateString), format); + const dateValue = moment.utc(trim(dateString), format); - return dateValue.isValid() ? dateValue.toDate() : null; + return dateValue.isValid() ? dateValue.toDate() : null; } function formatDate(dateValue, format, inputFormat) { - if (inputFormat) { - return moment(dateValue, inputFormat).format(format); - } + if (inputFormat) { + return moment(dateValue, inputFormat).format(format); + } - return moment(dateValue).format(format); + return moment(dateValue).format(format); } function prefixUrl(urlValue, origin, protocol = 'https') { - if (protocol && /^\/\//.test(urlValue)) { - return `${protocol}:${urlValue}`; - } + if (protocol && /^\/\//.test(urlValue)) { + return `${protocol}:${urlValue}`; + } - if (origin && /^\//.test(urlValue)) { - return `${origin}${urlValue}`; - } + if (origin && /^\//.test(urlValue)) { + return `${origin}${urlValue}`; + } - return urlValue; + return urlValue; } function q(context, selector, attrArg, applyTrim = true) { - const attr = attrArg === true ? 'textContent' : attrArg; + const attr = attrArg === true ? 'textContent' : attrArg; - if (attr) { - const value = selector - ? context.querySelector(selector)?.[attr] || context.querySelector(selector)?.attributes[attr]?.value - : context[attr] || context.attributes[attr]?.value; + if (attr) { + const value = selector + ? context.querySelector(selector)?.[attr] || context.querySelector(selector)?.attributes[attr]?.value + : context[attr] || context.attributes[attr]?.value; - return applyTrim && value ? trim(value) : value; - } + return applyTrim && value ? trim(value) : value; + } - return selector ? context.querySelector(selector) : context; + return selector ? context.querySelector(selector) : context; } function all(context, selector, attrArg, applyTrim = true) { - const attr = attrArg === true ? 'textContent' : attrArg; + const attr = attrArg === true ? 'textContent' : attrArg; - if (attr) { - return Array.from(context.querySelectorAll(selector), el => q(el, null, attr, applyTrim)); - } + if (attr) { + return Array.from(context.querySelectorAll(selector), el => q(el, null, attr, applyTrim)); + } - return Array.from(context.querySelectorAll(selector)); + return Array.from(context.querySelectorAll(selector)); } function exists(context, selector) { - return !!q(context, selector); + return !!q(context, selector); } function html(context, selector) { - const el = q(context, selector, null, true); + const el = q(context, selector, null, true); - return el && el.innerHTML; + return el && el.innerHTML; } function texts(context, selector, applyTrim = true, filter = true) { - const el = q(context, selector, null, applyTrim); - if (!el) return null; + const el = q(context, selector, null, applyTrim); + if (!el) return null; - const nodes = Array.from(el.childNodes) - .filter(node => node.nodeName === '#text') - .map(node => (applyTrim ? trim(node.textContent) : node.textContent)); + const nodes = Array.from(el.childNodes) + .filter(node => node.nodeName === '#text') + .map(node => (applyTrim ? trim(node.textContent) : node.textContent)); - return filter ? nodes.filter(Boolean) : nodes; + return filter ? nodes.filter(Boolean) : nodes; } function text(context, selector, applyTrim = true) { - const nodes = texts(context, selector, applyTrim, true); - if (!nodes) return null; + const nodes = texts(context, selector, applyTrim, true); + if (!nodes) return null; - const textValue = nodes.join(' '); + const textValue = nodes.join(' '); - return applyTrim ? trim(textValue) : textValue; + return applyTrim ? trim(textValue) : textValue; } function meta(context, selector, attrArg = 'content', applyTrim = true) { - if (/meta\[.*\]/.test(selector)) { - return q(context, selector, attrArg, applyTrim); - } + if (/meta\[.*\]/.test(selector)) { + return q(context, selector, attrArg, applyTrim); + } - return q(context, `meta[${selector}]`, attrArg, applyTrim); + return q(context, `meta[${selector}]`, attrArg, applyTrim); } function date(context, selector, format, match, attr = 'textContent') { - const dateString = q(context, selector, attr, true); + const dateString = q(context, selector, attr, true); - if (!dateString) return null; + if (!dateString) return null; - return extractDate(dateString, format, match); + return extractDate(dateString, format, match); } function image(context, selector = 'img', attr = 'src', origin, protocol = 'https') { - const imageEl = q(context, selector, attr); + const imageEl = q(context, selector, attr); - // no attribute means q output will be HTML element - return attr ? prefixUrl(imageEl, origin, protocol) : imageEl; + // no attribute means q output will be HTML element + return attr ? prefixUrl(imageEl, origin, protocol) : imageEl; } function images(context, selector = 'img', attr = 'src', origin, protocol = 'https') { - const imageEls = all(context, selector, attr); + const imageEls = all(context, selector, attr); - return attr ? imageEls.map(imageEl => prefixUrl(imageEl, origin, protocol)) : imageEls; + return attr ? imageEls.map(imageEl => prefixUrl(imageEl, origin, protocol)) : imageEls; } function url(context, selector = 'a', attr = 'href', origin, protocol = 'https') { - const urlEl = q(context, selector, attr); + const urlEl = q(context, selector, attr); - return attr ? prefixUrl(urlEl, origin, protocol) : urlEl; + return attr ? prefixUrl(urlEl, origin, protocol) : urlEl; } function urls(context, selector = 'a', attr = 'href', origin, protocol = 'https') { - const urlEls = all(context, selector, attr); + const urlEls = all(context, selector, attr); - return attr ? urlEls.map(urlEl => prefixUrl(urlEl, origin, protocol)) : urlEls; + return attr ? urlEls.map(urlEl => prefixUrl(urlEl, origin, protocol)) : urlEls; } function poster(context, selector = 'video', attr = 'poster', origin, protocol = 'https') { - const posterEl = q(context, selector, attr); + const posterEl = q(context, selector, attr); - return attr ? prefixUrl(posterEl, origin, protocol) : posterEl; + return attr ? prefixUrl(posterEl, origin, protocol) : posterEl; } function video(context, selector = 'source', attr = 'src', origin, protocol = 'https') { - const trailerEl = q(context, selector, attr); + const trailerEl = q(context, selector, attr); - return attr ? prefixUrl(trailerEl, origin, protocol) : trailerEl; + return attr ? prefixUrl(trailerEl, origin, protocol) : trailerEl; } function videos(context, selector = 'source', attr = 'src', origin, protocol = 'https') { - const trailerEls = all(context, selector, attr); + const trailerEls = all(context, selector, attr); - return attr ? trailerEls.map(trailerEl => prefixUrl(trailerEl, origin, protocol)) : trailerEls; + return attr ? trailerEls.map(trailerEl => prefixUrl(trailerEl, origin, protocol)) : trailerEls; } function duration(context, selector, match, attr = 'textContent') { - const durationString = q(context, selector, attr); + const durationString = q(context, selector, attr); - if (!durationString) return null; - const durationMatch = durationString.match(match || /(\d+:)?\d+:\d+/); + if (!durationString) return null; + const durationMatch = durationString.match(match || /(\d+:)?\d+:\d+/); - if (durationMatch) { - const segments = ['00'].concat(durationMatch[0].split(':')).slice(-3); + if (durationMatch) { + const segments = ['00'].concat(durationMatch[0].split(':')).slice(-3); - return moment.duration(segments.join(':')).asSeconds(); - } + return moment.duration(segments.join(':')).asSeconds(); + } - return null; + return null; } const legacyFuncs = { - q, - qa: all, - qall: all, - qd: date, - qdate: date, - qh: html, - qhtml: html, - qi: image, - qimage: image, - qimages: images, - qis: images, - ql: duration, - qlength: duration, - qm: meta, - qmeta: meta, - qp: poster, - qposter: poster, - qs: all, - qt: video, - qtext: text, - qtexts: texts, - qtrailer: video, - qtrailers: videos, - qts: videos, - qtx: text, - qtxs: texts, - qtxt: text, - qtxts: texts, - // qu: url, - qurl: url, - qurls: urls, - qus: urls, + q, + qa: all, + qall: all, + qd: date, + qdate: date, + qh: html, + qhtml: html, + qi: image, + qimage: image, + qimages: images, + qis: images, + ql: duration, + qlength: duration, + qm: meta, + qmeta: meta, + qp: poster, + qposter: poster, + qs: all, + qt: video, + qtext: text, + qtexts: texts, + qtrailer: video, + qtrailers: videos, + qts: videos, + qtx: text, + qtxs: texts, + qtxt: text, + qtxts: texts, + // qu: url, + qurl: url, + qurls: urls, + qus: urls, }; const quFuncs = { - all, - html, - date, - dur: duration, - duration, - exists, - image, - images, - img: image, - imgs: images, - length: duration, - meta, - poster, - q, - text, - texts, - trailer: video, - url, - urls, - video, - videos, + all, + html, + date, + dur: duration, + duration, + exists, + image, + images, + img: image, + imgs: images, + length: duration, + meta, + poster, + q, + text, + texts, + trailer: video, + url, + urls, + video, + videos, }; function init(element, window) { - if (!element) return null; + if (!element) return null; - const legacyContextFuncs = Object.entries(legacyFuncs) // dynamically attach methods with context - .reduce((acc, [key, func]) => ({ - ...acc, - [key]: (...args) => (window && args[0] instanceof window.HTMLElement // allow for different context - ? func(...args) - : func(element, ...args)), - }), {}); + const legacyContextFuncs = Object.entries(legacyFuncs) // dynamically attach methods with context + .reduce((acc, [key, func]) => ({ + ...acc, + [key]: (...args) => (window && args[0] instanceof window.HTMLElement // allow for different context + ? func(...args) + : func(element, ...args)), + }), {}); - const quContextFuncs = Object.entries(quFuncs) // dynamically attach methods with context - .reduce((acc, [key, func]) => ({ - ...acc, - [key]: (...args) => (window && args[0] instanceof window.HTMLElement // allow for different context - ? func(...args) - : func(element, ...args)), - }), {}); + const quContextFuncs = Object.entries(quFuncs) // dynamically attach methods with context + .reduce((acc, [key, func]) => ({ + ...acc, + [key]: (...args) => (window && args[0] instanceof window.HTMLElement // allow for different context + ? func(...args) + : func(element, ...args)), + }), {}); - return { - element, - el: element, - html: element.outerHTML || element.body.outerHTML, - text: trim(element.textContent), - ...(window && { - window, - document: window.document, - }), - ...legacyContextFuncs, - qu: quContextFuncs, - }; + return { + element, + el: element, + html: element.outerHTML || element.body.outerHTML, + text: trim(element.textContent), + ...(window && { + window, + document: window.document, + }), + ...legacyContextFuncs, + qu: quContextFuncs, + }; } function initAll(context, selector, window) { - if (Array.isArray(context)) { - return context.map(element => init(element, window)); - } + if (Array.isArray(context)) { + return context.map(element => init(element, window)); + } - return Array.from(context.querySelectorAll(selector)) - .map(element => init(element, window)); + return Array.from(context.querySelectorAll(selector)) + .map(element => init(element, window)); } function extract(htmlValue, selector) { - const { window } = new JSDOM(htmlValue); + const { window } = new JSDOM(htmlValue); - if (selector) { - return init(window.document.querySelector(selector), window); - } + if (selector) { + return init(window.document.querySelector(selector), window); + } - return init(window.document, window); + return init(window.document, window); } function extractAll(htmlValue, selector) { - const { window } = new JSDOM(htmlValue); + const { window } = new JSDOM(htmlValue); - return initAll(window.document, selector, window); + return initAll(window.document, selector, window); } async function get(urlValue, selector, headers, options, queryAll = false) { - const res = await http.get(urlValue, headers); + const res = await http.get(urlValue, headers); - if (res.statusCode === 200) { - const item = queryAll - ? extractAll(res.body.toString(), selector) - : extract(res.body.toString(), selector); + if (res.statusCode === 200) { + const item = queryAll + ? extractAll(res.body.toString(), selector) + : extract(res.body.toString(), selector); - return { - item, - items: all ? item : [item], - res, - ok: true, - status: res.statusCode, - }; - } + return { + item, + items: all ? item : [item], + res, + ok: true, + status: res.statusCode, + }; + } - return { - item: null, - items: [], - res, - ok: false, - status: res.statusCode, - }; + return { + item: null, + items: [], + res, + ok: false, + status: res.statusCode, + }; } async function getAll(urlValue, selector, headers, options) { - return get(urlValue, selector, headers, options, true); + return get(urlValue, selector, headers, options, true); } module.exports = { - extractDate, - extract, - extractAll, - init, - initAll, - formatDate, - get, - getAll, - context: init, - contextAll: initAll, - ed: extractDate, - ex: extract, - exa: extractAll, - fd: formatDate, - parseDate: extractDate, - ctx: init, - ctxa: initAll, - geta: getAll, - qu: quFuncs, - ...legacyFuncs, + extractDate, + extract, + extractAll, + init, + initAll, + formatDate, + get, + getAll, + context: init, + contextAll: initAll, + ed: extractDate, + ex: extract, + exa: extractAll, + fd: formatDate, + parseDate: extractDate, + ctx: init, + ctxa: initAll, + geta: getAll, + qu: quFuncs, + ...legacyFuncs, }; diff --git a/src/utils/rename.js b/src/utils/rename.js deleted file mode 100644 index 95bd7f04..00000000 --- a/src/utils/rename.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -const path = require('path'); -const Promise = require('bluebird'); -const fs = require('fs-extra'); -const fetchScene = require('../scrape-releases'); - -const argv = require('../argv'); - -async function renameFiles() { - const filenames = await fs.readdir(process.cwd()); - - const curated = await Promise.map(filenames, async (filename) => { - const shootId = filename.split(' ')[1]; - const scene = await fetchScene(`https://kink.com/shoot/${shootId}`); - - if (argv.confirm) { - await fs.rename(path.join(process.cwd(), filename), path.join(process.cwd(), `${scene.filename}.mp4`)); - } - - return scene.filename; - }, { - concurrency: 5, - }); - - console.log(curated); -} - -renameFiles(); diff --git a/src/utils/resolve-place.js b/src/utils/resolve-place.js index 09de6073..7470e872 100644 --- a/src/utils/resolve-place.js +++ b/src/utils/resolve-place.js @@ -3,26 +3,26 @@ const bhttp = require('bhttp'); async function resolvePlace(query) { - if (!query) { - return null; - } + if (!query) { + return null; + } - const res = await bhttp.get(`https://nominatim.openstreetmap.org/search/${encodeURI(query)}?format=json&accept-language=en&addressdetails=1`); - const [item] = res.body; + const res = await bhttp.get(`https://nominatim.openstreetmap.org/search/${encodeURI(query)}?format=json&accept-language=en&addressdetails=1`); + const [item] = res.body; - if (item && item.address) { - const rawPlace = item.address; - const place = {}; + if (item && item.address) { + const rawPlace = item.address; + const place = {}; - if (rawPlace.city) place.city = rawPlace.city; - if (rawPlace.state) place.state = rawPlace.state; - if (rawPlace.country_code) place.country = rawPlace.country_code.toUpperCase(); - if (rawPlace.continent) place.continent = rawPlace.continent; + if (rawPlace.city) place.city = rawPlace.city; + if (rawPlace.state) place.state = rawPlace.state; + if (rawPlace.country_code) place.country = rawPlace.country_code.toUpperCase(); + if (rawPlace.continent) place.continent = rawPlace.continent; - return place; - } + return place; + } - return null; + return null; } module.exports = resolvePlace; diff --git a/src/utils/scorelogos.js b/src/utils/scorelogos.js index 45c555c9..527c7ff2 100644 --- a/src/utils/scorelogos.js +++ b/src/utils/scorelogos.js @@ -6,32 +6,32 @@ const fs = require('fs-extra'); const knex = require('../knex'); async function init() { - const sites = await knex('sites') - .select('networks.name', 'sites.slug') - .join('networks', 'networks.id', 'sites.network_id') - .where('networks.slug', 'score'); + const sites = await knex('sites') + .select('networks.name', 'sites.slug') + .join('networks', 'networks.id', 'sites.network_id') + .where('networks.slug', 'score'); - await Promise.map(sites, async (site) => { - const url = `https://cdn77.scoreuniverse.com/${site.slug}/images/logo.png`; + await Promise.map(sites, async (site) => { + const url = `https://cdn77.scoreuniverse.com/${site.slug}/images/logo.png`; - console.log(url); + console.log(url); - const res = await bhttp.get(url, { - responseTimeout: 5000, - }); + const res = await bhttp.get(url, { + responseTimeout: 5000, + }); - if (res.statusCode === 200) { - console.log(`Saving logo for ${site.slug}`); + if (res.statusCode === 200) { + console.log(`Saving logo for ${site.slug}`); - await fs.writeFile(`./score/${site.slug}.png`, res.body); - } + await fs.writeFile(`./score/${site.slug}.png`, res.body); + } - console.log(`No logo found for ${site.slug}`); - }, { - concurrency: 10, - }); + console.log(`No logo found for ${site.slug}`); + }, { + concurrency: 10, + }); - knex.destroy(); + knex.destroy(); } init(); diff --git a/src/utils/shuffle.js b/src/utils/shuffle.js index 5b96bc6e..13424cb4 100644 --- a/src/utils/shuffle.js +++ b/src/utils/shuffle.js @@ -1,14 +1,14 @@ 'use strict'; function shuffle(array) { - const shuffledArray = [...array]; + const shuffledArray = [...array]; - for (let i = array.length - 1; i > 0; i -= 1) { - const j = Math.floor(Math.random() * (i + 1)); - [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]]; - } + for (let i = array.length - 1; i > 0; i -= 1) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]]; + } - return shuffledArray; + return shuffledArray; } module.exports = shuffle; diff --git a/src/utils/slugify.js b/src/utils/slugify.js index ae5a6d38..a14c76c5 100644 --- a/src/utils/slugify.js +++ b/src/utils/slugify.js @@ -1,30 +1,30 @@ 'use strict'; function slugify(string, delimiter = '-', { - encode = false, - limit = 1000, + encode = false, + limit = 1000, } = {}) { - if (!string) { - return string; - } + if (!string) { + return string; + } - const slugComponents = string.trim().toLowerCase().match(/\w+/g); + const slugComponents = string.trim().toLowerCase().match(/\w+/g); - if (!slugComponents) { - return ''; - } + if (!slugComponents) { + return ''; + } - const slug = slugComponents.reduce((acc, component, index) => { - const accSlug = `${acc}${index > 0 ? delimiter : ''}${component}`; + const slug = slugComponents.reduce((acc, component, index) => { + const accSlug = `${acc}${index > 0 ? delimiter : ''}${component}`; - if (accSlug.length < limit) { - return accSlug; - } + if (accSlug.length < limit) { + return accSlug; + } - return acc; - }, ''); + return acc; + }, ''); - return encode ? encodeURI(slug) : slug; + return encode ? encodeURI(slug) : slug; } module.exports = slugify; diff --git a/src/utils/stream.js b/src/utils/stream.js index e51c820a..54a532a4 100644 --- a/src/utils/stream.js +++ b/src/utils/stream.js @@ -11,45 +11,45 @@ const sharp = require('sharp'); const url = 'https://thumbs.julesjordan.com/trial/content//upload/dl03/julesjordan/oil_overload_16_scene2//photos/alina_lopez_jules_jordan_com_77.jpg'; async function init() { - const hash = new blake2.Hash('blake2b'); - hash.setEncoding('hex'); + const hash = new blake2.Hash('blake2b'); + hash.setEncoding('hex'); - const res = await bhttp.get(url, { - stream: true, - }); + const res = await bhttp.get(url, { + stream: true, + }); - const metaStream = sharp(); - const hashStream = new PassThrough(); - const target = fs.createWriteStream(path.join(config.media.path, 'temp', 'alina.jpg')); - const thumbTarget = fs.createWriteStream(path.join(config.media.path, 'temp', 'alina_thumb.jpg')); + const metaStream = sharp(); + const hashStream = new PassThrough(); + const target = fs.createWriteStream(path.join(config.media.path, 'temp', 'alina.jpg')); + const thumbTarget = fs.createWriteStream(path.join(config.media.path, 'temp', 'alina_thumb.jpg')); - hashStream.on('data', (chunk) => { - hash.write(chunk); - }); + hashStream.on('data', (chunk) => { + hash.write(chunk); + }); - metaStream.clone() - .resize(320) - .pipe(thumbTarget); + metaStream.clone() + .resize(320) + .pipe(thumbTarget); - const stream = res - .pipe(metaStream) - .pipe(hashStream) - .pipe(target); + const stream = res + .pipe(metaStream) + .pipe(hashStream) + .pipe(target); - stream.on('finish', () => { - hash.end(); - const digest = hash.read(); + stream.on('finish', () => { + hash.end(); + const digest = hash.read(); - console.log('stream', digest); - }); + console.log('stream', digest); + }); - metaStream.on('info', (info) => { - console.log('info', info); - }); + metaStream.on('info', (info) => { + console.log('info', info); + }); - const stats = await metaStream.stats(); + const stats = await metaStream.stats(); - console.log('stats', stats); + console.log('stats', stats); } init(); diff --git a/src/utils/timeout.js b/src/utils/timeout.js index 6327ed90..f07c7617 100644 --- a/src/utils/timeout.js +++ b/src/utils/timeout.js @@ -6,15 +6,15 @@ const sleep = 5000; const timeout = 1000; async function init() { - try { - const res = await bhttp.get(`https://httpstat.us/200?sleep=${sleep}`, { - responseTimeout: timeout, - }); + try { + const res = await bhttp.get(`https://httpstat.us/200?sleep=${sleep}`, { + responseTimeout: timeout, + }); - console.log(res.statusCode); - } catch (error) { - console.log(error); - } + console.log(res.statusCode); + } catch (error) { + console.log(error); + } } /* diff --git a/src/utils/titles.js b/src/utils/titles.js index 8d8c9503..283b00c7 100644 --- a/src/utils/titles.js +++ b/src/utils/titles.js @@ -4,18 +4,18 @@ const argv = require('../argv'); const knex = require('../knex'); async function printTitles() { - const titles = await knex('releases') - .where((builder) => { - if (argv.sites) builder.whereIn('sites.slug', argv.sites); - if (argv.networks) builder.orWhereIn('networks.slug', argv.networks); - }) - .join('sites', 'sites.id', 'releases.site_id') - .join('networks', 'networks.id', 'sites.network_id') - .pluck('title'); + const titles = await knex('releases') + .where((builder) => { + if (argv.sites) builder.whereIn('sites.slug', argv.sites); + if (argv.networks) builder.orWhereIn('networks.slug', argv.networks); + }) + .join('sites', 'sites.id', 'releases.site_id') + .join('networks', 'networks.id', 'sites.network_id') + .pluck('title'); - console.log(titles.join('\n')); + console.log(titles.join('\n')); - knex.destroy(); + knex.destroy(); } printTitles(); diff --git a/src/utils/upsert.js b/src/utils/upsert.js index 72a07ab1..23033794 100644 --- a/src/utils/upsert.js +++ b/src/utils/upsert.js @@ -4,54 +4,54 @@ const knex = require('../knex'); const logger = require('../logger')(__filename); async function upsert(table, items, identifier = ['id'], _knex) { - const identifiers = Array.isArray(identifier) ? identifier : [identifier]; + const identifiers = Array.isArray(identifier) ? identifier : [identifier]; - const duplicates = await knex(table).whereIn(identifiers, items.map(item => identifiers.map(identifierX => item[identifierX]))); - const duplicatesByIdentifiers = duplicates.reduce((acc, duplicate) => { - const duplicateIdentifier = identifiers.map(identifierX => duplicate[identifierX]).toString(); + const duplicates = await knex(table).whereIn(identifiers, items.map(item => identifiers.map(identifierX => item[identifierX]))); + const duplicatesByIdentifiers = duplicates.reduce((acc, duplicate) => { + const duplicateIdentifier = identifiers.map(identifierX => duplicate[identifierX]).toString(); - return { ...acc, [duplicateIdentifier]: duplicate }; - }, {}); + return { ...acc, [duplicateIdentifier]: duplicate }; + }, {}); - const { insert, update } = items.reduce((acc, item) => { - const itemIdentifier = identifiers.map(identifierX => item[identifierX]).toString(); + const { insert, update } = items.reduce((acc, item) => { + const itemIdentifier = identifiers.map(identifierX => item[identifierX]).toString(); - if (duplicatesByIdentifiers[itemIdentifier]) { - acc.update.push(item); - return acc; - } + if (duplicatesByIdentifiers[itemIdentifier]) { + acc.update.push(item); + return acc; + } - acc.insert.push(item); - return acc; - }, { - insert: [], - update: [], - }); + acc.insert.push(item); + return acc; + }, { + insert: [], + update: [], + }); - if (knex) { - logger.debug(`${table}: Inserting ${insert.length}`); - logger.debug(`${table}: Updating ${update.length}`); + if (knex) { + logger.debug(`${table}: Inserting ${insert.length}`); + logger.debug(`${table}: Updating ${update.length}`); - const [inserted, updated] = await Promise.all([ - knex(table).returning('*').insert(insert), - knex.transaction(async trx => Promise.all(update.map((item) => { - const clause = identifiers.reduce((acc, identifierX) => ({ ...acc, [identifierX]: item[identifierX] }), {}); + const [inserted, updated] = await Promise.all([ + knex(table).returning('*').insert(insert), + knex.transaction(async trx => Promise.all(update.map((item) => { + const clause = identifiers.reduce((acc, identifierX) => ({ ...acc, [identifierX]: item[identifierX] }), {}); - return trx - .where(clause) - .update(item) - .into(table) - .returning('*'); - }))), - ]); + return trx + .where(clause) + .update(item) + .into(table) + .returning('*'); + }))), + ]); - return { - inserted: Array.isArray(inserted) ? inserted : [], - updated: updated.reduce((acc, updatedItems) => acc.concat(updatedItems), []), - }; - } + return { + inserted: Array.isArray(inserted) ? inserted : [], + updated: updated.reduce((acc, updatedItems) => acc.concat(updatedItems), []), + }; + } - return { insert, update }; + return { insert, update }; } module.exports = upsert; diff --git a/src/utils/where-or.js b/src/utils/where-or.js index bc9049a8..a3eb5ce4 100644 --- a/src/utils/where-or.js +++ b/src/utils/where-or.js @@ -1,25 +1,25 @@ 'use strict'; function whereOr(query, table, builder) { - if (!query) { - return {}; - } + if (!query) { + return {}; + } - Object.entries(query).forEach(([key, value]) => { - if (value === undefined) { - return builder; - } + Object.entries(query).forEach(([key, value]) => { + if (value === undefined) { + return builder; + } - if (Array.isArray(value)) { - builder.orWhereIn(`${table}.${key}`, value); - return builder; - } + if (Array.isArray(value)) { + builder.orWhereIn(`${table}.${key}`, value); + return builder; + } - builder.orWhere(`${table}.${key}`, value); - return builder; - }); + builder.orWhere(`${table}.${key}`, value); + return builder; + }); - return builder; + return builder; } module.exports = whereOr; diff --git a/src/web/actors.js b/src/web/actors.js index c43cca32..b6cace5c 100644 --- a/src/web/actors.js +++ b/src/web/actors.js @@ -3,29 +3,29 @@ const { fetchActors } = require('../actors'); async function fetchActorsApi(req, res) { - const actorId = typeof req.params.actorId === 'number' ? req.params.actorId : null; - const actorSlug = typeof req.params.actorId === 'string' ? req.params.actorId : null; + const actorId = typeof req.params.actorId === 'number' ? req.params.actorId : null; + const actorSlug = typeof req.params.actorId === 'string' ? req.params.actorId : null; - if (actorId || actorSlug) { - const actors = await fetchActors({ - id: actorId, - slug: actorSlug, - }); + if (actorId || actorSlug) { + const actors = await fetchActors({ + id: actorId, + slug: actorSlug, + }); - if (actors.length > 0) { - res.send(actors[0]); - return; - } + if (actors.length > 0) { + res.send(actors[0]); + return; + } - res.status(404).send(); - return; - } + res.status(404).send(); + return; + } - const actors = await fetchActors(null, req.query.limit); + const actors = await fetchActors(null, req.query.limit); - res.send(actors); + res.send(actors); } module.exports = { - fetchActors: fetchActorsApi, + fetchActors: fetchActorsApi, }; diff --git a/src/web/networks.js b/src/web/networks.js index 909afd18..d41b38b0 100644 --- a/src/web/networks.js +++ b/src/web/networks.js @@ -3,24 +3,24 @@ const { fetchNetworks, fetchNetworksFromReleases } = require('../networks'); async function fetchNetworksApi(req, res) { - const networkId = typeof req.params.networkId === 'number' ? req.params.networkId : undefined; // null will literally include NULL results - const networkSlug = typeof req.params.networkId === 'string' ? req.params.networkId : undefined; + const networkId = typeof req.params.networkId === 'number' ? req.params.networkId : undefined; // null will literally include NULL results + const networkSlug = typeof req.params.networkId === 'string' ? req.params.networkId : undefined; - const networks = await fetchNetworks({ - id: networkId, - slug: networkSlug, - }); + const networks = await fetchNetworks({ + id: networkId, + slug: networkSlug, + }); - res.send(networks); + res.send(networks); } async function fetchNetworksFromReleasesApi(req, res) { - const networks = await fetchNetworksFromReleases(); + const networks = await fetchNetworksFromReleases(); - res.send(networks); + res.send(networks); } module.exports = { - fetchNetworks: fetchNetworksApi, - fetchNetworksFromReleases: fetchNetworksFromReleasesApi, + fetchNetworks: fetchNetworksApi, + fetchNetworksFromReleases: fetchNetworksFromReleasesApi, }; diff --git a/src/web/plugins/actors.js b/src/web/plugins/actors.js index 9899dce5..fceea5f6 100644 --- a/src/web/plugins/actors.js +++ b/src/web/plugins/actors.js @@ -5,7 +5,7 @@ const moment = require('moment'); const { cmToFeetInches, kgToLbs } = require('../../utils/convert'); const schemaExtender = makeExtendSchemaPlugin(_build => ({ - typeDefs: gql` + typeDefs: gql` enum Units { METRIC IMPERIAL @@ -17,32 +17,32 @@ const schemaExtender = makeExtendSchemaPlugin(_build => ({ weight(units:Units): String @requires(columns: ["weight"]) } `, - resolvers: { - Actor: { - age(parent, _args, _context, _info) { - if (!parent.birthdate) return null; + resolvers: { + Actor: { + age(parent, _args, _context, _info) { + if (!parent.birthdate) return null; - return moment().diff(parent.birthdate, 'years'); - }, - height(parent, args, _context, _info) { - if (!parent.height) return null; + return moment().diff(parent.birthdate, 'years'); + }, + height(parent, args, _context, _info) { + if (!parent.height) return null; - if (args.units === 'IMPERIAL') { - const { feet, inches } = cmToFeetInches(parent.height); - return `${feet}' ${inches}"`; - } + if (args.units === 'IMPERIAL') { + const { feet, inches } = cmToFeetInches(parent.height); + return `${feet}' ${inches}"`; + } - return parent.height.toString(); - }, - weight(parent, args, _context, _info) { - if (!parent.weight) return null; + return parent.height.toString(); + }, + weight(parent, args, _context, _info) { + if (!parent.weight) return null; - return args.units === 'IMPERIAL' - ? kgToLbs(parent.weight).toString() - : parent.weight.toString(); - }, - }, - }, + return args.units === 'IMPERIAL' + ? kgToLbs(parent.weight).toString() + : parent.weight.toString(); + }, + }, + }, })); module.exports = [schemaExtender]; diff --git a/src/web/plugins/plugins.js b/src/web/plugins/plugins.js index 5f320ca7..ec09f859 100644 --- a/src/web/plugins/plugins.js +++ b/src/web/plugins/plugins.js @@ -5,7 +5,7 @@ const SitePlugins = require('./sites'); // const ReleasePlugins = require('./releases'); module.exports = { - ActorPlugins, - SitePlugins, - ReleasePlugins: [], + ActorPlugins, + SitePlugins, + ReleasePlugins: [], }; diff --git a/src/web/plugins/releases.js b/src/web/plugins/releases.js index ec0bf68a..9ff05f63 100644 --- a/src/web/plugins/releases.js +++ b/src/web/plugins/releases.js @@ -3,16 +3,16 @@ const { makeExtendSchemaPlugin, gql } = require('graphile-utils'); const schemaExtender = makeExtendSchemaPlugin(_build => ({ - typeDefs: gql` + typeDefs: gql` extend type Release {} `, - resolvers: { - Release: { - async foo(_parent, _args, _context, _info) { - // template - }, - }, - }, + resolvers: { + Release: { + async foo(_parent, _args, _context, _info) { + // template + }, + }, + }, })); module.exports = [schemaExtender]; diff --git a/src/web/plugins/sites.js b/src/web/plugins/sites.js index 5e25dfee..634e0aad 100644 --- a/src/web/plugins/sites.js +++ b/src/web/plugins/sites.js @@ -3,18 +3,18 @@ const { makeExtendSchemaPlugin, gql } = require('graphile-utils'); const schemaExtender = makeExtendSchemaPlugin(_build => ({ - typeDefs: gql` + typeDefs: gql` extend type Site { independent: Boolean @requires(columns: ["parameters"]) } `, - resolvers: { - Site: { - independent(parent, _args, _context, _info) { - return !!parent.parameters?.independent; - }, - }, - }, + resolvers: { + Site: { + independent(parent, _args, _context, _info) { + return !!parent.parameters?.independent; + }, + }, + }, })); module.exports = [schemaExtender]; diff --git a/src/web/releases.js b/src/web/releases.js index 2c24c1a3..68e1f70f 100644 --- a/src/web/releases.js +++ b/src/web/releases.js @@ -3,15 +3,15 @@ const { fetchReleases, searchReleases } = require('../releases'); async function fetchReleasesApi(req, res) { - const query = req.query.query || req.query.q; + const query = req.query.query || req.query.q; - const releases = query - ? await searchReleases(query, req.query.limit) - : await fetchReleases(req.query.limit); + const releases = query + ? await searchReleases(query, req.query.limit) + : await fetchReleases(req.query.limit); - res.send(releases); + res.send(releases); } module.exports = { - fetchReleases: fetchReleasesApi, + fetchReleases: fetchReleasesApi, }; diff --git a/src/web/server.js b/src/web/server.js index 43eee6f9..48024c62 100644 --- a/src/web/server.js +++ b/src/web/server.js @@ -15,68 +15,68 @@ const logger = require('../logger')(__filename); const { ActorPlugins, SitePlugins, ReleasePlugins } = require('./plugins/plugins'); const { - fetchReleases, + fetchReleases, } = require('./releases'); function initServer() { - const app = express(); - const router = Router(); + const app = express(); + const router = Router(); - const connectionString = `postgres://${config.database.user}:${config.database.password}@${config.database.host}:5432/${config.database.database}`; + const connectionString = `postgres://${config.database.user}:${config.database.password}@${config.database.host}:5432/${config.database.database}`; - app.use(postgraphile( - connectionString, - 'public', - { - // watchPg: true, - dynamicJson: true, - graphiql: true, - enhanceGraphiql: true, - allowExplain: () => true, - simpleCollections: 'only', - graphileBuildOptions: { - pgOmitListSuffix: true, - connectionFilterRelations: true, - }, - appendPlugins: [ - PgSimplifyInflectorPlugin, - PgConnectionFilterPlugin, - PgOrderByRelatedPlugin, - ...ActorPlugins, - ...SitePlugins, - ...ReleasePlugins, - ], - }, - )); + app.use(postgraphile( + connectionString, + 'public', + { + // watchPg: true, + dynamicJson: true, + graphiql: true, + enhanceGraphiql: true, + allowExplain: () => true, + simpleCollections: 'only', + graphileBuildOptions: { + pgOmitListSuffix: true, + connectionFilterRelations: true, + }, + appendPlugins: [ + PgSimplifyInflectorPlugin, + PgConnectionFilterPlugin, + PgOrderByRelatedPlugin, + ...ActorPlugins, + ...SitePlugins, + ...ReleasePlugins, + ], + }, + )); - app.set('view engine', 'ejs'); + app.set('view engine', 'ejs'); - router.use('/media', express.static(config.media.path)); - router.use(express.static('public')); + router.use('/media', express.static(config.media.path)); + router.use(express.static('public')); - router.use('/img', (req, res) => { - res.status(404).send(); - }); + router.use('/img', (req, res) => { + res.status(404).send(); + }); - router.use(bodyParser.json({ strict: false })); + router.use(bodyParser.json({ strict: false })); - router.get('/api/releases', fetchReleases); + router.get('/api/releases', fetchReleases); - router.get('*', (req, res) => { - res.render(path.join(__dirname, '../../assets/index.ejs'), { - env: JSON.stringify({ - sfw: !!req.headers.sfw || Object.prototype.hasOwnProperty.call(req.query, 'sfw'), - }), - }); - }); + router.get('*', (req, res) => { + res.render(path.join(__dirname, '../../assets/index.ejs'), { + env: JSON.stringify({ + sfw: !!req.headers.sfw || Object.prototype.hasOwnProperty.call(req.query, 'sfw'), + }), + }); + }); - app.use(router); + app.use(router); - const server = app.listen(config.web.port, config.web.host, () => { - const { address, port } = server.address(); + const server = app.listen(config.web.port, config.web.host, () => { + const { address, port } = server.address(); - logger.info(`Web server listening on ${address}:${port}`); - }); + logger.info(`Web server listening on ${address}:${port}`); + }); } module.exports = initServer; diff --git a/src/web/sites.js b/src/web/sites.js index 9cbd63be..f5aebc8a 100644 --- a/src/web/sites.js +++ b/src/web/sites.js @@ -3,24 +3,24 @@ const { fetchSites, fetchSitesFromReleases } = require('../sites'); async function fetchSitesApi(req, res) { - const siteId = typeof req.params.siteId === 'number' ? req.params.siteId : undefined; - const siteSlug = typeof req.params.siteId === 'string' ? req.params.siteId : undefined; + const siteId = typeof req.params.siteId === 'number' ? req.params.siteId : undefined; + const siteSlug = typeof req.params.siteId === 'string' ? req.params.siteId : undefined; - const sites = await fetchSites({ - id: siteId, - slug: siteSlug, - }); + const sites = await fetchSites({ + id: siteId, + slug: siteSlug, + }); - res.send(sites); + res.send(sites); } async function fetchSitesFromReleasesApi(req, res) { - const sites = await fetchSitesFromReleases(); + const sites = await fetchSitesFromReleases(); - res.send(sites); + res.send(sites); } module.exports = { - fetchSites: fetchSitesApi, - fetchSitesFromReleases: fetchSitesFromReleasesApi, + fetchSites: fetchSitesApi, + fetchSitesFromReleases: fetchSitesFromReleasesApi, }; diff --git a/src/web/tags.js b/src/web/tags.js index 315bc9ce..b935b5fa 100644 --- a/src/web/tags.js +++ b/src/web/tags.js @@ -3,38 +3,38 @@ const { fetchTags } = require('../tags'); async function fetchTagsApi(req, res) { - const tagId = typeof req.params.tagId === 'number' ? req.params.tagId : undefined; // null will literally include NULL results - const tagSlug = typeof req.params.tagId === 'string' ? req.params.tagId : undefined; + const tagId = typeof req.params.tagId === 'number' ? req.params.tagId : undefined; // null will literally include NULL results + const tagSlug = typeof req.params.tagId === 'string' ? req.params.tagId : undefined; - if (tagId || tagSlug) { - const tags = await fetchTags({ - id: tagId, - slug: tagSlug, - }, null, req.query.limit); + if (tagId || tagSlug) { + const tags = await fetchTags({ + id: tagId, + slug: tagSlug, + }, null, req.query.limit); - if (tags.length > 0) { - res.send(tags[0]); - return; - } + if (tags.length > 0) { + res.send(tags[0]); + return; + } - res.status(404).send(); - return; - } + res.status(404).send(); + return; + } - const query = {}; - const groupsQuery = {}; + const query = {}; + const groupsQuery = {}; - if (req.query.priority) query.priority = req.query.priority.split(','); - if (req.query.slug) query.slug = req.query.slug.split(','); - if (req.query.group) { - groupsQuery.slug = req.query.group.split(','); - } + if (req.query.priority) query.priority = req.query.priority.split(','); + if (req.query.slug) query.slug = req.query.slug.split(','); + if (req.query.group) { + groupsQuery.slug = req.query.group.split(','); + } - const tags = await fetchTags(query, groupsQuery, req.query.limit); + const tags = await fetchTags(query, groupsQuery, req.query.limit); - res.send(tags); + res.send(tags); } module.exports = { - fetchTags: fetchTagsApi, + fetchTags: fetchTagsApi, }; diff --git a/webpack.config.babel.js b/webpack.config.babel.js index 11d4ac0f..52e2650a 100644 --- a/webpack.config.babel.js +++ b/webpack.config.babel.js @@ -4,70 +4,70 @@ import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import autoprefixer from 'autoprefixer'; export default { - entry: './assets/js/main.js', - output: { - filename: 'bundle.js', - path: path.join(__dirname, 'public/js'), - }, - module: { - rules: [ - { - test: /\.vue$/, - include: [ - path.resolve(__dirname, 'assets'), - ], - loader: 'vue-loader', - options: { - preserveWhitespace: false, - }, - }, - { - test: /\.js$/, - exclude: /node_modules/, - use: [ - { - loader: 'babel-loader', - options: { - babelrc: false, - plugins: [ - '@babel/plugin-proposal-object-rest-spread', - ], - }, - }, - 'eslint-loader', - ], - }, - { - test: /\.scss$/, - use: [ - MiniCssExtractPlugin.loader, - 'css-loader?sourceMap', - { - loader: 'postcss-loader', - options: { - plugins: [autoprefixer], - sourceMap: true, - }, - }, - 'sass-loader?sourceMap', - ], - }, - { - test: /\.svg/, - use: 'raw-loader', - }, - ], - }, - plugins: [ - new VueLoaderPlugin(), - new MiniCssExtractPlugin({ - filename: '../css/style.css', - }), - ], - resolve: { - alias: { - theme: path.join(__dirname, 'assets/css/_theme.scss'), - config: path.join(__dirname, `assets/js/config/${process.env.NODE_ENV || 'default'}.js`), - }, - }, + entry: './assets/js/main.js', + output: { + filename: 'bundle.js', + path: path.join(__dirname, 'public/js'), + }, + module: { + rules: [ + { + test: /\.vue$/, + include: [ + path.resolve(__dirname, 'assets'), + ], + loader: 'vue-loader', + options: { + preserveWhitespace: false, + }, + }, + { + test: /\.js$/, + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + options: { + babelrc: false, + plugins: [ + '@babel/plugin-proposal-object-rest-spread', + ], + }, + }, + 'eslint-loader', + ], + }, + { + test: /\.scss$/, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader?sourceMap', + { + loader: 'postcss-loader', + options: { + plugins: [autoprefixer], + sourceMap: true, + }, + }, + 'sass-loader?sourceMap', + ], + }, + { + test: /\.svg/, + use: 'raw-loader', + }, + ], + }, + plugins: [ + new VueLoaderPlugin(), + new MiniCssExtractPlugin({ + filename: '../css/style.css', + }), + ], + resolve: { + alias: { + theme: path.join(__dirname, 'assets/css/_theme.scss'), + config: path.join(__dirname, `assets/js/config/${process.env.NODE_ENV || 'default'}.js`), + }, + }, };