forked from DebaucheryLibrarian/traxxx
111 lines
3.5 KiB
JavaScript
111 lines
3.5 KiB
JavaScript
|
'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,
|
||
|
};
|