Added georestriction with SFW mode.

This commit is contained in:
2026-02-04 05:39:14 +01:00
parent ce107e6b65
commit 1a84f899e7
35 changed files with 777 additions and 112 deletions

View File

@@ -1,6 +1,7 @@
import config from 'config';
import { MerkleJson } from 'merkle-json';
import argv from './argv.js';
import { knexQuery as knex, knexOwner, knexManticore } from './knex.js';
import { utilsApi } from './manticore.js';
import { HttpError } from './errors.js';
@@ -15,18 +16,19 @@ import promiseProps from '../utils/promise-props.js';
import initLogger from './logger.js';
import { curateRevision } from './revisions.js';
import { getAffiliateSceneUrl } from './affiliates.js';
import { censor } from './censor.js';
const logger = initLogger();
const mj = new MerkleJson();
function curateScene(rawScene, assets, reqUser) {
function curateScene(rawScene, assets, reqUser, context) {
if (!rawScene) {
return null;
}
const curatedScene = {
id: rawScene.id,
title: rawScene.title,
title: censor(rawScene.title, context.restriction),
slug: rawScene.slug,
url: rawScene.url,
entryId: rawScene.entry_id,
@@ -34,14 +36,14 @@ function curateScene(rawScene, assets, reqUser) {
datePrecision: rawScene.date_precision,
createdAt: rawScene.created_at,
effectiveDate: rawScene.effective_date,
description: rawScene.description,
description: censor(rawScene.description, context.restriction),
duration: rawScene.duration,
shootId: rawScene.shoot_id,
productionDate: rawScene.production_date,
channel: {
id: assets.channel.id,
slug: assets.channel.slug,
name: assets.channel.name,
name: censor(assets.channel.name, context.restriction),
type: assets.channel.type,
isIndependent: assets.channel.independent,
hasLogo: assets.channel.has_logo,
@@ -49,7 +51,7 @@ function curateScene(rawScene, assets, reqUser) {
network: assets.channel.network_id ? {
id: assets.channel.network_id,
slug: assets.channel.network_slug,
name: assets.channel.network_name,
name: censor(assets.channel.network_name, context.restriction),
type: assets.channel.network_type,
hasLogo: assets.channel.network_has_logo,
} : null,
@@ -78,7 +80,7 @@ function curateScene(rawScene, assets, reqUser) {
tags: assets.tags.map((tag) => ({
id: tag.id,
slug: tag.slug,
name: tag.name,
name: censor(tag.name, context.restriction),
priority: tag.priority,
})),
chapters: assets.chapters.map((chapter) => ({
@@ -86,7 +88,9 @@ function curateScene(rawScene, assets, reqUser) {
title: chapter.title,
time: chapter.time,
duration: chapter.duration,
poster: curateMedia(chapter.chapter_poster),
poster: context.restriction
? null
: (chapter.chapter_poster),
tags: chapter.chapter_tags.map((tag) => ({
id: tag.id,
name: tag.name,
@@ -98,14 +102,18 @@ function curateScene(rawScene, assets, reqUser) {
movies: assets.movies.map((movie) => ({
id: movie.id,
slug: movie.slug,
title: movie.title,
covers: movie.movie_covers?.map((cover) => curateMedia(cover, { type: 'cover' })).toSorted((coverA, coverB) => coverA.index - coverB.index) || [],
title: censor(movie.title, context.restriction),
covers: movie.movie_covers && !context.restriction
? movie.movie_covers.map((cover) => curateMedia(cover, { type: 'cover' })).toSorted((coverA, coverB) => coverA.index - coverB.index)
: [],
})),
series: assets.series.map((serie) => ({
id: serie.id,
slug: serie.slug,
title: serie.title,
poster: curateMedia(serie.serie_poster, { type: 'poster' }),
poster: context.restriction
? null
: (serie.serie_poster, { type: 'poster' }),
})),
poster: curateMedia(assets.poster, { type: 'poster' }),
photos: assets.photos?.map((photo) => curateMedia(photo, { type: 'photo' })) || [],
@@ -189,14 +197,17 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
.select(
'actors.*',
knex.raw('row_to_json(avatars) as avatar'),
knex.raw('row_to_json(sfw_media) as sfw_avatar'),
'countries.name as birth_country_name',
'countries.alias as birth_country_alias',
'releases_actors.release_id',
)
.leftJoin('actors', 'actors.id', 'releases_actors.actor_id')
.leftJoin('media as avatars', 'avatars.id', 'actors.avatar_media_id')
.leftJoin('media as sfw_media', 'sfw_media.id', 'avatars.sfw_media_id')
.leftJoin('countries', 'countries.alpha2', 'actors.birth_country_alpha2')
.whereIn('release_id', sceneIds),
.whereIn('release_id', sceneIds)
.groupBy('actors.id', 'releases_actors.release_id', 'avatars.id', 'countries.name', 'countries.alias', 'sfw_media.id'),
directors: knex('releases_directors')
.whereIn('release_id', sceneIds)
.leftJoin('actors as directors', 'directors.id', 'releases_directors.director_id'),
@@ -234,17 +245,23 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
.whereIn('scene_id', sceneIds)
.groupBy('series.id', 'series_scenes.scene_id', 'media.*') : [],
posters: knex('releases_posters')
.select('media.*', 'releases_posters.release_id', knex.raw('row_to_json(sfw_media) as sfw_media'))
.whereIn('release_id', sceneIds)
.leftJoin('media', 'media.id', 'releases_posters.media_id'),
photos: context.includeAssets ? knex.transaction(async (trx) => {
.leftJoin('media', 'media.id', 'releases_posters.media_id')
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
.groupBy('media.id', 'releases_posters.release_id', 'sfw_media.id'),
photos: context.includeAssets && !context.restriction ? knex.transaction(async (trx) => {
if (reqUser) {
await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id }));
}
return trx('releases_photos')
.select('media.*', 'releases_photos.release_id', knex.raw('row_to_json(sfw_media) as sfw_media'))
.leftJoin('media', 'media.id', 'releases_photos.media_id')
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
.whereIn('release_id', sceneIds)
.orderBy('index');
.orderBy('index')
.groupBy('media.id', 'releases_photos.release_id', 'sfw_media.id');
}) : [],
caps: context.includeAssets ? knex.transaction(async (trx) => {
if (reqUser) {
@@ -252,9 +269,12 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
}
return trx('releases_caps')
.select('media.*', 'releases_caps.release_id', knex.raw('row_to_json(sfw_media) as sfw_media'))
.leftJoin('media', 'media.id', 'releases_caps.media_id')
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
.whereIn('release_id', sceneIds)
.orderBy('index');
.orderBy('index')
.groupBy('media.id', 'releases_caps.release_id', 'sfw_media.id');
}) : [],
trailers: context.includeAssets ? knex.transaction(async (trx) => {
if (reqUser) {
@@ -350,7 +370,7 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
stashes: sceneStashes,
actorStashes: sceneActorStashes,
lastBatchId: lastBatch?.id,
}, reqUser);
}, reqUser, context);
}).filter(Boolean);
}
@@ -545,7 +565,7 @@ async function queryManticoreSql(filters, options, _reqUser) {
? sqlQuery
: sqlQuery.replace(/scenes\./g, '');
if (process.env.NODE_ENV === 'development') {
if (process.env.NODE_ENV === 'development' && argv.debug) {
console.log(curatedSqlQuery);
}
@@ -603,14 +623,18 @@ function countAggregations(buckets) {
return Object.fromEntries(buckets.map((bucket) => [bucket.key, { count: bucket.doc_count }]));
}
export async function fetchScenes(filters, rawOptions, reqUser) {
export async function fetchScenes(filters, rawOptions, reqUser, context) {
const options = curateOptions(rawOptions);
console.log('filters', filters);
console.log('options', options);
if (argv.debug) {
console.log('filters', filters);
console.log('options', options);
}
console.time('manticore sql');
const result = await queryManticoreSql(filters, options, reqUser);
console.timeEnd('manticore sql');
const aggYears = options.aggregateYears && result.aggregations.years.map((bucket) => ({ year: bucket.key, count: bucket.doc_count }));
@@ -623,16 +647,16 @@ export async function fetchScenes(filters, rawOptions, reqUser) {
console.time('fetch aggregations');
const [aggActors, aggTags, aggChannels] = await Promise.all([
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.aggregateChannels ? fetchEntitiesById(entityIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: channelCounts }) : [],
options.aggregateActors ? fetchActorsById(result.aggregations.actorIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: actorCounts }, reqUser) : [],
options.aggregateTags ? fetchTagsById(result.aggregations.tagIds.map((bucket) => bucket.key), { order: [knex.raw('lower(name)'), 'asc'], append: tagCounts }, reqUser, context) : [],
options.aggregateChannels ? fetchEntitiesById(entityIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: channelCounts }, reqUser, context) : [],
]);
console.timeEnd('fetch aggregations');
console.time('fetch full');
const sceneIds = result.scenes.map((scene) => Number(scene.id));
const scenes = await fetchScenesById(sceneIds, { reqUser });
const scenes = await fetchScenesById(sceneIds, { reqUser, ...context });
console.timeEnd('fetch full');
return {