DEV Community

Robert
Robert

Posted on

Construyendo un Sistema de Streaming Local con Node.js, React y FFmpeg

No he escrito en dev.to desde el 2022. Ha habido muchos cambios en estos 4 años. Nuevos empleos, nuevos proyectos, nuevas tecnologías, etc. He regresado con un proyecto que me ha causado mucho interes

Tabla de Contenido

Introducción

¿Alguna vez has querido eliminar una o varias pistas de audio de un video?

Quizas tienes una pelicula o el episodio de alguna serie con mas de un audio pero quieres conservarlo solo con la pista de audio que te interesa. O tambien esta la posibilidad de que quisieras dejarlo sin pistas de audio para poder editarlo o por motivos de accesibilidad

para esa cuestión ejecutaba un comando como este:

ffmpeg -i test-video.1080p-Dual.mkv -map 0:v -map 0:a:1 -c copy D:test-video.es.mkv
Enter fullscreen mode Exit fullscreen mode

¿Qué hace el comando?

Parte Explicación
ffmpeg El programa que ejecuta el comando
-i test-video.1080p-Dual.mkv Archivo de entrada (el video original)
-map 0:v Selecciona todas las pistas de video del archivo 0
-map 0:a:1 Selecciona la pista de audio número 1 del archivo 0 (en este caso, español)
-c copy Copia los codecs (sin re-codificar)
D:test-video.es.mkv Archivo de salida (el video procesado)

El Problema

Los archivos de video suelen contener múltiples pistas de audio:

  • Diferentes idiomas (inglés, español, francés, etc.)
  • Pistas de comentarios
  • Descripciones de audio para accesibilidad
  • Múltiples formatos o bitrates

La mayoría de los editores de video son excesivos para esta tarea. Quería una herramienta ligera, rápida y gratuita que:

  • Funcione localmente (sin subir archivos a la nube)
  • Procese varios videos a la vez
  • Muestre el progreso en tiempo real
  • Recuerde las selecciones de pistas por video
  • Tenga una interfaz limpia y fácil de usar

Tecnologías

Capa (Layer) Tecnología
Frontend React, TypeScript, Vite
Backend Node.js, Express, TypeScript
Procesamiento de Video FFmpeg (fluent-ffmpeg)
Actualizaciones en Tiempo Real Server-Sent Events (SSE)
Colas Cola de procesamiento en memoria
Tests Vitest

Retos y Soluciones

1. Procesar videos sin subirlos

Como esta es una herramienta local, no quería subir archivos a un servidor. El backend lee los videos directamente del sistema de archivos usando la ruta configurada en .env.

// config.ts
const getVideoFolder = (): string => {
    const envPath = process.env.VIDEO_FOLDER_PATH;
    return envPath ? path.resolve(envPath) : path.join(__dirname, '../videos');
};
Enter fullscreen mode Exit fullscreen mode

¿Por qué importa? La herramienta es 100% local. No hay subidas, no hay límites de tamaño, no hay dependencia de internet y tus archivos nunca salen de tu computadora.

2. Progreso en tiempo real con SSE

FFmpeg emite eventos de progreso, pero necesitaba transmitirlos al frontend. Elegí Server-Sent Events en lugar de WebSockets porque es más simple para comunicación unidireccional.

// Backend - emitiendo eventos de progreso
command.on('progress', (progress) => {
    const percent = Math.round(progress.percent || 0);
    this.emit('job-progress', item, percent);
});
Enter fullscreen mode Exit fullscreen mode
// Frontend - escuchando eventos
const eventSource = new EventSource(`/api/events?jobId=${jobId}`);
eventSource.onmessage = (event) => {
    const data = JSON.parse(event.data);
    setProcessProgress(data.progress);
};
Enter fullscreen mode Exit fullscreen mode

¿Por qué SSE? Está integrado en los navegadores, maneja reconexiones automáticamente y es perfecto para actualizaciones servidor-cliente.

3. Persistencia de selección de pistas por video

Necesitaba recordar qué pistas seleccionó el usuario para cada video. Usé un Map en el estado del frontend:

const [selectedTracksMap, setSelectedTracksMap] = useState<Map<string, number[]>>(new Map());
Enter fullscreen mode Exit fullscreen mode

Al cambiar entre videos, las pistas seleccionadas/deseleccionadas se mantienen.

4. Procesamiento por lotes con cola

Procesar varios videos simultáneamente saturaría el sistema. Por lo que implementé una cola simple en memoria

5. Manejo de videos sin pistas de audio

Quise colocar la opción de poder eliminar todas las pistas de audio solo para probar. El backend soporta tracksToKeep: [] usando el flag -an de FFmpeg.

Conclusión

Este proyecto empezo como una solucion personal a un problema, ejecutar manualmente comandos para eliminar pistas de audio o tener que crear scripts para lotes de videos era muy repetitivo. Este proyecto termino siendo un ejercicio practico para construir una aplicacion local.

Lo que hace este proyecto interesante:

  • Integracion con FFmpeg – Trabajar con una herramienta CLI desde Node.js me enseñó a como manejar subprocesos, analiza la salida, y streamear eventos de progreso.
  • comunicación en tiempo real – Los eventos Server-sent demostraron ser una simple pero efectiva alternativa a websockets, es el segundo proyecto donde hago uso de esa herramienta.
  • Architectura – Separar el frontend en componentes y custom hooks, y el backend en controladores y servicios hace que el codigo sea mantenible y escalable.
  • Local-first – Desde mi punto de vista tiene mucho valor haber construido una herramienta útil como esta que se ejecuta completamente en la máquina del usuario sin depender de la nube.

El proyecto es de código abierto. Siéntete libre de usarlo, modificarlo, o aprender de el.

GitHub Repository

Video Demo

Contacto

Si te ha parecido interesante este proyecto y estás buscando un desarrollador Full Stack, no dudes en contactarme.

¡Gracias por leer! 🙌

Top comments (0)