From 73a86d3e239da1b818294a25f587cef23b3b59e5 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 7 Sep 2026 22:26:28 +0200 Subject: [PATCH] Show which provider's bytes were slow Added beside the upstream panel and for the same reason: the owner reported anime stuttering and it took an evening of measurement to establish that the television was fine and TorBox's CDN had dipped to 5.5 Mbps. That is a question a dashboard should answer in a glance. Median rather than mean, because one fast session should not hide ten slow ones, and the share of watched time spent waiting alongside it, because that is the number that matches what the viewer actually felt. Co-Authored-By: Claude Opus 5 --- pb_hooks/status.pb.js | 3 ++- pb_public/status.html | 29 ++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pb_hooks/status.pb.js b/pb_hooks/status.pb.js index 420b879..60fd29c 100644 --- a/pb_hooks/status.pb.js +++ b/pb_hooks/status.pb.js @@ -195,7 +195,8 @@ routerAdd("GET", "/api/status", (e) => { // Upstream liveness rides along on the same call rather than a // second round trip: it is produced by the same service and read // by the same screen. - upstreams: stats.upstreams } + upstreams: stats.upstreams, + throughput: stats.throughput } }) return e.json(200, { diff --git a/pb_public/status.html b/pb_public/status.html index bd0deb6..97a8a74 100644 --- a/pb_public/status.html +++ b/pb_public/status.html @@ -417,6 +417,33 @@ function sinceLabel(unix){ ("0" + d.getMinutes()).slice(-2); } +// Whose bytes were slow. Not a ranking input and deliberately not per file: a +// stall is a property of neither the upload nor the hostname, because TorBox +// hands out CDN nodes per request. This exists to answer "was it them or us" +// for a person looking at a screen, which is the question that took an evening +// of measurement the first time it was asked. +function panelThroughput(a){ + var t = a && a.throughput; + if (!t || !t.length) return ""; + var rows = t.map(function(x){ + // Mbit/s reads the way a person thinks about a connection; kbps does not. + var med = (x.medianKbps / 1000).toFixed(1); + var worst = x.worstMinKbps === null ? "—" : (x.worstMinKbps / 1000).toFixed(1); + // Time spent waiting is the number that matches what the viewer felt. + var stall = x.stalledPct === null ? "—" : x.stalledPct + " %"; + var bad = x.stalledPct !== null && x.stalledPct >= 2; + return "" + esc(x.provider) + '' + x.sessions + + '' + med + '' + worst + + '" + stall + + ""; + }).join(""); + return '

Rychlost zdrojů za 24 h

' + + '' + + '' + rows + + '
poskytovatelrelacímedián Mb/snejhorší propadčekání

Medián, ne průměr: jedna rychlá relace nemá schovat deset pomalých. ' + + "Sloupec čekání je podíl sledovaného času stráveného načítáním.

"; +} + function panelAccounts(a){ if (!a || a.error) return ""; return '

Účty

' + @@ -1191,7 +1218,7 @@ async function load(){ el("main").innerHTML = panelServices(d.coolify) + panelReleases(d.releases) + panelClients(d.clients) + panelApi(d.amberApi) + panelAccounts(d.accounts) + - panelUpstreams(d.amberApi) + + panelUpstreams(d.amberApi) + panelThroughput(d.amberApi) + panelErrors(errs); wire(); } catch (e) {