Summary
media-time-range never moves backward when the seek is smaller than 3% of the media duration. The bar freezes at the pre-seek position and only starts moving again once playback catches up with it, so the freeze lasts exactly as long as the amount that was seeked back.
Steps to reproduce
- Play a long VOD (mine: 1:38:30,
duration = 5909.6).
- While playing, seek backward by 20 s (arrow-key hotkey,
media-seek-backward-button, or a mediaseekrequest with detail = currentTime - 20).
- Watch
media-time-range.
Expected: the bar jumps back to the new position.
Actual: the bar stays where it was for the next 20 s of playback, then resumes normally.
Measured on the page (Chrome 150, VOD via hls.js 1.6, media-chrome 4.19.2), seek from 2301 s to 2281 s:
|
value |
mediacurrenttime attribute |
2281.01 → 2281.22 → 2281.49 … (correct) |
timeRange.range.valueAsNumber |
0.38936 (= 2301 s) for 20 s, unchanged |
So state propagation is fine — only the range animation refuses to move.
Cause
dist/utils/range-animation.js:
update({ start, duration, playbackRate }) {
const increase = start - this.#range.valueAsNumber;
const durationDelta = Math.abs(duration - this.duration);
if (increase > 0 || increase < -0.03 || durationDelta >= 0.5) {
this.callback(start);
}
...
}
start is normalized to 0..1, so the -0.03 guard means 3% of the media duration, not a fixed amount of time:
| duration |
backward seeks that never move the bar |
| 5 min |
< 9 s |
| 1 h 38 min |
< 177 s |
| 3 h |
< 324 s |
When the callback is skipped, #animate takes the increase <= 0 branch and decays #lastRangeIncrease by 0.995 per frame, so the bar visually stands still instead of following the media.
Suggested fix
The guard looks intended to absorb the sub-second lag between timeupdate values and the position the animation has already advanced to — that intent is time-based, but the constant is expressed in fractions of duration. Something like:
if (increase > 0 || increase < -0.5 / duration || durationDelta >= 0.5) {
keeps the anti-jitter behaviour (0.5 s of tolerance) without scaling it with the media length.
Happy to send a PR if you agree with the approach.
Possibly related, separate
For seeks into an unbuffered range, the bar and media-time-display also stay at the old position for the whole buffering stall (measured: ~0.8 s on a fast connection, arbitrarily long on a slow one). mediaCurrentTime in the state mediator only updates on timeupdate/loadedmetadata, and the browser does not fire timeupdate until the seek completes, while MEDIA_SEEK_REQUEST in request-map.js sets media.currentTime without returning an optimistic state delta. Returning { mediaCurrentTime: value } there would make the UI reflect the requested position immediately. Let me know if you'd like that as its own issue.
Environment
- media-chrome 4.19.2 (code is identical in 4.19.3-canary.1)
- Chrome 150, macOS
- hls.js 1.6.15, VOD stream
I searched open and closed issues for this and did not find a duplicate — apologies if I missed one.
Summary
media-time-rangenever moves backward when the seek is smaller than 3% of the media duration. The bar freezes at the pre-seek position and only starts moving again once playback catches up with it, so the freeze lasts exactly as long as the amount that was seeked back.Steps to reproduce
duration = 5909.6).media-seek-backward-button, or amediaseekrequestwithdetail = currentTime - 20).media-time-range.Expected: the bar jumps back to the new position.
Actual: the bar stays where it was for the next 20 s of playback, then resumes normally.
Measured on the page (Chrome 150, VOD via hls.js 1.6,
media-chrome4.19.2), seek from 2301 s to 2281 s:mediacurrenttimeattributetimeRange.range.valueAsNumberSo state propagation is fine — only the range animation refuses to move.
Cause
dist/utils/range-animation.js:startis normalized to0..1, so the-0.03guard means 3% of the media duration, not a fixed amount of time:When the callback is skipped,
#animatetakes theincrease <= 0branch and decays#lastRangeIncreaseby0.995per frame, so the bar visually stands still instead of following the media.Suggested fix
The guard looks intended to absorb the sub-second lag between
timeupdatevalues and the position the animation has already advanced to — that intent is time-based, but the constant is expressed in fractions of duration. Something like:keeps the anti-jitter behaviour (0.5 s of tolerance) without scaling it with the media length.
Happy to send a PR if you agree with the approach.
Possibly related, separate
For seeks into an unbuffered range, the bar and
media-time-displayalso stay at the old position for the whole buffering stall (measured: ~0.8 s on a fast connection, arbitrarily long on a slow one).mediaCurrentTimein the state mediator only updates ontimeupdate/loadedmetadata, and the browser does not firetimeupdateuntil the seek completes, whileMEDIA_SEEK_REQUESTinrequest-map.jssetsmedia.currentTimewithout returning an optimistic state delta. Returning{ mediaCurrentTime: value }there would make the UI reflect the requested position immediately. Let me know if you'd like that as its own issue.Environment
I searched open and closed issues for this and did not find a duplicate — apologies if I missed one.