Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
117c8c693f
|
@ -16,13 +16,13 @@
|
||||||
>{{ actor.name }}</span>
|
>{{ actor.name }}</span>
|
||||||
|
|
||||||
<router-link
|
<router-link
|
||||||
v-if="actor.network"
|
v-if="actor.entity"
|
||||||
v-tooltip="actor.network.name"
|
v-tooltip="actor.entity.name"
|
||||||
:to="{ name: 'network', params: { networkSlug: actor.network.slug } }"
|
:to="{ name: actor.entity.type, params: { entitySlug: actor.entity.slug, range: 'new', pageNumber: 1 } }"
|
||||||
class="favicon"
|
class="favicon"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/img/logos/${actor.network.slug}/favicon.png`"
|
:src="`/img/logos/${actor.entity.slug}/favicon_dark.png`"
|
||||||
class="favicon-icon"
|
class="favicon-icon"
|
||||||
>
|
>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
|
@ -61,7 +61,7 @@ const actorFields = `
|
||||||
lazy
|
lazy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
network: entity {
|
entity {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
slug
|
slug
|
||||||
|
|
|
@ -682,6 +682,11 @@ exports.up = knex => Promise.resolve()
|
||||||
.inTable('actors')
|
.inTable('actors')
|
||||||
.onDelete('cascade');
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.integer('alias_id', 12)
|
||||||
|
.references('id')
|
||||||
|
.inTable('actors')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
table.unique(['release_id', 'actor_id']);
|
table.unique(['release_id', 'actor_id']);
|
||||||
|
|
||||||
table.datetime('created_at')
|
table.datetime('created_at')
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "traxxx",
|
"name": "traxxx",
|
||||||
"version": "1.171.0",
|
"version": "1.172.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "traxxx",
|
"name": "traxxx",
|
||||||
"version": "1.171.0",
|
"version": "1.172.0",
|
||||||
"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": {
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
|
@ -180,6 +180,11 @@ function toBaseActors(actorsOrNames, release) {
|
||||||
return baseActors;
|
return baseActors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCollisionLikely(actor) {
|
||||||
|
// actor with single name
|
||||||
|
return actor.name.match(/\w+/g).length === 1;
|
||||||
|
}
|
||||||
|
|
||||||
function curateActor(actor, withDetails = false, isProfile = false) {
|
function curateActor(actor, withDetails = false, isProfile = false) {
|
||||||
if (!actor) {
|
if (!actor) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -260,11 +265,13 @@ function curateActor(actor, withDetails = false, isProfile = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function curateActorEntry(baseActor, batchId) {
|
function curateActorEntry(baseActor, batchId) {
|
||||||
|
const collisionLikely = getCollisionLikely(baseActor);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: baseActor.name,
|
name: baseActor.name,
|
||||||
slug: baseActor.slug,
|
slug: baseActor.slug,
|
||||||
entity_id: null,
|
entity_id: collisionLikely ? baseActor.entity.id : null,
|
||||||
entry_id: baseActor.entryId,
|
entry_id: collisionLikely ? baseActor.entryId : null,
|
||||||
batch_id: batchId,
|
batch_id: batchId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -641,6 +648,11 @@ async function scrapeProfiles(actor, sources, entitiesBySlug, existingProfilesBy
|
||||||
const scraper = scrapers[scraperSlug];
|
const scraper = scrapers[scraperSlug];
|
||||||
const layoutScraper = resolveLayoutScraper(entity, scraper);
|
const layoutScraper = resolveLayoutScraper(entity, scraper);
|
||||||
|
|
||||||
|
if (!layoutScraper?.fetchProfile) {
|
||||||
|
logger.warn(`No profile profile scraper available for ${scraperSlug}`);
|
||||||
|
throw new Error(`No profile profile scraper available for ${scraperSlug}`);
|
||||||
|
}
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
...entity,
|
...entity,
|
||||||
// legacy
|
// legacy
|
||||||
|
@ -653,11 +665,6 @@ async function scrapeProfiles(actor, sources, entitiesBySlug, existingProfilesBy
|
||||||
|
|
||||||
const label = context.entity?.name;
|
const label = context.entity?.name;
|
||||||
|
|
||||||
if (!layoutScraper?.fetchProfile) {
|
|
||||||
logger.warn(`No profile profile scraper available for ${scraperSlug}`);
|
|
||||||
throw new Error(`No profile profile scraper available for ${scraperSlug}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!context.entity) {
|
if (!context.entity) {
|
||||||
logger.warn(`No entity found for ${scraperSlug}`);
|
logger.warn(`No entity found for ${scraperSlug}`);
|
||||||
throw new Error(`No entity found for ${scraperSlug}`);
|
throw new Error(`No entity found for ${scraperSlug}`);
|
||||||
|
@ -813,33 +820,53 @@ async function scrapeActors(argNames) {
|
||||||
|
|
||||||
async function getOrCreateActors(baseActors, batchId) {
|
async function getOrCreateActors(baseActors, batchId) {
|
||||||
// WHERE IN causes stack depth error and performance issues with a large amount of values, no knex VALUES helper available
|
// WHERE IN causes stack depth error and performance issues with a large amount of values, no knex VALUES helper available
|
||||||
const actorValues = baseActors.map(actor => knex.raw('(:slug, :entityId)', { slug: actor.slug, entityId: actor.entity.id })).join(', ');
|
const actorValues = baseActors.map(actor => knex.raw('(:slug, :entityId, :entryId, :collisionLikely)', {
|
||||||
|
slug: actor.slug,
|
||||||
|
entityId: actor.entity.id,
|
||||||
|
entryId: actor.entryId,
|
||||||
|
collisionLikely: getCollisionLikely(actor),
|
||||||
|
})).join(', ');
|
||||||
|
|
||||||
const existingActors = await knex
|
const existingActors = await knex
|
||||||
.select('actors.*')
|
.select('actors.*')
|
||||||
.from(knex.raw(`actors, (VALUES ${actorValues}) AS base_actors (slug, entity_id)`))
|
.from(knex.raw(`actors, (VALUES ${actorValues}) AS base_actors (slug, entity_id, entry_id, collision_likely)`))
|
||||||
.whereRaw('actors.slug = base_actors.slug AND actors.entity_id IS NULL')
|
.whereRaw(`
|
||||||
.orWhereRaw('actors.slug = base_actors.slug AND actors.entity_id = base_actors.entity_id');
|
actors.slug = base_actors.slug
|
||||||
|
AND actors.entity_id IS NULL
|
||||||
|
AND NOT base_actors.collision_likely
|
||||||
|
`)
|
||||||
|
.orWhereRaw(`
|
||||||
|
actors.slug = base_actors.slug
|
||||||
|
AND actors.entity_id = base_actors.entity_id
|
||||||
|
AND ((actors.entry_id IS NULL AND base_actors.entry_id IS NULL)
|
||||||
|
OR actors.entry_id = base_actors.entry_id)
|
||||||
|
`);
|
||||||
|
|
||||||
// const existingActorSlugs = new Set(existingActors.map(actor => actor.slug));
|
// const existingActorSlugs = new Set(existingActors.map(actor => actor.slug));
|
||||||
const existingActorSlugs = existingActors.reduce((acc, actor) => ({
|
const existingActorSlugs = existingActors.reduce((acc, actor) => ({
|
||||||
...acc,
|
...acc,
|
||||||
[actor.entity_id]: {
|
[actor.entity_id]: {
|
||||||
...acc[actor.entity_id],
|
...acc[actor.entity_id],
|
||||||
[actor.slug]: true,
|
[actor.entry_id]: {
|
||||||
|
...acc[actor.entity_id]?.[actor.entry_id],
|
||||||
|
[actor.slug]: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}), {});
|
}), {});
|
||||||
|
|
||||||
const uniqueBaseActors = baseActors.filter(baseActor => !existingActorSlugs[baseActor.entity.id]?.[baseActor.slug] && !existingActorSlugs.null?.[baseActor.slug]);
|
const uniqueBaseActors = baseActors.filter(baseActor => !existingActorSlugs[baseActor.entity.id]?.[baseActor.entryId]?.[baseActor.slug] && !existingActorSlugs.null?.null?.[baseActor.slug]);
|
||||||
|
|
||||||
const curatedActorEntries = curateActorEntries(uniqueBaseActors, batchId);
|
const curatedActorEntries = curateActorEntries(uniqueBaseActors, batchId);
|
||||||
|
|
||||||
const newActors = await bulkInsert('actors', curatedActorEntries);
|
const newActors = await bulkInsert('actors', curatedActorEntries);
|
||||||
|
|
||||||
const newActorIdsByEntityIdAndSlug = newActors.reduce((acc, actor) => ({
|
const newActorIdsByEntityIdEntryIdAndSlug = newActors.reduce((acc, actor) => ({
|
||||||
...acc,
|
...acc,
|
||||||
[actor.entity_id]: {
|
[actor.entity_id]: {
|
||||||
...acc[actor.entity_id],
|
...acc[actor.entity_id],
|
||||||
[actor.slug]: actor.id,
|
[actor.entry_id]: {
|
||||||
|
...acc[actor.entity_id]?.[actor.entry_id],
|
||||||
|
[actor.slug]: actor.id,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}), {});
|
}), {});
|
||||||
|
|
||||||
|
@ -847,7 +874,7 @@ async function getOrCreateActors(baseActors, batchId) {
|
||||||
.filter(actor => actor.hasProfile)
|
.filter(actor => actor.hasProfile)
|
||||||
.map(actor => ({
|
.map(actor => ({
|
||||||
...actor,
|
...actor,
|
||||||
id: newActorIdsByEntityIdAndSlug[actor.entity?.id]?.[actor.slug] || newActorIdsByEntityIdAndSlug.null?.[actor.slug],
|
id: newActorIdsByEntityIdEntryIdAndSlug[actor.entity?.id]?.[actor.entryId]?.[actor.slug] || newActorIdsByEntityIdEntryIdAndSlug.null?.null?.[actor.slug],
|
||||||
}))
|
}))
|
||||||
.filter(actor => !!actor.id)
|
.filter(actor => !!actor.id)
|
||||||
.map(actor => curateProfile(actor)));
|
.map(actor => curateProfile(actor)));
|
||||||
|
@ -885,16 +912,32 @@ async function associateActors(releases, batchId) {
|
||||||
|
|
||||||
const actors = await getOrCreateActors(uniqueBaseActors, batchId);
|
const actors = await getOrCreateActors(uniqueBaseActors, batchId);
|
||||||
|
|
||||||
|
/*
|
||||||
const actorIdsBySlug = actors.reduce((acc, actor) => ({
|
const actorIdsBySlug = actors.reduce((acc, actor) => ({
|
||||||
...acc,
|
...acc,
|
||||||
[actor.slug]: actor.alias_for || actor.id,
|
[actor.slug]: actor.alias_for || actor.id,
|
||||||
}), {});
|
}), {});
|
||||||
|
*/
|
||||||
|
|
||||||
|
const actorIdsByEntityIdEntryIdAndSlug = actors.reduce((acc, actor) => ({
|
||||||
|
...acc,
|
||||||
|
[actor.entity_id]: {
|
||||||
|
...acc[actor.entity_id],
|
||||||
|
[actor.entry_id]: {
|
||||||
|
...acc[actor.entity_id]?.[actor.entry_id],
|
||||||
|
[actor.slug]: {
|
||||||
|
actor_id: actor.alias_for || actor.id,
|
||||||
|
alias_id: actor.alias_for ? actor.id : null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}), {});
|
||||||
|
|
||||||
const releaseActorAssociations = Object.entries(baseActorsByReleaseId)
|
const releaseActorAssociations = Object.entries(baseActorsByReleaseId)
|
||||||
.map(([releaseId, releaseActors]) => releaseActors
|
.map(([releaseId, releaseActors]) => releaseActors
|
||||||
.map(releaseActor => ({
|
.map(releaseActor => ({
|
||||||
release_id: releaseId,
|
release_id: releaseId,
|
||||||
actor_id: actorIdsBySlug[releaseActor.slug],
|
...(actorIdsByEntityIdEntryIdAndSlug[releaseActor.entity?.id]?.[releaseActor.entryId]?.[releaseActor.slug] || actorIdsByEntityIdEntryIdAndSlug.null.null[releaseActor.slug]),
|
||||||
})))
|
})))
|
||||||
.flat();
|
.flat();
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ function scrapeAllClassic(scenes, channel) {
|
||||||
release.title = query.cnt('.updateInfo h5 a');
|
release.title = query.cnt('.updateInfo h5 a');
|
||||||
|
|
||||||
release.actors = query.cnts('.tour_update_models a');
|
release.actors = query.cnts('.tour_update_models a');
|
||||||
release.date = query.date('.availdate, .updateInfo p span:nth-child(2)', 'MM/DD/YYYY');
|
release.date = query.date('.availdate, .updateInfo p span:last-child', 'MM/DD/YYYY');
|
||||||
|
|
||||||
release.poster = query.img('.updateThumb img');
|
release.poster = query.img('.updateThumb img');
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ function scrapeScene({ query }, url, channel) {
|
||||||
release.poster = query.img('#video-poster', 'data-poster', { origin: channel.url });
|
release.poster = query.img('#video-poster', 'data-poster', { origin: channel.url });
|
||||||
release.photos = query.imgs('#gallery .photo-item img', 'data-src', { origin: channel.url });
|
release.photos = query.imgs('#gallery .photo-item img', 'data-src', { origin: channel.url });
|
||||||
|
|
||||||
release.trailer = query.video('.trailer source');
|
release.trailer = query.video();
|
||||||
|
|
||||||
release.channel = slugify(query.q('.video-detail-logo img', 'alt'), '');
|
release.channel = slugify(query.q('.video-detail-logo img', 'alt'), '');
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ function resolveLayoutScraper(entity, scraper) {
|
||||||
return scraper[entity.parameters.layout];
|
return scraper[entity.parameters.layout];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity.parent) {
|
if (entity?.parent) {
|
||||||
return resolveLayoutScraper(entity.parent, scraper);
|
return resolveLayoutScraper(entity.parent, scraper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -222,7 +222,11 @@ function actors(release) {
|
||||||
: Math.floor(Math.random() * 3) + 2;
|
: Math.floor(Math.random() * 3) + 2;
|
||||||
|
|
||||||
return Array.from({ length }, () => ({
|
return Array.from({ length }, () => ({
|
||||||
name: faker.name.findName(),
|
name: faker.name
|
||||||
|
.findName()
|
||||||
|
.split(' ')
|
||||||
|
.slice(0, Math.random() < 0.2 ? 1 : 2) // sometimes only use the first name
|
||||||
|
.join(' '),
|
||||||
gender: gender(),
|
gender: gender(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue