diff --git a/components/campaigns/campaign.vue b/components/campaigns/campaign.vue index 9288eb3..5cb8987 100644 --- a/components/campaigns/campaign.vue +++ b/components/campaigns/campaign.vue @@ -72,5 +72,6 @@ const bannerSrc = (() => { height: auto; max-height: 100%; max-width: 100%; + object-fit: contain; } diff --git a/pages/entities/@entitySlug/+onBeforeRender.js b/pages/entities/@entitySlug/+onBeforeRender.js index 47f57d4..0b4b290 100644 --- a/pages/entities/@entitySlug/+onBeforeRender.js +++ b/pages/entities/@entitySlug/+onBeforeRender.js @@ -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 }); diff --git a/pages/tags/@tagId/+onBeforeRender.js b/pages/tags/@tagId/+onBeforeRender.js index 244bb17..4839581 100644 --- a/pages/tags/@tagId/+onBeforeRender.js +++ b/pages/tags/@tagId/+onBeforeRender.js @@ -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, + }, }, }; } diff --git a/pages/updates/+onBeforeRender.js b/pages/updates/+onBeforeRender.js index ddf1dca..b88e748 100644 --- a/pages/updates/+onBeforeRender.js +++ b/pages/updates/+onBeforeRender.js @@ -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 { diff --git a/src/campaigns.js b/src/campaigns.js index d20a717..8916519 100644 --- a/src/campaigns.js +++ b/src/campaigns.js @@ -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.*') diff --git a/src/web/consent.js b/src/web/consent.js index fc6b48b..86f6546 100644 --- a/src/web/consent.js +++ b/src/web/consent.js @@ -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)); diff --git a/src/web/scenes.js b/src/web/scenes.js index 2ca68ef..9ef9979 100644 --- a/src/web/scenes.js +++ b/src/web/scenes.js @@ -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,