Showing curated campaigns on tags page.
This commit is contained in:
parent
64bf3b65ac
commit
f9b7a8731e
|
@ -72,5 +72,6 @@ const bannerSrc = (() => {
|
|||
height: auto;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -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 });
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.*')
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue