MEMORIA FUNCIONAL — VideoGen
Documento generado a partir del código real del proyecto (
code/videogen-v2/frontend +code/src/backend FastAPI). Fecha: 2026-08-05 · Live: https://videogen.theboomer.dev
1. Introducción
VideoGen es un servicio SaaS de generación y postproducción de vídeos cortos para redes sociales (TikTok, Instagram Reels, YouTube Shorts). El producto combina:
- Generación de vídeo a partir de una idea textual (prompt) con selección de duración, estilo visual y plataforma destino.
- Pipeline de edición post-generación basado en FFmpeg: recorte (trim), redimensionado (resize), fundidos (fade), normalización de audio (loudness LUFS), subtítulos automáticos (Whisper), música de fondo y marca de agua (watermark).
- Presets por plataforma que encapsulan la configuración óptima (resolución/aspecto) para cada red social.
- Entrega de outputs por HTTP con TTL (los archivos expiran, por defecto 1 hora).
Arquitectura real
| Capa | Tecnología | Ruta |
|---|---|---|
| Frontend (landing) | React + Vite + Tailwind + shadcn/ui + react-i18next | code/videogen-v2/src/ |
| Backend | FastAPI (Python) + Motor (MongoDB async, opcional) | code/src/ |
| Procesamiento | FFmpeg CLI (runner async) + OpenAI Whisper CLI | code/src/utils/ffmpeg_runner.py, code/src/services/ |
| Proveedores IA | Adaptadores InVideo / HeyGen / Runway / fal.ai (demo local por defecto) | code/src/providers.py |
Estado actual del frontend: la app publicada (videogen-v2) es una landing page genérica de plantilla SaaS (secciones Hero, Features, FAQ, CTA) con i18n y modo oscuro. La funcionalidad real del producto vive en la API backend (/api/v1/*), documentada en la sección 3. La landing es el punto de entrada comercial; el generador/editor es consumible vía API (los clientes pueden conectarse al backend directamente).
2. Tipos de usuario
| Tipo | Descripción | Alcance real en el código |
|---|---|---|
| Visitante | Llega a la landing sin autenticar | Ve Hero, Features, FAQ, CTA, footer. Puede cambiar idioma y tema. Los CTAs apuntan a anclas (#signup, #learn-more) sin sección implementada. |
| Usuario registrado | Cuenta creada vía /api/v1/auth/register (email, password, name) |
Recibe token sk_<usuario> y tier free. Actualmente no hay UI de dashboard conectada a la API; el acceso a los endpoints de generación/edición es vía API. |
| Cliente API / integrador | Consume los endpoints REST de generación y edición | POST /generate, /generate-edited, /edit, /edit-with-captions, /edit-v3, presets y outputs. |
| Operador/backend | Mantiene el servicio | /health, /docs (OpenAPI), limpieza periódica de outputs caducados, variables de entorno para tokens. |
Nota: el backend incluye un módulo de auth (Slice 1: register/login/me) pero no está conectado a la landing publicada; los outputs pueden protegerse con OUTPUT_TOKEN.
3. Funcionalidades
F3.1 — Landing multilingüe (frontend)
- Descripción: Página única con header sticky (logo, nav, selector de idioma, modo oscuro, menú móvil) y secciones Hero, Features, FAQ y CTA final.
- i18n: 4 idiomas —
en(fallback),es,fr,it— definidos ensrc/i18n.ts. Detección:querystring (?lang=) → cookie → localStorage → navigator; cachea enlocalStorageycookie. - Tema:
ModeToggleleelocalStorage.themeoprefers-color-scheme; alterna clasedarken<html>. - Menú móvil: botón hamburguesa/X; al abrir muestra nav vertical y cierra al hacer clic en un enlace.
- FAQ: acordeón (
Accordionsingle/collapsible) con 3 preguntas hardcodeadas en inglés (What is this? / How much? / Data privacy?). - Flujo: cargar
/→ render secciones → navegar por anclas (#features,#faq) → cambiar idioma/tema. - Validaciones / observaciones reales: los ítems de nav
pricingycontactno tienen sección con eseiden el DOM (el ancla no lleva a nada); los CTAs apuntan a#signup/#learn-moreinexistentes; los textos de features/FAQ son placeholders (no mencionan vídeo).
F3.2 — Generación de vídeo desde idea (POST /api/v1/generate)
- Descripción: genera un vídeo a partir de una idea textual.
- Entrada:
idea(texto, min 1 char),duracion∈ {15, 30, 60} s,estilo∈ {animado,real,corporativo},plataforma∈ {instagram,tiktok,youtube_shorts}. - Flujo:
1. Validar
ideano vacía → errorE001(400) si vacía. 2. Validarduracion∈ {15,30,60} → errorE002(400). 3.select_provider(estilo, plataforma)devuelve lista ordenada de proveedores. 4. Probar cada proveedor en orden hasta éxito; si todos fallan →E003: All providers failed(503). 5. Devolverstatus, video_url, proveedor, duracion_real, timestamp. - Modo demo (por defecto): en
DEMO_MODE=truelos proveedores renderizan un MP4 local real con ffmpeg: fondo de color según estilo (animado=púrpura,real=gris oscuro,corporativo=azul), resolución según plataforma (tiktok/shorts 1080x1920 9:16, instagram 1080x1080 1:1, resto 1280x720), texto del prompt centrado con caja semitransparente, 24 fps, codec libx264. Latencia simulada 0.3–0.8 s.
F3.3 — Edición de vídeo (POST /api/v1/edit)
- Descripción: aplica operaciones de edición a un vídeo local o URL.
- Entrada:
source(ruta local o URL http/https, auto-descargada), y al menos una de:trim(start/end en segundos),resize(width ≤ 7680, height ≤ 4320, fitscale|pad|crop),fade(in/out ≤ 10 s, colorblack|white),normalize(LUFS objetivo -30..-6, por defecto -16). Opcionales:output_filename,timeout(5–600 s, defecto 60). - Flujo:
1. Resolver
source(local o descargar URL) → 400 si no se resuelve, 502 si falla la descarga. 2. Validar que hay al menos una operación → 400at least one of trim/resize/fade/normalize must be provided. 3. Sioutput_filenameya existe en el workdir → 409. 4. Ejecutar cadena ffmpeg con-filter_complex(cadenas de vídeo y audio separadas por;). 5. Devolverstatus, output_path, size_bytes, elapsed_seconds, duration_seconds, width, height(oerrorsi ffmpeg falla). - Validaciones del modelo:
trim.end ≥ 0,resizedims > 0,fade0–10 s,normalize-30..-6 LUFS.
F3.4 — Edición con subtítulos automáticos (POST /api/v1/edit-with-captions)
- Descripción: igual que
/edit+ subtítulos quemados generados con Whisper local a partir de la pista de audio. - Entrada adicional:
captions—enabled(bool),model∈ {tiny, base, small, medium, large, turbo} (defectobase),languageISO-639-1 (opcional, auto-detect),style(fontDejaVu Sans, font_size 8–72, primary_color y outline_color entre white/black/yellow/red/green/blue, outline_width 0–8, margin_v 0–200, alignment 1=abajo-izq | 2=abajo-centro),timeout10–1800 s (defecto 600). - Flujo: resolución de fuente → validación (al menos una op o captions) → ffmpeg + whisper → respuesta igual que
/edit.
F3.5 — Edición completa con música y watermark (POST /api/v1/edit-v3)
- Descripción: combina trim/resize/fade/normalize + captions + música de fondo + marca de agua en un solo endpoint.
- Música (
music):source(local o URL),volume0–2 (defecto 0.5),fade_in_seconds0–10,fade_out_seconds0–10 (defecto 1),loop(bool, defecto true — si la música es más corta, se repite). Se mezcla sobre el audio original sin cambiar la ganancia de la voz. - Watermark (
watermark):source(local o URL PNG/JPG),position∈ {top-left, top-right, bottom-left, bottom-right, center} (defecto bottom-right) ocustom_position(x,y en píxeles, prioridad sobre position),opacity0–1 (defecto 0.7),scale0.01–1 (defecto 0.15 del ancho del vídeo),margin0–200 px (defecto 20). - Validación: al menos una operación de las 7 posibles → 400 en caso contrario.
F3.6 — Generación + edición atómica (POST /api/v1/generate-edited)
- Descripción: genera y edita en una sola llamada. Dos flujos:
- Flujo 1 (idea): si
ideano está vacía → genera internamente (/generate), descarga el resultado y le aplica eledit(EditRequestV3; su camposourcese ignora y se sobreescribe con el vídeo generado). - Flujo 2 (source): si no hay
ideapero sísource(URL/ruta) → salta la generación y edita directamente el vídeo existente (proveedor="caller-supplied"). - Si no viene ni idea ni source → 400
either idea (to generate) or source (existing video) is required. - Respuesta:
status, generated_url, proveedor, timestamp, output_path, size_bytes, elapsed_seconds, duration_seconds, width, height, music, watermark, captions, error. Si ffmpeg falla devuelvestatus="error"con el detalle (HTTP 200 + flag, no excepción).
F3.7 — Presets de plataforma (GET /api/v1/presets, GET /api/v1/presets/{name})
- Descripción: configuración lista para usar por plataforma. 7 presets definidos en
services/presets.py:
| Preset | Resolución | Fit | Fade | Uso |
|---|---|---|---|---|
tiktok-vertical |
1080x1920 | pad | 0.3/0.3 | TikTok / Reels / Shorts full-frame |
youtube-shorts |
1080x1920 | pad | 0.3/0.3 | YouTube Shorts |
instagram-reel |
1080x1920 | crop | 0.2/0.4 | Instagram Reels (zona segura) |
instagram-square |
1080x1080 | pad | — | Posts feed Instagram |
youtube-landscape |
1920x1080 | pad | — | YouTube estándar |
podcast-clean |
1280x720 | scale | 0.1/0.1 | Clips podcast/talking-head + loudnorm -16 LUFS |
x-twitter |
1280x720 | scale | — | Vídeos in-feed X/Twitter |
GET /presets/{name}→ 404preset not found: <name>si no existe.
F3.8 — Salida de vídeos (outputs) (GET/HEAD /api/v1/outputs/*)
- Descripción: listado y descarga de los vídeos editados.
GET /api/v1/outputs/→{count, ttl_seconds, items: [{filename, size_bytes, created_at, age_seconds, expires_in_seconds}]}ordenado por fecha desc.GET|HEAD /api/v1/outputs/{filename}→ sirve el archivo con soporte Range (seeking de vídeo),Accept-Ranges: bytes,Cache-Control: private, max-age=300.- Seguridad real: solo nombres de archivo planos (sin
/ni\, sin ocultos); path resuelto debe quedar dentro del workdir (anti path-traversal); extensiones permitidas: mp4, webm, mov, mkv, avi, jpg, png, gif; auth opcional víaOUTPUT_TOKEN(headerX-Output-Tokeno query?token=). - TTL: archivos mayores a
OUTPUT_TTL_SECONDS(defecto 3600 s = 1 h) → 410 Goneoutput expired (age=... > ttl=...). Un taskperiodic_cleanup(intervaloCLEANUP_INTERVAL_SECONDS, defecto 600 s) borra caducados. - Errores: 400 filename inválido / tipo no permitido / traversal; 401 token inválido; 404 no encontrado; 410 expirado.
F3.9 — Autenticación de API (POST /api/v1/auth/*)
POST /auth/register{email, password, name} → 201{user:{id: usr_<local-part>, email, name, tier:"free"}, token:"sk_<local-part>"}.POST /auth/login{email, password} →{token: sk_<local-part>}.GET /auth/me→ requiere headerapi_key; 401API key requiredsi falta; devuelve usuario de ejemplo.- Nota: implementación stub/simulada (no valida credenciales contra BD; el token se deriva del email).
F3.10 — Health check y operación (GET /health)
- Devuelve
{status:"healthy", service:"videogen", version, mongodb: "connected"|"skipped"|"unavailable"}. - Headers: cada respuesta HTTP lleva
X-Tentpole-Version(versión auto-generada por commit, formato0.<día-del-año>.<HHMMSS>si no hay_version.py). - MongoDB:
MONGODB_SKIP=truesalta conexión (dev/CI);MONGODB_REQUIRED=trueaborta el arranque si falla; en otro caso avisa y continúa. - La app sirve el frontend compilado (
frontend/dist) si existe, con SPA fallback aindex.html(rutasapi/,docs,openapiexcluidas).
4. Pantallas (wireframes textuales)
P1 — Landing (videogen-v2, vista pública) — la única pantalla del frontend publicado
┌────────────────────────────────────────────────────────────────────────┐
│ HEADER (sticky, backdrop-blur, borde inferior) │
│ [Logo] [Features] [Pricing] [FAQ] [Contact] [🌐 EN ▾] [🌙] [≡] │
│ (desktop: nav horizontal · móvil: botón hamburguesa que abre panel) │
├────────────────────────────────────────────────────────────────────────┤
│ HERO (centrado, py-20/32) │
│ Tu Título Aquí ← hero.title (i18n, 4xl/6xl bold) │
│ Tu descripción va aquí ← hero.subtitle (muted) │
│ [Comenzar] [Más Información] ← CTAs → #signup / #learn-more │
├────────────────────────────────────────────────────────────────────────┤
│ FEATURES (bg-muted/50, grid 3 columnas) │
│ "Características" │
│ [⚡ Fast Performance] [🔒 Secure] [🌍 i18n Ready] │
│ (cards: icono 4xl + título + descripción, border + bg-card) │
├────────────────────────────────────────────────────────────────────────┤
│ FAQ (max-w-2xl) │
│ "Preguntas Frecuentes" │
│ ▸ What is this? ← acordeón (1 abierto a la vez, colapsable) │
│ ▸ How much? │
│ ▸ Data privacy? │
├────────────────────────────────────────────────────────────────────────┤
│ CTA FINAL (bg-primary, texto claro) │
│ ¿Listo para comenzar? │
│ Únete a miles de usuarios hoy │
│ [Prueba Gratis] ← botón secondary → #signup │
├────────────────────────────────────────────────────────────────────────┤
│ FOOTER (border-t) │
│ © 2024 TentPole. Todos los derechos reservados. │
└────────────────────────────────────────────────────────────────────────┘
Comportamientos: menú idioma desplegable (EN/ES/FR/IT, marca el actual con font-medium, cierra al hacer clic fuera); toggle tema oscuro/claro con persistencia en localStorage; en móvil (<768px) la nav se oculta y aparece el panel desplegable.
P2 — API Docs (OpenAPI, servida por el backend en /docs)
┌───────────────────────────────────────────────────────────┐
│ Swagger UI — VideoGen API │
│ auth → /api/v1/auth/* (register, login, me) │
│ generate → /generate, /generate-edited │
│ edit → /edit, /edit-with-captions, /edit-v3, presets │
│ outputs → /outputs/, /outputs/{filename} (GET/HEAD) │
│ Misc → /health, /docs │
└───────────────────────────────────────────────────────────┘
P3 — Pantallas del producto (no implementadas en UI, expuestas vía API)
Wireframe conceptual de las vistas que consumiría un dashboard conectado a la API (basado en los parámetros de los endpoints; no inventar como implementadas):
Generador: [Idea textual________] [Duración: 15/30/60 ▾] [Estilo ▾] [Plataforma ▾]
[Generar] → spinner → resultado: video_url, proveedor, duración real, timestamp
Editor: [Source (URL/path)] [Trim 0..N] [Resize WxH fit] [Fade in/out] [Normalize LUFS]
[Captions on/off + model + style] [Music source/vol/loop] [Watermark src/pos/opacity]
[Aplicar edición] → output_path, size, duration, w×h
Outputs: Lista: filename, size, age, expires_in · acciones: descargar
Presets: tiktok-vertical · youtube-shorts · instagram-reel · instagram-square
youtube-landscape · podcast-clean · x-twitter
5. Flujos de trabajo
Flujo A — Generar un vídeo desde idea (API)
- Cliente llama
POST /api/v1/generateconidea,duracion,estilo,plataforma. - Backend valida (E001/E002) y selecciona proveedores para estilo+plataforma.
- Prueba proveedores en orden hasta éxito; en demo renderiza MP4 local (color + prompt).
- Devuelve
video_url(+ proveedor, duración real, timestamp). - El vídeo queda disponible para descargar o para editar.
Flujo B — Generar + editar en un solo paso
- Cliente llama
POST /api/v1/generate-editedconidea+edit(osource+edit). - Si hay
idea: genera (Flujo A) y descarga el vídeo al workdir. Si haysource: lo resuelve (URL→archivo local). - Construye las operaciones (trim/resize/fade/normalize/music/watermark/captions) a partir del spec.
- Ejecuta el pipeline ffmpeg (+ whisper si captions).
- Devuelve metadatos del edit y la ruta del output.
- El cliente descarga el output vía
GET /api/v1/outputs/{filename}(TTL 1 h).
Flujo C — Editar vídeo existente con preset mental
- Cliente consulta
GET /api/v1/presetsy elige (p. ej.tiktok-vertical). - Usa su
resize/fadeenPOST /api/v1/edit-v3(o/edit). - Aplica ediciones + extras (música, watermark, captions).
- Recibe
output_pathy descarga.
Flujo D — Ciclo de vida de un output
- Un output se crea al editar (workdir
/tmp/videogen-editspor defecto). - Es descargable mientras
age ≤ TTL(3600 s). periodic_cleanupborra archivos caducados cadaCLEANUP_INTERVAL_SECONDS.- Si se solicita un archivo expirado → HTTP 410
output expired.
Flujo E — Visitante en la landing
- Carga
https://videogen.theboomer.dev. - Ve hero + features + FAQ + CTA; puede cambiar idioma (EN/ES/FR/IT) y tema.
- Los CTAs (
#signup,#learn-more) no tienen sección destino implementada (placeholder de plantilla).
6. Reglas de negocio
- Duraciones válidas: solo 15, 30 o 60 segundos (error
E002si otro valor). - Estilos válidos:
animado,real,corporativo. - Plataformas válidas:
instagram,tiktok,youtube_shorts(resolución por defecto 1280x720 si otra). - Idea obligatoria: no vacía (error
E001); en/generate-editeddebe venirideaosource(nunca ambos vacíos). - Al menos una operación de edición en
/edit,/edit-with-captionsy/edit-v3(400 en caso contrario). - Rangos de validación: resize width ≤ 7680 / height ≤ 4320 y > 0; fade ≤ 10 s; normalize -30..-6 LUFS; música volume 0–2 y fades ≤ 10 s; watermark opacity 0–1, scale 0.01–1, margin 0–200; captions font_size 8–72, outline_width 0–8, margin_v 0–200; timeout ffmpeg 5–600 s, whisper 10–1800 s.
- Nombres de output únicos:
output_filenamerepetido → 409 Conflict. - Fallback de proveedores: si un proveedor falla se prueba el siguiente; si todos fallan → 503
E003. - TTL de outputs: 1 hora por defecto (
OUTPUT_TTL_SECONDS); caducados → 410 y limpieza periódica. - Seguridad de descargas: sin path traversal, sin archivos ocultos, extensiones permitidas whitelist, auth opcional por token (
OUTPUT_TOKEN), soporte Range. - Demo mode: en
DEMO_MODE=true(defecto) no se necesitan API keys externas; se generan MP4 placeholder reales (color + prompt quemado). - Headless por diseño: el frontend publicado es una landing de marketing; toda la funcionalidad de negocio se expone vía REST (clientes API/integradores).
- Auth: el token derivado del email (
sk_<usuario>) es un stub; la protección real de outputs esOUTPUT_TOKEN. - CORS: configurable vía
CORS_ORIGINS(por defecto*).