Removed info channels from overview. Fixed poster gap.

This commit is contained in:
DebaucheryLibrarian 2024-06-12 17:09:53 +02:00
parent bfe6dc866d
commit 41d6324c28
13 changed files with 302 additions and 99 deletions

View File

@ -1,17 +1,33 @@
<template> <template>
<div class="campaign"> <iframe
<a v-if="campaign?.banner?.type === 'html'"
:href="campaign.url" ref="iframe"
target="_blank" :width="campaign.banner.width"
class="campaign-link" :height="campaign.banner.height"
:src="bannerSrc"
scrolling="no"
marginwidth="0"
marginheight="0"
class="campaign frame"
data-umami-event="campaign-click"
:data-umami-event-campaign-id="`${campaign.entity.slug}-${campaign.id}`"
/>
<a
v-else-if="campaign.banner"
:href="campaign.url || campaign.affiliate?.url"
target="_blank"
class="campaign"
data-umami-event="campaign-click"
:data-umami-event-campaign-id="`${campaign.entity.slug}-${campaign.id}`"
>
<img
:src="bannerSrc"
:width="campaign.banner.width"
:height="campaign.banner.height"
class="campaign-banner"
> >
<img </a>
v-if="campaign.banner"
:src="getBanner(campaign)"
class="campaign-banner"
>
</a>
</div>
</template> </template>
<script setup> <script setup>
@ -24,17 +40,19 @@ const props = defineProps({
// console.log(props.campaign?.banner); // console.log(props.campaign?.banner);
function getBanner(campaign) { const bannerSrc = (() => {
if (campaign.banner.entity.type === 'network' || !campaign.banner.entity.parent) { if (props.campaign.banner) {
return `/banners/${campaign.banner.entity.slug}/${campaign.banner.id}.${campaign.banner.type || 'jpg'}`; if (props.campaign.banner.entity.type === 'network' || !props.campaign.banner.entity.parent) {
} return `/banners/${props.campaign.banner.entity.slug}/${props.campaign.banner.id}.${props.campaign.banner.type || 'jpg'}`;
}
if (campaign.banner.entity.type === 'channel' && campaign.banner.entity.parent?.type === 'network') { if (props.campaign.banner.entity.type === 'channel' && props.campaign.banner.entity.parent?.type === 'network') {
return `/banners/${campaign.banner.entity.parent.slug}/${campaign.banner.entity.slug}/${campaign.banner.id}.${campaign.banner.type || 'jpg'}`; return `/banners/${props.campaign.banner.entity.parent.slug}/${props.campaign.banner.entity.slug}/${props.campaign.banner.id}.${props.campaign.banner.type || 'jpg'}`;
}
} }
return null; return null;
} })();
</script> </script>
<style scoped> <style scoped>
@ -44,7 +62,14 @@ function getBanner(campaign) {
justify-content: center; justify-content: center;
} }
.frame {
border: none;
overflow: hidden;
}
.campaign-banner { .campaign-banner {
width: auto;
height: auto;
max-height: 100%; max-height: 100%;
max-width: 100%; max-width: 100%;
} }

View File

@ -1,5 +1,10 @@
<template> <template>
<div class="pagination-container"> <div class="pagination-container">
<Campaign
v-if="campaigns?.pagination"
:campaign="campaigns.pagination"
/>
<div <div
v-if="currentPage === pageTotal && total > env.maxMatches" v-if="currentPage === pageTotal && total > env.maxMatches"
class="more" class="more"
@ -89,6 +94,8 @@
import { computed, inject } from 'vue'; import { computed, inject } from 'vue';
import { parse } from 'path-to-regexp'; import { parse } from 'path-to-regexp';
import Campaign from '#/components/campaigns/campaign.vue';
const props = defineProps({ const props = defineProps({
page: { page: {
type: Number, type: Number,
@ -117,7 +124,6 @@ const props = defineProps({
}); });
const emit = defineEmits(['navigation']); const emit = defineEmits(['navigation']);
const pageContext = inject('pageContext'); const pageContext = inject('pageContext');
const { const {
@ -125,6 +131,7 @@ const {
urlParsed, urlParsed,
pageProps, pageProps,
env, env,
campaigns,
} = pageContext; } = pageContext;
const currentPage = computed(() => props.page || Number(routeParams?.page)); const currentPage = computed(() => props.page || Number(routeParams?.page));
@ -203,6 +210,7 @@ function getPath(page) {
.pagination-container { .pagination-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center;
justify-content: center; justify-content: center;
} }
@ -286,6 +294,12 @@ function getPath(page) {
font-size: 1.1rem; font-size: 1.1rem;
} }
.campaign {
max-width: 100%;
max-height: 6rem;
padding: 0 1rem;
}
@media (--small-30) { @media (--small-30) {
.pagination { .pagination {
height: 4.5rem; height: 4.5rem;

View File

@ -54,6 +54,11 @@
> >
<div class="meta">{{ total }} results</div> <div class="meta">{{ total }} results</div>
<Campaign
v-if="campaigns?.meta"
:campaign="campaigns.meta"
/>
<select <select
v-model="scope" v-model="scope"
class="input" class="input"
@ -83,23 +88,30 @@
v-if="showScopeTabs" v-if="showScopeTabs"
class="scopes" class="scopes"
> >
<Link <div class="scopes-pills">
:href="getPath('latest')" <Link
class="scope nolink" :href="getPath('latest')"
:active="scope === 'latest'" class="scope nolink"
>Latest</Link> :active="scope === 'latest'"
>Latest</Link>
<Link <Link
:href="getPath('upcoming')" :href="getPath('upcoming')"
class="scope nolink" class="scope nolink"
:active="scope === 'upcoming'" :active="scope === 'upcoming'"
>Upcoming</Link> >Upcoming</Link>
<Link <Link
:href="getPath('new')" :href="getPath('new')"
class="scope nolink" class="scope nolink"
:active="scope === 'new'" :active="scope === 'new'"
>New</Link> >New</Link>
</div>
<Campaign
v-if="campaigns?.scope"
:campaign="campaigns.scope"
/>
</nav> </nav>
<ul <ul
@ -107,10 +119,10 @@
> >
<template v-for="item in campaignScenes"> <template v-for="item in campaignScenes">
<li <li
v-if="item === 'campaign' && campaign" v-if="item === 'campaign' && sceneCampaign"
:key="`campaign-${item.id}`" :key="`campaign-${item.id}`"
> >
<Campaign :campaign="campaign" /> <Campaign :campaign="sceneCampaign" />
</li> </li>
<li <li
@ -172,7 +184,12 @@ const props = defineProps({
}, },
}); });
const { pageProps, routeParams, urlParsed } = inject('pageContext'); const {
pageProps,
routeParams,
urlParsed,
campaigns,
} = inject('pageContext');
const { const {
actor: pageActor, actor: pageActor,
@ -206,10 +223,10 @@ const filters = ref({
actors: queryActors, actors: queryActors,
}); });
const campaign = pageProps.campaigns?.scenes; const sceneCampaign = campaigns?.scenes;
const campaignIndex = pageProps.campaigns?.index; const campaignIndex = campaigns?.index;
const campaignScenes = computed(() => scenes.value.flatMap((scene, index) => (index === campaignIndex ? ['campaign', scene] : scene))); const campaignScenes = computed(() => scenes.value.flatMap((scene, index) => (sceneCampaign && index === campaignIndex ? ['campaign', scene] : scene)));
function getPath(targetScope, preserveQuery) { function getPath(targetScope, preserveQuery) {
const path = parse(routeParams.path).map((segment) => { const path = parse(routeParams.path).map((segment) => {
@ -302,8 +319,15 @@ function updateFilter(prop, value, reload = true) {
.scenes-header { .scenes-header {
display: flex; display: flex;
align-items: center; align-items: flex-end;
justify-content: space-between;
padding: .5rem 1rem .25rem 3rem; padding: .5rem 1rem .25rem 3rem;
.campaign {
max-height: 6rem;
justify-content: center;
margin: .5rem 1rem 0 1rem;
}
} }
.scenes-container { .scenes-container {
@ -314,9 +338,9 @@ function updateFilter(prop, value, reload = true) {
.meta { .meta {
display: flex; display: flex;
flex-grow: 1;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: .5rem;
} }
.scenes { .scenes {
@ -328,8 +352,21 @@ function updateFilter(prop, value, reload = true) {
.scopes { .scopes {
display: flex; display: flex;
align-items: flex-end;
justify-content: space-between;
padding: .75rem 1rem .25rem 1rem;
.campaign {
max-height: 6rem;
justify-content: flex-end;
margin-left: 1rem;
}
}
.scopes-pills {
display: flex;
align-items: center;
gap: .5rem; gap: .5rem;
padding: .75rem 0 .25rem 1rem;
} }
.scope { .scope {
@ -370,6 +407,19 @@ function updateFilter(prop, value, reload = true) {
.scopes { .scopes {
justify-content: center; justify-content: center;
flex-direction: column-reverse;
.campaign {
width: 100%;
justify-content: center;
margin-left: 0;
margin-bottom: 1rem;
}
}
.scopes-pills {
width: 100%;
justify-content: center;
} }
} }

View File

@ -253,7 +253,6 @@ const scrollable = computed(() => children.value?.scrollWidth > children.value?.
@media(--small-20) { @media(--small-20) {
.logo { .logo {
height: 1rem;
padding: .5rem 1rem; padding: .5rem 1rem;
} }

View File

@ -3,6 +3,7 @@ import { render } from 'vike/abort'; /* eslint-disable-line import/extensions */
import { fetchEntitiesById } from '#/src/entities.js'; import { fetchEntitiesById } from '#/src/entities.js';
import { fetchScenes } from '#/src/scenes.js'; import { fetchScenes } from '#/src/scenes.js';
import { curateScenesQuery } from '#/src/web/scenes.js'; import { curateScenesQuery } from '#/src/web/scenes.js';
import { getRandomCampaigns } from '#/src/campaigns.js';
import redis from '#/src//redis.js'; import redis from '#/src//redis.js';
export async function onBeforeRender(pageContext) { export async function onBeforeRender(pageContext) {
@ -12,7 +13,10 @@ export async function onBeforeRender(pageContext) {
throw render(404, `Cannot find ${pageContext.routeParams.entityType} '${pageContext.routeParams.entitySlug}'.`); throw render(404, `Cannot find ${pageContext.routeParams.entityType} '${pageContext.routeParams.entitySlug}'.`);
} }
const [[entity], entityScenes] = await Promise.all([ const [
[entity],
entityScenes,
] = await Promise.all([
fetchEntitiesById([Number(entityId)], { includeChildren: true }), fetchEntitiesById([Number(entityId)], { includeChildren: true }),
fetchScenes(await curateScenesQuery({ fetchScenes(await curateScenesQuery({
...pageContext.urlQuery, ...pageContext.urlQuery,
@ -26,6 +30,23 @@ export async function onBeforeRender(pageContext) {
}, pageContext.user), }, pageContext.user),
]); ]);
const campaigns = await getRandomCampaigns([
{
entityIds: [entity.id, entity.parent?.id].filter(Boolean),
minRatio: 1.5,
},
{
entityIds: [entity.id, entity.parent?.id].filter(Boolean),
minRatio: 0.75,
maxRatio: 1.25,
},
{
entityIds: [entity.id, entity.parent?.id].filter(Boolean),
parentEntityId: entity.parent?.id,
minRatio: 1.5,
},
]);
const { const {
scenes, scenes,
aggActors, aggActors,
@ -35,6 +56,9 @@ export async function onBeforeRender(pageContext) {
limit, limit,
} = entityScenes; } = entityScenes;
const campaignIndex = Math.floor((Math.random() * (0.5 - 0.2) + 0.2) * scenes.length);
const [metaCampaign, sceneCampaign, paginationCampaign] = campaigns;
return { return {
pageContext: { pageContext: {
title: entity.name, title: entity.name,
@ -47,6 +71,12 @@ export async function onBeforeRender(pageContext) {
total, total,
limit, limit,
}, },
campaigns: {
index: campaignIndex,
meta: metaCampaign,
scenes: scenes.length > 5 && sceneCampaign,
pagination: paginationCampaign,
},
}, },
}; };
} }

View File

@ -36,12 +36,12 @@
</div> </div>
<div <div
v-if="scene.photos.length > 0" v-if="scene.photos.length > 0 || scene.caps.length > 0"
class="album" class="album"
:class="{ single: scene.photos.length === 1 }" :class="{ single: (scene.photos.length + scene.caps.length) === 1 }"
> >
<div <div
v-for="photo in scene.photos" v-for="photo in [...scene.photos, ...scene.caps]"
:key="`photo-${photo.id}`" :key="`photo-${photo.id}`"
class="photo-container" class="photo-container"
> >
@ -444,6 +444,11 @@ function copySummary() {
margin-right: .5rem; margin-right: .5rem;
} }
.poster-link {
height: 100%;
width: calc(21/9 * 16rem);
}
.poster { .poster {
height: 100%; height: 100%;
width: 100%; width: 100%;
@ -638,7 +643,7 @@ function copySummary() {
overflow-x: auto; overflow-x: auto;
.actor { .actor {
width: 8rem; width: 10rem;
flex-shrink: 0; flex-shrink: 0;
} }
} }
@ -786,6 +791,10 @@ function copySummary() {
flex-shrink: 1; flex-shrink: 1;
} }
.poster-link {
width: 100%;
}
.trailer { .trailer {
margin: 0; margin: 0;
flex-shrink: 1; flex-shrink: 1;
@ -812,7 +821,9 @@ function copySummary() {
} }
.actors { .actors {
grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); .actor {
width: 9rem;
}
} }
.series { .series {
@ -833,10 +844,4 @@ function copySummary() {
font-size: .9rem; font-size: .9rem;
} }
} }
@media(--small-60) {
.actors {
grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
}
}
</style> </style>

View File

@ -15,6 +15,8 @@ function getTitle(scene) {
export async function onBeforeRender(pageContext) { export async function onBeforeRender(pageContext) {
const [scene] = await fetchScenesById([Number(pageContext.routeParams.sceneId)], { const [scene] = await fetchScenesById([Number(pageContext.routeParams.sceneId)], {
reqUser: pageContext.user, reqUser: pageContext.user,
includeAssets: true,
includePartOf: true,
actorStashes: true, actorStashes: true,
}); });

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="updates">
<Scenes <Scenes
:show-filters="!!query" :show-filters="!!query"
:show-meta="!!query" :show-meta="!!query"
@ -16,3 +16,10 @@ import Scenes from '#/components/scenes/scenes.vue';
const pageContext = inject('pageContext'); const pageContext = inject('pageContext');
const query = Object.hasOwn(pageContext.urlParsed.search, 'q'); const query = Object.hasOwn(pageContext.urlParsed.search, 'q');
</script> </script>
<style scoped>
.updates {
display: flex;
flex-grow: 1;
}
</style>

View File

@ -1,10 +1,31 @@
import { fetchScenes } from '#/src/scenes.js'; import { fetchScenes } from '#/src/scenes.js';
import { curateScenesQuery } from '#/src/web/scenes.js'; import { curateScenesQuery } from '#/src/web/scenes.js';
import { getRandomCampaign } from '#/src/campaigns.js'; import { getRandomCampaigns } from '#/src/campaigns.js';
export async function onBeforeRender(pageContext) { export async function onBeforeRender(pageContext) {
const withQuery = Object.hasOwn(pageContext.urlParsed.search, 'q'); const withQuery = Object.hasOwn(pageContext.urlParsed.search, 'q');
const [
sceneResults,
campaigns,
] = await Promise.all([
fetchScenes(await curateScenesQuery({
...pageContext.urlQuery,
scope: pageContext.routeParams.scope || 'latest',
isShowcased: withQuery ? null : true,
tagFilter: pageContext.tagFilter,
}), {
page: Number(pageContext.routeParams.page) || 1,
limit: Number(pageContext.urlParsed.search.limit) || 29,
aggregate: withQuery,
}, pageContext.user),
getRandomCampaigns([
{ minRatio: 1.5 },
{ minRatio: 0.75, maxRatio: 1.25 },
{ minRatio: 1.5 },
]),
]);
const { const {
scenes, scenes,
aggTags, aggTags,
@ -12,20 +33,10 @@ export async function onBeforeRender(pageContext) {
aggActors, aggActors,
limit, limit,
total, total,
} = await fetchScenes(await curateScenesQuery({ } = sceneResults;
...pageContext.urlQuery,
scope: pageContext.routeParams.scope || 'latest',
isShowcased: withQuery ? null : true,
tagFilter: pageContext.tagFilter,
}), {
page: Number(pageContext.routeParams.page) || 1,
limit: Number(pageContext.urlParsed.search.limit) || 29,
aggregate: withQuery,
}, pageContext.user);
// const campaignIndex = Math.floor(Math.random() * (scenes.length - 5)) + 5;
const campaignIndex = Math.floor((Math.random() * (0.5 - 0.2) + 0.2) * scenes.length); const campaignIndex = Math.floor((Math.random() * (0.5 - 0.2) + 0.2) * scenes.length);
const sceneCampaign = await getRandomCampaign({ minRatio: 0.75, maxRatio: 1.25 }); const [scopeCampaign, sceneCampaign, paginationCampaign] = campaigns;
return { return {
pageContext: { pageContext: {
@ -37,10 +48,12 @@ export async function onBeforeRender(pageContext) {
aggActors, aggActors,
limit, limit,
total, total,
campaigns: { },
index: campaignIndex, campaigns: {
scenes: sceneCampaign, index: campaignIndex,
}, scope: scopeCampaign,
scenes: scenes.length > 5 && sceneCampaign,
pagination: paginationCampaign,
}, },
}, },
}; };

View File

@ -6,6 +6,7 @@ export default {
'urlParsed', 'urlParsed',
'env', 'env',
'user', 'user',
'campaigns',
'meta', 'meta',
], ],
}; };

View File

@ -15,6 +15,7 @@ function curateCampaign(campaign) {
return { return {
id: campaign.id, id: campaign.id,
url: campaign.url, url: campaign.url,
entity: campaign.entity && curateEntity({ ...campaign.entity, parent: campaign.parent_entity }),
banner: campaign.banner && { banner: campaign.banner && {
id: campaign.banner.id, id: campaign.banner.id,
type: campaign.banner.type, type: campaign.banner.type,
@ -37,8 +38,8 @@ function curateCampaign(campaign) {
} }
export async function getRandomCampaign(options = {}) { export async function getRandomCampaign(options = {}) {
const rawCampaigns = await redis.hGetAll('traxxx:campaigns'); const campaigns = options.campaigns
const campaigns = Object.values(rawCampaigns).map((rawCampaign) => JSON.parse(rawCampaign)); || await redis.hGetAll('traxxx:campaigns').then((rawCampaigns) => Object.values(rawCampaigns).map((rawCampaign) => JSON.parse(rawCampaign)));
const validCampaigns = campaigns.filter((campaign) => { const validCampaigns = campaigns.filter((campaign) => {
if (options.minRatio && (!campaign.banner || campaign.banner.ratio < options.minRatio)) { if (options.minRatio && (!campaign.banner || campaign.banner.ratio < options.minRatio)) {
@ -49,12 +50,34 @@ export async function getRandomCampaign(options = {}) {
return false; return false;
} }
if (options.entityIds && !options.entityIds.some((entityId) => campaign.entity.id === entityId || campaign.entity.parent?.id === entityId)) {
return false;
}
return true; return true;
}); });
const randomCampaign = validCampaigns[crypto.randomInt(validCampaigns.length)]; const primaryCampaigns = validCampaigns.filter((campaign) => campaign.entity.id === options.entityIds?.[0]);
return randomCampaign; if (validCampaigns.length > 0) {
const randomCampaign = primaryCampaigns.length > 0
? primaryCampaigns[crypto.randomInt(primaryCampaigns.length)]
: validCampaigns[crypto.randomInt(validCampaigns.length)];
return randomCampaign;
}
return null;
}
export async function getRandomCampaigns(allOptions = []) {
const rawCampaigns = await redis.hGetAll('traxxx:campaigns');
const campaigns = Object.values(rawCampaigns).map((rawCampaign) => JSON.parse(rawCampaign));
return Promise.all(allOptions.map(async (options) => getRandomCampaign({
...options,
campaigns,
})));
} }
export async function cacheCampaigns() { export async function cacheCampaigns() {
@ -64,17 +87,29 @@ export async function cacheCampaigns() {
'campaigns.*', 'campaigns.*',
knex.raw('row_to_json(affiliates) as affiliate'), knex.raw('row_to_json(affiliates) as affiliate'),
knex.raw('row_to_json(banners) as banner'), knex.raw('row_to_json(banners) as banner'),
knex.raw('row_to_json(entities) as banner_entity'), knex.raw('row_to_json(entities) as entity'),
knex.raw('row_to_json(parents) as banner_parent_entity'), knex.raw('row_to_json(parents) as parent_entity'),
knex.raw('row_to_json(banner_entities) as banner_entity'),
knex.raw('row_to_json(banner_parents) as banner_parent_entity'),
knex.raw('json_agg(tags.slug) filter (where tags.id is not null) as banner_tags'), knex.raw('json_agg(tags.slug) filter (where tags.id is not null) as banner_tags'),
) )
.leftJoin('affiliates', 'affiliates.id', 'campaigns.affiliate_id') .leftJoin('affiliates', 'affiliates.id', 'campaigns.affiliate_id')
.leftJoin('banners', 'banners.id', 'campaigns.banner_id') .leftJoin('banners', 'banners.id', 'campaigns.banner_id')
.leftJoin('banners_tags', 'banners_tags.banner_id', 'banners.id') .leftJoin('banners_tags', 'banners_tags.banner_id', 'banners.id')
.leftJoin('tags', 'tags.id', 'banners_tags.tag_id') .leftJoin('tags', 'tags.id', 'banners_tags.tag_id')
.leftJoin('entities', 'entities.id', 'banners.entity_id') .leftJoin('entities', 'entities.id', 'campaigns.entity_id')
.leftJoin('entities as parents', 'parents.id', 'entities.parent_id') .leftJoin('entities as parents', 'parents.id', 'entities.parent_id')
.groupBy('campaigns.id', 'affiliates.id', 'banners.id', 'entities.id', 'parents.id'); .leftJoin('entities as banner_entities', 'banner_entities.id', 'banners.entity_id')
.leftJoin('entities as banner_parents', 'banner_parents.id', 'banner_entities.parent_id')
.groupBy(
'campaigns.id',
'affiliates.id',
'entities.id',
'parents.id',
'banners.id',
'banner_entities.id',
'banner_parents.id',
);
await redis.del('traxxx:campaigns'); await redis.del('traxxx:campaigns');

View File

@ -30,16 +30,24 @@ export async function fetchEntities(options) {
if (options.query) { if (options.query) {
builder.where((whereBuilder) => { builder.where((whereBuilder) => {
whereBuilder whereBuilder
.whereILike('entities.name', `%${options.query}%`) .where((subBuilder) => {
.orWhereILike('entities.slug', `%${options.query}%`); subBuilder
.whereILike('entities.name', `%${options.query}%`)
.orWhereILike('entities.slug', `%${options.query}%`);
})
.whereNot('entities.type', 'info');
}); });
} }
if (options.type === 'primary') { if (options.type === 'primary') {
builder builder
.where('entities.type', 'network') .where((subBuilder) => {
.orWhere('entities.independent', true) subBuilder
.orWhereNull('entities.parent_id'); .where('entities.type', 'network')
.orWhere('entities.independent', true)
.orWhereNull('entities.parent_id');
})
.whereNot('entities.type', 'info');
return; return;
} }

View File

@ -73,7 +73,8 @@ function curateScene(rawScene, assets) {
poster: curateMedia(assets.poster), poster: curateMedia(assets.poster),
trailer: curateMedia(assets.trailer), trailer: curateMedia(assets.trailer),
teaser: curateMedia(assets.teaser), teaser: curateMedia(assets.teaser),
photos: assets.photos.map((photo) => curateMedia(photo)), photos: assets.photos?.map((photo) => curateMedia(photo)) || [],
caps: assets.caps?.map((cap) => curateMedia(cap)) || [],
stashes: assets.stashes?.map((stash) => curateStash(stash)) || [], stashes: assets.stashes?.map((stash) => curateStash(stash)) || [],
createdBatchId: rawScene.created_batch_id, createdBatchId: rawScene.created_batch_id,
updatedBatchId: rawScene.updated_batch_id, updatedBatchId: rawScene.updated_batch_id,
@ -92,6 +93,7 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
series, series,
posters, posters,
photos, photos,
caps,
trailers, trailers,
teasers, teasers,
stashes, stashes,
@ -125,24 +127,24 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
.whereNotNull('tags.id') .whereNotNull('tags.id')
.whereIn('release_id', sceneIds) .whereIn('release_id', sceneIds)
.orderBy('priority', 'desc'), .orderBy('priority', 'desc'),
movies: knex('movies_scenes') movies: context.includePartOf ? knex('movies_scenes')
.select('movies_scenes.scene_id', 'movies.*', knex.raw('json_agg(media) as movie_covers')) .select('movies_scenes.scene_id', 'movies.*', knex.raw('json_agg(media) as movie_covers'))
.leftJoin('movies', 'movies.id', 'movies_scenes.movie_id') .leftJoin('movies', 'movies.id', 'movies_scenes.movie_id')
.leftJoin('movies_covers', 'movies_covers.movie_id', 'movies.id') .leftJoin('movies_covers', 'movies_covers.movie_id', 'movies.id')
.leftJoin('media', 'media.id', 'movies_covers.media_id') .leftJoin('media', 'media.id', 'movies_covers.media_id')
.whereIn('scene_id', sceneIds) .whereIn('scene_id', sceneIds)
.groupBy('movies.id', 'movies_scenes.scene_id'), .groupBy('movies.id', 'movies_scenes.scene_id') : [],
series: knex('series_scenes') series: context.includePartOf ? knex('series_scenes')
.select('series_scenes.scene_id', 'series.*', knex.raw('row_to_json(media) as serie_poster')) .select('series_scenes.scene_id', 'series.*', knex.raw('row_to_json(media) as serie_poster'))
.leftJoin('series', 'series.id', 'series_scenes.serie_id') .leftJoin('series', 'series.id', 'series_scenes.serie_id')
.leftJoin('series_posters', 'series_posters.serie_id', 'series.id') .leftJoin('series_posters', 'series_posters.serie_id', 'series.id')
.leftJoin('media', 'media.id', 'series_posters.media_id') .leftJoin('media', 'media.id', 'series_posters.media_id')
.whereIn('scene_id', sceneIds) .whereIn('scene_id', sceneIds)
.groupBy('series.id', 'series_scenes.scene_id', 'media.*'), .groupBy('series.id', 'series_scenes.scene_id', 'media.*') : [],
posters: knex('releases_posters') posters: knex('releases_posters')
.whereIn('release_id', sceneIds) .whereIn('release_id', sceneIds)
.leftJoin('media', 'media.id', 'releases_posters.media_id'), .leftJoin('media', 'media.id', 'releases_posters.media_id'),
photos: knex.transaction(async (trx) => { photos: context.includeAssets ? knex.transaction(async (trx) => {
if (reqUser) { if (reqUser) {
await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id })); await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id }));
} }
@ -151,8 +153,18 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
.leftJoin('media', 'media.id', 'releases_photos.media_id') .leftJoin('media', 'media.id', 'releases_photos.media_id')
.whereIn('release_id', sceneIds) .whereIn('release_id', sceneIds)
.orderBy('index'); .orderBy('index');
}), }) : [],
trailers: knex.transaction(async (trx) => { caps: context.includeAssets ? knex.transaction(async (trx) => {
if (reqUser) {
await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id }));
}
return trx('releases_caps')
.leftJoin('media', 'media.id', 'releases_caps.media_id')
.whereIn('release_id', sceneIds)
.orderBy('index');
}) : [],
trailers: context.includeAssets ? knex.transaction(async (trx) => {
if (reqUser) { if (reqUser) {
await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id })); await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id }));
} }
@ -160,8 +172,8 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
return trx('releases_trailers') return trx('releases_trailers')
.whereIn('release_id', sceneIds) .whereIn('release_id', sceneIds)
.leftJoin('media', 'media.id', 'releases_trailers.media_id'); .leftJoin('media', 'media.id', 'releases_trailers.media_id');
}), }) : [],
teasers: knex.transaction(async (trx) => { teasers: context.includeAssets ? knex.transaction(async (trx) => {
if (reqUser) { if (reqUser) {
await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id })); await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id }));
} }
@ -169,7 +181,7 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
return trx('releases_teasers') return trx('releases_teasers')
.whereIn('release_id', sceneIds) .whereIn('release_id', sceneIds)
.leftJoin('media', 'media.id', 'releases_teasers.media_id'); .leftJoin('media', 'media.id', 'releases_teasers.media_id');
}), }) : [],
lastBatch: knex('batches') lastBatch: knex('batches')
.select('id') .select('id')
.where('showcased', true) .where('showcased', true)
@ -205,6 +217,7 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
const sceneSeries = series.filter((serie) => serie.scene_id === sceneId); const sceneSeries = series.filter((serie) => serie.scene_id === sceneId);
const scenePoster = posters.find((poster) => poster.release_id === sceneId); const scenePoster = posters.find((poster) => poster.release_id === sceneId);
const scenePhotos = photos.filter((photo) => photo.release_id === sceneId); const scenePhotos = photos.filter((photo) => photo.release_id === sceneId);
const sceneCaps = caps.filter((cap) => cap.release_id === sceneId);
const sceneTrailers = trailers.find((trailer) => trailer.release_id === sceneId); const sceneTrailers = trailers.find((trailer) => trailer.release_id === sceneId);
const sceneTeasers = teasers.find((teaser) => teaser.release_id === sceneId); const sceneTeasers = teasers.find((teaser) => teaser.release_id === sceneId);
const sceneStashes = stashes.filter((stash) => stash.scene_id === sceneId); const sceneStashes = stashes.filter((stash) => stash.scene_id === sceneId);
@ -219,6 +232,7 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
series: sceneSeries, series: sceneSeries,
poster: scenePoster, poster: scenePoster,
photos: scenePhotos, photos: scenePhotos,
caps: sceneCaps,
trailer: sceneTrailers, trailer: sceneTrailers,
teaser: sceneTeasers, teaser: sceneTeasers,
stashes: sceneStashes, stashes: sceneStashes,