ptp-proxy IPTV · archivos · nodos PTP

ptp-proxy — IPTV, archivos y procesamiento multimedia IPTV, almacenamiento y nodo multimedia

ptp-proxy reúne en un único servicio la entrega de canales IPTV, el restream compartido, la transcodificación, el túnel VOD, las grabaciones y la publicación segura de archivos locales o remotos. Puede ejecutarse por sí solo o conectarse como nodo a un servidor PTP.

¿Para qué sirve?
  • Ocultar las credenciales de proveedores IPTV y orígenes de archivos
  • Publicar canales HLS detrás de una URL propia
  • Compartir una sola conexión de restream entre varios reproductores
  • Aplicar perfiles de transcodificación de software o hardware
  • Tunelizar VOD y descargas con rangos y seek
  • Servir carpetas locales, discos y unidades montadas
  • Unificar WebDAV, SFTP, SMB, NFS, FTP/FTPS y catálogos HTTP
  • Conectar proveedores externos como S3, BitTorrent, aMule, rclone, IPFS o SABnzbd
  • Preparar contenido bajo demanda mediante operaciones asíncronas
  • Programar grabaciones de canales o URLs
  • Funcionar como nodo local o remoto administrado por PTP
  • Ejecutar como servicio de Windows o demonio de Linux
¿Cómo funciona?

El cliente utiliza URLs de ptp-proxy para canales, VOD o archivos. El servidor valida la identidad y los permisos, accede al origen configurado, transforma el contenido cuando hace falta y entrega una respuesta uniforme sin revelar secretos del proveedor.

Lo que NO es
  • No incluye canales, películas, series ni archivos de terceros
  • No descubre automáticamente servicios o credenciales: el administrador debe añadir las fuentes autorizadas
  • No sustituye a una VPN: puede usar una red o túnel existente, pero no lo crea
  • No convierte un addon en contenido público: todos los proveedores siguen sujetos a permisos, límites y políticas de fuente

Modos de funcionamiento

ModoQué resuelveFlujo de datosFFmpeg
Proxy HLSReescribe playlists, segmentos y claves sin recodificar el vídeo.Cada cliente mantiene su propia sesión con el origen.No
RestreamCopia o transcodifica un canal y distribuye una salida compartida.Una sesión por canal puede atender a varios clientes.Sí
Túnel VODEntrega archivos de vídeo remotos con tamaño, descarga y seek.El cliente recibe un flujo byte-range independiente.No
Servidor de archivosPublica archivos locales, montados o remotos mediante una API común.Cliente → ptp-proxy → fuente de almacenamiento.No
Preparación bajo demandaInicia un trabajo remoto y publica el resultado cuando está disponible.Cliente → operación → proveedor externo → objeto listo.Opcional
Nodo PTPExpone fuentes, perfiles y operaciones a un servidor PTP central.El nodo inicia la conexión de control; la entrega puede ser directa o mediante relay.Según la tarea

Qué puedes construir con ptp-proxy

Las mismas funciones pueden combinarse. Un equipo puede ser a la vez proxy IPTV, servidor de archivos, nodo de transcodificación y agente remoto de PTP.

Gateway IPTV privado

Publica playlists y streams con tus propias URLs y credenciales de acceso.

  • Proxy HLS
  • Claims HMAC
  • Cabeceras privadas
  • CONNECT opcional
Restream compartido

Reduce conexiones al proveedor y adapta el códec o bitrate a los dispositivos.

  • Perfiles JSON
  • CPU o GPU
  • Salida disk, memory_fs o pipe_ram
  • Grabaciones
Servidor de archivos multimedia

Convierte carpetas y almacenamientos remotos en URLs reproducibles con seek.

  • Local y unidades montadas
  • Protocolos remotos
  • Rangos HTTP
  • Enlaces temporales
Preparación de contenido

Controla tareas que tardan en completar y publica el archivo solo cuando está listo.

  • BitTorrent
  • aMule/eD2k
  • IPFS
  • SABnzbd
Nodo de borde para PTP

Mantén las credenciales y los archivos cerca del origen mientras PTP coordina el catálogo.

  • Conexión saliente
  • Inventario de fuentes
  • Perfiles disponibles
  • Entrega directa o relay
Conector extensible

Añade nuevos proveedores mediante plugins sin cambiar la API que usan los clientes.

  • SDK y ejemplo
  • Manifiestos validados
  • Integridad
  • Estado y reinicio administrativo

Funciones principales de un nodo

FunciónEntrada habitualSalida para el clienteUso típico
Canales IPTVPlaylist o stream del proveedorHLS propio o restreamTV en directo
VODURL remota o token claimStream con Range o descargaPelículas y episodios
AlmacenamientoCarpeta, protocolo remoto o catálogoListado y objetos HTTPBiblioteca de archivos
Proveedor externoServicio especializadoObjeto listo mediante la API comúnS3, P2P, cloud
GrabaciónCanal o URLArchivo generado en el nodoProgramas y emisiones
Nodo PTPTrabajos del servidor centralInventario, resultados y entregaInstalaciones distribuidas

Formas de despliegue

DespliegueQuién lo administraConectividadCuándo elegirlo
IndependienteSu propio config.jsonClientes conectan directamente al proxyUna sola máquina o uso autónomo
Local junto a PTPPTP inicia y supervisa el procesoLoopback o red localInstalación conjunta sencilla
Nodo remoto de PTPPTP central mediante conexión salienteFunciona detrás de NAT o firewallNAS, vivienda remota, VPS o sede secundaria
Gateway público controladoAdministrador del nodoTLS, tokens y límites obligatoriosEntrega directa a clientes externos
Ruta rápida según tu objetivo
  1. Solo IPTV: configura channels y prueba /playlist.m3u.
  2. Servidor de archivos: añade storage.sources y prueba /api/storage.
  3. Transcodificación: instala perfiles, configura FFmpeg y elige profile.
  4. Contenido bajo demanda: habilita una fuente plugin con allow_prepare.
  5. Integración con PTP: habilita managed_node o deja que PTP inicie el nodo local.

Requisitos del sistema

Windows
Windows 10 / 11 (64-bit)
  • FFmpeg — ejecutable ffmpeg.exe en PATH o en la misma carpeta
  • Visual C++ Redistributable 2022 (normalmente ya instalado)
  • Sin GPU: solo CPU, sin drivers adicionales
  • Con GPU: driver NVIDIA ≥ 570.0
Linux
Ubuntu 20.04+ / Debian 11+
  • FFmpeg — sudo apt install ffmpeg
  • glibc 2.31+ (Ubuntu 20.04 en adelante lo incluye)
  • Sin GPU: solo CPU, sin drivers adicionales
  • Con GPU: driver NVIDIA + CUDA instalados
FFmpeg no es obligatorio para proxy HLS, túnel VOD, servidor de archivos, listados remotos ni proveedores que ya entregan el objeto final. Solo es necesario para restream, transcodificación y determinadas grabaciones.

Dependencias por función

Instala solo lo que necesita tu despliegue. El binario principal puede trabajar con canales y fuentes nativas sin Python ni herramientas de addons.

FunciónObligatorioOpcionalNotas
Proxy HLS y VODptp-proxy y acceso de redTLS propioNo requiere FFmpeg si no se recodifica.
Restream, transcodificación y grabaciónFFmpegGPU y driversEl perfil elegido debe existir en el nodo.
Servidor de archivos local/remotoAcceso a la fuenteCA privada o montajeNo requiere FFmpeg para servir el archivo.
Plugins PythonPython 3Dependencia del proveedorS3 y los addons incluidos se ejecutan fuera del núcleo.
Nodo administrado por PTPAcceso HTTPS saliente a PTPURL pública directaPuede funcionar detrás de NAT y usar relay como fallback.
memory_fsFilesystem en memoria preparadoLímites del sistemaptp-proxy no monta la unidad automáticamente.
Herramientas opcionales para addons
  • aMule/aMuled para el proveedor eD2k.
  • rclone para remotos cloud configurados por el administrador.
  • Kubo para raíces IPFS/IPNS.
  • SABnzbd para trabajos NZB.
  • aria2c cuando se usa el proveedor BitTorrent incluido.
  • Las dependencias opcionales no son necesarias si no configuras ese proveedor.
Conectividad y puertos
  • El listener HTTP predeterminado es 127.0.0.1:8180.
  • Para acceso remoto, configura TLS o un reverse proxy y una clave de acceso.
  • Un nodo administrado inicia la conexión hacia PTP; no necesita publicar su API administrativa en Internet.
  • Las fuentes remotas deben ser alcanzables desde la máquina que ejecuta ptp-proxy.
  • Los addons de control deben permanecer en loopback siempre que sea posible.

Notas importantes sobre FFmpeg

FFmpeg es obligatorio si usas canales en modo restream. Si solo usas modo proxy o VOD tunnel, FFmpeg no es necesario, pero es recomendable tenerlo disponible.

Instalar FFmpeg en Windows

  1. Ir a gyan.dev/ffmpeg/builds y descargar la versión release-full
  2. Descomprimir en, por ejemplo, C:\ffmpeg\
  3. Añadir C:\ffmpeg\bin al PATH del sistema o bien copiar ffmpeg.exe a la misma carpeta que ptp-proxy.exe
  4. Verificar: abrir terminal y ejecutar ffmpeg -version

Instalar FFmpeg en Linux / WSL2

sudo apt update && sudo apt install ffmpeg
# Verificar:
ffmpeg -version

¿Qué versión de FFmpeg necesito?

FunciónVersión mínima FFmpeg
Copy / proxy / HLS básico4.0+
Transcodificación CPU (libx264)4.0+ con libx264 compilado
GPU NVIDIA (h264_nvenc)4.4+ con NVENC support + driver ≥ 570.0
Todo lo anterior (recomendado)6.0 o superior

Verificar soporte NVENC (GPU NVIDIA)

# Debe aparecer "h264_nvenc" en la lista
ffmpeg -hide_banner -encoders | grep nvenc
# Prueba rápida — si da error, el driver no es compatible
ffmpeg -hide_banner -f lavfi -i nullsrc -t 1 -c:v h264_nvenc -f null -

Arquitectura — flujos de conexión

Los primeros diagramas explican IPTV y restream. Los flujos adicionales muestran cómo ptp-proxy publica archivos, coordina proveedores asíncronos y trabaja como nodo administrado por PTP.

Leyenda: Control / señalización Stream de vídeo Contenido web (imágenes, EPG)

1. Conexión directa — sin proxy

DVB-T Servidor media servidor Otros… IPTV Proveedor Túnel Cloudflare ⚠ credenciales expuestas Web IPTV Navegador cliente App / VLC cliente ctrl video epg

Los usuarios se conectan directamente al proveedor IPTV. Cada cliente abre su propio conjunto de conexiones (control, vídeo, EPG). Las credenciales del proveedor viajan en cada petición y son visibles para el cliente.

2. ptp-proxy insertado

IPTV Proveedor ptp-proxy ✓ credenciales ocultas ⚙ configurable por flujo Navegador cliente App / VLC cliente 2×

ptp-proxy se sitúa entre los clientes y el proveedor. Los clientes solo ven la dirección del proxy. Las credenciales nunca salen del proxy. Cada cliente sigue generando su propia conexión upstream (modo proxy). Las líneas discontinuas indican flujos que el servidor puede activar o desactivar individualmente — el tráfico de control, vídeo y reproductor web puede pasar por el proxy de forma independiente.

3. Modo restream — upstream compartido

IPTV Proveedor ptp-proxy FFmpeg 1 conexión upstream Cliente 1 Cliente 2 Cliente 3 ← N clientes 1×

En modo restream, FFmpeg abre una sola conexión upstream por canal. Todos los clientes reciben el stream redistribuido. El proveedor ve exactamente una conexión sin importar cuántos espectadores haya.

4. Escenario VPN — proxy dentro de un túnel

IPTV Proveedor VPN / túnel ptp-proxy ↑ IP VPN Navegador cliente App / VLC cliente

Si ptp-proxy corre en un host que está dentro de una VPN (o un túnel Cloudflare, WireGuard, etc.). Todo el tráfico upstream al proveedor sale por la IP de la VPN. Los clientes se conectan normalmente al proxy desde el exterior.

5. Gateway de almacenamiento

VLC / aplicación / PTP
↓ lista o solicita un objeto
ptp-proxy — permisos, límites y enlace temporal
↓
Local / NAS / WebDAV / SFTP / SMB / NFS / FTP / HTTP / plugin

El cliente utiliza la misma API sin conocer dónde vive el archivo. ptp-proxy conserva las credenciales, publica metadatos y traduce el seek del reproductor a rangos del origen.

6. Nodo administrado por PTP

ptp-proxy remoto → HTTPS saliente → PTP
PTP → inventario, exploración y trabajos → nodo
nodo → resultados, perfiles y operaciones → PTP
reproductor → nodo directo o reproductor → PTP → relay → nodo

PTP coordina varios nodos sin convertirlos en simples proxies de túnel. Cada nodo mantiene sus fuentes y plugins, y PTP conserva usuarios, catálogo y selección de ubicación.

7. Preparación asíncrona mediante plugins

cliente / PTP → POST prepare
ptp-proxy → proveedor externo
queued → preparing → ready
resultado ready → /storage/<source>/<path>
cliente → reproducción normal con Range

La operación controla contenido que todavía no existe como archivo reproducible. Cuando termina, el resultado usa exactamente la misma API y permisos que cualquier otra fuente.

Quién conserva cada responsabilidad

ComponenteResponsabilidad principal
ClienteSolicita playlists, objetos, rangos o operaciones autorizadas.
ptp-proxyAcceso a orígenes, seguridad local, streaming, transcodificación y plugins.
PTPUsuarios, catálogo, biblioteca, coordinación de nodos y selección de entrega.
Proveedor externoConexión a un servicio especializado y publicación del resultado.
FFmpegCopia, transcodificación o grabación cuando el flujo lo requiere.

Instalación en Windows

Instalación = copiar un archivo. No hay instalador, no hay registro, no hay dependencias del sistema que instalar.
  1. Descargar ptp-proxy.exe desde la sección Descargas de esta guía
  2. Crear una carpeta, por ejemplo C:\ptp-proxy\
  3. Copiar ptp-proxy.exe a esa carpeta
  4. Copiar el archivo config.example.json a la misma carpeta y renombrarlo a config.json
  5. Editar config.json con un editor de texto (Notepad++, VS Code...)
  6. Abrir el terminal (cmd o PowerShell) en esa carpeta y ejecutar:
ptp-proxy.exe --config config.json
Si ves el mensaje Listening on 0.0.0.0:8180 en la consola (o en el log), el proxy está funcionando.

Ejecutar automáticamente al inicio (opcional)

Para que el proxy arranque automáticamente con Windows, usa el soporte de servicio integrado en el propio ejecutable (opción recomendada, sin herramientas adicionales) o bien el Programador de tareas / NSSM (alternativa).

Instalar como Servicio de Windows (recomendado)

Requiere Símbolo del sistema o PowerShell con privilegios de Administrador. El servicio arranca automáticamente con Windows y se gestiona desde el sc / panel de Servicios.
# Instalar e iniciar el servicio
ptp-proxy.exe --install-service --config "C:\ptp-proxy\config.json"
ptp-proxy.exe --start-service
sc query ptp-proxy

# Detener y desinstalar el servicio
ptp-proxy.exe --stop-service
ptp-proxy.exe --uninstall-service

Por defecto el servicio se instala con inicio automático. Si prefieres iniciarlo a mano, añade --demand:

# Arranque manual (solo cuando se solicite)
ptp-proxy.exe --install-service --demand --config "C:\ptp-proxy\config.json"

Alternativa: NSSM

NSSM sigue siendo válido si ya lo usas para otros servicios.

# NSSM (https://nssm.cc)
nssm install PtpProxy "C:\ptp-proxy\ptp-proxy.exe" "--config C:\ptp-proxy\config.json"
nssm start PtpProxy

Diferencias Windows vs Linux

AspectoWindowsLinux
Binarioptp-proxy.exeptp-proxy (sin extensión)
Rutas en config.jsonC:/streams/ o ./streams//home/user/streams/ o ./streams/
GPU NVIDIASí, con driver WindowsSí, con driver Linux + CUDA
Job Object (kill procesos hijo)Automático — FFmpeg se mata al cerrar el proxySeñal SIGTERM propagada
Separador de rutas/ o \ (ambos válidos en config.json)Solo /
FFmpeg en PATHAñadir C:\ffmpeg\bin al PATH o poner "path": "C:/ffmpeg/bin/ffmpeg.exe"Generalmente ya en PATH tras apt install

Instalación en Linux

  1. Descargar el binario ptp-proxy (Linux x64) a, por ejemplo, /opt/ptp-proxy/
  2. Darle permisos de ejecución:
chmod +x /opt/ptp-proxy/ptp-proxy
  1. Copiar config.example.json como config.json en la misma carpeta y editar
  2. Ejecutar:
cd /opt/ptp-proxy
./ptp-proxy --config config.json

Ejecutar como servicio systemd (recomendado en Linux)

Crear el archivo /etc/systemd/system/ptp-proxy.service

[Unit]
Description=ptp-proxy
After=network.target

[Service]
ExecStart=/opt/ptp-proxy/ptp-proxy --config /opt/ptp-proxy/config.json
WorkingDirectory=/opt/ptp-proxy
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now ptp-proxy
sudo systemctl status ptp-proxy
journalctl -u ptp-proxy -f

El binario es estático (no requiere nada extra)

El binario Linux incluye OpenSSL compilado de forma estática. Solo necesita glibc (incluido en cualquier Ubuntu/Debian moderno) y FFmpeg en el sistema para los canales restream.

Usar el proxy en WSL2 (Linux dentro de Windows)

¿Por qué WSL2? El caso de uso principal es enrutar el tráfico del proxy por una VPN de Linux. Si la VPN está en la VM de Linux, todo el tráfico del proxy sale por esa interfaz, aunque el cliente (app web, VLC) esté en Windows.

Cómo funciona la red en WSL2

WSL2 tiene su propia interfaz de red, pero Windows incluye un mecanismo llamado localhost forwarding: si una aplicación dentro de WSL2 escucha en 127.0.0.1, Windows redirige automáticamente las conexiones a 127.0.0.1 del host hacia la VM.

Windows 127.0.0.1:8180 ──→ (localhost forwarding automático) ──→ WSL2 Ubuntu 127.0.0.1:8180
El proxy escucha en WSL2 Linux → Windows lo ve en su propio 127.0.0.1
Todo el tráfico saliente del proxy usa la interfaz de Linux/VPN, no la de Windows

Paso 1 — Instalar WSL2 y Ubuntu

wsl --install
wsl --install -d Ubuntu-24.04

Paso 2 — Copiar el binario a WSL2

wsl -d Ubuntu-24.04 -- bash -c "cp /mnt/c/path/ptp-proxy /home/user/ptp-proxy"
wsl -d Ubuntu-24.04 -- bash -c "chmod +x /home/user/ptp-proxy"

O acceder a WSL2 directamente y mover los archivos:

wsl
cp /mnt/c/path/to/binary/ptp-proxy ~/ptp-proxy
chmod +x ~/ptp-proxy

Paso 3 — Configurar el config.json para WSL2

La clave es hacer que el proxy escuche en 127.0.0.1 dentro de WSL2. Windows se encarga del forwarding automáticamente.

"server": {
  "listen": "127.0.0.1",   // ← IMPORTANTE: no 0.0.0.0
  "port":   8180
}
No usar 0.0.0.0 en WSL2 si el objetivo es que solo Windows acceda al proxy a través del forwarding. Con 127.0.0.1, el proxy solo es accesible desde el propio host (Windows incluido vía localhost forwarding), no desde la red local.

Paso 4 — Iniciar el proxy desde PowerShell

wsl -d Ubuntu-24.04 -- bash -c "cd /path/to/proxy ; nohup ./ptp-proxy > ptp-proxy.log 2>&1 &"
wsl -d Ubuntu-24.04 -- bash -c "tail -5 /path/to/proxy/ptp-proxy.log"
wsl -d Ubuntu-24.04 -- bash -c "pkill ptp-proxy"

Paso 5 — Verificar desde Windows

Invoke-WebRequest http://127.0.0.1:8180/health
curl http://127.0.0.1:8180/health
Si ves ok como respuesta, el proxy en WSL2 está funcionando y es accesible desde Windows.

Rutas en config.json para WSL2

Puedes poner la carpeta de streams directamente en Windows para ver los archivos fácilmente:

"storage_path": "/mnt/c/path/streams/"   // carpeta de Windows accesible desde WSL2

FFmpeg en WSL2

sudo apt update && sudo apt install ffmpeg
FFmpeg de WSL2 no puede usar la GPU NVIDIA de Windows a menos que tengas configurado CUDA for WSL2. Para la mayoría de casos, usa transcodificación CPU (libx264) en WSL2.

Configuración básica (config.json)

El archivo config.json es el único fichero de configuración. Se edita con cualquier editor de texto. A continuación se explica cada bloque.

Mapa de la configuración

El archivo se divide por responsabilidad. Puedes empezar con servidor, seguridad y una sola fuente o canal, y añadir el resto después.

BloqueControlaNecesario cuando
serverListener, TLS y límites HTTPSiempre
securityClaves, IPs de gestión, SSRF y rate limitingSiempre
ffmpegRuta del ejecutable y duración HLSRestream, transcodificación o grabación
transcodingDirectorio y perfil predeterminadoUsas perfiles
restreamSalida HLS, directorio y tiempos de vidaCanales restream
storageFuentes, permisos, tokens, plugins y límitesServidor de archivos o addons
coreClaims dinámicos con un servidor de validaciónPTP u otro core entrega tokens
managed_nodeRegistro y conexión saliente a PTPEl proxy será un nodo administrado
channelsCanales estáticos proxy/restreamNo dependen de un core dinámico
recordingsDirectorio y política de grabaciónUsas la API de grabaciones

Estructura completa con valores por defecto

{
  // ── Servidor ──────────────────────────────────────────────
  "server": {
    "listen": "127.0.0.1",
    "port":   8180,
    "worker_threads": 0,
    "request_header_limit": 32768,
    "request_body_limit": 1048576,
    "request_timeout_sec": 30,
    "write_timeout_sec": 60,
    "keep_alive_timeout_sec": 30,
    "max_requests_per_connection": 100,
    "health_secret": "",
    "tls": {
      "enabled": false,
      "port":    8443,
      "cert":    "",
      "key":     ""
    }
  },
  // ── Logging ───────────────────────────────────────────────
  "logging": {
    "file": "ptp-proxy.log",
    "json": true
  },
  // ── Seguridad ─────────────────────────────────────────────
  "security": {
    "block_private_ips": true,
    "management_ips":   ["127.0.0.1", "::1"],
    "access_key":       "",
    "localhost_bypass": true,
    "proxy_raw_public": false,
    "key_exempt_paths": [],
    "allow_unauthenticated_remote": false,
    "allow_access_key_query": false,
    "allow_legacy_url_query": false,
    "verify_upstream_tls": false,
    "upstream_ca_file": "",
    "rate_limit": {
      "enabled":     true,
      "window_sec":  10,
      "max_requests": 300
    }
  },
  // ── FFmpeg ─────────────────────────────────────────────────
  "ffmpeg": {
    "path":             "ffmpeg",
    "hls_time_seconds": 4,
    "hls_list_size":    10,
    "extra_args":       []
  },
  // ── Transcoding profiles ───────────────────────────────────
  "transcoding": {
    "profiles_dir":            "./profiles",
    "default_profile":         "libopenh264_balanced",
    "allow_legacy_extra_args": true
  },
  // ── Restream output ────────────────────────────────────────
  "restream": {
    "output":           "disk",
    "storage_path":     "./streams/",
    "startup_grace_ms": 30000,
    "idle_timeout_sec": 120
  },
  // ── Storage sources ───────────────────────────────────────────
  "storage": {
    "sources": [
      {
        "id":        "media",
        "type":      "local",
        "root_path": "./media"
      }
    ]
  },
  // ── Core (claims integration) ─────────────────────────────────────────────────────────────
  "core": {
    "url":    "",
    "secret": ""
  },
  // ── Canales ────────────────────────────────────────────────
  "channels": []
}

Opciones de <code>server.listen</code>

ValorSignificadoCuándo usarlo
"0.0.0.0"Acepta conexiones en todas las interfaces de redServidor accesible desde la red local o internet
"127.0.0.1"Solo conexiones localesUso en WSL2 o detrás de nginx en el mismo servidor
"192.168.1.x"Solo conexiones desde esa interfaz de redQuieres servir solo en la red local, no en la VPN

Límites HTTP y capacidad

Los límites protegen frente a conexiones lentas, cuerpos descontrolados y sesiones keep-alive excesivas, manteniendo estable el servicio bajo carga.

CampoDefectoDescripción
worker_threadsNúmero máximo de operaciones simultáneas atendidas.Concurrencia disponible, con un mínimo de 4.
request_header_limit32768Máximo de bytes de cabeceras.
request_body_limit1048576Máximo de body de entrada.
request_timeout_sec30Tiempo máximo para leer una petición.
write_timeout_sec60Tiempo máximo para escribir la respuesta.
keep_alive_timeout_sec30Espera máxima entre peticiones keep-alive.
max_requests_per_connection100Peticiones máximas por conexión persistente.

Fuentes de almacenamiento y acceso HTTP público

Las fuentes local, WebDAV, SFTP, SMB, NFS, FTP/FTPS y catálogo HTTP comparten autorización por permisos, IDs públicos opacos, enlaces temporales, límites y reproducción con rangos.

"storage": {
  "sources": [
    {
      "id":        "media",
      "type":      "local",
      "root_path": "./media"
    }
  ]
}
Elige una fuente y configura únicamente sus datos de conexión. Los recursos montados pueden exponerse como local o mediante mount_path. El bypass de localhost sigue desactivado por defecto.
CampoUso
id / public_idNombre interno e ID opaco opcional expuesto en URLs públicas.
typelocal, webdav, sftp, smb, nfs, ftp o http_manifest.
allow_list / allow_stream / allow_downloadOperaciones permitidas por fuente para identidades no administradoras.
admin_onlyRestringe la fuente a storage:admin.
access_tokensTokens Bearer con permisos y fuentes internas opcionales.
link_secretSecreto HMAC para enlaces temporales de ruta exacta.
read_buffer_bytesBuffer de backpressure entre 16 KiB y 4 MiB.
max_concurrent_reads*Límites globales, por IP y por fuente.
idle_timeout_sec / initial_retry_countTimeout sin progreso y reintentos solo antes de las cabeceras.
root_path / base_url / mount_pathRaíz local, raíz remota, catálogo HTTP o recurso SMB/NFS ya montado.
verify_tls / ca_fileVerificación de certificados para conexiones TLS.
known_hosts / host_key_sha256Verificación del host SSH para SFTP.
domain / require_signing / require_encryptionAutenticación y política de transporte SMB.
connect_timeout_sec / transfer_timeout_secTimeouts positivos de conexión y transferencia total.

TLS / HTTPS en el listener

El servidor puede escuchar en HTTP y HTTPS simultáneamente (dos puertos independientes). Se soportan TLS 1.2 y TLS 1.3; SSLv2 / SSLv3 siempre desactivados.

"server": {
  "listen": "0.0.0.0",
  "port":   8180,
  "tls": {
    "enabled": true,
    "port":    8443,
    "cert":    "/etc/letsencrypt/live/mydomain.com/fullchain.pem",
    "key":     "/etc/letsencrypt/live/mydomain.com/privkey.pem"
  }
}
CampoPor defectoDescripción
enabledfalseActiva el listener HTTPS. El HTTP sigue funcionando en server.port.
port8443Puerto HTTPS. Los clientes deben usar https://host:8443/… cuando TLS está activo.
cert""Ruta al fichero PEM con la cadena de certificados completa (p.ej. fullchain.pem).
key""Ruta al fichero PEM con la clave privada (p.ej. privkey.pem).
Certificado auto-firmado para pruebas locales / LAN — los clientes mostrarán aviso de certificado; añade una excepción en el navegador o úsalo solo internamente. Generar con:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"
Luego pon "cert": "./cert.pem" y "key": "./key.pem" en el config.
Certificado gratuito con Let’s Encrypt (requiere dominio público y puerto 80 abierto):
certbot certonly --standalone -d midominio.com
Ficheros: /etc/letsencrypt/live/midominio.com/fullchain.pem y privkey.pem. Renovar automáticamente con certbot renew.
Si el proxy está detrás de nginx / Caddy / Traefik, deja TLS desactivado aquí y que el reverse proxy termine el HTTPS. El TLS nativo es útil cuando el binario se expone directamente a internet.

Salida HLS: disco, filesystem en memoria o RAM integrada

Memoria
"output": "memory_fs" — recomendado en memoria

Los segmentos se escriben en un directorio montado sobre memoria, como tmpfs o una unidad RAM. Se conserva el HLS normal y la configuración es igual en PTP y ptp-proxy.

Compatibilidad: pipe_ram mantiene el segmentador integrado para cargas pequeñas; ram sigue siendo su alias heredado.

Disco
"output": "disk"

Los segmentos se guardan en storage_path sobre un sistema de archivos normal. Es la opción más sencilla y permite conservarlos durante un reinicio del proceso.

El perfil de transcodificación se elige por separado mediante transcoding.default_profile o channels[].profile.

Perfiles de transcodificación compatibles con PTP

ptp-proxy carga perfiles JSON con el mismo formato que PTP. Cada perfil tiene un identificador estable, etiquetas, códec y variantes para reproducción directa, remux y HLS.

PTP solo envía el identificador y profile_mode (hlstrans o hlscopy). ptp-proxy valida ambos y no acepta argumentos de ejecución arbitrarios desde el core.

Catálogo y perfil por defecto

Configura el directorio de perfiles y un perfil por defecto. Los paquetes oficiales incluyen perfiles de software y aceleración por hardware.

"transcoding": {
  "profiles_dir":            "./profiles",
  "default_profile":         "libopenh264_balanced",
  "allow_legacy_extra_args": true
}
ffmpeg.extra_args y channels[].extra_args se mantienen únicamente para migración. Pueden desactivarse con allow_legacy_extra_args=false.

Perfil y variante HLS por canal

{
  "id":      "news",
  "mode":    "restream",
  "url":     "https://origin.example/live.m3u8",
  "profile":      "h264_nvenc_web",
  "profile_mode": "hlstrans"
}
curl -H "Authorization: Bearer ACCESS_KEY" http://127.0.0.1:8180/api/transcode/profiles

Qué define un perfil

DatoUso público
idIdentificador estable compartido con PTP.
labelNombre localizado para interfaces.
codecFamilia de salida que verá el cliente.
mse_testPrueba de compatibilidad del navegador.
hlstransVariante de HLS con transcodificación.
hlscopyVariante HLS sin recodificación.
direct / remuxVariantes reservadas para otros flujos de reproducción.

Cómo se selecciona el perfil

  • Un canal puede indicar profile y profile_mode.
  • Un claim dinámico puede solicitar un perfil por identificador.
  • Si no se indica, se usa transcoding.default_profile.
  • PTP consulta /api/transcode/profiles y solo ofrece perfiles disponibles en el nodo.
  • Los argumentos heredados pueden desactivarse después de completar la migración.

Dónde se genera la salida HLS

El perfil define cómo se codifica. El modo de salida define dónde se guardan los segmentos; son decisiones independientes.

ModoQué haceVentajasUso recomendado
diskEscribe playlists y segmentos en un filesystem normal.Simple, inspeccionable y persistente mientras no se limpie.Uso general y discos rápidos.
memory_fsEscribe HLS en un tmpfs o unidad RAM preparada externamente.Mantiene todas las capacidades normales de HLS con baja latencia de I/O.Opción recomendada cuando se quiere usar RAM.
pipe_ramMantiene segmentos MPEG-TS en la memoria del proceso.No necesita un filesystem temporal.Cargas pequeñas y escenarios controlados.
ramAlias heredado de pipe_ram.Compatibilidad de configuración.Migrar a un nombre explícito.
"restream": {
  "output": "memory_fs",
  "storage_path": "/dev/shm/ptp-proxy/streams"
}
memory_fs no monta ni crea la unidad de memoria. Configura restream.storage_path con una ruta ya preparada y limita su tamaño desde el sistema operativo.

Disponibilidad real del perfil

Que un archivo de perfil exista no garantiza que el equipo tenga el codificador o driver. Prueba el perfil en cada nodo y deja que PTP almacene la disponibilidad anunciada.

Perfil con aceleración por hardware

La aceleración por hardware puede reducir el uso de CPU. La capacidad real depende del equipo, del controlador, de la resolución y del perfil elegido.

Antes de seleccionar el perfil

  • Comprueba que el nodo dispone de un codificador compatible
  • Prueba el perfil con una retransmisión corta antes de asignarlo en PTP
  • Mantén un perfil software disponible como alternativa

Perfil incluido para NVIDIA

"profile": "h264_nvenc_web"

Selección desde PTP

PTP puede consultar /api/transcode/profiles y ofrecer únicamente los perfiles instalados en el nodo seleccionado.

Perfiles NVIDIA incluidos

PerfilCompatibilidadObjetivoUso recomendado
h264_nvenc_webH.264 BaselineCompatibilidad webNavegadores y dispositivos heterogéneos
h264_nvencH.264 MainCalidad generalClientes que admiten el perfil Main
Si un perfil no puede iniciarse, ptp-proxy informa del fallo y no lo sustituye silenciosamente. Revisa la disponibilidad del codificador en ese nodo o selecciona otro perfil.

Perfiles de transcodificación por software

Los perfiles software funcionan sin un codificador de vídeo dedicado. El rendimiento depende del procesador, la resolución y el número de retransmisiones simultáneas.

Perfil software recomendado

"profile": "libopenh264_balanced"

Perfiles software incluidos

PerfilCódecObjetivoUso recomendado
libopenh264_fastH.264Menor consumoEquipos limitados o pruebas
libopenh264_balancedH.264EquilibrioOpción general recomendada
libopenh264_qualityH.264Mayor calidadPocos canales simultáneos
libx264_fastH.264RapidezInstalaciones con FFmpeg GPL
libx264_qualityH.264CalidadInstalaciones con FFmpeg GPL
libx265HEVCEficienciaSolo clientes compatibles con HEVC
Realiza una prueba local antes de fijar el perfil por defecto. PTP puede elegir perfiles distintos por nodo sin transportar argumentos de ejecución.

Configuración de canales

Canal en modo proxy (más sencillo)

El proxy descarga la playlist del proveedor y reescribe los segmentos. No usa FFmpeg.

"channels": [
  {
    "id":      "my_channel",
    "name":    "My Channel 1",
    "mode":    "proxy",
    "url":     "http://panel.provider.com/live/USER/PASS/123.m3u8",
    "headers": []
  }
]

Canal en modo restream (con FFmpeg)

{
  "id":         "nvenc_channel",
  "mode":       "restream",
  "url":        "http://panel.provider.com/live/USER/PASS/456.m3u8",
  "profile":      "h264_nvenc_web",
  "profile_mode": "hlstrans",
  "skip_re":      true
}

Canal con headers de autenticación

{
  "id":      "auth_channel",
  "mode":    "proxy",
  "url":     "https://cdn.provider.com/live/stream.m3u8",
  "headers": [
    { "key": "Authorization", "value": "Bearer TOKEN_HERE" },
    { "key": "Referer",       "value": "https://provider.com/" }
  ]
}

Una vez configurado, el cliente accede al canal en:

http://your-server:8180/channels/my_channel/playlist.m3u8

Canales estáticos y flujos dinámicos

OrigenConfiguraciónVentaja
Canal estático proxychannels[].mode=proxyConfiguración simple y sin recodificación.
Canal estático restreamchannels[].mode=restreamSalida compartida y perfil controlado.
Claim dinámico liveEl core devuelve URL y tipo liveNo guarda credenciales en el cliente.
Claim dinámico restreamEl core añade profile y profile_modePTP elige un perfil instalado sin enviar comandos.
Claim VOD/seriesEl core devuelve URL y tipo de contenidoTúnel con Range, seek y descarga.
GrabaciónAPI /api/recordingsGuarda un canal o URL como archivo administrado.

Integración con un core

Cuando core.url está configurado, el cliente usa un token corto. ptp-proxy lo valida con el core y recibe la URL real, el tipo de contenido y, si corresponde, el perfil solicitado. Las credenciales reales nunca se devuelven al reproductor.

Grabaciones

La API de grabaciones puede programar un canal configurado o una URL autorizada, consultar el estado y cancelar el trabajo. El resultado se guarda en el directorio configurado y no se publica automáticamente como fuente salvo que lo añadas a almacenamiento.

  • Programa por hora de inicio/fin o por duración.
  • Usa un nombre de salida controlado y un directorio dedicado.
  • Revisa espacio libre y permisos antes de iniciar trabajos largos.
  • Las grabaciones y los restreams tienen métricas separadas.
No publiques ejemplos con credenciales reales. En producción usa claims, cabeceras privadas o fuentes protegidas y revisa siempre los logs redactados.

Nodo administrado por PTP

ptp-proxy puede funcionar como agente de un servidor PTP central. El nodo conserva el acceso físico a canales, archivos, perfiles y plugins, mientras PTP coordina usuarios, catálogo, exploración y reproducción.

La conexión de control la inicia el propio nodo. Esto permite instalarlo detrás de NAT o firewall sin publicar su API administrativa en Internet.

Tres formas de usar el mismo binario

ModoConfiguraciónControlEntrega
Independientemanaged_node.enabled=falseConfig localLos clientes acceden directamente.
Nodo local de PTPPTP inicia el proceso y entrega el registroPTP centralLoopback o red local.
Nodo remotoEl proxy se incorpora con un token de un solo usoPTP central por HTTPS salienteDirecta, relay o ambas.
Flujo administrado
ptp-proxy → incorporación de un solo uso → PTP
ptp-proxy → heartbeat, fuentes, perfiles y plugins → PTP
PTP → trabajos autorizados → ptp-proxy
ptp-proxy → resultados tipados y operaciones → PTP
reproductor → URL directa del nodo o relay de PTP
Si PTP no está disponible
  • El servidor HTTP del proxy sigue funcionando con su configuración local.
  • Las fuentes y plugins ya iniciados continúan disponibles localmente.
  • El nodo reintenta la conexión con backoff.
  • Los trabajos que dependan del control central esperan hasta la reconexión.
  • La credencial del nodo puede revocarse individualmente desde PTP.

Qué puede administrar PTP

CapacidadResultado
InventarioFuentes, perfiles y proveedores disponibles.
ExploraciónListados, metadatos y recorridos de directorios.
CatálogoImportación de ubicaciones remotas a la biblioteca central.
Operaciones v2Preparar, consultar y cancelar contenido bajo demanda.
PluginsEstado y reinicio administrativo de una fuente externa.
TranscodificaciónSelección de un perfil instalado en el nodo.
Entrega directaURL temporal hacia un nodo accesible.
Relay inversoLectura por rangos cuando el cliente no puede alcanzar el nodo.

Configuración del nodo

En una instalación integrada PTP puede proporcionar estos valores mediante el entorno. En un nodo remoto se guardan en config.json y el token de incorporación se sustituye después por una credencial propia del nodo.

"managed_node": {
  "enabled": true,
  "server_url": "https://ptp.example.com",
  "node_name": "NAS home",
  "enrollment_token": "ONE_TIME_TOKEN",
  "credential_file": "./state/ptp-node.json",
  "public_url": "",
  "direct_delivery": false,
  "reverse_relay": true,
  "verify_tls": true
}

Entrega directa y relay

ModoVentajaCoste o límite
DirectaEl vídeo viaja del nodo al cliente sin pasar por PTP.El cliente debe poder alcanzar public_url.
RelayFunciona aunque el nodo no acepte conexiones entrantes.PTP transporta los bytes y consume ancho de banda central.
MixtaPTP elige directa cuando es posible y relay como fallback.Requiere configurar y comprobar ambos caminos.
Límites del canal administrado
  • Solo acepta tipos de trabajo conocidos; no existe un comando shell genérico.
  • Las credenciales de las fuentes permanecen en el nodo.
  • Cada trabajo incluye identidad, límite y caducidad.
  • El relay está acotado por tamaño y usa rangos.
  • La administración de plugins sigue exigiendo permisos explícitos.
El modo administrado complementa el uso independiente; no obliga a que todos los clientes pasen por PTP ni convierte el canal de control en un túnel general.

Almacenamiento local y remoto

ptp-proxy también es un servidor de archivos y gateway de almacenamiento. Publica carpetas locales, unidades montadas, protocolos remotos, catálogos HTTP y proveedores externos mediante la misma API de listado, metadatos y reproducción.

Solo lectura: se admiten listado, metadatos, reproducción, rangos, seek y cancelación. No se implementan subidas, borrados, renombrados ni cambios de permisos remotos.

Cuatro formas de presentar contenido

La API pública no cambia aunque el archivo esté en el propio equipo, en un NAS, en un servicio remoto o todavía tenga que prepararse.

ModeloOrigenDisponibilidadEjemplo
Servidor localCarpeta, disco o unidad montadaInmediatatype: local
Gateway remotoWebDAV, SFTP, SMB, NFS o FTP/FTPSInmediata si el origen respondetype: webdav
Catálogo HTTPManifiesto JSON y objetos HTTP/HTTPSSegún el catálogotype: http_manifest
Proveedor externoS3, P2P, cloud o gestor de descargasInmediata o tras una operacióntype: plugin
Cómo se identifica un archivo

Cada objeto se referencia mediante un ID público de fuente y una ruta relativa. El ID interno, las credenciales y la URL real del origen permanecen en el servidor.

  • source.id: identificador interno del administrador.
  • source.public_id: identificador opaco usado en URLs.
  • path: ruta relativa validada dentro de la fuente.
  • etag y modified_at: detectan cambios.
  • range_supported: indica si el reproductor puede hacer seek.
Almacenamiento no es un catálogo multimedia

La API describe carpetas y archivos. Un servidor PTP puede importar esas ubicaciones, asociarlas a películas o episodios y mantener metadatos, pero ptp-proxy no inventa títulos ni carátulas por sí solo.

Ciclo de reproducción
  1. El cliente lista una fuente o recibe una ruta desde PTP.
  2. La autorización valida permiso, política de la fuente y límite de concurrencia.
  3. ptp-proxy consulta los metadatos y decide si puede servir rangos.
  4. La respuesta usa GET o HEAD con ETag, tamaño y tipo MIME.
  5. El cliente puede pedir un nuevo rango para hacer seek sin conocer el origen real.
Flujo común
Cliente / VLC / aplicación IPTV
↓ HTTP + Bearer o enlace firmado
ptp-proxy
↓ conexión de solo lectura
Local · WebDAV · SFTP · SMB · NFS · FTP/FTPS · Catálogo HTTP
Capacidades compartidas
  • Mismos endpoints para todas las fuentes
  • Rangos HTTP, seek, ETag y Last-Modified
  • Tokens por permisos y fuentes concretas
  • IDs públicos opacos y enlaces HMAC temporales
  • Límites globales, por IP y por fuente
  • Timeouts, cancelación, streaming estable y métricas

Inicio rápido

  1. Añade una fuente en storage.sources.
  2. Crea un token con storage:list y storage:read.
  3. Consulta GET /api/storage.
  4. Lista con /api/storage/<public_id>/list.
  5. Reproduce con /storage/<public_id>/ruta.

Fuentes y modos disponibles

Elige la fuente que corresponda al servicio remoto. Los recursos ya montados pueden exponerse como carpeta o unidad, y los proveedores externos amplían la lista sin cambiar la API pública.

FuenteConexión directaRecurso montadoRequisito principalUso recomendado
LocalSí—Carpeta legible por el procesoDiscos, carpetas y unidades montadas.
WebDAVSíOpcionalCuenta de solo lectura y TLSNextcloud, ownCloud, servidores DAV y rclone.
SFTPSíOpcionalVerificación del host SSHServidores de archivos por SSH.
SMBSegún plataformaSíShare accesible y credencialesNAS y recursos Windows/Samba.
NFSSegún plataformaSíExport autorizado para el hostAlmacenamiento Unix/Linux y NAS.
FTP / FTPSSíOpcionalFTPS recomendadoServidores FTP existentes y sistemas legacy.
Catálogo HTTPSí—Manifiesto JSON versionadoCDN, hosting estático y catálogos generados.
S3 compatibleSí—Proveedor instalado y credenciales de solo lecturaAmazon S3, MinIO, Ceph, Wasabi y servicios compatibles.
{
  "id": "local-media",
  "public_id": "media",
  "type": "local",
  "root_path": "D:/Media",
  "allow_list": true,
  "allow_stream": true,
  "allow_download": false
}
Usa rutas absolutas. En Windows es preferible D:/Media; en Linux, /srv/media.
{
  "id": "remote-media",
  "public_id": "cloud-a91e",
  "type": "webdav",
  "base_url": "https://dav.example.test/files/user/",
  "username": "reader",
  "password": "CHANGE_ME",
  "auth_method": "any",
  "verify_tls": true,
  "allow_list": true,
  "allow_stream": true,
  "allow_download": false
}
Mantén verify_tls=true. Para una CA privada usa ca_file; las redirecciones se limitan al mismo origen.
{
  "id": "archive",
  "public_id": "archive-a91e",
  "type": "sftp",
  "base_url": "sftp://files.example.test/media/",
  "username": "reader",
  "private_key": "./keys/id_ed25519",
  "known_hosts": "./keys/known_hosts",
  "allow_list": true,
  "allow_stream": true
}
Configura known_hosts o host_key_sha256 para verificar el servidor. Reserva allow_unknown_host para pruebas controladas.
{
  "id": "nas",
  "public_id": "nas-media",
  "type": "smb",
  "base_url": "smb://nas.example.test/video/",
  "username": "reader",
  "password": "CHANGE_ME",
  "domain": "MEDIA",
  "require_signing": true,
  "require_encryption": false
}
Puede usarse una conexión directa o un recurso ya montado. La firma y el cifrado dependen de la configuración de la fuente y del servidor.
{
  "id": "nfs-media",
  "public_id": "nfs",
  "type": "nfs",
  "server": "192.168.1.30",
  "export_path": "/exports/media",
  "remote_path": "/library",
  "version": 3,
  "uid": 1000,
  "gid": 1000
}
Puede usarse una conexión directa o un export montado mediante mount_path. El montaje es la opción recomendada para configuraciones avanzadas de identidad o Kerberos.
{
  "id": "legacy-media",
  "public_id": "ftp-media",
  "type": "ftp",
  "base_url": "ftp://ftp.example.test/media/",
  "username": "reader",
  "password": "CHANGE_ME",
  "tls_mode": "explicit",
  "verify_tls": true,
  "passive_mode": true,
  "allow_list": true,
  "allow_stream": true,
  "allow_download": false
}
Usa FTPS siempre que esté disponible. FTP sin cifrar exige allow_insecure_ftp=true; la fuente es de solo lectura y usa modo pasivo por defecto.
{
  "id": "cdn-catalog",
  "public_id": "cdn",
  "type": "http_manifest",
  "manifest_url": "https://catalog.example.test/media.json",
  "base_url": "https://cdn.example.test/media/",
  "refresh_sec": 300,
  "max_manifest_bytes": 8388608,
  "allowed_origins": ["https://cdn.example.test"],
  "allow_list": true,
  "allow_stream": true,
  "allow_download": false
}
El manifiesto define una biblioteca virtual de solo lectura. Sus URLs deben pertenecer al origen del catálogo, al base_url o a allowed_origins explícitos.
{
  "id": "s3-media",
  "public_id": "s3",
  "type": "plugin",
  "plugin_id": "s3-compatible",
  "backend_type": "s3",
  "plugin_config": {
    "endpoint": "https://s3.example.test",
    "bucket": "media",
    "prefix": "library/",
    "region": "us-east-1",
    "access_key": "CHANGE_ME",
    "secret_key": "CHANGE_ME",
    "path_style": true,
    "verify_tls": true
  },
  "allow_list": true,
  "allow_stream": true,
  "allow_download": false
}
El paquete incluye un proveedor S3 compatible de solo lectura. Otros proveedores se instalan como extensiones y conservan los mismos permisos, enlaces temporales, límites y endpoints.

Permisos, políticas y enlaces temporales

Storage tiene autorización propia. El bypass de localhost está desactivado por defecto y conocer una URL no concede acceso.

"storage": {
  "localhost_bypass": false,
  "inherit_access_key": true,
  "allow_unauthenticated_remote": false,
  "link_secret": "CHANGE_ME_WITH_A_LONG_RANDOM_SECRET",
  "access_tokens": [
    {
      "token": "CHANGE_ME_READER_TOKEN",
      "permissions": ["storage:list", "storage:read", "storage:prepare"],
      "sources": ["local-media"]
    }
  ]
}
PermisoAcción
storage:listVer fuentes autorizadas y listar directorios.
storage:readConsultar HEAD y reproducir con GET.
storage:downloadUsar ?download=1 cuando la fuente lo permita.
storage:prepareIniciar, consultar y cancelar preparaciones asíncronas en fuentes que lo permitan.
storage:adminVer todas las fuentes y crear enlaces HMAC temporales.
Política por fuente
  • public_id oculta el ID interno en URLs públicas.
  • allow_list, allow_stream y allow_download separan capacidades.
  • admin_only reserva una fuente para administradores.
  • max_concurrent_reads limita una fuente concreta.
  • Los tokens restringidos enumeran IDs internos, no public_id.
Enlaces firmados

POST /api/storage/sign crea una URL ligada a fuente, ruta, acción y expiración. Alterar cualquiera de esos valores invalida la firma y nunca evita la política de la fuente.

No incluyas tokens, usuarios o contraseñas en URLs. Usa cuentas remotas de solo lectura y secretos de ejemplo diferentes entre sí.

API y reproducción

Todas las fuentes usan las mismas rutas públicas y el mismo comportamiento HTTP.

Método y rutaUso
GET /api/storageLista las fuentes visibles para la identidad.
POST /api/storage/<source>/prepareInicia una preparación asíncrona; requiere storage:prepare.
GET /api/storage/<source>/operations/<id>Consulta estado, progreso y ruta resultante.
DELETE /api/storage/<source>/operations/<id>Cancela una operación cuando el proveedor lo admite.
GET /api/storage/<source>/list?path=...Lista una carpeta relativa.
POST /api/storage/signCrea una URL temporal de ruta exacta.
GET /api/storage/pluginsMuestra el estado operativo de los proveedores externos; requiere un Bearer explícito con storage:admin.
GET /api/storage/plugin-providersInventaría proveedores instalados, versiones, capacidades, duplicados e integridad; requiere storage:admin.
POST /api/storage/plugins/<source>/restartReinicia una fuente externa; requiere un Bearer explícito con storage:admin.
GET /storage/<source>/<path>Transmite el objeto completo o un rango.
HEAD /storage/<source>/<path>Devuelve metadatos sin body.
GET ...?download=1Fuerza descarga cuando está autorizada.
Se admite un único rango bytes, incluidos rangos abiertos y por sufijo, además de If-Range, If-None-Match e If-Modified-Since. Los rangos múltiples devuelven 416.

Ejemplos con curl

curl -H "Authorization: Bearer CHANGE_ME_READER_TOKEN" http://127.0.0.1:8180/api/storage

curl -H "Authorization: Bearer CHANGE_ME_READER_TOKEN" "http://127.0.0.1:8180/api/storage/media/list?path=films"

curl -I -H "Authorization: Bearer CHANGE_ME_READER_TOKEN" http://127.0.0.1:8180/storage/media/films/movie.mp4

curl -H "Authorization: Bearer CHANGE_ME_READER_TOKEN" -H "Range: bytes=1048576-2097151" http://127.0.0.1:8180/storage/media/films/movie.mp4

curl -X POST -H "Authorization: Bearer CHANGE_ME_ADMIN_TOKEN" -H "Content-Type: application/json" --data '{"source":"media","path":"films/movie.mp4","ttl_sec":300}' http://127.0.0.1:8180/api/storage/sign

Operación, métricas y diagnóstico

Límites y códigos de respuesta

RespuestaCausa habitual
401 / 403Falta identidad, permiso o política de fuente.
404Fuente u objeto inexistente.
416Rango inválido o la fuente no permite esa operación.
429Límite de transferencias por IP.
503Límite global/por fuente o fuente temporalmente no disponible.
504Timeout del servidor remoto.
Métricas Prometheus

El proxy publica transferencias activas, bytes, duración, errores, cancelaciones, rangos, rechazos por límite y reinicios/fallos de proveedores externos.

ptp_storage_active_transfers
ptp_storage_bytes_total
ptp_storage_transfer_duration_seconds_count
ptp_storage_transfer_duration_seconds_sum
ptp_storage_errors_total{code="..."}
ptp_storage_cancelled_total
ptp_storage_range_requests_total
ptp_storage_limit_rejections_total{reason="..."}
ptp_storage_plugin_restarts_total{plugin="...",reason="..."}
ptp_storage_plugin_failures_total{plugin="...",code="..."}
Checklist antes de publicar
  • Ejecuta python3 tests/run_all.py y CTest.
  • Prueba listado, reproducción completa, seek y desconexión con un servidor real.
  • Confirma que los logs no contienen credenciales ni tokens.
  • Usa public_id, permisos mínimos y allow_download=false por defecto.
  • Verifica certificados, identidad del servidor remoto, permisos del share/export y orígenes permitidos del catálogo HTTP.
Preparación asíncrona

Las extensiones con protocolo v2 pueden preparar contenido que tarda en estar disponible. El cliente inicia el trabajo, consulta el progreso y reproduce la ruta normal devuelta cuando está lista.

curl -X POST -H "Authorization: Bearer CHANGE_ME_PREPARE_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: example-1" --data '{"torrent_file":"sample.torrent"}' http://127.0.0.1:8180/api/storage/torrent/prepare

curl -H "Authorization: Bearer CHANGE_ME_PREPARE_TOKEN" http://127.0.0.1:8180/api/storage/torrent/operations/OPERATION_ID

curl -X DELETE -H "Authorization: Bearer CHANGE_ME_PREPARE_TOKEN" http://127.0.0.1:8180/api/storage/torrent/operations/OPERATION_ID
La preparación requiere storage:prepare y allow_prepare: true. Un token de lectura no puede iniciar descargas ni trabajos remotos.

Estados de una operación asíncrona

EstadoSignificadoAcción del cliente
queuedAceptada y esperando recursos.Consultar después del intervalo indicado.
preparingEl proveedor trabaja y puede informar progreso.Mostrar progreso y seguir consultando.
readyEl resultado ya tiene una ruta reproducible.Usar la URL normal de /storage.
failedLa operación terminó con un error estable.Mostrar el error o reintentar con una nueva clave.
cancelledEl usuario o administrador detuvo el trabajo.No intentar reproducir el resultado.

Fuentes nativas, montadas y plugins

ClaseQuién mantiene la conexiónCuándo usarla
Nativaptp-proxyProtocolos comunes con soporte oficial.
MontadaSistema operativoUnidades, FUSE, shares o exports ya montados.
Plugin v1Proveedor externoContenido disponible inmediatamente.
Plugin v2Proveedor externo con operacionesContenido que debe descargarse, restaurarse o generarse.
Proveedor BitTorrent opcional

Prepara contenido BitTorrent autorizado y publica el archivo terminado mediante la API común. Conserva operaciones terminadas entre reinicios y permite limitar espacio, reserva libre, retención y limpieza de parciales.

ptp-proxy no suministra catálogos ni contenidos. Define límites de almacenamiento antes de habilitar esta fuente y usa únicamente material autorizado.
Addons externos incluidos

Cuatro proveedores opcionales conectan servicios ya instalados sin ampliar el núcleo ni cambiar la API pública.

AddonDependenciaUsoSeek
eD2k / aMuleaMule o aMuled y amulecmdPrepara enlaces eD2k y publica solo archivos completados.Al terminar
rclonerclone y un remoto configuradoExpone remotos cloud o de archivos en modo de solo lectura.Según el remoto
IPFS / KuboNodo KuboLista raíces CID/IPNS y puede preparar una raíz autorizada.Sí
Usenet / SABnzbdSABnzbdPrepara NZB autorizados y publica únicamente el resultado final.Al terminar
Las APIs de control se limitan a loopback por defecto. Los trabajos asíncronos requieren storage:prepare y allow_prepare; instala únicamente addons y dependencias de confianza.
Estado de proveedores externos

Los administradores pueden consultar las instancias activas, inventariar los proveedores instalados y reiniciar una fuente concreta sin exponer configuración, credenciales ni rutas internas.

curl -H "Authorization: Bearer CHANGE_ME_ADMIN_TOKEN" http://127.0.0.1:8180/api/storage/plugins

curl -H "Authorization: Bearer CHANGE_ME_ADMIN_TOKEN" http://127.0.0.1:8180/api/storage/plugin-providers

curl -X POST -H "Authorization: Bearer CHANGE_ME_ADMIN_TOKEN" -H "Content-Type: application/json" --data '{}' http://127.0.0.1:8180/api/storage/plugins/s3/restart
Estas operaciones exigen un Bearer administrativo explícito. El inventario también indica si el paquete declara y supera su verificación de integridad.
Proveedores externos

Los proveedores externos ya pueden instalarse para conectar servicios adicionales sin cambiar la API pública. ptp-proxy conserva la autenticación, los permisos, los límites, los enlaces firmados y las métricas.

  • El paquete incluye proveedores S3, BitTorrent, aMule/eD2k, rclone, IPFS/Kubo y SABnzbd.
  • El SDK Python, el proveedor local de ejemplo y el validador permiten crear y comprobar extensiones independientes.
  • Cada proveedor declara listado, metadatos, rangos, seek y preparación asíncrona cuando corresponda.
  • Las extensiones usan los mismos endpoints, permisos, enlaces y límites que las fuentes nativas.
  • Instala únicamente proveedores de confianza y limita sus credenciales y APIs de control.

Diagnóstico rápido

SíntomaRevisar
La fuente no apareceToken, storage:list, IDs de fuente, admin_only y allow_list.
Seek devuelve 416Soporte de rangos, tamaño conocido y capacidades del servidor remoto.
WebDAV no listaAutenticación, certificado, URL raíz, redirecciones y límite de respuesta.
SFTP no arrancaVerificación del host, credenciales, clave privada y conectividad SSH.
SMB no conectaCredenciales, dominio, políticas de firma/cifrado o mount_path.
NFS no conectaPermisos del export, versión, identidad o mount_path.
FTP/FTPS no conectaModo TLS, certificado, modo pasivo, usuario y permisos de lectura.
El catálogo HTTP se rechazaEsquema/versión, rutas duplicadas, tamaños, URLs y allowed_origins.
El proveedor externo no arrancaConsulta /api/storage/plugin-providers y /api/storage/plugins; revisa ID duplicado, integridad, Python 3, permisos, endpoint y credenciales.
La preparación BitTorrent se rechazaLímite total, reserva de espacio libre, número máximo de operaciones y tamaño declarado por el torrent.
Cliente se cortaTimeout de escritura, timeout remoto, red y métricas de errores.
Documentación incluida
El paquete incluye una guía general, referencias para cada fuente nativa, una guía de proveedores externos y documentación del SDK y de la prueba de conformidad.

Cómo probar el proxy

1. Verificar que el servidor arranca

tail -f ptp-proxy.log
curl http://localhost:8180/health

2. Probar un canal con ffplay

curl http://localhost:8180/api/channels
ffplay http://localhost:8180/channels/my_channel/playlist.m3u8

3. Probar VOD tunnel con un token estático (sin servidor de validación)

Útil para probar el proxy sin tener un servidor de validación montado. Añadir al config.json:

"core": {
  "url": "", "secret": "",
  "static_vod_tokens": { "test": "https://your-cdn.com/movie.mkv" }
}
ffplay http://localhost:8180/stream?t=test

4. Probar con VLC

  1. Abrir VLC → Medio → Abrir ubicación de red
  2. Introducir: http://127.0.0.1:8180/channels/mi_canal/playlist.m3u8
  3. Click en Reproducir

5. Ver métricas

curl http://localhost:8180/metrics

6. Probar el servidor de archivos

Usa un token con storage:list y storage:read. Comprueba listado, cabeceras y un rango pequeño antes de abrir un vídeo completo.

curl -H "Authorization: Bearer CHANGE_ME_LIST_TOKEN" http://localhost:8180/api/storage
curl -H "Authorization: Bearer CHANGE_ME_LIST_TOKEN" "http://localhost:8180/api/storage/media/list?path="
curl -I -H "Authorization: Bearer CHANGE_ME_READ_TOKEN" http://localhost:8180/storage/media/video.mp4
curl -H "Authorization: Bearer CHANGE_ME_READ_TOKEN" -H "Range: bytes=0-1048575" http://localhost:8180/storage/media/video.mp4

7. Comprobar perfiles de transcodificación

curl -H "Authorization: Bearer CHANGE_ME" http://localhost:8180/api/transcode/profiles

8. Comprobar un nodo administrado

En PTP verifica que el nodo aparece conectado, publica fuentes y perfiles y puede completar un trabajo de listado. En el proxy revisa que el heartbeat no exponga secretos.

9. Ejecutar la suite automatizada

La suite crea servicios simulados locales y comprueba almacenamiento, seguridad, plugins, TLS, CONNECT, perfiles y operaciones asíncronas sin depender de proveedores reales.

cmake -S . -B build-tests -DPTP_BUILD_STORAGE_TESTS=ON
cmake --build build-tests
ctest --test-dir build-tests --output-on-failure
python3 tests/run_all.py
Pruebas reales recomendadas antes de publicar
  • Reproduce un archivo grande con VLC o ffplay y realiza varios seeks.
  • Desconecta un cliente durante la transferencia y confirma que el contador activo vuelve a cero.
  • Prueba cada protocolo remoto contra el servidor real que vas a usar.
  • Comprueba un perfil CPU y, si procede, uno de GPU.
  • Reinicia un plugin y una operación v2 sin perder el catálogo final.
  • Valida entrega directa y relay para un nodo PTP remoto.
  • Revisa que logs y métricas no contengan tokens ni credenciales.

Solución de problemas comunes

SíntomaCausa probableSolución
503 al abrir el canalFFmpeg todavía arrancandoNormal durante los primeros 5–30 s. Esperar o aumentar startup_grace_ms
503 permanenteFFmpeg falla al arrancarRevisar el log — buscar líneas "ERROR" de FFmpeg
NVENC fallaDriver antiguo o GPU sin soporteActualizar driver a ≥ 570.0 o cambiar a libx264
Stream cortado / artifactsFuente inestable o GOP incorrectoAñadir -force_key_frames, probar con libx264
No responde /healthEl proxy no arrancó o puerto bloqueadoRevisar log, revisar firewall, verificar puerto correcto
curl: (7) Failed to connectEl proxy no está en ejecuciónIniciar el proxy, verificar listen y port
La fuente no apareceFalta permiso, política o configuraciónRevisar storage:list, allow_list, public_id y logs de inicio
El archivo abre pero no permite seekEl origen no admite rangos o no informa tamañoProbar HEAD, revisar range_supported y el servidor remoto
La operación queda en preparingProveedor externo detenido o sin recursosConsultar estado del plugin, cuota, espacio libre y dependencia externa
El nodo PTP aparece desconectadoURL, TLS o credencial del nodoRevisar managed_node, reloj, CA y conectividad saliente
Perfil no disponibleCodificador o driver ausenteConsultar /api/transcode/profiles y probar el perfil en ese nodo

Leer los logs

El proxy genera logs estructurados y censura tokens, firmas, nonces, URLs y cabeceras sensibles. Los mensajes de stream muestran solo el origen upstream.

{"lvl":"INFO", "msg":"[stream] type=live mode=hls_proxy upstream=http://cdn:80"}
{"lvl":"WARN", "msg":"proxy_claim HTTP 403: Forbidden IP"}
{"lvl":"ERROR","msg":"ffmpeg exited with code 1"}

Campos de log importantes

CampoDescripción
[stream] mode=hls_proxyPetición a /stream resuelta como proxy HLS directo
[stream] mode=ffmpeg_restreamCanal restream lanzado o reutilizado
[stream] mode=vod_tunnelTunnel VOD activado con seek nativo
[stream] mode=static_bypassToken estático sin llamada al servidor de validación
proxy_claim HTTP 403El servidor de validación rechazó el token (expirado, no encontrado, IP no autorizada)
[connect] tunnel openTúnel CONNECT abierto para un cliente HTTP
ffmpeg exitedFFmpeg cerró — puede ser normal (stream acabó) o error
[storage]Listado, metadatos o transferencia de una fuente de archivos.
[plugin]Inicio, reinicio, fallo o operación de un proveedor externo.
[managed-node]Incorporación, heartbeat, trabajos o reconexión con PTP.
[recording]Programación, inicio, finalización o cancelación de una grabación.
rate limitUna identidad o IP superó un límite configurado.

Qué observar según la función

FunciónLogMétrica o comprobación
Canales proxyModo y origen redactadoPeticiones y estado HTTP.
RestreamArranque y salida de FFmpegRestreams activos y tiempo de actividad.
AlmacenamientoFuente y código de error estableTransferencias, bytes, rangos y límites.
PluginsEstado y reiniciosReinicios y fallos por proveedor.
Nodo PTPHeartbeat, trabajo y reconexiónPresencia del nodo en PTP.
GrabacionesEstado del trabajoGrabaciones activas y espacio libre.
Los logs deben permitir diagnosticar el flujo sin mostrar tokens, firmas, contraseñas, URLs con credenciales, cabeceras privadas ni configuración de plugins. Si un addon escribe sus propios logs, aplica la misma política.

Métricas útiles

  • ptp_storage_active_transfers y bytes transferidos.
  • Duración, cancelaciones, rangos y rechazos por límite.
  • Reinicios y fallos de plugins.
  • Restreams y grabaciones activas.
  • Tiempo de actividad y errores HTTP.

Seguir logs en tiempo real

tail -f ptp-proxy.log
# Con formato legible (requiere jq):
tail -f ptp-proxy.log | jq -r '.lvl + " | " + .msg'

Seguridad — Capas de protección

El proxy combina protecciones HTTP generales con permisos específicos para streams, almacenamiento, plugins, CONNECT y nodos administrados. No todas las rutas usan la misma credencial ni el mismo alcance.

Capa 1 — Rate limiting (anti-hammering)

Limita cuántas peticiones puede hacer una IP en una ventana de tiempo. Si se supera, devuelve 429 Too Many Requests.

"rate_limit": {
  "enabled":     true,
  "window_sec":  10,
  "max_requests": 60
}

Capa 2 — IPs de administración

Los endpoints sensibles (/metrics, /api/*, /proxy/raw) solo son accesibles desde las IPs listadas.

"management_ips": ["127.0.0.1", "::1"]

Capa 3 — Access key

Con access_key configurado, los endpoints protegidos requieren Authorization: Bearer. El query ?key= solo funciona si se activa expresamente allow_access_key_query.

# Header HTTP (curl, backends):
curl -H "Authorization: Bearer MY_KEY" http://proxy:8180/channels/my_channel/playlist.m3u8
Compatibilidad heredada: allow_access_key_query y allow_legacy_url_query están desactivados por defecto. Actívalos solo mientras migras clientes antiguos.
Con localhost_bypass: true, localhost puede omitir la clave. Al escuchar fuera de loopback, el arranque exige access_key salvo que se active explícitamente allow_unauthenticated_remote.

Configuración de producción recomendada

"security": {
  "block_private_ips": true,
  "management_ips":   ["127.0.0.1", "::1"],
  "access_key":       "long-random-key-here",
  "localhost_bypass": true,
  "allow_unauthenticated_remote": false,
  "allow_access_key_query": false,
  "allow_legacy_url_query": false,
  "verify_upstream_tls": false,
  "rate_limit": { "enabled": true, "window_sec": 10, "max_requests": 300 }
}

Superficies y credenciales

SuperficieProtección habitualObservación
Canales y playlistAccess key o claimNo expongas URLs del proveedor.
VOD dinámicoToken claim HMACEl core decide URL, tipo y perfil.
AlmacenamientoScopes storage:*La política se evalúa además por fuente.
Enlaces temporalesFirma HMAC y expiraciónLigados a fuente, ruta y acción.
Administración de pluginsstorage:admin explícitoEl bypass local no basta.
Preparación asíncronastorage:prepareRequiere también allow_prepare.
Nodo PTPCredencial propia del nodoSe obtiene tras una incorporación de un solo uso.
CONNECTSecreto o claim específicoMantén destinos y orígenes limitados.
Aislamiento de fuentes

Cada fuente define si permite listar, reproducir, descargar o preparar. Los tokens pueden limitarse a fuentes concretas y los IDs públicos evitan revelar identificadores internos.

Seguridad del nodo administrado

El nodo se conecta hacia PTP, guarda una credencial propia y solo ejecuta trabajos conocidos. No ofrece una shell, no entrega credenciales de sus fuentes y puede revocarse sin afectar a otros nodos.

Proveedores externos

Instala solo proveedores de confianza. Verifica manifiestos e integridad, usa credenciales de solo lectura, limita operaciones y revisa las dependencias externas que controlan.

Protecciones adicionales

  • SSRF sin doble resolución DNS: se conecta a los mismos endpoints que fueron validados.
  • Cada redirección se vuelve a validar contra la política SSRF y allow_hosts.
  • Las playlists usan contextos opacos; URL y headers upstream permanecen en memoria del servidor.
  • Los secretos y URLs sensibles se censuran en logs; los mensajes de stream solo muestran el origen.
  • Los nonces de /health no pueden reutilizarse y la caché está limitada a 65.536 entradas.
  • La verificación TLS saliente es opcional y está desactivada por defecto; puede activarse con verify_upstream_tls.

Checklist de exposición remota

  • Usa TLS o un reverse proxy seguro.
  • Configura una clave larga y desactiva compatibilidad por query.
  • Limita IPs de administración.
  • Mantén la verificación TLS de orígenes cuando sea posible.
  • Define tokens de almacenamiento por permiso y fuente.
  • Configura límites globales, por IP y por fuente.
  • No publiques endpoints de control de addons.
  • Prueba la revocación de nodos y enlaces temporales.
  • Revisa periódicamente logs, métricas e integridad de plugins.

Sistema claim — Autenticación segura de tokens

El sistema claim es la forma en que el proxy valida que un token de streaming proviene de tu servicio web (o de cualquier sistema que conozcas el secreto). Se basa en HMAC-SHA256, el mismo principio que usan los JWT o las APIs de AWS.

¿Por qué es necesario?

Sin un sistema de autenticación, cualquier persona que conozca la URL del proxy podría:

  • Adivinar o reutilizar tokens de otros usuarios
  • Hacer que el proxy acceda a URLs que tú no autorizaste (SSRF)
  • Usar el proxy como relay abierto para sus propias peticiones

El sistema claim resuelve esto: el proxy nunca confía en el token del cliente directamente. Siempre verifica con tu servidor de validación que ese token es válido y autentica la propia petición de verificación con una firma HMAC.

El flujo completo para un cliente IPTV

1. Cliente IPTV pide reproducir un canal
↓
2. Tu servicio web genera un token corto (ej: UUID, 30s TTL)
devuelve al cliente: http://proxy:8180/stream?t=TOKEN
↓
3. Cliente llama al proxy: GET /stream?t=TOKEN
↓
4. Proxy construye un claim firmado y hace POST a tu servicio:
POST https://your-service.com/api/claim?proxy_claim=1
Body JSON: { token, ts, nonce, sig }
↓
5. Tu servidor verifica la firma HMAC usando el secreto compartido
si OK → devuelve { ok: true, url: "http://panel/live/USER/PASS/123.m3u8", type: "live" }
↓
6. Proxy sirve el contenido al cliente sin exponer la URL real

El cliente nunca ve las credenciales reales. Solo ve http://proxy:8180/stream?t=TOKEN.

Por qué es seguro

Ataque posibleProtección
Interceptar el claim y reenviarloEl claim incluye un timestamp (ts) con ventana de ±5 minutos. Un claim capturado expira rápidamente.
Adivinar el secreto por fuerza brutaEl secreto nunca viaja en claro — solo viaja el resultado HMAC. Sin el secreto, un HMAC-SHA256 es computacionalmente imposible de invertir.
Reutilizar un token vencidoTu servidor controla el TTL del token en su base de datos. Puede invalidarlo tras el primer uso o tras X segundos.
Generar un claim falsoSin la clave secreta (core.secret), es imposible calcular un HMAC válido. Todas las peticiones sin firma válida reciben 403.

Cálculo del claim HMAC — Paso a paso

Este apartado explica exactamente cómo el proxy construye el claim que envía a tu servidor de validación. Si quieres implementar tu propio servidor de validación (en cualquier lenguaje), esto es todo lo que necesitas.

Ingredientes del claim

CampoTipoDescripción
tokenstringEl token que el cliente envió en ?t=TOKEN
tsint (Unix)Timestamp actual en segundos desde epoch
noncestring hex16 bytes aleatorios criptográficos, codificados en hex. Ejemplo: a3f9c2b1d8e04f72
sigstring hexHMAC-SHA256 del mensaje, en hex minúsculas

Fórmula exacta

Construcción del mensaje y firma
// 1. Construir el mensaje concatenando con "|"
message = token + "|" + ts + "|" + nonce

// Ejemplo concreto:
//   token = "e3df09b42ad23f8174eaf28bbe9ad96b"
//   ts    = 1740698753
//   nonce = "a3f9c2b1d8e04f72"
message = "e3df09b42ad23f8174eaf28bbe9ad96b|1740698753|a3f9c2b1d8e04f72"

// 2. Firmar con HMAC-SHA256 usando el secreto compartido:
sig = HMAC-SHA256(secret, message)  →  resultado en hex minúsculas

JSON que el proxy envía a tu servidor

POST https://your-php.com/api.php?proxy_claim=1
Content-Type: application/json

{
  "token": "e3df09b42ad23f8174eaf28bbe9ad96b",
  "ts":    1740698753,
  "nonce": "a3f9c2b1d8e04f72",
  "sig":   "2b9f3e7d1c45a8f2..."   // 64 hex chars
}

Respuesta que tu servidor debe devolver

// Token válido:
{ "ok": true, "url": "http://panel/live/USER/PASS/123.m3u8", "type": "live" }
// Token inválido:
{ "ok": false, "error": "Token not found" }
IMPORTANTE: siempre HTTP 200, incluso para tokens inválidos. Una respuesta HTTP ≠ 200 se trata como error del servidor.

Tipos de stream soportados en la respuesta

typeComportamiento del proxy
liveDescarga y reescribe el playlist HLS del proveedor. Sin FFmpeg. El cliente nunca ve la URL real.
restreamLanza FFmpeg dinámicamente con -c copy. Genera HLS propio. Múltiples clientes comparten un proceso FFmpeg.
vodTunnel Range-aware para MP4/MKV. Seek nativo sin buffering.
seriesIgual que vod.

Implementar tu propio servidor de claims

Si quieres construir tu propia integración, aquí tienes implementaciones de referencia en varios lenguajes.

<?php
// ── Endpoint: api.php?proxy_claim=1 ───────────────────────────────────────────
if (isset($_GET['proxy_claim'])) {
    $body  = json_decode(file_get_contents('php://input'), true);
    $token = $body['token'] ?? null;
    $ts    = $body['ts']    ?? null;
    $nonce = $body['nonce'] ?? null;
    $sig   = $body['sig']   ?? null;
    if (!$token || !$ts || !$nonce || !$sig) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'Missing fields']);
        exit;
    }
    // Verificar HMAC — el secreto NO viaja nunca en la petición
    $secret   = 'YOUR_SHARED_SECRET_HERE';
    $msg      = $token . '|' . $ts . '|' . $nonce;
    $expected = hash_hmac('sha256', $msg, $secret);
    // hash_equals hash_equals evita timing attacks
    if (!hash_equals($expected, $sig)) {
        echo json_encode(['ok' => false, 'error' => 'Invalid signature']);
        exit;
    }
    // Anti-replay: rechazar timestamps viejos (±5 min)
    if (abs(time() - (int)$ts) > 300) {
        echo json_encode(['ok' => false, 'error' => 'Timestamp expired']);
        exit;
    }
    $info = buscar_token_en_db($token);  // tu función aquí
    if (!$info) {
        echo json_encode(['ok' => false, 'error' => 'Token not found']);
        exit;
    }
    echo json_encode(['ok' => true, 'type' => $info['type'], 'url' => $info['url']]);
}
import hmac, hashlib, time, json
from flask import Flask, request, jsonify
app = Flask(__name__)
SECRET = 'YOUR_SHARED_SECRET_HERE'

@app.route('/api', methods=['POST'])
def proxy_claim():
    if request.args.get('proxy_claim') != '1':
        return jsonify({'ok': False}), 404
    body  = request.get_json()
    token, ts, nonce, sig = body.get('token'), body.get('ts'), body.get('nonce'), body.get('sig')
    if not all([token, ts, nonce, sig]):
        return jsonify({'ok': False, 'error': 'Missing fields'})
    msg      = f"{token}|{ts}|{nonce}"
    expected = hmac.new(SECRET.encode(), msg.encode(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig):
        return jsonify({'ok': False, 'error': 'Invalid signature'})
    if abs(time.time() - int(ts)) > 300:
        return jsonify({'ok': False, 'error': 'Timestamp expired'})
    info = buscar_token(token)
    if not info: return jsonify({'ok': False, 'error': 'Token not found'})
    return jsonify({'ok': True, 'type': info['type'], 'url': info['url']})
const crypto  = require('crypto');
const express = require('express');
const app     = express();
app.use(express.json());
const SECRET = 'YOUR_SHARED_SECRET_HERE';

app.post('/api', (req, res) => {
    if (req.query.proxy_claim !== '1') return res.status(404).end();
    const { token, ts, nonce, sig } = req.body;
    if (!token || !ts || !nonce || !sig) return res.json({ ok: false, error: 'Missing fields' });
    const msg      = `${token}|${ts}|${nonce}`;
    const expected = crypto.createHmac('sha256', SECRET).update(msg).digest('hex');
    const a = Buffer.from(sig, 'utf8'), b = Buffer.from(expected, 'utf8');
    if (a.length !== b.length || !crypto.timingSafeEqual(a, b))
        return res.json({ ok: false, error: 'Invalid signature' });
    if (Math.abs(Date.now() / 1000 - +ts) > 300)
        return res.json({ ok: false, error: 'Timestamp expired' });
    const info = findToken(token);
    if (!info) return res.json({ ok: false, error: 'Token not found' });
    res.json({ ok: true, type: info.type, url: info.url });
});
package main
import (
    "crypto/hmac"; "crypto/sha256"; "crypto/subtle"
    "encoding/hex"; "encoding/json"; "fmt"; "math"; "net/http"; "time"
)
const secret = "YOUR_SHARED_SECRET_HERE"

type ClaimBody struct {
    Token string `json:"token"`
    Ts    int64  `json:"ts"`
    Nonce string `json:"nonce"`
    Sig   string `json:"sig"`
}
func proxyClaim(w http.ResponseWriter, r *http.Request) {
    var b ClaimBody
    json.NewDecoder(r.Body).Decode(&b)
    msg := fmt.Sprintf("%s|%d|%s", b.Token, b.Ts, b.Nonce)
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(msg))
    expected := hex.EncodeToString(mac.Sum(nil))
    if subtle.ConstantTimeCompare([]byte(expected), []byte(b.Sig)) != 1 {
        json.NewEncoder(w).Encode(map[string]interface{}{"ok": false, "error": "Invalid signature"})
        return
    }
    if math.Abs(float64(time.Now().Unix()-b.Ts)) > 300 {
        json.NewEncoder(w).Encode(map[string]interface{}{"ok": false, "error": "Timestamp expired"})
        return
    }
    json.NewEncoder(w).Encode(map[string]interface{}{
        "ok": true, "type": "live", "url": "http://panel/live/USER/PASS/123.m3u8",
    })
}
Importante en todos los lenguajes: usar siempre una función de comparación en tiempo constante (hash_equals en PHP, hmac.compare_digest en Python, timingSafeEqual en Node.js, subtle.ConstantTimeCompare en Go). Nunca comparar strings con == para datos criptográficos — un atacante puede medir el tiempo de la comparación.

Proxy CONNECT — Tunelizar tráfico saliente a través de ptp-proxy

Además del sistema de claims para los clientes IPTV, el proxy incluye un handler para peticiones HTTP CONNECT. Esto permite que cualquier cliente HTTP (curl, tu app web, u otro servicio) tunelice sus conexiones salientes a través de ptp-proxy, de forma que todo el tráfico salga por la interfaz de red del host donde corre ptp-proxy — independientemente del sistema operativo. Un caso de uso habitual es enrutar las peticiones de tu servicio a través de un host o red diferente.

Cliente HTTP ──CONNECT──→ ptp-proxy (cualquier host) ────→ proveedor IPTV
Sin CONNECT: las peticiones del cliente salen por su propia interfaz de red
Con CONNECT: las peticiones salen por la interfaz de red de ptp-proxy (su IP es la que ve el proveedor)

Configuración en config.json

"connect_proxy": {
  "enabled":         true,
  "use_claim":       false,
  "secret":          "YOUR_SECRET_HERE",
  "allowed_origins": ["127.0.0.1", "::1"],
  "allowed_hosts":   []
}
CampoDescripciónDefault
enabledActiva el handler CONNECT. false → 405 en cualquier CONNECTfalse
use_claimfalse → secreto estático; true → claim HMAC firmadofalse
secretClave compartida. Con use_claim: false es el valor literal; con true es la clave HMAC""
allowed_originsIPs origen autorizadas. Si la IP del cliente no está aquí → 403["127.0.0.1","::1"]
allowed_hostsHostnames de destino permitidos. Vacío = sin restricción[]

Dos modos de autenticación para CONNECT

Modo A — Secreto estático (<code>use_claim: false</code>)

El cliente envía el secreto directamente en cada petición. Más simple. Recomendado cuando el cliente y el proxy están en el mismo host.

function curl_set_ptp_proxy_static(CurlHandle $ch): void {
    curl_setopt($ch, CURLOPT_PROXY,       'http://127.0.0.1:8180');
    curl_setopt($ch, CURLOPT_PROXYTYPE,   CURLPROTO_HTTP);
    curl_setopt($ch, CURLOPT_PROXYHEADER, ['X-Proxy-Secret: YOUR_SECRET_HERE']);
}

Modo B — Claim HMAC (<code>use_claim: true</code>)

El cliente firma cada petición CONNECT con HMAC. El secreto nunca viaja en claro. Necesario si el proxy está expuesto a internet.

function curl_set_ptp_proxy_claim(CurlHandle $ch, string $url): void {
    $parsed = parse_url($url);
    $host   = $parsed['host'];
    $port   = $parsed['port'] ?? ($parsed['scheme'] === 'https' ? 443 : 80);
    $ts    = time();
    $nonce = bin2hex(random_bytes(8));
    $msg   = "connect|{$host}:{$port}|{$ts}|{$nonce}";
    $sig   = hash_hmac('sha256', $msg, 'YOUR_SECRET_HERE');
    curl_setopt($ch, CURLOPT_PROXY,       'http://127.0.0.1:8180');
    curl_setopt($ch, CURLOPT_PROXYTYPE,   CURLPROTO_HTTP);
    curl_setopt($ch, CURLOPT_PROXYHEADER, [
        "X-Proxy-Ts: {$ts}",
        "X-Proxy-Nonce: {$nonce}",
        "X-Proxy-Sig: {$sig}",
    ]);
}

Fórmula del claim HMAC para CONNECT

Fórmula del claim HMAC para CONNECT
message = "connect" + "|" + host + ":" + port + "|" + ts + "|" + nonce
sig     = HMAC-SHA256(secret, message)  →  hex minúsculas
Ejemplo:  message = "connect|api.provider.com:443|1740698753|a3f9c2b1d8e04f72"
  sig     = hash_hmac('sha256', message, secret)

Exponer a internet con seguridad total

Si tu servicio está en un servidor remoto, necesitas que el proxy acepte conexiones desde internet. Con use_claim: true esto es seguro:

"connect_proxy": {
  "enabled":         true,
  "use_claim":       true,
  "secret":          "random-key-minimum-32-chars",
  "allowed_origins": [],
  "allowed_hosts":   ["api.provider1.com", "api.provider2.com"]
}
Seguro
Con use_claim: true
  • Secreto nunca viaja en claro
  • Replay bloqueado (ventana ±60 s)
  • Destinos restringidos por allowed_hosts
  • Sin secreto = sin acceso
Peligroso
Combinaciones prohibidas
  • allowed_origins: [] + use_claim: false + secret: "" → open proxy público
  • allowed_origins: [] + use_claim: false → secreto expuesto en tráfico

Generar un secreto seguro

# Linux / WSL2:
openssl rand -hex 32

Referencia de endpoints HTTP

Referencia pública de canales, VOD, grabaciones, perfiles, almacenamiento y administración. Las rutas de contenido, gestión y permisos de almacenamiento tienen políticas diferentes.

EndpointMétodoAccesoDescripción
/  o  /health GET Público Sonda de disponibilidad del proceso. Puede protegerse con la configuración de health cuando se usa fuera de loopback.
/playlist.m3u GET Público Playlist M3U con los canales habilitados y URLs HLS de ptp-proxy.
/channels/<id>/playlist.m3u8 GET Público Playlist HLS de un canal estático en modo proxy o restream.
/stream?t=<token>[&dl=1] GET Público Resuelve un claim dinámico live, restream, VOD o series y entrega el contenido autorizado.
/download?t=<token> GET Público Alias de descarga para un token de stream autorizado.
/api/transcode/profiles GET Solo gestión Publica IDs, etiquetas, códec y variantes de perfiles instalados sin exponer comandos de ejecución.
/api/channels GET Solo gestión Lista los canales estáticos y el perfil seleccionado.
/api/recordings GET / POST Solo gestión Lista o programa grabaciones de canales y URLs autorizadas.
/api/recordings/<id> GET / DELETE Solo gestión Consulta o cancela una grabación concreta.
/api/storage GET / HEAD Permiso de storage Lista las fuentes visibles para una identidad con storage:list.
/api/storage/<source>/list?path=<relative> GET / HEAD Permiso de storage Lista carpetas y archivos con tamaño, fecha, MIME, ETag y soporte de rangos.
/storage/<source>/<path>[?download=1] GET / HEAD Permiso de storage Entrega un objeto con rangos, seek, condicionales HTTP y política de reproducción o descarga.
/api/storage/sign POST Storage admin Crea un enlace temporal ligado a fuente, ruta, acción y expiración.
/api/storage/<source>/prepare POST Permiso de storage Inicia una operación v2 idempotente en una fuente con preparación habilitada.
/api/storage/<source>/operations/<id> GET / DELETE Permiso de storage Consulta el progreso o cancela una operación asíncrona.
/api/storage/plugins GET / HEAD Storage admin Estado no sensible de las instancias de plugins configuradas.
/api/storage/plugin-providers GET / HEAD Storage admin Inventario de proveedores instalados, versiones, capacidades e integridad.
/api/storage/plugins/<source>/restart POST Storage admin Reinicia administrativamente una fuente plugin.
/metrics GET Solo gestión Métricas Prometheus de servidor, restream, grabaciones, almacenamiento y plugins.
/proxy/raw?url=<encoded> GET Solo gestión Proxy de diagnóstico para una URL permitida. No debe publicarse sin controles de gestión.
CONNECT <host>:<port> CONNECT Público Túnel HTTP CONNECT sujeto a su política de autenticación y destinos.
PTP managed-node channel HTTPS saliente Canal saliente El agente se incorpora, publica heartbeat y recibe trabajos desde PTP. No añade una API administrativa entrante al nodo.
Distingue contenido, gestión y almacenamiento

Las rutas marcadas como gestión usan las IPs y clave generales. Las rutas de almacenamiento usan permisos storage:list, storage:read, storage:download, storage:prepare o storage:admin. El canal de nodo usa una credencial propia y conexión saliente.

Canal administrado
La integración con PTP no expone endpoints públicos adicionales en ptp-proxy. El agente inicia incorporación, heartbeat y long polling hacia las rutas de nodo del servidor PTP.

Referencia rápida con curl

# Listar canales
curl -H "Authorization: Bearer CHANGE_ME" http://127.0.0.1:8180/api/channels
# Listar fuentes de almacenamiento
curl -H "Authorization: Bearer CHANGE_ME_LIST_TOKEN" http://127.0.0.1:8180/api/storage
# Leer un rango de un archivo
curl -H "Authorization: Bearer CHANGE_ME_READ_TOKEN" -H "Range: bytes=0-1048575" http://127.0.0.1:8180/storage/media/films/movie.mkv
# Consultar perfiles
curl -H "Authorization: Bearer CHANGE_ME" http://127.0.0.1:8180/api/transcode/profiles
# Consultar plugins
curl -H "Authorization: Bearer CHANGE_ME_ADMIN_TOKEN" http://127.0.0.1:8180/api/storage/plugins
# Iniciar una preparación
curl -X POST -H "Authorization: Bearer CHANGE_ME_PREPARE_TOKEN" -H "Content-Type: application/json" --data '{"torrent_file":"sample.torrent"}' http://127.0.0.1:8180/api/storage/torrent/prepare

Descargas

Versión: 2026-07-16

⚡ Inicio rápido

  1. Descarga el paquete de tu plataforma y extrae todos los directorios.
  2. Copia config.example.json a config.json.
  3. Decide si usarás canales IPTV, almacenamiento, plugins, modo administrado o una combinación.
  4. Instala FFmpeg solo si vas a restream, transcodificar o grabar.
  5. Instala Python 3 solo si vas a usar proveedores Python incluidos o de terceros.
  6. Linux: chmod +x ptp-proxy && ./ptp-proxy --config config.json.
  7. Windows: ptp-proxy.exe --config config.json.
  8. Comprueba /health, /api/transcode/profiles y, si configuraste fuentes, /api/storage.

Qué incluye el paquete

  • Binario de ptp-proxy para la plataforma seleccionada.
  • Configuración de ejemplo y documentación bilingüe.
  • Perfiles de transcodificación compatibles con PTP.
  • SDK y proveedores externos incluidos.
  • Scripts de pruebas y verificación de release cuando corresponda.
2026-07-16 4 archivos
Plataforma Archivo Tamaño SHA-256
🪟 Windows (x64) ptp-proxy-20260716-windows-amd64.zip 4.9 MB c4fa9424… Descargar
🐧 Linux (x86_64) ptp-proxy-20260716-linux.zip 1.4 MB 15df47cb… Descargar
🐧 Linux ARM64 (RPi 3/4/5, aarch64) ptp-proxy-20260716-linux-arm64.zip 1.3 MB 5af960a7… Descargar
🐧 Linux ARMv7 (RPi 1/2/Zero, 32-bit) ptp-proxy-20260716-linux-armv7.zip 1.2 MB 74504de8… Descargar
2026-03-07 4 archivos
Plataforma Archivo Tamaño SHA-256
🪟 Windows (x64) ptp-proxy-20260307-windows.zip 3.3 MB 9944f8e1… Descargar
🐧 Linux (x86_64) ptp-proxy-20260307-linux.zip 2.7 MB f7f584ce… Descargar
🐧 Linux ARM64 (RPi 3/4/5, aarch64) ptp-proxy-20260307-linux-arm64.zip 2.4 MB e2d7fcd3… Descargar
🐧 Linux ARMv7 (RPi 1/2/Zero, 32-bit) ptp-proxy-20260307-linux-armv7.zip 2 MB 73148ea1… Descargar
2026-02-28 4 archivos
Plataforma Archivo Tamaño SHA-256
🪟 Windows (x64) ptp-proxy-20260228-windows.zip 0.7 MB 628a1786… Descargar
🐧 Linux (x86_64) ptp-proxy-20260228-linux.zip 2.7 MB 13012661… Descargar
🐧 Linux ARM64 (RPi 3/4/5, aarch64) ptp-proxy-20260228-linux-arm64.zip 2.4 MB 50674565… Descargar
🐧 Linux ARMv7 (RPi 1/2/Zero, 32-bit) ptp-proxy-20260228-linux-armv7.zip 2 MB 4b511a5c… Descargar

🐳 Docker

Imagen Docker con todos los plugins, perfiles y documentación incluidos.

docker pull hamboy75/ptp-proxy Docker Hub →