v0.5.110: stop carrying stale live badges; probe live status in local serve.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
voson
2026-07-30 01:11:13 +08:00
co-authored by Cursor
parent d6212169af
commit fe1b13b7c1
10 changed files with 125 additions and 56 deletions
+63 -3
View File
@@ -19,7 +19,9 @@ import argparse
import json
import mimetypes
import threading
import time
import webbrowser
from datetime import datetime, timezone
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlparse
@@ -47,11 +49,15 @@ from shared.paths import (
)
from shared.relations import DEFAULT_RELATIONS, load_relations
from fetch_streamer_live import probe_streamers
from mechanic_tags import QUERY_MECHANIC_ORDER, mechanic_query_payload
WEB_DIR = WEB_FRONTEND
MOBILE_DEMAND_PATH = ROOT / "web" / ".refresh" / "mobile_demand.json"
_MOBILE_DEMAND_LOCK = threading.Lock()
_LIVE_STATUS_LOCK = threading.Lock()
_LIVE_STATUS_TTL = 60
_LIVE_STATUS_CACHE: tuple[float, dict] | None = None
def _read_mobile_demand_count() -> int:
@@ -76,6 +82,62 @@ def _inc_mobile_demand_count() -> int:
return n
def _probe_live_status() -> dict:
"""Probe local streamer rooms without mutating streamers.json."""
global _LIVE_STATUS_CACHE
now = time.monotonic()
cached = _LIVE_STATUS_CACHE
if cached and now - cached[0] < _LIVE_STATUS_TTL:
return cached[1]
with _LIVE_STATUS_LOCK:
now = time.monotonic()
cached = _LIVE_STATUS_CACHE
if cached and now - cached[0] < _LIVE_STATUS_TTL:
return cached[1]
try:
payload = json.loads(STREAMERS_PATH.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
result = {
"probed_at": datetime.now(timezone.utc).isoformat(),
"ttl": _LIVE_STATUS_TTL,
"streamers": {},
}
_LIVE_STATUS_CACHE = (now, result)
return result
rows = payload.get("streamers")
if not isinstance(rows, list):
rows = []
for row in rows:
if isinstance(row, dict) and row.get("live_url"):
# A failed probe is unknown and must not preserve a stale live badge.
row["is_live"] = False
row.pop("live_probed_at", None)
probe_streamers(payload)
streamers = {}
for row in rows:
if not isinstance(row, dict) or not row.get("live_url"):
continue
sid = str(row.get("id") or "").strip()
if not sid:
continue
cell = {"is_live": row.get("is_live") is True}
if not row.get("live_probed_at"):
cell["stale"] = True
streamers[sid] = cell
result = {
"probed_at": datetime.now(timezone.utc).isoformat(),
"ttl": _LIVE_STATUS_TTL,
"streamers": streamers,
}
_LIVE_STATUS_CACHE = (time.monotonic(), result)
return result
GRID_ORDER_PATH = DATA / "hero_grid_order.json"
HERO_ITEMS_PATH = DATA / "hero_items.json"
HERO_STATS_PATH = DATA / "hero_stats.json"
@@ -842,9 +904,7 @@ class Handler(BaseHTTPRequestHandler):
self._json(200, build_payload())
return
if path == "/api/live-status":
# Local-dev stub for the Pages Function (edge probing runs in
# production only); the empty map keeps the UI on data.json is_live.
self._json(200, {"probed_at": None, "ttl": 300, "streamers": {}})
self._json(200, _probe_live_status())
return
if path == "/api/mobile-demand":
self._json(200, {"count": _read_mobile_demand_count()})