Files
KiraTV/backend/core/xtream_client.py
T
joaquin f5d6745ed0 Initial commit: KiraTV IPTV proxy server
Full-stack IPTV management system with Xtream Codes API compatibility,
multi-account provider fan-out, Jellyfin local library integration,
real-time dashboard, EPG support, and A/V-sync-safe stream recovery.
2026-05-17 23:22:05 +02: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