From cbeb1eece4bb6f417f975362e252b0037012ca49 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Mon, 14 Sep 2026 06:34:22 -0300 Subject: [PATCH] feat(C162): player do acervo resolve o trecho sob demanda e YouTube vira default (Closes #999) --- docs/changelog/2026-09-14-c162.md | 1 + docs/plans/acervo-vod-sob-demanda-impl.md | 165 +++++++++ package.json | 2 +- scripts/lib/camaraSpeeches.mjs | 79 +---- .../(app)/comunicacao/acervo/[id]/page.tsx | 27 +- .../comunicacao/acervo/resolver-vod/route.ts | 34 ++ .../comunicacao/acervo/resolver-vod/types.ts | 6 + src/app/(campaign)/campanha/actions/speech.ts | 56 ++++ .../campaign/speech/SpeechDetailPlayer.tsx | 315 ++++++++++++++++-- .../campaign/speech/SpeechResultCard.tsx | 8 +- src/lib/schemas/speechVod.ts | 28 ++ src/lib/speechVod.ts | 193 +++++++++++ src/utilities/speech/speechPageData.ts | 6 +- src/utilities/speech/speechViewModels.ts | 77 +++-- src/utilities/speech/speechVodResolver.ts | 102 ++++++ tests/e2e/campaignSpeechAcervo.e2e.spec.ts | 134 +++++++- tests/int/speechAcervo.int.spec.ts | 32 ++ tests/unit/speechDetailPlayer.unit.spec.tsx | 246 ++++++++++++++ tests/unit/speechVod.unit.spec.ts | 84 +++++ tests/unit/speechVodResolver.unit.spec.ts | 146 ++++++++ 20 files changed, 1564 insertions(+), 177 deletions(-) create mode 100644 docs/changelog/2026-09-14-c162.md create mode 100644 docs/plans/acervo-vod-sob-demanda-impl.md create mode 100644 src/app/(campaign)/campanha/(app)/comunicacao/acervo/resolver-vod/route.ts create mode 100644 src/app/(campaign)/campanha/(app)/comunicacao/acervo/resolver-vod/types.ts create mode 100644 src/app/(campaign)/campanha/actions/speech.ts create mode 100644 src/lib/schemas/speechVod.ts create mode 100644 src/lib/speechVod.ts create mode 100644 src/utilities/speech/speechVodResolver.ts create mode 100644 tests/unit/speechDetailPlayer.unit.spec.tsx create mode 100644 tests/unit/speechVod.unit.spec.ts create mode 100644 tests/unit/speechVodResolver.unit.spec.ts diff --git a/docs/changelog/2026-09-14-c162.md b/docs/changelog/2026-09-14-c162.md new file mode 100644 index 00000000..6ccf6761 --- /dev/null +++ b/docs/changelog/2026-09-14-c162.md @@ -0,0 +1 @@ +**C162 — VOD do acervo: player resolve o trecho sob demanda e YouTube vira default quando existe (2026-09-14):** o detalhe da fala deixa de depender do MP4 efêmero gravado (`vod.camara.leg.br`, apagado pelo CDN) e escolhe a superfície pelas coordenadas da linha: **YouTube default** quando `youtubeUrl` parseia (embed `youtube-nocookie`, sem chamada à Câmara no render) e **capa "Assistir o trecho"** quando só há VOD armazenado, resolvendo no clique o trecho exato (`video-sob-demanda?idAudio=&trecho=`) com links **verificados antes de tocar/baixar**. Dono novo das regras puras: `src/lib/speechVod.ts` recebe `CAMARA_USER_AGENT`/`buildVodUrl`/`parseVodStatus`/`parseDurationToSeconds` (movidos de `scripts/lib/camaraSpeeches.mjs`, que passa a re-exportar; `camara:pilot` ganha o mesmo loader tsx do `camara:import`) e adiciona `parseYoutubeVideoId` (watch/`youtu.be`/embed/live/shorts) e `excerptOffsetSeconds` (epoch ms → BRT `Intl` menos o naive `eventStartAt`, literal da intenção 15:43:54−15:00=2634). Resolver server-only `speechVodResolver` faz **1 chamada bounded** (2 tentativas/15s, UA da Câmara) e probe Range `bytes=0-1023` em paralelo no playback e no download (fallback HEAD em 405/501; `text/html` = hash morto), devolvendo `pronto`/`gerando`/`indisponivel`; a action `resolveSpeechVodForActor` gateia `canReadSpeechCatalog` antes da leitura (`overrideAccess: false`, select só dos campos de elegibilidade) e recusa fala sem VOD armazenado sem tocar a rede; a rota `POST /campanha/comunicacao/acervo/resolver-vod` usa `campaignJsonMutationRoute` (mesma origem) com mensagens constantes em `src/lib/schemas/speechVod.ts`. O view model perde `downloadUrl`/`vodDownloadUrl` da lista e do detalhe (o link armazenado nunca é renderizado — é só sinal de elegibilidade) e ganha `youtubeVideoId`/`youtubeOffsetSeconds`/`vodResolvable`; `SpeechDetailPlayer` reescrito com superfície única por quadrante (iframe com seek por reload `start=offset+segmento` — degradação C admitida no gate, gatilho registrado —, `