Citações: fontes → transcrições → citações → cues e clipes
Ferramenta: agent-citations/citekit.py (Python stdlib com mlx_whisper e ffmpeg). Ela nunca sobrescreve entradas e se recusa a substituir suas próprias saídas sem --force.
Layout de lote
Uma pasta por assunto em active_edits/<subject>/:
sources/video_sources.txt lista de URLs do usuário
sources/audio/<id>.m4a .info.json do yt-dlp (passo 1)
sources/transcripts/<id>.json .md passo 2
podcast/<ep>.json .md legendas do nosso próprio episódio, importadas (passo 2b)
sources/quotes.json passo 3, escrito por você
sources/citations/ saídas do passo 4
1. Download (o usuário executa)
O YouTube bloqueia o yt-dlp anônimo aqui. Dê ao usuário este comando em vez de executá-lo. Requer cookies do Firefox e os clientes tv/safari, e mantém o áudio pequeno (Whisper só precisa de 16 kHz mono):
cd active_edits/<subject>/sources && for id in ID1 ID2; do yt-dlp --cookies-from-browser firefox --extractor-args "youtube:player_client=tv,web_safari,mweb" -f "wa/w" -x --audio-format m4a --postprocessor-args "ExtractAudio:-ac 1 -ar 16000" --write-info-json --write-auto-subs --sub-langs en --sub-format json3 --no-overwrites -o "audio/%(id)s.%(ext)s" "https://youtu.be/$id"; done
Para o backlog de um canal, liste primeiro, depois selecione: yt-dlp --flat-playlist --print "%(id)s %(duration_string)s %(title)s" https://www.youtube.com/@CHANNEL/videos.
Faça triagem dos títulos com Jev (vibe-classification) se a lista for longa.
2. Transcrever
citekit.py transcribe sources/audio/<id>.m4a: mlx-whisper large-v3-turbo, timestamps de palavras. O prompt inicial é o título e a descrição do vídeo, que corrige a maioria das grafias de nomes. Roda cerca de 85× o tempo real neste Mac (um trecho de 15 min leva ~35 s; o modelo é baixado uma vez).citekit.py captions sources/audio/<id>.en.json3: legendas automáticas do YouTube. Instantâneas, mas sem pontuação. O cliente tv geralmente não as salva.- 2b. Nossos próprios episódios:
citekit.py itt EP.itt --id podcastN --media EP.mp4 --out active_edits/<subject>/podcast --fix 'REGEX=>Right'. As legendas do FCP distorcem nomes. Para o episódio 1:--fix '\bver ?m+[iu]l+e?\b=>Vermeule' --fix '\bvermil\b=>Vermeule' --fix '\bAsian Vermeule=>Adrian Vermeule'. O episódio 1 também tem correções em nível de palavra registradas empodcast/podcast1.fixes.json(Schmidt→Schmitt etc.), reaplicadas na reimportação.
Corrija erros de transcrição conforme avança (não pergunte)
Quando notar um erro claro de audição (um nome, uma citação distorcida, uma gagueira que o Whisper dobrou), corrija:
citekit.py fix TRANSCRIPT 'palavras antigas=>palavras novas' [...] [--near SEGUNDOS]. Ele edita <id>.json/.md no local
(as palavras novas compartilham o tempo do trecho antigo, correspondências podem cruzar segmentos, pontuação final é preservada),
mantém a saída bruta não modificada do motor como <id>.raw.json, e registra cada correção em <id>.fixes.json>. transcribe
e itt reproduzem esse registro, então as correções sobrevivem a uma nova execução. Não tem certeza? Re-ouça o ponto em vez de adivinhar:
ffmpeg -ss T -to T2 -i audio.m4a x.wav && mlx_whisper x.wav --output-format txt. Corrija primeiro, depois cite
as palavras corrigidas: locate corresponde à transcrição.
3. Mineração de citações (seu julgamento)
- Leia a transcrição do nosso episódio, se houver, e liste as afirmações que os apresentadores fazem sobre o assunto, cada uma com seu tempo no episódio. Esses são os momentos que um clipe pode apoiar ou refutar.
- Para cada afirmação,
citekit.py search 'regex' sources/transcripts/para encontrar candidatos, depois leia o.mdao redor do resultado. Leia palestras inteiras também: as melhores falas nem sempre são aquelas que os apresentadores nomearam. - Uma boa citação é curta (5–25 s falados), autocontida, inconfundivelmente a posição do próprio palestrante (não
ele resumindo um oponente), e citável em um lower third. Mantenha as palavras exatas da transcrição. Não
as limpe, ou o
locatenão conseguirá encontrá-las. - Escreva
sources/quotes.json:
{"project": "Ep1 Citações Vermeule", "fps": 29.97, "episode": "../podcast/podcast1.json",
"quotes": [{"id": "publius-weak-exec", "source": "Bw9M01S8MFI", "approx": 1234,
"text": "palavras exatas da transcrição",
"episode_at": 2575, "episode_context": "apresentadores sobre Hamilton e o ditador romano",
"why": "sua própria declaração da tese", "tags": ["executive", "Federalist"],
"citation": {"author": "~Adrian Vermeule", "title": "O Paradoxo de Públio", "date": "5 de junho de 2018",
"publication": "Chorley Lecture, LSE"}}]}
episode (relativo a quotes.json) é a transcrição do nosso episódio. Com ele, cada episode_at da citação (aproximadamente
quando os apresentadores levantam o ponto) se torna um ponto de inserção: o final da frase do apresentador que está sendo dita naquele momento.
Cite pensamentos completos. Se a próxima frase completa a imagem ou o argumento, inclua-a (um clipe de 5–25 s
é o alvo, não um limite rígido).
citation usa como padrão o título do vídeo, data de upload e canal. Coloque o local e a data reais (da
descrição) uma vez por palestra em um "sources": {"<id>": {"citation": {...}}} de nível superior.
O próprio citation de uma citação substitui esse valor. Autores recebem o prefixo ~ do programa, e \n quebra um título longo em duas linhas.
Verifique qualquer coisa que ele esteja citando (Hamilton, Kafka, o Digesto) em relação ao texto original. O Whisper ouve palavras erradas plausíveis: na palestra sobre Públio, ele deu "vasos de sacrifício" de Kafka como "quadros".
4. Cues e clipes
citekit.py cues sources/quotes.json ajusta cada citação aos timestamps das palavras (aproximado; avisa se a pontuação de correspondência
for < 0,8) e escreve em sources/citations/:
| Arquivo | Para |
|---|---|
citations.md | folha de revisão em ordem de episódio: tempo de inserção (como gravado e após inserções anteriores), palavras dos apresentadores imediatamente antes do corte, a citação, horários da fonte/clipe, link do YouTube ?t= |
clips.json | edição por agente: entrada/saída da fonte, entrada/saída com padding (padrão 5 s de cada lado, --pad-before/--pad-after, pad_before/pad_after por citação), insert_at, insert_at_after_earlier_inserts, insert_after |
citation_cues.json | agent-motion/titles_fcpxml.py: lower thirds Estilo Citação v2 com temporização para o episódio com clipes inseridos, surgindo conforme ele começa a falar. Citações não posicionadas são colocadas uma após a outra |
download_clips.sh | o usuário executa: vídeo em qualidade total apenas para os intervalos com padding (--download-sections) |
Depois citekit.py audio-clips sources/citations/clips.json corta prévias de áudio com padding a partir do áudio local,
para verificar de ouvido. Para posicionar os lower thirds no FCP, siga a skill motion-graphics:
python3 agent-motion/titles_fcpxml.py sources/citations/citation_cues.json -o citations.fcpxml.
Notas
-
transcribedecodifica para WAV de 16 kHz por conta própria e recusa áudio que decodifica muito mais curto que o vídeo. Um download do yt-dlp retomado com um-fdiferente emenda dois codecs em um único .m4a (visto em 2026-10-18: AAC-LC e depois HE-AAC), e o ffmpeg descarta silenciosamente tudo após a troca. Refaça o download com--no-continue. Nunca retome entre mudanças de formato. -
Verifique um clipe de ouvido, ou de forma barata:
mlx_whisper citations/audio_clips/<id>.m4a --output-format txtdeve mostrar a citação com alguns segundos de lead-in e tail. -
Os timestamps do Whisper são precisos em cerca de ±0,3 s no nível da palavra. É por isso que os paddings do clipe existem. As legendas iTT do FCP só temporizam linhas inteiras, então
ittespalha as palavras uniformemente ao longo de cada linha. -
O conector Wispr Flow lê apenas transcrições do Meeting Recorder. Não pode transcrever um arquivo.