docs: añadir README completo con arquitectura, instalación y notas técnicas
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,496 @@
|
||||
# KiraStream
|
||||
|
||||
Servidor de gestión de líneas IPTV compatible con el protocolo **Xtream Codes**. Permite administrar proveedores, usuarios, catálogos y monitorización en tiempo real desde un panel web, sirviendo los streams a cualquier reproductor IPTV estándar.
|
||||
|
||||
---
|
||||
|
||||
## Índice
|
||||
|
||||
- [Características](#características)
|
||||
- [Arquitectura](#arquitectura)
|
||||
- [Requisitos](#requisitos)
|
||||
- [Instalación](#instalación)
|
||||
- [Configuración](#configuración)
|
||||
- [Estructura del proyecto](#estructura-del-proyecto)
|
||||
- [API](#api)
|
||||
- [Panel de administración](#panel-de-administración)
|
||||
- [Gestión del servicio](#gestión-del-servicio)
|
||||
- [Registro y monitorización](#registro-y-monitorización)
|
||||
- [Notas técnicas de estabilidad](#notas-técnicas-de-estabilidad)
|
||||
|
||||
---
|
||||
|
||||
## Características
|
||||
|
||||
- **Proxy de streams en tiempo real** — reencaminamiento de canales en directo mediante ffmpeg en modo copy (sin recodificación). Fan-out a múltiples clientes sobre una única conexión upstream.
|
||||
- **API compatible con Xtream Codes** — funciona con IPTV Smarters, TiviMate, Perfect Player, VLC, Kodi y cualquier reproductor que soporte el protocolo Xtream.
|
||||
- **Gestión de proveedores** — múltiples cuentas, múltiples dominios/CDN por cuenta con failover automático, sincronización de catálogos y health check cada 5 minutos.
|
||||
- **Gestión de usuarios** — usuarios independientes con límite de conexiones simultáneas, prioridad, fecha de expiración y catálogos personalizados.
|
||||
- **EPG integrado** — fusión de múltiples fuentes XMLTV, filtrado por canal y servicio en `/xmltv.php`.
|
||||
- **VOD y Series** — proxy transparente para películas y series con soporte de `Range` (seek).
|
||||
- **Integración Jellyfin** — biblioteca local de películas y series desde un servidor Jellyfin.
|
||||
- **Monitorización en tiempo real** — WebSocket con métricas de streams, clientes, CPU de ffmpeg, salud A/V, buffer y reconexiones.
|
||||
- **Registro de fallos** — eventos de caída y recuperación de streams persistidos en base de datos y visibles en el panel.
|
||||
- **Reconexión transparente** — al caer un proveedor el buffer de cola del servidor absorbe el tiempo de reconexión; los clientes no ven pantalla negra.
|
||||
|
||||
---
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
Cliente IPTV (TV/móvil/PC)
|
||||
│ HTTP TS stream
|
||||
▼
|
||||
Nginx :80
|
||||
│ proxy_pass
|
||||
▼
|
||||
FastAPI / Uvicorn :8000
|
||||
│
|
||||
├── ProviderPool (pool.py)
|
||||
│ └── BroadcastGroup × canal activo
|
||||
│ └── ffmpeg (copy mode) ← proveedor IPTV
|
||||
│
|
||||
├── VodTracker (vod_tracker.py) ← películas / series
|
||||
├── EPGManager (epg_manager.py) ← XMLTV fusion
|
||||
└── HealthChecker ← ping proveedores c/5min
|
||||
|
||||
PostgreSQL 17 ←→ SQLAlchemy async (asyncpg)
|
||||
Redis ←→ pub/sub monitorización + slots de proveedor
|
||||
```
|
||||
|
||||
### Flujo de un canal en directo
|
||||
|
||||
1. El reproductor solicita `GET /{user}/{pass}/{stream_id}`.
|
||||
2. `ProviderPool.acquire()` busca o crea un `BroadcastGroup` para ese canal.
|
||||
3. El grupo lanza un proceso ffmpeg que lee del proveedor y escribe MPEG-TS en stdout.
|
||||
4. Cada chunk (64 KB) se despacha a las colas asyncio de todos los clientes conectados.
|
||||
5. El `StreamingResponse` de FastAPI lee de la cola y lo envía al reproductor via HTTP chunked.
|
||||
6. Cuando el proveedor cierra la sesión (~2 min), ffmpeg muere limpiamente y el loop de Python lo reinicia con una conexión fresca. El buffer de cola cubre el tiempo de reconexión.
|
||||
|
||||
---
|
||||
|
||||
## Requisitos
|
||||
|
||||
| Componente | Versión mínima |
|
||||
|---|---|
|
||||
| Python | 3.11+ (probado en 3.13) |
|
||||
| PostgreSQL | 14+ (probado en 17) |
|
||||
| Redis | 6+ |
|
||||
| Nginx | 1.18+ |
|
||||
| ffmpeg | 5+ (probado en 7.1) |
|
||||
| Node.js | 18+ (solo para compilar el frontend) |
|
||||
|
||||
---
|
||||
|
||||
## Instalación
|
||||
|
||||
### 1. Dependencias del sistema
|
||||
|
||||
```bash
|
||||
apt install -y python3 python3-venv python3-pip \
|
||||
postgresql redis-server nginx ffmpeg \
|
||||
nodejs npm
|
||||
```
|
||||
|
||||
### 2. Clonar el repositorio
|
||||
|
||||
```bash
|
||||
git clone https://git.kiracloud.es/joaquin/KiraTV.git /opt/kirastream
|
||||
cd /opt/kirastream
|
||||
```
|
||||
|
||||
### 3. Entorno Python
|
||||
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install fastapi uvicorn[standard] sqlalchemy[asyncio] asyncpg \
|
||||
aiohttp redis pydantic-settings alembic python-jose \
|
||||
passlib python-multipart
|
||||
```
|
||||
|
||||
### 4. Base de datos
|
||||
|
||||
```bash
|
||||
# Crear usuario y base de datos en PostgreSQL
|
||||
su - postgres -c "createuser kirastream"
|
||||
su - postgres -c "createdb -O kirastream kirastream"
|
||||
su - postgres -c "psql -c \"ALTER USER kirastream PASSWORD 'kirastream_secret_2024';\""
|
||||
```
|
||||
|
||||
Las tablas se crean automáticamente al arrancar la aplicación (`Base.metadata.create_all`).
|
||||
|
||||
### 5. Variables de entorno
|
||||
|
||||
Crear `/opt/kirastream/.env`:
|
||||
|
||||
```env
|
||||
DATABASE_URL=postgresql+asyncpg://kirastream:kirastream_secret_2024@localhost/kirastream
|
||||
REDIS_URL=redis://localhost:6379/0
|
||||
SECRET_KEY=cambia-esto-por-una-clave-segura
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=cambia-esto
|
||||
LOG_LEVEL=INFO
|
||||
```
|
||||
|
||||
### 6. Compilar el frontend
|
||||
|
||||
```bash
|
||||
cd /opt/kirastream/frontend
|
||||
npm install
|
||||
npm run build
|
||||
# Los archivos estáticos se generan en backend/static/
|
||||
```
|
||||
|
||||
### 7. Nginx
|
||||
|
||||
Copiar la configuración al directorio de Nginx:
|
||||
|
||||
```bash
|
||||
cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak
|
||||
```
|
||||
|
||||
Contenido de `/etc/nginx/sites-enabled/default`:
|
||||
|
||||
```nginx
|
||||
upstream kirastream_backend {
|
||||
server 127.0.0.1:8000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
|
||||
client_max_body_size 20m;
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
proxy_connect_timeout 10s;
|
||||
|
||||
location /panel/ {
|
||||
alias /opt/kirastream/backend/static/;
|
||||
try_files $uri $uri/ /panel/index.html;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
|
||||
location = / { return 302 /panel/; }
|
||||
|
||||
location / {
|
||||
proxy_pass http://kirastream_backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection upgrade;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
add_header X-Accel-Buffering no always;
|
||||
}
|
||||
|
||||
location /panel/assets/ {
|
||||
alias /opt/kirastream/backend/static/assets/;
|
||||
expires 30d;
|
||||
add_header Cache-Control 'public, immutable';
|
||||
}
|
||||
|
||||
gzip on;
|
||||
gzip_types application/json application/javascript text/css text/plain application/x-mpegurl;
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
### 8. Servicio systemd
|
||||
|
||||
Crear `/etc/systemd/system/kirastream.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=KiraStream IPTV Server
|
||||
After=network.target postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/kirastream
|
||||
|
||||
ExecStartPre=/bin/bash -c 'pg_ctlcluster 17 main start 2>/dev/null; true'
|
||||
ExecStartPre=/bin/bash -c 'redis-cli ping 2>/dev/null || (LANG=C LC_ALL=C redis-server --port 6379 --bind 127.0.0.1 --daemonize yes --dir /var/lib/redis/ && sleep 1); true'
|
||||
ExecStartPre=/bin/bash -c 'pkill -9 uvicorn 2>/dev/null; sleep 1; true'
|
||||
|
||||
ExecStart=/opt/kirastream/venv/bin/uvicorn backend.main:app \
|
||||
--host 0.0.0.0 --port 8000 --workers 1 --log-level info
|
||||
|
||||
StandardOutput=append:/opt/kirastream/logs/backend.log
|
||||
StandardError=append:/opt/kirastream/logs/backend.log
|
||||
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
KillMode=control-group
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/kirastream/logs
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now kirastream
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuración
|
||||
|
||||
Todas las variables se leen de `/opt/kirastream/.env` (o variables de entorno del sistema):
|
||||
|
||||
| Variable | Por defecto | Descripción |
|
||||
|---|---|---|
|
||||
| `DATABASE_URL` | `postgresql+asyncpg://kirastream:...@localhost/kirastream` | URL de conexión a PostgreSQL |
|
||||
| `REDIS_URL` | `redis://localhost:6379/0` | URL de Redis |
|
||||
| `SECRET_KEY` | *(cambiar)* | Clave para firmar tokens JWT |
|
||||
| `ALGORITHM` | `HS256` | Algoritmo JWT |
|
||||
| `ACCESS_TOKEN_EXPIRE_MINUTES` | `10080` (7 días) | TTL de tokens de admin |
|
||||
| `ADMIN_USERNAME` | `admin` | Usuario del panel de administración |
|
||||
| `ADMIN_PASSWORD` | `kiraadmin` | Contraseña del panel (**cambiar en producción**) |
|
||||
| `STREAM_CHUNK_SIZE` | `65536` | Tamaño de chunk en bytes (64 KB) |
|
||||
| `STREAM_QUEUE_MAXSIZE` | `512` | Chunks máximos en cola por cliente (≈ 32 MB) |
|
||||
| `STREAM_CONNECT_TIMEOUT` | `10` | Timeout de conexión ffmpeg (segundos) |
|
||||
| `STREAM_READ_TIMEOUT` | `30` | Timeout de lectura (segundos) |
|
||||
| `EPG_REFRESH_HOURS` | `12` | Intervalo de actualización de EPG |
|
||||
| `M3U_CACHE_MINUTES` | `30` | Caché de playlist M3U |
|
||||
| `LOG_LEVEL` | `INFO` | Nivel de log (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |
|
||||
|
||||
---
|
||||
|
||||
## Estructura del proyecto
|
||||
|
||||
```
|
||||
/opt/kirastream/
|
||||
├── backend/
|
||||
│ ├── main.py # Punto de entrada FastAPI, lifespan, routers
|
||||
│ ├── config.py # Settings (pydantic-settings + .env)
|
||||
│ ├── database.py # Engine async, Base, init_db()
|
||||
│ ├── api/
|
||||
│ │ ├── auth.py # Login JWT para el panel admin
|
||||
│ │ ├── xtream.py # API pública compatible Xtream Codes
|
||||
│ │ └── admin/
|
||||
│ │ ├── monitoring.py # WebSocket tiempo real + endpoints slots/streams
|
||||
│ │ ├── providers.py # CRUD proveedores, sync catálogos, health check
|
||||
│ │ ├── users.py # CRUD usuarios
|
||||
│ │ ├── catalogs.py # Catálogos y categorías
|
||||
│ │ ├── custom_categories.py # Categorías personalizadas
|
||||
│ │ ├── logs.py # Registro de conexiones y fallos de stream
|
||||
│ │ ├── epg_admin.py # Gestión EPG
|
||||
│ │ ├── jellyfin_admin.py # Integración Jellyfin
|
||||
│ │ └── settings.py # Configuración del servidor
|
||||
│ ├── core/
|
||||
│ │ ├── restream.py # BroadcastGroup: ffmpeg pump + fan-out a clientes
|
||||
│ │ ├── pool.py # ProviderPool: slots, adquisición y liberación
|
||||
│ │ ├── health_checker.py # Ping periódico a URLs de proveedor
|
||||
│ │ ├── epg_manager.py # Descarga y fusión de fuentes XMLTV
|
||||
│ │ ├── catalog.py # Consultas de catálogo por usuario
|
||||
│ │ ├── probe.py # ffprobe para info de stream (codec, resolución)
|
||||
│ │ ├── vod_tracker.py # Seguimiento de sesiones VOD/series
|
||||
│ │ ├── xtream_client.py # Cliente HTTP para API Xtream de proveedores
|
||||
│ │ └── jellyfin_client.py # Cliente HTTP para Jellyfin
|
||||
│ ├── models/
|
||||
│ │ ├── provider.py # ProviderAccount, ProviderUrl
|
||||
│ │ ├── channel.py # Channel (live/movie/series)
|
||||
│ │ ├── user.py # User
|
||||
│ │ ├── log.py # ConnectionLog
|
||||
│ │ ├── stream_event.py # StreamEvent (fallos y recuperaciones)
|
||||
│ │ ├── epg.py # EPGSource
|
||||
│ │ ├── custom_category.py # CustomCategory, CustomCategoryItem
|
||||
│ │ ├── notification.py # Notification
|
||||
│ │ └── jellyfin.py # JellyfinConfig, JellyfinItem
|
||||
│ └── static/ # Frontend compilado (servido por Nginx)
|
||||
├── frontend/
|
||||
│ ├── src/
|
||||
│ │ ├── pages/
|
||||
│ │ │ ├── Dashboard.tsx # Monitorización en tiempo real
|
||||
│ │ │ ├── Logs.tsx # Registro de conexiones y fallos
|
||||
│ │ │ ├── Providers.tsx # Gestión de proveedores
|
||||
│ │ │ ├── Users.tsx # Gestión de usuarios
|
||||
│ │ │ ├── Catalogs.tsx # Catálogos
|
||||
│ │ │ ├── CustomCategories.tsx
|
||||
│ │ │ ├── EPG.tsx
|
||||
│ │ │ ├── Jellyfin.tsx
|
||||
│ │ │ └── Settings.tsx
|
||||
│ │ ├── components/
|
||||
│ │ │ └── Layout.tsx # Navegación lateral
|
||||
│ │ └── lib/
|
||||
│ │ └── api.ts # Cliente axios con autenticación JWT
|
||||
│ ├── package.json
|
||||
│ └── vite.config.ts
|
||||
├── nginx/ # Configuración de Nginx
|
||||
├── scripts/
|
||||
│ └── install.sh # Script de arranque manual
|
||||
├── logs/ # Logs de la aplicación (no versionados)
|
||||
├── .env # Variables de entorno (no versionado)
|
||||
└── .gitignore
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
### Autenticación del panel
|
||||
|
||||
```
|
||||
POST /api/admin/auth/token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
username=admin&password=...
|
||||
```
|
||||
|
||||
Devuelve un token JWT que se incluye en las siguientes peticiones como `Authorization: Bearer <token>`.
|
||||
|
||||
### API Xtream Codes (pública)
|
||||
|
||||
| Endpoint | Descripción |
|
||||
|---|---|
|
||||
| `GET /player_api.php?username=&password=&action=` | API principal Xtream |
|
||||
| `GET /get.php?username=&password=&type=m3u_plus` | Playlist M3U |
|
||||
| `GET /xmltv.php?username=&password=` | EPG en formato XMLTV |
|
||||
| `GET /{user}/{pass}/{stream_id}` | Stream en directo |
|
||||
| `GET /live/{user}/{pass}/{stream_id}` | Stream en directo (ruta alternativa) |
|
||||
| `GET /movie/{user}/{pass}/{stream_id}.mkv` | VOD |
|
||||
| `GET /series/{user}/{pass}/{episode_id}.mkv` | Episodio de serie |
|
||||
|
||||
### API de administración
|
||||
|
||||
| Endpoint | Descripción |
|
||||
|---|---|
|
||||
| `GET /api/admin/monitoring/streams` | Lista streams activos |
|
||||
| `GET /api/admin/monitoring/slots` | Estado de slots de proveedor |
|
||||
| `WS /api/admin/monitoring/ws` | Actualizaciones en tiempo real |
|
||||
| `POST /api/admin/monitoring/kill` | Cortar conexión de un cliente |
|
||||
| `GET /api/admin/logs` | Registro de conexiones de usuarios |
|
||||
| `GET /api/admin/logs/stats` | Estadísticas agregadas |
|
||||
| `GET /api/admin/logs/events` | Registro de fallos y recuperaciones de stream |
|
||||
| `DELETE /api/admin/logs` | Limpiar registro de conexiones |
|
||||
| `DELETE /api/admin/logs/events` | Limpiar registro de fallos |
|
||||
| `GET /api/admin/providers` | Lista de proveedores |
|
||||
| `POST /api/admin/providers` | Añadir proveedor |
|
||||
| `GET /api/admin/users` | Lista de usuarios |
|
||||
| `POST /api/admin/users` | Crear usuario |
|
||||
| `GET /api/docs` | Documentación interactiva (Swagger UI) |
|
||||
|
||||
---
|
||||
|
||||
## Panel de administración
|
||||
|
||||
Accesible en `http://<servidor>/panel/`
|
||||
|
||||
### Dashboard
|
||||
|
||||
Monitorización en tiempo real vía WebSocket. Muestra por cada stream activo:
|
||||
|
||||
- Canal, proveedor y tipo (Directo / Película / Serie)
|
||||
- Resolución, FPS, códecs de vídeo y audio, bitrate
|
||||
- Estado de conexión y número de reconexiones
|
||||
- Salud A/V y correcciones automáticas
|
||||
- **Cola cliente** — porcentaje de cola del cliente más lento (0 % = consumo sin retraso; 100 % = cliente bloqueado)
|
||||
- **Reserva** — capacidad del buffer del servidor (32 MB). Muestra `ok` en reposo (normal) y los segundos de cobertura si hay datos en cola
|
||||
- Usuarios conectados con opción de cortar conexión individualmente
|
||||
- Métricas globales: streams activos, slots ocupados, ancho de banda total, CPU de ffmpeg
|
||||
|
||||
### Registro
|
||||
|
||||
Dos pestañas:
|
||||
|
||||
**Conexiones de usuarios** — historial de todas las sesiones con usuario, IP, canal, tipo, duración y datos transferidos. Filtros por usuario, canal, tipo y rango de fechas.
|
||||
|
||||
**Fallos de stream** — registro persistente de eventos del sistema: caídas de proveedor y recuperaciones automáticas. Cada evento incluye canal, proveedor, dominio que falló, mensaje de error y número de intento. Se genera automáticamente; no requiere intervención manual.
|
||||
|
||||
---
|
||||
|
||||
## Gestión del servicio
|
||||
|
||||
```bash
|
||||
# Estado
|
||||
systemctl status kirastream
|
||||
|
||||
# Reiniciar (necesario tras cambios en el backend)
|
||||
systemctl restart kirastream
|
||||
|
||||
# Ver logs en tiempo real
|
||||
tail -f /opt/kirastream/logs/backend.log
|
||||
|
||||
# Actualizar código y redeployar
|
||||
cd /opt/kirastream
|
||||
git pull
|
||||
cd frontend && npm run build && cd ..
|
||||
systemctl restart kirastream
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Registro y monitorización
|
||||
|
||||
### Logs de la aplicación
|
||||
|
||||
`/opt/kirastream/logs/backend.log` — log estructurado con nivel, módulo y mensaje. Rotación manual recomendada con `logrotate`.
|
||||
|
||||
### Eventos de stream
|
||||
|
||||
Los fallos y recuperaciones de streams se guardan en la tabla `stream_events` de PostgreSQL y son visibles en el panel en **Registro → Fallos de stream**.
|
||||
|
||||
Tipos de evento:
|
||||
|
||||
| Tipo | Descripción |
|
||||
|---|---|
|
||||
| `caida` | El stream falló por primera vez (ffmpeg no pudo conectar o el proveedor cerró la sesión) |
|
||||
| `recuperado` | Tras varios intentos fallidos, el stream estuvo estable más de 30 segundos |
|
||||
|
||||
### Health check de proveedores
|
||||
|
||||
El checker hace ping a cada URL de proveedor cada 5 minutos llamando a `/player_api.php?action=user_info` y actualiza el estado (`ok` / `error` / `timeout`) y la latencia en milisegundos. El resultado es visible en la sección Proveedores del panel.
|
||||
|
||||
---
|
||||
|
||||
## Notas técnicas de estabilidad
|
||||
|
||||
### Reconexión sin pantalla negra
|
||||
|
||||
Los proveedores IPTV cierran la sesión cada ~2 minutos como política de gestión de sesiones. Cuando ocurre:
|
||||
|
||||
1. ffmpeg detecta el cierre de la conexión TCP y termina.
|
||||
2. El loop de Python (`_pump_with_retry`) lanza inmediatamente un nuevo proceso ffmpeg con una conexión fresca al proveedor.
|
||||
3. **La cola del cliente NO se vacía durante la reconexión.** Los datos en cola (hasta 32 MB) siguen alimentando al reproductor mientras el nuevo ffmpeg arranca.
|
||||
4. El reproductor no experimenta pantalla negra si la reconexión es más rápida que el buffer local del reproductor.
|
||||
|
||||
> **Por qué no se usa `-reconnect_streamed` en ffmpeg:** ese flag hace que ffmpeg reconecte internamente sin morir. El proveedor re-envía desde el último keyframe (2-5 segundos atrás), lo que produce timestamps PTS hacia atrás que los reproductores interpretan como un rebobinado visible. La reconexión a nivel Python, en cambio, produce siempre una conexión limpia desde el punto de emisión actual.
|
||||
|
||||
### Buffer del servidor
|
||||
|
||||
La cola de cada `BroadcastGroup` tiene capacidad para 512 chunks de 64 KB = **32 MB por stream**. En operación normal la cola está casi vacía (el reproductor consume los datos a la misma velocidad que llegan); eso es correcto y no indica ausencia de protección. La protección real está en que la cola no se vacía activamente durante reconexiones.
|
||||
|
||||
### Fan-out eficiente
|
||||
|
||||
Un único proceso ffmpeg sirve a todos los clientes del mismo canal simultáneamente. Si 10 usuarios ven el mismo canal, solo hay una conexión upstream con el proveedor y un proceso ffmpeg. Los chunks se copian a la cola de cada cliente en el mismo dispatch loop.
|
||||
|
||||
### Evicción de clientes lentos
|
||||
|
||||
Si la cola de un cliente está llena durante más de 30 segundos continuados (red muy lenta o reproductor bloqueado), el cliente es desconectado automáticamente para no perjudicar a otros clientes del mismo stream.
|
||||
|
||||
---
|
||||
|
||||
## Stack tecnológico
|
||||
|
||||
| Capa | Tecnología |
|
||||
|---|---|
|
||||
| Backend | Python 3.13, FastAPI 0.115, Uvicorn 0.34 |
|
||||
| ORM | SQLAlchemy 2.x async + asyncpg 0.30 |
|
||||
| Base de datos | PostgreSQL 17 |
|
||||
| Cache / pub-sub | Redis 6+ (redis-py 5.3) |
|
||||
| Proxy HTTP | aiohttp 3.12 |
|
||||
| Procesado de stream | ffmpeg 7.1 (copy mode, sin recodificación) |
|
||||
| Validación | Pydantic 2.13 |
|
||||
| Frontend | React 18, TypeScript, Vite 6, Tailwind CSS |
|
||||
| Servidor web | Nginx (proxy inverso + archivos estáticos) |
|
||||
| Proceso | systemd |
|
||||
Reference in New Issue
Block a user