Instancia transcoder t1: cambios locales

This commit is contained in:
Joaquin
2026-07-13 15:19:13 +00:00
parent 0e26a90dae
commit bb189a472e
27 changed files with 2161 additions and 496 deletions
+162 -166
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="es">
<head>
<meta charset="UTF-8">
@@ -7,14 +7,14 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="mobile-web-app-capable" content="yes">
<title>nodecast-tv | IPTV Player</title>
<meta name="description" content="Stream live TV, movies, and series with EPG support">
<title>KiraTV | Televisión en el navegador</title>
<meta name="description" content="TV en directo, películas y series con guía EPG — KiraTV">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="stylesheet" href="css/main.css">
<link rel="apple-touch-icon" href="/img/kiratv-logo-hero.jpg">
<link rel="stylesheet" href="css/main.css?v=kira6">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Outfit:wght@800&display=swap"
rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&family=Montserrat:wght@500;600;700;800&display=swap" rel="stylesheet">
<!-- HLS.js for video playback -->
<script src="https://cdn.jsdelivr.net/npm/hls.js@1.5.7/dist/hls.min.js"></script>
</head>
@@ -24,15 +24,10 @@
<!-- Navigation -->
<nav class="navbar">
<div class="navbar-brand">
<span class="logo"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 187.63 210.6" fill="#6366f1"
class="icon">
<path
d="M169.71,87.87c-4.77,0-9.1,1.88-12.31,4.92L35.3,22.3c.35-1.4.56-2.86.56-4.37C35.86,8.03,27.83,0,17.93,0S0,8.03,0,17.93c0,8.44,5.84,15.5,13.69,17.4v140c-7.71,2.01-13.41,8.99-13.41,17.33,0,9.9,8.03,17.93,17.93,17.93s17.93-8.03,17.93-17.93c0-1.56-.22-3.07-.6-4.51l121.21-69.98c3.26,3.42,7.86,5.55,12.95,5.55,9.9,0,17.93-8.03,17.93-17.93s-8.03-17.93-17.93-17.93ZM152.78,99.92c-.14.41-.27.83-.38,1.25h-51.47l-44.18-24.72L30.4,30.8c.33-.32.64-.65.94-.99l121.45,70.12ZM22.17,175.2V35.33c.23-.05.45-.11.67-.17l26.35,45.65-.34,50.04-25.74,44.59c-.31-.09-.63-.17-.95-.24ZM31.52,180.69c-.27-.3-.55-.6-.84-.89l25.48-44.13,44.64-25.78h51.47c.07.32.15.63.24.94L31.52,180.69Z" />
</svg></span>
<span class="brand-text">nodecast-tv</span>
<img src="/img/kiratv-logo-hero.jpg" alt="KiraTV" class="navbar-logo-img" width="176" height="44" decoding="async">
<span id="version-badge" class="version-badge"></span>
</div>
<button class="mobile-menu-toggle" id="mobile-menu-toggle" aria-label="Toggle menu">
<button class="mobile-menu-toggle" id="mobile-menu-toggle" aria-label="Abrir o cerrar menú">
<span></span>
<span></span>
<span></span>
@@ -41,14 +36,14 @@
<!-- Now Playing indicator for movies/series -->
<a href="#" class="now-playing-indicator hidden" id="now-playing-indicator" data-page="watch">
<span class="now-playing-icon">▶</span>
<span class="now-playing-text" id="now-playing-text">Now Playing</span>
<span class="now-playing-text" id="now-playing-text">Reproduciendo</span>
</a>
<a href="#" class="nav-link active" data-page="home">
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
class="icon">
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
</svg></span>
<span>Home</span>
<span>Inicio</span>
</a>
<a href="#" class="nav-link" data-page="live">
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
@@ -56,14 +51,14 @@
<path
d="M21 6h-7.59l3.29-3.29L16 2l-4 4-4-4-.71.71L10.59 6H3a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8a2 2 0 0 0-2-2zm0 14H3V8h18v12zM9 10v8l7-4z" />
</svg></span>
<span>Live TV</span>
<span>TV en directo</span>
</a>
<a href="#" class="nav-link" data-page="guide">
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
class="icon">
<path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z" />
</svg></span>
<span>TV Guide</span>
<span>Guía TV</span>
</a>
<a href="#" class="nav-link" data-page="movies">
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
@@ -71,7 +66,7 @@
<path
d="M18 4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2V4h-4z" />
</svg></span>
<span>Movies</span>
<span>Películas</span>
</a>
<a href="#" class="nav-link" data-page="series">
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
@@ -87,7 +82,7 @@
<path
d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L3.16 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.04.64.09.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.58 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z" />
</svg></span>
<span>Settings</span>
<span>Ajustes</span>
</a>
</div>
</nav>
@@ -97,7 +92,7 @@
<!-- Home (Dashboard) Page -->
<div id="page-home" class="page active">
<!-- Content will be rendered by HomePage.js -->
<div class="loading-state">Loading Dashboard...</div>
<div class="loading-state">Cargando inicio…</div>
</div>
<!-- Live TV Page (formerly Home) -->
@@ -108,14 +103,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
<path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z" />
</svg>
<span>Channels</span>
<span>Canales</span>
</button>
<!-- Overlay for drawer -->
<div class="channel-sidebar-overlay" id="channel-sidebar-overlay"></div>
<!-- Expand button (visible when sidebar collapsed on desktop) -->
<button class="sidebar-expand-btn" id="sidebar-expand-btn" title="Expand sidebar">
<button class="sidebar-expand-btn" id="sidebar-expand-btn" title="Mostrar panel de canales">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
<path d="M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z" />
</svg>
@@ -126,11 +121,11 @@
<div class="sidebar-header">
<div class="sidebar-header-row">
<div class="search-wrapper">
<input type="text" id="channel-search" placeholder="Search channels..." class="search-input">
<button type="button" class="search-clear" title="Clear search">&times;</button>
<input type="text" id="channel-search" placeholder="Buscar canales…" class="search-input">
<button type="button" class="search-clear" title="Limpiar búsqueda">&times;</button>
</div>
<button id="sidebar-collapse-btn" class="btn btn-sm btn-ghost sidebar-collapse-btn"
title="Collapse sidebar">
title="Ocultar panel">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" />
</svg>
@@ -138,9 +133,9 @@
</div>
<div class="sidebar-controls">
<select id="source-select" class="source-select">
<option value="">All Sources</option>
<option value="">Todas las fuentes</option>
</select>
<button id="toggle-groups" class="btn btn-sm btn-ghost" title="Expand/Collapse All">
<button id="toggle-groups" class="btn btn-sm btn-ghost" title="Expandir o plegar todo">
<span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
<path
d="M12 5.83L15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9 12 5.83zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15 12 18.17z" />
@@ -151,8 +146,8 @@
</div>
<div id="channel-list" class="channel-list">
<div class="empty-state">
<p>No channels loaded</p>
<p class="hint">Add a source in Settings to get started</p>
<p>Cargando canales…</p>
<p class="hint">Las categorías vienen plegadas: pulsa un nombre de grupo para ver los canales. Puedes usar «Expandir/plegar todo» arriba.</p>
</div>
</div>
</aside>
@@ -225,9 +220,9 @@
</svg>
</button>
<div class="watch-captions-menu hidden" id="player-captions-menu">
<div class="captions-menu-title">Subtitles</div>
<div class="captions-menu-title">Subtítulos</div>
<div class="captions-menu-list" id="player-captions-list">
<button class="captions-option active" data-index="-1">Off</button>
<button class="captions-option active" data-index="-1">Desactivados</button>
</div>
</div>
</div>
@@ -257,7 +252,7 @@
<path
d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" />
</svg>
Copy Stream URL
Copiar URL del stream
</button>
</div>
</div>
@@ -267,18 +262,18 @@
<div class="player-overlay" id="player-overlay">
<div class="overlay-content">
<p>Select a channel to start watching</p>
<p>Elige un canal para empezar</p>
</div>
</div>
<!-- Now Playing Overlay -->
<div class="now-playing-overlay hidden" id="now-playing">
<div class="now-playing-info">
<h3 class="channel-name">No channel selected</h3>
<h3 class="channel-name">Ningún canal seleccionado</h3>
<p class="program-title"></p>
<p class="program-time"></p>
</div>
<div class="up-next">
<h4>Up Next</h4>
<h4>A continuación</h4>
<ul id="up-next-list"></ul>
</div>
</div>
@@ -290,24 +285,24 @@
<!-- Guide Page -->
<div id="page-guide" class="page">
<div class="guide-header">
<h2>TV Guide</h2>
<h2>Guía TV</h2>
<div class="guide-controls">
<select id="epg-group-select" class="source-select">
<option value="">All Groups</option>
<option value="">Todos los grupos</option>
</select>
<div class="search-wrapper">
<input type="text" id="epg-search" placeholder="Search channels..." class="search-input">
<button type="button" class="search-clear" title="Clear search">&times;</button>
<input type="text" id="epg-search" placeholder="Buscar canales…" class="search-input">
<button type="button" class="search-clear" title="Limpiar búsqueda">&times;</button>
</div>
<button id="guide-prev" class="btn btn-ghost">&larr; Earlier</button>
<span id="guide-date" class="guide-date">Today</span>
<button id="guide-next" class="btn btn-ghost">Later &rarr;</button>
<button id="guide-prev" class="btn btn-ghost">&larr; Antes</button>
<span id="guide-date" class="guide-date">Hoy</span>
<button id="guide-next" class="btn btn-ghost">Después &rarr;</button>
</div>
</div>
<div id="epg-grid" class="epg-grid">
<div class="empty-state">
<p>No EPG data available</p>
<p class="hint">Add an EPG source in Settings</p>
<p>No hay datos de guía (EPG)</p>
<p class="hint">Añade una fuente EPG en Ajustes</p>
</div>
</div>
</div>
@@ -315,29 +310,29 @@
<!-- Movies Page -->
<div id="page-movies" class="page">
<div class="movies-header">
<h2>Movies</h2>
<h2>Películas</h2>
<div class="movies-controls">
<select id="movies-source-select" class="source-select">
<option value="">All Sources</option>
<option value="">Todas las fuentes</option>
</select>
<select id="movies-category-select" class="source-select">
<option value="">All Categories</option>
<option value="">Todas las categorías</option>
</select>
<div class="search-wrapper">
<input type="text" id="movies-search" placeholder="Search movies..." class="search-input">
<button type="button" class="search-clear" title="Clear search">&times;</button>
<input type="text" id="movies-search" placeholder="Buscar películas…" class="search-input">
<button type="button" class="search-clear" title="Limpiar búsqueda">&times;</button>
</div>
<button id="movies-favorites-btn" class="btn btn-icon btn-ghost" title="Show Favorites Only">
<button id="movies-favorites-btn" class="btn btn-icon btn-ghost" title="Solo favoritos">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
<path
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
</svg> Favorites
</svg> Favoritos
</button>
</div>
</div>
<div id="movies-grid" class="movies-grid">
<div class="empty-state">
<p>Loading movies...</p>
<p>Cargando películas…</p>
</div>
</div>
</div>
@@ -348,20 +343,20 @@
<h2>Series</h2>
<div class="series-controls">
<select id="series-source-select" class="source-select">
<option value="">All Sources</option>
<option value="">Todas las fuentes</option>
</select>
<select id="series-category-select" class="source-select">
<option value="">All Categories</option>
<option value="">Todas las categorías</option>
</select>
<div class="search-wrapper">
<input type="text" id="series-search" placeholder="Search series..." class="search-input">
<button type="button" class="search-clear" title="Clear search">&times;</button>
<input type="text" id="series-search" placeholder="Buscar series…" class="search-input">
<button type="button" class="search-clear" title="Limpiar búsqueda">&times;</button>
</div>
<button id="series-favorites-btn" class="btn btn-icon btn-ghost" title="Show Favorites Only">
<button id="series-favorites-btn" class="btn btn-icon btn-ghost" title="Solo favoritos">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
<path
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
</svg> Favorites
</svg> Favoritos
</button>
</div>
</div>
@@ -369,12 +364,12 @@
<div class="series-content">
<div id="series-grid" class="series-grid">
<div class="empty-state">
<p>Loading series...</p>
<p>Cargando series…</p>
</div>
</div>
<!-- Series details panel (seasons/episodes) -->
<div id="series-details" class="series-details hidden">
<button class="series-back-btn">← Back to Series</button>
<button class="series-back-btn">← Volver a series</button>
<div class="series-details-header">
<img id="series-poster" src="" alt="" class="series-poster">
<div class="series-info">
@@ -390,59 +385,59 @@
<!-- Settings Page -->
<div id="page-settings" class="page">
<div class="settings-container">
<h2>Settings</h2>
<h2>Ajustes</h2>
<!-- Tabs -->
<div class="tabs">
<button class="tab active" data-tab="sources">Sources</button>
<button class="tab" data-tab="player">Player</button>
<button class="tab" data-tab="transcode">Transcoding</button>
<button class="tab" data-tab="content">Manage Content</button>
<button class="tab" data-tab="users" id="users-tab" style="display:none;">Users</button>
<button class="tab active" data-tab="sources">Fuentes</button>
<button class="tab" data-tab="player">Reproductor</button>
<button class="tab" data-tab="transcode">Transcodificación</button>
<button class="tab" data-tab="content">Contenido</button>
<button class="tab" data-tab="users" id="users-tab" style="display:none;">Usuarios</button>
</div>
<!-- Sources Tab -->
<div id="tab-sources" class="tab-content active">
<div class="source-section">
<div class="section-header">
<h3>Xtream Connections</h3>
<button class="btn btn-primary" id="add-xtream">+ Add Xtream</button>
<h3>Conexiones Xtream</h3>
<button class="btn btn-primary" id="add-xtream">+ Añadir Xtream</button>
</div>
<div id="xtream-list" class="source-list"></div>
</div>
<div class="source-section">
<div class="section-header">
<h3>M3U Playlists</h3>
<button class="btn btn-primary" id="add-m3u">+ Add M3U</button>
<h3>Listas M3U</h3>
<button class="btn btn-primary" id="add-m3u">+ Añadir M3U</button>
</div>
<div id="m3u-list" class="source-list"></div>
</div>
<div class="source-section">
<div class="section-header">
<h3>EPG Sources</h3>
<button class="btn btn-primary" id="add-epg">+ Add EPG</button>
<h3>Fuentes EPG</h3>
<button class="btn btn-primary" id="add-epg">+ Añadir EPG</button>
</div>
<div id="epg-list" class="source-list"></div>
</div>
<div class="source-section">
<h3>EPG Data Settings</h3>
<h3>Datos EPG</h3>
<div class="setting-item">
<label for="epg-refresh-interval">Auto-refresh EPG data every:</label>
<label for="epg-refresh-interval">Actualizar datos EPG automáticamente cada:</label>
<select id="epg-refresh-interval" class="form-input">
<option value="1">1 hour</option>
<option value="2">2 hours</option>
<option value="4">4 hours</option>
<option value="12">12 hours</option>
<option value="24" selected>24 hours</option>
<option value="0">Manual only</option>
<option value="1">1 hora</option>
<option value="2">2 horas</option>
<option value="4">4 horas</option>
<option value="12">12 horas</option>
<option value="24" selected>24 horas</option>
<option value="0">Solo manual</option>
</select>
</div>
<div class="setting-item">
<span class="setting-label">Last Refreshed:</span>
<span id="epg-last-refreshed" class="hint">Never</span>
<span class="setting-label">Última actualización:</span>
<span id="epg-last-refreshed" class="hint">Nunca</span>
</div>
<p class="hint">Use the <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
class="icon" style="width: 1em; height: 1em; vertical-align: middle;">
@@ -455,32 +450,32 @@
<!-- Player Tab -->
<div id="tab-player" class="tab-content">
<div class="settings-section">
<h3>Keyboard Controls</h3>
<h3>Teclado</h3>
<div class="setting-item">
<label class="setting-toggle">
<input type="checkbox" id="setting-arrow-keys" checked>
<span class="toggle-slider"></span>
</label>
<div class="setting-info">
<span class="setting-label">Arrow keys change channels</span>
<span class="setting-hint">When enabled, ↑/↓ change channels and ←/→ adjust volume</span>
<span class="setting-label">Las flechas cambian de canal</span>
<span class="setting-hint">Si está activo: ↑/↓ cambian canal y ←/→ ajustan volumen</span>
</div>
</div>
</div>
<div class="settings-section">
<h3>Now Playing Overlay</h3>
<h3>Overlay «Reproduciendo»</h3>
<div class="setting-item">
<label for="setting-overlay-duration">Display Duration (seconds)</label>
<label for="setting-overlay-duration">Duración en pantalla (segundos)</label>
<input type="number" id="setting-overlay-duration" class="form-input setting-number" min="1" max="30"
value="5">
</div>
</div>
<div class="settings-section">
<h3>Volume Settings</h3>
<h3>Volumen</h3>
<div class="setting-item">
<label for="setting-default-volume">Default Volume</label>
<label for="setting-default-volume">Volumen por defecto</label>
<div class="volume-slider-container">
<input type="range" id="setting-default-volume" class="volume-slider" min="0" max="100" value="80">
<span id="volume-value">80%</span>
@@ -492,36 +487,36 @@
<span class="toggle-slider"></span>
</label>
<div class="setting-info">
<span class="setting-label">Remember Last Volume</span>
<span class="setting-hint">When enabled, volume persists between sessions</span>
<span class="setting-label">Recordar último volumen</span>
<span class="setting-hint">Si está activo, el volumen se conserva entre sesiones</span>
</div>
</div>
</div>
<div class="settings-section">
<h3>Playback</h3>
<h3>Reproducción</h3>
<div class="setting-item">
<label class="setting-toggle">
<input type="checkbox" id="setting-autoplay-next">
<span class="toggle-slider"></span>
</label>
<div class="setting-info">
<span class="setting-label">Auto-play Next Episode</span>
<span class="setting-hint">Automatically play the next episode when one ends</span>
<span class="setting-label">Autoplay del siguiente episodio</span>
<span class="setting-hint">Cuando termine un episodio, pasa automáticamente al siguiente</span>
</div>
</div>
</div>
<div class="settings-section">
<h3>Keyboard Shortcuts</h3>
<h3>Atajos de teclado</h3>
<div class="shortcuts-grid">
<div class="shortcut"><kbd>↑</kbd> / <kbd>↓</kbd> Channel up/down (or volume)</div>
<div class="shortcut"><kbd>←</kbd> / <kbd>→</kbd> Volume down/up (when arrows = channels)</div>
<div class="shortcut"><kbd>Space</kbd> / <kbd>K</kbd> Play/Pause</div>
<div class="shortcut"><kbd>F</kbd> Fullscreen</div>
<div class="shortcut"><kbd>M</kbd> Mute/Unmute</div>
<div class="shortcut"><kbd>I</kbd> Toggle info overlay</div>
<div class="shortcut"><kbd>PgUp</kbd> / <kbd>PgDn</kbd> Channel up/down</div>
<div class="shortcut"><kbd>↑</kbd> / <kbd>↓</kbd> Canal anterior/siguiente (o volumen)</div>
<div class="shortcut"><kbd>←</kbd> / <kbd>→</kbd> Bajar/subir volumen (si flechas = canales)</div>
<div class="shortcut"><kbd>Espacio</kbd> / <kbd>K</kbd> Reproducir / Pausa</div>
<div class="shortcut"><kbd>F</kbd> Pantalla completa</div>
<div class="shortcut"><kbd>M</kbd> Silenciar / activar sonido</div>
<div class="shortcut"><kbd>I</kbd> Mostrar u ocultar información</div>
<div class="shortcut"><kbd>Re Pág</kbd> / <kbd>Av Pág</kbd> Canal anterior/siguiente</div>
</div>
</div>
</div>
@@ -529,14 +524,14 @@
<!-- Transcoding Tab -->
<div id="tab-transcode" class="tab-content">
<div class="settings-section">
<h3>Detected Hardware</h3>
<h3>Hardware detectado</h3>
<div id="hw-info-container" class="hw-info-container">
<p class="hint">Loading hardware information...</p>
<p class="hint">Cargando información del hardware…</p>
</div>
</div>
<div class="settings-section">
<h3>Encoder Settings</h3>
<h3>Codificador</h3>
<div class="setting-item">
<div class="setting-info" style="flex: 1;">
<span class="setting-label">Hardware Encoder</span>
@@ -678,12 +673,12 @@
<div class="setting-item">
<div class="setting-info" style="flex: 1;">
<span class="setting-label">Stream Output Format</span>
<span class="setting-hint">Container format for Xtream streams. Try TS if you experience buffering
issues.</span>
<span class="setting-hint">Para Dispatcharr: elige <strong>TS</strong> (aunque la URL lleve .m3u8, suele
ser video MPEG-TS; HLS falla en el navegador).</span>
</div>
<select id="setting-stream-format-tc" class="source-select" style="width: auto; min-width: 200px;">
<option value="m3u8">HLS (m3u8) - Recommended</option>
<option value="ts">MPEG-TS (ts) - Better buffer</option>
<option value="ts">MPEG-TS (ts) — recomendado con Dispatcharr</option>
<option value="m3u8">HLS (m3u8) — solo si el proveedor es manifiesto real</option>
</select>
</div>
</div>
@@ -734,30 +729,30 @@
<div class="content-browser">
<div class="content-browser-header">
<div class="content-type-toggle">
<button class="btn btn-sm active" id="content-type-channels">Channels</button>
<button class="btn btn-sm" id="content-type-movies">Movies</button>
<button class="btn btn-sm active" id="content-type-channels">Canales</button>
<button class="btn btn-sm" id="content-type-movies">Películas</button>
<button class="btn btn-sm" id="content-type-series">Series</button>
</div>
<select id="content-source-select" class="source-select">
<option value="">Select a source...</option>
<option value="">Selecciona una fuente…</option>
</select>
<div class="search-wrapper">
<input type="text" id="content-search" placeholder="Search groups/channels..." class="search-input">
<button type="button" class="search-clear" title="Clear search">&times;</button>
<input type="text" id="content-search" placeholder="Buscar grupos o canales…" class="search-input">
<button type="button" class="search-clear" title="Limpiar búsqueda">&times;</button>
</div>
<div class="content-actions">
<button class="btn btn-sm btn-secondary" id="content-show-all">Show All</button>
<button class="btn btn-sm btn-secondary" id="content-hide-all">Hide All</button>
<button class="btn btn-sm btn-secondary" id="content-show-all">Mostrar todo</button>
<button class="btn btn-sm btn-secondary" id="content-hide-all">Ocultar todo</button>
<button class="btn btn-sm btn-primary" id="content-save"><svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" fill="currentColor" class="icon">
<path
d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z" />
</svg> Save Changes</button>
</svg> Guardar cambios</button>
</div>
</div>
<p class="hint" style="margin-bottom: var(--space-sm);">✓ Checked = Visible, ☐ Unchecked = Hidden</p>
<p class="hint" style="margin-bottom: var(--space-sm);">✓ Marcado = visible · ☐ Sin marcar = oculto</p>
<div id="content-tree" class="content-tree">
<p class="hint">Select a source to view groups and channels</p>
<p class="hint">Selecciona una fuente para ver grupos y canales</p>
</div>
</div>
</div>
@@ -765,47 +760,47 @@
<!-- Users Tab (Admin Only) -->
<div id="tab-users" class="tab-content">
<div class="settings-section">
<h3>User Management</h3>
<p class="hint">Manage user accounts and permissions</p>
<h3>Usuarios</h3>
<p class="hint">Gestiona cuentas y permisos</p>
<div class="user-list-container">
<table class="user-table">
<thead>
<tr>
<th>Username</th>
<th>Email</th>
<th>Role</th>
<th>Created</th>
<th>Actions</th>
<th>Usuario</th>
<th>Correo</th>
<th>Rol</th>
<th>Creado</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="user-list">
<tr>
<td colspan="4" class="hint">Loading users...</td>
<td colspan="4" class="hint">Cargando usuarios…</td>
</tr>
</tbody>
</table>
</div>
<div class="add-user-section">
<h4>Add New User</h4>
<h4>Añadir usuario</h4>
<form id="add-user-form" class="user-form">
<div class="form-group">
<label for="new-username">Username</label>
<label for="new-username">Usuario</label>
<input type="text" id="new-username" name="username" class="form-input" required>
</div>
<div class="form-group">
<label for="new-password">Password</label>
<label for="new-password">Contraseña</label>
<input type="password" id="new-password" name="password" class="form-input" required minlength="6">
</div>
<div class="form-group">
<label for="new-role">Role</label>
<label for="new-role">Rol</label>
<select id="new-role" name="role" class="form-input" required>
<option value="viewer">Viewer</option>
<option value="admin">Admin</option>
<option value="viewer">Espectador</option>
<option value="admin">Administrador</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Add User</button>
<button type="submit" class="btn btn-primary">Añadir usuario</button>
</form>
</div>
</div>
@@ -823,7 +818,7 @@
<div class="watch-overlay" id="watch-overlay">
<!-- Top Bar: Back + Title -->
<div class="watch-top-bar vod-header">
<button class="watch-back-btn" id="watch-back-btn" title="Back">
<button class="watch-back-btn" id="watch-back-btn" title="Volver">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
</svg>
@@ -901,9 +896,9 @@
</svg>
</button>
<div class="watch-captions-menu hidden" id="watch-captions-menu">
<div class="captions-menu-title">Subtitles</div>
<div class="captions-menu-title">Subtítulos</div>
<div class="captions-menu-list" id="watch-captions-list">
<button class="captions-option active" data-index="-1">Off</button>
<button class="captions-option active" data-index="-1">Desactivados</button>
</div>
</div>
</div>
@@ -932,7 +927,7 @@
<path
d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" />
</svg>
Copy Stream URL
Copiar URL del stream
</button>
</div>
</div>
@@ -941,7 +936,7 @@
<!-- Scroll Indicator -->
<div class="watch-scroll-hint" id="watch-scroll-hint">
<span>Scroll for details</span>
<span>Desplázate para ver detalles</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
<path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
</svg>
@@ -951,12 +946,12 @@
<!-- Next Episode Panel -->
<div class="watch-next-episode hidden" id="watch-next-episode">
<div class="next-info">
<p class="next-label">Up Next</p>
<p class="next-label">Siguiente</p>
<h4 id="next-episode-title"></h4>
</div>
<span class="next-countdown" id="next-countdown">10</span>
<button class="btn btn-primary btn-sm" id="next-play-now">Play Now</button>
<button class="btn btn-ghost btn-sm" id="next-cancel">Cancel</button>
<button class="btn btn-primary btn-sm" id="next-play-now">Reproducir ya</button>
<button class="btn btn-ghost btn-sm" id="next-cancel">Cancelar</button>
</div>
</section>
@@ -978,7 +973,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
<path d="M8 5v14l11-7z" />
</svg>
<span id="watch-play-btn-text">Play</span>
<span id="watch-play-btn-text">Reproducir</span>
</button>
<button class="btn btn-ghost" id="watch-favorite-btn">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
@@ -991,7 +986,7 @@
<path
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
</svg>
<span>Favorite</span>
<span>Favorito</span>
</button>
</div>
</div>
@@ -999,7 +994,7 @@
<!-- Movies: Recommended Section -->
<div class="watch-recommended hidden" id="watch-recommended">
<h3>More Like This</h3>
<h3>Te puede gustar</h3>
<div class="watch-recommended-grid" id="watch-recommended-grid">
<!-- Movie cards inserted dynamically -->
</div>
@@ -1007,7 +1002,7 @@
<!-- Series: Seasons & Episodes -->
<div class="watch-episodes hidden" id="watch-episodes">
<h3>Episodes</h3>
<h3>Episodios</h3>
<div class="watch-seasons" id="watch-seasons">
<!-- Season accordions inserted dynamically -->
</div>
@@ -1022,7 +1017,7 @@
<div id="modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3 id="modal-title">Modal</h3>
<h3 id="modal-title">Ventana</h3>
<button class="modal-close">&times;</button>
</div>
<div id="modal-body" class="modal-body"></div>
@@ -1035,19 +1030,19 @@
<button class="context-item" data-action="play"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="currentColor" class="icon">
<path d="M8 5v14l11-7z" />
</svg> Play</button>
</svg> Reproducir</button>
<button class="context-item" data-action="hide"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="currentColor" class="icon">
<path
d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-4.01.7l2.16 2.16C10.85 7.18 11.4 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z" />
</svg> Hide</button>
</svg> Ocultar</button>
</div>
<!-- Edit User Modal -->
<div class="modal-overlay" id="edit-user-modal">
<div class="modal">
<div class="modal-header">
<h3 class="modal-title">Edit User</h3>
<h3 class="modal-title">Editar usuario</h3>
<button class="modal-close" id="edit-user-close">&times;</button>
</div>
<div class="modal-body">
@@ -1055,41 +1050,41 @@
<input type="hidden" id="edit-user-id">
<div class="modal-form-group">
<label for="edit-username">Username</label>
<label for="edit-username">Usuario</label>
<input type="text" id="edit-username" name="username" class="form-input" required>
</div>
<div class="modal-form-group">
<label for="edit-email">Email</label>
<input type="text" id="edit-email" class="form-input" readonly disabled placeholder="No email associated">
<label for="edit-email">Correo</label>
<input type="text" id="edit-email" class="form-input" readonly disabled placeholder="Sin correo asociado">
</div>
<div class="modal-form-group">
<label for="edit-role">Role</label>
<label for="edit-role">Rol</label>
<select id="edit-role" name="role" class="form-input" required>
<option value="viewer">Viewer</option>
<option value="admin">Admin</option>
<option value="viewer">Espectador</option>
<option value="admin">Administrador</option>
</select>
</div>
<div class="modal-form-group">
<label for="edit-password">New Password</label>
<label for="edit-password">Nueva contraseña</label>
<input type="password" id="edit-password" name="password" class="form-input"
placeholder="Leave blank to keep current">
<p class="hint" id="edit-password-hint" style="margin-top: 4px;">Optional. Leave blank to keep unchanged.
placeholder="Dejar en blanco para no cambiar">
<p class="hint" id="edit-password-hint" style="margin-top: 4px;">Opcional. Déjalo en blanco para mantener la actual.
</p>
</div>
<div class="modal-form-group hidden" id="oidc-info-group">
<label>OIDC Provider ID</label>
<label>ID de proveedor OIDC</label>
<code id="edit-oidc-id"
style="font-size: 0.8rem; color: var(--color-text-muted); padding: 4px; background: rgba(0,0,0,0.3); border-radius: 4px; display: block;"></code>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" id="edit-user-cancel">Cancel</button>
<button class="btn btn-primary" id="edit-user-save">Save Changes</button>
<button class="btn btn-ghost" id="edit-user-cancel">Cancelar</button>
<button class="btn btn-primary" id="edit-user-save">Guardar cambios</button>
</div>
</div>
</div>
@@ -1097,7 +1092,8 @@
<!-- JavaScript -->
<script src="/js/icons.js"></script>
<script src="/js/api.js?v=2"></script>
<script src="/js/components/VideoPlayer.js?v=2"></script>
<script src="/js/desktopPlayback.js?v=9"></script>
<script src="/js/components/VideoPlayer.js?v=6"></script>
<script src="/js/components/ChannelList.js?v=3"></script>
<script src="/js/components/SourceManager.js?v=2"></script>
<script src="/js/components/EpgGuide.js?v=2"></script>
@@ -1106,9 +1102,9 @@
<script src="/js/pages/Guide.js?v=2"></script>
<script src="/js/pages/MoviesPage.js?v=4"></script>
<script src="/js/pages/SeriesPage.js?v=1"></script>
<script src="/js/pages/Settings.js?v=2"></script>
<script src="/js/pages/WatchPage.js?v=1"></script>
<script src="/js/app.js?v=4"></script>
<script src="/js/pages/Settings.js?v=3"></script>
<script src="/js/pages/WatchPage.js?v=3"></script>
<script src="/js/app.js?v=5"></script>
<script>
// Check for SSO token in URL
(function () {