Skip to content

Commit 88aead4

Browse files
authored
Merge branch 'main' into type-enhancements
2 parents 4e5dbb1 + c7298d4 commit 88aead4

20 files changed

Lines changed: 469 additions & 43 deletions

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
<a name="8.23.0"></a>
2+
# [8.23.0](https://github.com/videojs/video.js/compare/v8.22.0...v8.23.0) (2025-03-11)
3+
4+
### Features
5+
6+
* Improve SmartTV scrubbing behavior ([#8988](https://github.com/videojs/video.js/issues/8988)) ([77c99d2](https://github.com/videojs/video.js/commit/77c99d2))
7+
* toJSON methods for text track serialization ([#8998](https://github.com/videojs/video.js/issues/8998)) ([1c282a3](https://github.com/videojs/video.js/commit/1c282a3))
8+
9+
### Bug Fixes
10+
11+
* Improve getFileExtension() readability and handle leading dot extensions. ([#8980](https://github.com/videojs/video.js/issues/8980)) ([5b9795d](https://github.com/videojs/video.js/commit/5b9795d))
12+
13+
### Code Refactoring
14+
15+
* **types:** track and track list types generation ([#8978](https://github.com/videojs/video.js/issues/8978)) ([8842d37](https://github.com/videojs/video.js/commit/8842d37)), closes [#8486](https://github.com/videojs/video.js/issues/8486) [/github.com/videojs/video.js/pull/8486/files#r1635782771](https://github.com//github.com/videojs/video.js/pull/8486/files/issues/r1635782771)
16+
117
<a name="8.22.0"></a>
218
# [8.22.0](https://github.com/videojs/video.js/compare/v8.21.1...v8.22.0) (2025-02-05)
319

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ Video.js was started in the middle of 2010 and is now used on over ~~50,000~~ ~~
2222
Thanks to the awesome folks over at [Fastly][fastly], there's a free, CDN hosted version of Video.js that anyone can use. Add these tags to your document's `<head>`:
2323

2424
```html
25-
<link href="//vjs.zencdn.net/8.22.0/video-js.min.css" rel="stylesheet">
26-
<script src="//vjs.zencdn.net/8.22.0/video.min.js"></script>
25+
<link href="//vjs.zencdn.net/8.23.0/video-js.min.css" rel="stylesheet">
26+
<script src="//vjs.zencdn.net/8.23.0/video.min.js"></script>
2727
```
2828

2929
Alternatively, you can include Video.js by getting it from [npm](https://videojs.com/getting-started/#install-via-npm), downloading it from [GitHub releases](https://github.com/videojs/video.js/releases) or by including it via [unpkg](https://unpkg.com) or another JavaScript CDN, like CDNjs.
@@ -34,12 +34,12 @@ Alternatively, you can include Video.js by getting it from [npm](https://videojs
3434
<script src="https://unpkg.com/video.js/dist/video.min.js"></script>
3535

3636
<!-- unpkg : use a specific version of Video.js (change the version numbers as necessary) -->
37-
<link href="https://unpkg.com/video.js@8.22.0/dist/video-js.min.css" rel="stylesheet">
38-
<script src="https://unpkg.com/video.js@8.22.0/dist/video.min.js"></script>
37+
<link href="https://unpkg.com/video.js@8.23.0/dist/video-js.min.css" rel="stylesheet">
38+
<script src="https://unpkg.com/video.js@8.23.0/dist/video.min.js"></script>
3939

4040
<!-- cdnjs : use a specific version of Video.js (change the version numbers as necessary) -->
41-
<link href="https://cdnjs.cloudflare.com/ajax/libs/video.js/8.22.0/video-js.min.css" rel="stylesheet">
42-
<script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/8.22.0/video.min.js"></script>
41+
<link href="https://cdnjs.cloudflare.com/ajax/libs/video.js/8.23.0/video-js.min.css" rel="stylesheet">
42+
<script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/8.23.0/video.min.js"></script>
4343
```
4444

4545
Next, using Video.js is as simple as creating a `<video>` element, but with an additional `data-setup` attribute. At a minimum, this attribute must have a value of `'{}'`, but it can include any Video.js [options][options] - just make sure it contains valid JSON!

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "video.js",
33
"description": "An HTML5 video player that supports HLS and DASH with a common API and skin.",
4-
"version": "8.22.0",
4+
"version": "8.23.0",
55
"main": "./dist/video.cjs.js",
66
"module": "./dist/video.es.js",
77
"style": "./dist/video-js.css",

src/css/components/_volume.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@
160160
}
161161
}
162162

163-
// here
164163
// Update placement of circle icon when using SVG icons
165164
.vjs-slider-horizontal .vjs-volume-level .vjs-svg-icon {
166165
right: -0.3em;

src/js/control-bar/progress-control/play-progress-bar.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,25 @@ class PlayProgressBar extends Component {
5656
* @param {number} seekBarPoint
5757
* A number from 0 to 1, representing a horizontal reference point
5858
* from the left edge of the {@link SeekBar}
59+
*
60+
* @param {Event} [event]
61+
* The `timeupdate` event that caused this function to run.
5962
*/
60-
update(seekBarRect, seekBarPoint) {
63+
update(seekBarRect, seekBarPoint, event) {
6164
const timeTooltip = this.getChild('timeTooltip');
6265

6366
if (!timeTooltip) {
6467
return;
6568
}
6669

67-
const time = (this.player_.scrubbing()) ?
68-
this.player_.getCache().currentTime :
69-
this.player_.currentTime();
70+
// Combined logic: if an event with a valid pendingSeekTime getter exists, use it.
71+
const time = (event &&
72+
event.target &&
73+
typeof event.target.pendingSeekTime === 'function') ?
74+
event.target.pendingSeekTime() :
75+
(this.player_.scrubbing() ?
76+
this.player_.getCache().currentTime :
77+
this.player_.currentTime());
7078

7179
timeTooltip.updateTime(seekBarRect, seekBarPoint, time);
7280
}

src/js/control-bar/progress-control/seek-bar.js

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,18 @@ class SeekBar extends Slider {
4444
// Avoid mutating the prototype's `children` array by creating a copy
4545
options.children = [...options.children];
4646

47-
const shouldDisableSeekWhileScrubbingOnMobile = player.options_.disableSeekWhileScrubbingOnMobile && (IS_IOS || IS_ANDROID);
47+
const shouldDisableSeekWhileScrubbing =
48+
(player.options_.disableSeekWhileScrubbingOnMobile && (IS_IOS || IS_ANDROID)) ||
49+
(player.options_.disableSeekWhileScrubbingOnSTV);
4850

4951
// Add the TimeTooltip as a child if we are on desktop, or on mobile with `disableSeekWhileScrubbingOnMobile: true`
50-
if ((!IS_IOS && !IS_ANDROID) || shouldDisableSeekWhileScrubbingOnMobile) {
52+
if ((!IS_IOS && !IS_ANDROID) || shouldDisableSeekWhileScrubbing) {
5153
options.children.splice(1, 0, 'mouseTimeDisplay');
5254
}
5355

5456
super(player, options);
5557

56-
this.shouldDisableSeekWhileScrubbingOnMobile_ = shouldDisableSeekWhileScrubbingOnMobile;
58+
this.shouldDisableSeekWhileScrubbing_ = shouldDisableSeekWhileScrubbing;
5759
this.pendingSeekTime_ = null;
5860

5961
this.setEventHandlers_();
@@ -196,7 +198,7 @@ class SeekBar extends Slider {
196198

197199
// update the progress bar time tooltip with the current time
198200
if (this.bar) {
199-
this.bar.update(Dom.getBoundingClientRect(this.el()), this.getProgress());
201+
this.bar.update(Dom.getBoundingClientRect(this.el()), this.getProgress(), event);
200202
}
201203
});
202204

@@ -233,6 +235,26 @@ class SeekBar extends Slider {
233235
this.player_.currentTime();
234236
}
235237

238+
/**
239+
* Getter and setter for pendingSeekTime.
240+
* Ensures the value is clamped between 0 and duration.
241+
*
242+
* @param {number|null} [time] - Optional. The new pending seek time, can be a number or null.
243+
* @return {number|null} - The current pending seek time.
244+
*/
245+
pendingSeekTime(time) {
246+
if (time !== undefined) {
247+
if (time !== null) {
248+
const duration = this.player_.duration();
249+
250+
this.pendingSeekTime_ = Math.max(0, Math.min(time, duration));
251+
} else {
252+
this.pendingSeekTime_ = null;
253+
}
254+
}
255+
return this.pendingSeekTime_;
256+
}
257+
236258
/**
237259
* Get the percentage of media played so far.
238260
*
@@ -242,8 +264,8 @@ class SeekBar extends Slider {
242264
getPercent() {
243265
// If we have a pending seek time, we are scrubbing on mobile and should set the slider percent
244266
// to reflect the current scrub location.
245-
if (this.pendingSeekTime_) {
246-
return this.pendingSeekTime_ / this.player_.duration();
267+
if (this.pendingSeekTime() !== null) {
268+
return this.pendingSeekTime() / this.player_.duration();
247269
}
248270

249271
const currentTime = this.getCurrentTime_();
@@ -284,7 +306,7 @@ class SeekBar extends Slider {
284306

285307
// Don't pause if we are on mobile and `disableSeekWhileScrubbingOnMobile: true`.
286308
// In that case, playback should continue while the player scrubs to a new location.
287-
if (!this.shouldDisableSeekWhileScrubbingOnMobile_) {
309+
if (!this.shouldDisableSeekWhileScrubbing_) {
288310
this.player_.pause();
289311
}
290312

@@ -351,8 +373,8 @@ class SeekBar extends Slider {
351373
}
352374

353375
// if on mobile and `disableSeekWhileScrubbingOnMobile: true`, keep track of the desired seek point but we won't initiate the seek until 'touchend'
354-
if (this.shouldDisableSeekWhileScrubbingOnMobile_) {
355-
this.pendingSeekTime_ = newTime;
376+
if (this.shouldDisableSeekWhileScrubbing_) {
377+
this.pendingSeekTime(newTime);
356378
} else {
357379
this.userSeek_(newTime);
358380
}
@@ -402,10 +424,10 @@ class SeekBar extends Slider {
402424
this.player_.scrubbing(false);
403425

404426
// If we have a pending seek time, then we have finished scrubbing on mobile and should initiate a seek.
405-
if (this.pendingSeekTime_) {
406-
this.userSeek_(this.pendingSeekTime_);
427+
if (this.pendingSeekTime() !== null) {
428+
this.userSeek_(this.pendingSeekTime());
407429

408-
this.pendingSeekTime_ = null;
430+
this.pendingSeekTime(null);
409431
}
410432

411433
/**
@@ -425,18 +447,46 @@ class SeekBar extends Slider {
425447
}
426448
}
427449

450+
/**
451+
* Handles pending seek time when `disableSeekWhileScrubbingOnSTV` is enabled.
452+
*
453+
* @param {number} stepAmount - The number of seconds to step (positive for forward, negative for backward).
454+
*/
455+
handlePendingSeek_(stepAmount) {
456+
if (!this.player_.paused()) {
457+
this.player_.pause();
458+
}
459+
460+
const currentPos = this.pendingSeekTime() !== null ?
461+
this.pendingSeekTime() :
462+
this.player_.currentTime();
463+
464+
this.pendingSeekTime(currentPos + stepAmount);
465+
this.player_.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
466+
}
467+
428468
/**
429469
* Move more quickly fast forward for keyboard-only users
430470
*/
431471
stepForward() {
432-
this.userSeek_(this.player_.currentTime() + this.options().stepSeconds);
472+
// if `disableSeekWhileScrubbingOnSTV: true`, keep track of the desired seek point but we won't initiate the seek
473+
if (this.shouldDisableSeekWhileScrubbing_) {
474+
this.handlePendingSeek_(this.options().stepSeconds);
475+
} else {
476+
this.userSeek_(this.player_.currentTime() + this.options().stepSeconds);
477+
}
433478
}
434479

435480
/**
436481
* Move more quickly rewind for keyboard-only users
437482
*/
438483
stepBack() {
439-
this.userSeek_(this.player_.currentTime() - this.options().stepSeconds);
484+
// if `disableSeekWhileScrubbingOnSTV: true`, keep track of the desired seek point but we won't initiate the seek
485+
if (this.shouldDisableSeekWhileScrubbing_) {
486+
this.handlePendingSeek_(-this.options().stepSeconds);
487+
} else {
488+
this.userSeek_(this.player_.currentTime() - this.options().stepSeconds);
489+
}
440490
}
441491

442492
/**
@@ -448,6 +498,10 @@ class SeekBar extends Slider {
448498
*
449499
*/
450500
handleAction(event) {
501+
if (this.pendingSeekTime() !== null) {
502+
this.userSeek_(this.pendingSeekTime());
503+
this.pendingSeekTime(null);
504+
}
451505
if (this.player_.paused()) {
452506
this.player_.play();
453507
} else {

src/js/control-bar/time-controls/current-time-display.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class CurrentTimeDisplay extends TimeDisplay {
3535

3636
if (this.player_.ended()) {
3737
time = this.player_.duration();
38+
} else if (event && event.target && typeof event.target.pendingSeekTime === 'function') {
39+
time = event.target.pendingSeekTime();
3840
} else {
3941
time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
4042
}

src/js/player.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5718,7 +5718,8 @@ Player.prototype.options_ = {
57185718
},
57195719
// Default smooth seeking to false
57205720
enableSmoothSeeking: false,
5721-
disableSeekWhileScrubbingOnMobile: false
5721+
disableSeekWhileScrubbingOnMobile: false,
5722+
disableSeekWhileScrubbingOnSTV: false
57225723
};
57235724

57245725
TECH_EVENTS_RETRIGGER.forEach(function(event) {

src/js/slider/slider.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,10 @@ class Slider extends Component {
325325
event.stopPropagation();
326326
this.stepForward();
327327
} else {
328+
if (this.pendingSeekTime()) {
329+
this.pendingSeekTime(null);
330+
this.userSeek_(this.player_.currentTime());
331+
}
328332
super.handleKeyDown(event);
329333
}
330334

0 commit comments

Comments
 (0)