Added studios to filters and scene page.

This commit is contained in:
DebaucheryLibrarian 2024-09-03 05:56:14 +02:00
parent c3362e614e
commit 60c7e2a876
12 changed files with 108 additions and 19 deletions

View File

@ -63,7 +63,8 @@
--text: #222; --text: #222;
--text-light: #fff; --text-light: #fff;
--link: #48f; /* --link: #48f; */
--link: var(--primary);
--male: #0af; --male: #0af;
--female: #f0a; --female: #f0a;

View File

@ -131,14 +131,14 @@ const entities = computed(() => {
return acc; return acc;
} }
if (channel.parent && !acc[channel.parent.id] && channel.type === 'channel') { if (!acc[channel.id] && channel.parent && !acc[channel.parent.id] && (channel.type === 'channel' || channel.type === 'studio')) {
acc[channel.parent.id] = { acc[channel.parent.id] = {
...channel.parent, ...channel.parent,
children: [], children: [],
}; };
} }
if (channel.parent && channel.type === 'channel') { if (!acc[channel.id] && channel.parent && (channel.type === 'channel' || channel.type === 'studio')) {
acc[channel.parent.id].children.push(channel); acc[channel.parent.id].children.push(channel);
} }

View File

@ -160,6 +160,7 @@ import { parse } from 'path-to-regexp';
import navigate from '#/src/navigate.js'; import navigate from '#/src/navigate.js';
import { get } from '#/src/api.js'; import { get } from '#/src/api.js';
import events from '#/src/events.js'; import events from '#/src/events.js';
import entityPrefixes from '#/src/entities-prefixes.js';
import { getActorIdentifier, parseActorIdentifier } from '#/src/query.js'; import { getActorIdentifier, parseActorIdentifier } from '#/src/query.js';
import Filters from '#/components/filters/filters.vue'; import Filters from '#/components/filters/filters.vue';
@ -211,6 +212,8 @@ const aggActors = ref(pageProps.aggActors || []);
const aggTags = ref(pageProps.aggTags || []); const aggTags = ref(pageProps.aggTags || []);
const aggChannels = ref(pageProps.aggChannels || []); const aggChannels = ref(pageProps.aggChannels || []);
console.log(aggChannels.value);
const currentPage = ref(Number(routeParams.page)); const currentPage = ref(Number(routeParams.page));
const scope = ref(routeParams.scope || props.defaultScope); const scope = ref(routeParams.scope || props.defaultScope);
const total = ref(Number(pageProps.sceneTotal || pageProps.total)); const total = ref(Number(pageProps.sceneTotal || pageProps.total));
@ -277,7 +280,8 @@ async function search(options = {}) {
}; };
const entity = filters.value.entity || pageEntity; const entity = filters.value.entity || pageEntity;
const entitySlug = entity?.type === 'network' ? `_${entity.slug}` : entity?.slug; // const entitySlug = entity?.type === 'network' ? `_${entity.slug}` : entity?.slug;
const entitySlug = `${entityPrefixes[entity.type]}${entity.slug}`;
loading.value = true; loading.value = true;
@ -286,7 +290,8 @@ async function search(options = {}) {
years: filters.value.years.join(',') || undefined, years: filters.value.years.join(',') || undefined,
actors: filters.value.actors.map((filterActor) => getActorIdentifier(filterActor)).join(',') || undefined, // don't include page actor ID in query, already a parameter actors: filters.value.actors.map((filterActor) => getActorIdentifier(filterActor)).join(',') || undefined, // don't include page actor ID in query, already a parameter
tags: filters.value.tags.join(',') || undefined, tags: filters.value.tags.join(',') || undefined,
e: filters.value.entity?.type === 'network' ? `_${filters.value.entity.slug}` : (filters.value.entity?.slug || undefined), // e: filters.value.entity?.type === 'network' ? `_${filters.value.entity.slug}` : (filters.value.entity?.slug || undefined),
e: filters.value.entity ? `${entityPrefixes[filters.value.entity.type]}${filters.value.entity.slug}` : undefined,
}, { redirect: false }); }, { redirect: false });
const res = await get('/scenes', { const res = await get('/scenes', {

View File

@ -109,6 +109,16 @@
class="row tags nolist" class="row tags nolist"
:title="scene.tags.map((tag) => tag.name).join(', ')" :title="scene.tags.map((tag) => tag.name).join(', ')"
> >
<li
v-if="scene.shootId"
class="tag shoot"
>
<Link
:href="scene.studio ? `/studio/${scene.studio.slug}` : null"
class="nolink"
>{{ scene.shootId }}</Link>
</li>
<li <li
v-for="tag in scene.tags" v-for="tag in scene.tags"
:key="`tag-${scene.id}-${tag.id}`" :key="`tag-${scene.id}-${tag.id}`"
@ -304,4 +314,10 @@ const favorited = ref(props.scene.stashes.some((sceneStash) => sceneStash.id ===
color: var(--glass-strong-10); color: var(--glass-strong-10);
font-size: .75rem; font-size: .75rem;
} }
.shoot {
color: var(--primary);
font-size: .75rem;
font-weight: bold;
}
</style> </style>

View File

@ -76,6 +76,19 @@
/> />
<div class="children-container"> <div class="children-container">
<div
v-show="expanded"
class="expand-container expand-top"
>
<button
class="expand"
@click="expanded = !expanded"
>
<span class="expand-text">Collapse channels</span>
<Icon icon="arrow-up3" />
</button>
</div>
<ul <ul
v-if="entity.children.length > 0" v-if="entity.children.length > 0"
ref="children" ref="children"
@ -252,7 +265,7 @@ const entityUrl = (() => {
align-items: center; align-items: center;
padding: .5rem; padding: .5rem;
border: none; border: none;
background: var(--grey-dark-50); background: var(--grey-dark-40);
color: var(--highlight-strong-30); color: var(--highlight-strong-30);
font-size: .9rem; font-size: .9rem;
font-weight: bold; font-weight: bold;
@ -274,6 +287,17 @@ const entityUrl = (() => {
} }
} }
.expand-top {
height: 0;
top: 3.5rem;
bottom: auto;
position: sticky;
.expand {
height: 2rem;
}
}
.domains-bar { .domains-bar {
display: none; display: none;
} }

View File

@ -1,6 +1,7 @@
import { render } from 'vike/abort'; /* eslint-disable-line import/extensions */ import { render } from 'vike/abort'; /* eslint-disable-line import/extensions */
import { fetchEntitiesById } from '#/src/entities.js'; import { fetchEntitiesById } from '#/src/entities.js';
import entityPrefixes from '#/src/entities-prefixes.js';
import { fetchScenes } from '#/src/scenes.js'; import { fetchScenes } from '#/src/scenes.js';
import { fetchMovies } from '#/src/movies.js'; import { fetchMovies } from '#/src/movies.js';
import { curateScenesQuery } from '#/src/web/scenes.js'; import { curateScenesQuery } from '#/src/web/scenes.js';
@ -35,7 +36,8 @@ async function fetchReleases(pageContext, entityId) {
} }
export async function onBeforeRender(pageContext) { export async function onBeforeRender(pageContext) {
const entityId = await redis.hGet('traxxx:entities:id_by_slug', pageContext.routeParams.entityType === 'network' ? `_${pageContext.routeParams.entitySlug}` : pageContext.routeParams.entitySlug); // const entityId = await redis.hGet('traxxx:entities:id_by_slug', pageContext.routeParams.entityType === 'network' ? `_${pageContext.routeParams.entitySlug}` : pageContext.routeParams.entitySlug);
const entityId = await redis.hGet('traxxx:entities:id_by_slug', `${entityPrefixes[pageContext.routeParams.entityType]}${pageContext.routeParams.entitySlug}`);
if (!entityId) { if (!entityId) {
throw render(404, `Cannot find ${pageContext.routeParams.entityType} '${pageContext.routeParams.entitySlug}'.`); throw render(404, `Cannot find ${pageContext.routeParams.entityType} '${pageContext.routeParams.entitySlug}'.`);

View File

@ -1,7 +1,7 @@
import { match } from 'path-to-regexp'; import { match } from 'path-to-regexp';
// import { resolveRoute } from 'vike/routing'; // eslint-disable-line import/extensions // import { resolveRoute } from 'vike/routing'; // eslint-disable-line import/extensions
const path = '/:entityType(channel|network)/:entitySlug/:domain(scenes|movies|series)?/:scope?/:page?'; const path = '/:entityType(channel|network|studio)/:entitySlug/:domain(scenes|movies|series)?/:scope?/:page?';
const urlMatch = match(path, { decode: decodeURIComponent }); const urlMatch = match(path, { decode: decodeURIComponent });
export default (pageContext) => { export default (pageContext) => {

View File

@ -205,6 +205,18 @@
{{ scene.shootId }} {{ scene.shootId }}
</div> </div>
<div
v-if="scene.studio"
class="detail"
>
<h3 class="heading">Studio</h3>
<a
:href="`/studio/${scene.studio.slug}`"
class="link"
>{{ scene.studio.name }}</a>
</div>
<div <div
v-if="scene.qualities.length > 0" v-if="scene.qualities.length > 0"
class="detail" class="detail"

6
src/entities-prefixes.js Normal file
View File

@ -0,0 +1,6 @@
export default {
channel: '',
network: '_',
studio: '*',
info: '@',
};

View File

@ -1,16 +1,10 @@
import knex from './knex.js'; import knex from './knex.js';
import redis from './redis.js'; import redis from './redis.js';
import initLogger from './logger.js'; import initLogger from './logger.js';
import entityPrefixes from './entities-prefixes.js';
const logger = initLogger(); const logger = initLogger();
const entityPrefixes = {
channel: '',
network: '_',
studio: '*',
info: '@',
};
export function curateEntity(entity, context) { export function curateEntity(entity, context) {
if (!entity) { if (!entity) {
return null; return null;

View File

@ -79,6 +79,13 @@ function curateScene(rawScene, assets) {
type: assets.channel.network_type, type: assets.channel.network_type,
hasLogo: assets.channel.network_has_logo, hasLogo: assets.channel.network_has_logo,
} : null, } : null,
studio: assets.studio ? {
id: assets.studio.id,
slug: assets.studio.slug,
name: assets.studio.name,
type: assets.studio.type,
hasLogo: assets.studio.has_logo,
} : null,
affiliate: assets.channel.affiliate ? { affiliate: assets.channel.affiliate ? {
id: assets.channel.affiliate.id, id: assets.channel.affiliate.id,
url: assets.channel.affiliate.url, url: assets.channel.affiliate.url,
@ -132,6 +139,7 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
const { const {
scenes, scenes,
channels, channels,
studios,
actors, actors,
directors, directors,
tags, tags,
@ -161,6 +169,10 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
.leftJoin('entities as networks', 'networks.id', 'channels.parent_id') .leftJoin('entities as networks', 'networks.id', 'channels.parent_id')
.leftJoin('affiliates', knex.raw('affiliates.entity_id in (channels.id, networks.id)')) .leftJoin('affiliates', knex.raw('affiliates.entity_id in (channels.id, networks.id)'))
.groupBy('channels.id', 'networks.id', 'affiliates.id'), .groupBy('channels.id', 'networks.id', 'affiliates.id'),
studios: knex('releases')
.whereIn('releases.id', sceneIds)
.leftJoin('entities as studios', 'studios.id', 'releases.studio_id'),
// .leftJoin('entities as networks', 'networks.id', 'studios.parent_id'),
actors: knex('releases_actors') actors: knex('releases_actors')
.select( .select(
'actors.*', 'actors.*',
@ -265,6 +277,7 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
} }
const sceneChannel = channels.find((entity) => entity.id === scene.entity_id); const sceneChannel = channels.find((entity) => entity.id === scene.entity_id);
const sceneStudio = studios.find((entity) => entity.id === scene.studio_id);
const sceneActors = actors.filter((actor) => actor.release_id === sceneId); const sceneActors = actors.filter((actor) => actor.release_id === sceneId);
const sceneDirectors = directors.filter((director) => director.release_id === sceneId); const sceneDirectors = directors.filter((director) => director.release_id === sceneId);
const sceneTags = tags.filter((tag) => tag.release_id === sceneId); const sceneTags = tags.filter((tag) => tag.release_id === sceneId);
@ -280,6 +293,7 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
return curateScene(scene, { return curateScene(scene, {
channel: sceneChannel, channel: sceneChannel,
studio: sceneStudio,
actors: sceneActors, actors: sceneActors,
directors: sceneDirectors, directors: sceneDirectors,
tags: sceneTags, tags: sceneTags,
@ -339,7 +353,8 @@ async function queryManticoreSql(filters, options, _reqUser) {
:yearsFacet: :yearsFacet:
:actorsFacet: :actorsFacet:
:tagsFacet: :tagsFacet:
:channelsFacet:; :channelsFacet:
:studiosFacet:;
show meta; show meta;
`, { `, {
query: knexManticore(filters.stashId ? 'scenes_stashed' : 'scenes') query: knexManticore(filters.stashId ? 'scenes_stashed' : 'scenes')
@ -470,6 +485,7 @@ async function queryManticoreSql(filters, options, _reqUser) {
actorsFacet: options.aggregateActors ? knex.raw('facet scenes.actor_ids order by count(*) desc limit ?', [aggSize]) : null, actorsFacet: options.aggregateActors ? knex.raw('facet scenes.actor_ids order by count(*) desc limit ?', [aggSize]) : null,
tagsFacet: options.aggregateTags ? knex.raw('facet scenes.tag_ids order by count(*) desc limit ?', [aggSize]) : null, tagsFacet: options.aggregateTags ? knex.raw('facet scenes.tag_ids order by count(*) desc limit ?', [aggSize]) : null,
channelsFacet: options.aggregateChannels ? knex.raw('facet scenes.channel_id order by count(*) desc limit ?', [aggSize]) : null, channelsFacet: options.aggregateChannels ? knex.raw('facet scenes.channel_id order by count(*) desc limit ?', [aggSize]) : null,
studiosFacet: options.aggregateChannels ? knex.raw('facet scenes.studio_id order by count(*) desc limit ?', [aggSize]) : null,
maxMatches: config.database.manticore.maxMatches, maxMatches: config.database.manticore.maxMatches,
maxQueryTime: config.database.manticore.maxQueryTime, maxQueryTime: config.database.manticore.maxQueryTime,
}).toString(); }).toString();
@ -507,6 +523,13 @@ async function queryManticoreSql(filters, options, _reqUser) {
?.data.map((row) => ({ key: row.channel_id || row['scenes.channel_id'], doc_count: row['count(*)'] })) ?.data.map((row) => ({ key: row.channel_id || row['scenes.channel_id'], doc_count: row['count(*)'] }))
|| []; || [];
const studioIds = results
.find((result) => (result.columns[0].studio_id || result.columns[0]['scenes.studio_id']) && result.columns[1]['count(*)'])
?.data
.map((row) => ({ key: row.studio_id || row['scenes.studio_id'], doc_count: row['count(*)'] }))
.filter((row) => !!row.key)
|| [];
const total = Number(results.at(-1).data.find((entry) => entry.Variable_name === 'total_found')?.Value) || 0; const total = Number(results.at(-1).data.find((entry) => entry.Variable_name === 'total_found')?.Value) || 0;
return { return {
@ -517,6 +540,7 @@ async function queryManticoreSql(filters, options, _reqUser) {
actorIds, actorIds,
tagIds, tagIds,
channelIds, channelIds,
studioIds,
}, },
}; };
} }
@ -540,19 +564,24 @@ export async function fetchScenes(filters, rawOptions, reqUser) {
console.timeEnd('manticore sql'); console.timeEnd('manticore sql');
const aggYears = options.aggregateYears && result.aggregations.years.map((bucket) => ({ year: bucket.key, count: bucket.doc_count })); const aggYears = options.aggregateYears && result.aggregations.years.map((bucket) => ({ year: bucket.key, count: bucket.doc_count }));
const entityIds = options.aggregateChannels && [...(result.aggregations.channelIds || []), ...(result.aggregations.studioIds || [])];
const actorCounts = options.aggregateActors && countAggregations(result.aggregations?.actorIds); const actorCounts = options.aggregateActors && countAggregations(result.aggregations?.actorIds);
const tagCounts = options.aggregateTags && countAggregations(result.aggregations?.tagIds); const tagCounts = options.aggregateTags && countAggregations(result.aggregations?.tagIds);
const channelCounts = options.aggregateChannels && countAggregations(result.aggregations?.channelIds); const channelCounts = options.aggregateChannels && countAggregations(entityIds);
console.log('entity ids', entityIds);
console.time('fetch aggregations'); console.time('fetch aggregations');
const [aggActors, aggTags, aggChannels] = await Promise.all([ const [aggActors, aggTags, aggChannels] = await Promise.all([
options.aggregateActors ? fetchActorsById(result.aggregations.actorIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: actorCounts }) : [], options.aggregateActors ? fetchActorsById(result.aggregations.actorIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: actorCounts }) : [],
options.aggregateTags ? fetchTagsById(result.aggregations.tagIds.map((bucket) => bucket.key), { order: [knex.raw('lower(name)'), 'asc'], append: tagCounts }) : [], options.aggregateTags ? fetchTagsById(result.aggregations.tagIds.map((bucket) => bucket.key), { order: [knex.raw('lower(name)'), 'asc'], append: tagCounts }) : [],
options.aggregateChannels ? fetchEntitiesById(result.aggregations.channelIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: channelCounts }) : [], options.aggregateChannels ? fetchEntitiesById(entityIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: channelCounts }) : [],
]); ]);
console.log('studio ids', aggChannels.filter((studio) => studio.slug === 'wgcz'));
console.timeEnd('fetch aggregations'); console.timeEnd('fetch aggregations');
console.time('fetch full'); console.time('fetch full');

2
static

@ -1 +1 @@
Subproject commit 5e1b2190f0d580321a310c1c1fc95ede8f33473b Subproject commit 333d2604c8987548786434c78162118f50c94bc8