Background CDN probe: log throughput + state flips every 3 min

64KB Range fetch from a fixed episode (JJK S2 E25) on a daemon thread;
logs '[probe] CDN OK/DEGRADED (N KB/s)' on state change so server.log
shows exactly when a degraded window ends. 18/18 tests, verified live.
This commit is contained in:
jozamudi committed 2026-08-31 15:17:20 +00:00
1 parent 4c5dbab4b0
commit eea538ab17
1 file changed
+37
+37
View File
@@ -7,6 +7,7 @@ Cloudflare workaround) and rewrites playlist URIs to go through itself.
import base64 import base64
import json import json
import os import os
import threading
import time import time
from collections import Counter, OrderedDict from collections import Counter, OrderedDict
from datetime import datetime from datetime import datetime
@@ -24,6 +25,42 @@ from . import db, upstream, users
ROOT = Path(__file__).parent.parent ROOT = Path(__file__).parent.parent
app = FastAPI(title="ani-web") app = FastAPI(title="ani-web")
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).
Probe target: any valid episode (36019 = JJK S2 E25).
ponytail: single fixed episode; if it ever disappears, the probe logs
DEGRADED forever — swap the ID, don't build a picker."""
def first(pl, base):
l = next(x.strip() for x in pl.splitlines()
if x.strip() and not x.startswith("#"))
return l if l.startswith("http") else base + l
state = None
while True:
try:
m = upstream.master_m3u8(36019, "jpn")
if not m:
raise RuntimeError("no master m3u8")
base = m.rsplit("/", 1)[0] + "/"
v = first(upstream._get(m, timeout=10).text, base)
seg = first(upstream._get(v, timeout=10).text, base)
t = time.time()
r = upstream._session().get(seg, headers={"Range": "bytes=0-65535"},
impersonate="chrome", timeout=10)
kbps = len(r.content) / 1024 / max(time.time() - t, 0.01)
except Exception:
kbps = 0.0
now = "OK" if kbps >= 40 else "DEGRADED"
if now != state:
print(f"[probe] CDN {now} ({kbps:.0f} KB/s)", flush=True)
state = now
time.sleep(180)
threading.Thread(target=_cdn_probe, daemon=True).start()
# ---------- auth: multi-user, signed cookies ---------- # ---------- auth: multi-user, signed cookies ----------
users.migrate_legacy() # file-based legacy import; db.init() absorbs its output users.migrate_legacy() # file-based legacy import; db.init() absorbs its output
db.init() db.init()