Compare commits
	
		
			No commits in common. "33bad4466e1636e4a753035cdab012599208ef11" and "958c6d83fafa6602081dfa40b85bc4cb6608f050" have entirely different histories.
		
	
	
		
			33bad4466e
			...
			958c6d83fa
		
	
		|  | @ -189,8 +189,12 @@ module.exports = { | ||||||
| 			'hotcrazymess', | 			'hotcrazymess', | ||||||
| 			'thatsitcomshow', | 			'thatsitcomshow', | ||||||
| 		], | 		], | ||||||
|  | 		[ | ||||||
|  | 			// Adult DVD Empire
 | ||||||
|  | 			'elegantangel', | ||||||
|  | 			'westcoastproductions', | ||||||
|  | 		], | ||||||
| 		'21sextury', | 		'21sextury', | ||||||
| 		'adultempire', |  | ||||||
| 		'julesjordan', | 		'julesjordan', | ||||||
| 		'dorcelclub', | 		'dorcelclub', | ||||||
| 		'bang', | 		'bang', | ||||||
|  |  | ||||||
|  | @ -1,65 +0,0 @@ | ||||||
| const config = require('config'); |  | ||||||
| 
 |  | ||||||
| exports.up = async (knex) => { |  | ||||||
| 	await knex.schema.alterTable('entities', (table) => { |  | ||||||
| 		// internal options, as opposed to parameters for scraper options
 |  | ||||||
| 		table.json('options'); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	await knex.schema.alterTable('releases', (table) => { |  | ||||||
| 		table.dropForeign('entity_id'); |  | ||||||
| 
 |  | ||||||
| 		table.foreign('entity_id') |  | ||||||
| 			.references('id') |  | ||||||
| 			.inTable('entities') |  | ||||||
| 			.onDelete('cascade'); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	await knex.schema.alterTable('releases_caps', (table) => { |  | ||||||
| 		table.unique(['release_id', 'media_id']); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	await knex.schema.createTable('movies_tags', (table) => { |  | ||||||
| 		table.integer('tag_id') |  | ||||||
| 			.references('id') |  | ||||||
| 			.inTable('tags'); |  | ||||||
| 
 |  | ||||||
| 		table.integer('movie_id') |  | ||||||
| 			.notNullable() |  | ||||||
| 			.references('id') |  | ||||||
| 			.inTable('movies') |  | ||||||
| 			.onDelete('cascade'); |  | ||||||
| 
 |  | ||||||
| 		table.text('original_tag'); |  | ||||||
| 
 |  | ||||||
| 		table.text('source') |  | ||||||
| 			.defaultTo('scraper'); |  | ||||||
| 
 |  | ||||||
| 		table.unique(['tag_id', 'movie_id']); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	await knex.raw('GRANT ALL ON ALL TABLES IN SCHEMA public TO :visitor;', { |  | ||||||
| 		visitor: knex.raw(config.database.query.user), |  | ||||||
| 	}); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| exports.down = async (knex) => { |  | ||||||
| 	await knex.schema.alterTable('entities', (table) => { |  | ||||||
| 		table.dropColumn('options'); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	await knex.schema.alterTable('releases', (table) => { |  | ||||||
| 		table.dropForeign('entity_id'); |  | ||||||
| 
 |  | ||||||
| 		table.foreign('entity_id') |  | ||||||
| 			.references('id') |  | ||||||
| 			.inTable('entities') |  | ||||||
| 			.onDelete('no action'); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	await knex.schema.alterTable('releases_caps', (table) => { |  | ||||||
| 		table.dropUnique(['release_id', 'media_id']); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	await knex.schema.dropTable('movies_tags'); |  | ||||||
| }; |  | ||||||
|  | @ -1,12 +1,12 @@ | ||||||
| { | { | ||||||
|     "name": "traxxx", |     "name": "traxxx", | ||||||
|     "version": "1.239.0", |     "version": "1.238.8", | ||||||
|     "lockfileVersion": 3, |     "lockfileVersion": 3, | ||||||
|     "requires": true, |     "requires": true, | ||||||
|     "packages": { |     "packages": { | ||||||
|         "": { |         "": { | ||||||
|             "name": "traxxx", |             "name": "traxxx", | ||||||
|             "version": "1.239.0", |             "version": "1.238.8", | ||||||
|             "license": "ISC", |             "license": "ISC", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@aws-sdk/client-s3": "^3.458.0", |                 "@aws-sdk/client-s3": "^3.458.0", | ||||||
|  | @ -47,7 +47,7 @@ | ||||||
|                 "express-session": "^1.17.3", |                 "express-session": "^1.17.3", | ||||||
|                 "face-api.js": "^0.22.2", |                 "face-api.js": "^0.22.2", | ||||||
|                 "file-type": "^18.7.0", |                 "file-type": "^18.7.0", | ||||||
|                 "fluent-ffmpeg": "^2.1.3", |                 "fluent-ffmpeg": "^2.1.2", | ||||||
|                 "fs-extra": "^11.1.1", |                 "fs-extra": "^11.1.1", | ||||||
|                 "graphile-build": "^4.14.0", |                 "graphile-build": "^4.14.0", | ||||||
|                 "graphile-utils": "^4.14.0", |                 "graphile-utils": "^4.14.0", | ||||||
|  | @ -88,7 +88,7 @@ | ||||||
|                 "tunnel": "0.0.6", |                 "tunnel": "0.0.6", | ||||||
|                 "ua-parser-js": "^1.0.37", |                 "ua-parser-js": "^1.0.37", | ||||||
|                 "undici": "^5.28.1", |                 "undici": "^5.28.1", | ||||||
|                 "unprint": "^0.11.8", |                 "unprint": "^0.11.5", | ||||||
|                 "url-pattern": "^1.0.3", |                 "url-pattern": "^1.0.3", | ||||||
|                 "v-tooltip": "^2.1.3", |                 "v-tooltip": "^2.1.3", | ||||||
|                 "video.js": "^8.6.1", |                 "video.js": "^8.6.1", | ||||||
|  | @ -9851,22 +9851,17 @@ | ||||||
|             "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" |             "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" | ||||||
|         }, |         }, | ||||||
|         "node_modules/fluent-ffmpeg": { |         "node_modules/fluent-ffmpeg": { | ||||||
|             "version": "2.1.3", |             "version": "2.1.2", | ||||||
|             "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", |             "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz", | ||||||
|             "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", |             "integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "async": "^0.2.9", |                 "async": ">=0.2.9", | ||||||
|                 "which": "^1.1.1" |                 "which": "^1.1.1" | ||||||
|             }, |             }, | ||||||
|             "engines": { |             "engines": { | ||||||
|                 "node": ">=18" |                 "node": ">=0.8.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/fluent-ffmpeg/node_modules/async": { |  | ||||||
|             "version": "0.2.10", |  | ||||||
|             "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", |  | ||||||
|             "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" |  | ||||||
|         }, |  | ||||||
|         "node_modules/fluent-ffmpeg/node_modules/which": { |         "node_modules/fluent-ffmpeg/node_modules/which": { | ||||||
|             "version": "1.3.1", |             "version": "1.3.1", | ||||||
|             "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", |             "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", | ||||||
|  | @ -18298,9 +18293,9 @@ | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/unprint": { |         "node_modules/unprint": { | ||||||
|             "version": "0.11.8", |             "version": "0.11.5", | ||||||
|             "resolved": "https://registry.npmjs.org/unprint/-/unprint-0.11.8.tgz", |             "resolved": "https://registry.npmjs.org/unprint/-/unprint-0.11.5.tgz", | ||||||
|             "integrity": "sha512-UCtfdbbHSNS/F0hlFwMa+ZmUqkVdp7V3SZVJjcMNnb0GUKm/7VWjhdvzHe+dIejhRdJykHfXWkI/BCbKwl51Vg==", |             "integrity": "sha512-tLhiFGeSU40GN12625+9oqmNGDFSToMPME60pB+DSGT9wd9fJM0L/lyZMQeNFmWMSThwa/id/FHAOnN7cE1aOw==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "axios": "^0.27.2", |                 "axios": "^0.27.2", | ||||||
|                 "bottleneck": "^2.19.5", |                 "bottleneck": "^2.19.5", | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| { | { | ||||||
|     "name": "traxxx", |     "name": "traxxx", | ||||||
|     "version": "1.239.0", |     "version": "1.238.8", | ||||||
|     "description": "All the latest porn releases in one place", |     "description": "All the latest porn releases in one place", | ||||||
|     "main": "src/app.js", |     "main": "src/app.js", | ||||||
|     "scripts": { |     "scripts": { | ||||||
|  | @ -106,7 +106,7 @@ | ||||||
|         "express-session": "^1.17.3", |         "express-session": "^1.17.3", | ||||||
|         "face-api.js": "^0.22.2", |         "face-api.js": "^0.22.2", | ||||||
|         "file-type": "^18.7.0", |         "file-type": "^18.7.0", | ||||||
|         "fluent-ffmpeg": "^2.1.3", |         "fluent-ffmpeg": "^2.1.2", | ||||||
|         "fs-extra": "^11.1.1", |         "fs-extra": "^11.1.1", | ||||||
|         "graphile-build": "^4.14.0", |         "graphile-build": "^4.14.0", | ||||||
|         "graphile-utils": "^4.14.0", |         "graphile-utils": "^4.14.0", | ||||||
|  | @ -147,7 +147,7 @@ | ||||||
|         "tunnel": "0.0.6", |         "tunnel": "0.0.6", | ||||||
|         "ua-parser-js": "^1.0.37", |         "ua-parser-js": "^1.0.37", | ||||||
|         "undici": "^5.28.1", |         "undici": "^5.28.1", | ||||||
|         "unprint": "^0.11.8", |         "unprint": "^0.11.5", | ||||||
|         "url-pattern": "^1.0.3", |         "url-pattern": "^1.0.3", | ||||||
|         "v-tooltip": "^2.1.3", |         "v-tooltip": "^2.1.3", | ||||||
|         "video.js": "^8.6.1", |         "video.js": "^8.6.1", | ||||||
|  |  | ||||||
|  | @ -1251,10 +1251,6 @@ const tags = [ | ||||||
| 		name: 'voodoo', | 		name: 'voodoo', | ||||||
| 		slug: 'voodoo', | 		slug: 'voodoo', | ||||||
| 	}, | 	}, | ||||||
| 	{ |  | ||||||
| 		name: 'bikini', |  | ||||||
| 		slug: 'bikini', |  | ||||||
| 	}, |  | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| const aliases = [ | const aliases = [ | ||||||
|  | @ -2549,30 +2545,6 @@ const aliases = [ | ||||||
| 		name: 'parasites', | 		name: 'parasites', | ||||||
| 		for: 'parasite', | 		for: 'parasite', | ||||||
| 	}, | 	}, | ||||||
| 	{ |  | ||||||
| 		name: 'threesome - fmm', |  | ||||||
| 		for: 'mfm', |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		name: '4k ultra hd', |  | ||||||
| 		for: '4k', |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		name: 'sex toy play', |  | ||||||
| 		for: 'toys', |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		name: 'cumshots', |  | ||||||
| 		for: 'cumshot', |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		name: 'bikini babes', |  | ||||||
| 		for: 'bikini', |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		name: 'threesomes', |  | ||||||
| 		for: 'threesome', |  | ||||||
| 	}, |  | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| const priorities = [ // higher index is higher priority
 | const priorities = [ // higher index is higher priority
 | ||||||
|  |  | ||||||
|  | @ -104,12 +104,6 @@ const networks = [ | ||||||
| 		}, | 		}, | ||||||
| 		parent: '21sextury', | 		parent: '21sextury', | ||||||
| 	}, | 	}, | ||||||
| 	{ |  | ||||||
| 		slug: 'adultempire', |  | ||||||
| 		name: 'Adult Empire', |  | ||||||
| 		url: 'https://www.adultempire.com', |  | ||||||
| 		type: 'info', |  | ||||||
| 	}, |  | ||||||
| 	{ | 	{ | ||||||
| 		slug: 'adulttime', | 		slug: 'adulttime', | ||||||
| 		name: 'Adult Time', | 		name: 'Adult Time', | ||||||
|  |  | ||||||
|  | @ -3270,15 +3270,6 @@ const sites = [ | ||||||
| 		slug: 'elegantangel', | 		slug: 'elegantangel', | ||||||
| 		name: 'Elegant Angel', | 		name: 'Elegant Angel', | ||||||
| 		url: 'https://www.elegantangel.com', | 		url: 'https://www.elegantangel.com', | ||||||
| 		options: { |  | ||||||
| 			spawn: [ |  | ||||||
| 				{ |  | ||||||
| 					parameters: { |  | ||||||
| 						latest: 'https://www.elegantangel.com/watch-exclusive-elegant-angel-scenes.html', |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 			], |  | ||||||
| 		}, |  | ||||||
| 	}, | 	}, | ||||||
| 	// EVIL ANGEL
 | 	// EVIL ANGEL
 | ||||||
| 	{ | 	{ | ||||||
|  | @ -13487,6 +13478,7 @@ const sites = [ | ||||||
| 		tags: ['black-cock'], | 		tags: ['black-cock'], | ||||||
| 		parameters: { | 		parameters: { | ||||||
| 			studio: false, | 			studio: false, | ||||||
|  | 			layout: 'grid', | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 	// WHALE MEMBER
 | 	// WHALE MEMBER
 | ||||||
|  | @ -13721,36 +13713,27 @@ exports.seed = (knex) => Promise.resolve() | ||||||
| 	.then(async () => { | 	.then(async () => { | ||||||
| 		await Promise.all(sites.map(async (channel) => { | 		await Promise.all(sites.map(async (channel) => { | ||||||
| 			if (channel.rename) { | 			if (channel.rename) { | ||||||
| 				await knex('entities') | 				return knex('entities') | ||||||
| 					.where({ | 					.where({ | ||||||
| 						type: channel.type || 'channel', | 						type: channel.type || 'channel', | ||||||
| 						slug: channel.rename, | 						slug: channel.rename, | ||||||
| 					}) | 					}) | ||||||
| 					.update('slug', channel.slug); | 					.update('slug', channel.slug); | ||||||
| 
 |  | ||||||
| 				return; |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (channel.delete) { | 			return null; | ||||||
| 				await knex('entities') |  | ||||||
| 					.where({ |  | ||||||
| 						type: channel.type || 'channel', |  | ||||||
| 						slug: channel.slug, |  | ||||||
| 					}) |  | ||||||
| 					.delete(); |  | ||||||
| 			} |  | ||||||
| 		}).filter(Boolean)); | 		}).filter(Boolean)); | ||||||
| 
 | 
 | ||||||
| 		const networks = await knex('entities') | 		const networks = await knex('entities') | ||||||
| 			.where('type', 'network') | 			.where('type', 'network') | ||||||
| 			.orWhereNull('parent_id'); | 			.orWhereNull('parent_id'); | ||||||
| 
 | 
 | ||||||
| 		const networksMap = networks.filter((network) => !network.delete).reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); | 		const networksMap = networks.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); | ||||||
| 
 | 
 | ||||||
| 		const tags = await knex('tags').select('*').whereNull('alias_for'); | 		const tags = await knex('tags').select('*').whereNull('alias_for'); | ||||||
| 		const tagsMap = tags.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); | 		const tagsMap = tags.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); | ||||||
| 
 | 
 | ||||||
| 		const sitesWithNetworks = sites.filter((site) => !site.delete).map((site) => ({ | 		const sitesWithNetworks = sites.map((site) => ({ | ||||||
| 			slug: site.slug, | 			slug: site.slug, | ||||||
| 			name: site.name, | 			name: site.name, | ||||||
| 			type: site.type || 'channel', | 			type: site.type || 'channel', | ||||||
|  | @ -13758,7 +13741,6 @@ exports.seed = (knex) => Promise.resolve() | ||||||
| 			description: site.description, | 			description: site.description, | ||||||
| 			url: site.url, | 			url: site.url, | ||||||
| 			parameters: site.parameters, | 			parameters: site.parameters, | ||||||
| 			options: site.options, |  | ||||||
| 			parent_id: networksMap[site.parent], | 			parent_id: networksMap[site.parent], | ||||||
| 			priority: site.priority || 0, | 			priority: site.priority || 0, | ||||||
| 			independent: !!site.independent, | 			independent: !!site.independent, | ||||||
|  |  | ||||||
|  | @ -410,7 +410,7 @@ async function curateProfile(profile, actor) { | ||||||
| 		curatedProfile.ethnicity = ethnicities[profile.ethnicity?.trim().toLowerCase()] || null; | 		curatedProfile.ethnicity = ethnicities[profile.ethnicity?.trim().toLowerCase()] || null; | ||||||
| 		curatedProfile.hairType = profile.hairType?.trim() || null; | 		curatedProfile.hairType = profile.hairType?.trim() || null; | ||||||
| 		curatedProfile.hairColor = hairColors[(profile.hairColor || profile.hair)?.toLowerCase().replace('hair', '').trim()] || null; | 		curatedProfile.hairColor = hairColors[(profile.hairColor || profile.hair)?.toLowerCase().replace('hair', '').trim()] || null; | ||||||
| 		curatedProfile.eyes = eyeColors[profile.eyes?.replace(/eyes?/i).trim().toLowerCase()] || null; | 		curatedProfile.eyes = eyeColors[profile.eyes?.trim().toLowerCase()] || null; | ||||||
| 
 | 
 | ||||||
| 		curatedProfile.tattoos = profile.tattoos?.trim() || null; | 		curatedProfile.tattoos = profile.tattoos?.trim() || null; | ||||||
| 		curatedProfile.piercings = profile.piercings?.trim() || null; | 		curatedProfile.piercings = profile.piercings?.trim() || null; | ||||||
|  | @ -878,7 +878,7 @@ async function scrapeActors(argNames) { | ||||||
| 	const entitySlugs = sources.flat(); | 	const entitySlugs = sources.flat(); | ||||||
| 
 | 
 | ||||||
| 	const [entitiesBySlug, existingActorEntries] = await Promise.all([ | 	const [entitiesBySlug, existingActorEntries] = await Promise.all([ | ||||||
| 		fetchEntitiesBySlug(entitySlugs, { types: ['channel', 'network', 'info'] }), | 		fetchEntitiesBySlug(entitySlugs, 'desc'), | ||||||
| 		knex('actors') | 		knex('actors') | ||||||
| 			.select(knex.raw('actors.id, actors.name, actors.slug, actors.entry_id, actors.entity_id, row_to_json(entities) as entity')) | 			.select(knex.raw('actors.id, actors.name, actors.slug, actors.entry_id, actors.entity_id, row_to_json(entities) as entity')) | ||||||
| 			.whereIn('actors.slug', baseActors.map((baseActor) => baseActor.slug)) | 			.whereIn('actors.slug', baseActors.map((baseActor) => baseActor.slug)) | ||||||
|  |  | ||||||
|  | @ -84,7 +84,7 @@ async function fetchScene(scraper, url, entity, baseRelease, options, type = 'sc | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if ((type === 'scene' && scraper.scrapeScene) || (type === 'movie' && scraper.scrapeMovie)) { | 	if ((type === 'scene' && scraper.scrapeScene) || (type === 'movie' && scraper.scrapeMovie)) { | ||||||
| 		if (scraper.useUnprint || (type === 'scene' && scraper.scrapeScene?.unprint) || (type === 'movie' && scraper.scrapeMovie?.unprint)) { | 		if (scraper.useUnprint || scraper.scrapeScene?.unprint || scraper.scrapeMovie?.unprint) { | ||||||
| 			return fetchUnprintScene(scraper, url, entity, baseRelease, options, type); | 			return fetchUnprintScene(scraper, url, entity, baseRelease, options, type); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -55,8 +55,7 @@ function curateEntity(entity, includeParameters = false) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (includeParameters) { | 	if (includeParameters) { | ||||||
| 		curatedEntity.options = entity.options; // global internal options
 | 		curatedEntity.parameters = entity.parameters; | ||||||
| 		curatedEntity.parameters = entity.parameters; // scraper-specific parameters
 |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (entity.children) { | 	if (entity.children) { | ||||||
|  | @ -67,25 +66,10 @@ function curateEntity(entity, includeParameters = false) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (entity.included_children) { | 	if (entity.included_children) { | ||||||
| 		curatedEntity.includedChildren = entity.included_children.flatMap((child) => { | 		curatedEntity.includedChildren = entity.included_children.map((child) => curateEntity({ | ||||||
| 			const curatedChild = curateEntity({ |  | ||||||
| 			...child, | 			...child, | ||||||
| 			parent: curatedEntity.id ? curatedEntity : null, | 			parent: curatedEntity.id ? curatedEntity : null, | ||||||
| 			}, includeParameters); | 		}, includeParameters)); | ||||||
| 
 |  | ||||||
| 			// allow entities to 'spawn' virtual copies of themselves, this is useful for sites that use two separate update pages (i.e. Elegant Angel)
 |  | ||||||
| 			if (child.options?.spawn) { |  | ||||||
| 				return [ |  | ||||||
| 					curatedChild, |  | ||||||
| 					...child.options.spawn.map((spawnEntity) => ({ |  | ||||||
| 						...curatedChild, |  | ||||||
| 						...spawnEntity, |  | ||||||
| 					})), |  | ||||||
| 				]; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			return curatedChild; |  | ||||||
| 		}); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const scraper = resolveScraper(curatedEntity); | 	const scraper = resolveScraper(curatedEntity); | ||||||
|  | @ -215,7 +199,7 @@ async function fetchIncludedEntities() { | ||||||
| 	return curatedNetworks; | 	return curatedNetworks; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function fetchEntitiesBySlug(entitySlugs, options = { prefer: 'channel' }) { | async function fetchEntitiesBySlug(entitySlugs, prefer = 'channel') { | ||||||
| 	const entities = await knex.raw(` | 	const entities = await knex.raw(` | ||||||
| 		WITH RECURSIVE entity_tree as ( | 		WITH RECURSIVE entity_tree as ( | ||||||
| 			SELECT to_jsonb(entities) as entity, | 			SELECT to_jsonb(entities) as entity, | ||||||
|  | @ -224,7 +208,7 @@ async function fetchEntitiesBySlug(entitySlugs, options = { prefer: 'channel' }) | ||||||
| 			FROM entities | 			FROM entities | ||||||
| 			WHERE (slug = ANY(:entitySlugs) | 			WHERE (slug = ANY(:entitySlugs) | ||||||
| 			OR url ILIKE ANY(:entityHosts)) | 			OR url ILIKE ANY(:entityHosts)) | ||||||
| 			AND type = ANY(:entityTypes) | 			AND type IN ('channel', 'network') | ||||||
| 
 | 
 | ||||||
| 			UNION ALL | 			UNION ALL | ||||||
| 
 | 
 | ||||||
|  | @ -252,8 +236,7 @@ async function fetchEntitiesBySlug(entitySlugs, options = { prefer: 'channel' }) | ||||||
| 	`, {
 | 	`, {
 | ||||||
| 		entitySlugs: entitySlugs.filter((slug) => !slug.includes('.')), | 		entitySlugs: entitySlugs.filter((slug) => !slug.includes('.')), | ||||||
| 		entityHosts: entitySlugs.filter((slug) => slug.includes('.')).map((hostname) => `%${hostname}`), | 		entityHosts: entitySlugs.filter((slug) => slug.includes('.')).map((hostname) => `%${hostname}`), | ||||||
| 		entityTypes: options.types || ['channel', 'network'], | 		sort: knex.raw(prefer === 'channel' ? 'asc' : 'desc'), | ||||||
| 		sort: knex.raw(options.prefer === 'channel' ? 'asc' : 'desc'), |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	// channel entity will overwrite network entity
 | 	// channel entity will overwrite network entity
 | ||||||
|  | @ -280,7 +263,7 @@ async function fetchReleaseEntities(baseReleases) { | ||||||
| 			.filter(Boolean), | 			.filter(Boolean), | ||||||
| 	)); | 	)); | ||||||
| 
 | 
 | ||||||
| 	return fetchEntitiesBySlug(entitySlugs, { prefer: argv.prefer || 'network' }); | 	return fetchEntitiesBySlug(entitySlugs, argv.prefer || 'network'); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function fetchEntity(entityId, type) { | async function fetchEntity(entityId, type) { | ||||||
|  |  | ||||||
|  | @ -648,12 +648,7 @@ streamQueue.define('fetchStreamSource', async ({ source, tempFileTarget, hashStr | ||||||
| 		.format('mp4') | 		.format('mp4') | ||||||
| 		.outputOptions(['-movflags frag_keyframe+empty_moov']) | 		.outputOptions(['-movflags frag_keyframe+empty_moov']) | ||||||
| 		.on('start', (cmd) => logger.verbose(`Fetching stream from ${source.stream} with "${cmd}"`)) | 		.on('start', (cmd) => logger.verbose(`Fetching stream from ${source.stream} with "${cmd}"`)) | ||||||
| 		.on('error', (error) => { | 		.on('error', (error) => logger.error(`Failed to fetch stream from ${source.stream}: ${error.message}`)) | ||||||
| 			logger.error(`Failed to fetch stream from ${source.stream}: ${error.message}`); |  | ||||||
| 
 |  | ||||||
| 			hashStream.end(); |  | ||||||
| 			tempFileTarget.end(); |  | ||||||
| 		}) |  | ||||||
| 		.pipe(); | 		.pipe(); | ||||||
| 
 | 
 | ||||||
| 	// await pipeline(video, hashStream, tempFileTarget);
 | 	// await pipeline(video, hashStream, tempFileTarget);
 | ||||||
|  |  | ||||||
|  | @ -1,61 +1,97 @@ | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| const unprint = require('unprint'); | const qu = require('../utils/qu'); | ||||||
| 
 |  | ||||||
| const http = require('../utils/http'); | const http = require('../utils/http'); | ||||||
| const slugify = require('../utils/slugify'); | const slugify = require('../utils/slugify'); | ||||||
| const { feetInchesToCm, lbsToKg } = require('../utils/convert'); | const { feetInchesToCm, lbsToKg } = require('../utils/convert'); | ||||||
| 
 | 
 | ||||||
| function scrapeAll(scenes, channel, _options) { | async function getPhotos(entryId, channel) { | ||||||
|  | 	const res = await http.get(`${channel.url}/Membership/GetScreenshots?sceneID=scene_${entryId}`); | ||||||
|  | 
 | ||||||
|  | 	if (res.ok) { | ||||||
|  | 		return res.body.split(/[\s,]+/).filter(Boolean); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return []; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function scrapeAllTour(scenes, channel) { | ||||||
| 	return scenes.map(({ query }) => { | 	return scenes.map(({ query }) => { | ||||||
| 		const release = {}; | 		const release = {}; | ||||||
| 
 | 
 | ||||||
| 		release.url = query.url('a.scene-title, a.scene-img', { origin: channel.url }); | 		release.url = query.url('.scene-update-details, .feature-update-details', 'href', { origin: channel.url }); | ||||||
| 		release.entryId = query.attribute('article[data-scene-id]', 'data-scene-id') || new URL(release.url).pathname.match(/^\/(\d+)/)?.[1]; | 		release.entryId = new URL(release.url).pathname.match(/\/(\d+)/)[1]; | ||||||
| 
 | 
 | ||||||
| 		release.title = query.content('.scene-title')?.trim(); | 		release.title = query.q('.scene-img-wrapper img', 'alt').replace(/\s*image$/i, ''); | ||||||
| 		release.duration = query.duration('.scene-length'); |  | ||||||
| 
 | 
 | ||||||
| 		release.actors = query.content('.scene-performer-names')?.split(/[,&]/).map((actor) => actor.trim()); | 		release.date = query.date('.scene-update-stats span, .feature-update-details span', 'MMM DD, YYYY'); | ||||||
|  | 		release.actors = query.cnt('.scene-update-details h3, .feature-update-details h2')?.split(/\s*\|\s*/).map((actor) => actor.trim()); | ||||||
| 
 | 
 | ||||||
| 		release.poster = query.sourceSet('.screenshot', 'data-srcset'); | 		const poster = query.img('.scene-img-wrapper img'); | ||||||
|  | 		release.poster = [ | ||||||
|  | 			poster.replace(/\/res\/\d+/, '/res/1920'), | ||||||
|  | 			poster.replace(/\/res\/\d+/, '/res/1600'), | ||||||
|  | 			poster, | ||||||
|  | 		]; | ||||||
| 
 | 
 | ||||||
| 		const sceneId = query.attribute('article[data-scene-id]', 'data-scene-id'); | 		release.trailer = { src: query.video('.scene-img-wrapper source') }; | ||||||
| 		const masterId = query.attribute('article[data-master-id]', 'data-master-id'); |  | ||||||
| 
 |  | ||||||
| 		if (sceneId && masterId) { |  | ||||||
| 			release.teaser = `https://video.adultempire.com/hls/previewscene/${masterId}/${sceneId}/index-f1-v1.m3u8`; |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		return release; | 		return release; | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const photoRegex = /(\/\w\/\d+\/)\d+/; | async function scrapeAllGrid(scenes, channel, options) { | ||||||
|  | 	return Promise.all(scenes.map(async ({ query, el }) => { | ||||||
|  | 		const release = {}; | ||||||
|  | 		const uri = query.url('.grid-item-title') || query.url('a.animated-screen'); | ||||||
| 
 | 
 | ||||||
| async function scrapeRelease({ query, html, element }, { url, entity, baseRelease, parameters }) { | 		release.entryId = el.id.match(/\d+/)?.[0] || uri.match(/^(\d+)\//)?.[1]; | ||||||
|  | 
 | ||||||
|  | 		release.title = query.cnt('.grid-item-title'); | ||||||
|  | 		release.url = qu.prefixUrl(uri, channel.url); | ||||||
|  | 
 | ||||||
|  | 		release.poster = query.img('.screenshot'); | ||||||
|  | 
 | ||||||
|  | 		if (options.includePhotos) { | ||||||
|  | 			release.photos = await getPhotos(release.entryId, channel); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return release; | ||||||
|  | 	})); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function scrapeMovieScenes(scenes) { | ||||||
|  | 	return scenes.map(({ query }) => { | ||||||
|  | 		const release = {}; | ||||||
|  | 
 | ||||||
|  | 		release.title = query.cnt('.scene-title a'); | ||||||
|  | 		release.url = query.url('.scene-title a', 'href', { origin: 'https://www.elegantangel.com' }); | ||||||
|  | 		release.entryId = new URL(release.url).pathname.match(/\/(\d+)/)[1]; | ||||||
|  | 
 | ||||||
|  | 		release.duration = query.number('.scene-length') * 60; | ||||||
|  | 		release.actors = query.cnts('.scene-cast-list a'); | ||||||
|  | 
 | ||||||
|  | 		release.poster = query.img('a img'); | ||||||
|  | 
 | ||||||
|  | 		return release; | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function scrapeRelease({ query, html }, url, channel, baseRelease, options) { | ||||||
| 	const release = {}; | 	const release = {}; | ||||||
| 	const type = query.exists('.scene-list-header') ? 'movie' : 'scene'; | 	const type = query.exists('.scene-list-header') ? 'movie' : 'scene'; | ||||||
| 
 | 
 | ||||||
| 	release.entryId = new URL(url).pathname.match(/\/(\d+)/)[1]; | 	release.entryId = new URL(url).pathname.match(/\/(\d+)/)[1]; | ||||||
| 
 | 
 | ||||||
| 	const title = query.content('.scene-page .description, .video-page .description'); | 	release.title = query.cnt('.scene-page .description, .video-page .description'); | ||||||
| 
 |  | ||||||
| 	if (/^scene \d+$/i.test(title)) { |  | ||||||
| 		release.sceneIndex = unprint.extractNumber(title); |  | ||||||
| 	} else { |  | ||||||
| 		release.title = title; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	release.date = query.date('.release-date:first-child', 'MMM DD, YYYY', /\w{3} \d{2}, \d{4}/); | 	release.date = query.date('.release-date:first-child', 'MMM DD, YYYY', /\w{3} \d{2}, \d{4}/); | ||||||
| 	release.duration = query.duration('.release-date:last-child'); |  | ||||||
| 
 | 
 | ||||||
| 	release.actors = query.all('.video-performer').map((el) => { | 	release.actors = query.all('.video-performer').map((el) => { | ||||||
| 		const avatar = unprint.query.img(el, 'img', 'data-bgsrc'); | 		const avatar = qu.query.img(el, 'img', 'data-bgsrc'); | ||||||
| 
 | 
 | ||||||
| 		return { | 		return { | ||||||
| 			name: unprint.query.content(el, 'span').trim(), | 			name: qu.query.cnt(el, 'span'), | ||||||
| 			url: unprint.query.url(el, 'a', { origin: entity.url }), | 			url: qu.query.url(el, 'a', 'href', { origin: channel.url }), | ||||||
| 			avatar: [ | 			avatar: [ | ||||||
| 				avatar.replace(/\/actor\/\d+/, '/actor/1600'), | 				avatar.replace(/\/actor\/\d+/, '/actor/1600'), | ||||||
| 				avatar, | 				avatar, | ||||||
|  | @ -63,8 +99,8 @@ async function scrapeRelease({ query, html, element }, { url, entity, baseReleas | ||||||
| 		}; | 		}; | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	release.tags = query.contents('.tags a, .categories a'); | 	release.tags = query.cnts('.tags a, .categories a'); | ||||||
| 	release.studio = parameters?.studio === false ? null : slugify(query.content('.studio span:last-child, .studio a'), ''); | 	release.studio = options?.parameters.studio === false ? null : slugify(query.cnt('.studio span:last-child'), ''); | ||||||
| 
 | 
 | ||||||
| 	if (type === 'scene') { | 	if (type === 'scene') { | ||||||
| 		release.director = query.text('.director'); | 		release.director = query.text('.director'); | ||||||
|  | @ -73,44 +109,87 @@ async function scrapeRelease({ query, html, element }, { url, entity, baseReleas | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (type === 'movie') { | 	if (type === 'movie') { | ||||||
| 		release.director = query.content('.director a'); | 		release.director = query.cnt('.director a'); | ||||||
| 		release.covers = [query.sourceSet('.carousel-item .boxcover-image', 'data-srcset')]; | 		release.covers = query.imgs('.carousel-item > img'); | ||||||
| 
 | 
 | ||||||
| 		release.scenes = scrapeAll(unprint.initAll(element, '#scenes .grid-item'), entity); | 		release.scenes = scrapeMovieScenes(qu.initAll(query.all('#scenes .grid-item')), channel); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (query.exists('.video-title .movie-title')) { | 	if (query.exists('.video-title .movie-title')) { | ||||||
| 		release.movie = { | 		release.movie = { | ||||||
| 			title: query.content('#viewLargeBoxcover .modal-title a'), | 			title: query.cnt('#viewLargeBoxcover .modal-title a'), | ||||||
| 			url: query.url('#viewLargeBoxcover .modal-title a', 'href', { origin: entity.url }), | 			url: query.url('#viewLargeBoxcover .modal-title a', 'href', { origin: channel.url }), | ||||||
| 			entryId: query.url('#viewLargeBoxcover .modal-title a')?.match(/(\d+)\//)[1], | 			entryId: query.url('#viewLargeBoxcover .modal-title a')?.match(/(\d+)\//)[1], | ||||||
| 			covers: query.imgs('#viewLargeBoxcover #viewLargeBoxcoverCarousel .carousel-item > img'), | 			covers: query.imgs('#viewLargeBoxcover #viewLargeBoxcoverCarousel .carousel-item > img'), | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	release.caps = query.imgs('#dv_frames a > img', { attribute: 'data-src' }).map((photo) => [ | 	release.photos = query.imgs('#dv_frames a > img').map((photo) => [ | ||||||
| 		photo.replace(photoRegex, (match, path) => `${path}1920`), | 		photo.replace(/(\/p\/\d+\/)\d+/, (match, path) => `${path}1920`), | ||||||
| 		photo.replace(photoRegex, (match, path) => `${path}1280`), | 		photo.replace(/(\/p\/\d+\/)\d+/, (match, path) => `${path}1600`), | ||||||
| 		photo, | 		photo, | ||||||
| 	]); | 	]); | ||||||
| 
 | 
 | ||||||
| 	const trailerId = html.match(/item: (\d+),/)?.[1]; | 	const trailerId = html.match(/item: (\d+),/)?.[1]; | ||||||
| 
 | 
 | ||||||
| 	if (trailerId) { | 	if (trailerId) { | ||||||
| 		release.trailer = `https://trailer.adultempire.com/hls/trailer/${trailerId}/master.m3u8`; | 		const trailerUrl = `https://www.adultempire.com/videoEmbed/${trailerId}?type=preview`; | ||||||
| 	} | 		const trailerRes = await qu.get(trailerUrl); | ||||||
| 
 | 
 | ||||||
| 	if (query.exists('.user-actions .btn-4k')) { | 		if (trailerRes.ok) { | ||||||
| 		release.qualities = [2160]; | 			const stream = trailerRes.item.query.video(); | ||||||
|  | 
 | ||||||
|  | 			release.trailer = { stream }; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return release; | 	return release; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function scrapeProfile({ query }) { | function scrapeMovies(movies, channel) { | ||||||
|  | 	return movies.map(({ query }) => { | ||||||
|  | 		const release = {}; | ||||||
|  | 
 | ||||||
|  | 		release.url = query.url('.boxcover', 'href', { origin: channel.url }); | ||||||
|  | 		release.entryId = new URL(release.url).pathname.match(/\/(\d+)/)[1]; | ||||||
|  | 
 | ||||||
|  | 		release.title = query.cnt('span'); | ||||||
|  | 
 | ||||||
|  | 		const cover = query.img('picture img'); | ||||||
|  | 
 | ||||||
|  | 		release.covers = [ | ||||||
|  | 			// filename is ignored, back-cover has suffix after media ID
 | ||||||
|  | 			cover.replace('_sq.jpg', '/front.jpg').replace(/\/product\/\d+/, '/product/500'), | ||||||
|  | 			cover.replace('_sq.jpg', 'b/back.jpg').replace(/\/product\/\d+/, '/product/500'), | ||||||
|  | 		]; | ||||||
|  | 
 | ||||||
|  | 		return release; | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function scrapeActorScenes(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('.grid-item-title'); | ||||||
|  | 
 | ||||||
|  | 		const poster = query.img('a img'); | ||||||
|  | 		release.poster = [ | ||||||
|  | 			poster.replace(/\/\d+\//, '/1600/'), | ||||||
|  | 			poster, | ||||||
|  | 		]; | ||||||
|  | 
 | ||||||
|  | 		return release; | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function scrapeProfile({ query }, url, channel, include) { | ||||||
| 	const profile = {}; | 	const profile = {}; | ||||||
| 
 | 
 | ||||||
| 	const bio = query.contents('#profileModal .well li').reduce((acc, info) => { | 	const bio = query.cnts('.performer-page-header li').reduce((acc, info) => { | ||||||
| 		const [key, value] = info.split(':'); | 		const [key, value] = info.split(':'); | ||||||
| 
 | 
 | ||||||
| 		return { | 		return { | ||||||
|  | @ -119,14 +198,11 @@ async function scrapeProfile({ query }) { | ||||||
| 		}; | 		}; | ||||||
| 	}, {}); | 	}, {}); | ||||||
| 
 | 
 | ||||||
| 	const bioText = query.content('#profileModal .well'); | 	const measurements = bio.meas?.match(/(\d+)(\w+)-(\d+)-(\d+)/); | ||||||
| 
 | 
 | ||||||
| 	profile.description = query.content('#profileModal .modal-body') | 	if (measurements) { | ||||||
| 		.slice(bioText.length) | 		[profile.bust, profile.cup, profile.waist, profile.hip] = measurements.slice(1); | ||||||
| 		.replace(/Biography Text ©Adult DVD Empire/i, '') | 	} | ||||||
| 		.trim(); |  | ||||||
| 
 |  | ||||||
| 	profile.measurements = bio.measurements?.replace(/["\s]+/g, ''); |  | ||||||
| 
 | 
 | ||||||
| 	profile.hair = bio.hair; | 	profile.hair = bio.hair; | ||||||
| 	profile.eyes = bio.eyes; | 	profile.eyes = bio.eyes; | ||||||
|  | @ -135,41 +211,79 @@ async function scrapeProfile({ query }) { | ||||||
| 	profile.height = feetInchesToCm(bio.height); | 	profile.height = feetInchesToCm(bio.height); | ||||||
| 	profile.weight = lbsToKg(bio.weight); | 	profile.weight = lbsToKg(bio.weight); | ||||||
| 
 | 
 | ||||||
| 	const avatar = query.img('picture img, .performer-image-container img'); | 	profile.avatar = query.img('picture img'); | ||||||
| 
 | 
 | ||||||
| 	if (avatar) { | 	if (include) { | ||||||
| 		profile.avatar = [ | 		const actorId = new URL(url).pathname.match(/\/(\d+)/)[1]; | ||||||
| 			avatar | 		const res = await qu.getAll(`${channel.url}/www.elegantangel.com/streaming-video-by-scene.html?cast=${actorId}`, '.grid-item', null, { | ||||||
| 				.replace('_bust', '_body') | 			rejectUnauthorized: false, | ||||||
| 				.replace(/\/actor\/\d+\//i, '/actor/1000/'), | 		}); | ||||||
| 			avatar, | 
 | ||||||
| 		]; | 		if (res.ok) { | ||||||
|  | 			profile.releases = scrapeActorScenes(res.items, channel); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return profile; | 	return profile; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function fetchLatest(channel, page, options) { | async function fetchLatestTour(channel, page = 1) { | ||||||
| 	// const res = await qu.getAll(`${channel.url}/watch-newest-clips-and-scenes.html?page=${page}&hybridview=member`, '.item-grid-scene .grid-item');
 | 	const url = `${channel.url}/tour?page=${page}`; | ||||||
| 	const res = await unprint.get(options.parameters?.latest | 	const res = await qu.getAll(url, '.scene-update', null, { | ||||||
| 		? `${options.parameters.latest}?page=${page}&view=grid` | 		// invalid certificate
 | ||||||
| 		: `${channel.url}/watch-newest-clips-and-scenes.html?page=${page}&view=grid`, { selectAll: '.item-grid-scene .grid-item' }); | 		rejectUnauthorized: false, | ||||||
|  | 	}); | ||||||
| 
 | 
 | ||||||
| 	if (res.ok) { | 	if (res.ok) { | ||||||
| 		return scrapeAll(res.context, channel, options); | 		return scrapeAllTour(res.items, channel); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return res.status; | 	return res.status; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function fetchProfilePage(actorUrl) { | async function fetchLatestGrid(channel, page, options) { | ||||||
| 	const res = await unprint.get(actorUrl, { | 	const res = await qu.getAll(`${channel.url}/watch-newest-clips-and-scenes.html?page=${page}&hybridview=member`, '.item-grid-scene .grid-item'); | ||||||
| 		select: '#content', | 
 | ||||||
|  | 	if (res.ok) { | ||||||
|  | 		return scrapeAllGrid(res.items, channel, options); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return res.status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function fetchMovie(url, channel, baseRelease, options) { | ||||||
|  | 	const res = await qu.get(url, null, null, { | ||||||
|  | 		// invalid certificate
 | ||||||
| 		rejectUnauthorized: false, | 		rejectUnauthorized: false, | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	if (res.ok) { | 	if (res.ok) { | ||||||
| 		return scrapeProfile(res.context); | 		return scrapeRelease(res.item, url, channel, baseRelease, options); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return res.status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function fetchMovies(channel, page = 1) { | ||||||
|  | 	const res = await qu.getAll(`https://www.elegantangel.com/streaming-elegant-angel-dvds-on-video.html?page=${page}`, '.grid-item', null, { | ||||||
|  | 		// invalid certificate
 | ||||||
|  | 		rejectUnauthorized: false, | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	if (res.ok) { | ||||||
|  | 		return scrapeMovies(res.items, channel); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return res.status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function fetchProfilePage(actorUrl, channel, include) { | ||||||
|  | 	const res = await qu.get(actorUrl, '.performer-page', null, { | ||||||
|  | 		rejectUnauthorized: false, | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	if (res.ok) { | ||||||
|  | 		return scrapeProfile(res.item, actorUrl, channel, include); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return res.status; | 	return res.status; | ||||||
|  | @ -184,15 +298,13 @@ async function fetchProfile(baseActor, channel, include) { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const searchRes = await http.get(`https://www.adultempire.com/search/SearchAutoComplete_Agg_EmpireDTRank?search_type=Pornstars&rows=9&name_startsWith=${slugify(baseActor.name, '+')}`); | 	const searchRes = await http.get(`${channel.url}/search/SearchAutoComplete_Agg_ByMedia?rows=9&name_startsWith=${slugify(baseActor.name, '+')}`); | ||||||
| 
 | 
 | ||||||
| 	if (searchRes.ok && searchRes.body.Results) { | 	if (searchRes.ok) { | ||||||
| 		const actorResult = searchRes.body.Results.find((result) => /performer/i.test(result.BasicResponseGroup?.displaytype) && new RegExp(baseActor.name, 'i').test(result.BasicResponseGroup?.description)); | 		const actorResult = searchRes.body.Results.find((result) => /performer/i.test(result.BasicResponseGroup?.displaytype) && new RegExp(baseActor.name, 'i').test(result.BasicResponseGroup?.description)); | ||||||
| 
 | 
 | ||||||
| 		if (actorResult) { | 		if (actorResult) { | ||||||
| 			const url = `https://www.adultempire.com/${actorResult.BasicResponseGroup.id}`; | 			return fetchProfilePage(`${channel.url}${actorResult.BasicResponseGroup.id}`, channel, include); | ||||||
| 
 |  | ||||||
| 			return fetchProfilePage(url); |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return null; | 		return null; | ||||||
|  | @ -202,15 +314,16 @@ async function fetchProfile(baseActor, channel, include) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | module.exports = { | ||||||
| 	fetchLatest, | 	fetchLatest: fetchLatestTour, | ||||||
| 	// fetchMovies,
 | 	fetchMovies, | ||||||
|  | 	fetchMovie, | ||||||
|  | 	fetchProfile, | ||||||
|  | 	scrapeScene: scrapeRelease, | ||||||
|  | 	scrapeMovie: scrapeRelease, | ||||||
|  | 	grid: { | ||||||
|  | 		fetchLatest: fetchLatestGrid, | ||||||
|  | 		scrapeScene: scrapeRelease, | ||||||
|  | 		fetchMovie, | ||||||
| 		fetchProfile, | 		fetchProfile, | ||||||
| 	scrapeScene: { |  | ||||||
| 		scraper: scrapeRelease, |  | ||||||
| 		unprint: true, |  | ||||||
| 	}, |  | ||||||
| 	scrapeMovie: { |  | ||||||
| 		scraper: scrapeRelease, |  | ||||||
| 		unprint: true, |  | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -3,10 +3,6 @@ | ||||||
| const scrapers = require('./scrapers'); | const scrapers = require('./scrapers'); | ||||||
| 
 | 
 | ||||||
| function resolveScraper(entity) { | function resolveScraper(entity) { | ||||||
| 	if (entity.parameters?.useScraper && scrapers.releases[entity.parameters.useScraper]) { |  | ||||||
| 		return scrapers.releases[entity.parameters.useScraper]; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (scrapers.releases[entity.slug]) { | 	if (scrapers.releases[entity.slug]) { | ||||||
| 		return scrapers.releases[entity.slug]; | 		return scrapers.releases[entity.slug]; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -177,7 +177,6 @@ const scrapers = { | ||||||
| 	actors: { | 	actors: { | ||||||
| 		'18vr': badoink, | 		'18vr': badoink, | ||||||
| 		'21sextury': gamma, | 		'21sextury': gamma, | ||||||
| 		adultempire, |  | ||||||
| 		allanal: mikeadriano, | 		allanal: mikeadriano, | ||||||
| 		amateureuro: porndoe, | 		amateureuro: porndoe, | ||||||
| 		americanpornstar, | 		americanpornstar, | ||||||
|  | @ -218,6 +217,7 @@ const scrapers = { | ||||||
| 		dorcelclub: dorcel, | 		dorcelclub: dorcel, | ||||||
| 		doubleviewcasting: firstanalquest, | 		doubleviewcasting: firstanalquest, | ||||||
| 		dtfsluts: fullpornnetwork, | 		dtfsluts: fullpornnetwork, | ||||||
|  | 		elegantangel: adultempire, | ||||||
| 		evilangel: gamma, | 		evilangel: gamma, | ||||||
| 		exploitedcollegegirls: elevatedx, | 		exploitedcollegegirls: elevatedx, | ||||||
| 		eyeontheguy: hush, | 		eyeontheguy: hush, | ||||||
|  | @ -323,6 +323,7 @@ const scrapers = { | ||||||
| 		vixen, | 		vixen, | ||||||
| 		vrcosplayx: badoink, | 		vrcosplayx: badoink, | ||||||
| 		wankzvr, | 		wankzvr, | ||||||
|  | 		westcoastproductions: adultempire, | ||||||
| 		wicked: gamma, | 		wicked: gamma, | ||||||
| 		wildoncam: cherrypimps, | 		wildoncam: cherrypimps, | ||||||
| 		xempire: gamma, | 		xempire: gamma, | ||||||
|  |  | ||||||
|  | @ -288,11 +288,7 @@ async function associateMovieScenes(movies, movieScenes) { | ||||||
| 		}, | 		}, | ||||||
| 	}), {}); | 	}), {}); | ||||||
| 
 | 
 | ||||||
| 	const associations = movieScenes | 	const associations = movieScenes.map((scene) => { | ||||||
| 		.toSorted((sceneA, sceneB) => { |  | ||||||
| 			return (sceneA.sceneIndex || 1) - (sceneB.sceneIndex || 1); |  | ||||||
| 		}) |  | ||||||
| 		.map((scene) => { |  | ||||||
| 		if (!scene.movie) { | 		if (!scene.movie) { | ||||||
| 			return null; | 			return null; | ||||||
| 		} | 		} | ||||||
|  | @ -308,8 +304,7 @@ async function associateMovieScenes(movies, movieScenes) { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return null; | 		return null; | ||||||
| 		}) | 	}).filter(Boolean); | ||||||
| 		.filter(Boolean); |  | ||||||
| 
 | 
 | ||||||
| 	await bulkInsert('movies_scenes', associations, false); | 	await bulkInsert('movies_scenes', associations, false); | ||||||
| } | } | ||||||
|  | @ -359,7 +354,6 @@ async function storeMovies(movies, useBatchId) { | ||||||
| 
 | 
 | ||||||
| 	await updateMovieSearch(moviesWithId.map((movie) => movie.id)); | 	await updateMovieSearch(moviesWithId.map((movie) => movie.id)); | ||||||
| 	await associateReleaseMedia(moviesWithId, 'movie'); | 	await associateReleaseMedia(moviesWithId, 'movie'); | ||||||
| 	await associateReleaseTags(moviesWithId, 'movie'); |  | ||||||
| 
 | 
 | ||||||
| 	return moviesWithId; | 	return moviesWithId; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -298,8 +298,6 @@ async function scrapeNetworkParallel(networkEntity) { | ||||||
| async function fetchUpdates() { | async function fetchUpdates() { | ||||||
| 	const includedNetworks = await fetchIncludedEntities(); | 	const includedNetworks = await fetchIncludedEntities(); | ||||||
| 
 | 
 | ||||||
| 	// console.log(includedNetworks[0]);
 |  | ||||||
| 
 |  | ||||||
| 	const scrapedNetworks = await Promise.map( | 	const scrapedNetworks = await Promise.map( | ||||||
| 		includedNetworks, | 		includedNetworks, | ||||||
| 		async (networkEntity) => (networkEntity.parameters?.sequential | 		async (networkEntity) => (networkEntity.parameters?.sequential | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue