diff --git a/.env.example b/.env.example index da42a51..aa21662 100644 --- a/.env.example +++ b/.env.example @@ -60,6 +60,13 @@ MONITORINK_CODEX_TOKEN_FILE=/hermes/auth.json #MONITORINK_TRACKER_TR4KER_URL=https://tr4ker.net #MONITORINK_TRACKER_TR4KER_USER=TonIdentifiant #MONITORINK_TRACKER_TR4KER_PASS=TonMotDePasse +# yggreborn (YggTorrent) : type "yggreborn" (Flask + CSRF). ⚠️ USER = EMAIL (login par email). +# Ratio seul (lu dans l'en-tête), pas de up/down ni jetons. +#MONITORINK_TRACKER_YGGREBORN_LABEL=yggreborn +#MONITORINK_TRACKER_YGGREBORN_TYPE=yggreborn +#MONITORINK_TRACKER_YGGREBORN_URL=https://www.yggreborn.org +#MONITORINK_TRACKER_YGGREBORN_USER=ton.email@exemple.com +#MONITORINK_TRACKER_YGGREBORN_PASS=TonMotDePasse #MONITORINK_TRACKER_TTL=1800 # Home Assistant (optionnel) — laisser vide pour désactiver diff --git a/backend/integrations/trackers.py b/backend/integrations/trackers.py index 16b01b6..9fe4ecb 100644 --- a/backend/integrations/trackers.py +++ b/backend/integrations/trackers.py @@ -29,6 +29,8 @@ from config import TrackerSpec, config _UA = ("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/126.0 Safari/537.36") _CSRF_META = re.compile(r'name="csrf-token"\s+content="([^"]+)"') +_CSRF_INPUT = re.compile(r'name="csrf_token"[^>]*value="([^"]+)"') +_YGG_RATIO = re.compile(r"Ratio\s*:\s*([\d.]+)") @dataclass @@ -205,7 +207,37 @@ async def _fetch_tr4ker(spec: TrackerSpec) -> TrackerStat: ) -_FETCHERS = {"unit3d_nuxt": _fetch_unit3d, "torr9": _fetch_torr9, "tr4ker": _fetch_tr4ker} +async def _fetch_yggreborn(spec: TrackerSpec) -> TrackerStat: + """yggreborn (YggTorrent) : site rendu serveur (Flask), login form classique avec + CSRF. ⚠️ l'`identifier` est l'EMAIL (champ `type=email`), pas le pseudo. Pas de token + API ; le ratio est affiché tel quel dans l'en-tête (« Ratio : 7.63 ») -> on le lit + directement (pas de up/down exposé simplement ; l'user veut juste le ratio).""" + if not (spec.base_url and spec.username and spec.password): + return TrackerStat(spec.key, spec.label, ok=False, error="non configuré") + + async with httpx.AsyncClient( + timeout=20, follow_redirects=True, headers={"User-Agent": _UA}, + ) as client: + page = await client.get(f"{spec.base_url}/login") + m = _CSRF_INPUT.search(page.text) + if not m: + raise _AuthError("csrf introuvable") + resp = await client.post(f"{spec.base_url}/login", data={ + "csrf_token": m.group(1), "identifier": spec.username, "password": spec.password, + }) + if "incorrect" in resp.text.lower(): + raise _AuthError("identifiants refusés (identifier = email)") + rm = _YGG_RATIO.search(resp.text) + if not rm: + raise _AuthError("ratio introuvable") + + return TrackerStat(spec.key, spec.label, ok=True, ratio=float(rm.group(1))) + + +_FETCHERS = { + "unit3d_nuxt": _fetch_unit3d, "torr9": _fetch_torr9, + "tr4ker": _fetch_tr4ker, "yggreborn": _fetch_yggreborn, +} async def _fetch_one(spec: TrackerSpec) -> TrackerStat: diff --git a/backend/templates/dashboard.html b/backend/templates/dashboard.html index 0c8550f..d6a3918 100644 --- a/backend/templates/dashboard.html +++ b/backend/templates/dashboard.html @@ -215,7 +215,7 @@ {% if t.ok %}{{ t.ratio_h }} {% else %}{{ t.error }}{% endif %} - {% if t.ok %}