rx-player 3.31.0 → 3.31.1-dev.2023062700

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rx-player",
3
3
  "author": "Canal+",
4
- "version": "3.31.0",
4
+ "version": "3.31.1-dev.2023062700",
5
5
  "description": "Canal+ HTML5 Video Player",
6
6
  "main": "./dist/rx-player.js",
7
7
  "keywords": [
@@ -1,7 +1,7 @@
1
1
  sonar.projectKey=rx-player
2
2
  sonar.organization=rx-player
3
3
  sonar.projectName=rx-player
4
- sonar.projectVersion=3.31.0
4
+ sonar.projectVersion=3.31.1-dev.2023062700
5
5
  sonar.sources=./src,./demo,./tests
6
6
  sonar.exclusions=demo/full/bundle.js,demo/standalone/lib.js,demo/bundle.js
7
7
  sonar.host.url=https://sonarcloud.io
@@ -370,7 +370,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
370
370
  // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
371
371
  videoElement.preload = "auto";
372
372
 
373
- this.version = /* PLAYER_VERSION */"3.31.0";
373
+ this.version = /* PLAYER_VERSION */"3.31.1-dev.2023062700";
374
374
  this.log = log;
375
375
  this.state = "STOPPED";
376
376
  this.videoElement = videoElement;
@@ -781,6 +781,8 @@ class Player extends EventEmitter<IPublicAPIEvent> {
781
781
  this.stop();
782
782
  this._priv_currentError = null;
783
783
  throw new Error("DirectFile feature not activated in your build.");
784
+ } else if (isNullOrUndefined(url)) {
785
+ throw new Error("No URL for a DirectFile content");
784
786
  }
785
787
  mediaElementTrackChoiceManager =
786
788
  this._priv_initializeMediaElementTrackChoiceManager(
@@ -3088,7 +3090,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
3088
3090
  return mediaElementTrackChoiceManager;
3089
3091
  }
3090
3092
  }
3091
- Player.version = /* PLAYER_VERSION */"3.31.0";
3093
+ Player.version = /* PLAYER_VERSION */"3.31.1-dev.2023062700";
3092
3094
 
3093
3095
  /** Every events sent by the RxPlayer's public API. */
3094
3096
  interface IPublicAPIEvent {
@@ -26,6 +26,7 @@ import {
26
26
  IKeySystemOption,
27
27
  IPlayerError,
28
28
  } from "../../public_types";
29
+ import assert from "../../utils/assert";
29
30
  import createSharedReference, {
30
31
  IReadOnlySharedReference,
31
32
  } from "../../utils/reference";
@@ -39,35 +40,68 @@ import initializeContentDecryption from "./utils/initialize_content_decryption";
39
40
  import RebufferingController from "./utils/rebuffering_controller";
40
41
  import listenToMediaError from "./utils/throw_on_media_error";
41
42
 
43
+ /**
44
+ * `ContentIntializer` which will load contents by putting their URL in the
45
+ * `src` attribute of the given HTMLMediaElement.
46
+ *
47
+ * Because such contents are mainly loaded by the browser, those (called
48
+ * "directfile" contents in the RxPlayer) needs a simpler logic in-JS when
49
+ * compared to a content that relies on the MSE API.
50
+ *
51
+ * @class DirectFileContentInitializer
52
+ */
42
53
  export default class DirectFileContentInitializer extends ContentInitializer {
54
+ /**
55
+ * Initial options given to the `DirectFileContentInitializer`.
56
+ */
43
57
  private _settings : IDirectFileOptions;
58
+ /**
59
+ * Allows to abort and clean everything the `DirectFileContentInitializer` is
60
+ * doing.
61
+ */
44
62
  private _initCanceller : TaskCanceller;
45
63
 
64
+ /**
65
+ * Creates a new `DirectFileContentInitializer` linked to the given settings.
66
+ * @param {Object} settings
67
+ */
46
68
  constructor(settings : IDirectFileOptions) {
47
69
  super();
48
70
  this._settings = settings;
49
71
  this._initCanceller = new TaskCanceller();
50
72
  }
51
73
 
52
- public prepare(): void {
74
+ /**
75
+ * "Prepare" content so it can later be played by calling `start`.
76
+ */
77
+ public prepare() : void {
53
78
  return; // Directfile contents do not have any preparation
54
79
  }
55
80
 
81
+ /**
82
+ * Start playback of the content linked to this `DirectFileContentInitializer`
83
+ * on the given `HTMLMediaElement` and its associated `PlaybackObserver`.
84
+ * @param {HTMLMediaElement} mediaElement - HTMLMediaElement on which the
85
+ * content will be played.
86
+ * @param {Object} playbackObserver - Object regularly emitting playback
87
+ * information.
88
+ */
56
89
  public start(
57
90
  mediaElement : HTMLMediaElement,
58
91
  playbackObserver : PlaybackObserver
59
- ): void {
92
+ ) : void {
60
93
  const cancelSignal = this._initCanceller.signal;
61
94
  const { keySystems, speed, url } = this._settings;
62
95
 
63
96
  clearElementSrc(mediaElement);
64
97
 
65
- if (url == null) {
66
- throw new Error("No URL for a DirectFile content");
67
- }
68
-
98
+ /**
99
+ * Create dummy encryption data emitter, as those are not sent from the
100
+ * RxPlayer for directfile contents.
101
+ */
69
102
  const decryptionRef = createSharedReference(null);
70
103
  decryptionRef.finish();
104
+
71
105
  const drmInitRef =
72
106
  initializeContentDecryption(mediaElement, keySystems, decryptionRef, {
73
107
  onError: (err) => this._onFatalError(err),
@@ -99,7 +133,7 @@ export default class DirectFileContentInitializer extends ContentInitializer {
99
133
 
100
134
  drmInitRef.onUpdate((evt, stopListeningToDrmUpdates) => {
101
135
  if (evt.initializationState.type === "uninitialized") {
102
- return;
136
+ return; // nothing done yet
103
137
  }
104
138
  stopListeningToDrmUpdates();
105
139
 
@@ -109,39 +143,57 @@ export default class DirectFileContentInitializer extends ContentInitializer {
109
143
  cancelSignal.register(() => {
110
144
  clearElementSrc(mediaElement);
111
145
  });
146
+
112
147
  if (evt.initializationState.type === "awaiting-media-link") {
113
148
  evt.initializationState.value.isMediaLinked.setValue(true);
114
149
  drmInitRef.onUpdate((newDrmStatus, stopListeningToDrmUpdatesAgain) => {
115
150
  if (newDrmStatus.initializationState.type === "initialized") {
116
151
  stopListeningToDrmUpdatesAgain();
117
152
  this._seekAndPlay(mediaElement, playbackObserver);
118
- return;
119
153
  }
120
154
  }, { emitCurrentValue: true, clearSignal: cancelSignal });
121
155
  } else {
156
+ assert(evt.initializationState.type === "initialized");
122
157
  this._seekAndPlay(mediaElement, playbackObserver);
123
- return;
124
158
  }
125
159
  }, { emitCurrentValue: true, clearSignal: cancelSignal });
126
160
  }
127
161
 
162
+ /**
163
+ * Update URL this `ContentIntializer` depends on.
164
+ * @param {Array.<string>|undefined} _urls
165
+ * @param {boolean} _refreshNow
166
+ */
128
167
  public updateContentUrls(_urls : string[] | undefined, _refreshNow : boolean) : void {
129
168
  throw new Error("Cannot update content URL of directfile contents");
130
169
  }
131
170
 
132
- public dispose(): void {
171
+ /**
172
+ * Stop content and free all resources linked to this `ContentIntializer`.
173
+ */
174
+ public dispose() : void {
133
175
  this._initCanceller.cancel();
134
176
  }
135
177
 
136
- private _onFatalError(err : unknown) {
178
+ /**
179
+ * Logic performed when a fatal error was triggered.
180
+ * @param {*} err - The fatal error in question.
181
+ */
182
+ private _onFatalError(err : unknown) : void {
137
183
  this._initCanceller.cancel();
138
184
  this.trigger("error", err);
139
185
  }
140
186
 
187
+ /**
188
+ * Perform the initial seek (to begin playback at an initially-calculated
189
+ * position based on settings) and auto-play if needed when loaded.
190
+ * @param {HTMLMediaElement} mediaElement
191
+ * @param {Object} playbackObserver
192
+ */
141
193
  private _seekAndPlay(
142
194
  mediaElement : HTMLMediaElement,
143
195
  playbackObserver : PlaybackObserver
144
- ) {
196
+ ) : void {
145
197
  const cancelSignal = this._initCanceller.signal;
146
198
  const { autoPlay, startAt } = this._settings;
147
199
  const initialTime = () => {
@@ -177,7 +229,7 @@ export default class DirectFileContentInitializer extends ContentInitializer {
177
229
  /**
178
230
  * calculate initial time as a position in seconds.
179
231
  * @param {HTMLMediaElement} mediaElement
180
- * @param {Object|undefined} startAt
232
+ * @param {Object|undefined} [startAt]
181
233
  * @returns {number}
182
234
  */
183
235
  function getDirectFileInitialTime(
@@ -219,11 +271,19 @@ function getDirectFileInitialTime(
219
271
  return 0;
220
272
  }
221
273
 
222
- // Argument used by `initializeDirectfileContent`
274
+ /** Options used by the `DirectFileContentInitializer` */
223
275
  export interface IDirectFileOptions {
276
+ /** If `true` we will play right after the content is considered "loaded". */
224
277
  autoPlay : boolean;
278
+ /**
279
+ * Encryption-related settings. Can be left as an empty array if the content
280
+ * isn't encrypted.
281
+ */
225
282
  keySystems : IKeySystemOption[];
283
+ /** Communicate the playback rate wanted by the user. */
226
284
  speed : IReadOnlySharedReference<number>;
285
+ /** Optional initial position to start at. */
227
286
  startAt? : IInitialTimeOptions | undefined;
228
- url? : string | undefined;
287
+ /** URL that should be played. */
288
+ url : string;
229
289
  }
@@ -54,9 +54,9 @@ export default function initializeContentDecryption(
54
54
  return;
55
55
  }
56
56
  stopListening();
57
- log.error("Init: Encrypted event but EME feature not activated");
57
+ log.error("Init: Encrypted event but no `keySystems` given");
58
58
  const err = new EncryptedMediaError("MEDIA_IS_ENCRYPTED_ERROR",
59
- "EME feature not activated.");
59
+ "no `keySystems` given.");
60
60
  callbacks.onError(err);
61
61
  }, { clearSignal: cancelSignal });
62
62
  const ref = createSharedReference({
@@ -22,7 +22,7 @@ import Manifest, {
22
22
  } from "../../../../manifest";
23
23
  import isNullOrUndefined from "../../../../utils/is_null_or_undefined";
24
24
  import { IReadOnlyPlaybackObserver } from "../../../api";
25
- import {
25
+ import SegmentBuffersStore, {
26
26
  IBufferedChunk,
27
27
  IEndOfSegmentOperation,
28
28
  SegmentBuffer,
@@ -218,6 +218,7 @@ function getRangeOfNeededSegments(
218
218
  // avoid ending the last Period - and by extension the content - with a
219
219
  // segment which isn't the last one.
220
220
  if (!isNullOrUndefined(lastIndexPosition) &&
221
+ SegmentBuffersStore.isNative(content.adaptation.type) &&
221
222
  initialWantedTime >= lastIndexPosition &&
222
223
  representationIndex.isInitialized() &&
223
224
  representationIndex.isFinished() &&