API de Submissão de Programação
Visão Geral
API com que um admin (adminRole: admin) submete a programação (EPG - Electronic Program Guide) de uma rádio, em nome dela, quando não está disponível automaticamente via MyTuner Radio.
Autenticação
Requerida: Sim: JWT Bearer Token de um admin com escrita (adminRole: admin). Um usuário da app ou um viewer recebe 403.
Authorization: Bearer <token_jwt_de_admin>
Endpoint: Submeter Programação
Request
POST /api/radios/:id/schedule
Content-Type: application/json
Authorization: Bearer <token>
Parâmetros
- id (path, obrigatório): ID da rádio
Body
{
"schedule": [
[
{
"time": "06:00",
"end_time": "10:00",
"title": "Manhã de Domingo",
"subtitle": "Com Maria Santos",
"description": "Música e conversas para começar o domingo",
"duration": 240,
"sunday": true
}
],
[
{
"time": "07:00",
"end_time": "10:00",
"title": "Despertador",
"subtitle": "Com João Silva",
"monday": true,
"tuesday": true,
"wednesday": true,
"thursday": true,
"friday": true
},
{
"time": "10:00",
"end_time": "13:00",
"title": "Manhã Livre",
"subtitle": "Com Ana Costa",
"monday": true,
"tuesday": true,
"wednesday": true,
"thursday": true,
"friday": true
}
],
[],
[],
[],
[],
[]
]
}
Estrutura do Schedule
O array schedule deve conter exatamente 7 elementos, um para cada dia da semana:
- Índice 0: Domingo
- Índice 1: Segunda-feira
- Índice 2: Terça-feira
- Índice 3: Quarta-feira
- Índice 4: Quinta-feira
- Índice 5: Sexta-feira
- Índice 6: Sábado
Cada dia contém um array de programas. Dias sem programação podem ter array vazio [].
Campos do Programa
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
time |
string | ✅ | Hora de início (formato HH:MM 24h) |
end_time |
string | ❌ | Hora de fim (formato HH:MM 24h) |
title |
string | ✅ | Título do programa |
subtitle |
string | ❌ | Apresentador ou subtítulo |
description |
string | ❌ | Descrição detalhada |
duration |
number | ❌ | Duração em minutos (1-1440) |
monday |
boolean | ❌ | Transmitido às segundas |
tuesday |
boolean | ❌ | Transmitido às terças |
wednesday |
boolean | ❌ | Transmitido às quartas |
thursday |
boolean | ❌ | Transmitido às quintas |
friday |
boolean | ❌ | Transmitido às sextas |
saturday |
boolean | ❌ | Transmitido aos sábados |
sunday |
boolean | ❌ | Transmitido aos domingos |
Validações
✅ Formato de Horários: Deve seguir padrão HH:MM (00:00 a 23:59)
✅ Número de Dias: Exatamente 7 dias obrigatórios
✅ Campos Obrigatórios: time e title são sempre necessários
Response Success
{
"message": "Programação submetida com sucesso",
"scheduleId": "675a1234567890abcdef1234"
}
Status: 201 Created
Response Errors
400 Bad Request
{
"statusCode": 400,
"message": ["time must be in format HH:MM", "title must be a string"],
"error": "Bad Request"
}
Erros comuns:
- Formato de horário inválido
- Campos obrigatórios faltando
- Tipos de dados incorretos
403 Forbidden
{
"statusCode": 403,
"message": "Schedule deve conter exatamente 7 dias de programação",
"error": "Forbidden"
}
Causas:
- Array
schedulenão tem 7 elementos - O token não é de um admin com escrita (
adminRole: admin)
404 Not Found
{
"statusCode": 404,
"message": "Rádio com ID 123 não encontrada",
"error": "Not Found"
}
401 Unauthorized
{
"statusCode": 401,
"message": "Unauthorized"
}
Causa: Token JWT inválido ou ausente
Endpoint: Consultar Programação
Request
GET /api/radios/:id/schedule
Parâmetros
- id (path, obrigatório): ID da rádio
Response Success
{
"_id": "675a1234567890abcdef1234",
"radio_id": "694830c00e1febe2e0cbfe38",
"radio_name": "Rádio Exemplo",
"mytuner_id": null,
"schedule": [
[
{
"time": "06:00",
"end_time": "10:00",
"title": "Manhã de Domingo",
"subtitle": "Com Maria Santos",
"description": "Música e conversas",
"duration": 240,
"sunday": true
}
],
[...],
...
],
"fetched_at": "2025-12-24T01:00:00.000Z",
"expires_at": "2025-12-31T01:00:00.000Z",
"source": "user-submitted",
"createdAt": "2025-12-24T01:00:00.000Z",
"updatedAt": "2025-12-24T01:00:00.000Z"
}
Status: 200 OK
Response Error
404 Not Found
{
"statusCode": 404,
"message": "Rádio com ID 123 não encontrada",
"error": "Not Found"
}
Causa: Rádio não existe ou não tem programação disponível
Fontes de Programação
O sistema suporta múltiplas fontes de EPG, identificadas pelo campo source:
1. MyTuner Radio API (`source: "mytuner"`)
- ✅ Coleta automática via script
collect-epg - ✅ ~30 rádios portuguesas com EPG disponível
- ✅ Atualização automática semanal
- ⚠️ Limitado a rádios no catálogo MyTuner
2. Submissão Manual (`source: "user-submitted"`)
- ✅ Um admin (
adminRole: admin) submete via API, em nome da rádio - ✅ Útil para rádios locais sem EPG automático
- ✅ Controle total sobre conteúdo
- ⚠️ Requer atualização manual
Prioridade de Fontes
Quando existem múltiplas fontes para mesma rádio:
- User-submitted (mais recente)
- MyTuner (se user-submitted expirou)
Expiração e Atualização
Validade
- Duração: 7 dias a partir da submissão
- Campo:
expires_atindica quando expira - Auto-limpeza: Schedules expirados são ignorados nas consultas
Atualização
Para atualizar programação existente:
- Submeta novamente via POST
/api/radios/:id/schedule - Sistema remove automaticamente programação anterior com
source: "user-submitted" - Nova programação fica válida por mais 7 dias
Nota: Programação de outras fontes (MyTuner) não é afetada.
Exemplos de Uso
Exemplo 1: Programação Simples (Segunda a Sexta)
{
"schedule": [
[],
[
{
"time": "08:00",
"end_time": "12:00",
"title": "Manhã na Rádio",
"monday": true,
"tuesday": true,
"wednesday": true,
"thursday": true,
"friday": true
},
{
"time": "14:00",
"end_time": "18:00",
"title": "Tarde Musical",
"monday": true,
"tuesday": true,
"wednesday": true,
"thursday": true,
"friday": true
}
],
[],
[],
[],
[],
[]
]
}
Exemplo 2: Programação Completa com Detalhes
{
"schedule": [
[
{
"time": "10:00",
"end_time": "14:00",
"title": "Domingão na Rádio",
"subtitle": "Com Pedro Santos",
"description": "Músicas e entrevistas para o domingo",
"duration": 240,
"sunday": true
}
],
[
{
"time": "06:00",
"end_time": "10:00",
"title": "Despertador",
"subtitle": "Com Ana Silva",
"description": "Notícias, música e clima",
"duration": 240,
"monday": true,
"tuesday": true,
"wednesday": true,
"thursday": true,
"friday": true
},
{
"time": "10:00",
"end_time": "14:00",
"title": "Tarde Livre",
"subtitle": "Com João Costa",
"duration": 240,
"monday": true,
"tuesday": true,
"wednesday": true,
"thursday": true,
"friday": true
}
],
[],
[],
[],
[],
[
{
"time": "12:00",
"end_time": "18:00",
"title": "Sábado Total",
"subtitle": "Com Maria Oliveira",
"description": "Música e pedidos especiais",
"duration": 360,
"saturday": true
}
]
]
}
Exemplo 3: Usando cURL
# Obter token JWT de um admin com escrita (adminRole: admin) primeiro
TOKEN="token_jwt_de_admin_aqui"
# Submeter programação
curl -X POST "http://localhost:3000/api/radios/694830c00e1febe2e0cbfe38/schedule" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"schedule": [
[],
[
{
"time": "08:00",
"title": "Programa Matinal",
"monday": true,
"tuesday": true,
"wednesday": true,
"thursday": true,
"friday": true
}
],
[],
[],
[],
[],
[]
]
}'
# Consultar programação
curl "http://localhost:3000/api/radios/694830c00e1febe2e0cbfe38/schedule"
Melhores Práticas
1. Estruture por Dia da Semana
Organize programas por dia real de transmissão, não por padrões de repetição:
✅ Correto:
{
"schedule": [
[], // Domingo
[{"time": "08:00", "title": "Manhã", "monday": true}], // Segunda
[{"time": "08:00", "title": "Manhã", "tuesday": true}], // Terça
...
]
}
❌ Evite:
{
"schedule": [
[], // Domingo
[{"time": "08:00", "title": "Manhã", "monday": true, "tuesday": true, ...}], // Todos os dias no índice de segunda
...
]
}
2. Inclua Horários de Fim
Facilita visualização de grade:
✅ Recomendado:
{
"time": "08:00",
"end_time": "12:00",
"title": "Manhã"
}
3. Use Subtítulos para Apresentadores
{
"title": "Café da Manhã",
"subtitle": "Com João Silva e Maria Santos"
}
4. Descrições Detalhadas
Ajudam usuários a decidirem ouvir:
{
"title": "Jazz à Noite",
"subtitle": "Com Pedro Costa",
"description": "As melhores músicas de jazz de todos os tempos, com foco em artistas brasileiros e portugueses"
}
5. Valide Antes de Submeter
Use ferramenta online ou script para validar JSON antes de enviar.
Limitações e Considerações
Permissões (Atual)
⚠️ Implementação Atual: Só admins com escrita (adminRole: admin) podem submeter programação, para qualquer rádio; um usuário da app ou um viewer recebe 403.
🚧 Futuro: Apenas proprietários da rádio poderão submeter (campo owner_id será adicionado).
Validação de Horários
✅ Sistema valida formato mas não verifica:
- Sobreposições de horários
- Gaps na programação
- Continuidade entre dias
Responsabilidade do cliente garantir consistência.
Performance
- Limite de Tamanho: Aproximadamente 100 programas por dia
- Rate Limiting: Segue limites gerais da API (100 req/min)
Cache
Programação é cacheada por 5 minutos após primeira consulta para melhor performance.
Troubleshooting
Erro: "time must be in format HH:MM"
✅ Solução: Use formato 24 horas com zero à esquerda:
- ✅
"08:00" - ❌
"8:00" - ❌
"08:00:00"
Erro: "Schedule deve conter exatamente 7 dias"
✅ Solução: Array deve ter 7 elementos, mesmo dias vazios:
{
"schedule": [[], [], [], [], [], [], []] // 7 dias vazios
}
Programação não aparece
Possíveis causas:
- Schedule expirou (> 7 dias)
- ID da rádio incorreto
- Programação de outra fonte tem prioridade
Solução: Consulte endpoint GET para verificar source e expires_at.
Token JWT inválido
Causas comuns:
- Token expirou
- Token mal formatado no header
- Usuário não autenticado
Solução: Obtenha novo token via /api/auth/login.
Recursos Relacionados
- Radio Schedules & EPG - Documentação completa do sistema
- Scripts - Scripts de coleta automática de EPG
- Now Playing API - API de "Agora a Tocar"
- Radios List API - Listagem de rádios
Suporte
Para questões sobre esta API, consulte a documentação técnica ou abra issue no repositório.