Pipeline de Video con IA (UGC / Afiliado TikTok)
La línea de producción de John: capturas de pantalla del producto → prompt de storyboard (estrategias de First Frame UGC Hub) → generación mediante Google Flow → subida web a TikTok Studio (espaciada, intervalo de 10 minutos) → notificaciones de estado por Telegram → John adjunta el producto en la aplicación. ANTI-REPETICIÓN + CORRECCIÓN CINEMATOGRÁFICA (6 de agosto de 2026): prompt maestro v2.2 INTEGRADO EN EL PIPELINE. Cada generación de guión ahora DEBE: (1) leer /home/john/google-flow-automation/master_prompt_v2.2.txt (secciones A-J), (2) ejecutar python3 /home/john/google-flow-automation/script_rotation.py pick y elegir SOLO de allowed_strategies/allowed_hooks/allowed_ctas/allowed_personas/allowed_cameras/allowed_lighting, (3) escribir los campos adicionales de prompt.json strategy/hook_type/persona/cta_line/camera_movement/lighting, (4) ejecutar script_rotation.py record --strategy N --hook ... --cta ... --persona ... --camera ... --lighting ... --starters ... después. El estado de rotación persiste en rotation_state.json (últimos 10 guiones: sin estrategia/hook/CTA/persona/cámara/iluminación consecutivas, sin iniciador de oración de la lista de los últimos 10, CTA 'yellow basket' máximo 1 de cada 3 videos). NUEVO EN v2.2 (solicitud de John del 6 de agosto): cada videoPrompt DEBE nombrar EXACTAMENTE UN movimiento de cámara (banco I.1: push-in lento, pull-back suave, dolly lateral, handheld drift, revelación con inclinación hacia abajo, push desde ángulo bajo, órbita, rack focus, micro-movimiento estático, caminata en POV — un movimiento continuo de 10 segundos, nunca un plano estático) + UNA iluminación/atmósfera (banco I.2: hora dorada, luz natural suave de ventana, atardecer acogedor, cinematográfico sombrío, estudio limpio, mañana fresca, bokeh cálido de café, noche de neón) + reglas de encuadre SIN ROSTROS (I.3: nunca mostrar una cara — manos/POV/sobre el hombro desde atrás/solo producto; voz en off, no un orador en pantalla) + especificación de TIEMPO DE VOZ (H: ≤20 palabras = ~7-8 segundos de habla, comienza en el segundo 1, micro-pausa entre compases, última palabra de CTA hacia el ~segundo 8, tarjeta final mantiene los segundos 8-10). Copia del prompt maestro en /home/john/google-flow-automation/master_prompt_v2.2.txt (Downloads/ai_studio_master_prompt.txt es la fuente de verdad sincronizada — copiar después de cada edición). Estado al 6 de agosto de 2026: EJECUCIÓN DE PRUEBA COMPLETADA — PIPELINE VERIFICADO DE PRINCIPIO A FIN. Flujo de subida a TikTok Studio MAPEADO esa misma noche (subir + alternar IA + adjuntar producto en la subida; ruta de clics en references/tiktok-studio-upload.md). Ruta B vía CDP (flow_driver.py, Chrome 151 nativo, perfil de GOOGLE FLOW): subir → adjuntar → prompt conforme → generación Omni Flash 10s 9:16 x1 → descarga ampliada a 1080p TODO FUNCIONÓ. Los intentos de generación tempranos fallaron de forma transitoria (mensaje genérico, sin créditos cobrados) pero el reintento tuvo éxito — ver Dificultades.
Ruta A — Generación mediante API SnapGen (VERIFICADO el 11 de agosto de 2026 — NUEVA PRINCIPAL)
snapgen_client.py es ahora la PRIMERA opción de generación (AUTO_PROMPT corregido el 11 de agosto): generación omni-flash pura por HTTP a través de api.snapgen.ai — sin Chrome/CDP/X11, sin créditos de Flow, sin fallos de congelación/diálogo/doble generación. Uso: timeout 400 ~/.venvs/tiktok/bin/python /home/john/google-flow-automation/snapgen_client.py <job-dir> [--no-upscale] [--max-retries 3] — lee videoPrompt de prompt.json + product_image*.png, envía model=omni-flash 10s 9:16 1080p (modo frame ≤2 imágenes, ingrediente 3), consulta historial ~2m15s, descarga mp4 firmado, amplía con ffmpeg a 1080x1920 (la API devuelve NATIVO 720x1280 — NO existe endpoint de ampliación; --no-upscale mantiene 720p), escribe generated_video.json. Salida 0 hecho / 1 fatal / 2 transitorio. 30 créditos por video; SNAPGEN_API_KEY en .env del proyecto (cuenta de John PP0001). REANUDAR TRAS FALLO (12 de agosto — ambos clientes): el uuid se persiste en <job-dir>/.snapgen_uuid (omni) / <seg-dir>/<out>.uuid (veo) INMEDIATAMENTE después de enviar; al re-ejecutar, el cliente consulta ese uuid del historial ANTES de enviar uno nuevo (el servidor SIGUE GENERANDO después de que el cliente muera — VERIFICADO el 11 de agosto: 3 envíos huérfanos de Veo se completaron y recuperaron; reenviar duplicaría los créditos). El marcador de reanudación se elimina en caso de éxito; la reanudación de veo se basa en la ausencia del registro <out>.json (no el mp4 — un mp4 parcial sin recortar NO debe contar como hecho). Cadena de respaldo: snapgen → flow_batch.py → CDP manual. La voz en off improvisa su propio texto Taglish a partir de la dirección del prompt (mismos compases, no texto literal). La generación de imágenes nano-banana-pro es GRATUITA vía API (5rq/min) para trabajos de cliente de imágenes fijas — NO está en el guión (John dictaminó el 11 de agosto: Nano Banana sigue siendo SOLO en navegador). Detalles completos + verificación: references/snapgen-api.md + /home/john/google-flow-automation/SNAPGEN_API_ANALYSIS.md.
Flujo interactivo /generate + ruta rápida Veo 3.1 (VERIFICADO el 11 de agosto de 2026)
Ruta rápida Veo 3.1 mediante API SnapGen VERIFICADA (11 de agosto): 4 créditos, 8,0 s, 9:16 ACEPTADO (la documentación decía solo 16:9 — la API es permisiva), frame a video con imagen de referencia, GENERA LA VOZ EN OFF EN TAGLISH siguiendo el guión (transcrito textualmente: "Magaan, komportable, pang araw-araw. Hanapin niyo sa TikTok Shop." — a diferencia de omni-flash, no improvisa mucho), nativo 720x1280 → ffmpeg 1080p, has_watermark=0, ~2m15s. Comando del bot /generate (telegram_bot.py, ConversationHandler + teclados en línea, activo el 11 de agosto): /generate → [Afiliado TikTok | Video normal*] (*en desarrollo) → [Veo 3.1 fast | Omni Flash] → estilo de guión (CCC/Budol/POV/IRR/Comercial de TV) → cantidad 1-4 → escribe job_orders/generate_<ts>.json (product_folder = la última carpeta auto-* con product_image.png + product_page.png; si no hay ninguna, el bot dice que primero envíe fotos) → genera un agente NUEVO con GENERATE_PROMPT (--max-turns 400, log job_orders/run_generate_*.log) → el agente: OCR hechos → divide el guión en N segmentos (Veo ≤16 palabras/seg 8 s, Omni ≤20/seg 10 s; coreografía de cámara de comercial de TV con UN movimiento por segmento + continuidad entre segmentos) → ruta Veo: flow_frame_gen.py primer frame → N × snapgen_veo_client.py → unir si >1 → envía el mp4 final mediante sendVideo de Telegram → paso (9): envía botones en línea '¿Subir a TikTok Studio?' (genup:yes: / genup:no:) → el manejador gen_upload_choice del BOT resuelve el basename bajo ~/tiktok-jobs/ y en Sí genera un agente SOLO DE SUBIDA (UPLOAD_PROMPT — final.mp4+prompt.json ya existen, subir/programar SOLO: siguiente espacio libre a las 6:00/18:00 → tiktok_studio_upload.py → verificar página de Posts → notificaciones); en No el video queda local. Ruta Omni: N × flow_batch.py (ingredientes) → unir → enviar → mismo paso (9). REGLAS ESTRICTAS en GENERATE_PROMPT (11 de agosto): el agente /generate NUNCA toca TikTok Studio — sin tiktok_studio_upload.py, sin páginas de Studio; los botones del paso (9) son la ÚNICA ruta para una subida, y el bot (no el agente) la ejecuta. Respaldo en caso de fallo de generación de frame (11 de agosto): reintentar una vez, luego REUTILIZAR el frame bueno anterior (frame_<i-1>.png) para el segmento fallido; si TODOS los frames fallan, ejecutar segmentos Veo sin --ref o recurrir a Omni — nunca bloquear el pedido por un frame, nunca improvisar una subida a Studio.
/generate APUNTA A LA ÚLTIMA CARPETA PREPARADA — trampa de producto desactualizado (11 de agosto): latest_product_folder() selecciona la carpeta auto-* más nueva que YA tenga product_image.png + product_page.png. Si John envía NUEVAS fotos para un PRODUCTO DIFERENTE y ejecuta /generate en el mismo minuto, el trabajo puede apuntar a la carpeta del PRODUCTO ANTERIOR (real: a las 06:17, /generate tomó auto-20260811_054254/Smasher mientras sus nuevas fotos estaban sin preparar en la Carpeta de Producto — su pedido de ingreso se puso en cola detrás de un trabajo de generación sobre el producto incorrecto, y preguntó "¿por qué es esto?"). La respuesta "⏳ el tuyo está en cola" es un comportamiento CORRECTO del guardián de concurrencia, no un error — pero cuando un pedido /generate está en cola, CONFIRMAR a qué carpeta auto-* apunta ANTES de dejarlo ejecutar; si es el producto incorrecto, matar el agente + eliminar su generate_<ts>.json para que el ingreso en cola (preparación de la nueva carpeta) se convierta en el trabajo activo.
PATRÓN DE EJECUCIÓN DESCONECTADA (ACTUALIZACIÓN del 11 de agosto — setsid ahora está BLOQUEADO): la herramienta de terminal rechaza setsid bash -c '...' & en primer plano ("envoltorios de fondo a nivel de shell"). Usar systemd-run en su lugar — misma semántica de desconexión, completamente fuera del grupo de procesos de la herramienta: systemd-run --user --unit=<name> --collect bash -c '<cmd> > <log> 2>&1; echo $? > <exitfile>', luego consultar cat <exitfile> cada ~30 s. Verificar lanzamiento con systemctl --user is-active <name> + seguir el log. Las unidades se limpian solas con --collect. VERIFICADO el 11 de agosto (generación de seg_2 + unión ambas se ejecutaron así; la reanudación tras fallo funcionó limpiamente).
REGLAS DE REANUDACIÓN TRAS FALLO (11 de agosto, real): cuando un agente del pipeline muere durante un pedido, la carpeta del trabajo contiene estado verificado — VERIFICARLO antes de regenerar cualquier cosa. seg_1.mp4 + seg_1.mp4.json (verificado con ffprobe) + seg_1.json/seg_2.json (guión ya escrito Y rotación ya registrada) + first_frame.png sobreviven al fallo. Reutilizarlos; generar solo el segmento faltante. NO volver a ejecutar script_rotation.py record (el doble conteo corrompe el estado anti-repetición) y NO reescribir los JSON de segmento (seg_1.mp4 se creó a partir de ese prompt exacto).
NUEVOS GUIONES (todos VERIFICADOS salida 0 el 11 de agosto):
flow_frame_gen.py <job-dir> --prompt "<9:16 image prompt>" [--out first_frame.png]— Primer frame de Nano Banana 2 mediante Flow CDP (SOLO NAVEGADOR — regla de John, nunca la API de imágenes): nuevo proyecto → subir a librería → attach_smart (solo hace clic en el más si la librería NO está ya abierta — el más ALTERNA y un segundo clic lo cierra, matando silenciosamente Añadir al Prompt; corrección VERIFICADA) → ventana emergente de ajustes mediante settings_chip (chip de la barra de prompts en y~714 abajo, texto "Video · 10s crop_9_16 x1"/"🍌 Nano Banana 2 …" — el primer acierto de find_btn alcanza el engranaje de la BARRA SUPERIOR, usar la sonda de y>550 + botón más pequeño) → rol='tab' 'Imagen' (el TEXTO de la pestaña TIENE PREFIJO DE GLIFO: "imageImage"/"videocamVideo" — coincidir con includes('Image') NO ==) → pestaña de aspecto crop_9_16 → píldora gris de Agente → escribir + focus+Enter enviar (la flecha no funciona para imágenes) → esperar tarjeta de imagen → descargar_frame mediante el colector cookie+curl (dump_cdp_cookies.py + flow_collect_media.py; elegir la imagen 9:16; renderizado de cuadrícula ~768x1376 — válido como referencia Veo; SIN hacer clic en el visor — el menú de Descarga del visor es un campo minado: solo se muestra "1K Tamaño original", y un clic mal sincronizado golpea el icono de PAPELERA adyacente ("1 elemento movido a la papelera" — restaurar desde la vista Papelera de la librería → Restaurar todo). CORRECCIÓN DEL COLECTOR el 12 de agosto: la hoja de personaje (1536x2752) TAMBIÉN es 9:16, por lo que el antiguo selector solo por aspecto tomaba la HOJA en lugar del frame generado (frame_1 y frame_3 de la ejecución de las 08:27 — el agente tuvo que corregir manualmente).download_frameahora EXCLUYE las dimensiones exactas de la hoja de personaje y prefiere (768,1376) directamente. Los proyectos nuevos ya tienen por defecto el modo IMAGEN (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]— un segmento mediante la API (reutiliza las funciones verificadas de snapgen_client; escribejob-dir/segments/). Salida 0/1/2.stitch_videos.py <out.mp4> <in...>o<job-dir> --out final.mp4(agrupasegments/*.mp4ordenados; CORREGIDO el 12 de agosto: los nativos*.raw.mp4ahora están EXCLUIDOS del agrupamiento — la antigua regla de 'mover los raw primero' ahora es un guardián de código, sin más inserciones de 720p en medio del video) — recodificación uniforme (1080x1920 24fps crf18 aac44.1 pad) + concatenación CON RECODIFICACIÓN (no-c copy:-c copyescribe un encabezado48/1 r_frame_rateincorrecto cuando las bases de tiempo de entrada difieren, por ejemplo, después deselect/setptsdetrim_dead_air; recodificar da un24/1limpio — corregido el 11 de agosto). VERIFICADO: 10s+8s → 18,0 s h264 1080x1920. PASO DE EDICIÓN + SUBTÍTULOS (15 de agosto):--edit autoes ahora el VALOR PREDETERMINADO — movimientos de cámara por segmento conscientes del prompt (los segmentos cuyo prompt nombra movimiento de cámara NO reciben movimiento extra; otros reciben push-in/push-out suave según posición), corrección de luz (contraste 1.03/saturación 1.05) y transiciones de 0.25 s entre segmentos (xfade de video + acrossfade de audio, c1=tri:c2=tri — este ffmpeg rechaza 'fade').--caption(modo Normal/Regular SOLO — afiliado NUNCA) transcribe el video final unido con Groq (gratuito, adaptador~/video-tools/groq_transcribe.py→deepcaption build_captions.py→~/video-tools/burn_ass.pyquema con resolución nativa, estilo mínimo predeterminado). Falta clave/herramientas → subtítulos omitidos con advertencia, stitch nunca falla. Todos los modos verificados el 15 de agosto en segmentos reales de vox_bf2 (22.97 s con transición, subtitulado con confirmación visual del frame). Ver skill groq-video-captions para el manual completo de subtítulos gratuitos.trim_dead_air.py <in.mp4> [--out ...]— RECORTE DE SILENCIO (regla de John del 11 de agosto: "recortar las partes donde nadie habla"): los clips de Veo/Omni llevan colas silenciosas largas (VERIFICADO: discurso de seg_1 0-3.26 s de 8 s = 4.7 s muertos; el 43% del primer video de prueba unido era sonido ambiental). Detecta el habla con marcas de tiempo de palabras de faster-whisper (referencia verdadera — los umbrales de energía FALLAN aquí porque la cola ambiental está por encima de cualquier corte RMS fijo), mantiene [primera_palabra−0.25 s, última_palabra+0.35 s], reconstruye mediante ffmpeg select/aselect en una sola pasada. Ejecutar con~/.venvs/crypto/bin/python(único venv con faster-whisper). Salida 0 recortado / 2 nada que recortar / 3 error (original conservado — NO FATAL en ninguna parte). AUTO-INTEGRADO ensnapgen_client.py,snapgen_veo_client.py(--no-trimpara desactivar) yflow_batch.py(después de descargar) — cada segmento generado se recorta en el origen; el agente /generate NO debe regenerar segmentos para "restaurar" la longitud nominal de 8 s/10 s (VERIFICADO: seg_1 8s→3.6s es correcto). El recorte recodifica a crf 18 para que la tasa de fotogramas/encabezado se mantengan limpios. DIFICULTAD (mayormente corregida el 12 de agosto):snapgen_veo_clientdeja<out>.raw.mp4(720p nativo) ensegments/—stitch_videos.pyahora EXCLUYE*.raw.mp4de su agrupamiento, por lo que una inserción de 720p en medio del video ya no puede ocurrir; aún así, es buena higiene mover los raw ajob-dir/raw_720p/. También:--out final.mp4se resuelve contra el DIRECTORIO DE TRABAJO ACTUAL DEL GUION, no el directorio del trabajo — pasar una ruta absoluta o hacer cd al directorio del trabajo primero (un--outrelativo con cwd=google-flow-automation escribe silenciosamentefinal.mp4allí).- GENERATE_PROMPT vive en
telegram_bot.py(fuente única de verdad; placeholders de.format(): config/mode/caption/model/style/count/product_folder/chat_id/duration — SIN llaves sin escapar en la plantilla, las llaves literales de JSON rompen format). Reiniciar el bot después de editar. 15 de agosto: el asistente /generate ahora admite AMBOS modos (regular ya no termina en punto muerto) — el modo regular pregunta "Añadir subtítulos?" (sí/no), y el modo afiliado omite esta pregunta. Después de la selección de modo/estilo, el flujo es idéntico (generar → unir → enviar → paso 9).
Herramientas de referencia (diccionario de comandos)
| Comando | Función |
|---|---|
dump_cdp_cookies.py | Extraer cookies de Chrome para curl del colector de medios |
flow_collect_media.py | Descargar activos generados de la cuadrícula de Flow |
snapgen_client.py | Generación omni-flash pura por HTTP (ruta principal) |
snapgen_veo_client.py | Generación de un segmento Veo 3.1 fast por API |
flow_frame_gen.py | Generar primer frame vía Nano Banana 2 en Flow (solo navegador) |
stitch_videos.py | Unir segmentos con recodificación uniforme + pasada de edición |
trim_dead_air.py | Recortar colas silenciosas usando marcas de tiempo de faster-whisper |
tiktok_studio_upload.py | Subir a TikTok Studio (solo agente de subida) |
script_rotation.py | Elegir y registrar elementos de guión (anti-repetición) |
telegram_bot.py | Bot de Telegram con comando /generate + manejo de subida |
Dificultades Conocidas (al 12 de agosto de 2026)
- Trampa de producto desactualizado: /generate apunta a la última carpeta
auto-*con imágenes de producto. Si las nuevas fotos aún no están en una carpeta, se reutiliza la anterior. El bot ahora confirma la carpeta de destino antes de ejecutar. - Selector de pestañas de Flow: las pestañas tienen prefijo de glifo (
imageImage/videocamVideo). Usarincludes('Image'), no==. - Menú de descarga del visor: la descarga del visor es frágil (riesgo de clic en papelera). Usar el colector cookie+curl en su lugar.
- Selector de frame del colector: la hoja de personaje (1536x2752) también es 9:16.
download_frameahora excluye dimensiones de hoja y prefiere (768,1376). - Residuo
*.raw.mp4: el cliente veo escribe nativos 720p. Stitch ahora excluye*.raw.mp4del agrupamiento, pero moverlos araw_720p/sigue siendo buena práctica. - Recorte de silencio: intercepta la duración nominal del segmento (8s→3,6s). No intentar "restaurar" la longitud; es correcto.
- Ruta de
--out:stitch_videos.py --out final.mp4escribe en el CWD del guión, no en el directorio del trabajo. Usar ruta absoluta o hacer cd primero. - Doble recuento de rotación: Al reanudar, verificar
rotation_state.jsony NO volver a ejecutarscript_rotation.py record. - Modo Concurso (15 de agosto): implementación parcial — solo modo Regular; afiliado aún no tiene concurso.
- Flow CDP sin imágenes de referencia: si
--product_imagefalta, Flow CDP puede fallar silenciosamente. El agente ahora verifica la existencia del archivo antes de enviar.