From 3592b9f95ad3b885d3bc0b78139de7f09a5dd97d Mon Sep 17 00:00:00 2001 From: Deivid Soto Date: Sun, 31 May 2026 10:30:33 +0200 Subject: [PATCH] docs(roadmap): design hueco #3 (device-profile + direct-play + ABR) --- Docs/plans/unarr-agent-roadmap.md | 93 ++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/Docs/plans/unarr-agent-roadmap.md b/Docs/plans/unarr-agent-roadmap.md index accc8b9..7b9fcbd 100644 --- a/Docs/plans/unarr-agent-roadmap.md +++ b/Docs/plans/unarr-agent-roadmap.md @@ -49,10 +49,12 @@ torrent. La promesa "play instantáneo cache-fast" no ocurre. Falta: source debr en el path de streaming + cache-availability + **fallback torrent↔debrid mid-stream**. Diseño por fases (2a direct-play / 2b HLS-desde-URL / 2c fallback) en el estado abajo. -### Hueco #3 — Device-profile + direct-play + ABR ⬜ +### Hueco #3 — Device-profile + direct-play + ABR 🔵 EN CURSO (ver estado abajo) El path HLS **siempre re-encoda** (incluso mp4 h264/aac ya compatible). `DecideAction` (passthrough/remux) existe pero muerto en el path browser. Sin negociación por capacidades del dispositivo. Sin ABR multi-bitrate. +Diseño por fases (3a direct-play / 3b remux-HLS / 3c capability-negotiation / 3d ABR) +en el estado abajo. Fase 3a en implementación. ### Huecos medios ⬜ - Sin gestión de espacio en disco (`Statfs`) → disco lleno revienta a mitad. @@ -194,3 +196,92 @@ Empezar por 2a (valor inmediato, riesgo bajo), 2b y 2c como iteraciones. **Mejora detectada:** `resolve.go:22` ordena `torrent > debrid > usenet`; para el diferenciador cache-fast convendría que, **cuando hay cache debrid confirmada**, el orden de STREAMING (no el de descarga) prefiera debrid. + +--- + +### Hueco #3 — Device-profile + direct-play + ABR +**Estado:** 🔵 EN CURSO (2026-05-31). Análisis cerrado; fase 3a en implementación. + +**Problema (confirmado en el análisis):** +- El path browser usa **HLS y SIEMPRE re-encoda**: `buildHLSFFmpegArgsAt` + (`engine/hls.go`) pone `-c:v libx264|nvenc|…` + cadena de filtros completa + (scale/format/setparams) + AAC, sin rama de copia. Un mp4 h264/aac 8-bit SDR + que el navegador reproduciría tal cual se transcodifica entero. Coste de CPU + puro desperdicio. +- `DecideAction` + `diskFileSource`/`transcodeSource` (`engine/probe.go`, + `engine/stream_source.go`) **son código muerto**: cero callers en producción, + solo tests. Distinguen `passthrough/remux/remux-audio/transcode-video` y detectan + 10-bit/HDR — la lógica de decisión ya existe, no está cableada. + +**Lo que ya hay y se reaprovecha:** +- El agente ya expone **dos paths** en el StreamServer (puerto 11818): + - `/stream` → sirve el fichero crudo con `http.ServeContent` (HTTP Range + completo, sin ffmpeg, ya tokenizado). **Direct-play ya es posible aquí.** + - `/hls//…` → transcode HLS. +- El web **construye las URLs** (HLS hoy) desde la info de red del agente + (`streamPort`, `tailscaleIp`, `lanIp`, `funnelUrl`, `streamSecret`) y **puede + mintear tokens** (`mintStreamToken`, scope `stream` es constante). O sea: el web + puede construir la URL `/stream?t=…` de direct-play él mismo. +- `libraryItem` ya guarda del scan: `videoCodec`, `audioCodec`, `bitDepth`, `hdr`, + `resolution`. Con el contenedor (extensión de `fileName`), el web tiene todo + para decidir direct-play SIN re-probar. + +**Diseño por fases (de menos a más riesgo):** + +- **Fase 3a — direct-play passthrough para items de biblioteca.** *El web decide.* + *Slice acotado, ambos sentidos de version-skew seguros vía gate de versión.* + 1. WEB `decidePlayMethod({videoCodec,audioCodec,bitDepth,hdr,container})` → + `"direct" | "hls"` (espeja la rama passthrough de Go `DecideAction`: solo + `mp4/m4v` + `h264` + `aac` + 8-bit + SDR → direct; todo lo demás → hls). + 2. WEB gate: `supportsDirectPlay(agentVersion)` (constante de versión mínima). + Direct-play solo si el agente la soporta; si no → hls (sin regresión). + 3. WEB sesión: en la rama `libraryItemPublicId`, seleccionar los campos codec; + calcular `playMethod` (gated); persistirlo en `streamingSession.play_method` + (migración aditiva, `db:generate`); devolver `playMethod` + `streamUrls` + (`/stream?t=` minteadas por el web, lan/ts/funnel) en la respuesta. + 4. WEB sync: `getPendingStreamSessions` emite `playMethod` al agente. + 5. CLI: `StreamSession.PlayMethod string`; en `OnStreamSession`, si + `PlayMethod=="direct"` → `streamSrv.SetFile(NewDiskFileProvider(path))` + + `MarkSessionReady` (sin ffmpeg). Else → `StartHLSSession` (actual). + 6. WEB player (`HlsStreamPlayer.tsx`): si `data.playMethod==="direct"` → usar + `data.streamUrls` + attach nativo `