Showing curated campaigns on tags page.

This commit is contained in:
DebaucheryLibrarian 2024-06-17 01:28:20 +02:00
parent 64bf3b65ac
commit f9b7a8731e
7 changed files with 81 additions and 25 deletions

View File

@ -72,5 +72,6 @@ const bannerSrc = (() => {
height: auto;
max-height: 100%;
max-width: 100%;
object-fit: contain;
}
</style>

View File

@ -34,16 +34,19 @@ export async function onBeforeRender(pageContext) {
{
entityIds: [entity.id, entity.parent?.id].filter(Boolean),
minRatio: 1.5,
allowRandomFallback: false,
},
{
entityIds: [entity.id, entity.parent?.id].filter(Boolean),
minRatio: 0.75,
maxRatio: 1.25,
allowRandomFallback: false,
},
{
entityIds: [entity.id, entity.parent?.id].filter(Boolean),
parentEntityId: entity.parent?.id,
minRatio: 1.5,
allowRandomFallback: false,
},
], { tagFilter: pageContext.tagFilter });

View File

@ -4,12 +4,15 @@ import markdownItClass from '@toycode/markdown-it-class';
import { fetchTagsById } from '#/src/tags.js';
import { fetchScenes } from '#/src/scenes.js';
import { curateScenesQuery } from '#/src/web/scenes.js';
import { getRandomCampaigns, getCampaignIndex } from '#/src/campaigns.js';
const md = markdownIt().use(markdownItClass, { a: 'link' });
export async function onBeforeRender(pageContext) {
const [[tag], tagScenes] = await Promise.all([
fetchTagsById([pageContext.routeParams.tagSlug]),
const tagSlug = pageContext.routeParams.tagSlug;
const [[tag], tagScenes, campaigns] = await Promise.all([
fetchTagsById([tagSlug]),
fetchScenes(await curateScenesQuery({
...pageContext.urlQuery,
scope: pageContext.routeParams.scope || 'latest',
@ -20,6 +23,11 @@ export async function onBeforeRender(pageContext) {
limit: Number(pageContext.urlParsed.search.limit) || 30,
aggregate: true,
}, pageContext.user),
getRandomCampaigns([
{ tagSlugs: [tagSlug], minRatio: 1.5 },
{ tagSlugs: [tagSlug], minRatio: 0.75, maxRatio: 1.25 },
{ tagSlugs: [tagSlug], minRatio: 1.5 },
], { tagFilter: pageContext.tagFilter }),
]);
const {
@ -33,6 +41,9 @@ export async function onBeforeRender(pageContext) {
const description = tag.description && md.renderInline(tag.description);
const campaignIndex = getCampaignIndex(scenes.length);
const [metaCampaign, sceneCampaign, paginationCampaign] = campaigns;
return {
pageContext: {
title: tag.name,
@ -46,6 +57,12 @@ export async function onBeforeRender(pageContext) {
total,
limit,
},
campaigns: {
index: campaignIndex,
meta: metaCampaign,
scenes: scenes.length > 5 && sceneCampaign,
pagination: paginationCampaign,
},
},
};
}

View File

@ -1,6 +1,6 @@
import { fetchScenes } from '#/src/scenes.js';
import { curateScenesQuery } from '#/src/web/scenes.js';
import { getRandomCampaigns } from '#/src/campaigns.js';
import { getRandomCampaigns, getCampaignIndex } from '#/src/campaigns.js';
export async function onBeforeRender(pageContext) {
const withQuery = Object.hasOwn(pageContext.urlParsed.search, 'q');
@ -35,7 +35,7 @@ export async function onBeforeRender(pageContext) {
total,
} = sceneResults;
const campaignIndex = Math.floor((Math.random() * (0.5 - 0.2) + 0.2) * scenes.length);
const campaignIndex = getCampaignIndex(scenes.length);
const [scopeCampaign, sceneCampaign, paginationCampaign] = campaigns;
return {

View File

@ -33,6 +33,26 @@ function curateCampaign(campaign) {
};
}
function selectRandomCampaign(primaryCampaigns, entityCampaigns, preferredCampaigns, allCampaigns, options) {
if (primaryCampaigns.length > 0) {
return primaryCampaigns[crypto.randomInt(primaryCampaigns.length)];
}
if (entityCampaigns.length > 0) {
return entityCampaigns[crypto.randomInt(entityCampaigns.length)];
}
if (preferredCampaigns.length > 0) {
return preferredCampaigns[crypto.randomInt(preferredCampaigns.length)];
}
if (allCampaigns.length > 0 && options.allowRandomFallback !== false) {
return allCampaigns[crypto.randomInt(allCampaigns.length)];
}
return null;
}
export async function getRandomCampaign(options = {}, context = {}) {
const campaigns = options.campaigns
|| await redis.hGetAll('traxxx:campaigns').then((rawCampaigns) => Object.values(rawCampaigns).map((rawCampaign) => JSON.parse(rawCampaign)));
@ -50,40 +70,40 @@ export async function getRandomCampaign(options = {}, context = {}) {
return false;
}
if (context.tagFilter && campaign.banner && campaign.banner.tags.some((tag) => context.tagFilter.includes(tag))) {
if (context.tagFilter && campaign.banner && campaign.banner.tags.some((tag) => context.tagFilter.includes(tag) && !options.tagSlugs?.includes(tag))) {
return false;
}
// tag page overrides tag filter
if (options.tagSlugs && campaign.banner && !campaign.banner.tags.some((tag) => options.tagSlugs.includes(tag))) {
return false;
}
return true;
});
if (validCampaigns.length > 0) {
const campaignsByEntityId = validCampaigns.reduce((acc, campaign) => {
const entityId = campaign.entity.parent?.id || campaign.entity.id;
// console.log(validCampaigns);
if (!acc[entityId]) {
acc[entityId] = [];
}
const campaignsByEntityId = validCampaigns.reduce((acc, campaign) => {
const entityId = campaign.entity.parent?.id || campaign.entity.id;
acc[entityId].push(campaign);
if (!acc[entityId]) {
acc[entityId] = [];
}
return acc;
}, {});
acc[entityId].push(campaign);
// randomize entities first to ensure fair exposure for entities with fewer banners
const entityIds = Object.keys(campaignsByEntityId);
const randomEntityCampaigns = campaignsByEntityId[entityIds[crypto.randomInt(entityIds.length)]];
return acc;
}, {});
const primaryCampaigns = randomEntityCampaigns.filter((campaign) => campaign.entity.id === options.entityIds?.[0]);
// randomize entities first to ensure fair exposure for entities with fewer banners
const entityIds = Object.keys(campaignsByEntityId);
const randomEntityCampaigns = entityIds.length > 0 ? campaignsByEntityId[entityIds[crypto.randomInt(entityIds.length)]] : [];
const primaryCampaigns = randomEntityCampaigns.filter((campaign) => campaign.entity.id === options.entityIds?.[0]);
const randomCampaign = (primaryCampaigns.length > 0 ? primaryCampaigns[crypto.randomInt(primaryCampaigns.length)] : null)
|| (randomEntityCampaigns.length > 0 ? randomEntityCampaigns[crypto.randomInt(randomEntityCampaigns.length)] : null)
|| validCampaigns[crypto.randomInt(validCampaigns.length)];
const randomCampaign = selectRandomCampaign(primaryCampaigns, randomEntityCampaigns, validCampaigns, campaigns, options);
return randomCampaign;
}
return null;
return randomCampaign;
}
export async function getRandomCampaigns(allOptions = [], context = {}) {
@ -96,6 +116,10 @@ export async function getRandomCampaigns(allOptions = [], context = {}) {
}, context)));
}
export function getCampaignIndex(scenesCount) {
return Math.floor((Math.random() * (0.5 - 0.2) + 0.2) * scenesCount); // avoid start and end of scenes list
}
export async function cacheCampaigns() {
const campaigns = await knex('campaigns')
.select('campaigns.*')

View File

@ -1,6 +1,11 @@
export default function consentHandler(req, res, next) {
const redirect = req.headers.referer && new URL(req.headers.referer).searchParams.get('redirect');
if (req.path.includes('/api')) {
next();
return;
}
if (Object.hasOwn(req.query, 'lgbt')) {
const lgbtFilters = (req.tagFilter || []).filter((tag) => !['gay', 'bisexual', 'transsexual'].includes(tag));

View File

@ -24,6 +24,8 @@ export async function curateScenesQuery(query) {
notEntityIds: await getIdsBySlug(splitEntities.filter((entity) => entity.charAt(0) === '!').map((entity) => entity.slice(1)), 'entities'),
});
console.log('QUERY', query);
return {
scope: query.scope || 'latest',
query: query.q,
@ -41,6 +43,8 @@ export async function curateScenesQuery(query) {
}
export async function fetchScenesApi(req, res) {
console.log('REQUEST', req.query);
const {
scenes,
aggActors,
@ -56,6 +60,8 @@ export async function fetchScenesApi(req, res) {
limit: Number(req.query.limit) || 30,
}, req.user);
console.log('OUTPUT', scenes.length);
res.send(stringify({
scenes,
aggActors,