eon: fix ex-yu providers

This commit is contained in:
Nirvana
2026-10-20 20:03:21 +02:00
parent a83151fa38
commit 2b18f99fbb
3 changed files with 61 additions and 14 deletions
+16 -1
View File
@@ -372,7 +372,21 @@ class EonAuth:
def _grant_refresh(self, refresh: str) -> EonTokenResponse:
if self._oidc is None:
return self._grant({"grant_type": "refresh_token", "refresh_token": refresh})
# Browser capture: parameters in the query, EMPTY multipart body
# (just the closing boundary -- 44 bytes with a WebKit-style boundary).
boundary = "----WebKitFormBoundary" + secrets.token_hex(8)
body = f"--{boundary}--\r\n".encode("ascii")
headers = {
"User-Agent": self._config.user_agent,
"Accept": "application/json, text/plain, */*",
"Authorization": self._config.basic_auth_value(),
"Content-Type": f"multipart/form-data; boundary={boundary}",
**self._config.origin_headers(),
}
return self._grant(
{"grant_type": "refresh_token", "refresh_token": refresh},
data=body, headers=headers,
)
client = self._config.oidc_client
# UNVERIFIED against a browser capture: the standard refresh (same Basic auth as the code exchange)
# request. A rejection just falls through to a full login (see _login).
@@ -448,6 +462,7 @@ class EonAuth:
"Accept": "application/json",
"Authorization": self._config.basic_auth_value(),
"Content-Type": f"multipart/form-data; boundary={boundary}",
**self._config.origin_headers(),
}
# 400/401 = wrong credentials (permanent); a 403 may be a WAF -> transient.
return self._grant({"grant_type": "password"}, data=body.encode("utf-8"), headers=headers,
@@ -102,15 +102,35 @@ class EonDefaults:
# 604800000 ms = 168 h in every sample seen).
CATCHUP_WINDOW_HOURS = 168
# --- Browser origin (web platform only) ------------------------------
# The web client sends these on its token calls; an Origin/Referer check
# on the token endpoint would explain a 403 otherwise.
WEB_ORIGIN = "https://eon.tv"
WEB_REFERER = "https://eon.tv/"
# brand key (config) -> CDN identifier as the broker knows it
# brand key (config) -> broker identifier (v2/brands)
BRAND_IDENTIFIERS: Dict[str, str] = {
"sbb": "sbb-qa",
"telemach": "telemach",
# Legacy alias: existing configs may still carry brand: "telemach".
"telemach": "telemach-ba",
"telemach-ba": "telemach-ba",
"telemach-me": "telemach-me",
"telemach-hr": "telemach-hr",
"telemach-si": "telemach-si",
"vivacom": "vivacom",
"nova": "nova",
"vivacom": "vivacom",
}
# Used when config has no explicit "brand". Keys are lower-case country codes.
COUNTRY_DEFAULT_BRANDS: Dict[str, str] = {
"ba": "telemach-ba",
"me": "telemach-me",
"hr": "telemach-hr",
"si": "telemach-si",
"rs": "sbb",
"gr": "nova",
"bg": "vivacom",
}
@@ -235,10 +255,15 @@ class EonConfig:
bootstrap and managers. Never rebuild a second config from a subset.
"""
def __init__(self, config_dict: Optional[dict] = None):
def __init__(self, config_dict: Optional[dict] = None, country: Optional[str] = None):
c = config_dict or {}
self.brand = str(c.get("brand", EonDefaults.DEFAULT_BRAND)).lower()
# Brand: explicit config wins, else the country's default brand,
# else the global default.
default_brand = COUNTRY_DEFAULT_BRANDS.get(
(country or "").lower(), EonDefaults.DEFAULT_BRAND
)
self.brand = str(c.get("brand", default_brand)).lower()
if self.brand not in BRAND_IDENTIFIERS:
raise ConfigurationError(
f"{EonDefaults.PROVIDER_LABEL}: unknown brand {self.brand!r} "
@@ -325,4 +350,10 @@ class EonConfig:
requests without a recognised browser / smart-TV UA; it must NOT get
API headers (tokens).
"""
return {"User-Agent": self.user_agent}
return {"User-Agent": self.user_agent}
def origin_headers(self) -> dict:
"""Browser origin the web client sends on token calls (web platform only)."""
if self.platform.is_android_tv:
return {}
return {"Origin": EonDefaults.WEB_ORIGIN, "Referer": EonDefaults.WEB_REFERER}
@@ -1,5 +1,4 @@
# streaming_providers/providers/eon/provider.py
# streaming_providers/providers/eon/provider.py
"""
Eon provider -- orchestrator only.
@@ -8,10 +7,11 @@ endpoints, auth, api, catalog, caches) and BUILDS the managers. Everything
identical across providers is inherited from ManagedProvider.
Eon is a white-label platform: `config["brand"]` (vivacom, nova, sbb,
telemach, telemach-hr, telemach-si) picks the operator, `config["platform"]`
("web" / "androidtv") the client identity. Geo-unblocking users also need
Kodi's global proxy: the CDN requests are made by inputstream.adaptive, which
does not use the backend's ProxyConfig.
telemach-ba, telemach-me, telemach-hr, telemach-si) picks the operator,
`config["platform"]` ("web" / "androidtv") the client identity. When no
brand is configured, the construction country picks the default brand.
Geo-unblocking users also need Kodi's global proxy: the CDN requests are made
by inputstream.adaptive, which does not use the backend's ProxyConfig.
"""
from typing import ClassVar, Dict, List, Optional
@@ -61,8 +61,9 @@ class EonProvider(ManagedProvider):
super().__init__(country=country)
self.country = self.country.lower()
# 1. ONE config object, shared by every layer.
self.provider_config = EonConfig(config or {})
# 1. ONE config object, shared by every layer. The country selects the
# default brand when config has no explicit "brand".
self.provider_config = EonConfig(config or {}, country=self.country)
# 2. HTTP manager.
self.http_manager = self._setup_http_manager(