Home — página inicial montada pelo painel¶
ADR-0197. Toda fileira da home é um cadastro (home_sections): ordem, título, plataforma,
público e itens vêm do painel. O app monta a página com uma rota, igual em web, celular e TV.
Erros no formato da API: {"error": {"code", "message", "details", "field", "timestamp"}}.
App: GET /api/v1/home¶
Authorization: Bearer <JWT> de assinante (admin passa direto). Cabeçalhos e parâmetros:
| Quem | Valor | Efeito |
|---|---|---|
platform (query) |
web (padrão) · mobile · tv |
só as seções que valem nessa plataforma |
X-Profile-Id |
id do perfil | perfil infantil vê só seções all/kids e itens até a faixa dele (ADR-0184); sem o header, o perfil padrão |
Accept-Language |
en-US,en;q=0.9 |
título em title_i18n[idioma]; sem tradução, title |
{
"sections": [
{
"id": "6af0…", "title": "Destaques", "layout": "hero", "type": "manual", "see_all": null,
"items": [
{"kind": "video", "video": {"…documento de GET /videos…": ""},
"preview": {"type": "trailer", "video_id": "<trailer_video_id>"}},
{"kind": "series", "season": {"…documento de GET /seasons…": ""}, "episodes_count": 5,
"preview": {"type": "image"}}
]
},
{
"id": "6af1…", "title": "Continuar assistindo", "layout": "row",
"type": "continue_watching", "see_all": null,
"items": [{"kind": "video", "video": {"…": ""}, "progress_seconds": 812.0}]
}
],
"generated_at": "2026-11-04T12:00:00Z"
}
layout:hero(carrossel) ·row(capas) ·ranking(números, o antigo "Top").see_all:{"kind": "category", "category_id": "…"}(página da categoria),{"kind": "section"}(useGET /home/sections/{id}) ounull.videoé o mesmo documento deGET /videos(categorias, tags,effective_age_rating);seasoné o deGET /seasons.previewsó vem no carrossel.preview.type = "trailer"quando o item temcarousel_media = image_and_traileretrailer_video_id; senão"image". Sem trailer cadastrado é só a foto: não cai mais no trecho do próprio vídeo. Ovideo_iddopreviewé otrailer_video_id(URL do iframe do Bunny ou GUID); para tocar, useGET /videos/{id}/trailer-url(ou/seasons/{id}/…).
Como a API monta cada seção¶
- Vale se está ligada (
enabled), no período (starts_at/ends_at), naplatforme no público do perfil. Ordem:order. - Itens:
manual=itemsna ordem. Automáticas e categoria = fixados (items) primeiro, depois a regra, sem osexcluded, semshow_on_home: false, sem repetir. - Todo item precisa estar publicado (
enable), dentro da janela de exibição (availability_*, exceto emcoming_soon) e dentro do teto do perfil infantil. Item barrado some sem deixar buraco. - Corta em
max_items(padrão 20; carrossel 10). Seção sem item não vem na resposta.
type |
Regra | Itens no painel |
|---|---|---|
manual |
só items |
toda a lista |
category |
vídeos da categoria, o mais recém-editado primeiro | fixar + excluir |
coming_soon |
availability_start_date no futuro, a mais próxima primeiro |
fixar + excluir |
recently_added |
release_date nos últimos rules.days dias (padrão 30), a mais nova primeiro |
fixar + excluir |
continue_watching |
histórico do perfil, o mais recente primeiro, um por vídeo; traz progress_seconds |
— |
recommended |
GET /me/recommendations do perfil |
— |
because_you_watched |
parecidos (categoria/tag) com o último visto; {title} já vem substituído |
— |
Série. Em categoria is_serial_type, recently_added, coming_soon e nas pessoais, os
episódios (season_id + season_number + episode_number) viram um card
(kind: "series", ordenado por Season.index). Em categoria comum entram os episódios soltos,
como o app já fazia. No card da série vale o show_on_home da temporada.
show_on_home: false tira o vídeo das fileiras automáticas e de categoria. Continua na
busca, na página da categoria, em "Minha Lista" e por link. Se ele estiver numa seção
manual, vale a seção (decisão do curador).
GET /api/v1/home/sections/{section_id}: "Ver tudo"¶
Mesmos parâmetros da home, mais qty_docs_page (1–100, padrão 20) e current_page (0…).
Devolve todos os itens da seção, sem o corte do max_items:
{"section": {"id": "…", "title": "…", "layout": "row", "type": "coming_soon",
"see_all": {"kind": "section"}, "items": [ … ]},
"current_page": 0, "qty_docs_page": 20, "qty_total_docs": 37}
Seção desligada, fora do período, de outra plataforma ou público: 404.
Cache¶
O pedaço comum (manual, category, coming_soon, recently_added) fica em cache de 60 s por
processo, por platform + público + teto de faixa. As pessoais saem do banco a cada pedido.
Salvar seção, vídeo, série ou categoria limpa o cache da réplica que recebeu o pedido; a
outra atualiza em até 60 s. Reordenar no painel chega ao app em, no máximo, 1 minuto.
Painel: cadastro das seções¶
Só administrador (user_type = admin). Escrever exige a permissão de video_* do perfil:
criar = create, editar/ordenar/itens = update, apagar = delete. Ler (GET) fica liberado a
qualquer admin, como GET /videos.
| Rota | Uso |
|---|---|
GET /admin/home-sections |
Todas, na ordem, com items_count, deletable e items_preview (5 primeiros) |
GET /admin/home-sections/{id} |
Uma seção, com items e excluded resolvidos |
POST /admin/home-sections |
Criar (201); entra no fim |
PUT /admin/home-sections/{id} |
Editar campos, sem items/excluded/order/type |
DELETE /admin/home-sections/{id} |
Apagar (204); as 3 pessoais e o carrossel dão 409 |
PUT /admin/home-sections/order |
{"order": ["<id>", …]} — todas as seções; incompleta, repetida ou id desconhecido: 422 |
PUT /admin/home-sections/{id}/items |
{"items": [{"kind","id"}], "excluded"?: […]} — substitui a lista; pessoal ou item inexistente: 422 |
GET /admin/home-sections/{id}/candidates?q= |
vídeos e séries que podem entrar (publicados, fora da lista; na category, só os da categoria) |
Corpo de POST (os de PUT são subconjuntos):
{
"title": "Comédias", "title_i18n": {"en": "Comedies"}, "type": "category",
"layout": "row", "enabled": true, "max_items": 20,
"starts_at": null, "ends_at": null,
"platforms": ["web", "mobile", "tv"], "audience": "all",
"category_id": "…", "rules": {}, "see_all": {"kind": "category", "category_id": "…"},
"items": [{"kind": "video", "id": "…"}], "excluded": []
}
Regras (422): category_id obrigatório só em category; layout: hero só em manual;
rules.days (1–365) só em recently_added; ends_at depois de starts_at; platforms não
vazia; title_i18n só en/es; categoria, vídeo ou série inexistente.
Item resolvido (no detalhe e na prévia): {kind, id, order, title, thumbnail_horizontal,
thumbnail_vertical, badges}. badges explica por que o item não sairia no app, e ele
continua na lista: disabled · not_yet_available · no_longer_available ·
show_on_home_off · missing (apagado).
Apagar ou desabilitar um vídeo (ou apagar uma série) o tira de items e excluded de todas as
seções, e a ordem é renumerada.
Pelo vídeo (ou pela série)¶
| Rota | Uso |
|---|---|
GET /admin/videos/{id}/home-sections · GET /admin/seasons/{id}/home-sections |
as caixas |
PUT /admin/videos/{id}/home-sections · PUT /admin/seasons/{id}/home-sections |
{"sections": {"<id da seção>": true\|false}} |
GET devolve {"sections": [{section_id, title, type, layout, checked, disabled, reason, item,
in_rule, pinned}]}; PUT devolve o mesmo mais changed (ids que mudaram).
manual: marcado = está emitems; marcar põe no fim.- Automática/categoria em que o item entraria pela regra: marcado = não está em
excluded; desmarcar exclui. - Em que não entraria:
disabled: truecom o motivo emreason(ex.: "Não é desta categoria."). Marcar uma assim, ou citar seção pessoal/desconhecida: 422 e nada é gravado. itemdiz o que é gravado. Num episódio de fileira que agrupa séries é{"kind": "series", …}: vale para a série inteira.
show_on_home e carousel_media entram no PUT /videos e no PUT /seasons normais.
Campos novos¶
| Onde | Campo | Padrão |
|---|---|---|
| Vídeo e temporada | show_on_home (bool) |
true |
| Vídeo e temporada | carousel_media: image · image_and_trailer |
image_and_trailer |
main_highlight, section_highlight e is_show_in_slide_carousel continuam no vídeo, só para a
migração.
Auditoria e migração¶
Criar, editar, ordenar, trocar itens, apagar e a escolha pelo vídeo gravam em audit_logs com
resource = home_sections e o antes/depois: home_section.create|update|delete|reorder|
items_update|item_update.
scripts/migrar_secoes_da_home.py (relatório por padrão, --apply grava, roda uma vez) cria as
seções que reproduzem a home de hoje: Destaques · Continuar assistindo · Recomendados ·
Porque você assistiu · Em breve · Recém-adicionados · uma por categoria (por index; ranking
para séries/Filmes/Séries). A fileira "Outros" não é migrada.