ExplicacionExtensibilidad

Plugins y fuentes

El seam de plugins en TypeScript, su ciclo de vida, la precedencia explícita y el contrato SeekableMediaSource de las fuentes.

Implementado en parte. El seam de plugins en TypeScript (@styx/plugin-sdk) y los tres módulos propios existen. Lo que no existe todavía es la aplicación de permisos, la negociación de versiones y el aislamiento: están declarados y se aplican en track/plugins-sdk. El estado de los gates de track/plugin-seams está en su vista del roadmap.

Dos niveles de plugin

NivelEstado
TypeScript (plugin-sdk)Vivo hoy. Los plugins corren dentro del servicio que los registra
Nativo (C ABI)Objetivo. protocols/plugin-abi/ no se implementa hasta definir el modelo de confianza

Un plugin nunca lee bytes de vídeo: los bytes los lee el daemon (regla r04/r48).

Qué ofrece el seam

@styx/plugin-sdk define cuatro puntos de extensión (PluginKind), cada uno con su contrato en un único mapa, de modo que no puede registrarse un tipo de plugin sin interfaz:

TipoContratoConsumidorQué hace
metadataIMetadataProvidercatalog-svcEnriquece los metadatos de un asset ya indexado
searchISearchProvidercatalog-svcBusca en fuentes externas
sourceISourceProvidersources-svcResuelve un URI a un adaptador de fuente
seedISeedProvidercatalog-svcSiembra un catálogo de demostración al arrancar

Módulos propios incluidos: @styx/plugin-demo-open-cinema (siembra), @styx/plugin-source-local (fuente de ficheros locales) y @styx/plugin-metadata-local-nfo (metadatos desde ficheros .nfo). Cada servicio con plugins instancia su registro estático en src/service/plugins/registry.ts; añadir un proveedor es añadirlo al registro del servicio, sin tocar el seam. Una decisión posterior (dec-0114) añade el escaneo e identificación por contenido como un plugin más sobre este seam.

El manifiesto es declarativo

El manifiesto describe qué es el plugin y qué pediría, pero hoy nadie lo aplica. Lo declarado:

  • Cuatro versiones por separado: la del plugin, la del esquema del manifiesto, la de la API de extensión y la del contrato de dominio. Rotan a ritmos distintos.
  • Nivel de confianza: builtin, official, trusted-native, sandboxed, remote y community. dec-0117 ya fija una consecuencia: sólo los tres primeros pueden ejecutar código dentro del proceso del daemon.
  • Autoridad: local-authoritative o derived. Es entrada de la precedencia: un .nfo escrito por el usuario gana a un dato derivado.
  • Permisos (fs:read, fs:write, net:outbound, secrets:own, bus:publish, bus:subscribe): documentación estructurada; declararlos no concede nada y omitirlos no impide nada.

Precedencia explícita

Entre varios proveedores del mismo tipo, el orden no lo decide el orden de importación. La cadena es: bloqueo manual, después autoridad local, después el orden de proveedores configurado, después el desempate por id. Un bloqueo hacia un plugin inexistente o que no cubre ese tipo es un error de construcción: se falla en el arranque en lugar de elegir en silencio a otro.

Ciclo de vida y salud

Dos máquinas de estados separadas:

  • Ciclo de vida: registered, initializing, active, stopping, stopped. Un fallo de initialize() lleva a stopped; el porqué vive en la salud. La inicialización tiene un plazo.
  • Salud: healthy, degraded, unavailable, failed, misconfigured y disabled. Los estados observados los mueve una sonda libremente; los latcheados (failed, misconfigured) se abandonan siempre pasando por unavailable, nunca saltando a healthy; y disabled es administrativo. Sirven tráfico healthy y degraded: un proveedor caído no debe impedir arrancar el catálogo.

Fuentes: SeekableMediaSource

Toda fuente de medios (local, HTTP con rangos, S3, WebDAV, SFTP, torrent, Jellyfin, Xtream o compuesta) se normaliza a la interfaz ISeekableMediaSource de @styx/source-sdk (decisión r04):

  • lectura por rango, con un propósito (probe, playback, seek, trick-play, subtitle, background-cache) que afecta a prioridad, precarga y expulsión;
  • cancelación por petición (cancel(requestId)), no global;
  • plazo opcional y resultado parcial permitido, con la latencia observada para las métricas.

La misma abstracción existe en Zig dentro del daemon (media-core/source), y los códigos de error de la fuente se generan una vez y se comparten entre ambos lados. Sin FUSE en el camino caliente: el almacenamiento remoto va por su API nativa.