Estrutura (pastas em português, numeradas pra ordenar no Finder; a mídia em data/ é toda hardlink, não duplica espaço):
lula-dataset/
├── dados_lula.xlsx ← um arquivo com tudo (abas em PT): vídeos, destaques, cortes, categorias, canais, edições, campanha
├── README.md
├── scripts/ ← .py do pipeline + queries*.txt (logs em work/logs/)
└── data/
├── 01_destaques/ melhores cortes (gem_score alto)
├── 02_cartazes/ cartazes / pôsteres
├── 03_por_tema/ cortes agrupados por assunto (hardlinks)
├── 04_cortes_rosto/ cortes curtos com o rosto do Lula (ArcFace)
├── 05_cortes_continuos/ blocos contínuos de fala
├── 06_transcricoes_cortes/ transcrição por corte (Whisper), pros vídeos sem legenda
├── 07_transcricoes_videos/ transcrição por vídeo (legenda do YouTube), <video_id>.txt
├── 08_edicoes_fas/ edits de fã com música
├── 09_campanha2026/ sub-dataset da campanha de 2026 (DATASET=campaign2026)
└── 99_indices/ jsonl/csv crus (leitura por máquina, com transcript_segments)
Os índices .jsonl/.csv (inclusive campos que não cabem no Excel, como transcript_segments) ficam em data/99_indices/.
Nomes de arquivo .jsonl citados abaixo estão lá. Caminhos são relativos à raiz quando não dito o contrário.
Vídeos do YouTube com footage antiga do Lula (foco: antes de 1990) e transcript das legendas do próprio YouTube.
Construído em 14/09/2026 a partir de 172 buscas (queries.txt), 5.589 vídeos únicos encontrados.
Ordenação: pré-1990 primeiro, depois menção a Lula, depois ano estimado, depois views.
- video_id,- url,- title,- channel,- channel_url,- upload_date(YYYYMMDD),- duration_s,- view_count,- like_count,- comment_count,- description,- tags,- categories,- chapters,- thumbnail,- language
- mentions_lula(título ou descrição),- lula_in_title
- footage_year_guess,- era_source(- title_year|- description_year|- keywords),- years_mentioned,- era_keywords,- pre_1990_guess
- transcript_text(junção dos segmentos),- transcript_segments(- start,- dur,- text),- transcript_lang,- transcript_type(- manual|- auto),- transcript_source(- youtube-transcript-api|- yt-dlp),- transcript_available_langs,- transcript_error,- transcript_chars
- found_by_queries
Ano no título manda (Roda Viva | Lula | 1986 → 1986). Sem ano no título, um ano entre 1975 e 1989 na descrição.
1945 (nascimento) e 1964 em bio genérica são ignorados. Sem ano algum, palavras-chave de época
(greve, ABC, Vila Euclides, DOPS, Diretas, Constituinte, Collor, Roda Viva…) marcam pre_1990_guess com era_source = keywords,
desde que não haja marcador pós-1990 (Bolsonaro, Dilma, Lava Jato, ano ≥ 1990…); é a fatia mais ruidosa (165 vídeos). upload_date é a data do upload, não da gravação.
Quase tudo é legenda automática do YouTube (transcript_type = auto): erros de reconhecimento em nomes e em áudio antigo são comuns.
Os vídeos sem legenda no YouTube (documentários inteiros, ex.: ABC da Greve) foram transcritos por corte com Whisper: 301 cortes
via API da OpenAI (whisper-1, com timestamps), em 06_transcricoes_cortes/ (scripts/whisper_api.py; whisper_clips.py faz o mesmo local com faster-whisper).
- youtube-transcript-api e o web client do yt-dlp levam bloqueio de IP (RequestBlocked/ "confirm you're not a bot" / 429) depois de algumas centenas de requests.
- Metadados: --extractor-args youtube:player_client=mweb --ignore-no-formats-errorpassa.
- Legendas: player_client=web_embeddedexpõe as auto captions; pedir só a língua original (pt, ou.*-origpara vídeo em outra língua). Pedir tradução (ptde vídeo em espanhol,pt-PT) dá 429.
videos_pre1990.jsonl ainda tinha muito vídeo sobre o Lula. clip.py run baixa cada candidato em 360p, varre um frame a cada 2 s com
reconhecimento facial (insightface buffalo_l, ArcFace) contra work/lula_ref.npy (3.484 rostos dele extraídos do Roda Viva 1986 e do
Canal Livre 1981; separação dos jornalistas: Lula p5 = 0,63, outros p99 = 0,36; limiar 0,45) e corta só os trechos em que ele aparece.
Os dois perfis saem do mesmo download. Regras do perfil cont: junta sumidas do rosto de até 45 s (pergunta do entrevistador, corte de câmera), exige 10 s e presença do Lula em pelo menos 30% do trecho (lula_frac), acolchoa 2 s e encosta o corte na mudança de cena mais próxima (até 4 s). Parâmetros por env (GAP, MIN_RUN, PAD, MIN_FRAC); mudar e rodar de novo recorta os vídeos já varridos sem refazer a varredura (baixa de novo a fonte). Fonte baixada é apagada após o corte
(KEEP_SRC=1 mantém). Só rosto conta: voz em off do Lula sem imagem dele fica de fora. Fotos dele mostradas por um youtuber entram (trechos curtos, mean_sim alto).
./clip.py stats resume; a fila é resumível.
Download exige PO token: plugin bgutil-ytdlp-pot-provider + servidor node em ~/tools/bgutil-ytdlp-pot-provider/server (o script sobe sozinho).
Downloads sequenciais com 20 s de pausa (PACE) e espera de 10/20/40 min quando o YouTube bloqueia.
Mesmo pipeline com QUERIES_FILE=queries_edits.txt OUT=edits_ (70 buscas: edit, phonk, 4K, sigma, tribute, remix, mashup, melhores momentos… + uploads do canal do "Lula | Gangsta's Paradise").
edits_videos.jsonl/csv é o bruto (1.760 vídeos, muito Lula Molusco e receita de lula). edits_filter.py gera edits_fan.jsonl/csv (363 vídeos):
Lula no título, sem molusco/receita/jornal, título com marcador de edit, até 15 min, ordenado por views, com fan_edit_score. Transcripts não foram puxados para essa leva (é música).
Cada bloco contínuo com transcript passa por um modelo (claude -p, sonnet) que devolve categories (1 a 3 de uma taxonomia fixa: greve_sindicato,
ditadura_prisao_anistia, eleicao_1989_collor, eleicoes_82_86_88, constituinte_congresso, fome_pobreza_salario, trabalhador_patrao_classe,
democracia_diretas_voto, economia_inflacao_divida, religiao_igreja, infancia_familia_pessoal, humor_carisma, imprensa_midia, internacional,
pt_partido, comicio_discurso, entrevista_estudio, musica_jingle, outro), gem_score 0-5, gem_type, summary, quote e lula_speaks.
Resultado em clips_enriched.jsonl e cache em work/gems/. Blocos sem transcript ficam em sem_transcript.
Rerodar scripts/export_xlsx.py regenera a planilha da raiz a partir dos 99_indices.
Nota: export_xlsx.py já aponta pros nomes novos; build.py/clip.py/gems.py/posters.py ainda referenciam os nomes antigos (data/clips_continuous…) e precisariam de ajuste de path pra re-rodar o scrape.
Mesmo pipeline, outro alvo: QUERIES_FILE=queries_campaign2026.txt OUT=campaign2026_ build.py all (buscas por relevância e por data, date: usa ytsearchdate),
campaign_filter.py (upload >= jun/2026, Lula de verdade, ordenado por views). Cortes em 09_campanha2026/cortes_continuos/ e 09_campanha2026/cortes_rosto/.
Depois DATASET=campaign2026 clip.py ref <ids> (referência facial do Lula de 2026, o de 1981 não serve), DATASET=campaign2026 clip.py run,
DATASET=campaign2026 gems.py (taxonomia de 2026: economia, programas sociais, isenção do IR, Bolsonaro/adversários, 8 de janeiro, Trump/tarifas, soberania…).
As abas campanha_videos, campanha_cortes e campanha_rostos de dados_lula.xlsx saem daí. Nome dos cortes começa pela data do upload (AAAAMMDD).
Busca imagens em Bing/Google (icrawler), mantém só alta resolução (menor lado >= 800 px e área >= 1,2 MP), converte tudo pra PNG e deduplica por hash perceptual.
Um teste de achatamento de cor separa cartaz de foto de comício: retrato ou muita cor chapada fica em data/02_cartazes/, o resto vai pra data/02_cartazes/_maybe_photo/ pra revisão.
02_cartazes/index.jsonl tem width, height, megapixels, orientation, poster_score, kind. Afrouxar limiar: MIN_SIDE=600 MIN_AREA=700000 posters.py filter.
- Downloads last month
- 715