⚠ Aviso Legal y Exención de Responsabilidad
FlaskCast es una herramienta de uso privado y local diseñada exclusivamente para organizar y reproducir contenido multimedia al que el usuario posee los derechos legales de uso. En ningún caso FlaskCast facilita, promueve ni permite la obtención o distribución de contenido protegido por derechos de autor sin la debida autorización.
El usuario es el único responsable del contenido que alberga, organiza y reproduce mediante esta aplicación. El desarrollador de FlaskCast declina toda responsabilidad derivada del uso indebido de esta herramienta en relación con material sobre el que el usuario no tenga derechos legales.
El uso de FlaskCast implica la aceptación de estas condiciones.
- Catálogo por carpetas: organiza series, temporadas y capítulos directamente desde el sistema de archivos.
- Diferenciación Películas/Series: detección automática por estructura de carpetas, con badge visual 🎬/📺 y filtro en catálogo. Soporte para override manual vía
_meta.jsono tablacontent_metadata. - Continuar viendo: sección destacada en el catálogo que muestra los capítulos en progreso ordenados por recencia, con barra de porcentaje.
- Transcodificación asíncrona: convierte
.avi/.mkva.mp4en segundo plano usando FFmpeg, con detección automática del mejor códec H.264 disponible (libx264,libopenh264,h264_nvenc, etc.), barra de progreso en tiempo real y verificación de éxito/fallo. - Miniaturas dinámicas: extrae fotogramas como
.jpgpara previsualizaciones. - Seguimiento por usuario: guarda la posición exacta (segundos), marca "En progreso" y "Visto" según umbrales configurables.
- Listas personalizadas: organiza contenido en Favoritos, Pendiente, Viendo y Visto con pestañas filtrables.
- Paginación del catálogo: 24 elementos por página con controles de navegación.
- Tema claro/oscuro: toggle entre tema oscuro (por defecto) y claro, guardado por usuario.
- Transiciones SPA: navegación entre páginas con fade animations y fetch AJAX.
- API REST completa: añade, elimina, lista, descarga vídeos y gestiona el progreso mediante endpoints protegidos por toggle.
- Streaming en Vivo: reproduce streams en directo (HLS, iframes, vídeos) con soporte para listas M3U, modo SmartTV y fallback automático con múltiples fuentes. Incluye gestión web de streams desde el navegador (añadir/editar/eliminar/reordenar, tipo
hls/video/iframe/m3u/auto), visible solo para cuentas registradas. - Modo SmartTV: reproductor optimizado para televisores conectados a la red local.
- Auto-reproducción: el reproductor avanza automáticamente al siguiente capítulo de la temporada.
- Interfaz responsive: diseño adaptable con sidebar colapsable en móvil (hamburger menu), breakpoints a 900px, 768px y 480px.
- Interfaz ligera: HTML5, CSS y JavaScript Vanilla para reproducción y búsqueda en tiempo real.
- Multi-idioma (i18n): soporte español/inglés con cambio por usuario. Géneros y descripciones de OMDb se traducen automáticamente.
- Integración OMDb: obtención automática de portadas, descripciones, valoraciones y metadatos desde OMDb API. Clave guardada en
.envy reutilizable desde la web o el panel de administración. - Biblioteca web (Gestionar Biblioteca): gestión completa de series, películas, temporadas y vídeos desde el navegador (
/biblioteca), con la UI de administración integrada en el sidebar (justo antes de "Cerrar sesión") y conector OMDb incluido. El enlace solo aparece para cuentas registradas (oculto para invitados). - Diseño hero: vista detalle con banner de portada difuminada, metadata visual y botones de acción.
- Panel de administración GUI: gestión visual de contenido, streams, configuración y OMDb con interfaz en tkinter (5 pestañas, bilingual ES/EN).
- Autenticación opcional: protección con contraseña para acceder a la aplicación. Se activa/desactiva desde el panel de administración. Cuando está activa, todas las rutas web requieren autenticarse previamente con la contraseña configurada.
- Backend: Python 3 + Flask
- Base de datos: SQLite3 (
flaskcast.db) - Procesamiento de vídeo: FFmpeg (gestión automática con
static-ffmpeg) - Concurrencia:
threadingpara evitar colisiones en conversiones - Rate Limiting: Flask-Limiter (protección contra abuso de API)
- Frontend: HTML5, CSS3 y JavaScript
- Template Engine: Jinja2 con herencia de plantillas (
base.html) - Servidores de producción: Waitress (Windows) / Gunicorn (Linux)
- Compresión: py7zr (exportar/importar contenido)
- Traducción: MyMemory API (descripciones) + diccionario local (géneros)
- Clona o descarga el proyecto y sitúate en la carpeta raíz:
git clone <repo> && cd FlaskCast- Crea y activa un entorno virtual (recomendado):
python -m venv venv
venv\Scripts\activate # Windows
source venv/bin/activate # Linux / macOS- Instala las dependencias:
pip install -r requirements.txt- Ejecuta la aplicación:
python app.pyAccede desde el navegador en http://localhost:5000 o usando la IP de tu equipo en la red local.
⚠️ Linux: FFmpeg es obligatorio. La aplicación no arrancará sin él.
📥 Instalar FFmpeg en Linux (click para desplegar)
# Debian / Ubuntu / Linux Mint / Pop!_OS
sudo apt-get update && sudo apt-get install -y ffmpeg
# Fedora / RHEL / CentOS / Rocky / AlmaLinux
sudo dnf install -y ffmpeg
# Arch Linux / Manjaro / EndeavourOS
sudo pacman -S --noconfirm ffmpeg
# openSUSE / SUSE
sudo zypper install -y ffmpeg
# Alpine Linux
sudo apk add ffmpeg
# Void Linux
sudo xbps-install -Sy ffmpeg
# Gentoo
sudo emerge media-video/ffmpeg🪟 Windows/macOS:
static-ffmpegdescargará y configurará los binarios de FFmpeg automáticamente la primera vez que se ejecute la aplicación. No se necesita instalación manual.
py7zrse usa para las funciones de exportar/importar contenido multimedia en el panel de administración.Flask-Limiterproporciona rate limiting para proteger contra abuso de la API.waitressse usa como servidor WSGI de producción en Windows y Linux.
En Windows y Linux, FlaskCast usa Waitress (6 threads). El estado de las conversiones se comparte en memoria dentro del único proceso, igual que antes con Gunicorn. El puerto se configura en data/config.json o mediante el panel de administración.
FlaskCast puede usarse sin instalar Python de dos formas:
1. Binarios precompilados (FlaskCast.exe en Windows, ELF FlaskCast en Linux):
- El binario lleva Python embebido (PyInstaller), así que el usuario final no necesita instalar nada. Copia el binario donde quieras ejecutarlo.
⚠️ Windows: al ser un.exesin firma digital, Smart App Control / SmartScreen puede bloquearlo la primera vez (ver la Guía de instalación).⚠️ Linux: el ELF distribuido está compilado con la versión más nueva de Ubuntu y depende de su glibc. Funciona en esa distro y en las más recientes, pero puede fallar en distros más antiguas (error típico:GLIBC_x.x not found). Si falla, compila tu propio binario (punto 2).
2. Compilar tu propio binario (cada binario se genera en su propio sistema y se guarda en bin/; los binarios no se suben al repositorio):
- Windows: ejecuta
build_windows.bat(equivale apython -m PyInstaller --noconfirm --clean --distpath bin FlaskCast.spec). - Linux: ejecuta
bash build_linux.sh. El script comprueba que Tkinter esté disponible (python3-tk), instalarequirements.txty PyInstaller automáticamente (funciona incluso en un entorno limpio tipo WSL) y equivale apython3 -m PyInstaller --noconfirm --clean --distpath bin FlaskCast.spec. - 💡 En Linux compila en la distro más antigua que quieras soportar (p. ej. Debian 11 / Ubuntu 20.04): el binario hereda la versión de glibc del sistema de compilación. Compilar dentro de WSL es válido; el ELF resultante corre en Linux (no en Windows).
Comportamiento del binario:
- Al arrancar pregunta si quieres iniciar el servidor (Enter, por defecto) o abrir la configuración (
C). Al cerrar la configuración vuelve a preguntar si quieres iniciar el servidor. Desde la web, el botón "Abrir Panel de Administración" de/ajusteslanza el panel dentro del propio ejecutable. - El puerto se elige al iniciar el servidor por consola. Si solo pulsas Enter, usa el de
data/config.json(5000 por defecto). Si el puerto escrito está ocupado, muestra un error ("El puerto X está ocupado") y vuelve al menú sin iniciar el servicio. Con la opción de puerto eliminada del panel tkinter, el puerto ya no se configura ahí. - Espera una carpeta
data/junto a él. Si falta, crea automáticamentedata/,data/media/,data/config.json,data/live_streams.jsony.enven el directorio del ejecutable. - En la primera ejecución muestra un diálogo informando qué se creó (y avisa si
data/mediaestá vacía). - Incluye plantillas y estáticos;
static-ffmpegdescarga FFmpeg la primera vez en la caché del usuario, así que el primer arranque puede tardar un poco. - La ventana de consola permanece abierta mostrando los logs del servidor y el menú de inicio.
- El
.exede Windows lleva el icono destatic/logo.pngembebido (icon.icose genera solo durante la compilación congenerar_icono.py). En Linux los binarios ELF no incorporan iconos en el ejecutable:build_linux.shgenerabin/FlaskCast.desktopjunto al binario apuntando abin/logo.pngpara el lanzador.
docker-compose up --buildNota para Docker: si ejecutas FlaskCast mediante
docker-compose, el cambio de puerto desdeconfig_admin.pyno tendrá efecto. El puerto se define en el archivodocker-compose.ymlmediante el mapeoports:.
FlaskCast utiliza un archivo .env en la raíz del proyecto para almacenar credenciales sensibles. Este archivo está incluido en .gitignore y no se sube al repositorio.
| Variable | Descripción |
|---|---|
OMDB_API_KEY |
Clave de API de OMDb para obtención de metadatos y portadas. Se guarda automáticamente al validar desde el panel de administración o se puede configurar con --omdb-key. |
.
├── app.py # Aplicación Flask (rutas, API, BD, FFmpeg)
├── config_admin.py # Panel de administración (GUI tkinter + CLI)
├── translations.py # Sistema i18n (ES/EN), traducción de metadatos
├── requirements.txt
├── .env # Variables de entorno (OMDB_API_KEY) — gitignored
├── Dockerfile
├── docker-compose.yml
├── data/
│ ├── config.json # Configuración del servidor
│ ├── flaskcast.db # Base de datos SQLite
│ ├── live_streams.json # Configuración de streams en vivo
│ └── media/ # Contenido multimedia
├── static/
│ ├── css/
│ │ └── estilos.css # Estilos globales (tema, responsive, skeletons, badges, hero)
│ ├── js/
│ │ └── reproductor.js # Lógica del reproductor de vídeo
│ └── logo.png
└── templates/
├── base.html # Plantilla base (sidebar, SPA transitions, layout)
├── index.html # Catálogo principal con paginación, filtros y continuar viendo
├── serie.html # Detalle de serie/película con hero banner y capítulos
├── listas.html # Listas personales (Favoritos, Pendiente, Viendo, Visto)
├── player_tv.html # Reproductor SmartTV (sin sidebar)
├── live.html # Lista de streams en vivo
├── live_tv.html # Reproductor SmartTV para streams (sin sidebar)
├── usuarios.html # Panel de gestión de usuarios
└── ajustes.html # Panel de configuración (tema, idioma, marcado automático)
| Archivo | Contenido |
|---|---|
.env |
OMDB_API_KEY — clave de API de OMDb |
data/config.json |
Puerto, botones de apagado, API habilitada, idioma del admin, autenticación (auth_enabled, auth_password) |
data/flaskcast.db |
SQLite: usuarios, progreso, favoritos, listas, content_metadata |
data/live_streams.json |
Definición de canales en vivo |
data/media/ |
Carpetas de series/películas, portadas, miniaturas |
FlaskCast usa SQLite con modo WAL para concurrencia. Las tablas se crean automáticamente al iniciar la aplicación.
| Tabla | Descripción |
|---|---|
usuarios |
Perfiles de usuario (nombre, emoji, tema, idioma, auto_marcar, mostrar_progreso) |
progreso |
Posición de reproducción por usuario y vídeo (segundos, visto, duración) |
favoritos |
Series/películas marcadas como favoritas por usuario |
listas |
Estado en lista por usuario y serie (0=Pendiente, 1=Viendo, 2=Visto) |
content_metadata |
Override manual de tipo de contenido (pelicula/serie) |
Todas las plantillas heredan de base.html usando Jinja2:
{% extends 'base.html' %}
{% block title %}Mi Página{% endblock %}
{% block head %}
<style>/* Estilos específicos */</style>
{% endblock %}
{% block content %}
<!-- Contenido de la página -->
{% endblock %}base.html incluye:
- Sidebar de navegación (se oculta en reproductores SmartTV)
- Bloques extensibles:
title,head,content,body_class,sidebar
Coloca tu contenido en data/media/ siguiendo este patrón:
data/
└── media/
├── Nombre de la Serie A/
│ ├── _img.png <-- Portada (obligatoria para mostrar imagen)
│ ├── _meta.json <-- Metadatos (opcional, override manual)
│ ├── Temporada 1/
│ │ ├── Capitulo_01.mp4
│ │ └── Capitulo_02.avi
│ └── Temporada 2/
│ ├── Capitulo_01.mkv
│ └── Capitulo_02.mp4
├── Nombre de la Serie B (Sin Temporadas)/
│ ├── _img.png
│ ├── Video_Suelto_01.mp4
│ └── Video_Suelto_02.mp4
└── Mi Pelicula/
├── _img.png
└── pelicula.mp4
- El archivo de portada debe llamarse exactamente
_img.png. Si no existe, la interfaz mostrará un icono genérico (🎬 o 📺 según el tipo). - Si no usas subcarpetas de temporada, los vídeos en la raíz aparecerán bajo "Contenido Disponible".
El archivo _meta.json permite definir metadatos y forzar el tipo de contenido. Formato completo:
{
"tipo": "pelicula",
"titulo": "Mi Película",
"descripcion": "Una descripción de la película.",
"anio": "2024",
"genero": ["Acción", "Aventura"],
"director": "Nombre del Director",
"valoracion": "8.5",
"duracion_min": 120,
"temporadas": 1
}| Campo | Tipo | Descripción |
|---|---|---|
tipo |
string | "pelicula" o "serie" — fuerza el tipo detectado |
titulo |
string | Título display del contenido |
descripcion |
string | Descripción (se traduce automáticamente al español si el usuario tiene idioma ES) |
anio |
string | Año de estreno |
genero |
array | Lista de géneros en inglés (se traducen automáticamente) |
director |
string | Nombre del director |
valoracion |
string | Puntuación (ej: "8.5") |
duracion_min |
integer | Duración en minutos (películas) |
temporadas |
integer | Número de temporadas (series) |
Los campos titulo, descripcion, anio, genero, director, valoracion, duracion_min y temporadas se rellenan automáticamente al aplicar metadatos de OMDb.
FlaskCast detecta automáticamente si una carpeta es una película o una serie mediante un sistema de 3 niveles:
| Prioridad | Fuente | Descripción |
|---|---|---|
| 1 | Tabla content_metadata (BD) |
Override manual guardado en la base de datos |
| 2 | Archivo _meta.json |
Campo tipo en el archivo de metadatos |
| 3 | Estructura de carpetas | Subcarpetas = serie, solo vídeos = película |
Diferencias en la UI:
- Catálogo: badge 🎬 (dorado) o 📺 (azul) en cada card, con filtro "Todo / Películas / Series"
- Vista detalle: las películas muestran "🎬 Película" como título de sección y ocultan el acordeón de temporadas; las series muestran "Temporadas Disponibles" con acordeón expandible
- Hero banner: vista detalle con banner de portada difuminada, metadata visual (año, género, director, valoración, duración) y botones de acción
FlaskCast traduce automáticamente los metadatos de OMDb según el idioma del usuario:
- Géneros: se traducen localmente mediante un diccionario de 26 géneros (inglés → español) en
translations.py. - Descripciones: se traducen en tiempo real mediante la API gratuita de MyMemory sin necesidad de clave de API.
- Fallback: si la traducción falla, se muestra el texto original en inglés.
- Formatos web nativos (
.mp4,.webm,.ogg) se reproducen directamente. - Formatos no nativos (
.avi,.mkv) aparecen como "Pendiente" y pueden convertirse a.mp4mediante un botón en la interfaz. - La conversión se realiza de forma asíncrona; la app usa
threadingy bloqueos para evitar conflictos en conversiones simultáneas. - Detección automática de códec: al iniciar, la app prueba
ffmpeg -encodersy selecciona el mejor codificador H.264 disponible. Compatible conlibx264(Windows),libopenh264(Linux/Fedora),h264_nvenc,h264_amf,h264_qsv,h264_vaapi. - Progreso en tiempo real: la barra de conversión muestra el porcentaje de avance parseando la salida de FFmpeg.
- Verificación de éxito: se comprueba el código de salida de FFmpeg y la existencia del
.mp4antes de marcar como completado. - Ocultación durante conversión: el archivo
.mp4no aparece en la interfaz hasta que la conversión alcanza el 100%. - Aislamiento de procesos: FFmpeg se ejecuta en una nueva sesión para resistir
Ctrl+Csin abortar la conversión. - Conversión: FFmpeg con códec detectado (vídeo) + AAC (audio), CRF 23.
- Puedes crear perfiles de usuario con nombre y emoji desde el panel de usuarios.
- El reproductor envía actualizaciones periódicas al servidor guardando la marca de tiempo actual en la base de datos.
- Umbrales de marcado automático:
-
10% reproducido → estado "Viendo" (azul).
-
85% reproducido → estado "Visto" (verde).
-
- También puedes marcar manualmente un capítulo como "Visto" desde la UI.
- Cada usuario tiene sus propias preferencias: tema, idioma, marcado automático, mostrar progreso.
El catálogo principal incluye una sección "▶ Continuar viendo" que muestra hasta 12 capítulos en progreso (estado visto = 1), ordenados por los más recientemente vistos. Cada card muestra una barra de progreso porcentual. Al hacer clic, el reproductor retoma la posición guardada.
FlaskCast incluye una página de listas (/listas) donde los usuarios pueden organizar su contenido:
| Pestaña | Color | Descripción |
|---|---|---|
| ⭐ Favoritos | Dorado (#f5a623) | Series/películas marcadas como favoritas |
| 📋 Pendiente | Gris (#888) | Contenido pendiente de ver |
| 👁️ Viendo | Azul (#0d6efd) | Contenido en progreso |
| ✅ Visto | Verde (#1fcc72) | Contenido completado |
- Al abrir la página, la pestaña "Favoritos" se muestra seleccionada por defecto.
- Clic en una pestaña muestra solo esa sección; clic de nuevo la oculta (toggle).
- Cada card muestra un badge de tipo (🎬/📺) según el contenido.
- La favoritos se gestionan desde el botón ⭐ en la vista de detalle de cada serie.
- Pendiente/Viendo/Visto se gestionan desde el botón "+ Añadir" en la vista de detalle.
El catálogo principal muestra 24 elementos por página con controles de navegación:
- Botones de página con ellipsis (
...) para saltos largos. - Botones "Anterior" / "Siguiente" para navegación secuencial.
- Indicador de rango: "Mostrando X–Y de Z elementos".
- La paginación es server-side; cada página carga su propio subset de datos.
FlaskCast se adapta automáticamente al tamaño de pantalla:
| Breakpoint | Comportamiento |
|---|---|
| > 900px | Layout completo con sidebar fijo |
| ≤ 900px | Sidebar colapsable con hamburger menu |
| ≤ 768px | Grid de cards ajustado, header compacto |
| ≤ 480px | Grid de 2 columnas, controles apilados |
- Hamburger menu: botón de 3 líneas que abre/cierra el sidebar en móvil.
- Overlay: al abrir el sidebar, se muestra un overlay oscuro que lo cierra al hacer clic.
- Transiciones SPA: la navegación entre páginas usa fade animations con skeleton loading durante la carga.
FlaskCast simula navegación SPA usando AJAX:
- Al hacer clic en un enlace interno, el contenido actual hace fade-out.
- Se muestra un skeleton placeholder mientras se carga la nueva página.
- El nuevo contenido se inyecta con fade-in animation.
- La URL se actualiza con
history.pushState(). - El sidebar se actualiza para reflejar la sección activa.
Excluidos de las transiciones: enlaces externos, # anchors, modales, reproductores, y elementos con data-no-transition.
FlaskCast incluye una sección de streaming en vivo que permite reproducir canales de televisión, radios o cualquier stream en directo desde la interfaz web.
Los streams se configuran en el archivo data/live_streams.json. Si el archivo no existe, créalo manualmente con el siguiente formato:
[
{
"titulo": "Canal de Ejemplo",
"url": "http://ejemplo.com/stream.m3u8",
"tipo": "auto"
},
{
"titulo": "Canal HLS con Fallback",
"urls": [
"https://principal.com/stream.m3u8",
"https://backup1.com/stream.m3u8",
"https://backup2.com/stream.m3u8"
],
"tipo": "hls"
},
{
"titulo": "Canal Iframe",
"url": "https://ejemplo.com/embebed-player",
"tipo": "iframe"
},
{
"titulo": "Lista M3U Completa",
"url": "https://ejemplo.com/canales.m3u",
"tipo": "m3u"
}
]Los streams también se pueden gestionar visualmente desde:
- La pestaña "Streamings" del panel de administración (
config_admin.py). - Desde el navegador: botón ⚙️ Gestionar Streams en la cabecera de
En Directo(/live), que abre/live/gestioncon CRUD completo, reordenación (▲/▼) y soporte de tiposhls,video,iframe,m3uyauto(los dos últimos no existen en la pestaña del panel). Incluye validación (título y URL obligatorios), confirmación al eliminar y límites de peticiones por minuto en todos los endpoints. El botón solo aparece para cuentas registradas (los invitados no lo ven) y las rutas/live/gestiony/live/streams/*redirigen o rechazan sin sesión de cuenta.
| Endpoint | Método | Descripción | Límite |
|---|---|---|---|
/live/gestion |
GET | Página de gestión de streams (título, URL principal, backups por línea, tipo) | — |
/live/streams/crear |
POST | Añade un stream nuevo (titulo, url, urls, tipo) |
10/min |
/live/streams/editar |
POST | Actualiza el stream del índice (indice + datos) |
10/min |
/live/streams/eliminar |
POST | Elimina el stream del índice (indice) |
10/min |
/live/streams/mover |
POST | Reordena el stream (indice, direccion: arriba/abajo) |
20/min |
Los streams se guardan en
data/live_streams.jsoncon bloqueo (threading.RLock) para escrituras concurrentes seguras.
Cada stream puede definir múltiples fuentes de reproducción mediante el campo urls (array). Cuando una fuente falla, el reproductor salta automáticamente a la siguiente sin intervención del usuario.
- Si solo se usa
"url"(string), se comporta como una única fuente. - Si se usa
"urls"(array), el player intentará cada fuente en orden hasta encontrar una que funcione. - El campo
"url"se mantiene para retrocompatibilidad; si se proporciona"urls", este tiene prioridad.
| Tipo | Descripción | Ejemplo |
|---|---|---|
auto |
Detecta automáticamente el tipo por extensión (.mp4, .m3u8, etc.) | http://server/video.mp4 |
hls |
Stream HLS (m3u8) con soporte nativo o mediante HLS.js | https://server/live.m3u8 |
video |
Archivo de vídeo directo (mp4, webm, ogg) | http://server/film.mp4 |
iframe |
Página embebida en iframe (reproductores de terceros) | https://tv.com/player/123 |
m3u |
Lista M3U que se parsea automáticamente en múltiples canales | https://list.com/channels.m3u |
El parser soporta tanto listas M3U remotas (HTTP/HTTPS) como locales. Formato esperado:
#EXTM3U
#EXTINF:-1 tvg-name="Canal 1",Canal 1
http://server/canal1/stream.m3u8
#EXTINF:-1 tvg-name="Canal 2",Canal 2
http://server/canal2/stream.m3u8
- Lista de canales:
http://localhost:5000/live - Reproductor SmartTV:
http://localhost:5000/live/tv/<indice>(donde<indice>es la posición del canal en la lista, empezando por 0)
- Modal en PC: al hacer clic en "Ver", se abre un reproductor modal superpuesto.
- Modo SmartTV: vista optimizada con pantalla completa y controles grandes para televisores.
- Soporte HLS: usa HLS.js como fallback cuando el navegador no soporta HLS nativo (todos excepto Safari).
- Fallback automático: si un stream falla, el player intenta la siguiente URL disponible sin intervención del usuario.
- SmartTV con HLS.js: el reproductor SmartTV incluye HLS.js desde CDN, permitiendo reproducción HLS en navegadores sin soporte nativo.
- Tecla Escape: cierra el reproductor modal.
FlaskCast incluye un reproductor optimizado para Smart TV que permite ver tanto vídeos del catálogo como streams en vivo.
http://localhost:5000/tv/reproducir/<nombre_serie>/<temporada>/<archivo.mp4>
Características:
- Reproducción automática al cargar
- Pantalla completa por defecto
- Barra de información con título del vídeo
- Botón para volver a la lista de streams
http://localhost:5000/live/tv/<indice>
- Soporta vídeo directo, HLS (con HLS.js) e iframes
- Fallback automático entre múltiples fuentes
- Diseñado para ser controlado con el mando del televisor
Desde el panel de ajustes (/ajustes) puedes configurar:
- Idioma: alterna entre español e inglés. La preferencia se guarda por usuario en la BD y se aplica a toda la interfaz y a los metadatos de OMDb.
- Tema: alterna entre tema oscuro (por defecto) y tema claro. La preferencia se guarda por usuario en la BD.
- Marcado automático: activa o desactiva el cambio automático de estado "Viendo"/"Visto" según el progreso de reproducción.
- Mostrar progreso: activa o desactiva la barra de progreso en las cards de capítulos.
- Habilitar API: activa o desactiva los endpoints REST. Al activarla, los endpoints requieren sesión de usuario. Un botón informativo (
i) muestra la documentación completa de la API en un modal. Esta opción se gestiona desde el panel de administración (config_admin.py).
FlaskCast incluye un sistema de autenticación opcional que protege el acceso a toda la aplicación con una contraseña global.
- Abre el panel de administración (
python config_admin.py). - En la pestaña General, marca la casilla "Activar autenticación".
- Escribe la contraseña que quieras usar en el campo de al lado.
- Pulsa "Guardar y Cerrar".
- Reinicia FlaskCast para que los cambios surtan efecto.
- Activada: al acceder a cualquier página de la aplicación, se redirige a una pantalla de login donde debes introducir la contraseña.
- Desactivada: la aplicación funciona exactamente como antes, sin ningún paso de autenticación.
- En el sidebar aparece un enlace "Cerrar sesión" 🔒 solo cuando la autenticación está activa y has iniciado sesión.
- La contraseña se guarda en texto plano en
data/config.json. No se sube al repositorio (está en.gitignore).
También puedes gestionar la autenticación desde la línea de comandos:
# Activar/desactivar autenticación
python config_admin.py --auth
# Establecer la contraseña
python config_admin.py --auth-password "miclave123"
# Ver estado (incluye autenticación)
python config_admin.py --statusFlaskCast soporta español e inglés en toda la interfaz:
- Web: ~160 cadenas traducidas en
translations.py(secciónTRANSLATIONS). Cambio por usuario desde ajustes. - Panel de administración: ~140 cadenas traducidas (sección
ADMIN_TRANSLATIONS). Cambio desde la pestaña "Language". - Géneros: 26 géneros traducidos localmente (inglés → español) en
GENRE_TRANSLATIONS. - Descripciones: traducción automática en tiempo real vía API de MyMemory (gratuita, sin clave).
- Fallback: si el idioma es inglés, los metadatos se muestran sin traducir; si falla la traducción, se muestra el texto original.
FlaskCast incluye rate limiting mediante Flask-Limiter para proteger la API y las rutas web contra floods de requests y abuso.
| Endpoint | Límite | Razón |
|---|---|---|
| General (todas las rutas) | 200/min | Protección base |
POST /api/videos/add |
10/min | Evitar uploads masivos |
POST /api/videos/rm |
10/min | Evitar eliminaciones masivas |
POST /api/convertir/... |
5/min | Las conversiones son pesadas |
POST /api/eliminar/... |
10/min | Evitar eliminaciones masivas |
POST /api/progreso/guardar |
60/min | Se llama frecuentemente durante reproducción |
POST /api/favoritos/toggle |
30/min | Operación moderada |
POST /api/lista/guardar |
30/min | Operación moderada |
POST /usuarios/crear |
5/min | Evitar creación masiva de usuarios |
POST /usuarios/editar |
10/min | Moderado |
POST /usuarios/eliminar |
5/min | Operación destructiva |
GET /api/off |
2/min | Crítico |
GET /api/off/all |
1/min | Crítico |
{
"error": "Too Many Requests",
"message": "You have exceeded the rate limit. Please slow down.",
"retry_after": 42
}Código de estado HTTP: 429 Too Many Requests
FlaskCast incluye un panel de administración (config_admin.py) con interfaz gráfica (GUI) en tkinter y modo línea de comandos (CLI). Tiene 5 pestañas y soporte bilingüe (ES/EN).
Se abre la ventana de administración sin pasar ningún argumento:
python config_admin.py- Mostrar botón "Apagar Servidor": activa/desactiva la visibilidad del botón que apaga solo el proceso de Flask.
- Mostrar botón "Apagar Todo": activa/desactiva la visibilidad del botón que apaga todo el sistema operativo.
- Habilitar API REST: activa/desactiva los endpoints de la API REST.
- Activar autenticación: activa/desactiva la protección con contraseña. Al activarla, aparece un campo para definir la contraseña de acceso.
- Puerto: cambia el puerto en el que escucha el servidor (requiere reiniciar la aplicación).
- Exportar data/ (.fkmedia): comprime toda la carpeta
data/en un archivo.fkmedia(formato 7z internamente):media/(vídeos, miniaturas y metadatos),config.json,live_streams.jsonyflaskcast.db. La base de datos se copia con la API de backup de SQLite, por lo que el archivo es consistente aunque el servidor esté escribiendo. No se incluyen los archivos-wal/-shm.- Eliges tú la ruta con el diálogo "Guardar como" (nombre sugerido
flaskcast-AAAA-MM-DD.fkmedia); al terminar te muestra la ruta exacta y el SHA-256. - Barra de progreso con porcentaje, velocidad, tiempo restante y Cancelar. Si cancelas, se borra el archivo parcial y
data/no se toca. - Modo de compresión:
Rápido(copia sin recomprimir, muy veloz) oCompacto(LZMA2, bastante más pequeño pero más lento). - Exclusiones: nombres de carpeta/archivo separados por comas, admitiendo comodines
*y?. Un patrón que empieza por/se ancla a la raíz dedata/. Por defecto se excluye.thumbnails. - Tamaño mínimo (MB): omite cualquier archivo por encima de ese tamaño, útil para respaldar ajustes y bases de datos sin los vídeos grandes.
- Junto al
.fkmediase escribe un.sha256para poder comprobar que el archivo no se ha corrompido.
- Eliges tú la ruta con el diálogo "Guardar como" (nombre sugerido
- Restaurar data/ (.fkmedia): extrae un
.fkmediasobredata/. Los archivos con el mismo nombre se sobreescriben y tu configuración actual se sustituye por la del archivo. Los.fkmediaantiguos (que solo conteníanmedia/) siguen siendo compatibles.- Al elegir el archivo se muestra una vista previa con su formato, fecha y qué contiene.
- Puedes restaurar todo, solo los datos (ajustes, streams y base de datos) o solo los vídeos, y Deshacer (rollback) devuelve el estado anterior.
- Se descomprime primero en una carpeta temporal y solo se mueve sobre
data/cuando todo ha salido bien: si algo falla,data/queda intacto. Necesitas espacio libre equivalente al tamaño sin comprimir. - Deshacer está marcado por defecto y copia tu estado actual (ajustes, streams y base de datos; no los vídeos, así que tarda segundos) a un
.fkmediajunto al que vas a restaurar, por si necesitas volver atrás.
- Ver contenido: muestra la fecha, el formato y el resumen de un
.fkmediasin extraerlo, y te dice si su.sha256sigue siendo correcto. - Guardar y Cerrar: aplica los cambios y cierra la ventana.
- Campo de entrada para la clave de API de OMDb con validación en tiempo real.
- La clave se guarda automáticamente en el archivo
.enval validar con éxito. - Árbol de biblioteca que muestra todas las carpetas multimedia con columnas: nombre, tipo (película/serie), tiene metadatos, tiene portada.
- Botón "Aplicar OMDb" que busca y aplica metadatos automáticamente (título, descripción, año, género, director, valoración, duración/temporadas) y descarga la portada como
_img.png.
💡 Esta funcionalidad también está disponible desde el navegador en la Biblioteca web.
- Árbol jerárquico con toda la estructura multimedia (Series → Temporadas → Vídeos, Películas → Vídeos).
- Muestra tamaños de archivo en MB.
- Acciones disponibles:
- Añadir película o serie (diálogo único con campos de metadata: título, descripción, año, género, director, valoración, tipo, duración, temporadas).
- Añadir temporada.
- Añadir vídeo (copia desde máquina local).
- Editar metadata (actualiza
_meta.jsony tablacontent_metadatade la BD). - Renombrar (cascada en BD: content_metadata, favoritos, progreso, listas).
- Eliminar (con confirmación, limpia miniaturas y progreso).
- Menú contextual (clic derecho) con opciones según el nivel del nodo seleccionado.
💡 Esta funcionalidad también está disponible desde el navegador: Biblioteca web (Gestionar Biblioteca en el sidebar, solo para cuentas registradas).
- CRUD completo de streams en vivo almacenados en
data/live_streams.json. - Árbol con columnas: título, URL principal, tipo.
- Acciones: añadir, editar, eliminar, mover arriba/abajo (reordenar), actualizar.
- Cada stream tiene: título, URL principal, URLs de backup (una por línea), tipo (hls/iframe/video).
- Alternativa web: ⚙️ Gestionar Streams desde
/live(ver Gestión web de streams), con tipos adicionalesm3uyauto.
- Botones de radio para Español (ES) y English (EN).
- Al cambiar el idioma, toda la interfaz se reconstruye en el idioma seleccionado.
- La preferencia se guarda en
data/config.json(admin_idioma).
config_admin.py usa tkinter, que no siempre viene incluido por defecto en algunas distribuciones Linux. Si al ejecutarlo obtienes un error como ModuleNotFoundError: No module named 'tkinter', instálalo con:
sudo apt install python3-tk # Debian / Ubuntu
sudo dnf install python3-tkinter # Fedora
sudo pacman -S tk # Arch LinuxÚtil para servidores sin interfaz gráfica (headless). Permite modificar la configuración y gestionar archivos multimedia directamente desde la terminal.
python config_admin.py [OPCIONES]| Bandera | Descripción |
|---|---|
--status |
Muestra la configuración actual del servidor (incluye estado de la clave OMDb) |
--toggle-server |
Activa/desactiva el botón "Apagar Servidor" |
--toggle-all |
Activa/desactiva el botón "Apagar Todo" |
--api |
Activa/desactiva la API REST |
--auth |
Activa/desactiva la autenticación |
--auth-password CONTRASEÑA |
Establece la contraseña de autenticación |
--port PUERTO |
Cambia el puerto del servidor (1-65535) |
--omdb-key API_KEY |
Guarda una clave de API de OMDb en el archivo .env |
--export ARCHIVO |
Exporta toda la carpeta data/ a un archivo .fkmedia |
--import ARCHIVO |
Restaura toda la carpeta data/ desde un archivo .fkmedia |
--compresion MODO |
rapido (copia directa, muy veloz) o compactado (LZMA2). Se guarda como predeterminado |
--exclude PATRÓN |
Excluye carpeta/archivo; admite * y ?, y /ruta para anclar a la raíz de data/. Repetible |
--exclude-mb MB |
Omite archivos mayores de ese tamaño (0 = sin límite) |
--alcance PARTE |
Al restaurar: todo, datos o media |
--rollback |
Antes de restaurar, guarda tu estado actual (ajustes, streams y base de datos) en un .fkmedia junto al archivo, para poder deshacer |
--solo-datos |
Al exportar, guarda solo ajustes, streams y base de datos (sin vídeos): una copia de seguridad rápida |
--list ARCHIVO |
Muestra qué contiene un .fkmedia sin extraerlo |
--verificar ARCHIVO |
Comprueba el archivo y su .sha256 para detectar corrupción |
Se pueden combinar varias banderas en una sola ejecución:
python config_admin.py --api --toggle-all --port 8080Ejemplos prácticos:
# Ver configuración actual
python config_admin.py --status
# Activar la API REST
python config_admin.py --api
# Activar autenticación con contraseña
python config_admin.py --auth --auth-password "miclave123"
# Cambiar puerto a 8080
python config_admin.py --port 8080
# Guardar clave de OMDb
python config_admin.py --omdb-key tu_clave_aqui
# Exportar todo el contenido multimedia
python config_admin.py --export backup.fkmedia
# Exportar más compacto (más lento, archivo más pequeño)
python config_admin.py --export backup.fkmedia --compresion compactado
# Copia de seguridad rápida: solo ajustes, streams y base de datos
python config_admin.py --export ajustes.fkmedia --solo-datos
# Dejar fuera miniaturas y cualquier .nfo
python config_admin.py --export backup.fkmedia --exclude .thumbnails --exclude "*.nfo"
# Ver qué contiene un backup y comprobar que no se ha corrompido
python config_admin.py --list backup.fkmedia
python config_admin.py --verificar backup.fkmedia
# Importar contenido desde un backup
python config_admin.py --import backup.fkmedia
# Restaurar solo la base de datos y los ajustes, dejando los vídeos
python config_admin.py --import backup.fkmedia --alcance datos
# Restaurar guardando antes tu estado actual para poder deshacer
python config_admin.py --import backup.fkmedia --rollbackAdemás del panel de escritorio, FlaskCast incluye una gestión completa de contenido desde el navegador. Se accede desde el sidebar con el enlace 🗂️ Gestionar Biblioteca, situado justo antes de "Cerrar sesión" (ruta GET /biblioteca). No depende del toggle de la API REST, por lo que funciona siempre que se tenga acceso a la web. Solo está disponible para cuentas registradas: el enlace no aparece para invitados y la ruta redirige a / si no hay sesión (lo mismo aplica a todos los endpoints /biblioteca/... y /live/streams/..., protegidos con requiere_cuenta).
- Muestra toda la estructura multimedia (Series → Temporadas → Vídeos, Películas → Vídeos) con el tamaño de cada vídeo en MB.
- Todo aparece colapsado por defecto; un clic en una serie o temporada la expande/oculta y la selecciona.
- Las acciones de la barra superior se habilitan según el elemento seleccionado.
| Botón | Requiere | Función |
|---|---|---|
| +Añadir Película/Serie | — | Crea la carpeta + _meta.json; para series genera automáticamente Season N según el número de temporadas indicado |
| +Añadir Temporada | Serie seleccionada | Crea la subcarpeta de temporada (sugiere Season N automáticamente) |
| +Añadir Vídeo | Película, serie sin temporadas o temporada | Sube uno o varios vídeos (.mp4, .webm, .ogg, .avi, .mkv) al destino correcto |
| Editar Metadata | Película/Serie seleccionada | Actualiza _meta.json y la tabla content_metadata |
| Renombrar | Cualquier nodo | Renombra serie (cascada en favoritos/progreso/listas), temporada o vídeo |
| Eliminar | Cualquier nodo | Borra con confirmación la serie completa, una temporada o un vídeo (limpia miniaturas y progreso) |
| OMDb | Película/Serie seleccionada | Abre el modal de integración con OMDb |
| Refrescar | — | Recarga el árbol desde disco |
El botón OMDb abre un modal que replica la pestaña OMDb del panel de administración:
- Campo de API key que, si ya existe una clave guardada en el archivo
.env, aparece precargada (junto con el indicador "✓ API key válida (guardada)"). - Botón Validar que comprueba la clave contra la API de OMDb y la guarda en
.env. - Enlace "Obtener API Key" con acceso a la página de registro de OMDb.
- Checkbox "Descargar portada" (activado por defecto) que descarga el póster como
_img.png. - Botón "Aplicar OMDb": busca el contenido seleccionado por el nombre de su carpeta, elige la mejor coincidencia y rellena automáticamente título, descripción, año, género, director, valoración y duración/temporadas.
| Método | Ruta | Descripción | Rate Limit |
|---|---|---|---|
| GET | /biblioteca |
Página de gestión con el árbol de contenido | — |
| POST | /biblioteca/crear |
Crear película/serie con metadata | 5/min |
| POST | /biblioteca/temporada |
Crear temporada en una serie | 5/min |
| POST | /biblioteca/videos/add |
Subir uno o varios vídeos (multipart) | 10/min |
| POST | /biblioteca/meta |
Guardar/actualizar metadata | 10/min |
| POST | /biblioteca/renombrar |
Renombrar serie, temporada o vídeo | 10/min |
| POST | /biblioteca/eliminar |
Eliminar serie, temporada o vídeo | 5/min |
| GET | /biblioteca/omdb/estado |
Muestra si hay clave OMDb guardada y su valor | — |
| POST | /biblioteca/omdb/validar |
Valida y guarda la API key de OMDb | 5/min |
| POST | /biblioteca/omdb/aplicar |
Aplica metadatos OMDb al contenido seleccionado | 2/min |
FlaskCast incluye una API REST protegida por sesión de usuario y activada desde el panel de administración. Todos los endpoints requieren el encabezado de cookie de sesión y que el usuario tenga la API habilitada. Todos los endpoints están protegidos con rate limiting.
POST /api/videos/add
Content-Type: multipart/form-data
| Parámetro | Tipo | Descripción |
|---|---|---|
serie |
string (obligatorio) | Nombre de la serie |
temporada |
string | Obligatorio si la serie tiene subcarpetas. No usar si no las tiene. |
archivo |
file (obligatorio) | Archivo de vídeo a subir |
curl -X POST http://localhost:5000/api/videos/add \
-b "session=TU_SESSION" \
-F "serie=Mi Serie" \
-F "temporada=Temporada 1" \
-F "archivo=@/ruta/al/video.mp4"GET /api/videos → Todas las series y sus capítulos
GET /api/videos/Mi%20Serie → Una serie específica
curl http://localhost:5000/api/videos -b "session=TU_SESSION"GET /api/video/<serie>/<ruta/al/archivo.mp4>
curl http://localhost:5000/api/video/Mi%20Serie/Temporada%201/cap1.mp4 \
-b "session=TU_SESSION" -o cap1.mp4POST /api/videos/rm
Content-Type: application/json
| Parámetro | Tipo | Descripción |
|---|---|---|
serie |
string (obligatorio) | Nombre de la serie |
filename |
string (obligatorio) | Ruta del archivo dentro de la serie |
curl -X POST http://localhost:5000/api/videos/rm \
-b "session=TU_SESSION" \
-H "Content-Type: application/json" \
-d '{"serie": "Mi Serie", "filename": "Temporada 1/video.mp4"}'Inicia la conversión de un vídeo incompatible (.avi, .mkv) a .mp4 en segundo plano.
POST /api/convertir/<serie>/<ruta/archivo.avi>
Respuesta:
{"status": "procesando"}Si ya existe un archivo .mp4 con el mismo nombre:
{"status": "ya_existe_mp4"}Si ya hay una conversión en curso:
{"status": "ya_en_progreso"}curl -X POST http://localhost:5000/api/convertir/Mi%20Serie/Temporada%201/cap1.avi \
-b "session=TU_SESSION"Elimina un archivo específico sin necesidad de usar el endpoint de "videos/rm".
POST /api/eliminar/<serie>/<ruta/archivo.mp4>
curl -X POST http://localhost:5000/api/eliminar/Mi%20Serie/Temporada%201/cap1.mp4 \
-b "session=TU_SESSION"Devuelve la lista de identificadores de vídeos que se están convirtiendo actualmente, junto con el porcentaje de progreso de cada una.
GET /api/estados
Respuesta:
{"activos": ["Mi Serie/Temporada 1/cap1.avi"], "progreso": {"Mi Serie/Temporada 1/cap1.avi": 42}}curl http://localhost:5000/api/estados -b "session=TU_SESSION"Devuelve el resultado de una conversión completada (éxito o fallo).
GET /api/conversion_result/<serie>/<archivo>
Respuesta:
{"status": "ok", "mp4_exists": true}curl http://localhost:5000/api/conversion_result/Mi%20Serie/Temporada%201/cap1.avi \
-b "session=TU_SESSION"Guarda la posición de reproducción de un vídeo para el usuario actual.
POST /api/progreso/guardar
Content-Type: application/json
| Parámetro | Tipo | Descripción |
|---|---|---|
serie |
string (obligatorio) | Nombre de la serie |
filename |
string (obligatorio) | Ruta del archivo (ej: Temporada 1/cap1.mp4) |
segundos |
number | Posición actual en segundos |
duracion |
number | Duración total del vídeo |
visto |
number | Estado forzado (0=Sin ver, 1=Viendo, 2=Visto) — opcional |
Nota: Si se envía el campo visto, se ignora el cálculo automático y se fuerza ese estado.
curl -X POST http://localhost:5000/api/progreso/guardar \
-b "session=TU_SESSION" \
-H "Content-Type: application/json" \
-d '{"serie": "Mi Serie", "filename": "Temporada 1/cap1.mp4", "segundos": 1250, "duracion": 3600}'Obtiene la posición guardada y el estado de un vídeo para el usuario actual.
GET /api/progreso/obtener?serie=<serie>&filename=<archivo>
Respuesta:
{"segundos": 1250, "visto": 1}curl "http://localhost:5000/api/progreso/obtener?serie=Mi%20Serie&filename=Temporada%201/cap1.mp4" \
-b "session=TU_SESSION"Añade o elimina una serie/película de la lista de favoritos.
POST /api/favoritos/toggle
Content-Type: application/json
| Parámetro | Tipo | Descripción |
|---|---|---|
serie |
string (obligatorio) | Nombre de la serie/película |
Respuesta:
{"favorito": true}Guarda el estado de una serie en la lista del usuario.
POST /api/lista/guardar
Content-Type: application/json
| Parámetro | Tipo | Descripción |
|---|---|---|
serie |
string (obligatorio) | Nombre de la serie |
estado |
integer | 0=Pendiente, 1=Viendo, 2=Visto |
GET /api/lista/estado?serie=<serie>
GET /api/lista/obtener
Abre la ventana de config_admin.py (solo accesible desde localhost).
GET /api/abrir_config_admin
Verifica que el servidor está activo.
GET /api/ping
Respuesta:
{"status": "servidor en linea"}curl http://localhost:5000/api/pingDetiene únicamente el proceso de FlaskCast. Solo funciona si está habilitado en config_admin.py.
GET /api/off
Respuesta:
{"status": "Apagando servidor..."}Apaga todo el sistema operativo. Solo funciona si está habilitado en config_admin.py.
GET /api/off/all
Respuesta:
{"status": "Apagando sistema..."}Estos endpoints sirven el contenido audiovisual directamente desde el navegador. No requieren autenticación.
| Endpoint | Descripción |
|---|---|
GET /video/<serie>/<archivo> |
Sirve un archivo de vídeo para reproducción en el navegador |
GET /thumbnail/<serie>/<archivo> |
Sirve o genera automáticamente una miniatura del vídeo (fotograma a los 3 segundos) |
GET /portada/<serie> |
Sirve la imagen de portada _img.png de una serie |
GET /serie/<nombre_serie> |
Página web con el catálogo de capítulos de una serie |
GET /tv/reproducir/<serie>/<archivo> |
Reproductor optimizado para SmartTV |
http://localhost:5000/video/Mi%20Serie/Temporada%201/cap1.mp4
http://localhost:5000/thumbnail/Mi%20Serie/Temporada%201/cap1.mp4
http://localhost:5000/portada/Mi%20Serie
http://localhost:5000/serie/Mi%20Serie
http://localhost:5000/tv/reproducir/Mi%20Serie/Temporada%201/cap1.mp4
Cuando se solicita una miniatura que no existe, FlaskCast genera automáticamente un fotograma extraído a los 3 segundos del vídeo usando FFmpeg. Las miniaturas se almacenan en:
data/media/<Serie>/.thumbnails/<nombre_archivo>.jpg
El reproductor de FlaskCast incluye funciones avanzadas de seguimiento y auto-reproducción.
- Cada 10 segundos, el reproductor envía la posición actual al servidor.
- Si el usuario tiene activada la opción "Marcado automático" en Ajustes:
- >10% reproducido → Estado cambia a "Viendo" (badge azul)
- >85% reproducido → Estado cambia a "Visto" (badge verde)
- Si se desactiva el marcado automático, los estados solo cambian al hacer clic manualmente sobre la etiqueta del capítulo.
Al finalizar un vídeo, el reproductor carga automáticamente el siguiente capítulo de la misma temporada (si existe). Esta función:
- Solo aplica a vídeos en formatos web (
.mp4,.webm,.ogg) - No avanza si es el último capítulo de la temporada
- Muestra un botón "Siguiente" al finalizar
| Formato | Soporte nativo | Conversión necesaria |
|---|---|---|
.mp4 (H.264) |
✅ Sí | No |
.webm |
✅ Sí | No |
.ogg |
✅ Sí | No |
.avi |
❌ No | Sí (a .mp4) |
.mkv |
❌ No | Sí (a .mp4) |
| Método | Ruta | Descripción |
|---|---|---|
| GET | / |
Catálogo principal con paginación, filtros y continuar viendo |
| GET | /page/<n> |
Catálogo paginado |
| GET | /serie/<nombre> |
Detalle de una serie/película con hero banner y capítulos |
| GET | /listas |
Listas personales (Favoritos, Pendiente, Viendo, Visto) |
| GET | /tv/reproducir/<serie>/<archivo> |
Reproductor SmartTV |
| GET | /live |
Lista de streams en vivo |
| GET | /live/tv/<indice> |
Reproductor SmartTV para streams en vivo |
| GET | /live/gestion |
Gestión web de streams (ver Gestión web de streams) |
| POST | /live/streams/crear |
Añadir stream (ver Gestión web de streams) |
| POST | /live/streams/editar |
Editar stream (ver Gestión web de streams) |
| POST | /live/streams/eliminar |
Eliminar stream (ver Gestión web de streams) |
| POST | /live/streams/mover |
Reordenar stream (ver Gestión web de streams) |
| GET | /usuarios_panel |
Panel de gestión de usuarios |
| GET | /ajustes |
Panel de configuración (tema, idioma, marcado automático) |
| GET/POST | /ajustes |
Guardar ajustes del usuario |
| GET/POST | /login |
Pantalla de autenticación (solo si auth habilitada) |
| GET | /logout |
Cerrar sesión de autenticación |
| GET | /biblioteca |
Gestión web de la biblioteca (Ver Biblioteca web) |
| Método | Ruta | Descripción | Rate Limit |
|---|---|---|---|
| POST | /api/videos/add |
Subir vídeo a una serie | 10/min |
| POST | /api/videos/rm |
Eliminar vídeo por nombre | 10/min |
| GET | /api/videos |
Listar todas las series | 200/min |
| GET | /api/videos/<serie> |
Obtener estructura de una serie | 200/min |
| GET | /api/video/<serie>/<archivo> |
Descargar/archivo de vídeo | 200/min |
| POST | /api/convertir/<serie>/<archivo> |
Convertir vídeo incompatible a MP4 | 5/min |
| POST | /api/eliminar/<serie>/<archivo> |
Eliminar archivo directo | 10/min |
| GET | /api/estados |
Consultar conversiones activas con progreso | 200/min |
| GET | /api/conversion_result/<serie>/<archivo> |
Consultar resultado de conversión | 200/min |
| POST | /api/progreso/guardar |
Guardar posición de reproducción | 60/min |
| GET | /api/progreso/obtener |
Obtener posición guardada | 200/min |
| POST | /api/favoritos/toggle |
Añadir/quitar de favoritos | 30/min |
| GET | /api/favoritos |
Obtener lista de favoritos | 200/min |
| GET | /api/lista/estado |
Obtener estado de lista de una serie | 200/min |
| POST | /api/lista/guardar |
Guardar estado en lista (Pendiente/Viendo/Visto) | 30/min |
| GET | /api/lista/obtener |
Obtener todas las listas del usuario | 200/min |
| GET | /api/abrir_config_admin |
Abrir panel de administración (solo localhost; dentro del ejecutable) | 200/min |
| GET | /api/ping |
Verificar estado del servidor | 200/min |
| GET | /api/off |
Apagar servidor (si habilitado) | 2/min |
| GET | /api/off/all |
Apagar sistema (si habilitado) | 1/min |
| Método | Ruta | Descripción |
|---|---|---|
| GET | /video/<serie>/<archivo> |
Servir archivo de vídeo |
| GET | /thumbnail/<serie>/<archivo> |
Servir/generar miniatura |
| GET | /portada/<serie> |
Servir portada de serie/película |
- Ejecutar localmente:
python app.py - Entorno virtual (Windows):
venv\Scripts\activate - Docker:
docker-compose up --build - Ver config del admin:
python config_admin.py --status - Guardar clave OMDb:
python config_admin.py --omdb-key tu_clave
Correcciones críticas:
- Corregido: transacción fallaba en Linux — El codificador
libx264estaba hardcodeado pero no disponible en distribuciones Linux (Fedora/RHEL). Ahora la app detecta automáticamente el mejor codificador H.264 disponible al iniciar:libx264→libopenh264→h264_nvenc→h264_amf→h264_qsv→h264_vaapi→h264_v4l2m2m. - Corregido: Ctrl+C abortaba conversiones — FFmpeg se ejecutaba como hijo directo del proceso Python y recibía SIGINT al pulsar Ctrl+C (returncode=255). Ahora se usa
start_new_session=Truepara aislar FFmpeg en una nueva sesión de procesos. - Corregido: estado inconsistente entre workers de Gunicorn — Con
--workers 4, cada worker tenía su propioconversiones_activasen memoria. El frontend recibía "desconocido" al consultar un worker que no tenía la conversión. Solución:--workers 1 --threads 6para compartir el estado. - Corregido: verificación de éxito de conversión — Antes se asumía que si el identificador desaparecía de
conversiones_activas, la conversión había tenido éxito. Ahora se verificareturncode == 0,os.path.exists(mp4)ygetsize > 0.
Nuevas funcionalidades:
- Barra de progreso en tiempo real — Durante la conversión se muestra una barra de progreso con porcentaje en la tarjeta del capítulo, parseando la salida
time=HH:MM:SSde FFmpeg. - Ocultación de
.mp4durante conversión — Los archivos.mp4generados no aparecen en la interfaz hasta que la conversión alcanza el 100%. - Endpoint
/api/conversion_result— Nuevo endpoint para consultar el resultado de una conversión completada (éxito/fallo + existencia del.mp4). - Logging detallado — Cada paso de la conversión se registra en la consola: inicio, PID de FFmpeg, progreso cada 10%, resultado final con tamaño del archivo, y errores con las últimas líneas de stderr.
Cambios técnicos:
hilo_conversion()reescrito consubprocess.Popen()en vez desubprocess.run()resultados_conversionesdict almacena éxito/fallo por identificadorprogreso_conversionesdict almacena el porcentaje actual por conversiónGET /api/estadosahora devuelve{"activos": [...], "progreso": {...}}- Frontend (
verificarEstados()) bifurca entre éxito y fallo al consultar el resultado - Funciones auxiliares
_obtener_duracion()(ffprobe) y_parsear_progreso_ffmpeg() - CSS:
.conversion-progress,.conversion-progress-bar,.conversion-progress-text - Plantilla
serie.html: barra de progreso añadida a tarjetas incompatibles
Si quieres contribuir, abre un issue o crea un pull request. Para cambios mayores, escribe primero un issue describiendo la propuesta.
Revisa el archivo LICENSE incluido en el repositorio.
