fix(ui): hold must-watch seek lock on progressive (non-HLS) video
The lock clamped currentTime only during the seeking event. HLS defers the forward seek until the fragment loads, so that clamp caught it; but for progressive MP4 the forward seek takes effect immediately and the mid-seek write is ignored, so playback leaked past the watched point (snackbar showed, no snap-back). Keep the immediate seeking-time clamp (HLS relies on it to avoid buffering ahead) and add an authoritative re-clamp in seeked: if the playhead still landed past the limit, snap back via a backward seek into buffered content, which holds. Snackbar now fires once per blocked gesture and only when a clamp actually happens.
This commit is contained in:
@@ -84,7 +84,8 @@
|
|||||||
let playedSeconds = 0;
|
let playedSeconds = 0;
|
||||||
let lastValidTime = element.currentTime;
|
let lastValidTime = element.currentTime;
|
||||||
let isSeeking = false;
|
let isSeeking = false;
|
||||||
let hasBlockedThisSeek = false;
|
let isReclamping = false;
|
||||||
|
let seekBlockNotified = false;
|
||||||
let lastHeartbeatAt = 0;
|
let lastHeartbeatAt = 0;
|
||||||
const seekTolerance = 0.5;
|
const seekTolerance = 0.5;
|
||||||
const heartbeatIntervalMs = 15_000;
|
const heartbeatIntervalMs = 15_000;
|
||||||
@@ -105,18 +106,50 @@
|
|||||||
playedSeconds = 0;
|
playedSeconds = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isAheadOfLimit = () => player.currentTime > furthestSeconds + seekTolerance;
|
||||||
|
|
||||||
|
const notifySeekBlocked = () => {
|
||||||
|
if (seekBlockNotified) return;
|
||||||
|
|
||||||
|
seekBlockNotified = true;
|
||||||
|
policy.onSeekBlocked?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Immediate clamp. For HLS this short-circuits the forward fragment fetch
|
||||||
|
// so the player never buffers ahead. For progressive MP4 the write is
|
||||||
|
// often ignored mid-seek, so `onSeeked` re-asserts it once settled.
|
||||||
const onSeeking = () => {
|
const onSeeking = () => {
|
||||||
isSeeking = true;
|
isSeeking = true;
|
||||||
if (!hasBlockedThisSeek && player.currentTime > furthestSeconds + seekTolerance) {
|
if (isReclamping) return;
|
||||||
hasBlockedThisSeek = true;
|
|
||||||
|
if (isAheadOfLimit()) {
|
||||||
|
notifySeekBlocked();
|
||||||
player.currentTime = furthestSeconds;
|
player.currentTime = furthestSeconds;
|
||||||
policy.onSeekBlocked?.();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSeeked = () => {
|
const onSeeked = () => {
|
||||||
|
// Consume the `seeked` produced by our own clamp write, then settle.
|
||||||
|
if (isReclamping) {
|
||||||
|
isReclamping = false;
|
||||||
|
isSeeking = false;
|
||||||
|
seekBlockNotified = false;
|
||||||
|
lastValidTime = player.currentTime;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authoritative enforcement: if the seek landed past the limit (the
|
||||||
|
// mid-seek clamp didn't hold, e.g. progressive MP4), snap back now —
|
||||||
|
// a backward seek into buffered content, which holds reliably.
|
||||||
|
if (isAheadOfLimit()) {
|
||||||
|
isReclamping = true;
|
||||||
|
notifySeekBlocked();
|
||||||
|
player.currentTime = furthestSeconds;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
isSeeking = false;
|
isSeeking = false;
|
||||||
hasBlockedThisSeek = false;
|
seekBlockNotified = false;
|
||||||
lastValidTime = player.currentTime;
|
lastValidTime = player.currentTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user