'use strict'; const logger = require('./logger')(__filename); const knex = require('./knex'); const whereOr = require('./utils/where-or'); async function curateTag(tag) { const [aliases, media] = await Promise.all([ knex('tags').where({ alias_for: tag.id }), knex('media') .where('domain', 'tags') .andWhere('target_id', tag.id) .orderBy('index'), ]); return { id: tag.id, name: tag.name, slug: tag.slug, description: tag.description, poster: media.find(photo => photo.role === 'poster'), photos: media.filter(photo => photo.role === 'photo'), group: { id: tag.group_id, name: tag.group_name, description: tag.group_description, slug: tag.group_slug, }, aliases: aliases.map(({ name }) => name), }; } function curateTags(tags) { return Promise.all(tags.map(async tag => curateTag(tag))); } async function matchTags(rawTags) { const filteredTags = rawTags.filter(Boolean); const tags = filteredTags .concat(filteredTags.map(tag => tag.toLowerCase())) .concat(filteredTags.map(tag => tag.toUpperCase())); const tagEntries = await knex('tags') .pluck('aliases.id') .whereIn('tags.name', tags) .leftJoin('tags as aliases', function join() { this .on('tags.alias_for', 'aliases.id') .orOn('tags.id', 'aliases.id'); }) .where(function where() { this .whereNull('tags.alias_for') .orWhereNull('aliases.alias_for'); }) .groupBy('aliases.id'); return tagEntries; } async function associateTags(release, releaseId) { const siteTags = release.site?.tags?.filter(tag => tag.inherit === true).map(tag => tag.id) || []; const rawReleaseTags = release.tags?.filter(Boolean) || []; const releaseTags = rawReleaseTags.some(tag => typeof tag === 'string') ? await matchTags(release.tags) // scraper returned raw tags : rawReleaseTags; // tags already matched by (outdated) scraper const tags = Array.from(new Set(releaseTags.concat(siteTags))); if (tags.length === 0) { logger.info(`No tags available for (${release.site.name}, ${releaseId}) "${release.title}"`); return; } const associationEntries = await knex('releases_tags') .where('release_id', releaseId) .whereIn('tag_id', tags); const existingAssociations = new Set(associationEntries.map(association => association.tag_id)); const newAssociations = tags.filter(tagId => !existingAssociations.has(tagId)); await knex('releases_tags').insert(newAssociations.map(tagId => ({ tag_id: tagId, release_id: releaseId, }))); } async function fetchTags(queryObject, groupsQueryObject, limit = 100) { const tags = await knex('tags') .where(builder => whereOr(queryObject, 'tags', builder)) .orWhere(builder => whereOr(groupsQueryObject, 'tags_groups', builder)) .andWhere({ 'tags.alias_for': null }) .select( 'tags.*', 'tags_groups.id as group_id', 'tags_groups.name as group_name', 'tags_groups.slug as group_slug', 'tags_groups.description as groups_description', ) .leftJoin('tags_groups', 'tags.group_id', 'tags_groups.id') .orderBy('name') .limit(limit); return curateTags(tags); } module.exports = { associateTags, fetchTags, matchTags, };