← Manual

Integración del visor y API

Diseña tu propia web, consulta el catálogo dinámico e incorpora el reproductor correspondiente.

Contrato revisado: 8 de noviembre de 2026 · Streaming 4174 · Ejemplos sin credenciales reales

Inserción del visor público

<iframe
  src="https://TU-SERVIDOR-ELI/embed"
  title="Transmisiones"
  allow="autoplay; fullscreen; encrypted-media; picture-in-picture"
  allowfullscreen
  style="display:block;width:100%;height:80vh;border:0">
</iframe>

Integración recomendada: tu diseño, nuestro reproductor

Tu aplicación consulta GET /api/videos desde su backend, presenta los deportes, ligas y eventos con su propio diseño y abre el campo reproductor del evento en un iframe. El identificador y la URL deben salir de la respuesta actual: no inventar IDs ni guardar URLs de segmentos como enlaces permanentes.

La placa LIVE y el subtítulo pertenecen al reproductor HLS de ELI. Se ajustan al contenedor y a pantalla completa. El deporte proviene de los metadatos; si no está informado, muestra «Programación en vivo». No hay reconocimiento automático del deporte mediante imágenes.

Los perfiles de posición medidos corresponden a ESPN, Fox Sports y TNT Sports. Otros canales usan una posición genérica: no equivale a detectar todas las marcas. Las rutas externas TB4/NanoPlayer y TB5/Perform, y el PiP nativo, no tienen esta capa verificada. Un archivo HLS no lleva el logo grabado en sus fotogramas.

Tres formas de mostrar el contenido

1. Visor público completo: iframe a /embed. Incluye búsqueda, deporte, liga y selector de evento. /embed?evento=ID permite comenzar con un evento del catálogo.

2. Un evento dentro de tu diseño: iframe al campo reproductor de GET /api/videos. Usa el campo devuelto, especialmente con TB4: no todas las fuentes pasan por la misma ruta.

3. API sin interfaz: GET /api/videos, /api/deportes y /api/senal/ID. Sirve para integrar metadatos y consultar señales. La reproducción HLS directa en un reproductor de otro dominio no está soportada por el proxy actual; no conserva LIVE por sí sola.

La página / es el panel de operación local, con rutas internas. Para visitantes de otro equipo se recomienda /embed, no el panel administrativo.

Dirección del servidor y acceso

Los ejemplos usan BASE como la dirección de ELI accesible al visitante. http://127.0.0.1:4174 solo funciona en el mismo PC: en el PC de tu amigo apuntaría a su propio equipo.

Para publicar: usar una dirección HTTPS accesible, proteger las rutas de administración en un frontal y mantener las rutas internas privadas. Cambiar únicamente el host a 0.0.0.0 no constituye un despliegue protegido. Este trabajo no publica el servidor en Internet.

La API pública exige X-API-Key cuando la llamada no viene del mismo origen. La clave local está en datos/clave-api.txt. Mantenerla en variables de entorno del backend; no copiarla a JavaScript público ni al iframe. CORS de la API admite GET, HEAD, OPTIONS y X-API-Key; no es un permiso para publicar operaciones internas.

Dentro del iframe ELI las consultas son del mismo origen. No añadas interno=1 a enlaces públicos. No hay un control de acceso por usuario implementado solo por incluir una clave en el backend: el acceso al visor debe definirse también en el despliegue.

Catálogo y filtros públicos

GET /api/estado: identificación del servicio; no requiere clave.

GET /api/videos: {total, videos, deportes, barrido_tb4}. Devuelve señales verificadas recientemente; catálogo vacío no significa error de formato.

GET /api/eventos: filtra eventos; incluye videos y alias eventos.

GET /api/canales: filtra canales; incluye videos y alias canales.

GET /api/deportes: {total_deportes, deportes}; cada deporte contiene deporte_id, deporte, videos y ligas; cada liga contiene liga_id, liga, videos.

GET /api/videos/ID, /api/eventos/ID, /api/canales/ID: detalle de una entrada disponible. Preferir evento_id recibido de la API pública.

GET /api/senal/ID: {evento_id, nombre, deporte, liga, inicio_ms, senales}. Cada señal tiene id, label, tipo y sus campos de reproducción. No garantiza que el proveedor siga emitiendo después de la consulta.

Filtros admitidos en el catálogo público: deporte, deporte_id, liga, liga_id, q, nombre, canal. deporte, liga, nombre y canal comparan valores normalizados, sin distinguir mayúsculas ni acentos; q busca todas las palabras sin exigir su orden, sin distinguir mayúsculas ni acentos. Busca en título, equipos local y visitante, participantes, país, liga, deporte, fuente e identificadores publicados. Ignora separadores como vs, versus y contra. Por ejemplo, q=Alianza%20Chankas, q=Chankas%20vs%20Alianza y q=Peru%20Alianza encuentran el mismo evento cuando esos datos están publicados. No deduce datos ausentes ni elimina los demás filtros seleccionados. Los filtros se combinan con AND. Usa URLSearchParams para codificarlos.

fuente, salud e incluir_no_disponibles NO están admitidos en la API pública. Están reservados para vistas locales. No enviar el emoji o imagen del icono como parte del valor: usar deporte y liga originales.

La lista de categorías debe construirse desde cada respuesta; no fijar un conjunto de deportes o un número de canales. Los IDs de categorías desconocidas pueden compartirse: conservar también el nombre. Las ligas públicas usan IDs lg_…; no mezclarlos con identificadores del panel interno.

Campos y disponibilidad

evento_id: identificador para llamadas públicas; conservar como cadena. nombre: título del evento o del canal. deporte/deporte_id y liga/liga_id: clasificación, no lectura del contenido del video. clase: canal o evento. inicio_ms: instante Unix en milisegundos, 0 cuando no se informa. reproductor: URL lista para iframe. embed: URL de inserción devuelta por esa fuente. senales: URL de consulta de señales.

En canales generales como ESPN, no se puede deducir el partido o deporte actual solo por el nombre. programacion_disponible=false y evento_actual=null indican ausencia de esa información.

El JSON local incluye pendientes y fuentes en reposo: no usar su cantidad como número de videos reproduciéndose. TV1/TV5 pueden estar en reposo. TB6 está registrado como enlace cifrado que requiere licencia autorizada; no está certificado como reproducible.

Actualización sin sobrecargas

No comprobar todos los videos por cada visitante. ELI realiza las comprobaciones y comparte sus resultados. La web externa puede reutilizar el catálogo durante 30 segundos y consultar cada 45–60 segundos con un pequeño desfase aleatorio.

No volver a asignar src al iframe en cada actualización del catálogo. Cambiarlo solo cuando el usuario elige otro evento. Si una consulta falla, indicar el error; no etiquetar datos antiguos como comprobación nueva. Las entradas pueden desaparecer o incorporarse durante el día.

El ejemplo incluido usa caché compartida y un bloqueo para que varias solicitudes simultáneas no disparen varias consultas al origen. Los reintentos del reproductor siguen siendo finitos.

Video directo, sesiones y cabeceras

Las URLs /v/TOKEN/m.m3u8 y /v/TOKEN/r/N son temporales. El token está asociado a IP y User-Agent del cliente que lo obtuvo. Se elimina tras 180 segundos sin uso; la reproducción continua renueva su actividad. No entregar al navegador un token obtenido desde otro backend esperando que funcione igual.

El proxy rechaza solicitudes de reproducción cross-site y aplica política same-origin. No se soluciona pasando la clave a una etiqueta video. Para mantener el diseño LIVE y las protecciones actuales, usar el iframe devuelto por el catálogo.

Las cabeceras de proveedor se gestionan en el servidor. No son configuración pública del consumidor. El sistema no renueva automáticamente firmas de proveedores sin una integración de renovación ni omite DRM.

Los mensajes eli:playback del reproductor están destinados al mismo origen; no hay callbacks públicos cross-origin documentados. Un evento load del iframe no demuestra avance del video.

JSON, remoto y salas: operaciones locales

GET /api/json o /api/json/total: catálogo local agrupado por fuente y estado de salas.

GET /api/json/fuente/TB1 … TB9 o /api/json/fuente/todas: entradas por fuente. Permite filtros fuente/deporte/deporte_id/liga/liga_id/q/nombre/canal/salud según el catálogo local.

GET /api/json/sala/sala1: selección y catálogo de la sala.

POST /api/json/sala/sala1 con {"fuente":"TB2","evento_id":"ID_INTERNO"}: selecciona un evento del catálogo local. Con {"fuente":"todas","evento_id":null}: deja la sala sin selección. Para opciones TB4/TB5 puede añadirse variante_id válido.

/sala?player=sala1: pantalla de esa sala; /remoto: operación local. Las rutas /api/json se limitan a loopback y los POST rechazan orígenes externos. Las rutas /interno exigen además Sec-Fetch-Site same-origin. No falsificar esas cabeceras para consumirlas desde otro servidor.

Los identificadores del JSON local y los de /api/videos no son intercambiables. La clave API no convierte el control de sala local en API remota pública.

Errores que debe manejar el consumidor

400: filtro inválido, duplicado, no admitido o identificador mal formado. 401: clave pública ausente/incorrecta. 403: ruta local, origen o sesión de reproducción no admitidos. 404: evento no disponible o ruta inexistente. 409: selección incompatible o identificador ambiguo. 410 en /v: recurso/token expirado. 502/504: origen o comunicación no disponible.

Si GET /api/videos devuelve 200 y videos=[], mostrar «No hay transmisiones disponibles en este momento» y consultar después. No sustituirlo por un partido de ejemplo. Ante un error de red, no iniciar bucles rápidos de recarga.

Ejemplo ejecutable incluido

La carpeta ejemplos/integracion contiene servidor.py e index.html. Usa aiohttp, ya instalado en el entorno del proyecto. Configura ELI_URL y ELI_API_KEY en el entorno del backend y ejecuta servidor.py. Abre http://127.0.0.1:8891.

El ejemplo muestra una página propia con filtros de deporte y liga, obtiene el catálogo del backend /catalogo y abre el reproductor en iframe. La clave no se incorpora al HTML ni al catálogo. Escucha solo en loopback y no se inicia automáticamente. Es una base de integración, no una publicación en Internet.

Ejecutar el ejemplo desde la carpeta del proyecto

# En una consola del servidor del ejemplo; usa tus valores privados.
$env:ELI_URL = 'http://127.0.0.1:4174'
$env:ELI_API_KEY = 'TU_CLAVE_PRIVADA'
.\.venv\Scripts\python.exe ejemplos/integracion/servidor.py
# Abrir http://127.0.0.1:8891
# Detener solo el ejemplo con Ctrl+C.

Validación realizada

8 de noviembre de 2026: 231 pruebas del proyecto sin fallos; 26 comprobaciones HTTP del servidor local, incluidos autenticación, preflight, filtros, detalle y señales. El catálogo público devolvió 66 entradas en esa consulta; este número es dinámico y no significa que se hayan reproducido simultáneamente las 66.

El ejemplo se ejecutó temporalmente en el puerto 8891: consultó el catálogo, abrió un iframe del origen 4174 e incorporó los elementos LIVE y subtítulo. Se verificó que la clave no apareciera en el HTML. Se detuvo el servidor de ejemplo al terminar; el servicio de streaming continúa. Las pruebas de la capa y la geometría no certifican disponibilidad permanente de los proveedores.

Detalle del evento en la señal

La respuesta de /api/senal/{id} conserva los datos del catálogo: deporte_id, deporte, liga_id, liga, pais, pais_codigo, ambito, participantes, enfrentamiento y detalle_evento. origen_participantes distingue metadatos publicados de nombres extraídos del título. Un canal multideporte sin programación no permite identificar quién juega. Estos campos describen el evento; no confirman que el video esté reproduciendo. Los permisos y criterios de disponibilidad de la API no cambian.