Added The Flourish, adapted Arch Angel scraper.
| After Width: | Height: | Size: 2.1 KiB | 
| After Width: | Height: | Size: 2.1 KiB | 
| After Width: | Height: | Size: 2.1 KiB | 
| After Width: | Height: | Size: 5.1 KiB | 
| After Width: | Height: | Size: 2.9 KiB | 
| After Width: | Height: | Size: 2.9 KiB | 
| After Width: | Height: | Size: 2.9 KiB | 
| After Width: | Height: | Size: 2.3 KiB | 
| After Width: | Height: | Size: 3.7 KiB | 
| After Width: | Height: | Size: 2.3 KiB | 
| After Width: | Height: | Size: 3.0 KiB | 
| After Width: | Height: | Size: 8.2 KiB | 
| After Width: | Height: | Size: 2.3 KiB | 
| After Width: | Height: | Size: 6.2 KiB | 
| After Width: | Height: | Size: 44 KiB | 
| After Width: | Height: | Size: 98 KiB | 
| After Width: | Height: | Size: 78 KiB | 
| After Width: | Height: | Size: 65 KiB | 
| After Width: | Height: | Size: 16 KiB | 
| Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB | 
| After Width: | Height: | Size: 52 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 103 KiB | 
| After Width: | Height: | Size: 5.1 KiB | 
| After Width: | Height: | Size: 65 KiB | 
| After Width: | Height: | Size: 29 KiB | 
| After Width: | Height: | Size: 12 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 2.9 KiB | 
| After Width: | Height: | Size: 2.9 KiB | 
| After Width: | Height: | Size: 2.9 KiB | 
| After Width: | Height: | Size: 6.0 KiB | 
| After Width: | Height: | Size: 38 KiB | 
| After Width: | Height: | Size: 6.0 KiB | 
| After Width: | Height: | Size: 23 KiB | 
| After Width: | Height: | Size: 13 KiB | 
| After Width: | Height: | Size: 14 KiB | 
| After Width: | Height: | Size: 70 KiB | 
| Before Width: | Height: | Size: 32 KiB | 
|  | @ -855,6 +855,10 @@ const tags = [ | |||
| 		name: 'pissing', | ||||
| 		slug: 'pissing', | ||||
| 	}, | ||||
| 	{ | ||||
| 		name: 'pole dancing', | ||||
| 		slug: 'pole-dancing', | ||||
| 	}, | ||||
| 	{ | ||||
| 		name: 'POV', | ||||
| 		slug: 'pov', | ||||
|  | @ -2423,6 +2427,10 @@ const aliases = [ | |||
| 		name: 'coroas', | ||||
| 		for: 'milf', | ||||
| 	}, | ||||
| 	{ | ||||
| 		name: 'strip pole dancing', | ||||
| 		for: 'strip-pole-dancing', | ||||
| 	}, | ||||
| ]; | ||||
| 
 | ||||
| const priorities = [ // higher index is higher priority
 | ||||
|  |  | |||
|  | @ -323,6 +323,11 @@ const networks = [ | |||
| 		name: 'First Anal Quest', | ||||
| 		url: 'http://www.firstanalquest.com', | ||||
| 	}, | ||||
| 	{ | ||||
| 		slug: 'theflourish', | ||||
| 		name: 'The Flourish', | ||||
| 		url: 'https://theflourish.io', | ||||
| 	}, | ||||
| 	{ | ||||
| 		slug: 'forbondage', | ||||
| 		name: 'ForBondage', | ||||
|  |  | |||
|  | @ -717,6 +717,9 @@ const sites = [ | |||
| 		slug: 'archangel', | ||||
| 		name: 'ArchAngel', | ||||
| 		url: 'https://www.archangelvideo.com', | ||||
| 		parameters: { | ||||
| 			path: '/tour', | ||||
| 		}, | ||||
| 	}, | ||||
| 	// ASSYLUM
 | ||||
| 	{ | ||||
|  | @ -3842,6 +3845,39 @@ const sites = [ | |||
| 			layout: 'c', | ||||
| 		}, | ||||
| 	}, | ||||
| 	// FLOURISH
 | ||||
| 	{ | ||||
| 		slug: 'theflourishxxx', | ||||
| 		name: 'The Flourish XXX', | ||||
| 		url: 'https://tour.theflourishxxx.com', | ||||
| 		parent: 'theflourish', | ||||
| 	}, | ||||
| 	{ | ||||
| 		slug: 'theflourishamateurs', | ||||
| 		name: 'The Flourish Amateurs', | ||||
| 		url: 'https://tour.theflourishamateurs.com', | ||||
| 		parent: 'theflourish', | ||||
| 	}, | ||||
| 	{ | ||||
| 		slug: 'theflourishfetish', | ||||
| 		name: 'The Flourish Fetish', | ||||
| 		url: 'https://tour.theflourishfetish.com', | ||||
| 		parent: 'theflourish', | ||||
| 	}, | ||||
| 	{ | ||||
| 		slug: 'theflourishpov', | ||||
| 		name: 'The Flourish POV', | ||||
| 		url: 'https://tour.theflourishxxx.com', | ||||
| 		tags: ['pov'], | ||||
| 		parent: 'theflourish', | ||||
| 	}, | ||||
| 	{ | ||||
| 		slug: 'milfcandy', | ||||
| 		name: 'MILF Candy', | ||||
| 		url: 'https://tour.milfcandy.com', | ||||
| 		tags: ['milf'], | ||||
| 		parent: 'theflourish', | ||||
| 	}, | ||||
| 	// FOR BONDAGE
 | ||||
| 	{ | ||||
| 		name: 'Crowd Bondage', | ||||
|  |  | |||
|  | @ -1,5 +1,7 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| // ALSO USED BY THE FLOURISH
 | ||||
| 
 | ||||
| const unprint = require('unprint'); | ||||
| 
 | ||||
| const slugify = require('../utils/slugify'); | ||||
|  | @ -29,12 +31,27 @@ function scrapeAll(scenes) { | |||
| 		})); | ||||
| 
 | ||||
| 		const poster = query.img('img.mainThumb'); | ||||
| 		const previewCount = query.number('img.mainThumb', { attribute: 'cnt' }); | ||||
| 
 | ||||
| 		if (poster && !placeholder.test(poster)) { | ||||
| 			release.poster = poster; | ||||
| 			const posterFallbacks = [ | ||||
| 				poster.replace('-1x', '-3x'), | ||||
| 				poster.replace('-1x', '-2x'), | ||||
| 				poster.replace('-1x', '-4x'), | ||||
| 				poster, | ||||
| 			]; | ||||
| 
 | ||||
| 			release.poster = posterFallbacks; | ||||
| 		} | ||||
| 
 | ||||
| 		release.photoCount = query.number('.timeDate'); | ||||
| 		if (previewCount) { | ||||
| 			release.photos = Array.from( | ||||
| 				{ length: previewCount - 1 }, | ||||
| 				(value, index) => [3, 2, 4, 1].map((scale) => unprint.prefixUrl(query.img('img.mainThumb', { attribute: `src${index + 1}_${scale}x` }))).filter(Boolean), // 4x is unnecessarily big and possibly upscaled
 | ||||
| 			).filter(Boolean); | ||||
| 		} | ||||
| 
 | ||||
| 		release.photoCount = query.number('.timeDate', { match: /(\d+) photos/i, matchIndex: 1 }); | ||||
| 
 | ||||
| 		release.entryId = getEntryId(release); | ||||
| 
 | ||||
|  | @ -42,7 +59,7 @@ function scrapeAll(scenes) { | |||
| 	}); | ||||
| } | ||||
| 
 | ||||
| function scrapeScene({ query, html }, { url }) { | ||||
| function scrapeScene({ query, html }, { url, entity, baseRelease }) { | ||||
| 	const release = { url }; | ||||
| 
 | ||||
| 	release.title = query.content('.title h2'); | ||||
|  | @ -56,18 +73,35 @@ function scrapeScene({ query, html }, { url }) { | |||
| 		url: unprint.query.url(actorEl, null), | ||||
| 	})); | ||||
| 
 | ||||
| 	const poster = query.img('.update_thumb') || html.match(/poster="(.*\.jpg)"/)?.[1]; | ||||
| 	const poster = unprint.prefixUrl(query.img('.update_thumb') || html.match(/poster="(.*\.jpg)"/)?.[1], entity.url); | ||||
| 
 | ||||
| 	if (poster && !placeholder.test(poster)) { | ||||
| 		release.poster = poster; | ||||
| 		const posterFallbacks = [ | ||||
| 			poster.replace('-1x', '-3x'), | ||||
| 			poster.replace('-1x', '-2x'), | ||||
| 			poster.replace('-1x', '-4x'), | ||||
| 			poster, | ||||
| 		]; | ||||
| 
 | ||||
| 		// scene page poster usually different from overview page, don't replace
 | ||||
| 		if (baseRelease?.poster && baseRelease.poster !== poster) { | ||||
| 			release.photos = baseRelease.photos | ||||
| 				? [posterFallbacks, ...baseRelease.photos] | ||||
| 				: [posterFallbacks]; | ||||
| 		} else { | ||||
| 			release.poster = posterFallbacks; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	release.trailer = html.match(/src="(.*\.mp4)"/)?.[1]; | ||||
| 	const trailer = html.match(/src="(.*\.mp4)"/)?.[1]; | ||||
| 
 | ||||
| 	release.photoCount = query.number('.info', { match: /(\d+) photos/i, matchIndex: 1 }); | ||||
| 	if (trailer) { | ||||
| 		release.trailer = unprint.prefixUrl(encodeURI(trailer), entity.url); | ||||
| 	} | ||||
| 
 | ||||
| 	release.tags = query.contents('.info .tags a'); | ||||
| 
 | ||||
| 	release.photoCount = query.number('.info', { match: /(\d+) photos/i, matchIndex: 1 }); | ||||
| 	release.entryId = getEntryId(release); | ||||
| 
 | ||||
| 	return release; | ||||
|  | @ -129,8 +163,8 @@ function scrapeProfile({ query, element }, { url, entity }) { | |||
| 	return profile; | ||||
| } | ||||
| 
 | ||||
| async function fetchLatest(channel, page = 1) { | ||||
| 	const url = `${channel.url}/tour/categories/movies_${page}_d.html`; | ||||
| async function fetchLatest(channel, page = 1, context) { | ||||
| 	const url = `${channel.url}${context.parameters.path || ''}/categories/movies_${page}_d.html`; | ||||
| 	const res = await unprint.get(url, { selectAll: '.item-video' }); | ||||
| 
 | ||||
| 	if (res.ok) { | ||||
|  | @ -140,11 +174,11 @@ async function fetchLatest(channel, page = 1) { | |||
| 	return res.status; | ||||
| } | ||||
| 
 | ||||
| async function fetchProfile({ name: actorName, url: actorUrl }, { entity, include }) { | ||||
| async function fetchProfile({ name: actorName, url: actorUrl }, { entity, include, parameters }) { | ||||
| 	const res = await [ | ||||
| 		actorUrl, | ||||
| 		`${entity.url}/tour/models/${slugify(actorName, '-')}.html`, | ||||
| 		`${entity.url}/tour/models/${slugify(actorName, '')}.html`, | ||||
| 		`${entity.url}${parameters.path || ''}/models/${slugify(actorName, '-')}.html`, | ||||
| 		`${entity.url}${parameters.path || ''}/models/${slugify(actorName, '')}.html`, | ||||
| 	].reduce(async (chain, url) => { | ||||
| 		const prevRes = await chain; | ||||
| 
 | ||||
|  |  | |||
|  | @ -153,6 +153,7 @@ const scrapers = { | |||
| 		sexyhub: mindgeek, | ||||
| 		spizoo, | ||||
| 		swallowsalon: julesjordan, | ||||
| 		theflourish: archangel, | ||||
| 		teencoreclub, | ||||
| 		teenmegaworld, | ||||
| 		teamskeet, | ||||
|  | @ -294,6 +295,11 @@ const scrapers = { | |||
| 		silviasaint: famedigital, | ||||
| 		spizoo, | ||||
| 		swallowed: mikeadriano, | ||||
| 		milfcandy: archangel, | ||||
| 		theflourishamateurs: archangel, | ||||
| 		theflourishpov: archangel, | ||||
| 		theflourishfetish: archangel, | ||||
| 		theflourishxxx: archangel, | ||||
| 		teamskeet, | ||||
| 		teencoreclub, | ||||
| 		teenmegaworld, | ||||
|  |  | |||
|  | @ -90,9 +90,6 @@ function scrapeProfile({ query }, url, entity) { | |||
| 	profile.avatar = query.img('.model-profile-image-picture source', { origin: entity.url, attribute: 'srcset' }) || query.img('.model-profile-image-picture img', { origin: entity.url }); | ||||
| 	profile.scenes = scrapeAll(unprint.initAll(query.all('.video-list .thumb')), entity); | ||||
| 
 | ||||
| 	console.log(bio); | ||||
| 	console.log(profile); | ||||
| 
 | ||||
| 	return profile; | ||||
| } | ||||
| 
 | ||||
|  |  | |||