“Un Reproductor de Podcasts Que Sobrevive a la Navegacion”

Hugo es estatico. Cuando haces clic en un enlace, la pagina se recarga. Todo reproductor de audio se reinicia. Los oyentes de podcasts pierden su posicion. Queria un reproductor que siguiera sonando en cada transicion de pagina, que persistiera el estado de reproduccion y que funcionara con emisoras de radio y plataformas de podcast sin necesidad de un framework JavaScript.

Como funciona

Wavecast proporciona dos Web Components personalizados: podcast-player para uso en linea y podcast-footer para una barra inferior fija. Ambos estan sincronizados bidireccionalmente. Pausar cualquier reproductor pausa todo el audio. Solo se reproduce una fuente a la vez. La posicion, el volumen, el silencio y la velocidad de reproduccion se guardan en sessionStorage y se restauran al navegar entre paginas.

El reproductor se distribuye como modulo Hugo y como tema Hugo. Coloca un shortcode en cualquier pagina y se renderiza un reproductor completamente funcional sin configuracion adicional.

Decisiones tecnicas

Web Components en lugar de React o Vue. Un sitio Hugo es HTML. Anadir un framework JavaScript solo para reproducir audio es excesivo. Los Web Components funcionan de forma nativa en todos los navegadores modernos sin dependencias. El Shadow DOM aisla los estilos para que el reproductor nunca colisione con el CSS del sitio anfitrion.

Selectores ::part() para personalizacion visual. El Shadow DOM evita fugas accidentales de estilos, pero tambien impide la personalizacion intencionada. El pseudo-elemento CSS ::part() permite a los usuarios estilizar elementos individuales (boton de play, barra de progreso, control de volumen) desde fuera del Shadow DOM sin necesidad de modificar el componente. Doce propiedades CSS personalizadas controlan colores, tamanos y anillos de enfoque para temas claros y oscuros.

Sincronizacion bidireccional mediante eventos personalizados. Cuando cualquier reproductor se pausa, emite un evento podcast-pause que se propaga por el DOM. Todas las demas instancias del reproductor escuchan estos eventos y se sincronizan. Esto funciona independientemente de si los reproductores estan en la misma pagina o separados por un iframe.

Adaptadores de fuente como sistema de plugins. Cada tipo de fuente de audio (archivos locales, emisoras AzuraCast, episodios de iVoox) tiene su propia clase adaptadora que implementa fetchStreamUrl() y opcionalmente fetchMetadata(). Las reglas de deteccion automatica asocian URLs con adaptadores. Anadir un nuevo tipo de fuente significa implementar una clase y registrarla, sin cambios en el reproductor principal.

Persistencia de navegacion entre frameworks. El reproductor sobrevive a Turbolinks, Turbo, htmx y cargas de pagina tradicionales. Utiliza los atributos data-turbolinks-permanent, data-turbo-permanent y hx-preserve para persistencia especifica del framework, mas un respaldo en sessionStorage para todo lo demas. La restauracion de posicion tiene en cuenta el tiempo transcurrido si el audio estaba reproduciendose cuando el usuario navego fuera.

Lo que aprendi

Construir un Web Component que funcione en todas partes es mas dificil de lo que parece. El Shadow DOM significa que no hay estilos globales, asi que cada boton, deslizador y tooltip tuvo que ser estilizado desde cero. Las APIs de audio entre navegadores se comportan de manera diferente: Safari limita los eventos timeupdate, Firefox no dispara loadedmetadata de forma fiable para ciertos codecs, y la politica de autoplay de Chrome cambia segun la interaccion del usuario.

La suite de pruebas detecto la mayoria de estos problemas. Las pruebas unitarias de Vitest cubren la logica principal. Las pruebas E2E de Playwright levantan un sitio Hugo real con reproductores de ejemplo y verifican que play, pause, seek, volumen y sincronizacion funcionen en todos los navegadores.

Ver en GitHub – Demo en vivo