diff --git a/resources/views/videos/types/match.blade.php b/resources/views/videos/types/match.blade.php index bb2e305..9d4b4b8 100644 --- a/resources/views/videos/types/match.blade.php +++ b/resources/views/videos/types/match.blade.php @@ -4756,6 +4756,9 @@ timeInp.value = toMinuteSecondClock(video.currentTime || 0); 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 clamped = Math.max(0, dur > 0 ? Math.min(dur, secs) : secs); try { video.currentTime = clamped; } catch (_) {} @@ -4808,6 +4811,10 @@ _pcbCtx = { roundNumber, roundId, video, wrap, bar, slider, onSlide, onSlideCommit, onVideoTime, onKey, onSliderGrab, 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, existingBlueId: existingBlue ? existingBlue.id : null, existingRedId: existingRed ? existingRed.id : null }; @@ -4991,7 +4998,11 @@ } function pcbNudge(deltaSeconds) { 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 const dir = deltaSeconds >= 0 ? 1 : -1; const step = FRAME_STEP() * dir; @@ -5570,6 +5581,10 @@ sliderS, sliderE, fillEl, onSlideStart, onSlideEnd, onSlideCommit, onVideoTime, onKey, 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, noteInp, previewFn, overlay, // Seed from existing position when editing, else default @@ -5686,6 +5701,9 @@ const val = document.getElementById(which === 'end' ? 'rcbEnd' : 'rcbStart').value; const secs = parsePcbTimeInput(val); 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 (_) {} // Keep dual sliders in sync with the input value if (_rcbCtx.sliderS && which === 'start') _rcbCtx.sliderS.value = secs; @@ -5711,6 +5729,9 @@ inp.value = toMinuteSecondClock(_rcbCtx.video.currentTime || 0); 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 clamped = Math.max(0, dur > 0 ? Math.min(dur, secs) : secs); inp.value = toMinuteSecondClock(clamped); @@ -5787,7 +5808,10 @@ // Nudge the LAST-TOUCHED scrubber (start or end) by ±1 frame. function rcbNudge(deltaSeconds) { 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 target = which === 'end' ? sliderE : sliderS; const otherVal = Number(which === 'end' ? sliderS.value : sliderE.value) || 0;