Sistema de Cola de Rendering

El sistema automáticamente encola el renderizado de videos cuando todos los clips están procesados.

Flujo Completo

1. POST /api/videos
       ↓
2. Clips se encolan (CLIP_PROCESSING queue)
       ↓
3. Workers procesan clips en paralelo (TTS, etc)
       ↓
4. Último clip procesado → Video READY
       ↓
5. Se envía webhook (si está configurado)
       ↓
6. Se encola en VIDEO_RENDERING queue
       ↓
7. Render worker procesa el video
       ↓
8. Video generado → Status COMPLETED

Colas en RabbitMQ

El sistema utiliza 3 colas:

1. CLIP_PROCESSING (alta prioridad)

  • Procesa clips individuales (TTS, metadata)
  • Workers: 5 por defecto
  • Concurrencia: 5 clips simultáneos por worker

2. VIDEO_PROCESSING (legacy)

  • Procesamiento secuencial de todos los clips de un video
  • Workers: 2 por defecto
  • Uso: Solo para estrategia "sequential"

3. VIDEO_RENDERING (nueva)

  • Renderizado del video final con Remotion/ffmpeg
  • Workers: Configurables
  • Se activa automáticamente cuando video está READY

Cuando un Video está READY

Automáticamente se ejecutan estas acciones:

1. Enviar Webhook (si está configurado)

if (video.webhookUrl) {
  await webhookService.sendVideoReadyWebhook(videoId);
}

2. Encolar Rendering

await videoQueue.enqueueVideoRendering(videoId);

Ambas acciones ocurren automáticamente, no necesitas hacer nada.


Render Worker (Próximo Paso)

El render worker aún no está implementado. Cuando lo implementes, debe:

  1. Consumir la cola VIDEO_RENDERING
  2. Obtener el JSON final del video
  3. Renderizar el video con Remotion o ffmpeg
  4. Actualizar el estado a RENDERINGCOMPLETED
  5. Guardar la URL del video generado

Ejemplo de Render Worker

// app/workers/render-worker.server.ts
export async function startRenderWorker() {
  const channel = await getRabbitMQChannel();

  await channel.assertQueue(QUEUES.VIDEO_RENDERING, { durable: true });
  await channel.prefetch(2); // 2 renders simultáneos

  channel.consume(QUEUES.VIDEO_RENDERING, async (msg) => {
    const job: VideoRenderingJob = JSON.parse(msg.content.toString());

    try {
      // 1. Marcar como RENDERING
      await prisma.videoConfiguration.update({
        where: { id: job.videoConfigId },
        data: { status: 'RENDERING' }
      });

      // 2. Obtener JSON final
      const finalJSON = await clipProcessor.buildFinalJSON(job.videoConfigId);

      // 3. Renderizar con Remotion
      const videoUrl = await renderWithRemotion(finalJSON);

      // 4. Marcar como COMPLETED
      await prisma.videoConfiguration.update({
        where: { id: job.videoConfigId },
        data: {
          status: 'COMPLETED',
          videoUrl: videoUrl
        }
      });

      channel.ack(msg);
    } catch (error) {
      console.error('Render failed:', error);

      await prisma.videoConfiguration.update({
        where: { id: job.videoConfigId },
        data: { status: 'FAILED' }
      });

      channel.nack(msg, false, true); // Reintenta
    }
  });
}

Verificar Cola de Rendering

Ver mensajes pendientes

# Con PM2 logs
pm2 logs render-worker

# O con script de estadísticas
npx tsx scripts/queue-stats.ts

Crear script de estadísticas

// scripts/queue-stats.ts
import { videoQueue } from '../app/services/video-queue.server';

const stats = await videoQueue.getQueueStats();

console.log('Video Processing:', stats.videoQueue.messages, 'messages');
console.log('Clip Processing:', stats.clipQueue.messages, 'messages');
console.log('Video Rendering:', stats.renderQueue.messages, 'messages');

Estados del Video

PENDING     → Clips creados, esperando procesamiento
PROCESSING  → Procesando clips
READY       → Todos los clips listos, JSON disponible (encola rendering)
RENDERING   → Renderizando video (worker procesa)
COMPLETED   → Video renderizado exitosamente
FAILED      → Error en procesamiento o rendering
CANCELLED   → Cancelado por el usuario

Comportamiento Actual

Sin render worker:

  • Videos llegan a estado READY
  • Se envía webhook con JSON final
  • Se encola en VIDEO_RENDERING
  • Pero no se procesa (no hay worker escuchando)

Con render worker:

  • Videos llegan a READY
  • Se encola en VIDEO_RENDERING
  • Render worker toma el job
  • Renderiza el video
  • Marca como COMPLETED

Configuración

Variables de Entorno

# Número de renders simultáneos por worker
RENDER_WORKER_CONCURRENCY=2

# Timeout para rendering (en minutos)
RENDER_TIMEOUT=30

PM2 Ecosystem

Añade el render worker a ecosystem.config.cjs:

{
  name: 'render-worker',
  script: './node_modules/.bin/tsx',
  args: 'scripts/start-render-worker.ts',
  instances: 2,  // 2 renders simultáneos
  env: {
    RENDER_WORKER_CONCURRENCY: '1',  // 1 video por instancia
    RENDER_TIMEOUT: '30'
  },
  max_memory_restart: '2G'  // Rendering usa más memoria
}

Ejemplo Completo

1. Usuario envía video

curl -X POST http://localhost:5173/api/videos \
  -H "Authorization: Bearer api-key" \
  -d '{
    "webhookUrl": "https://mi-servidor.com/webhook",
    "output": { "fps": 30, "width": 1920, "height": 1080 },
    "timeline": [
      { "asset": { "type": "tts", "text": "Hola" }, "start": 0, "length": "{{audio.duration}}" }
    ]
  }'

# Respuesta: { "videoId": "cmg6..." }

2. Clips se procesan automáticamente

[ClipWorker] Processing clip cmg6...
[ClipProcessor] Generating TTS audio...
[ClipWorker] Clip READY

3. Video READY → Webhook + Rendering

[ClipWorker] Video cmg6... completed with status: READY
[ClipWorker] Sending webhook for video cmg6...
[Webhook] Sending webhook to https://mi-servidor.com/webhook
[ClipWorker] Enqueuing rendering for video cmg6...
[VideoQueue] Enqueued video rendering: cmg6...

4. Render worker procesa (futuro)

[RenderWorker] Processing video cmg6...
[RenderWorker] Rendering with Remotion...
[RenderWorker] Video rendered successfully
[RenderWorker] Video cmg6... completed

Prioridades de Cola

Si tienes muchos videos pendientes, puedes configurar prioridades:

// Alta prioridad para rendering
channel.sendToQueue(
  QUEUES.VIDEO_RENDERING,
  Buffer.from(JSON.stringify(job)),
  {
    persistent: true,
    priority: 10  // Mayor = más prioridad
  }
);

Monitoreo

CloudAMQP Dashboard

Ve a https://customer.cloudamqp.com para ver:

  • Mensajes en video-rendering queue
  • Consumers activos
  • Throughput

Logs

pm2 logs render-worker
pm2 logs clip-worker

Próximos Pasos

Para completar el sistema de rendering:

  1. ✅ Cola VIDEO_RENDERING creada
  2. ✅ Auto-encolado cuando video READY
  3. Falta: Crear render worker
  4. Falta: Integrar Remotion o ffmpeg
  5. Falta: Subir videos a S3 o storage

¡El sistema está listo para que implementes el render worker! 🎬