Match capture strips: freeze video on every scrubber interaction
The point-capture and coach-review strips have a forcePause helper that pauses the video (and re-pauses 30ms later to beat HLS.js's async resume) whenever the slider is grabbed or scrubbed. It wasn't being called from the frame-step buttons or the typed-timestamp commit, so using the ± nudge to land on an exact moment while the video was playing would happily keep the video playing and drift past the target. Expose forcePause on both _pcbCtx and _rcbCtx and call it from: - pcbNudge / rcbNudge (frame-step buttons) - commitTimeInput / _rcbCommitTimeInput (typed timestamp on Enter) - _rcbSetActiveSlot (switching start/end slot re-scrubs the video) Playback now resumes only on an explicit play press. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3e18bd2150
commit
9f82d3cf31
@ -4756,6 +4756,9 @@
|
|||||||
timeInp.value = toMinuteSecondClock(video.currentTime || 0);
|
timeInp.value = toMinuteSecondClock(video.currentTime || 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Typing a timestamp is a scrubber interaction — freeze the
|
||||||
|
// frame. Only the play button resumes playback.
|
||||||
|
forcePause();
|
||||||
const dur = Number(video.duration) || 0;
|
const dur = Number(video.duration) || 0;
|
||||||
const clamped = Math.max(0, dur > 0 ? Math.min(dur, secs) : secs);
|
const clamped = Math.max(0, dur > 0 ? Math.min(dur, secs) : secs);
|
||||||
try { video.currentTime = clamped; } catch (_) {}
|
try { video.currentTime = clamped; } catch (_) {}
|
||||||
@ -4808,6 +4811,10 @@
|
|||||||
_pcbCtx = { roundNumber, roundId, video, wrap, bar, slider,
|
_pcbCtx = { roundNumber, roundId, video, wrap, bar, slider,
|
||||||
onSlide, onSlideCommit, onVideoTime, onKey, onSliderGrab,
|
onSlide, onSlideCommit, onVideoTime, onKey, onSliderGrab,
|
||||||
timeInp, commitTimeInput, onTimeInputKey,
|
timeInp, commitTimeInput, onTimeInputKey,
|
||||||
|
// Exposed so pcbNudge / commitTimeInput (defined
|
||||||
|
// outside this closure) can guarantee the video
|
||||||
|
// stays paused during any scrubber interaction.
|
||||||
|
forcePause,
|
||||||
existingId: existingSingle ? existingSingle.id : null,
|
existingId: existingSingle ? existingSingle.id : null,
|
||||||
existingBlueId: existingBlue ? existingBlue.id : null,
|
existingBlueId: existingBlue ? existingBlue.id : null,
|
||||||
existingRedId: existingRed ? existingRed.id : null };
|
existingRedId: existingRed ? existingRed.id : null };
|
||||||
@ -4991,7 +4998,11 @@
|
|||||||
}
|
}
|
||||||
function pcbNudge(deltaSeconds) {
|
function pcbNudge(deltaSeconds) {
|
||||||
if (!_pcbCtx) return;
|
if (!_pcbCtx) return;
|
||||||
const { video, slider } = _pcbCtx;
|
const { video, slider, forcePause } = _pcbCtx;
|
||||||
|
// Any scrubber interaction must freeze the frame — the user is
|
||||||
|
// trying to land on a precise moment. Playback resumes only when
|
||||||
|
// they explicitly press the play button.
|
||||||
|
if (forcePause) forcePause();
|
||||||
// Direction only — nudge is always ±1 frame, at every zoom level
|
// Direction only — nudge is always ±1 frame, at every zoom level
|
||||||
const dir = deltaSeconds >= 0 ? 1 : -1;
|
const dir = deltaSeconds >= 0 ? 1 : -1;
|
||||||
const step = FRAME_STEP() * dir;
|
const step = FRAME_STEP() * dir;
|
||||||
@ -5570,6 +5581,10 @@
|
|||||||
sliderS, sliderE, fillEl,
|
sliderS, sliderE, fillEl,
|
||||||
onSlideStart, onSlideEnd, onSlideCommit, onVideoTime, onKey,
|
onSlideStart, onSlideEnd, onSlideCommit, onVideoTime, onKey,
|
||||||
onSliderGrabR,
|
onSliderGrabR,
|
||||||
|
// Exposed for rcbNudge / commitStart / commitEnd so
|
||||||
|
// every scrubber interaction freezes the frame — only
|
||||||
|
// an explicit play press should resume playback.
|
||||||
|
forcePause: forcePauseR,
|
||||||
startInp, endInp, commitStart, commitEnd,
|
startInp, endInp, commitStart, commitEnd,
|
||||||
noteInp, previewFn, overlay,
|
noteInp, previewFn, overlay,
|
||||||
// Seed from existing position when editing, else default
|
// Seed from existing position when editing, else default
|
||||||
@ -5686,6 +5701,9 @@
|
|||||||
const val = document.getElementById(which === 'end' ? 'rcbEnd' : 'rcbStart').value;
|
const val = document.getElementById(which === 'end' ? 'rcbEnd' : 'rcbStart').value;
|
||||||
const secs = parsePcbTimeInput(val);
|
const secs = parsePcbTimeInput(val);
|
||||||
if (secs !== null && _rcbCtx && _rcbCtx.video) {
|
if (secs !== null && _rcbCtx && _rcbCtx.video) {
|
||||||
|
// Switching the active slot re-scrubs the video — pause it
|
||||||
|
// so the user doesn't unexpectedly resume playback.
|
||||||
|
if (_rcbCtx.forcePause) _rcbCtx.forcePause();
|
||||||
try { _rcbCtx.video.currentTime = secs; } catch (_) {}
|
try { _rcbCtx.video.currentTime = secs; } catch (_) {}
|
||||||
// Keep dual sliders in sync with the input value
|
// Keep dual sliders in sync with the input value
|
||||||
if (_rcbCtx.sliderS && which === 'start') _rcbCtx.sliderS.value = secs;
|
if (_rcbCtx.sliderS && which === 'start') _rcbCtx.sliderS.value = secs;
|
||||||
@ -5711,6 +5729,9 @@
|
|||||||
inp.value = toMinuteSecondClock(_rcbCtx.video.currentTime || 0);
|
inp.value = toMinuteSecondClock(_rcbCtx.video.currentTime || 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Typing a timestamp is a scrubber interaction — freeze the
|
||||||
|
// frame. Playback resumes only on an explicit play press.
|
||||||
|
if (_rcbCtx.forcePause) _rcbCtx.forcePause();
|
||||||
const dur = Number(_rcbCtx.video.duration) || 0;
|
const dur = Number(_rcbCtx.video.duration) || 0;
|
||||||
const clamped = Math.max(0, dur > 0 ? Math.min(dur, secs) : secs);
|
const clamped = Math.max(0, dur > 0 ? Math.min(dur, secs) : secs);
|
||||||
inp.value = toMinuteSecondClock(clamped);
|
inp.value = toMinuteSecondClock(clamped);
|
||||||
@ -5787,7 +5808,10 @@
|
|||||||
// Nudge the LAST-TOUCHED scrubber (start or end) by ±1 frame.
|
// Nudge the LAST-TOUCHED scrubber (start or end) by ±1 frame.
|
||||||
function rcbNudge(deltaSeconds) {
|
function rcbNudge(deltaSeconds) {
|
||||||
if (!_rcbCtx) return;
|
if (!_rcbCtx) return;
|
||||||
const { video, sliderS, sliderE, startInp, endInp } = _rcbCtx;
|
const { video, sliderS, sliderE, startInp, endInp, forcePause } = _rcbCtx;
|
||||||
|
// Freeze the frame — scrubber steps are for precise landing,
|
||||||
|
// not incidental resume-playback triggers.
|
||||||
|
if (forcePause) forcePause();
|
||||||
const which = (typeof _rcbCtxLastTouched === 'function' ? _rcbCtxLastTouched() : 'start');
|
const which = (typeof _rcbCtxLastTouched === 'function' ? _rcbCtxLastTouched() : 'start');
|
||||||
const target = which === 'end' ? sliderE : sliderS;
|
const target = which === 'end' ? sliderE : sliderS;
|
||||||
const otherVal = Number(which === 'end' ? sliderS.value : sliderE.value) || 0;
|
const otherVal = Number(which === 'end' ? sliderS.value : sliderE.value) || 0;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user