From e5dd43390627795227db2dafd29f18b23855a654 Mon Sep 17 00:00:00 2001 From: sascha Date: Sun, 16 Aug 2026 22:04:20 +0200 Subject: [PATCH] Fix long Emby history ranges (app.py) --- app.py | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/app.py b/app.py index 5171de7..b23d8f2 100644 --- a/app.py +++ b/app.py @@ -484,6 +484,12 @@ def _analyze_emby_sharing(series: list[dict], step_seconds: int) -> dict: } +def _emby_history_step(days: int) -> int: + """Keep query_range below Prometheus' 11,000-points-per-series limit.""" + duration_seconds = days * 86400 + return max(60, math.ceil((duration_seconds / 10_500) / 60) * 60) + + async def _fetch_emby_session_history(days: int, server: str) -> tuple[list[dict], int]: cfg = SERVICES.get("grafana") if not cfg: @@ -497,23 +503,43 @@ async def _fetch_emby_session_history(days: int, server: str) -> tuple[list[dict query = f"max by ({labels}) ({selector})" end = int(time.time()) start = end - days * 86400 - step = max(60, math.ceil(((end - start) / 30000) / 60) * 60) + step = _emby_history_step(days) url = f"{request_data['base_url'].rstrip('/')}/api/datasources/proxy/uid/{datasource_uid}/api/v1/query_range" + series_by_metric: dict[str, dict] = {} + chunk_seconds = 30 * 86400 try: async with httpx.AsyncClient(timeout=90) as client: - response = await client.get( - url, - params={"query": query, "start": start, "end": end, "step": step}, - headers=request_data["headers"], cookies=request_data["cookies"], - ) - response.raise_for_status() - payload = response.json() + chunk_start = start + while chunk_start < end: + chunk_end = min(chunk_start + chunk_seconds, end) + response = await client.get( + url, + params={"query": query, "start": chunk_start, "end": chunk_end, "step": step}, + headers=request_data["headers"], cookies=request_data["cookies"], + ) + response.raise_for_status() + payload = response.json() + if payload.get("status") != "success": + raise HTTPException(502, "Prometheus rejected the Emby session history query") + for item in payload.get("data", {}).get("result", []): + metric = item.get("metric", {}) + key = json.dumps(metric, sort_keys=True, separators=(",", ":")) + merged = series_by_metric.setdefault(key, {"metric": metric, "values": []}) + merged["values"].extend(item.get("values", [])) + chunk_start = chunk_end except (httpx.HTTPError, ValueError) as exc: log.warning("Emby sharing history query failed: %s", type(exc).__name__) raise HTTPException(502, "Emby session history is temporarily unavailable") - if payload.get("status") != "success": - raise HTTPException(502, "Prometheus rejected the Emby session history query") - return payload.get("data", {}).get("result", []), step + + series = [] + for item in series_by_metric.values(): + values_by_timestamp = { + float(value[0]): value for value in item["values"] + if isinstance(value, (list, tuple)) and len(value) >= 2 + } + item["values"] = [values_by_timestamp[ts] for ts in sorted(values_by_timestamp)] + series.append(item) + return series, step @app.get("/emby/account-sharing")