a resting mouse is not the user
The chrome came back for a couple of seconds every few minutes during a film with nobody touching anything. Only three things wake it, and none of them is periodic — so the culprit is a stray pointermove. A mouse sitting on a desk still emits the odd one-pixel event, a bumped table emits a few, and the browser synthesises a zero-delta move of its own when the cursor style changes, which this player does every single time it hides the cursor. A move now has to travel eight manhattan pixels from wherever the pointer last genuinely woke it. The anchor only advances on a real wake, so a slow deliberate drift still accumulates past the threshold — it is jitter around a fixed point that stops counting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,8 @@ import Hls from './vendor/hls.mjs';
|
||||
|
||||
const PROGRESS_INTERVAL_MS = 10_000;
|
||||
const CHROME_HIDE_MS = 2_500;
|
||||
/** Manhattan pixels a pointer must travel before it counts as the user, rather than as a resting mouse. */
|
||||
const WAKE_THRESHOLD_PX = 8;
|
||||
const VOLUME_KEY = 'jellyfin:volume';
|
||||
|
||||
type Mode = 'file' | 'hls-mse' | 'progressive';
|
||||
@@ -110,6 +112,8 @@ export const VideoPlayer = ({ id }: { id: string }) => {
|
||||
// re-run) every time one of them changes.
|
||||
const audioRef = useRef<number | null>(null);
|
||||
const bitrateRef = useRef<number | null>(null);
|
||||
// Where the pointer was when it last counted as movement — see `onPointerMove`.
|
||||
const pointerRef = useRef<{ x: number; y: number } | null>(null);
|
||||
|
||||
const mode = useMemo(() => (plan ? transportFor(plan) : null), [plan]);
|
||||
const hasTimeline = mode !== null && mode !== 'progressive';
|
||||
@@ -305,6 +309,25 @@ export const VideoPlayer = ({ id }: { id: string }) => {
|
||||
|
||||
const wake = () => setChrome(true);
|
||||
|
||||
/**
|
||||
* Wake on real movement only.
|
||||
*
|
||||
* A bare `pointermove` handler is too credulous to sit in front of a two-hour film: a mouse resting on a
|
||||
* desk still emits the odd one-pixel event, a bumped table emits a few, and a browser will synthesise a
|
||||
* zero-delta move of its own when the cursor style changes — which this player does every time it hides the
|
||||
* cursor. Any one of those read as "the user is here" and put the scrubber back for two and a half seconds,
|
||||
* minutes apart, with nobody having touched anything.
|
||||
*
|
||||
* The anchor only moves when a wake actually happens, so a slow deliberate drift still accumulates past the
|
||||
* threshold and wakes; it is jitter around a fixed point that never does.
|
||||
*/
|
||||
const onPointerMove = (ev: React.PointerEvent) => {
|
||||
const last = pointerRef.current;
|
||||
if (last && Math.abs(ev.clientX - last.x) + Math.abs(ev.clientY - last.y) < WAKE_THRESHOLD_PX) return;
|
||||
pointerRef.current = { x: ev.clientX, y: ev.clientY };
|
||||
wake();
|
||||
};
|
||||
|
||||
// Keyboard on the container rather than on window: the player is a panel inside a workspace, and stealing
|
||||
// space or the arrow keys from whatever else is on screen would be wrong. Autofocus puts it in reach.
|
||||
const onKeyDown = (ev: React.KeyboardEvent) => {
|
||||
@@ -355,7 +378,7 @@ export const VideoPlayer = ({ id }: { id: string }) => {
|
||||
tabIndex={-1}
|
||||
autoFocus
|
||||
onKeyDown={onKeyDown}
|
||||
onPointerMove={wake}
|
||||
onPointerMove={onPointerMove}
|
||||
onPointerDown={wake}
|
||||
className={`absolute inset-0 z-30 bg-black outline-none ${idle ? 'cursor-none' : ''}`}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user