diff --git a/docs/changelog/2026-09-25-c226.md b/docs/changelog/2026-09-25-c226.md new file mode 100644 index 00000000..f742a96d --- /dev/null +++ b/docs/changelog/2026-09-25-c226.md @@ -0,0 +1 @@ +**Frame estático para vídeos com arquivo na Central (C226, Issue #1352):** toda peça de vídeo publicada e com arquivo no acervo passa a exibir um frame estático leve no card público **antes** do play, sem nenhum request do arquivo de vídeo antes do gesto (`preload="none"` intacto, S27 preservado). O frame é um derivado privado na coleção `contentMedia` (nome determinístico `content-piece-frame-.jpg`), gerado por um dono só (`ensureContentPieceFrame`) chamado pelo job C211 — que já tem o arquivo em disco, então a extração sai de graça — e pela rota `GET /conteudos//frame` em miss, que cura o acervo anterior peça a peça, sem backfill, fila ou collection nova e **sem migration**. A porta reusa o gate de slug publicado e o proxy de mídia privada do `/midia`: o kill switch fecha as duas de uma vez. Sem frame possível (falha de extração, arquivo acima do cap de 256 MB, peça sem arquivo), o slot mostra o estado neutro honesto com play e o marcador "Frame indisponível" — nunca imagem quebrada, nunca retry. A projeção declara o frame por **elegibilidade** (vídeo com arquivo) e não pela existência do derivado, então a cura é invisível para o cache da listagem e não há tag para bustar; a mesma peça também ganha o frame na página da peça e no card da home (variante compacta de 124px, cena 05 do artefato). O guard de falha de e2e passou a aceitar `RegExp` e a checar os paths declarados antes do caso do favicon (todo 404 do browser traz a mesma mensagem do favicon). Débitos registrados: teto agregado da cura pública (#1356) e paridade de cromo pré-existente do slot (#1358). diff --git a/docs/plans/central-conteudos-preview-frame-video-impl.md b/docs/plans/central-conteudos-preview-frame-video-impl.md new file mode 100644 index 00000000..d065f9fc --- /dev/null +++ b/docs/plans/central-conteudos-preview-frame-video-impl.md @@ -0,0 +1,207 @@ +# Impl: C226 — Central de Conteúdos: frame estático para vídeos com arquivo + +Status: aprovado +Atualizado em: 2026-09-25 +Issue: #1352 +Intenção: docs/plans/central-conteudos-preview-frame-video.md +Appetite restante: herdado (~1–2 dias eng) — sem corte novo: o plano reusa o gate público do S27, o proxy de mídia privada do C193/C199, o `runFfmpeg` dono (C199), a linha privada `contentMedia` e o slot de mídia compartilhado; **sem migration, sem fila, sem backfill e sem collection nova**. Editor de frame, backfill, player novo e o acervo de falas (C182) seguem fora (intenção). + +> Modo `--auto` (work-issue): plano nasce aprovado pelo agente. Design UI do gate: `docs/plans/central-conteudos-preview-frame-video-ui-design.html` — **não redesenhar**. Trigger de designer declarado em **D5: non-trigger** — o markup aprovado (CENA 01/02 com frame, CENA 04 fallback neutro) recebe só o `` e o marcador; qualquer estado novo que a execução precise para além disso é **gatilho de designer antes do markup**, não improviso. + +## Leitura da intenção + +- **Outcome:** toda peça de vídeo publicada e com arquivo no acervo mostra, no catálogo público, pelo menos **um frame estático** antes do play — imagem leve, carregada como imagem, **sem nenhum request do arquivo de vídeo** antes do gesto; sem frame possível, o slot neutro desenhado com play honesto. +- **O que NÃO negociar:** + - **Play sob demanda:** nada de elemento de mídia antes do gesto, `preload="none"` preservado, o arquivo de vídeo só entra no play (S27). + - **Kill switch:** peça despublicada **não** expõe sua mídia privada pelo novo preview. Derivado em coleção pública é proibido; a porta pública do frame usa o mesmo gate de slug publicado do `/midia`. + - **Arquivo privado continua privado:** `ContentMedia` segue `canReadContentPiece`; o frame nunca entra em `media` (anonymous-read por contrato, `Media.ts:16-20`). + - **Falha isolada:** frame que falha **não** marca a peça `falhou`, não escreve `error`, não invalida `step`; e o card não tenta repetir a busca sem limite. + - **Sem frame de terceiro:** peça-link (Instagram/YouTube sem arquivo) continua sem frame local; thumbnail de plataforma não conta. + - **Superfícies:** alvo obrigatório é `/conteudos`; a lista interna `/campanha/comunicacao/conteudos` **não** ganha player nem segunda visualização de frame (o player dela é separado, `preload="metadata"`, e fica intocado). + - **C182 proibido:** não reusar nem alterar o preview do acervo de falas (`speechPoster*.ts`, rota `/acervo/[id]/poster`). + - **Escopo de produto:** sem backfill/reprocessamento em massa, sem editor de timestamp, sem analytics nova, sem Consent, sem alteração do kill switch. +- **O que reavaliar (hipóteses da intenção, confirmadas ou resolvidas no código):** + - "o card só monta `