diff --git a/config/default.js b/config/default.js index 94f527e1..da913c15 100644 --- a/config/default.js +++ b/config/default.js @@ -171,6 +171,9 @@ module.exports = { 'pervertgallery', 'povperverts', ], + 'wankzvr', + 'milfvr', + 'tranzvr', 'topwebmodels', 'pascalssubsluts', 'kellymadison', @@ -252,7 +255,7 @@ module.exports = { thumbnailQuality: 100, lazySize: 90, lazyQuality: 90, - trailerQuality: [480, 720, 360, 1080, 320, 540, 2160, 270, 240, 180], + trailerQuality: [480, 540, 360, 720, 1080, 320, 1440, 1600, 1920, 2160, 270, 240, 180], limit: 25, // max number of photos per release streamConcurrency: 2, // max number of video streams (m3u8 etc.) to fetch and process at once }, diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js index e03d367a..55eb57ae 100644 --- a/migrations/20190325001339_releases.js +++ b/migrations/20190325001339_releases.js @@ -34,6 +34,7 @@ exports.up = knex => Promise.resolve() table.integer('quality', 6); table.integer('width', 6); table.integer('height', 6); + table.boolean('vr'); table.float('entropy'); table.float('sharpness'); diff --git a/public/img/logos/wankzvr/favicon.png b/public/img/logos/wankzvr/favicon.png new file mode 100644 index 00000000..4516ea47 Binary files /dev/null and b/public/img/logos/wankzvr/favicon.png differ diff --git a/public/img/logos/wankzvr/favicon_dark.png b/public/img/logos/wankzvr/favicon_dark.png new file mode 100644 index 00000000..93b58d0b Binary files /dev/null and b/public/img/logos/wankzvr/favicon_dark.png differ diff --git a/public/img/logos/wankzvr/favicon_light.png b/public/img/logos/wankzvr/favicon_light.png new file mode 100644 index 00000000..bd1a599a Binary files /dev/null and b/public/img/logos/wankzvr/favicon_light.png differ diff --git a/public/img/logos/wankzvr/lazy/favicon.png b/public/img/logos/wankzvr/lazy/favicon.png new file mode 100644 index 00000000..3289c969 Binary files /dev/null and b/public/img/logos/wankzvr/lazy/favicon.png differ diff --git a/public/img/logos/wankzvr/lazy/favicon_dark.png b/public/img/logos/wankzvr/lazy/favicon_dark.png new file mode 100644 index 00000000..2221caae Binary files /dev/null and b/public/img/logos/wankzvr/lazy/favicon_dark.png differ diff --git a/public/img/logos/wankzvr/lazy/favicon_light.png b/public/img/logos/wankzvr/lazy/favicon_light.png new file mode 100644 index 00000000..409a1577 Binary files /dev/null and b/public/img/logos/wankzvr/lazy/favicon_light.png differ diff --git a/public/img/logos/wankzvr/lazy/milfvr.png b/public/img/logos/wankzvr/lazy/milfvr.png new file mode 100644 index 00000000..691f1fd5 Binary files /dev/null and b/public/img/logos/wankzvr/lazy/milfvr.png differ diff --git a/public/img/logos/wankzvr/lazy/network.png b/public/img/logos/wankzvr/lazy/network.png new file mode 100644 index 00000000..ade9b4ff Binary files /dev/null and b/public/img/logos/wankzvr/lazy/network.png differ diff --git a/public/img/logos/wankzvr/lazy/transvr.png b/public/img/logos/wankzvr/lazy/transvr.png new file mode 100644 index 00000000..d3747aef Binary files /dev/null and b/public/img/logos/wankzvr/lazy/transvr.png differ diff --git a/public/img/logos/wankzvr/lazy/tranzvr.png b/public/img/logos/wankzvr/lazy/tranzvr.png new file mode 100644 index 00000000..c2664670 Binary files /dev/null and b/public/img/logos/wankzvr/lazy/tranzvr.png differ diff --git a/public/img/logos/wankzvr/lazy/wankzvr.png b/public/img/logos/wankzvr/lazy/wankzvr.png new file mode 100644 index 00000000..d1f701bc Binary files /dev/null and b/public/img/logos/wankzvr/lazy/wankzvr.png differ diff --git a/public/img/logos/wankzvr/milfvr.png b/public/img/logos/wankzvr/milfvr.png new file mode 100644 index 00000000..6739b230 Binary files /dev/null and b/public/img/logos/wankzvr/milfvr.png differ diff --git a/public/img/logos/wankzvr/misc/milf-vr.svg b/public/img/logos/wankzvr/misc/milf-vr.svg new file mode 100644 index 00000000..95ded5c1 --- /dev/null +++ b/public/img/logos/wankzvr/misc/milf-vr.svg @@ -0,0 +1 @@ + diff --git a/public/img/logos/wankzvr/misc/milf-vr_dark.png b/public/img/logos/wankzvr/misc/milf-vr_dark.png new file mode 100644 index 00000000..15adac96 Binary files /dev/null and b/public/img/logos/wankzvr/misc/milf-vr_dark.png differ diff --git a/public/img/logos/wankzvr/misc/milf-vr_light.png b/public/img/logos/wankzvr/misc/milf-vr_light.png new file mode 100644 index 00000000..75f499d2 Binary files /dev/null and b/public/img/logos/wankzvr/misc/milf-vr_light.png differ diff --git a/public/img/logos/wankzvr/misc/tranz-vr.svg b/public/img/logos/wankzvr/misc/tranz-vr.svg new file mode 100644 index 00000000..cdfc547f --- /dev/null +++ b/public/img/logos/wankzvr/misc/tranz-vr.svg @@ -0,0 +1 @@ + diff --git a/public/img/logos/wankzvr/misc/wankz-vr.svg b/public/img/logos/wankzvr/misc/wankz-vr.svg new file mode 100644 index 00000000..2cd9a9ef --- /dev/null +++ b/public/img/logos/wankzvr/misc/wankz-vr.svg @@ -0,0 +1 @@ + diff --git a/public/img/logos/wankzvr/network.png b/public/img/logos/wankzvr/network.png new file mode 100644 index 00000000..f49489a8 Binary files /dev/null and b/public/img/logos/wankzvr/network.png differ diff --git a/public/img/logos/wankzvr/thumbs/favicon.png b/public/img/logos/wankzvr/thumbs/favicon.png new file mode 100644 index 00000000..3289c969 Binary files /dev/null and b/public/img/logos/wankzvr/thumbs/favicon.png differ diff --git a/public/img/logos/wankzvr/thumbs/favicon_dark.png b/public/img/logos/wankzvr/thumbs/favicon_dark.png new file mode 100644 index 00000000..2221caae Binary files /dev/null and b/public/img/logos/wankzvr/thumbs/favicon_dark.png differ diff --git a/public/img/logos/wankzvr/thumbs/favicon_light.png b/public/img/logos/wankzvr/thumbs/favicon_light.png new file mode 100644 index 00000000..409a1577 Binary files /dev/null and b/public/img/logos/wankzvr/thumbs/favicon_light.png differ diff --git a/public/img/logos/wankzvr/thumbs/milfvr.png b/public/img/logos/wankzvr/thumbs/milfvr.png new file mode 100644 index 00000000..f4258595 Binary files /dev/null and b/public/img/logos/wankzvr/thumbs/milfvr.png differ diff --git a/public/img/logos/wankzvr/thumbs/network.png b/public/img/logos/wankzvr/thumbs/network.png new file mode 100644 index 00000000..dae50b90 Binary files /dev/null and b/public/img/logos/wankzvr/thumbs/network.png differ diff --git a/public/img/logos/wankzvr/thumbs/transvr.png b/public/img/logos/wankzvr/thumbs/transvr.png new file mode 100644 index 00000000..bdd661c4 Binary files /dev/null and b/public/img/logos/wankzvr/thumbs/transvr.png differ diff --git a/public/img/logos/wankzvr/thumbs/tranzvr.png b/public/img/logos/wankzvr/thumbs/tranzvr.png new file mode 100644 index 00000000..31e4fa20 Binary files /dev/null and b/public/img/logos/wankzvr/thumbs/tranzvr.png differ diff --git a/public/img/logos/wankzvr/thumbs/wankzvr.png b/public/img/logos/wankzvr/thumbs/wankzvr.png new file mode 100644 index 00000000..9a79d1dd Binary files /dev/null and b/public/img/logos/wankzvr/thumbs/wankzvr.png differ diff --git a/public/img/logos/wankzvr/tranzvr.png b/public/img/logos/wankzvr/tranzvr.png new file mode 100644 index 00000000..c481ea36 Binary files /dev/null and b/public/img/logos/wankzvr/tranzvr.png differ diff --git a/public/img/logos/wankzvr/wankzvr.png b/public/img/logos/wankzvr/wankzvr.png new file mode 100644 index 00000000..76b3b6f4 Binary files /dev/null and b/public/img/logos/wankzvr/wankzvr.png differ diff --git a/seeds/00_tags.js b/seeds/00_tags.js index 9649362a..8dad2d8f 100644 --- a/seeds/00_tags.js +++ b/seeds/00_tags.js @@ -57,6 +57,14 @@ const groups = [ ]; const tags = [ + { + name: '180°', + slug: '180', + }, + { + name: '60 FPS', + slug: '60fps', + }, { name: '3D', slug: '3d', @@ -72,6 +80,16 @@ const tags = [ slug: '5k', description: 'Available in very high quality 5K resolution.', }, + { + name: '7K', + slug: '7k', + description: 'Available in super high quality 7K resolution.', + }, + { + name: '8K', + slug: '8k', + description: 'Available in extremely high quality 8K resolution.', + }, { name: '69', slug: '69', diff --git a/seeds/01_networks.js b/seeds/01_networks.js index 02bcd42a..07ae1015 100644 --- a/seeds/01_networks.js +++ b/seeds/01_networks.js @@ -543,6 +543,11 @@ const networks = [ url: 'https://www.vixen.com', description: 'Vixen.com features the world’s finest cinematic adult films with 4K quality and high-end erotic photography.', }, + { + slug: 'wankzvr', + name: 'WankzVR', + url: 'https://www.wankzvr.com', + }, { slug: 'pierrewoodman', name: 'Pierre Woodman', diff --git a/seeds/02_sites.js b/seeds/02_sites.js index 0e7f719b..81031a78 100644 --- a/seeds/02_sites.js +++ b/seeds/02_sites.js @@ -9365,6 +9365,28 @@ const sites = [ 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.', }, + // WANKZ VR + { + name: 'WankzVR', + slug: 'wankzvr', + url: 'https://www.wankzvr.com', + tags: ['vr'], + parent: 'wankzvr', + }, + { + name: 'MilfVR', + slug: 'milfvr', + url: 'https://www.milfvr.com', + tags: ['vr', 'milf'], + parent: 'wankzvr', + }, + { + name: 'TranzVR', + slug: 'tranzvr', + url: 'https://www.tranzvr.com', + tags: ['vr', 'transsexual'], + parent: 'wankzvr', + }, // WHALE MEMBER { name: 'Cum 4K', diff --git a/src/deep.js b/src/deep.js index 30ec19bc..bdfca6d1 100644 --- a/src/deep.js +++ b/src/deep.js @@ -56,10 +56,17 @@ async function fetchScene(scraper, url, entity, baseRelease, options) { } if (scraper.scrapeScene) { - const res = await qu.get(url); + const session = qu.session(); + const res = await qu.get(url, null, null, { session }); + const cookie = await session._sessionOptions.cookieJar.get(url); if (res.ok) { - return scraper.scrapeScene(res.item, url, entity, baseRelease, options); + return scraper.scrapeScene(res.item, url, entity, baseRelease, options, { + session, + headers: res.headers, + cookieJar: session._sessionOptions.cookieJar, + cookie, + }); } return res.status; diff --git a/src/media.js b/src/media.js index 36ff092e..ca122211 100644 --- a/src/media.js +++ b/src/media.js @@ -108,6 +108,8 @@ function toBaseSource(rawSource) { if (rawSource.interval) baseSource.interval = rawSource.interval; if (rawSource.concurrency) baseSource.concurrency = rawSource.concurrency; + if (rawSource.vr) baseSource.vr = rawSource.vr; + if (rawSource.credit !== undefined) baseSource.credit = rawSource.credit; if (rawSource.comment) baseSource.comment = rawSource.comment; if (rawSource.group) baseSource.group = rawSource.group; @@ -155,17 +157,19 @@ function baseSourceToBaseMedia(baseSource, role, metadata) { function sortBaseTrailersByQuality(sources, role) { if (role === 'trailers') { const sortedSources = sources.sort((sourceA, sourceB) => { - if (config.media.trailerQuality.indexOf(sourceA.quality) > config.media.trailerQuality.indexOf(sourceB.quality)) { + if (config.media.trailerQuality.includes(sourceB.quality) && config.media.trailerQuality.indexOf(sourceA.quality) > config.media.trailerQuality.indexOf(sourceB.quality)) { return 1; } - if (config.media.trailerQuality.indexOf(sourceA.quality) < config.media.trailerQuality.indexOf(sourceB.quality)) { + if (config.media.trailerQuality.includes(sourceA.quality) && config.media.trailerQuality.indexOf(sourceA.quality) < config.media.trailerQuality.indexOf(sourceB.quality)) { return -1; } return 0; }); + console.log(sortedSources); + return sortedSources; } @@ -632,6 +636,7 @@ function curateMediaEntry(media, index) { size: media.meta.size, width: media.meta.width, height: media.meta.height, + vr: media.vr, entropy: media.meta.entropy, sharpness: media.meta.sharpness, source: media.src, diff --git a/src/scrapers/scrapers.js b/src/scrapers/scrapers.js index 41f708ce..4cd518b1 100644 --- a/src/scrapers/scrapers.js +++ b/src/scrapers/scrapers.js @@ -57,6 +57,7 @@ const traxxx = require('./traxxx'); const vivid = require('./vivid'); const vixen = require('./vixen'); const vogov = require('./vogov'); +const wankzvr = require('./wankzvr'); const whalemember = require('./whalemember'); const xempire = require('./xempire'); @@ -138,6 +139,7 @@ const scrapers = { vivid, vixen, vogov, + wankzvr, whalemember, xempire, }, @@ -212,6 +214,7 @@ const scrapers = { men: mindgeek, metrohd: mindgeek, milehighmedia: mindgeek, + milfvr: wankzvr, mofos: mindgeek, mugfucked: fullpornnetwork, naughtyamerica, @@ -248,6 +251,7 @@ const scrapers = { topwebmodels, transangels: mindgeek, transbella: porndoe, + tranzvr: wankzvr, trueanal: mikeadriano, tushy: vixen, tushyraw: vixen, @@ -255,6 +259,7 @@ const scrapers = { vipsexvault: porndoe, vixen, vrcosplayx: badoink, + wankzvr, wicked: gamma, wildoncam: cherrypimps, xempire, diff --git a/src/scrapers/wankzvr.js b/src/scrapers/wankzvr.js new file mode 100644 index 00000000..6f541dcf --- /dev/null +++ b/src/scrapers/wankzvr.js @@ -0,0 +1,177 @@ +'use strict'; + +const qu = require('../utils/qu'); +const http = require('../utils/http'); +const slugify = require('../utils/slugify'); + +async function getTrailerUrl(release, channel, request) { + const csrfToken = request.cookie.match('csrfst=(.*?);')?.[1]; + + if (!csrfToken) { + return null; + } + + const res = await http.post(`${channel.url}/ajax/player-config.json`, { + item_id: release.entryId, + }, { + headers: { + 'X-CSRF-Token': csrfToken, + }, + session: request.session, + encodeJSON: false, + }); + + if (res.ok) { + const trailers = res.body.streams.map(trailer => ({ + src: trailer.url, + quality: Number(trailer.id?.match(/\d+/)?.[0] || trailer?.name.match(/\d+/)?.[0]), + vr: true, + })); + + return { + trailers, + poster: qu.prefixUrl(res.body.poster, res.body.thumbCDN), + }; + } + + return null; +} + +function scrapeAll(scenes, channel) { + return scenes.map(({ query }) => { + const release = {}; + + release.url = query.url('a', 'href', { origin: channel.url }); + release.entryId = new URL(release.url).pathname.match(/(\d+)\/?$/)?.[1]; + + release.title = query.cnt('.card__h'); + release.date = query.date('.card__date', 'D MMMM, YYYY'); + + release.actors = query.all('.card__links a').map(el => ({ + name: qu.query.cnt(el), + url: qu.query.url(el, null, 'href', { origin: channel.url }), + })); + + const poster = query.srcset('picture source[type="image/jpeg"]', 'data-srcset') + || query.srcset('picture source[type="image/jpeg"]', 'srcset') + || query.srcset('.video__cover', 'srcset'); + + if (poster?.[0]) { + release.poster = [ + poster[0].replace(/small|tiny/, 'large'), + ...poster, + ]; + + release.teaser = poster[0].replace(/\b(cover|hero|\d+)\/[a-z0-9_]+\.[a-z]+$/i, 'roll.webm'); // actually how site generates teaser URL + } + + release.channel = channel.slug; // avoid being assigned to WankzVR network + + return release; + }); +} + +async function scrapeScene({ query }, url, channel, baseRelease, options, request) { + const release = {}; + + release.entryId = new URL(url).pathname.match(/(\d+)\/?$/)?.[1]; + + release.title = query.cnt('.detail__title'); + release.description = query.cnt('.detail__txt'); + + release.date = query.date('.detail__date', 'D MMMM, YYYY'); + release.duration = query.number('.time') * 60; + + release.actors = (query.all('.detail__header-lg .detail__models a') || query.all('.detail__header-sm .detail__models a')).map(el => ({ + name: qu.query.cnt(el), + url: qu.query.url(el, null, 'href', { origin: channel.url }), + })); + + release.tags = query.cnts('.tag-list .tag').concat(query.cnts('.detail__specs-list .detail__specs-item')); + + release.photos = query.all('.photo-strip__slide').map(el => ([ + qu.query.img(el, null, 'data-src'), + qu.query.img(el, 'img', 'src'), + ])); + + if (options.includePosters || options.includeTrailers) { + const { trailers, poster } = await getTrailerUrl(release, channel, request); + + release.trailer = trailers; + release.poster = poster; + } + + return release; +} + +async function fetchActorScenes({ query }, url, entity, page = 1, accScenes = []) { + const scenes = scrapeAll(qu.initAll(query.all('.cards-list .card')), entity); + const hasNextPage = !query.exists('.pagenav__link.inactive'); + + if (hasNextPage) { + const { origin, pathname, searchParams } = new URL(url); + searchParams.set('p', page + 1); + + const res = await qu.get(`${origin}${pathname}?${searchParams}`); + + if (res.ok) { + return fetchActorScenes(res.item, url, entity, page + 1, accScenes.concat(scenes)); + } + } + + return accScenes.concat(scenes); +} + +async function scrapeProfile({ query }, url, entity, options) { + const profile = {}; + + const bio = query.all('.person__meta__item').reduce((acc, el) => ({ + ...acc, + [slugify(qu.query.cnt(el, '.person__meta__label'))]: qu.query.text(el), + }), {}); + + profile.description = query.cnt('.person__content'); + + profile.gender = entity.slug === 'tranzvr' ? 'transsexual' : 'female'; + profile.age = bio.age; + + profile.birthPlace = bio.birthplace; + + profile.height = parseInt(bio.height, 10); + profile.measurements = bio.measurements; + + profile.avatar = query.srcset('.person__avatar img'); + + if (options.includeActorScenes) { + profile.scenes = await fetchActorScenes({ query }, url, entity); + } + + return profile; +} + +async function fetchLatest(channel, page) { + const res = await qu.getAll(`${channel.url}/videos?o=d&p=${page}`, '.cards-list .card'); + + if (res.ok) { + return scrapeAll(res.items, channel); + } + + return res.status; +} + +async function fetchProfile(baseActor, { entity }, options) { + const url = `${entity.url}/${baseActor.slug}`; + const res = await qu.get(url); + + if (res.ok) { + return scrapeProfile(res.item, url, entity, options); + } + + return res.status; +} + +module.exports = { + fetchLatest, + scrapeScene, + fetchProfile, +};