Normalização de Géneros e Categorias
Visão Geral
Sistema de normalização de géneros e categorias para garantir dados limpos e coerentes para IA/ML. Converte automaticamente tags e categorias diversas em géneros normalizados consistentes.
Motivação
Problema
- Inconsistência: Mesmo conteúdo com tags diferentes (
EDM,Eletrónica,Dance) - Ruído: Dados não estruturados dificultam ML e recomendações
- Multilíngue: Conteúdos em PT, EN, ES com termos diferentes
Solução
- 38 géneros normalizados bem definidos
- ~300+ variantes mapeadas para géneros normalizados
- Processamento automático em toda a ingestão de conteúdos
Géneros Normalizados
📚 Tabela de Referência (38 Géneros)
Notícias e Informação
news- Notícias e atualidadetalk- Conversas, entrevistas, debates
Música (11 géneros)
pop- Pop e mainstreamrock- Rock, metal, alternativoelectronic- Electrónica, dance, EDMclassical- Clássica, ópera, sinfónicajazz- Jazz, blues, soul, funkfolk- Folk, country, acústicoworld- Músicas do mundo, fado, étnicahip-hop- Hip hop, rap, traprnb- R&B, neo soulreggae- Reggae, ska, dublatin- Latina, salsa, reggaeton, bossa nova
Desporto e Entretenimento
sports- Desporto em geralcomedy- Comédia e humorentertainment- Entretenimento geralgaming- Gaming e esports
Cultura e Educação
culture- Cultura em geraleducation- Educação e aprendizagemscience- Ciênciastechnology- Tecnologia e inovaçãohistory- Históriaarts- Artes visuaisliterature- Literatura e livros
Lifestyle
health- Saúde e bem-estarbusiness- Negócios e economiafood- Alimentação e culináriatravel- Viagens e turismofashion- Moda e beleza
Outros
religion- Religião e espiritualidadekids- Infantil e famíliadocumentary- Documentáriosdrama- Drama e ficçãotrue-crime- True crime e investigaçãopolitics- Políticasociety- Sociedadeother- Outros (fallback)
Mapeamentos
Exemplos de Variantes → Normalizado
Música Electrónica
EDM, Eletrónica, Eletronica, Dance, Techno, House, Trance
→ electronic
Hip Hop
Hip Hop, Hip-Hop, Rap, Trap, Underground
→ hip-hop
Notícias
News, Notícias, Information, Informação, Journalism, Atualidade
→ news
Desporto
Sports, Desporto, Futebol, Football, Soccer, F1, Ciclismo
→ sports
Mapeamento Completo
Ver ficheiro de referência: genre-mappings.ts
Arquitetura
Componentes
┌─────────────────────────────────────────────────┐
│ Ingestão de Conteúdos │
│ (Rádios, Podcasts, Episódios, Programas) │
└──────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ ContentNormalizationService │
│ │
│ • normalizeGenres(tags[]) │
│ • normalizeLanguage(lang) │
│ • normalizeCountry(country) │
└──────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ Ficheiro de Referência │
│ genre-mappings.ts │
│ │
│ • 38 géneros normalizados │
│ • ~300+ variantes mapeadas │
│ • Suporte PT, EN, ES │
└──────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ ContentFeatures │
│ (Dados Normalizados para ML) │
│ │
│ genres: NormalizedGenre[] │
│ language: string (ISO 639-1) │
│ country: string (ISO 3166-1) │
└─────────────────────────────────────────────────┘
Fluxo de Processamento
// 1. Conteúdo é ingerido com tags brutas
const podcast = {
title: 'Tech Talks PT',
categories: ['Tecnologia', 'Digital', 'Innovation']
};
// 2. Normalização automática
const genres = contentNormalizationService.normalizeGenres(
podcast.categories
);
// Result: [NormalizedGenre.TECHNOLOGY]
// 3. Armazenamento em ContentFeatures
await contentFeaturesService.upsertPodcastFeatures({
...podcast,
// genres é automaticamente normalizado
});
Uso
1. Todos os Conteúdos Passam pelo Normalizador
O serviço ContentFeaturesService automaticamente normaliza géneros em todas as operações:
// Rádio
await contentFeaturesService.upsertRadioFeatures({
tags: ['Pop', 'Top 40', 'Hits']
// → normalizado para [NormalizedGenre.MUSIC_POP]
});
// Podcast
await contentFeaturesService.upsertPodcastFeatures({
categories: ['True Crime', 'Investigation']
// → normalizado para [NormalizedGenre.TRUE_CRIME]
});
// Episódio herda géneros do podcast pai
await contentFeaturesService.upsertEpisodeFeatures({
podcastId: podcast._id
// → herda géneros normalizados do podcast
});
2. Normalização Manual (se necessário)
import { ContentNormalizationService } from '@app/common/services';
// Normalizar géneros
const genres = normalizationService.normalizeGenres([
'EDM', 'Dance', 'Eletrónica'
]);
// Result: [NormalizedGenre.MUSIC_ELECTRONIC]
// Normalizar idioma
const language = normalizationService.normalizeLanguage('português');
// Result: 'pt'
// Normalizar país
const country = normalizationService.normalizeCountry('Portugal');
// Result: 'PT'
3. Consulta por Géneros Normalizados
// Buscar conteúdos por género normalizado
const electronicContent = await contentFeaturesModel.find({
genres: NormalizedGenre.MUSIC_ELECTRONIC
});
// Buscar múltiplos géneros
const musicContent = await contentFeaturesModel.find({
genres: {
$in: [
NormalizedGenre.MUSIC_POP,
NormalizedGenre.MUSIC_ROCK,
NormalizedGenre.MUSIC_ELECTRONIC
]
}
});
Integração com Ingestão
População Inicial
# Executar migration para normalizar conteúdos existentes
npm run migrate:content-features
Este script:
- ✅ Processa todas as rádios ativas
- ✅ Processa todos os podcasts ativos
- ✅ Processa episódios (herdam géneros dos podcasts)
- ✅ Normaliza géneros, idiomas e países
- ✅ Cria/atualiza ContentFeatures
Integração Contínua
Os serviços de sincronização já integram normalização:
// sync-radios.processor.ts
for (const radio of newRadios) {
await this.contentFeaturesService.upsertRadioFeatures({
// ... dados da rádio
// tags são automaticamente normalizadas
});
}
// sync-podcasts.processor.ts
for (const podcast of updatedPodcasts) {
await this.contentFeaturesService.upsertPodcastFeatures({
// ... dados do podcast
// categories são automaticamente normalizadas
});
}
Adição de Novos Géneros
1. Adicionar ao Enum
// content-features.schema.ts
export enum NormalizedGenre {
// ... existentes
NEW_GENRE = 'new-genre',
}
2. Adicionar Mapeamentos
// genre-mappings.ts
export const GENRE_MAPPINGS: Record<string, NormalizedGenre> = {
// ... existentes
'variant 1': NormalizedGenre.NEW_GENRE,
'variant 2': NormalizedGenre.NEW_GENRE,
'variante pt': NormalizedGenre.NEW_GENRE,
};
3. Re-executar Migration
# Re-processar conteúdos existentes
npm run migrate:content-features
Validação e Testes
Verificar Normalização
// Testar normalização de uma tag
const genres = normalizationService.normalizeGenres(['EDM']);
console.log(genres); // [NormalizedGenre.MUSIC_ELECTRONIC]
// Verificar tags não mapeadas
const unknownGenres = normalizationService.normalizeGenres(['unknown-tag']);
console.log(unknownGenres); // [NormalizedGenre.OTHER]
Estatísticas de Géneros
# Ver distribuição de géneros
curl http://localhost:3333/api/admin/content-features/stats
Resposta:
{
"byGenre": {
"electronic": 150,
"news": 89,
"pop": 75,
...
}
}
Boas Práticas
✅ Fazer
- Sempre usar géneros normalizados em queries e filtros
- Adicionar variantes ao ficheiro de mapeamentos quando necessário
- Re-executar migration após adicionar novos géneros
- Verificar estatísticas regularmente para detetar tags não mapeadas
❌ Evitar
- ❌ Usar tags brutas em queries
- ❌ Hardcode de strings em vez de enum
- ❌ Ignorar validação de géneros normalizados
- ❌ Criar géneros ad-hoc sem adicionar ao enum
Exemplos de Mapeamentos Comuns
| Tags Originais | Género Normalizado | Nota |
|---|---|---|
EDM, Dance, Eletrónica |
electronic |
Música electrónica |
Pop, Top 40, Charts |
pop |
Música pop |
Notícias, News, Journalism |
news |
Notícias |
True Crime, Crime, Mystery |
true-crime |
True crime |
Tech, Tecnologia, Digital |
technology |
Tecnologia |
Sports, Futebol, Football |
sports |
Desporto |
Podcast, Talk, Interview |
talk |
Conversas |
Hip Hop, Rap, Trap |
hip-hop |
Hip hop |
Monitorização
Métricas Importantes
- Taxa de normalização: % de tags que mapeiam com sucesso
- Tags não mapeadas: Quais tags caem em
OTHER - Distribuição de géneros: Identificar desbalanceamentos
Alertas
Configure alertas para:
- Alta taxa de géneros
OTHER(>10%) - Novos tags frequentes não mapeados
- Mudanças súbitas na distribuição
Referências
- Content Features ML - Sistema completo de features
- Scripts - Script de migration
genre-mappings.ts- Mapeamentos completosContentNormalizationService- Serviço de normalização
Changelog
v1.0 - 24 Dezembro 2025
- ✅ 38 géneros normalizados
- ✅ ~300+ variantes mapeadas
- ✅ Suporte PT, EN, ES
- ✅ Integração automática com ingestão
- ✅ Documentação completa