mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 06:43:06 +00:00
docs(plan): C162 — VOD do acervo sob demanda + YouTube default (Related #999)
This commit is contained in:
parent
37b338a700
commit
913da49b90
2 changed files with 558 additions and 0 deletions
454
docs/plans/acervo-vod-sob-demanda-ui-draft.html
Normal file
454
docs/plans/acervo-vod-sob-demanda-ui-draft.html
Normal file
|
|
@ -0,0 +1,454 @@
|
|||
<!doctype html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||
<title>UI draft — Acervo de falas: VOD sob demanda + YouTube (C162)</title>
|
||||
</head>
|
||||
<body class="bg-zinc-950 text-zinc-100 antialiased">
|
||||
<main class="mx-auto max-w-5xl p-6 sm:p-8">
|
||||
<h1 class="text-lg font-medium text-zinc-100">
|
||||
UI draft — Acervo de falas: VOD sob demanda + YouTube (C162)
|
||||
</h1>
|
||||
<p class="mt-1 text-sm text-zinc-400">
|
||||
A assessoria de comunicação abre a fala, assiste o trecho exato e baixa o MP4 mesmo quando o
|
||||
CDN da Câmara já apagou o arquivo: YouTube da sessão quando existe; VOD do trecho resolvido
|
||||
sob demanda no clique; "Baixar" só entrega arquivo verificado.
|
||||
</p>
|
||||
<p class="mt-2 text-sm text-zinc-500">
|
||||
Cinza = estrutura existente · <span class="text-amber-400">acento</span> = novo neste item ·
|
||||
barras cinza = conteúdo não especificado. Cenas estáticas: sem interação, sem dados reais.
|
||||
</p>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Cena 1 · desktop — detalhe com YouTube (default quando existe) -->
|
||||
<!-- ============================================================ -->
|
||||
<section class="mt-8">
|
||||
<p class="text-xs tracking-wide text-zinc-500 uppercase">
|
||||
Cena 1 · desktop (~1280px) — detalhe com YouTube embed + ações
|
||||
</p>
|
||||
<div class="mt-3 overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900">
|
||||
<div class="px-6 py-5">
|
||||
<p class="text-xs text-zinc-500">← Voltar ao acervo</p>
|
||||
<h2 class="mt-2 text-base font-medium text-zinc-100">BREVES COMUNICAÇÕES</h2>
|
||||
<p class="mt-1 text-xs text-zinc-500">
|
||||
11/08/2026 · 18:48 · Breves Comunicações · 4min25s · Presidiu: [placeholder]
|
||||
</p>
|
||||
|
||||
<div class="mt-4 grid grid-cols-[1fr_260px] gap-5">
|
||||
<div>
|
||||
<!-- Player YouTube (default quando existe) -->
|
||||
<div
|
||||
class="relative flex aspect-video items-center justify-center rounded-lg border border-zinc-800 bg-zinc-950"
|
||||
>
|
||||
<div
|
||||
class="flex h-12 w-12 items-center justify-center rounded-full bg-zinc-800 text-zinc-300"
|
||||
>
|
||||
▶
|
||||
</div>
|
||||
<span
|
||||
class="absolute top-3 right-3 rounded border border-amber-500/60 px-2 py-0.5 text-[10px] tracking-wide text-amber-400 uppercase"
|
||||
>
|
||||
default quando existe
|
||||
</span>
|
||||
<p class="absolute bottom-3 left-3 text-xs text-zinc-400">
|
||||
Vídeo da sessão no YouTube
|
||||
</p>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-zinc-500">
|
||||
Clique na transcrição posiciona o vídeo somando o offset do trecho na sessão.
|
||||
</p>
|
||||
|
||||
<div class="mt-3 flex flex-wrap items-center gap-2">
|
||||
<button
|
||||
class="min-h-11 rounded-lg bg-amber-500 px-4 text-sm font-medium text-zinc-950"
|
||||
>
|
||||
Baixar vídeo (MP4)
|
||||
</button>
|
||||
<button
|
||||
class="min-h-11 rounded-lg border border-zinc-700 px-4 text-sm text-zinc-300"
|
||||
>
|
||||
Abrir fonte
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-zinc-500">
|
||||
O MP4 do trecho é resolvido e verificado na Câmara no clique — nunca um arquivo
|
||||
morto.
|
||||
</p>
|
||||
|
||||
<p class="mt-5 text-xs tracking-wide text-zinc-500 uppercase">
|
||||
Transcrição (clique para posicionar)
|
||||
</p>
|
||||
<div class="mt-2 space-y-1">
|
||||
<div class="flex gap-3 rounded-lg px-2 py-1.5">
|
||||
<span class="w-12 shrink-0 text-xs text-zinc-500">00:00</span>
|
||||
<span class="text-sm text-zinc-400"
|
||||
>… agradeço, Presidente. Quero falar sobre a saúde pública …</span
|
||||
>
|
||||
</div>
|
||||
<div class="flex gap-3 rounded-lg bg-zinc-800/60 px-2 py-1.5">
|
||||
<span class="w-12 shrink-0 text-xs text-amber-400">00:43</span>
|
||||
<span class="text-sm text-zinc-100"
|
||||
>… o acesso a medicamentos na Bahia precisa chegar a quem mora no interior
|
||||
…</span
|
||||
>
|
||||
</div>
|
||||
<div class="flex gap-3 rounded-lg px-2 py-1.5">
|
||||
<span class="w-12 shrink-0 text-xs text-zinc-500">02:10</span>
|
||||
<span class="text-sm text-zinc-400"
|
||||
>… e é preciso garantir o financiamento tripartite …</span
|
||||
>
|
||||
</div>
|
||||
<div class="flex gap-3 rounded-lg px-2 py-1.5">
|
||||
<span class="w-12 shrink-0 text-xs text-zinc-500">04:02</span>
|
||||
<span class="text-sm text-zinc-400">… muito obrigado, Presidente. …</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="space-y-4">
|
||||
<div>
|
||||
<p class="text-xs tracking-wide text-zinc-500 uppercase">Sumário oficial</p>
|
||||
<div class="mt-2 space-y-1.5">
|
||||
<div class="h-3 w-full rounded bg-zinc-800"></div>
|
||||
<div class="h-3 w-5/6 rounded bg-zinc-800"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs tracking-wide text-zinc-500 uppercase">Temas</p>
|
||||
<div class="mt-1.5 flex flex-wrap gap-1.5">
|
||||
<span class="rounded-full bg-zinc-800 px-2 py-0.5 text-xs text-zinc-300"
|
||||
>Saúde</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs tracking-wide text-zinc-500 uppercase">Alcance</p>
|
||||
<div class="mt-1.5 flex flex-wrap gap-1.5">
|
||||
<span class="rounded-full bg-zinc-800 px-2 py-0.5 text-xs text-zinc-300"
|
||||
>Bahia</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs tracking-wide text-zinc-500 uppercase">Municípios citados</p>
|
||||
<div class="mt-1.5 flex flex-wrap gap-1.5">
|
||||
<span
|
||||
class="rounded-full border border-zinc-700 px-2 py-0.5 text-xs text-zinc-400"
|
||||
>[Município A]</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs tracking-wide text-zinc-500 uppercase">
|
||||
Palavras-chave oficiais
|
||||
</p>
|
||||
<div class="mt-1.5 flex flex-wrap gap-1.5">
|
||||
<span
|
||||
class="rounded-full border border-zinc-700 px-2 py-0.5 text-xs text-zinc-400"
|
||||
>Farmácia Popular</span
|
||||
>
|
||||
<span
|
||||
class="rounded-full border border-zinc-700 px-2 py-0.5 text-xs text-zinc-400"
|
||||
>Medicamento</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-t border-zinc-800 pt-3">
|
||||
<p class="text-xs text-zinc-500">Fonte: Câmara dos Deputados · CC BY 4.0</p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Cena 2 · mobile — mesma hierarquia -->
|
||||
<!-- ============================================================ -->
|
||||
<section class="mt-8">
|
||||
<p class="text-xs tracking-wide text-zinc-500 uppercase">
|
||||
Cena 2 · mobile (~390px) — mesma hierarquia
|
||||
</p>
|
||||
<div class="mt-3 w-[390px] overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900">
|
||||
<div class="flex items-center justify-between border-b border-zinc-800 px-4 py-3">
|
||||
<span class="text-zinc-400">☰</span>
|
||||
<span class="text-sm font-medium text-zinc-200">Comunicação</span>
|
||||
<span class="h-6 w-6 rounded-full bg-zinc-800"></span>
|
||||
</div>
|
||||
<div class="px-4 py-4">
|
||||
<p class="text-xs text-zinc-500">← Voltar ao acervo</p>
|
||||
<h2 class="mt-2 text-sm font-medium text-zinc-100">BREVES COMUNICAÇÕES</h2>
|
||||
<p class="mt-1 text-xs text-zinc-500">11/08/2026 · 4min25s</p>
|
||||
|
||||
<div
|
||||
class="relative mt-3 flex aspect-video items-center justify-center rounded-lg border border-zinc-800 bg-zinc-950"
|
||||
>
|
||||
<div
|
||||
class="flex h-12 w-12 items-center justify-center rounded-full bg-zinc-800 text-zinc-300"
|
||||
>
|
||||
▶
|
||||
</div>
|
||||
<span
|
||||
class="absolute top-2 right-2 rounded border border-amber-500/60 px-1.5 py-0.5 text-[9px] tracking-wide text-amber-400 uppercase"
|
||||
>default</span
|
||||
>
|
||||
<p class="absolute bottom-2 left-2 text-[10px] text-zinc-400">
|
||||
Vídeo da sessão no YouTube
|
||||
</p>
|
||||
</div>
|
||||
<p class="mt-2 text-[11px] text-zinc-500">
|
||||
A transcrição posiciona o vídeo somando o offset do trecho.
|
||||
</p>
|
||||
|
||||
<button
|
||||
class="mt-3 h-11 w-full rounded-lg bg-amber-500 text-sm font-medium text-zinc-950"
|
||||
>
|
||||
Baixar vídeo (MP4)
|
||||
</button>
|
||||
<button
|
||||
class="mt-2 h-11 w-full rounded-lg border border-zinc-700 text-sm text-zinc-300"
|
||||
>
|
||||
Abrir fonte
|
||||
</button>
|
||||
|
||||
<p class="mt-4 text-xs tracking-wide text-zinc-500 uppercase">
|
||||
Transcrição (clique para posicionar)
|
||||
</p>
|
||||
<div class="mt-2 space-y-1">
|
||||
<div class="flex gap-3 rounded-lg px-2 py-1.5">
|
||||
<span class="w-10 shrink-0 text-xs text-zinc-500">00:00</span>
|
||||
<span class="text-sm text-zinc-400">… agradeço, Presidente. …</span>
|
||||
</div>
|
||||
<div class="flex gap-3 rounded-lg bg-zinc-800/60 px-2 py-1.5">
|
||||
<span class="w-10 shrink-0 text-xs text-amber-400">00:43</span>
|
||||
<span class="text-sm text-zinc-100">… o acesso a medicamentos na Bahia …</span>
|
||||
</div>
|
||||
<div class="flex gap-3 rounded-lg px-2 py-1.5">
|
||||
<span class="w-10 shrink-0 text-xs text-zinc-500">02:10</span>
|
||||
<span class="text-sm text-zinc-400">… o financiamento tripartite …</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="mt-4 text-xs tracking-wide text-zinc-500 uppercase">Temas</p>
|
||||
<div class="mt-1.5 flex flex-wrap gap-1.5">
|
||||
<span class="rounded-full bg-zinc-800 px-2 py-0.5 text-xs text-zinc-300">Saúde</span>
|
||||
<span class="rounded-full bg-zinc-800 px-2 py-0.5 text-xs text-zinc-300">Bahia</span>
|
||||
</div>
|
||||
<p class="mt-3 border-t border-zinc-800 pt-3 text-xs text-zinc-500">
|
||||
Fonte: Câmara dos Deputados · CC BY 4.0
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Cena 3 · resolvendo o trecho na Câmara (pendência do clique) -->
|
||||
<!-- ============================================================ -->
|
||||
<section class="mt-8">
|
||||
<p class="text-xs tracking-wide text-zinc-500 uppercase">
|
||||
Cena 3 · resolvendo o trecho na Câmara — pendência do clique
|
||||
</p>
|
||||
<div class="mt-3 overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900">
|
||||
<div class="px-6 py-5">
|
||||
<p class="text-xs text-zinc-500">← Voltar ao acervo</p>
|
||||
<h2 class="mt-2 text-base font-medium text-zinc-100">BREVES COMUNICAÇÕES</h2>
|
||||
<p class="mt-1 text-xs text-zinc-500">11/08/2026 · 4min25s · só VOD (sem YouTube)</p>
|
||||
|
||||
<div class="mt-4 grid grid-cols-[1fr_260px] gap-5">
|
||||
<div>
|
||||
<div
|
||||
class="flex aspect-video flex-col items-center justify-center gap-3 rounded-lg border border-zinc-800 bg-zinc-950 text-center"
|
||||
>
|
||||
<div
|
||||
class="h-8 w-8 rounded-full border-2 border-zinc-700 border-t-amber-500"
|
||||
></div>
|
||||
<p class="text-sm text-zinc-200">Resolvendo o trecho na Câmara…</p>
|
||||
<p class="max-w-xs text-xs text-zinc-500">
|
||||
Isso pode levar alguns segundos. Se demorar, tente novamente em instantes — a
|
||||
página não trava.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 flex flex-wrap items-center gap-2">
|
||||
<button
|
||||
class="min-h-11 cursor-not-allowed rounded-lg bg-amber-500/40 px-4 text-sm font-medium text-zinc-950/60"
|
||||
>
|
||||
Baixar vídeo (MP4)
|
||||
</button>
|
||||
<button
|
||||
class="min-h-11 rounded-lg border border-zinc-700 px-4 text-sm text-zinc-300"
|
||||
>
|
||||
Abrir fonte
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-zinc-500">
|
||||
O trecho pedido é o `excerptTMs` exato do acervo — nunca um vizinho.
|
||||
</p>
|
||||
|
||||
<p class="mt-5 text-xs tracking-wide text-zinc-500 uppercase">
|
||||
Transcrição (clique para posicionar)
|
||||
</p>
|
||||
<div class="mt-2 space-y-1 opacity-60">
|
||||
<div class="flex gap-3 rounded-lg px-2 py-1.5">
|
||||
<span class="w-12 shrink-0 text-xs text-zinc-500">00:00</span>
|
||||
<span class="text-sm text-zinc-400">… agradeço, Presidente. …</span>
|
||||
</div>
|
||||
<div class="flex gap-3 rounded-lg px-2 py-1.5">
|
||||
<span class="w-12 shrink-0 text-xs text-zinc-500">00:43</span>
|
||||
<span class="text-sm text-zinc-400">… o acesso a medicamentos na Bahia …</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="space-y-4">
|
||||
<div>
|
||||
<p class="text-xs tracking-wide text-zinc-500 uppercase">Sumário oficial</p>
|
||||
<div class="mt-2 space-y-1.5">
|
||||
<div class="h-3 w-full rounded bg-zinc-800"></div>
|
||||
<div class="h-3 w-4/6 rounded bg-zinc-800"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-t border-zinc-800 pt-3">
|
||||
<p class="text-xs text-zinc-500">Fonte: Câmara dos Deputados · CC BY 4.0</p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Cena 4 · falha total — nem YouTube nem VOD -->
|
||||
<!-- ============================================================ -->
|
||||
<section class="mt-8">
|
||||
<p class="text-xs tracking-wide text-zinc-500 uppercase">
|
||||
Cena 4 · falha total — nem YouTube nem VOD (bloco indisponível + abrir fonte)
|
||||
</p>
|
||||
<div class="mt-3 overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900">
|
||||
<div class="px-6 py-5">
|
||||
<p class="text-xs text-zinc-500">← Voltar ao acervo</p>
|
||||
<h2 class="mt-2 text-base font-medium text-zinc-100">BREVES COMUNICAÇÕES</h2>
|
||||
<p class="mt-1 text-xs text-zinc-500">11/08/2026 · 4min25s</p>
|
||||
|
||||
<div
|
||||
class="mt-4 flex aspect-video w-full max-w-[720px] flex-col items-center justify-center gap-2 rounded-lg border border-zinc-800 bg-zinc-950 px-6 text-center"
|
||||
>
|
||||
<div class="h-8 w-8 rounded border-2 border-zinc-700"></div>
|
||||
<p class="text-sm font-medium text-zinc-200">
|
||||
Não foi possível carregar o vídeo deste trecho
|
||||
</p>
|
||||
<p class="max-w-md text-xs text-zinc-500">
|
||||
Esta fala não tem vídeo no YouTube e a Câmara não está servindo o arquivo agora. A
|
||||
transcrição e a fonte oficial continuam disponíveis.
|
||||
</p>
|
||||
<div class="mt-2 flex flex-wrap items-center justify-center gap-2">
|
||||
<button
|
||||
class="min-h-11 rounded-lg bg-amber-500 px-4 text-sm font-medium text-zinc-950"
|
||||
>
|
||||
Tentar novamente
|
||||
</button>
|
||||
<button
|
||||
class="min-h-11 rounded-lg border border-zinc-700 px-4 text-sm text-zinc-300"
|
||||
>
|
||||
Abrir fonte
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-zinc-500">
|
||||
"Abrir fonte" leva ao PDF oficial do Diário quando existir.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Cena 5 · VOD disponível — player nativo (como hoje) -->
|
||||
<!-- ============================================================ -->
|
||||
<section class="mt-8">
|
||||
<p class="text-xs tracking-wide text-zinc-500 uppercase">
|
||||
Cena 5 · VOD disponível — player nativo, como hoje (contraste)
|
||||
</p>
|
||||
<div class="mt-3 overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900">
|
||||
<div class="px-6 py-5">
|
||||
<p class="text-xs text-zinc-500">← Voltar ao acervo</p>
|
||||
<h2 class="mt-2 text-base font-medium text-zinc-100">BREVES COMUNICAÇÕES</h2>
|
||||
<p class="mt-1 text-xs text-zinc-500">11/08/2026 · 4min25s · só VOD</p>
|
||||
|
||||
<div class="mt-4 max-w-[720px]">
|
||||
<div class="relative aspect-video rounded-lg border border-zinc-800 bg-zinc-950">
|
||||
<div
|
||||
class="absolute top-1/2 left-1/2 flex h-12 w-12 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full bg-zinc-800 text-zinc-300"
|
||||
>
|
||||
▶
|
||||
</div>
|
||||
<span
|
||||
class="absolute top-3 right-3 rounded border border-amber-500/60 px-2 py-0.5 text-[10px] tracking-wide text-amber-400 uppercase"
|
||||
>
|
||||
resolvido no clique
|
||||
</span>
|
||||
<div class="absolute inset-x-3 bottom-3">
|
||||
<div class="h-1 rounded bg-zinc-700">
|
||||
<div class="relative h-1 w-1/3 rounded bg-amber-500">
|
||||
<span
|
||||
class="absolute -top-1 -right-1.5 h-3 w-3 rounded-full bg-amber-500"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 flex items-center gap-3 text-[10px] text-zinc-400">
|
||||
<span>▶</span>
|
||||
<span>00:43 / 04:25</span>
|
||||
<span class="ml-auto flex items-center gap-2">
|
||||
<span class="h-2 w-2 rounded-sm bg-zinc-600"></span>
|
||||
<span class="h-2 w-2 rounded-sm bg-zinc-600"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-zinc-500">
|
||||
Arquivo do trecho verificado antes de tocar; a transcrição usa os segundos do
|
||||
arquivo (0-based).
|
||||
</p>
|
||||
|
||||
<div class="mt-3 flex flex-wrap items-center gap-2">
|
||||
<button
|
||||
class="min-h-11 rounded-lg bg-amber-500 px-4 text-sm font-medium text-zinc-950"
|
||||
>
|
||||
Baixar vídeo (MP4)
|
||||
</button>
|
||||
<button
|
||||
class="min-h-11 rounded-lg border border-zinc-700 px-4 text-sm text-zinc-300"
|
||||
>
|
||||
Abrir fonte
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="mt-5 text-xs tracking-wide text-zinc-500 uppercase">
|
||||
Transcrição (clique para posicionar)
|
||||
</p>
|
||||
<div class="mt-2 max-w-[720px] space-y-1">
|
||||
<div class="flex gap-3 rounded-lg px-2 py-1.5">
|
||||
<span class="w-12 shrink-0 text-xs text-zinc-500">00:00</span>
|
||||
<span class="text-sm text-zinc-400">… agradeço, Presidente. …</span>
|
||||
</div>
|
||||
<div class="flex gap-3 rounded-lg bg-zinc-800/60 px-2 py-1.5">
|
||||
<span class="w-12 shrink-0 text-xs text-amber-400">00:43</span>
|
||||
<span class="text-sm text-zinc-100">… o acesso a medicamentos na Bahia …</span>
|
||||
</div>
|
||||
<div class="flex gap-3 rounded-lg px-2 py-1.5">
|
||||
<span class="w-12 shrink-0 text-xs text-zinc-500">02:10</span>
|
||||
<span class="text-sm text-zinc-400">… o financiamento tripartite …</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="mt-4 border-t border-zinc-800 pt-3 text-xs text-zinc-500">
|
||||
Fonte: Câmara dos Deputados · CC BY 4.0
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
104
docs/plans/acervo-vod-sob-demanda.md
Normal file
104
docs/plans/acervo-vod-sob-demanda.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# Acervo de falas: player resolve o trecho sob demanda e YouTube vira default quando existe
|
||||
|
||||
Status: plano — registrado (blocked até plano em main)
|
||||
Atualizado em: 2026-09-14
|
||||
Issue: #999
|
||||
Priority: P1
|
||||
Impeccable: B — encaixe no detalhe do acervo (player + ações)
|
||||
Rascunho UI: docs/plans/acervo-vod-sob-demanda-ui-draft.html
|
||||
Appetite: ~1,5–2 dias eng; um outcome verificável — com o CDN da Câmara vazio, abrir uma fala ainda toca (YouTube quando houver) e "Baixar" nunca entrega arquivo inexistente
|
||||
Responsável: —
|
||||
|
||||
## Intenção
|
||||
|
||||
O acervo guarda o link do MP4 do trecho ("vídeo sob demanda") que a própria Câmara gera — e apaga. Medido em 2026-09-14: entre 04:35 e 08:05 o CDN derrubou os 924 MP4s do acervo, a página do evento da Câmara falha no mesmo URL, o endpoint de download responde "Não existe o arquivo …" e um probe Range dá 403/timeout. Não é regressão do C160 (que só tocou `officialTextUrl`): o link gravado sempre foi efêmero — o banco é cache, não fonte da verdade. O detalhe da fala precisa tocar e baixar mesmo assim: o player passa a usar o YouTube da sessão (estável, presente em 802 das 997 falas) quando existir e a resolver o trecho exato sob demanda na API da Câmara no clique; o "Baixar" só entrega arquivo verificado. Sem espelhar MP4 no S3.
|
||||
|
||||
## Persona e fluxo
|
||||
|
||||
- **Persona / contexto:** assessoria de comunicação da campanha (vertical `/campanha/comunicacao`; communicator, coordenação e candidatura), no escritório ou em campo, montando peça com prazo curto.
|
||||
- **Job principal:** assistir e baixar o trecho exato de uma fala do acervo, confiando que o vídeo/arquivo existe — mesmo quando o link gravado já morreu.
|
||||
- **Fluxo desejado:** abre a fala → o player toca (YouTube da sessão quando existe; senão, um clique resolve o VOD na Câmara) → clica na transcrição e vai ao ponto exato → "Baixar vídeo (MP4)" resolve e só entrega arquivo verificado → "Abrir fonte" segue para o Diário (C160).
|
||||
- **Anti-goals de produto:** segundo player/editor; espelhar ou transcodificar MP4; regenerar os 924 em lote; ajustar o trecho por vizinhança; expor o acervo fora de `/campanha`; novo `Consent`/collection.
|
||||
|
||||
### Esboço de fluxo (B/C/D)
|
||||
|
||||
```text
|
||||
[abre a fala] → YouTube (default quando existe) | capa "resolver trecho" (só VOD)
|
||||
→ clique em assistir/baixar → "Resolvendo o trecho na Câmara…"
|
||||
→ PRONTO → player nativo no trecho (segmentos 0-based) | no YouTube, seek = offset + segmento
|
||||
→ "Baixar" → arquivo verificado | falha → estado honesto + "Abrir fonte"
|
||||
```
|
||||
|
||||
### Rascunho UI (B/C/D)
|
||||
|
||||
- Rascunho UI (gate): `docs/plans/acervo-vod-sob-demanda-ui-draft.html` — cenas desktop e mobile do detalhe, resolvendo, indisponível e VOD nativo.
|
||||
|
||||
## Objetivo e aceite
|
||||
|
||||
- **Com ambos (773 falas):** o detalhe toca a sessão no YouTube no load (sem chamada à Câmara no render); "Baixar vídeo (MP4)" resolve o trecho exato na Câmara no clique e só entrega arquivo verificado.
|
||||
- **Só VOD (151):** o player oferece resolver o trecho no clique; `PRONTO` → player nativo como hoje; `GERANDO` → pendência visível (com limite; convite a tentar de novo, sem travar a página); `INDISPONIVEL`/`PRONTO` com hash morto → estado honesto, nunca arquivo morto.
|
||||
- **Só YouTube (29):** player embed; sem botão de MP4 (não há arquivo) — "Abrir fonte" cobre.
|
||||
- **Nenhum (44):** bloco "vídeo indisponível neste momento" + "Abrir fonte" quando houver (PDF do Diário do C160).
|
||||
- A resolução usa `excerptTMs` verbatim (nunca arredonda/ajusta); clique na transcrição com YouTube posiciona somando o offset da sessão — nunca cai em outro orador.
|
||||
- O "Baixar" direto do card da lista (mesmo link efêmero) sai; o download passa a ser do detalhe, que resolve e verifica no clique.
|
||||
- **Guardrails:** link do banco é cache (pode ser atualizado ou ignorado; nunca requisito — render não chama a Câmara); resolução só no clique; crédito "Fonte: Câmara dos Deputados · CC BY 4.0" mantido; gate do acervo vale para a resolução; URLs públicas do acervo intocadas; sem S3/espelho, sem novo consent/collection.
|
||||
|
||||
## Dados (intenção)
|
||||
|
||||
- **Vou apresentar dados?** Não — a superfície é player + ações; nenhum KPI/gráfico novo.
|
||||
- **Decisões desbloqueadas:** a assessoria decide cortar/usar o trecho sem arriscar arquivo morto; a coordenação decide, pelo uso, se o download continua no card da lista.
|
||||
- **Forma:** _adiada ao plano de implementação_ — restrição: o estado "resolvendo/indisponível" é feedback de ação, não dado de acervo.
|
||||
|
||||
## Dados da decisão (literais)
|
||||
|
||||
- **API (resolução sob demanda):** `https://www.camara.leg.br/evento-legislativo/{eventId}/video-sob-demanda?idAudio={audioId}&trecho={excerptTMs}` → `{ estado: 'GERANDO' | 'PRONTO' | 'INDISPONIVEL', video: { linkParaReproducao, linkParaDownload, duracao, titulo } }`.
|
||||
- **Trecho exato:** `trecho` = `excerptTMs` do banco (epoch ms); valor vizinho cai em outro orador — nunca alterar. Elegibilidade para tentar a Câmara: ter `eventId` + `audioId` + `excerptTMs` **e** VOD armazenado (ambos/só VOD); sem VOD armazenado, não tentar (o cache orienta a oferta, não a verdade do arquivo).
|
||||
- **Offset no YouTube:** `excerptTMs` (epoch ms) → hora BRT (`Intl`, America/Sao_Paulo) − `eventStartAt` (naive); ex.: `1786473834650` = 15:43:54 BRT, sessão 15:00 → 43m54s (2634 s). Segmentos ASR são 0-based no MP4 do trecho: no VOD, seek direto; no YouTube, seek = offset + `startSeconds`.
|
||||
- **Quadrantes (997 falas, 2026-09-14):** 773 ambos · 151 só VOD · 29 só YouTube · 44 nenhum; o CDN apagou os 924 MP4s entre 04:35 e 08:05.
|
||||
- **Regeneração observada:** ausente → `GERANDO` → `PRONTO` com hash novo (arquivo responde 206 `video/mp4`); `PRONTO` velho pode devolver hash morto e não aceita forçar (id 996) → tratar como indisponível.
|
||||
- **Default de produto:** YouTube quando existir; VOD da Câmara sob demanda para o trecho exato/download. Crédito mantido: `Fonte: Câmara dos Deputados · CC BY 4.0`.
|
||||
|
||||
## Direção no codebase (hipótese)
|
||||
|
||||
- **Áreas prováveis:** `src/components/campaign/speech/SpeechDetailPlayer.tsx` (player + transcrição); `src/utilities/speech/` (resolução VOD server-side); `src/app/(campaign)/campanha/(app)/comunicacao/acervo/[id]/page.tsx` + `speechPageData.ts`/`speechViewModels.ts` (levar `eventId`/`audioId`/`excerptTMs`/`eventStartAt` ao view — hoje não chegam); `src/app/(campaign)/campanha/actions/` ou rota JSON com `campaignJsonMutationRoute`; `SpeechResultCard.tsx` (se o "Baixar" da lista sair).
|
||||
- **Precedente a olhar:** fetch externo com timeout em `src/utilities/speech/speechClassifier.ts:89-105` e `src/utilities/googleCalendarClient.ts:32`; pendência com `useActionState` em `src/components/campaign/advisor/AdvisorPasswordResetButton.tsx`; embed `youtube-nocookie` em `src/components/CampaignStorySection.tsx:54-61`; ladder `getCampaignActionContext` + `runCampaignFormAction`; e2e `tests/e2e/campaignSpeechAcervo.e2e.spec.ts` (contrato `<video>`, "Baixar vídeo (MP4)", "Abrir fonte", "VOD indisponível" — renegociar no item).
|
||||
- **Risco de acoplamento:** `resolveVod`/`probeLink`/`buildVodUrl`/`parseVodStatus` vivem em `scripts/lib/camara*.mjs` e **não são importáveis de `src/`** (extrair para dono único é decisão do plano de implementação); escrita no `speech` exige `canUpdateSpeech` (unrestricted/admin) — persistir o link novo é cache opcional e não pode ampliar a escrita; o manifest de e2e mapeia `src/utilities/speech`/`src/components/campaign/speech`/`comunicacao` → `campaignSpeechAcervo`; gate `speechCatalog` vale para a resolução.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Entregues (duras, satisfeitas): C153/C154/C155 (acervo em produção, 997 falas), C160 ("Abrir fonte" = PDF oficial do Diário).
|
||||
- Nenhuma dependência bloqueante.
|
||||
|
||||
## Fora de escopo
|
||||
|
||||
- Espelhar/re-hospedar MP4 (S3/Garage) e transcodificar.
|
||||
- Regenerar os 924 em lote / botão manual "gerar novamente" / verificação em background e cron.
|
||||
- Editor/corte no browser, favoritos/curadoria, segundo player, download em lote.
|
||||
- Busca/filtros e "Abrir fonte" (C160 entregue); exposição pública do acervo.
|
||||
- Redesenhar detalhe/lista além da saída do "Baixar" do card.
|
||||
- Migration/collection/`Consent` novos; tornar o link do banco obrigatório.
|
||||
|
||||
## Rabbit holes de produto
|
||||
|
||||
- **Re-hospedar MP4 no S3.** Se alguém "só completar": storage, custo, retenção e direitos. **Corte neste item:** link sob demanda da Câmara, sem espelho.
|
||||
- **Botão "gerar novamente" manual.** Se alguém "só completar": fila de render e suporte a estado assíncrono na tela. **Corte neste item:** a resolução é implícita no clique (assistir/baixar) e o estado explica a espera.
|
||||
- **Sincronizar/cachear todo link regenerado.** Se alguém "só completar": cron, reconciliation e novas escritas. **Corte neste item:** o banco continua "último conhecido", opcional e não confiável.
|
||||
- **Redescobrir o trecho por vizinhança quando o exato falha.** Se alguém "só completar": muda o orador (fato medido). **Corte neste item:** nunca alterar o `trecho`; falha → estado honesto + fonte.
|
||||
|
||||
## Questões em aberto (produto)
|
||||
|
||||
- **Player default com ambos?** **Opções:** A) YouTube sempre que existir | B) VOD sob demanda primeiro, YouTube como fallback. **Recomendação:** A — sinalizado pelo humano; o CDN é efêmero e o YouTube é estável, e o trecho exato segue alcançável (transcrição com offset + MP4 sob demanda). _(assumido — validar no gate)_
|
||||
- **"Baixar" quando só houver YouTube?** **Opções:** A) ocultar o MP4 | B) oferecer "abrir no YouTube". **Recomendação:** A — nada de arquivo inexistente; "Abrir fonte" já leva à fonte. _(assumido)_
|
||||
- **Clique na transcrição com YouTube?** **Opções:** A) somar offset via IFrame API | B) desabilitar seek | C) link com `t=`. **Recomendação:** A — mantém o clique do C154; se a implementação do IFrame API não couber no appetite, C é a degradação aceitável; B é regressão. _(assumido)_
|
||||
|
||||
## Referências
|
||||
|
||||
- GitHub Issue: #999
|
||||
- Rascunho UI (gate): `docs/plans/acervo-vod-sob-demanda-ui-draft.html`
|
||||
- Planos irmãos: `acervo-abrir-fonte-pdf-oficial.md` (C160), `acervo-videos-comunicacao.md` (C154), `backfill-acervo-falas.md` (C155), `catalogo-falas-solla.md` (C153)
|
||||
- Arquivos-chave: `src/components/campaign/speech/SpeechDetailPlayer.tsx`, `src/components/campaign/speech/SpeechResultCard.tsx`, `src/utilities/speech/speechPageData.ts`, `src/utilities/speech/speechViewModels.ts`, `src/app/(campaign)/campanha/(app)/comunicacao/acervo/[id]/page.tsx`, `scripts/lib/camaraFetch.mjs`, `scripts/lib/camaraSpeeches.mjs` (referência, não importável), `tests/e2e/campaignSpeechAcervo.e2e.spec.ts`
|
||||
- `AGENTS.md` / `AGENTS-campaign.md` — vertical Comunicação, gate do acervo e crédito CC BY.
|
||||
|
||||
## Self-score (shaping)
|
||||
|
||||
5/5 — (1) fatia = um outcome verificável (abrir a fala toca e baixar nunca entrega arquivo morto, com o CDN da Câmara vazio); (2) cabe no appetite de ~1,5–2 dias (comportamento + estados + testes, sem schema); (3) persona, job e aceite legíveis sem jargão de stack; (4) direção no codebase é hipótese (áreas, precedentes e o alerta de que os helpers do VOD nos scripts não são importáveis); (5) zero decisão dura de engenharia no plano (rota, fetch, persistência e extração de helper ficam para o plano de implementação).
|
||||
Loading…
Reference in a new issue