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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/assets/themes/j1/core/js/template.js +0 -12
  3. data/assets/themes/j1/core/js/template.min.js.map +1 -1
  4. data/assets/themes/j1/modules/iframeResizer/README.md +105 -105
  5. data/assets/themes/j1/modules/lightGallery/js/plugins/lg-video.js +30 -5
  6. data/assets/themes/j1/modules/videojs/assets/material-icons.woff +0 -0
  7. data/assets/themes/j1/modules/videojs/assets/material-icons.woff2 +0 -0
  8. data/assets/themes/j1/modules/videojs/css/plugins/zoom.css +96 -0
  9. data/assets/themes/j1/modules/videojs/css/plugins/zoom.min.css +21 -0
  10. data/assets/themes/j1/modules/videojs/css/themes/city.css +1 -1
  11. data/assets/themes/j1/modules/videojs/css/themes/city.min.css +1 -1
  12. data/assets/themes/j1/modules/videojs/css/themes/fantasy.css +1 -1
  13. data/assets/themes/j1/modules/videojs/css/themes/fantasy.min.css +1 -1
  14. data/assets/themes/j1/modules/videojs/css/themes/forest.css +1 -1
  15. data/assets/themes/j1/modules/videojs/css/themes/forest.min.css +1 -1
  16. data/assets/themes/j1/modules/videojs/css/themes/sea.css +1 -1
  17. data/assets/themes/j1/modules/videojs/css/themes/sea.min.css +1 -1
  18. data/assets/themes/j1/modules/videojs/css/themes/uno.css +16 -18
  19. data/assets/themes/j1/modules/videojs/css/themes/uno.min.css +3 -2
  20. data/assets/themes/j1/modules/videojs/css/{video-js.css → video.css} +9 -7
  21. data/assets/themes/j1/modules/videojs/css/{video-js.min.css → video.min.css} +2 -2
  22. data/assets/themes/j1/modules/videojs/js/plugins/zoom.js +377 -0
  23. data/assets/themes/j1/modules/videojs/js/plugins/zoom.min.js +21 -0
  24. data/assets/themes/j1/modules/videojs/js/video.js +641 -71
  25. data/assets/themes/j1/modules/videojs/js/video.min.js +10 -9
  26. data/lib/j1/version.rb +1 -1
  27. data/lib/starter_web/README.md +5 -5
  28. data/lib/starter_web/_config.yml +1 -1
  29. data/lib/starter_web/_data/modules/gallery.yml +7 -2
  30. data/lib/starter_web/_data/resources.yml +4 -2
  31. data/lib/starter_web/_data/templates/feed.xml +1 -1
  32. data/lib/starter_web/_plugins/asciidoctor/videojs-block.rb +3 -2
  33. data/lib/starter_web/_plugins/index/lunr.rb +3 -1
  34. data/lib/starter_web/assets/videos/gallery/video_james_carpool_caraoke.1920.jpg +0 -0
  35. data/lib/starter_web/package.json +1 -1
  36. data/lib/starter_web/pages/public/tools/previewer/preview_docsearch.adoc +92 -92
  37. data/lib/starter_web/pages/public/tools/previewer/preview_iframer.adoc +106 -106
  38. metadata +11 -4
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Video.js 8.6.0 <http://videojs.com/>
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.0";
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} type
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 EventTarget~EventListener
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
- * @private
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|string}
4638
- * The width when getting, zero if there is no width. Can be a string
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|string}
4656
- * The width when getting, zero if there is no width. Can be a string
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 {Event} event
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
- * @private
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
- * @private
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 {AudioTrack[]} [tracks=[]]
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
- if (cue.constructor && cue.constructor.name !== 'VTTCue') {
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} Tech~SourceObject
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 {Tech~SourceObject} source
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 {Tech~SourceObject} srcObj
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 {Tech~SourceObject} source
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 {Event} event
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 {Event} event
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_ && event.clientX && event.clientY) {
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 {Event} event
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 {Event} event
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 {Event} event
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 recomended)
22386
- * 2. By using it directly via `videojs.players.example_video_1;`
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
- this.error(error);
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 {Event} event
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 {Plugin~PluginEventHash} hash
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 {Plugin~PluginEventHash}
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 Plugin~PluginEventHash|PluginEventHash}.
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 {Plugin~PluginEventHash}
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 {Plugin~PluginEventHash}
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 {Plugin~PluginEventHash}
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 {Plugin~PluginEventHash}
27722
+ * @type {PluginEventHash}
27697
27723
  */
27698
27724
 
27699
27725
  /**
27700
- * @typedef {Object} Plugin~PluginEventHash
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.6.0 @license Apache-2.0 */
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
- const seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd, liveEdgePadding);
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
- this.refreshMedia_(this.media().id);
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; // Use the TextDecoder if one was created for this service
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
- charCodeArray = [currentByte, nextByte];
45091
- i++;
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
- charCodeArray = [currentByte];
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 || !data.byteLength || !segment.transmuxer) {
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
- // Since we're on the final non-excluded playlist, and we're about to exclude
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.6.0";
62196
- var version$3 = "7.0.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";