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 —, `