j1-template 2024.0.2 → 2024.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/assets/themes/j1/core/js/template.js +0 -12
- data/assets/themes/j1/core/js/template.min.js.map +1 -1
- data/assets/themes/j1/modules/iframeResizer/README.md +105 -105
- data/assets/themes/j1/modules/lightGallery/js/plugins/lg-video.js +30 -5
- data/assets/themes/j1/modules/videojs/assets/material-icons.woff +0 -0
- data/assets/themes/j1/modules/videojs/assets/material-icons.woff2 +0 -0
- data/assets/themes/j1/modules/videojs/css/plugins/zoom.css +96 -0
- data/assets/themes/j1/modules/videojs/css/plugins/zoom.min.css +21 -0
- data/assets/themes/j1/modules/videojs/css/themes/city.css +1 -1
- data/assets/themes/j1/modules/videojs/css/themes/city.min.css +1 -1
- data/assets/themes/j1/modules/videojs/css/themes/fantasy.css +1 -1
- data/assets/themes/j1/modules/videojs/css/themes/fantasy.min.css +1 -1
- data/assets/themes/j1/modules/videojs/css/themes/forest.css +1 -1
- data/assets/themes/j1/modules/videojs/css/themes/forest.min.css +1 -1
- data/assets/themes/j1/modules/videojs/css/themes/sea.css +1 -1
- data/assets/themes/j1/modules/videojs/css/themes/sea.min.css +1 -1
- data/assets/themes/j1/modules/videojs/css/themes/uno.css +16 -18
- data/assets/themes/j1/modules/videojs/css/themes/uno.min.css +3 -2
- data/assets/themes/j1/modules/videojs/css/{video-js.css → video.css} +9 -7
- data/assets/themes/j1/modules/videojs/css/{video-js.min.css → video.min.css} +2 -2
- data/assets/themes/j1/modules/videojs/js/plugins/zoom.js +377 -0
- data/assets/themes/j1/modules/videojs/js/plugins/zoom.min.js +21 -0
- data/assets/themes/j1/modules/videojs/js/video.js +641 -71
- data/assets/themes/j1/modules/videojs/js/video.min.js +10 -9
- data/lib/j1/version.rb +1 -1
- data/lib/starter_web/README.md +5 -5
- data/lib/starter_web/_config.yml +1 -1
- data/lib/starter_web/_data/modules/gallery.yml +7 -2
- data/lib/starter_web/_data/resources.yml +4 -2
- data/lib/starter_web/_data/templates/feed.xml +1 -1
- data/lib/starter_web/_plugins/asciidoctor/videojs-block.rb +3 -2
- data/lib/starter_web/_plugins/index/lunr.rb +3 -1
- data/lib/starter_web/assets/videos/gallery/video_james_carpool_caraoke.1920.jpg +0 -0
- data/lib/starter_web/package.json +1 -1
- data/lib/starter_web/pages/public/tools/previewer/preview_docsearch.adoc +92 -92
- data/lib/starter_web/pages/public/tools/previewer/preview_iframer.adoc +106 -106
- metadata +11 -4
@@ -1,6 +1,6 @@
|
|
1
1
|
/**
|
2
2
|
* @license
|
3
|
-
* Video.js 8.6.
|
3
|
+
* Video.js 8.6.1 <http://videojs.com/>
|
4
4
|
* Copyright Brightcove, Inc. <https://www.brightcove.com/>
|
5
5
|
* Available under Apache License Version 2.0
|
6
6
|
* <https://github.com/videojs/video.js/blob/main/LICENSE>
|
@@ -8,7 +8,7 @@
|
|
8
8
|
* Includes vtt.js <https://github.com/mozilla/vtt.js>
|
9
9
|
* Available under Apache License Version 2.0
|
10
10
|
* <https://github.com/mozilla/vtt.js/blob/main/LICENSE>
|
11
|
-
|
11
|
+
*/
|
12
12
|
|
13
13
|
(function (global, factory) {
|
14
14
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
@@ -16,7 +16,7 @@
|
|
16
16
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.videojs = factory());
|
17
17
|
})(this, (function () { 'use strict';
|
18
18
|
|
19
|
-
var version$5 = "8.6.
|
19
|
+
var version$5 = "8.6.1";
|
20
20
|
|
21
21
|
/**
|
22
22
|
* An Object that contains lifecycle hooks as keys which point to an array
|
@@ -1996,7 +1996,7 @@
|
|
1996
1996
|
* @param {Element|Object} elem
|
1997
1997
|
* Element or object to bind listeners to
|
1998
1998
|
*
|
1999
|
-
* @param {string}
|
1999
|
+
* @param {string[]} types
|
2000
2000
|
* Type of event to bind to.
|
2001
2001
|
*
|
2002
2002
|
* @param {Function} callback
|
@@ -2719,7 +2719,7 @@
|
|
2719
2719
|
/**
|
2720
2720
|
* All event listeners should follow the following format.
|
2721
2721
|
*
|
2722
|
-
* @callback
|
2722
|
+
* @callback EventListener
|
2723
2723
|
* @this {EventTarget}
|
2724
2724
|
*
|
2725
2725
|
* @param {Event} event
|
@@ -2736,7 +2736,7 @@
|
|
2736
2736
|
* will have extra functionality. See that function for more information.
|
2737
2737
|
*
|
2738
2738
|
* @property EventTarget.prototype.allowedEvents_
|
2739
|
-
* @
|
2739
|
+
* @protected
|
2740
2740
|
*/
|
2741
2741
|
EventTarget$2.prototype.allowedEvents_ = {};
|
2742
2742
|
|
@@ -4169,7 +4169,6 @@
|
|
4169
4169
|
/**
|
4170
4170
|
* Add a child `Component` inside the current `Component`.
|
4171
4171
|
*
|
4172
|
-
*
|
4173
4172
|
* @param {string|Component} child
|
4174
4173
|
* The name or instance of a child to add.
|
4175
4174
|
*
|
@@ -4180,6 +4179,7 @@
|
|
4180
4179
|
* @param {number} [index=this.children_.length]
|
4181
4180
|
* The index to attempt to add a child into.
|
4182
4181
|
*
|
4182
|
+
*
|
4183
4183
|
* @return {Component}
|
4184
4184
|
* The `Component` that gets added as a child. When using a string the
|
4185
4185
|
* `Component` will get created by this process.
|
@@ -4634,9 +4634,8 @@
|
|
4634
4634
|
* @param {boolean} [skipListeners]
|
4635
4635
|
* Skip the componentresize event trigger
|
4636
4636
|
*
|
4637
|
-
* @return {number|
|
4638
|
-
* The width when getting, zero if there is no width
|
4639
|
-
* postpixed with '%' or 'px'.
|
4637
|
+
* @return {number|undefined}
|
4638
|
+
* The width when getting, zero if there is no width
|
4640
4639
|
*/
|
4641
4640
|
width(num, skipListeners) {
|
4642
4641
|
return this.dimension('width', num, skipListeners);
|
@@ -4652,9 +4651,8 @@
|
|
4652
4651
|
* @param {boolean} [skipListeners]
|
4653
4652
|
* Skip the componentresize event trigger
|
4654
4653
|
*
|
4655
|
-
* @return {number|
|
4656
|
-
* The
|
4657
|
-
* postpixed with '%' or 'px'.
|
4654
|
+
* @return {number|undefined}
|
4655
|
+
* The height when getting, zero if there is no height
|
4658
4656
|
*/
|
4659
4657
|
height(num, skipListeners) {
|
4660
4658
|
return this.dimension('height', num, skipListeners);
|
@@ -4700,7 +4698,7 @@
|
|
4700
4698
|
* @param {boolean} [skipListeners]
|
4701
4699
|
* Skip componentresize event trigger
|
4702
4700
|
*
|
4703
|
-
* @return {number}
|
4701
|
+
* @return {number|undefined}
|
4704
4702
|
* The dimension when getting or 0 if unset
|
4705
4703
|
*/
|
4706
4704
|
dimension(widthOrHeight, num, skipListeners) {
|
@@ -4875,7 +4873,7 @@
|
|
4875
4873
|
* delegates to `handleKeyDown`. This means anyone calling `handleKeyPress`
|
4876
4874
|
* will not see their method calls stop working.
|
4877
4875
|
*
|
4878
|
-
* @param {
|
4876
|
+
* @param {KeyboardEvent} event
|
4879
4877
|
* The event that caused this function to be called.
|
4880
4878
|
*/
|
4881
4879
|
handleKeyPress(event) {
|
@@ -4887,7 +4885,7 @@
|
|
4887
4885
|
* support toggling the controls through a tap on the video. They get enabled
|
4888
4886
|
* because every sub-component would have extra overhead otherwise.
|
4889
4887
|
*
|
4890
|
-
* @
|
4888
|
+
* @protected
|
4891
4889
|
* @fires Component#tap
|
4892
4890
|
* @listens Component#touchstart
|
4893
4891
|
* @listens Component#touchmove
|
@@ -6524,7 +6522,7 @@
|
|
6524
6522
|
* Events that can be called with on + eventName. See {@link EventHandler}.
|
6525
6523
|
*
|
6526
6524
|
* @property {Object} TrackList#allowedEvents_
|
6527
|
-
* @
|
6525
|
+
* @protected
|
6528
6526
|
*/
|
6529
6527
|
TrackList.prototype.allowedEvents_ = {
|
6530
6528
|
change: 'change',
|
@@ -6574,7 +6572,7 @@
|
|
6574
6572
|
/**
|
6575
6573
|
* Create an instance of this class.
|
6576
6574
|
*
|
6577
|
-
* @param {
|
6575
|
+
* @param { import('./audio-track').default[] } [tracks=[]]
|
6578
6576
|
* A list of `AudioTrack` to instantiate the list with.
|
6579
6577
|
*/
|
6580
6578
|
constructor(tracks = []) {
|
@@ -8012,7 +8010,9 @@
|
|
8012
8010
|
*/
|
8013
8011
|
addCue(originalCue) {
|
8014
8012
|
let cue = originalCue;
|
8015
|
-
|
8013
|
+
|
8014
|
+
// Testing if the cue is a VTTCue in a way that survives minification
|
8015
|
+
if (!('getCueAsHTML' in cue)) {
|
8016
8016
|
cue = new window.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
|
8017
8017
|
for (const prop in originalCue) {
|
8018
8018
|
if (!(prop in cue)) {
|
@@ -8055,6 +8055,7 @@
|
|
8055
8055
|
|
8056
8056
|
/**
|
8057
8057
|
* cuechange - One or more cues in the track have become active or stopped being active.
|
8058
|
+
* @protected
|
8058
8059
|
*/
|
8059
8060
|
TextTrack.prototype.allowedEvents_ = {
|
8060
8061
|
cuechange: 'cuechange'
|
@@ -8313,6 +8314,10 @@
|
|
8313
8314
|
});
|
8314
8315
|
}
|
8315
8316
|
}
|
8317
|
+
|
8318
|
+
/**
|
8319
|
+
* @protected
|
8320
|
+
*/
|
8316
8321
|
HTMLTrackElement.prototype.allowedEvents_ = {
|
8317
8322
|
load: 'load'
|
8318
8323
|
};
|
@@ -10110,7 +10115,7 @@
|
|
10110
10115
|
* * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
|
10111
10116
|
* `var SourceString = 'http://example.com/some-video.mp4';`
|
10112
10117
|
*
|
10113
|
-
* @typedef {Object|string}
|
10118
|
+
* @typedef {Object|string} SourceObject
|
10114
10119
|
*
|
10115
10120
|
* @property {string} src
|
10116
10121
|
* The url to the source
|
@@ -10546,7 +10551,7 @@
|
|
10546
10551
|
* > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
|
10547
10552
|
* It only checks whether the source has played at all or not.
|
10548
10553
|
*
|
10549
|
-
* @return {TimeRange}
|
10554
|
+
* @return { import('../utils/time').TimeRange }
|
10550
10555
|
* - A single time range if this video has played
|
10551
10556
|
* - An empty set of ranges if not.
|
10552
10557
|
*/
|
@@ -11310,7 +11315,7 @@
|
|
11310
11315
|
*
|
11311
11316
|
* TODO: Answer question: should 'probably' be prioritized over 'maybe'
|
11312
11317
|
*
|
11313
|
-
* @param {
|
11318
|
+
* @param {SourceObject} source
|
11314
11319
|
* The source object
|
11315
11320
|
*
|
11316
11321
|
* @param {Object} options
|
@@ -11335,7 +11340,7 @@
|
|
11335
11340
|
/**
|
11336
11341
|
* Check if the tech can support the given source.
|
11337
11342
|
*
|
11338
|
-
* @param {
|
11343
|
+
* @param {SourceObject} srcObj
|
11339
11344
|
* The source object
|
11340
11345
|
*
|
11341
11346
|
* @param {Object} options
|
@@ -11390,7 +11395,7 @@
|
|
11390
11395
|
* and source handlers.
|
11391
11396
|
* Should never be called unless a source handler was found.
|
11392
11397
|
*
|
11393
|
-
* @param {
|
11398
|
+
* @param {SourceObject} source
|
11394
11399
|
* A source object with src and type keys
|
11395
11400
|
*/
|
11396
11401
|
_Tech.prototype.setSource = function (source) {
|
@@ -12169,7 +12174,7 @@
|
|
12169
12174
|
*
|
12170
12175
|
* By default, if the key is Space or Enter, it will trigger a `click` event.
|
12171
12176
|
*
|
12172
|
-
* @param {
|
12177
|
+
* @param {KeyboardEvent} event
|
12173
12178
|
* The `keydown` event that caused this function to be called.
|
12174
12179
|
*
|
12175
12180
|
* @listens keydown
|
@@ -12925,7 +12930,7 @@
|
|
12925
12930
|
* This gets called when a `Button` has focus and `keydown` is triggered via a key
|
12926
12931
|
* press.
|
12927
12932
|
*
|
12928
|
-
* @param {
|
12933
|
+
* @param {KeyboardEvent} event
|
12929
12934
|
* The event that caused this function to get called.
|
12930
12935
|
*
|
12931
12936
|
* @listens keydown
|
@@ -12979,7 +12984,7 @@
|
|
12979
12984
|
* This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
|
12980
12985
|
* for more detailed information on what a click can be.
|
12981
12986
|
*
|
12982
|
-
* @param {KeyboardEvent} event
|
12987
|
+
* @param {KeyboardEvent|MouseEvent|TouchEvent} event
|
12983
12988
|
* The `keydown`, `tap`, or `click` event that caused this function to be
|
12984
12989
|
* called.
|
12985
12990
|
*
|
@@ -12990,7 +12995,7 @@
|
|
12990
12995
|
const playPromise = this.player_.play();
|
12991
12996
|
|
12992
12997
|
// exit early if clicked via the mouse
|
12993
|
-
if (this.mouseused_ &&
|
12998
|
+
if (this.mouseused_ && 'clientX' in event && 'clientY' in event) {
|
12994
12999
|
silencePromise(playPromise);
|
12995
13000
|
if (this.player_.tech(true)) {
|
12996
13001
|
this.player_.tech(true).focus();
|
@@ -13010,10 +13015,29 @@
|
|
13010
13015
|
this.setTimeout(playFocus, 1);
|
13011
13016
|
}
|
13012
13017
|
}
|
13018
|
+
|
13019
|
+
/**
|
13020
|
+
* Event handler that is called when a `BigPlayButton` receives a
|
13021
|
+
* `keydown` event.
|
13022
|
+
*
|
13023
|
+
* @param {KeyboardEvent} event
|
13024
|
+
* The `keydown` event that caused this function to be called.
|
13025
|
+
*
|
13026
|
+
* @listens keydown
|
13027
|
+
*/
|
13013
13028
|
handleKeyDown(event) {
|
13014
13029
|
this.mouseused_ = false;
|
13015
13030
|
super.handleKeyDown(event);
|
13016
13031
|
}
|
13032
|
+
|
13033
|
+
/**
|
13034
|
+
* Handle `mousedown` events on the `BigPlayButton`.
|
13035
|
+
*
|
13036
|
+
* @param {MouseEvent} event
|
13037
|
+
* `mousedown` or `touchstart` event that triggered this function
|
13038
|
+
*
|
13039
|
+
* @listens mousedown
|
13040
|
+
*/
|
13017
13041
|
handleMouseDown(event) {
|
13018
13042
|
this.mouseused_ = true;
|
13019
13043
|
}
|
@@ -13099,7 +13123,7 @@
|
|
13099
13123
|
*
|
13100
13124
|
* By default, if the key is Esc, it will trigger a `click` event.
|
13101
13125
|
*
|
13102
|
-
* @param {
|
13126
|
+
* @param {KeyboardEvent} event
|
13103
13127
|
* The `keydown` event that caused this function to be called.
|
13104
13128
|
*
|
13105
13129
|
* @listens keydown
|
@@ -16726,7 +16750,7 @@
|
|
16726
16750
|
/**
|
16727
16751
|
* Handle a `keydown` event on this menu. This listener is added in the constructor.
|
16728
16752
|
*
|
16729
|
-
* @param {
|
16753
|
+
* @param {KeyboardEvent} event
|
16730
16754
|
* A `keydown` event that happened on the menu.
|
16731
16755
|
*
|
16732
16756
|
* @listens keydown
|
@@ -17323,7 +17347,7 @@
|
|
17323
17347
|
* Ignore keys which are used by the menu, but pass any other ones up. See
|
17324
17348
|
* {@link ClickableComponent#handleKeyDown} for instances where this is called.
|
17325
17349
|
*
|
17326
|
-
* @param {
|
17350
|
+
* @param {KeyboardEvent} event
|
17327
17351
|
* The `keydown` event that caused this function to be called.
|
17328
17352
|
*
|
17329
17353
|
* @listens keydown
|
@@ -22382,8 +22406,8 @@
|
|
22382
22406
|
*
|
22383
22407
|
* After an instance has been created it can be accessed globally in three ways:
|
22384
22408
|
* 1. By calling `videojs.getPlayer('example_video_1');`
|
22385
|
-
* 2. By calling `videojs('example_video_1');` (not
|
22386
|
-
* 2. By using it directly via
|
22409
|
+
* 2. By calling `videojs('example_video_1');` (not recommended)
|
22410
|
+
* 2. By using it directly via `videojs.players.example_video_1;`
|
22387
22411
|
*
|
22388
22412
|
* @extends Component
|
22389
22413
|
* @global
|
@@ -24122,7 +24146,9 @@
|
|
24122
24146
|
*/
|
24123
24147
|
handleTechError_() {
|
24124
24148
|
const error = this.tech_.error();
|
24125
|
-
|
24149
|
+
if (error) {
|
24150
|
+
this.error(error);
|
24151
|
+
}
|
24126
24152
|
}
|
24127
24153
|
|
24128
24154
|
/**
|
@@ -25078,7 +25104,7 @@
|
|
25078
25104
|
* This allows player-wide hotkeys (either as defined below, or optionally
|
25079
25105
|
* by an external function).
|
25080
25106
|
*
|
25081
|
-
* @param {
|
25107
|
+
* @param {KeyboardEvent} event
|
25082
25108
|
* The `keydown` event that caused this function to be called.
|
25083
25109
|
*
|
25084
25110
|
* @listens keydown
|
@@ -27282,7 +27308,7 @@
|
|
27282
27308
|
* @param {Player} player
|
27283
27309
|
* A Video.js player instance.
|
27284
27310
|
*
|
27285
|
-
* @param {
|
27311
|
+
* @param {PluginEventHash} hash
|
27286
27312
|
* A plugin event hash.
|
27287
27313
|
*
|
27288
27314
|
* @param {boolean} [before]
|
@@ -27435,7 +27461,7 @@
|
|
27435
27461
|
* @param {Object} [hash={}]
|
27436
27462
|
* An object to be used as event an event hash.
|
27437
27463
|
*
|
27438
|
-
* @return {
|
27464
|
+
* @return {PluginEventHash}
|
27439
27465
|
* An event hash object with provided properties mixed-in.
|
27440
27466
|
*/
|
27441
27467
|
getEventHash(hash = {}) {
|
@@ -27454,7 +27480,7 @@
|
|
27454
27480
|
*
|
27455
27481
|
* @param {Object} [hash={}]
|
27456
27482
|
* Additional data hash to merge with a
|
27457
|
-
* {@link
|
27483
|
+
* {@link PluginEventHash|PluginEventHash}.
|
27458
27484
|
*
|
27459
27485
|
* @return {boolean}
|
27460
27486
|
* Whether or not default was prevented.
|
@@ -27670,7 +27696,7 @@
|
|
27670
27696
|
* Signals that a plugin is about to be set up on a player.
|
27671
27697
|
*
|
27672
27698
|
* @event Player#beforepluginsetup
|
27673
|
-
* @type {
|
27699
|
+
* @type {PluginEventHash}
|
27674
27700
|
*/
|
27675
27701
|
|
27676
27702
|
/**
|
@@ -27678,14 +27704,14 @@
|
|
27678
27704
|
* is the name of the plugin.
|
27679
27705
|
*
|
27680
27706
|
* @event Player#beforepluginsetup:$name
|
27681
|
-
* @type {
|
27707
|
+
* @type {PluginEventHash}
|
27682
27708
|
*/
|
27683
27709
|
|
27684
27710
|
/**
|
27685
27711
|
* Signals that a plugin has just been set up on a player.
|
27686
27712
|
*
|
27687
27713
|
* @event Player#pluginsetup
|
27688
|
-
* @type {
|
27714
|
+
* @type {PluginEventHash}
|
27689
27715
|
*/
|
27690
27716
|
|
27691
27717
|
/**
|
@@ -27693,11 +27719,11 @@
|
|
27693
27719
|
* is the name of the plugin.
|
27694
27720
|
*
|
27695
27721
|
* @event Player#pluginsetup:$name
|
27696
|
-
* @type {
|
27722
|
+
* @type {PluginEventHash}
|
27697
27723
|
*/
|
27698
27724
|
|
27699
27725
|
/**
|
27700
|
-
* @typedef {Object}
|
27726
|
+
* @typedef {Object} PluginEventHash
|
27701
27727
|
*
|
27702
27728
|
* @property {string} instance
|
27703
27729
|
* For basic plugins, the return value of the plugin function. For
|
@@ -28024,10 +28050,10 @@
|
|
28024
28050
|
* @param {string} name
|
28025
28051
|
* The class name of the component
|
28026
28052
|
*
|
28027
|
-
* @param {Component} comp
|
28053
|
+
* @param {typeof Component} comp
|
28028
28054
|
* The component class
|
28029
28055
|
*
|
28030
|
-
* @return {Component}
|
28056
|
+
* @return {typeof Component}
|
28031
28057
|
* The newly registered component
|
28032
28058
|
*/
|
28033
28059
|
videojs.registerComponent = (name, comp) => {
|
@@ -28110,9 +28136,11 @@
|
|
28110
28136
|
*
|
28111
28137
|
* @param {string} name
|
28112
28138
|
* The plugin name
|
28113
|
-
|
28114
|
-
* @param {Plugin|Function} plugin
|
28139
|
+
*
|
28140
|
+
* @param {typeof Plugin|Function} plugin
|
28115
28141
|
* The plugin sub-class or function
|
28142
|
+
*
|
28143
|
+
* @return {typeof Plugin|Function}
|
28116
28144
|
*/
|
28117
28145
|
videojs.plugin = (name, plugin) => {
|
28118
28146
|
log$1.warn('videojs.plugin() is deprecated; use videojs.registerPlugin() instead');
|
@@ -39079,7 +39107,7 @@
|
|
39079
39107
|
};
|
39080
39108
|
var clock_1 = clock.ONE_SECOND_IN_TS;
|
39081
39109
|
|
39082
|
-
/*! @name @videojs/http-streaming @version 3.
|
39110
|
+
/*! @name @videojs/http-streaming @version 3.7.0 @license Apache-2.0 */
|
39083
39111
|
|
39084
39112
|
/**
|
39085
39113
|
* @file resolve-url.js - Handling how URLs are resolved and manipulated
|
@@ -39813,9 +39841,13 @@
|
|
39813
39841
|
const seekable = function (playlist, expired, liveEdgePadding) {
|
39814
39842
|
const useSafeLiveEnd = true;
|
39815
39843
|
const seekableStart = expired || 0;
|
39816
|
-
|
39844
|
+
let seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd, liveEdgePadding);
|
39817
39845
|
if (seekableEnd === null) {
|
39818
39846
|
return createTimeRanges();
|
39847
|
+
} // Clamp seekable end since it can not be less than the seekable start
|
39848
|
+
|
39849
|
+
if (seekableEnd < seekableStart) {
|
39850
|
+
seekableEnd = seekableStart;
|
39819
39851
|
}
|
39820
39852
|
return createTimeRanges(seekableStart, seekableEnd);
|
39821
39853
|
};
|
@@ -42253,7 +42285,10 @@
|
|
42253
42285
|
}); // live playlist staleness timeout
|
42254
42286
|
|
42255
42287
|
this.on('mediaupdatetimeout', () => {
|
42256
|
-
|
42288
|
+
// We handle live content steering in the playlist controller
|
42289
|
+
if (!this.media().attributes.serviceLocation) {
|
42290
|
+
this.refreshMedia_(this.media().id);
|
42291
|
+
}
|
42257
42292
|
});
|
42258
42293
|
this.state = 'HAVE_NOTHING';
|
42259
42294
|
this.loadedPlaylists_ = {};
|
@@ -45083,18 +45118,31 @@
|
|
45083
45118
|
var nextByte = packetData[i + 1];
|
45084
45119
|
var win = service.currentWindow;
|
45085
45120
|
var char;
|
45086
|
-
var charCodeArray; //
|
45121
|
+
var charCodeArray; // Converts an array of bytes to a unicode hex string.
|
45122
|
+
|
45123
|
+
function toHexString(byteArray) {
|
45124
|
+
return byteArray.map(byte => {
|
45125
|
+
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
|
45126
|
+
}).join('');
|
45127
|
+
}
|
45128
|
+
if (isMultiByte) {
|
45129
|
+
charCodeArray = [currentByte, nextByte];
|
45130
|
+
i++;
|
45131
|
+
} else {
|
45132
|
+
charCodeArray = [currentByte];
|
45133
|
+
} // Use the TextDecoder if one was created for this service
|
45087
45134
|
|
45088
45135
|
if (service.textDecoder_ && !isExtended) {
|
45136
|
+
char = service.textDecoder_.decode(new Uint8Array(charCodeArray));
|
45137
|
+
} else {
|
45138
|
+
// We assume any multi-byte char without a decoder is unicode.
|
45089
45139
|
if (isMultiByte) {
|
45090
|
-
|
45091
|
-
|
45140
|
+
const unicode = toHexString(charCodeArray); // Takes a unicode hex string and creates a single character.
|
45141
|
+
|
45142
|
+
char = String.fromCharCode(parseInt(unicode, 16));
|
45092
45143
|
} else {
|
45093
|
-
|
45144
|
+
char = get708CharFromCode(extended | currentByte);
|
45094
45145
|
}
|
45095
|
-
char = service.textDecoder_.decode(new Uint8Array(charCodeArray));
|
45096
|
-
} else {
|
45097
|
-
char = get708CharFromCode(extended | currentByte);
|
45098
45146
|
}
|
45099
45147
|
if (win.pendingNewLine && !win.isEmpty()) {
|
45100
45148
|
win.newLine(this.getPts(i));
|
@@ -52091,7 +52139,7 @@
|
|
52091
52139
|
segment.bytes = bytesAsUint8Array = emsgData; // Run through the CaptionParser in case there are captions.
|
52092
52140
|
// Initialize CaptionParser if it hasn't been yet
|
52093
52141
|
|
52094
|
-
if (!tracks.video || !
|
52142
|
+
if (!tracks.video || !emsgData.byteLength || !segment.transmuxer) {
|
52095
52143
|
finishLoading(undefined, id3Frames);
|
52096
52144
|
return;
|
52097
52145
|
}
|
@@ -59082,15 +59130,11 @@
|
|
59082
59130
|
*/
|
59083
59131
|
AUDIO: (type, settings) => () => {
|
59084
59132
|
const {
|
59085
|
-
segmentLoaders: {
|
59086
|
-
[type]: segmentLoader
|
59087
|
-
},
|
59088
59133
|
mediaTypes: {
|
59089
59134
|
[type]: mediaType
|
59090
59135
|
},
|
59091
59136
|
excludePlaylist
|
59092
|
-
} = settings;
|
59093
|
-
stopLoaders(segmentLoader, mediaType); // switch back to default audio track
|
59137
|
+
} = settings; // switch back to default audio track
|
59094
59138
|
|
59095
59139
|
const activeTrack = mediaType.activeTrack();
|
59096
59140
|
const activeGroup = mediaType.activeGroup();
|
@@ -59126,15 +59170,11 @@
|
|
59126
59170
|
*/
|
59127
59171
|
SUBTITLES: (type, settings) => () => {
|
59128
59172
|
const {
|
59129
|
-
segmentLoaders: {
|
59130
|
-
[type]: segmentLoader
|
59131
|
-
},
|
59132
59173
|
mediaTypes: {
|
59133
59174
|
[type]: mediaType
|
59134
59175
|
}
|
59135
59176
|
} = settings;
|
59136
59177
|
videojs.log.warn('Problem encountered loading the subtitle track.' + 'Disabling subtitle track.');
|
59137
|
-
stopLoaders(segmentLoader, mediaType);
|
59138
59178
|
const track = mediaType.activeTrack();
|
59139
59179
|
if (track) {
|
59140
59180
|
track.mode = 'disabled';
|
@@ -59721,6 +59761,388 @@
|
|
59721
59761
|
return mediaTypes;
|
59722
59762
|
};
|
59723
59763
|
|
59764
|
+
/**
|
59765
|
+
* A utility class for setting properties and maintaining the state of the content steering manifest.
|
59766
|
+
*
|
59767
|
+
* Content Steering manifest format:
|
59768
|
+
* VERSION: number (required) currently only version 1 is supported.
|
59769
|
+
* TTL: number in seconds (optional) until the next content steering manifest reload.
|
59770
|
+
* RELOAD-URI: string (optional) uri to fetch the next content steering manifest.
|
59771
|
+
* SERVICE-LOCATION-PRIORITY or PATHWAY-PRIORITY a non empty array of unique string values.
|
59772
|
+
*/
|
59773
|
+
|
59774
|
+
class SteeringManifest {
|
59775
|
+
constructor() {
|
59776
|
+
this.priority_ = [];
|
59777
|
+
}
|
59778
|
+
set version(number) {
|
59779
|
+
// Only version 1 is currently supported for both DASH and HLS.
|
59780
|
+
if (number === 1) {
|
59781
|
+
this.version_ = number;
|
59782
|
+
}
|
59783
|
+
}
|
59784
|
+
set ttl(seconds) {
|
59785
|
+
// TTL = time-to-live, default = 300 seconds.
|
59786
|
+
this.ttl_ = seconds || 300;
|
59787
|
+
}
|
59788
|
+
set reloadUri(uri) {
|
59789
|
+
if (uri) {
|
59790
|
+
// reload URI can be relative to the previous reloadUri.
|
59791
|
+
this.reloadUri_ = resolveUrl(this.reloadUri_, uri);
|
59792
|
+
}
|
59793
|
+
}
|
59794
|
+
set priority(array) {
|
59795
|
+
// priority must be non-empty and unique values.
|
59796
|
+
if (array && array.length) {
|
59797
|
+
this.priority_ = array;
|
59798
|
+
}
|
59799
|
+
}
|
59800
|
+
get version() {
|
59801
|
+
return this.version_;
|
59802
|
+
}
|
59803
|
+
get ttl() {
|
59804
|
+
return this.ttl_;
|
59805
|
+
}
|
59806
|
+
get reloadUri() {
|
59807
|
+
return this.reloadUri_;
|
59808
|
+
}
|
59809
|
+
get priority() {
|
59810
|
+
return this.priority_;
|
59811
|
+
}
|
59812
|
+
}
|
59813
|
+
/**
|
59814
|
+
* This class represents a content steering manifest and associated state. See both HLS and DASH specifications.
|
59815
|
+
* HLS: https://developer.apple.com/streaming/HLSContentSteeringSpecification.pdf and
|
59816
|
+
* https://datatracker.ietf.org/doc/draft-pantos-hls-rfc8216bis/ section 4.4.6.6.
|
59817
|
+
* DASH: https://dashif.org/docs/DASH-IF-CTS-00XX-Content-Steering-Community-Review.pdf
|
59818
|
+
*
|
59819
|
+
* @param {function} xhr for making a network request from the browser.
|
59820
|
+
* @param {function} bandwidth for fetching the current bandwidth from the main segment loader.
|
59821
|
+
*/
|
59822
|
+
|
59823
|
+
class ContentSteeringController extends videojs.EventTarget {
|
59824
|
+
constructor(xhr, bandwidth) {
|
59825
|
+
super();
|
59826
|
+
this.currentPathway = null;
|
59827
|
+
this.defaultPathway = null;
|
59828
|
+
this.queryBeforeStart = null;
|
59829
|
+
this.availablePathways_ = new Set();
|
59830
|
+
this.excludedPathways_ = new Set();
|
59831
|
+
this.steeringManifest = new SteeringManifest();
|
59832
|
+
this.proxyServerUrl_ = null;
|
59833
|
+
this.manifestType_ = null;
|
59834
|
+
this.ttlTimeout_ = null;
|
59835
|
+
this.request_ = null;
|
59836
|
+
this.excludedSteeringManifestURLs = new Set();
|
59837
|
+
this.logger_ = logger('Content Steering');
|
59838
|
+
this.xhr_ = xhr;
|
59839
|
+
this.getBandwidth_ = bandwidth;
|
59840
|
+
}
|
59841
|
+
/**
|
59842
|
+
* Assigns the content steering tag properties to the steering controller
|
59843
|
+
*
|
59844
|
+
* @param {string} baseUrl the baseURL from the manifest for resolving the steering manifest url
|
59845
|
+
* @param {Object} steeringTag the content steering tag from the main manifest
|
59846
|
+
*/
|
59847
|
+
|
59848
|
+
assignTagProperties(baseUrl, steeringTag) {
|
59849
|
+
this.manifestType_ = steeringTag.serverUri ? 'HLS' : 'DASH'; // serverUri is HLS serverURL is DASH
|
59850
|
+
|
59851
|
+
const steeringUri = steeringTag.serverUri || steeringTag.serverURL;
|
59852
|
+
if (!steeringUri) {
|
59853
|
+
this.logger_(`steering manifest URL is ${steeringUri}, cannot request steering manifest.`);
|
59854
|
+
this.trigger('error');
|
59855
|
+
return;
|
59856
|
+
} // Content steering manifests can be encoded as a data URI. We can decode, parse and return early if that's the case.
|
59857
|
+
|
59858
|
+
if (steeringUri.startsWith('data:')) {
|
59859
|
+
this.decodeDataUriManifest_(steeringUri.substring(steeringUri.indexOf(',') + 1));
|
59860
|
+
return;
|
59861
|
+
} // With DASH queryBeforeStart, we want to use the steeringUri as soon as possible for the request.
|
59862
|
+
|
59863
|
+
this.steeringManifest.reloadUri = this.queryBeforeStart ? steeringUri : resolveUrl(baseUrl, steeringUri); // pathwayId is HLS defaultServiceLocation is DASH
|
59864
|
+
|
59865
|
+
this.defaultPathway = steeringTag.pathwayId || steeringTag.defaultServiceLocation; // currently only DASH supports the following properties on <ContentSteering> tags.
|
59866
|
+
|
59867
|
+
this.queryBeforeStart = steeringTag.queryBeforeStart || false;
|
59868
|
+
this.proxyServerUrl_ = steeringTag.proxyServerURL || null; // trigger a steering event if we have a pathway from the content steering tag.
|
59869
|
+
// this tells VHS which segment pathway to start with.
|
59870
|
+
// If queryBeforeStart is true we need to wait for the steering manifest response.
|
59871
|
+
|
59872
|
+
if (this.defaultPathway && !this.queryBeforeStart) {
|
59873
|
+
this.trigger('content-steering');
|
59874
|
+
}
|
59875
|
+
if (this.queryBeforeStart) {
|
59876
|
+
this.requestSteeringManifest(this.steeringManifest.reloadUri);
|
59877
|
+
}
|
59878
|
+
}
|
59879
|
+
/**
|
59880
|
+
* Requests the content steering manifest and parse the response. This should only be called after
|
59881
|
+
* assignTagProperties was called with a content steering tag.
|
59882
|
+
*
|
59883
|
+
* @param {string} initialUri The optional uri to make the request with.
|
59884
|
+
* If set, the request should be made with exactly what is passed in this variable.
|
59885
|
+
* This scenario is specific to DASH when the queryBeforeStart parameter is true.
|
59886
|
+
* This scenario should only happen once on initalization.
|
59887
|
+
*/
|
59888
|
+
|
59889
|
+
requestSteeringManifest(initialUri) {
|
59890
|
+
const reloadUri = this.steeringManifest.reloadUri;
|
59891
|
+
if (!initialUri && !reloadUri) {
|
59892
|
+
return;
|
59893
|
+
} // We currently don't support passing MPD query parameters directly to the content steering URL as this requires
|
59894
|
+
// ExtUrlQueryInfo tag support. See the DASH content steering spec section 8.1.
|
59895
|
+
// This request URI accounts for manifest URIs that have been excluded.
|
59896
|
+
|
59897
|
+
const uri = initialUri || this.getRequestURI(reloadUri); // If there are no valid manifest URIs, we should stop content steering.
|
59898
|
+
|
59899
|
+
if (!uri) {
|
59900
|
+
this.logger_('No valid content steering manifest URIs. Stopping content steering.');
|
59901
|
+
this.trigger('error');
|
59902
|
+
this.dispose();
|
59903
|
+
return;
|
59904
|
+
}
|
59905
|
+
this.request_ = this.xhr_({
|
59906
|
+
uri
|
59907
|
+
}, (error, errorInfo) => {
|
59908
|
+
if (error) {
|
59909
|
+
// If the client receives HTTP 410 Gone in response to a manifest request,
|
59910
|
+
// it MUST NOT issue another request for that URI for the remainder of the
|
59911
|
+
// playback session. It MAY continue to use the most-recently obtained set
|
59912
|
+
// of Pathways.
|
59913
|
+
if (errorInfo.status === 410) {
|
59914
|
+
this.logger_(`manifest request 410 ${error}.`);
|
59915
|
+
this.logger_(`There will be no more content steering requests to ${uri} this session.`);
|
59916
|
+
this.excludedSteeringManifestURLs.add(uri);
|
59917
|
+
return;
|
59918
|
+
} // If the client receives HTTP 429 Too Many Requests with a Retry-After
|
59919
|
+
// header in response to a manifest request, it SHOULD wait until the time
|
59920
|
+
// specified by the Retry-After header to reissue the request.
|
59921
|
+
|
59922
|
+
if (errorInfo.status === 429) {
|
59923
|
+
const retrySeconds = errorInfo.responseHeaders['retry-after'];
|
59924
|
+
this.logger_(`manifest request 429 ${error}.`);
|
59925
|
+
this.logger_(`content steering will retry in ${retrySeconds} seconds.`);
|
59926
|
+
this.startTTLTimeout_(parseInt(retrySeconds, 10));
|
59927
|
+
return;
|
59928
|
+
} // If the Steering Manifest cannot be loaded and parsed correctly, the
|
59929
|
+
// client SHOULD continue to use the previous values and attempt to reload
|
59930
|
+
// it after waiting for the previously-specified TTL (or 5 minutes if
|
59931
|
+
// none).
|
59932
|
+
|
59933
|
+
this.logger_(`manifest failed to load ${error}.`);
|
59934
|
+
this.startTTLTimeout_();
|
59935
|
+
return;
|
59936
|
+
}
|
59937
|
+
const steeringManifestJson = JSON.parse(this.request_.responseText);
|
59938
|
+
this.startTTLTimeout_();
|
59939
|
+
this.assignSteeringProperties_(steeringManifestJson);
|
59940
|
+
});
|
59941
|
+
}
|
59942
|
+
/**
|
59943
|
+
* Set the proxy server URL and add the steering manifest url as a URI encoded parameter.
|
59944
|
+
*
|
59945
|
+
* @param {string} steeringUrl the steering manifest url
|
59946
|
+
* @return the steering manifest url to a proxy server with all parameters set
|
59947
|
+
*/
|
59948
|
+
|
59949
|
+
setProxyServerUrl_(steeringUrl) {
|
59950
|
+
const steeringUrlObject = new window.URL(steeringUrl);
|
59951
|
+
const proxyServerUrlObject = new window.URL(this.proxyServerUrl_);
|
59952
|
+
proxyServerUrlObject.searchParams.set('url', encodeURI(steeringUrlObject.toString()));
|
59953
|
+
return this.setSteeringParams_(proxyServerUrlObject.toString());
|
59954
|
+
}
|
59955
|
+
/**
|
59956
|
+
* Decodes and parses the data uri encoded steering manifest
|
59957
|
+
*
|
59958
|
+
* @param {string} dataUri the data uri to be decoded and parsed.
|
59959
|
+
*/
|
59960
|
+
|
59961
|
+
decodeDataUriManifest_(dataUri) {
|
59962
|
+
const steeringManifestJson = JSON.parse(window.atob(dataUri));
|
59963
|
+
this.assignSteeringProperties_(steeringManifestJson);
|
59964
|
+
}
|
59965
|
+
/**
|
59966
|
+
* Set the HLS or DASH content steering manifest request query parameters. For example:
|
59967
|
+
* _HLS_pathway="<CURRENT-PATHWAY-ID>" and _HLS_throughput=<THROUGHPUT>
|
59968
|
+
* _DASH_pathway and _DASH_throughput
|
59969
|
+
*
|
59970
|
+
* @param {string} uri to add content steering server parameters to.
|
59971
|
+
* @return a new uri as a string with the added steering query parameters.
|
59972
|
+
*/
|
59973
|
+
|
59974
|
+
setSteeringParams_(url) {
|
59975
|
+
const urlObject = new window.URL(url);
|
59976
|
+
const path = this.getPathway();
|
59977
|
+
const networkThroughput = this.getBandwidth_();
|
59978
|
+
if (path) {
|
59979
|
+
const pathwayKey = `_${this.manifestType_}_pathway`;
|
59980
|
+
urlObject.searchParams.set(pathwayKey, path);
|
59981
|
+
}
|
59982
|
+
if (networkThroughput) {
|
59983
|
+
const throughputKey = `_${this.manifestType_}_throughput`;
|
59984
|
+
urlObject.searchParams.set(throughputKey, networkThroughput);
|
59985
|
+
}
|
59986
|
+
return urlObject.toString();
|
59987
|
+
}
|
59988
|
+
/**
|
59989
|
+
* Assigns the current steering manifest properties and to the SteeringManifest object
|
59990
|
+
*
|
59991
|
+
* @param {Object} steeringJson the raw JSON steering manifest
|
59992
|
+
*/
|
59993
|
+
|
59994
|
+
assignSteeringProperties_(steeringJson) {
|
59995
|
+
this.steeringManifest.version = steeringJson.VERSION;
|
59996
|
+
if (!this.steeringManifest.version) {
|
59997
|
+
this.logger_(`manifest version is ${steeringJson.VERSION}, which is not supported.`);
|
59998
|
+
this.trigger('error');
|
59999
|
+
return;
|
60000
|
+
}
|
60001
|
+
this.steeringManifest.ttl = steeringJson.TTL;
|
60002
|
+
this.steeringManifest.reloadUri = steeringJson['RELOAD-URI']; // HLS = PATHWAY-PRIORITY required. DASH = SERVICE-LOCATION-PRIORITY optional
|
60003
|
+
|
60004
|
+
this.steeringManifest.priority = steeringJson['PATHWAY-PRIORITY'] || steeringJson['SERVICE-LOCATION-PRIORITY']; // TODO: HLS handle PATHWAY-CLONES. See section 7.2 https://datatracker.ietf.org/doc/draft-pantos-hls-rfc8216bis/
|
60005
|
+
// 1. apply first pathway from the array.
|
60006
|
+
// 2. if first pathway doesn't exist in manifest, try next pathway.
|
60007
|
+
// a. if all pathways are exhausted, ignore the steering manifest priority.
|
60008
|
+
// 3. if segments fail from an established pathway, try all variants/renditions, then exclude the failed pathway.
|
60009
|
+
// a. exclude a pathway for a minimum of the last TTL duration. Meaning, from the next steering response,
|
60010
|
+
// the excluded pathway will be ignored.
|
60011
|
+
// See excludePathway usage in excludePlaylist().
|
60012
|
+
// If there are no available pathways, we need to stop content steering.
|
60013
|
+
|
60014
|
+
if (!this.availablePathways_.size) {
|
60015
|
+
this.logger_('There are no available pathways for content steering. Ending content steering.');
|
60016
|
+
this.trigger('error');
|
60017
|
+
this.dispose();
|
60018
|
+
}
|
60019
|
+
const chooseNextPathway = pathwaysByPriority => {
|
60020
|
+
for (const path of pathwaysByPriority) {
|
60021
|
+
if (this.availablePathways_.has(path)) {
|
60022
|
+
return path;
|
60023
|
+
}
|
60024
|
+
} // If no pathway matches, ignore the manifest and choose the first available.
|
60025
|
+
|
60026
|
+
return [...this.availablePathways_][0];
|
60027
|
+
};
|
60028
|
+
const nextPathway = chooseNextPathway(this.steeringManifest.priority);
|
60029
|
+
if (this.currentPathway !== nextPathway) {
|
60030
|
+
this.currentPathway = nextPathway;
|
60031
|
+
this.trigger('content-steering');
|
60032
|
+
}
|
60033
|
+
}
|
60034
|
+
/**
|
60035
|
+
* Returns the pathway to use for steering decisions
|
60036
|
+
*
|
60037
|
+
* @return {string} returns the current pathway or the default
|
60038
|
+
*/
|
60039
|
+
|
60040
|
+
getPathway() {
|
60041
|
+
return this.currentPathway || this.defaultPathway;
|
60042
|
+
}
|
60043
|
+
/**
|
60044
|
+
* Chooses the manifest request URI based on proxy URIs and server URLs.
|
60045
|
+
* Also accounts for exclusion on certain manifest URIs.
|
60046
|
+
*
|
60047
|
+
* @param {string} reloadUri the base uri before parameters
|
60048
|
+
*
|
60049
|
+
* @return {string} the final URI for the request to the manifest server.
|
60050
|
+
*/
|
60051
|
+
|
60052
|
+
getRequestURI(reloadUri) {
|
60053
|
+
if (!reloadUri) {
|
60054
|
+
return null;
|
60055
|
+
}
|
60056
|
+
const isExcluded = uri => this.excludedSteeringManifestURLs.has(uri);
|
60057
|
+
if (this.proxyServerUrl_) {
|
60058
|
+
const proxyURI = this.setProxyServerUrl_(reloadUri);
|
60059
|
+
if (!isExcluded(proxyURI)) {
|
60060
|
+
return proxyURI;
|
60061
|
+
}
|
60062
|
+
}
|
60063
|
+
const steeringURI = this.setSteeringParams_(reloadUri);
|
60064
|
+
if (!isExcluded(steeringURI)) {
|
60065
|
+
return steeringURI;
|
60066
|
+
} // Return nothing if all valid manifest URIs are excluded.
|
60067
|
+
|
60068
|
+
return null;
|
60069
|
+
}
|
60070
|
+
/**
|
60071
|
+
* Start the timeout for re-requesting the steering manifest at the TTL interval.
|
60072
|
+
*
|
60073
|
+
* @param {number} ttl time in seconds of the timeout. Defaults to the
|
60074
|
+
* ttl interval in the steering manifest
|
60075
|
+
*/
|
60076
|
+
|
60077
|
+
startTTLTimeout_(ttl = this.steeringManifest.ttl) {
|
60078
|
+
// 300 (5 minutes) is the default value.
|
60079
|
+
const ttlMS = ttl * 1000;
|
60080
|
+
this.ttlTimeout_ = window.setTimeout(() => {
|
60081
|
+
this.requestSteeringManifest();
|
60082
|
+
}, ttlMS);
|
60083
|
+
}
|
60084
|
+
/**
|
60085
|
+
* Clear the TTL timeout if necessary.
|
60086
|
+
*/
|
60087
|
+
|
60088
|
+
clearTTLTimeout_() {
|
60089
|
+
window.clearTimeout(this.ttlTimeout_);
|
60090
|
+
this.ttlTimeout_ = null;
|
60091
|
+
}
|
60092
|
+
/**
|
60093
|
+
* aborts any current steering xhr and sets the current request object to null
|
60094
|
+
*/
|
60095
|
+
|
60096
|
+
abort() {
|
60097
|
+
if (this.request_) {
|
60098
|
+
this.request_.abort();
|
60099
|
+
}
|
60100
|
+
this.request_ = null;
|
60101
|
+
}
|
60102
|
+
/**
|
60103
|
+
* aborts steering requests clears the ttl timeout and resets all properties.
|
60104
|
+
*/
|
60105
|
+
|
60106
|
+
dispose() {
|
60107
|
+
this.off('content-steering');
|
60108
|
+
this.off('error');
|
60109
|
+
this.abort();
|
60110
|
+
this.clearTTLTimeout_();
|
60111
|
+
this.currentPathway = null;
|
60112
|
+
this.defaultPathway = null;
|
60113
|
+
this.queryBeforeStart = null;
|
60114
|
+
this.proxyServerUrl_ = null;
|
60115
|
+
this.manifestType_ = null;
|
60116
|
+
this.ttlTimeout_ = null;
|
60117
|
+
this.request_ = null;
|
60118
|
+
this.excludedSteeringManifestURLs = new Set();
|
60119
|
+
this.availablePathways_ = new Set();
|
60120
|
+
this.excludedPathways_ = new Set();
|
60121
|
+
this.steeringManifest = new SteeringManifest();
|
60122
|
+
}
|
60123
|
+
/**
|
60124
|
+
* adds a pathway to the available pathways set
|
60125
|
+
*
|
60126
|
+
* @param {string} pathway the pathway string to add
|
60127
|
+
*/
|
60128
|
+
|
60129
|
+
addAvailablePathway(pathway) {
|
60130
|
+
if (pathway) {
|
60131
|
+
this.availablePathways_.add(pathway);
|
60132
|
+
}
|
60133
|
+
}
|
60134
|
+
/**
|
60135
|
+
* clears all pathways from the available pathways set
|
60136
|
+
*/
|
60137
|
+
|
60138
|
+
clearAvailablePathways() {
|
60139
|
+
this.availablePathways_.clear();
|
60140
|
+
}
|
60141
|
+
excludePathway(pathway) {
|
60142
|
+
return this.availablePathways_.delete(pathway);
|
60143
|
+
}
|
60144
|
+
}
|
60145
|
+
|
59724
60146
|
/**
|
59725
60147
|
* @file playlist-controller.js
|
59726
60148
|
*/
|
@@ -59948,6 +60370,10 @@
|
|
59948
60370
|
tech.addWebVttScript_();
|
59949
60371
|
})
|
59950
60372
|
}), options);
|
60373
|
+
const getBandwidth = () => {
|
60374
|
+
return this.mainSegmentLoader_.bandwidth;
|
60375
|
+
};
|
60376
|
+
this.contentSteeringController_ = new ContentSteeringController(this.vhs_.xhr, getBandwidth);
|
59951
60377
|
this.setupSegmentLoaderListeners_();
|
59952
60378
|
if (this.bufferBasedABR) {
|
59953
60379
|
this.mainPlaylistLoader_.one('loadedplaylist', () => this.startABRTimer_());
|
@@ -60033,6 +60459,32 @@
|
|
60033
60459
|
}
|
60034
60460
|
this.mainPlaylistLoader_.media(playlist, delay);
|
60035
60461
|
}
|
60462
|
+
/**
|
60463
|
+
* A function that ensures we switch our playlists inside of `mediaTypes`
|
60464
|
+
* to match the current `serviceLocation` provided by the contentSteering controller.
|
60465
|
+
* We want to check media types of `AUDIO`, `SUBTITLES`, and `CLOSED-CAPTIONS`.
|
60466
|
+
*
|
60467
|
+
* This should only be called on a DASH playback scenario while using content steering.
|
60468
|
+
* This is necessary due to differences in how media in HLS manifests are generally tied to
|
60469
|
+
* a video playlist, where in DASH that is not always the case.
|
60470
|
+
*/
|
60471
|
+
|
60472
|
+
switchMediaForDASHContentSteering_() {
|
60473
|
+
['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(type => {
|
60474
|
+
const mediaType = this.mediaTypes_[type];
|
60475
|
+
const activeGroup = mediaType ? mediaType.activeGroup() : null;
|
60476
|
+
const pathway = this.contentSteeringController_.getPathway();
|
60477
|
+
if (activeGroup && pathway) {
|
60478
|
+
// activeGroup can be an array or a single group
|
60479
|
+
const mediaPlaylists = activeGroup.length ? activeGroup[0].playlists : activeGroup.playlists;
|
60480
|
+
const dashMediaPlaylists = mediaPlaylists.filter(p => p.attributes.serviceLocation === pathway); // Switch the current active playlist to the correct CDN
|
60481
|
+
|
60482
|
+
if (dashMediaPlaylists.length) {
|
60483
|
+
this.mediaTypes_[type].activePlaylistLoader.media(dashMediaPlaylists[0]);
|
60484
|
+
}
|
60485
|
+
}
|
60486
|
+
});
|
60487
|
+
}
|
60036
60488
|
/**
|
60037
60489
|
* Start a timer that periodically calls checkABR_
|
60038
60490
|
*
|
@@ -60181,8 +60633,9 @@
|
|
60181
60633
|
}
|
60182
60634
|
let updatedPlaylist = this.mainPlaylistLoader_.media();
|
60183
60635
|
if (!updatedPlaylist) {
|
60184
|
-
// exclude any variants that are not supported by the browser before selecting
|
60636
|
+
this.initContentSteeringController_(); // exclude any variants that are not supported by the browser before selecting
|
60185
60637
|
// an initial media as the playlist selectors do not consider browser support
|
60638
|
+
|
60186
60639
|
this.excludeUnsupportedVariants_();
|
60187
60640
|
let selectedMedia;
|
60188
60641
|
if (this.enableLowInitialPlaylist) {
|
@@ -60763,10 +61216,22 @@
|
|
60763
61216
|
return this.mainPlaylistLoader_.load(isFinalRendition);
|
60764
61217
|
}
|
60765
61218
|
if (isFinalRendition) {
|
60766
|
-
//
|
61219
|
+
// If we're content steering, try other pathways.
|
61220
|
+
if (this.main().contentSteering) {
|
61221
|
+
const pathway = this.pathwayAttribute_(playlistToExclude); // Ignore at least 1 steering manifest refresh.
|
61222
|
+
|
61223
|
+
const reIncludeDelay = this.contentSteeringController_.steeringManifest.ttl * 1000;
|
61224
|
+
this.contentSteeringController_.excludePathway(pathway);
|
61225
|
+
this.excludeThenChangePathway_();
|
61226
|
+
setTimeout(() => {
|
61227
|
+
this.contentSteeringController_.addAvailablePathway(pathway);
|
61228
|
+
}, reIncludeDelay);
|
61229
|
+
return;
|
61230
|
+
} // Since we're on the final non-excluded playlist, and we're about to exclude
|
60767
61231
|
// it, instead of erring the player or retrying this playlist, clear out the current
|
60768
61232
|
// exclusion list. This allows other playlists to be attempted in case any have been
|
60769
61233
|
// fixed.
|
61234
|
+
|
60770
61235
|
let reincluded = false;
|
60771
61236
|
playlists.forEach(playlist => {
|
60772
61237
|
// skip current playlist which is about to be excluded
|
@@ -61104,6 +61569,7 @@
|
|
61104
61569
|
this.decrypter_.terminate();
|
61105
61570
|
this.mainPlaylistLoader_.dispose();
|
61106
61571
|
this.mainSegmentLoader_.dispose();
|
61572
|
+
this.contentSteeringController_.dispose();
|
61107
61573
|
if (this.loadOnPlay_) {
|
61108
61574
|
this.tech_.off('play', this.loadOnPlay_);
|
61109
61575
|
}
|
@@ -61444,6 +61910,110 @@
|
|
61444
61910
|
videoDuration
|
61445
61911
|
});
|
61446
61912
|
}
|
61913
|
+
pathwayAttribute_(playlist) {
|
61914
|
+
return playlist.attributes['PATHWAY-ID'] || playlist.attributes.serviceLocation;
|
61915
|
+
}
|
61916
|
+
/**
|
61917
|
+
* Initialize content steering listeners and apply the tag properties.
|
61918
|
+
*/
|
61919
|
+
|
61920
|
+
initContentSteeringController_() {
|
61921
|
+
const initialMain = this.main();
|
61922
|
+
if (!initialMain.contentSteering) {
|
61923
|
+
return;
|
61924
|
+
}
|
61925
|
+
const updateSteeringValues = main => {
|
61926
|
+
for (const playlist of main.playlists) {
|
61927
|
+
this.contentSteeringController_.addAvailablePathway(this.pathwayAttribute_(playlist));
|
61928
|
+
}
|
61929
|
+
this.contentSteeringController_.assignTagProperties(main.uri, main.contentSteering);
|
61930
|
+
};
|
61931
|
+
updateSteeringValues(initialMain);
|
61932
|
+
this.contentSteeringController_.on('content-steering', this.excludeThenChangePathway_.bind(this)); // We need to ensure we update the content steering values when a new
|
61933
|
+
// manifest is loaded in live DASH with content steering.
|
61934
|
+
|
61935
|
+
if (this.sourceType_ === 'dash') {
|
61936
|
+
this.mainPlaylistLoader_.on('mediaupdatetimeout', () => {
|
61937
|
+
this.mainPlaylistLoader_.refreshMedia_(this.mainPlaylistLoader_.media().id); // clear past values
|
61938
|
+
|
61939
|
+
this.contentSteeringController_.abort();
|
61940
|
+
this.contentSteeringController_.clearTTLTimeout_();
|
61941
|
+
this.contentSteeringController_.clearAvailablePathways();
|
61942
|
+
updateSteeringValues(this.main());
|
61943
|
+
});
|
61944
|
+
} // Do this at startup only, after that the steering requests are managed by the Content Steering class.
|
61945
|
+
// DASH queryBeforeStart scenarios will be handled by the Content Steering class.
|
61946
|
+
|
61947
|
+
if (!this.contentSteeringController_.queryBeforeStart) {
|
61948
|
+
this.tech_.one('canplay', () => {
|
61949
|
+
this.contentSteeringController_.requestSteeringManifest();
|
61950
|
+
});
|
61951
|
+
}
|
61952
|
+
}
|
61953
|
+
/**
|
61954
|
+
* Simple exclude and change playlist logic for content steering.
|
61955
|
+
*/
|
61956
|
+
|
61957
|
+
excludeThenChangePathway_() {
|
61958
|
+
const currentPathway = this.contentSteeringController_.getPathway();
|
61959
|
+
if (!currentPathway) {
|
61960
|
+
return;
|
61961
|
+
}
|
61962
|
+
const main = this.main();
|
61963
|
+
const playlists = main.playlists;
|
61964
|
+
const ids = new Set();
|
61965
|
+
let didEnablePlaylists = false;
|
61966
|
+
Object.keys(playlists).forEach(key => {
|
61967
|
+
const variant = playlists[key];
|
61968
|
+
const pathwayId = this.pathwayAttribute_(variant);
|
61969
|
+
const differentPathwayId = pathwayId && currentPathway !== pathwayId;
|
61970
|
+
const steeringExclusion = variant.excludeUntil === Infinity && variant.lastExcludeReason_ === 'content-steering';
|
61971
|
+
if (steeringExclusion && !differentPathwayId) {
|
61972
|
+
delete variant.excludeUntil;
|
61973
|
+
delete variant.lastExcludeReason_;
|
61974
|
+
didEnablePlaylists = true;
|
61975
|
+
}
|
61976
|
+
const noExcludeUntil = !variant.excludeUntil && variant.excludeUntil !== Infinity;
|
61977
|
+
const shouldExclude = !ids.has(variant.id) && differentPathwayId && noExcludeUntil;
|
61978
|
+
if (!shouldExclude) {
|
61979
|
+
return;
|
61980
|
+
}
|
61981
|
+
ids.add(variant.id);
|
61982
|
+
variant.excludeUntil = Infinity;
|
61983
|
+
variant.lastExcludeReason_ = 'content-steering'; // TODO: kind of spammy, maybe move this.
|
61984
|
+
|
61985
|
+
this.logger_(`excluding ${variant.id} for ${variant.lastExcludeReason_}`);
|
61986
|
+
});
|
61987
|
+
if (this.contentSteeringController_.manifestType_ === 'DASH') {
|
61988
|
+
Object.keys(this.mediaTypes_).forEach(key => {
|
61989
|
+
const type = this.mediaTypes_[key];
|
61990
|
+
if (type.activePlaylistLoader) {
|
61991
|
+
const currentPlaylist = type.activePlaylistLoader.media_; // Check if the current media playlist matches the current CDN
|
61992
|
+
|
61993
|
+
if (currentPlaylist && currentPlaylist.attributes.serviceLocation !== currentPathway) {
|
61994
|
+
didEnablePlaylists = true;
|
61995
|
+
}
|
61996
|
+
}
|
61997
|
+
});
|
61998
|
+
}
|
61999
|
+
if (didEnablePlaylists) {
|
62000
|
+
this.changeSegmentPathway_();
|
62001
|
+
}
|
62002
|
+
}
|
62003
|
+
/**
|
62004
|
+
* Changes the current playlists for audio, video and subtitles after a new pathway
|
62005
|
+
* is chosen from content steering.
|
62006
|
+
*/
|
62007
|
+
|
62008
|
+
changeSegmentPathway_() {
|
62009
|
+
const nextPlaylist = this.selectPlaylist();
|
62010
|
+
this.pauseLoading(); // Switch audio and text track playlists if necessary in DASH
|
62011
|
+
|
62012
|
+
if (this.contentSteeringController_.manifestType_ === 'DASH') {
|
62013
|
+
this.switchMediaForDASHContentSteering_();
|
62014
|
+
}
|
62015
|
+
this.switchMedia_(nextPlaylist, 'content-steering');
|
62016
|
+
}
|
61447
62017
|
}
|
61448
62018
|
|
61449
62019
|
/**
|
@@ -62192,8 +62762,8 @@
|
|
62192
62762
|
const reloadSourceOnError = function (options) {
|
62193
62763
|
initPlugin(this, options);
|
62194
62764
|
};
|
62195
|
-
var version$4 = "3.
|
62196
|
-
var version$3 = "7.0.
|
62765
|
+
var version$4 = "3.7.0";
|
62766
|
+
var version$3 = "7.0.1";
|
62197
62767
|
var version$2 = "1.2.2";
|
62198
62768
|
var version$1 = "7.1.0";
|
62199
62769
|
var version = "4.0.1";
|