traxxx-web/src/tags.js

119 lines
3.5 KiB
JavaScript

import knex from './knex.js';
import redis from './redis.js';
import initLogger from './logger.js';
import { curateEntity } from './entities.js';
const logger = initLogger();
function curateTag(tag, context) {
return {
id: tag.id,
name: tag.name,
slug: tag.slug,
description: tag.description,
priority: tag.priority,
poster: tag.poster && {
id: tag.poster.id,
path: tag.poster.path,
thumbnail: tag.poster.thumbnail,
lazy: tag.poster.lazy,
isS3: tag.poster.is_s3,
comment: tag.poster.comment,
entity: tag.poster.entity && curateEntity({
...tag.poster.entity,
parent: tag.poster.entity_parent,
}),
},
...context?.append?.[tag.id],
};
}
export async function fetchTags(options = {}) {
const [tags, posters] = await Promise.all([
knex('tags')
.select('tags.*')
.modify((builder) => {
if (!options.includeAliases) {
builder.whereNull('alias_for');
}
if (options.query) {
builder
.where('name', 'like', `%${options.query}%`)
.orWhere('slug', 'like', `%${options.query}%`);
}
}),
knex('tags_posters')
.select('tags_posters.tag_id', 'media.*', knex.raw('row_to_json(entities) as entity'), knex.raw('row_to_json(parents) as entity_parent'))
.leftJoin('media', 'media.id', 'tags_posters.media_id')
.leftJoin('entities', 'entities.id', 'media.entity_id')
.leftJoin('entities as parents', 'entities.id', 'entities.parent_id'),
]);
const postersByTagId = Object.fromEntries(posters.map((poster) => [poster.tag_id, poster]));
return tags.map((tagEntry) => curateTag({
...tagEntry,
poster: postersByTagId[tagEntry.id],
}));
}
export async function fetchTagsById(tagIds, options = {}) {
const [tags, posters] = await Promise.all([
knex('tags')
.whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number'))
.orWhereIn('tags.slug', tagIds.filter((tagId) => typeof tagId === 'string'))
.modify((builder) => {
if (options.order) {
builder.orderBy(...options.order);
}
}),
knex('tags_posters')
.select('tags_posters.tag_id', 'media.*', knex.raw('row_to_json(entities) as entity'), knex.raw('row_to_json(parents) as entity_parent'))
.leftJoin('tags', 'tags.id', 'tags_posters.tag_id')
.leftJoin('media', 'media.id', 'tags_posters.media_id')
.leftJoin('entities', 'entities.id', 'media.entity_id')
.leftJoin('entities as parents', 'parents.id', 'entities.parent_id')
.whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number'))
.orWhereIn('tags.slug', tagIds.filter((tagId) => typeof tagId === 'string')),
]);
const postersByTagId = Object.fromEntries(posters.map((poster) => [poster.tag_id, poster]));
if (options.order) {
return tags.map((tagEntry) => curateTag({
...tagEntry,
poster: postersByTagId[tagEntry.id],
}, { append: options.append }));
}
const curatedTags = tagIds.map((tagId) => {
const tag = tags.find((tagEntry) => (typeof tagId === 'string' ? tagEntry.slug === tagId : tagEntry.id === tagId));
if (!tag) {
console.warn(`Can't match tag ${tagId}`);
return null;
}
return curateTag({
...tag,
poster: postersByTagId[tag.id],
}, { append: options.append });
}).filter(Boolean);
return curatedTags;
}
export async function cacheTagIds() {
const tags = await knex('tags')
.select('id', 'slug')
.whereNull('alias_for')
.whereNotNull('slug');
await redis.del('traxxx:tags:id_by_slug');
await redis.hSet('traxxx:tags:id_by_slug', tags.map((tag) => [tag.slug, tag.id]));
logger.info('Cached tag IDs by slug');
}