From 310f37f29b4c50757b38330ca679fd44199d4bd7 Mon Sep 17 00:00:00 2001 From: sravan Date: Thu, 23 Jul 2026 11:48:11 +0530 Subject: [PATCH] fix(video): centre spinner never appeared on the one stall users actually see MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported: "the loading buffer is not the spinner, it still loads at the left of the timer." Correct — and it was our bug, not a cosmetic preference. The spinner was driven by a hand-picked event list (waiting/seeking/stalled). But a cold start from preload="none" NEVER fires `waiting`: it runs loadstart -> loadedmetadata -> loadeddata -> canplay -> playing straight through. Since the bandwidth fix, that cold start is the only stall left — so the spinner sat out the exact moment it existed for, leaving just the OS control bar's own small indicator where the play button sits, i.e. left of the timer. Now the spinner is derived from the element's real state rather than guessed from events: busy = seeking || (!paused && !ended && readyState < HAVE_FUTURE_DATA), recomputed on every relevant media event. Simulated against the real event sequences before shipping — cold start, mid-stream stall and seek all spin; paused/ended/idle never do. Also dim the frame to 72% brightness while buffering so the spinner reads instantly against a bright poster, and give it a dark backing disc. Note: the small indicator inside the native control bar belongs to the OS's own video controls and cannot be suppressed while we use them. Ours is now the loud, central one; removing the OS indicator entirely would mean custom controls. Co-Authored-By: Claude Opus 4.8 --- server/public/home.html | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/server/public/home.html b/server/public/home.html index 8c8703d..d349bdd 100644 --- a/server/public/home.html +++ b/server/public/home.html @@ -666,8 +666,10 @@ small/unclear. The spinner is display-only (pointer-events:none) so it never blocks the controls. */ .att-vid{position:relative;width:min(260px,74vw);max-height:340px;border-radius:10px;overflow:hidden;display:block;margin:.15rem 0;background:#000;line-height:0;} .att-vid-v{display:block;width:100%;max-height:340px;background:#000;object-fit:contain;} - .att-vid-spin{position:absolute;top:50%;left:50%;width:46px;height:46px;margin:-23px 0 0 -23px;border-radius:50%;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;opacity:0;pointer-events:none;z-index:3;} + .att-vid-spin{position:absolute;top:50%;left:50%;width:46px;height:46px;margin:-23px 0 0 -23px;border-radius:50%;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;background:rgba(0,0,0,.45);opacity:0;pointer-events:none;z-index:3;} .att-vid.buffering .att-vid-spin{opacity:1;animation:ptrspin .8s linear infinite;} + /* Dim the frame while buffering so the centre spinner reads instantly against a bright poster. */ + .att-vid.buffering .att-vid-v{filter:brightness(.72);} .bubble.mine .att-file{background:rgba(255,255,255,.18);border-color:rgba(255,255,255,.3);} .att-file .att-sz{opacity:.65;font-size:.75rem;flex:0 0 auto;} /* groups */ @@ -1165,7 +1167,7 @@ -