Pipeline de Vídeo AI (UGC / Afiliado TikTok)
Linha de produção do John: capturas de tela do produto → prompt de storyboard (estratégias do First Frame UGC Hub) → geração via Google Flow → upload web no TikTok Studio (ritmado, intervalo de 10 min) → pings de status no Telegram → John anexa o produto no aplicativo. ANTI-REPETIÇÃO + CORREÇÃO CINEMATOGRÁFICA (6 de agosto de 2026): prompt mestre v2.2 INTEGRADO AO PIPELINE. Toda geração de roteiro AGORA DEVE: (1) ler /home/john/google-flow-automation/master_prompt_v2.2.txt (seções A-J), (2) executar python3 /home/john/google-flow-automation/script_rotation.py pick e escolher APENAS de allowed_strategies/allowed_hooks/allowed_ctas/allowed_personas/allowed_cameras/allowed_lighting, (3) escrever os campos extras do prompt.json: strategy/hook_type/persona/cta_line/camera_movement/lighting, (4) executar script_rotation.py record --strategy N --hook ... --cta ... --persona ... --camera ... --lighting ... --starters ... em seguida. O estado da rotação persiste em rotation_state.json (últimos 10 scripts: sem repetir strategy/hook/CTA/persona/camera/lighting consecutivamente, sem starter de frase da lista dos últimos 10, CTA 'cesta amarela' no máximo 1 em cada 3 vídeos). NOVO na v2.2 (solicitação do John em 6 de agosto): todo videoPrompt DEVE nomear EXATAMENTE um movimento de câmera (banco I.1: push-in lento, pull-back suave, dolly lateral, drift com a mão, revelação com inclinação para baixo, push de ângulo baixo, órbita, rack focus, micro-movimento estático, walk-in POV — um movimento contínuo de 10s, nunca um plano estático) + UMA iluminação/atmosfera (banco I.2: hora dourada, luz do dia suave de janela, entardecer aconchegante, cinematográfico dramático, estúdio limpo, fresco matinal, bokeh de café quente, noite neon) + regras de enquadramento SEM ROSTO (I.3: nunca mostrar um rosto — mãos/POV/over-shoulder por trás/só produto; narração, não um apresentador na tela) + especificação de TEMPO DE VOZ (H: ≤20 palavras = ~7-8s de fala, começa no segundo 1, micropausa entre batidas, última palavra do CTA até ~segundo 8, cartão final segura os segundos 8-10). Cópia do prompt mestre em /home/john/google-flow-automation/master_prompt_v2.2.txt (Downloads/ai_studio_master_prompt.txt é a fonte de verdade sincronizada — cp após cada edição). Status em 6 de agosto de 2026: EXECUÇÃO SOMBRA CONCLUÍDA — PIPELINE VERIFICADO DE PONTA A PONTA. Fluxo de upload do TikTok Studio MAPEADO na mesma noite (upload + alternância AI + anexar produto no upload; caminho de cliques em references/tiktok-studio-upload.md). Rota B via CDP (flow_driver.py, Chrome 151 nativo, perfil GOOGLE FLOW): upload → anexar → prompt conforme → geração Omni Flash 10s 9:16 x1 → download Upscaled 1080p TUDO FUNCIONOU. Tentativas de geração iniciais falharam transitoriamente (mensagem genérica, sem créditos cobrados), mas a repetição foi bem-sucedida — consulte Pitfalls.
Rota A — Geração via SnapGen API (VERIFICADO em 11 de agosto de 2026 — NOVO PRIMÁRIO)
O snapgen_client.py agora é a PRIMEIRA escolha de geração (AUTO_PROMPT corrigido em 11 de agosto): geração omni-flash puramente via HTTP por api.snapgen.ai — sem Chrome/CDP/X11, sem créditos Flow, sem congelamento/diálogo/falha dupla de geração. Uso: timeout 400 ~/.venvs/tiktok/bin/python /home/john/google-flow-automation/snapgen_client.py <job-dir> [--no-upscale] [--max-retries 3] — lê o videoPrompt do prompt.json + product_image*.png, envia modelo=omni-flash 10s 9:16 1080p (modo frame ≤2 imagens, ingrediente 3), consulta histórico ~2m15s, baixa mp4 assinado, faz upscale via ffmpeg para 1080x1920 (a API retorna NATIVO 720x1280 — NÃO existe endpoint de upscale; --no-upscale mantém 720p), escreve generated_video.json. Saída 0 concluído / 1 fatal / 2 transitório. 30 créditos por vídeo; SNAPGEN_API_KEY no .env do projeto (conta PP0001 do John). RETOMAR APÓS CRASH (12 de agosto — ambos os clientes): o uuid é persistido em <job-dir>/.snapgen_uuid (omni) / <seg-dir>/<out>.uuid (veo) IMEDIATAMENTE após o envio; ao reexecutar, o cliente consulta esse uuid do histórico ANTES de enviar algo novo (o servidor CONTINUA GERANDO após o cliente morrer — VERIFICADO em 11 de agosto: 3 envios Veo órfãos foram concluídos e recuperados; reenviar gastaria créditos em dobro). O marcador de retomada é removido no sucesso; a retomada veo é baseada no registro <out>.json ausente (não no mp4 — um mp4 parcial não cortado NÃO deve ser considerado concluído). Cadeia de fallback: snapgen → flow_batch.py → CDP manual. A narração improvisa seu próprio texto Taglish a partir da direção do prompt (mesmas batidas, não roteiro literal). Geração de imagem nano-banana-pro é GRATUITA via API (5rq/min) para trabalho de cliente de imagem estática — NÃO roteirizada (John decidiu em 11 de agosto: Nano Banana permanece APENAS no navegador). Detalhes completos + verificação: references/snapgen-api.md + /home/john/google-flow-automation/SNAPGEN_API_ANALYSIS.md.
Fluxo interativo /generate + caminho rápido Veo 3.1 (VERIFICADO em 11 de agosto de 2026)
Veo 3.1 rápido via SnapGen API VERIFICADO (11 de agosto): 4 créditos, 8.0s, 9:16 ACEITO (docs diziam apenas 16:9 — API é permissiva), frame-para-vídeo com imagem de referência, GERA A NARRAÇÃO TAGLISH seguindo o roteiro (transcrito literalmente: "Magaan, komportable, pang araw-araw. Hanapin niyo sa TikTok Shop." — ao contrário do omni-flash, ele NÃO improvisa muito), nativo 720x1280 → ffmpeg 1080p, has_watermark=0, ~2m15s. Comando do bot /generate (telegram_bot.py, ConversationHandler + teclados inline, ativo em 11 de agosto): /generate → [Afiliado TikTok | Vídeo Regular*] (stub) → [Veo 3.1 rápido | Omni Flash] → estilo de roteirização (CCC/Budol/POV/IRR/Comercial de TV) → quantidade 1-4 → escreve job_orders/generate_<ts>.json (product_folder = pasta auto- MAIS RECENTE com product_image.png + product_page.png; se não houver, o bot diz para enviar fotos primeiro) → cria um agente NOVO com GENERATE_PROMPT (--max-turns 400, log job_orders/run_generate_*.log) → agente: OCR fatos → divide o roteiro em N segmentos (Veo ≤16 palavras/seg 8s, Omni ≤20/seg 10s; coreografia de câmera de comercial de TV com UM movimento por segmento + continuidade entre segmentos) → caminho Veo: flow_frame_gen.py primeiro frame → N × snapgen_veo_client.py → costurar se >1 → envia o mp4 final via Telegram sendVideo → passo (9): envia botões inline 'Enviar para TikTok Studio?' (genup:sim: / genup:não:) → o manipulador gen_upload_choice do BOT resolve o basename em ~/tiktok-jobs/ e ao clicar em Sim cria um agente SOMENTE DE UPLOAD (UPLOAD_PROMPT — final.mp4+prompt.json já existem, upload/agendamento APENAS: próximo slot livre 6h/18h → tiktok_studio_upload.py → verificar página de Posts → pings); ao clicar em Não o vídeo permanece local. Caminho Omni: N × flow_batch.py (ingredientes) → costurar → enviar → mesmo passo (9). REGRRA RÍGIDA incorporada ao GENERATE_PROMPT (11 de agosto): o agente /generate NUNCA toca no TikTok Studio — sem tiktok_studio_upload.py, sem páginas do Studio; os botões do passo (9) são a ÚNICA rota para upload, e o bot (não o agente) o executa. Fallback de falha na geração de frame (11 de agosto): repetir uma vez, depois REUTILIZAR o frame bom anterior (frame_<i-1>.png) para o segmento com falha; se TODOS os frames falharem, executar segmentos Veo sem --ref ou cair para Omni — nunca bloquear o pedido por um frame, nunca improvisar um upload no Studio.
/generate MIRA NA ÚLTIMA PASTA PREPARADA — armadilha de produto desatualizado (11 de agosto): latest_product_folder() escolhe a pasta auto-* mais nova que JÁ tenha product_image.png + product_page.png. Se John enviar NOVAS fotos para um produto DIFERENTE e executar /generate no mesmo minuto, o trabalho pode mirar na pasta do PRODUTO ANTERIOR (real: 06:17 /generate pegou auto-20260811_054254/Smasher enquanto suas novas fotos estavam sem preparo em Product Folder — seu pedido de entrada então ficou na fila atrás de um trabalho de geração no produto errado, e ele perguntou "por que isso?"). A resposta "⏳ o seu está na fila" é um comportamento CORRETO de guarda de concorrência, não um bug — mas quando um pedido /generate está na fila, confirme para qual pasta auto-* ele aponta ANTES de deixá-lo executar; se for o produto errado, mate o agente + delete seu generate_<ts>.json para que a entrada na fila (preparando a nova pasta) se torne o trabalho ativo.
PADRÃO DE EXECUÇÃO DESANEXADA (ATUALIZAÇÃO de 11 de agosto — setsid agora ESTÁ BLOQUEADO): a ferramenta de terminal recusa setsid bash -c '...' & em primeiro plano ("wrappers de background no nível do shell"). Use systemd-run — mesma semântica de desanexação, totalmente fora do grupo de processos da ferramenta: systemd-run --user --unit=<name> --collect bash -c '<cmd> > <log> 2>&1; echo $? > <exitfile>', então consulte cat <exitfile> a cada ~30s. Verifique o lançamento com systemctl --user is-active <name> + tail no log. As unidades se autolimpam com --collect. VERIFICADO em 11 de agosto (a geração seg_2 + costura ambas executaram assim; retomada após crash funcionou perfeitamente).
REGRA DE RETOMADA APÓS CRASH (11 de agosto, real): quando um agente do pipeline morre no meio de um pedido, a pasta do trabalho mantém o estado verificado — VERIFIQUE-A antes de regenerar qualquer coisa. seg_1.mp4 + seg_1.mp4.json (verificado com ffprobe) + seg_1.json/seg_2.json (roteiro já escrito E rotação já registrada) + first_frame.png sobrevivem ao crash. Reutilize-os; gere apenas o segmento ausente. NÃO reexecute script_rotation.py record (contagem dupla corrompe o estado de anti-repetição) e NÃO reescreva os JSONs dos segmentos (seg_1.mp4 foi feito exatamente com esse prompt).
NOVOS SCRIPTS (todos VERIFICADOS com saída 0 em 11 de agosto):
flow_frame_gen.py <job-dir> --prompt "<9:16 prompt de imagem>" [--out first_frame.png]— Primeiro frame Nano Banana 2 via Flow CDP (APENAS NO NAVEGADOR — regra do John, nunca a API de imagem): novo projeto → upload na biblioteca → attach_smart (só clica no mais se a biblioteca NÃO estiver já aberta — o mais ALTERNA e um segundo clique o fecha, matando silenciosamente Adicionar ao Prompt; correção VERIFICADA) → popup de configurações via settings_chip (chip da barra de prompt no y~714 inferior, texto "Vídeo · 10s crop_9_16 x1"/"🍌 Nano Banana 2 …" — o find_btn de primeira correspondência acerta a engrenagem da BARRA SUPERIOR, use a sonda y>550 + menor botão) → role=tab 'Imagem' (o TEXTO DA TAB TEM PREFIXO DE GLIFO: "imageImage"/"videocamVideo" — combine com includes('Image') NÃO ==) → guia de aspecto crop_9_16 → pílula Agente com check cinza → digite + foco+Enter enviar (a seta não funciona para imagens) → aguarde o cartão de imagem → download_frame via o coletor de cookie+curl (dump_cdp_cookies.py+flow_collect_media.py; escolha a imagem 9:16; renderização da grade ~768x1376 — bom como referência Veo; NÃO clique no visualizador — o menu Download do visualizador é uma mina: apenas "1K Tamanho original" aparece, e um clique no momento errado atinge o ícone de LIXEIRA adjacente ("1 Item movido para a lixeira" — restaure da visualização Lixeira da biblioteca → Restaurar Todos). CORREÇÃO DO COLETOR em 12 de agosto: a folha de personagem (1536x2752) TAMBÉM é 9:16, então o seletor antigo baseado apenas em aspecto pegava a FOLHA em vez do frame gerado (frame_1 e frame_3 da execução das 08:27 — o agente teve que corrigir manualmente). Odownload_frameagora EXCLUI as dimensões exatas da folha de personagem e prefere (768,1376) diretamente. Projetos novos já são padrão no modo IMAGEM (Nano Banana 2 crop_9_16).snapgen_veo_client.py <job-dir> --prompt "<seg>" --ref <frame.png> --out seg_<i>.mp4 [--model veo-3.1-fast] [--duration 8] [--aspect 9:16] [--no-upscale]— um segmento via API (reutiliza as funções verificadas dosnapgen_client; escreve emjob-dir/segments/). Saída 0/1/2.stitch_videos.py <out.mp4> <in...>ou<job-dir> --out final.mp4(globssegments/*.mp4ordenados; CORRIGIDO em 12 de agosto: os*.raw.mp4nativos agora são EXCLUÍDOS do glob — a regra antiga de 'mover os raws primeiro' agora é uma proteção de código, sem mais inserções de 720p no meio do vídeo) — recodificação uniforme (1080x1920 24fps crf18 aac44.1 pad) + concat com RECODIFICAÇÃO (não-c copy:-c copyescreve um cabeçalho de r_frame_rate incorreto 48/1 quando os timebases de entrada diferem, por exemplo, após o select/setpts dotrim_dead_air; recodificar dá um 24/1 limpo — corrigido em 11 de agosto). VERIFICADO: 10s+8s → 18.0s h264 1080x1920. PASSO DE EDIÇÃO + LEGENDAS (15 de agosto):--edit autoagora é o PADRÃO — movimentos de câmera por segmento cientes do prompt (segmentos cujo prompt nomeia movimento de câmera NÃO recebem movimento extra; outros recebem push-in/push-out suave por posição), gradação de luz (contraste 1.03/sat 1.05), e crossfades de 0.25s entre segmentos (xfade de vídeo + acrossfade de áudio, c1=tri:c2=tri — este ffmpeg rejeita 'fade').--caption(modo Regular/Normal APENAS — afiliado NUNCA) transcreve o vídeo final costurado com Groq (grátis, adaptador~/video-tools/groq_transcribe.py→build_captions.pydo deepcaption →~/video-tools/burn_ass.pyqueima em resolução nativa, estilo mínimo padrão). Chave/ferramentas ausentes → legendas puladas com aviso, costura nunca falha. Todos os modos verificados em 15 de agosto em segmentos reais vox_bf2 (22.97s com crossfade, legendas confirmadas visualmente no frame). Consulte a habilidade groq-video-captions para o manual completo de legendas gratuitas.trim_dead_air.py <in.mp4> [--out ...]— CORTE DE SILÊNCIO (regra do John em 11 de agosto: "corte as partes onde ninguém está falando"): clipes Veo/Omni carregam longas caudas silenciosas (VERIFICADO: fala seg_1 0-3.26s de 8s = 4.7s mortos; 43% do primeiro vídeo costurado de teste era ruído ambiente). Detecta fala com timestamps de palavras do faster-whisper (verdade absoluta — limites de energia FALHAM aqui porque a cauda ambiente está acima de qualquer corte RMS fixo), mantém [primeira_palavra−0.25s, última_palavra+0.35s], reconstrói via select/aselect do ffmpeg em passagem única. Execute com ~/.venvs/crypto/bin/python (único venv com faster-whisper). Saída 0 aparado / 2 nada para aparar / 3 erro (original mantido — NÃO-FATAL em todos os lugares). AUTO-INTEGRADO emsnapgen_client.py,snapgen_veo_client.py(--no-trimpara desabilitar), eflow_batch.py(após download) — todo segmento gerado é aparado na fonte; o agente /generate NÃO deve regenerar segmentos para "restaurar" o comprimento nominal de 8s/10s (VERIFICADO: seg_1 8s→3.6s está correto). Aparar recodifica em crf 18 para que a taxa de quadros/cabeçalho permaneçam limpos. PITFALL (principalmente corrigido em 12 de agosto):snapgen_veo_clientdeixa<out>.raw.mp4(720p nativo) emsegments/—stitch_videos.pyagora EXCLUI*.raw.mp4de seu glob, então uma inserção de 720p no meio do vídeo não pode mais acontecer; ainda é boa higiene mover os raws parajob-dir/raw_720p/. Também:--out final.mp4resolve em relação ao WD do SCRIPT, não ao diretório do job — passe um caminho absoluto oucdpara o diretório do job primeiro (um--outrelativo com cwd=google-flow-automation escreve silenciosamentefinal.mp4lá).- GENERATE_PROMPT vive em
telegram_bot.py(fonte única de verdade; placeholders .format(): config/mode/caption/model/style/count/product_folder/chat_id/duration — NENHUMA chave não escapada no modelo, chaves literais de JSON quebram o format). Reinicie o bot após editar. 15 de agosto: o assistente /generate agora suporta AMBOS os modos (regular não termina mais em beco sem saída) — o modo regular pergunta "Adicionar wo...