import { createHash } from 'crypto' import { logger } from '../../helpers/logger' import { client } from '../../helpers/meilisearch' import { CONFIG } from '../../initializers/constants' import { DBChannel, APIVideoChannel, IndexableChannel } from '../../types/channel.model' import { ChannelsSearchQuery } from '../../types/search-query/channel-search.model' import { buildInValuesArray, buildSort, extractSearchQueryResult } from './meilisearch-queries' import { formatActorImageForAPI, formatActorImageForDB, formatActorImagesForAPI, formatActorImagesForDB } from './shared/meilisearch-avatar' export async function queryChannels (search: ChannelsSearchQuery) { const filter: string[] = [ 'videosCount != 0' ] if (search.host) filter.push(`host = '${search.host}'`) if (search.blockedAccounts) filter.push(`ownerAccount.handle NOT IN ${buildInValuesArray(search.blockedAccounts)}`) if (search.handles) filter.push(`handle IN ${buildInValuesArray(search.handles)}`) if (search.blockedHosts) filter.push(`host NOT IN ${buildInValuesArray(search.blockedHosts)}`) logger.debug({ filter }, 'Will query Meilisearch for channels.') const result = await client.index(CONFIG.MEILISEARCH.INDEXES.CHANNELS).search(search.search, { offset: search.start, limit: search.count, sort: buildSort(search.sort), showRankingScore: true, filter }) return extractSearchQueryResult(result) } export function formatChannelForAPI (c: DBChannel, fromHost?: string): APIVideoChannel { return { id: c.id, score: c._rankingScore, url: c.url, name: c.name, host: c.host, followingCount: c.followingCount, followersCount: c.followersCount, createdAt: new Date(c.createdAt), updatedAt: new Date(c.updatedAt), avatar: formatActorImageForAPI(c.avatar), avatars: formatActorImagesForAPI(c.avatars, c.avatar), banner: formatActorImageForAPI(c.banner), banners: formatActorImagesForAPI(c.banners, c.banner), displayName: c.displayName, description: c.description, support: c.support, isLocal: fromHost === c.host, videosCount: c.videosCount || 0, ownerAccount: { id: c.ownerAccount.id, url: c.ownerAccount.url, displayName: c.ownerAccount.displayName, description: c.ownerAccount.description, name: c.ownerAccount.name, host: c.ownerAccount.host, followingCount: c.ownerAccount.followingCount, followersCount: c.ownerAccount.followersCount, createdAt: new Date(c.ownerAccount.createdAt), updatedAt: new Date(c.ownerAccount.updatedAt), avatar: formatActorImageForAPI(c.ownerAccount.avatar), avatars: formatActorImagesForAPI(c.ownerAccount.avatars, c.ownerAccount.avatar) } } } export function formatChannelForDB (c: IndexableChannel): DBChannel { return { primaryKey: buildDBChannelPrimaryKey(c), id: c.id, name: c.name, host: c.host, url: c.url, avatar: formatActorImageForDB(c.avatar, c.host), avatars: formatActorImagesForDB(c.avatars, c.host), banner: formatActorImageForDB(c.banner, c.host), banners: formatActorImagesForDB(c.banners, c.host), displayName: c.displayName, indexedAt: new Date().getTime(), createdAt: new Date(c.createdAt).getTime(), updatedAt: new Date(c.updatedAt).getTime(), followingCount: c.followingCount, followersCount: c.followersCount, description: c.description, support: c.support, videosCount: c.videosCount, handle: `${c.name}@${c.host}`, ownerAccount: { id: c.ownerAccount.id, url: c.ownerAccount.url, displayName: c.ownerAccount.displayName, description: c.ownerAccount.description, name: c.ownerAccount.name, host: c.ownerAccount.host, followingCount: c.ownerAccount.followingCount, followersCount: c.ownerAccount.followersCount, createdAt: new Date(c.ownerAccount.createdAt).getTime(), updatedAt: new Date(c.ownerAccount.updatedAt).getTime(), handle: `${c.ownerAccount.name}@${c.ownerAccount.host}`, avatar: formatActorImageForDB(c.ownerAccount.avatar, c.ownerAccount.host), avatars: formatActorImagesForDB(c.ownerAccount.avatars, c.ownerAccount.host) } } } export function buildDBChannelPrimaryKey (c: { id: number, host: string }) { return `${c.id}_${md5(c.host)}` } // Collisions are fine, we just want to generate a primary key in an efficient way function md5 (value: string) { return createHash('md5').update(value).digest('hex') }