Files
KiraTV/backend/core/xtream_client.py
T
KiraStream b969b7e5af Mejoras de estabilidad de stream IPTV y nuevo registro de fallos
- restream.py: eliminar -reconnect_streamed de ffmpeg para evitar rebobinados
  al reconectar (el proveedor re-enviaba desde keyframe anterior causando PTS
  backward). El loop de Python gestiona las reconexiones con conexión fresca.
- restream.py: eliminar _drain_queue del loop de reconexión para que el buffer
  de la cola cubra el tiempo de reconexión sin pantalla negra en el player.
- restream.py: añadir buffer_server_bytes/secs/capacity a stats() y método
  _log_stream_event() para persistir eventos de caída y recuperación en BD.
- models/stream_event.py: nuevo modelo StreamEvent para registro de fallos.
- database.py: registrar StreamEvent en init_db.
- api/admin/logs.py: nuevos endpoints GET/DELETE /logs/events.
- Dashboard.tsx: mostrar reserva de buffer del servidor (capacidad + estado).
- Logs.tsx: añadir pestaña "Fallos de stream" con tabla de eventos persistidos.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 14:43:31 +00:00

88 lines
3.4 KiB
Python

"""Client for consuming Xtream Codes API from provider."""
import aiohttp
import asyncio
from typing import Any
import logging
logger = logging.getLogger(__name__)
class XtreamClient:
def __init__(self, base_url: str, username: str, password: str):
self.base_url = base_url.rstrip("/")
self.username = username
self.password = password
def _api_url(self) -> str:
return f"{self.base_url}/player_api.php"
async def _get(self, params: dict) -> Any:
params = {"username": self.username, "password": self.password, **params}
timeout = aiohttp.ClientTimeout(total=30)
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.get(self._api_url(), params=params, ssl=False) as resp:
resp.raise_for_status()
return await resp.json(content_type=None)
async def get_user_info(self) -> dict:
return await self._get({})
async def get_live_categories(self) -> list[dict]:
return await self._get({"action": "get_live_categories"})
async def get_live_streams(self, category_id: str | None = None) -> list[dict]:
params = {"action": "get_live_streams"}
if category_id:
params["category_id"] = category_id
return await self._get(params)
async def get_vod_categories(self) -> list[dict]:
return await self._get({"action": "get_vod_categories"})
async def get_vod_streams(self, category_id: str | None = None) -> list[dict]:
params = {"action": "get_vod_streams"}
if category_id:
params["category_id"] = category_id
return await self._get(params)
async def get_series_categories(self) -> list[dict]:
return await self._get({"action": "get_series_categories"})
async def get_series(self, category_id: str | None = None) -> list[dict]:
params = {"action": "get_series"}
if category_id:
params["category_id"] = category_id
return await self._get(params)
async def get_series_info(self, series_id: str) -> dict:
return await self._get({"action": "get_series_info", "series_id": series_id})
async def get_vod_info(self, vod_id: str) -> dict:
return await self._get({"action": "get_vod_info", "vod_id": vod_id})
async def get_epg(self, stream_id: str | None = None) -> dict:
params = {"action": "get_short_epg"}
if stream_id:
params["stream_id"] = stream_id
return await self._get(params)
def build_live_stream_url(self, stream_id: str, ext: str = "ts") -> str:
return f"{self.base_url}/{self.username}/{self.password}/{stream_id}.{ext}"
def build_vod_stream_url(self, stream_id: str, ext: str = "mkv") -> str:
return f"{self.base_url}/movie/{self.username}/{self.password}/{stream_id}.{ext}"
def build_series_stream_url(self, stream_id: str, ext: str = "mkv") -> str:
return f"{self.base_url}/series/{self.username}/{self.password}/{stream_id}.{ext}"
def get_xmltv_url(self) -> str:
return f"{self.base_url}/xmltv.php?username={self.username}&password={self.password}"
async def test_connection(self) -> bool:
try:
info = await self.get_user_info()
return info.get("user_info", {}).get("status") == "Active"
except Exception as e:
logger.warning(f"Provider connection test failed: {e}")
return False