diff --git a/server/app.py b/server/app.py index 675ddc7..b94b5bb 100644 --- a/server/app.py +++ b/server/app.py @@ -49,8 +49,15 @@ VERSION = _version() # app.js uses the same value as these stats. Change here only. DONE_RATIO = 0.95 -# latest probe result + last 20 samples (~1h at 3-min spacing), for GET /api/cdn +# latest probe result + last 20 samples, for GET /api/cdn _CDN = {"state": "unknown", "kbps": 0.0, "ts": 0.0, "hist": []} +# ponytail: probe only when someone is around to care — skip the 64 KB video +# download while the app has been idle (no stream requested in 15 min), and +# back off to 30 min while DEGRADED (re-hitting a blocked CDN every 3 min is +# just churn). api_stream() bumps _LAST_PLAY. +_LAST_PLAY = 0.0 +_IDLE_SECS = 900 +_DEGRADED_RETRY = 1800 # origin (primary provider pages/API) health — separate host, separate failure # mode (observed 2026-09-03: CDN fine while origin 503'd "Under Maintenance") _ORIGIN = {"state": "unknown", "msg": "", "ts": 0.0, "hist": []} @@ -60,8 +67,9 @@ _CDN_THRESHOLD = float(os.environ.get("ANIWEB_CDN_KBPS", "40")) def _cdn_probe(): - """Background probe: pull 64KB from the CDN every 3 min and log state - flips, so server.log shows when a degraded window ends (CDN RECOVERED). + """Background probe: pull 64KB from the CDN and log state flips, so + server.log shows when a degraded window ends (CDN RECOVERED). Skipped + while the app is idle and backed off while DEGRADED (see _LAST_PLAY). Probe target: ep_id 1 = One Piece E1 on the primary provider. ponytail: single fixed episode; if it ever disappears, the probe logs DEGRADED forever — swap the ID, don't build a picker.""" @@ -72,6 +80,9 @@ def _cdn_probe(): state = None while True: + if time.time() - _LAST_PLAY > _IDLE_SECS: + time.sleep(60) # nobody's watching: don't burn upstream bandwidth + continue try: m = upstream.master_m3u8(1, "jpn") if not m: @@ -97,7 +108,7 @@ def _cdn_probe(): _CDN["hist"].append({"t": time.time(), "kbps": round(kbps)}) del _CDN["hist"][:-20] _origin_probe() - time.sleep(180) + time.sleep(_DEGRADED_RETRY if now == "DEGRADED" else 180) def _origin_probe(): @@ -559,6 +570,8 @@ def api_episodes(anime_id: int): @app.get("/api/episode/{ep_id}/stream") def api_stream(ep_id: int, lang: str = "jpn", request: Request = None): + global _LAST_PLAY + _LAST_PLAY = time.time() # wakes the CDN probe: a viewer is here def go(): master = _upstream(upstream.master_m3u8, ep_id, lang) if not master: