rx-player 3.31.0-dev.2023052200 → 3.31.0

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-dev.2023052200",
4
+ "version": "3.31.0",
5
5
  "description": "Canal+ HTML5 Video Player",
6
6
  "main": "./dist/rx-player.js",
7
7
  "keywords": [
@@ -80,39 +80,39 @@
80
80
  "next-tick": "1.1.0"
81
81
  },
82
82
  "devDependencies": {
83
- "@babel/core": "7.21.4",
84
- "@babel/plugin-transform-runtime": "7.21.4",
85
- "@babel/preset-env": "7.21.4",
86
- "@babel/preset-react": "7.18.6",
87
- "@types/chai": "4.3.4",
88
- "@types/jest": "29.5.0",
83
+ "@babel/core": "7.22.5",
84
+ "@babel/plugin-transform-runtime": "7.22.5",
85
+ "@babel/preset-env": "7.22.5",
86
+ "@babel/preset-react": "7.22.5",
87
+ "@types/chai": "4.3.5",
88
+ "@types/jest": "29.5.2",
89
89
  "@types/mocha": "10.0.1",
90
- "@types/node": "18.15.11",
91
- "@types/react": "18.0.33",
92
- "@types/react-dom": "18.0.11",
93
- "@types/sinon": "10.0.13",
94
- "@typescript-eslint/eslint-plugin": "5.57.1",
95
- "@typescript-eslint/eslint-plugin-tslint": "5.57.1",
96
- "@typescript-eslint/parser": "5.57.1",
90
+ "@types/node": "20.3.1",
91
+ "@types/react": "18.2.12",
92
+ "@types/react-dom": "18.2.5",
93
+ "@types/sinon": "10.0.15",
94
+ "@typescript-eslint/eslint-plugin": "5.59.11",
95
+ "@typescript-eslint/eslint-plugin-tslint": "5.59.11",
96
+ "@typescript-eslint/parser": "5.59.11",
97
97
  "arraybuffer-loader": "1.0.8",
98
98
  "babel-loader": "9.1.2",
99
99
  "chai": "4.3.7",
100
- "core-js": "3.30.0",
101
- "docgen.ico": "^0.2.2",
102
- "esbuild": "0.17.15",
103
- "eslint": "8.37.0",
100
+ "core-js": "3.31.0",
101
+ "docgen.ico": "^0.2.3",
102
+ "esbuild": "0.18.2",
103
+ "eslint": "8.42.0",
104
104
  "eslint-plugin-ban": "1.6.0",
105
105
  "eslint-plugin-import": "2.27.5",
106
- "eslint-plugin-jsdoc": "40.1.1",
106
+ "eslint-plugin-jsdoc": "46.2.6",
107
107
  "eslint-plugin-react": "7.32.2",
108
108
  "esm": "3.2.25",
109
109
  "express": "4.18.2",
110
110
  "github-buttons": "2.27.0",
111
- "html-entities": "2.3.3",
111
+ "html-entities": "2.3.6",
112
112
  "jest": "29.5.0",
113
113
  "jest-environment-jsdom": "29.5.0",
114
- "karma": "6.4.1",
115
- "karma-chrome-launcher": "3.1.1",
114
+ "karma": "6.4.2",
115
+ "karma-chrome-launcher": "3.2.0",
116
116
  "karma-firefox-launcher": "2.1.2",
117
117
  "karma-mocha": "2.0.1",
118
118
  "karma-webpack": "5.0.0",
@@ -122,17 +122,17 @@
122
122
  "react": "18.2.0",
123
123
  "react-dom": "18.2.0",
124
124
  "regenerator-runtime": "0.13.11",
125
- "rimraf": "4.4.1",
126
- "semver": "7.3.8",
127
- "sinon": "15.0.3",
128
- "terser-webpack-plugin": "5.3.7",
125
+ "rimraf": "5.0.1",
126
+ "semver": "7.5.1",
127
+ "sinon": "15.1.2",
128
+ "terser-webpack-plugin": "5.3.9",
129
129
  "ts-jest": "29.1.0",
130
- "ts-loader": "9.4.2",
130
+ "ts-loader": "9.4.3",
131
131
  "tslint": "6.1.3",
132
- "typescript": "5.0.3",
133
- "webpack": "5.77.0",
134
- "webpack-bundle-analyzer": "4.8.0",
135
- "webpack-cli": "5.0.1"
132
+ "typescript": "5.1.3",
133
+ "webpack": "5.86.0",
134
+ "webpack-bundle-analyzer": "4.9.0",
135
+ "webpack-cli": "5.1.4"
136
136
  },
137
137
  "scripts-list": {
138
138
  "Build a demo page (e.g. to test a code change)": {
@@ -22,7 +22,7 @@ const { spawn } = require("child_process");
22
22
  const fs = require("fs/promises");
23
23
  const os = require("os");
24
24
  const path = require("path");
25
- const rimraf = require("rimraf");
25
+ const { rimraf } = require("rimraf");
26
26
 
27
27
  const BUILD_DIR_FROM_ROOT = "dist/_esm5.processed";
28
28
 
@@ -66,8 +66,8 @@ function fastDemoBuild(options) {
66
66
  const { errors, warnings } = result;
67
67
  console.log(`\x1b[33m[${getHumanReadableHours()}]\x1b[0m ` +
68
68
  `Demo re-built with ${errors.length} error(s) and ` +
69
- ` ${warnings.length} warning(s) ` +
70
- `(in ${stats.endTime - stats.startTime} ms).`);
69
+ ` ${warnings.length} warning(s) `);
70
+ return;
71
71
  }
72
72
  console.log(`\x1b[32m[${getHumanReadableHours()}]\x1b[0m ` +
73
73
  "Demo built!");
@@ -79,6 +79,7 @@ function fastDemoBuild(options) {
79
79
  esbuild.context({
80
80
  entryPoints: [path.join(__dirname, "../demo/full/scripts/index.tsx")],
81
81
  bundle: true,
82
+ target: "es2017",
82
83
  minify,
83
84
  outfile: path.join(__dirname, "../demo/full/bundle.js"),
84
85
  plugins: [consolePlugin],
@@ -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-dev.2023052200
4
+ sonar.projectVersion=3.31.0
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
@@ -69,6 +69,7 @@ import {
69
69
  IVideoTrackPreference,
70
70
  } from "../../public_types";
71
71
  import areArraysOfNumbersEqual from "../../utils/are_arrays_of_numbers_equal";
72
+ import arrayIncludes from "../../utils/array_includes";
72
73
  import assert from "../../utils/assert";
73
74
  import EventEmitter, {
74
75
  IEventPayload,
@@ -121,9 +122,9 @@ import MediaElementTrackChoiceManager from "./tracks_management/media_element_tr
121
122
  import TrackChoiceManager from "./tracks_management/track_choice_manager";
122
123
  import {
123
124
  constructPlayerStateReference,
125
+ emitPlayPauseEvents,
124
126
  emitSeekEvents,
125
127
  isLoadedState,
126
- // emitSeekEvents,
127
128
  PLAYER_STATES,
128
129
  } from "./utils";
129
130
 
@@ -307,6 +308,11 @@ class Player extends EventEmitter<IPublicAPIEvent> {
307
308
  reloadPosition?: number;
308
309
  };
309
310
 
311
+ /**
312
+ * Store last value of autoPlay, from the last load or reload.
313
+ */
314
+ private _priv_lastAutoPlay: boolean;
315
+
310
316
  /** All possible Error types emitted by the RxPlayer. */
311
317
  static get ErrorTypes() : Record<IErrorType, IErrorType> {
312
318
  return ErrorTypes;
@@ -364,7 +370,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
364
370
  // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
365
371
  videoElement.preload = "auto";
366
372
 
367
- this.version = /* PLAYER_VERSION */"3.31.0-dev.2023052200";
373
+ this.version = /* PLAYER_VERSION */"3.31.0";
368
374
  this.log = log;
369
375
  this.state = "STOPPED";
370
376
  this.videoElement = videoElement;
@@ -481,6 +487,8 @@ class Player extends EventEmitter<IPublicAPIEvent> {
481
487
  this._priv_preferredVideoTracks = preferredVideoTracks;
482
488
 
483
489
  this._priv_reloadingMetadata = {};
490
+
491
+ this._priv_lastAutoPlay = false;
484
492
  }
485
493
 
486
494
  /**
@@ -551,6 +559,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
551
559
  log.info("API: Calling loadvideo", options.url, options.transport);
552
560
  this._priv_reloadingMetadata = { options };
553
561
  this._priv_initializeContentPlayback(options);
562
+ this._priv_lastAutoPlay = options.autoPlay;
554
563
  }
555
564
 
556
565
  /**
@@ -839,11 +848,12 @@ class Player extends EventEmitter<IPublicAPIEvent> {
839
848
  log.warn("API: Sending warning:", formattedError);
840
849
  this.trigger("warning", formattedError);
841
850
  });
842
- initializer.addEventListener("reloadingMediaSource", () => {
851
+ initializer.addEventListener("reloadingMediaSource", (payload) => {
843
852
  contentInfos.segmentBuffersStore = null;
844
853
  if (contentInfos.trackChoiceManager !== null) {
845
854
  contentInfos.trackChoiceManager.resetPeriods();
846
855
  }
856
+ this._priv_lastAutoPlay = payload.autoPlay;
847
857
  });
848
858
  initializer.addEventListener("inbandEvents", (inbandEvents) =>
849
859
  this.trigger("inbandEvents", inbandEvents));
@@ -946,6 +956,53 @@ class Player extends EventEmitter<IPublicAPIEvent> {
946
956
  }
947
957
  };
948
958
 
959
+ /**
960
+ * `TaskCanceller` allowing to stop emitting `"play"` and `"pause"`
961
+ * events.
962
+ * `null` when such events are not emitted currently.
963
+ */
964
+ let playPauseEventsCanceller : TaskCanceller | null = null;
965
+
966
+ /**
967
+ * Callback emitting `"play"` and `"pause`" events once the content is
968
+ * loaded, starting from the state indicated in argument.
969
+ * @param {boolean} willAutoPlay - If `false`, we're currently paused.
970
+ */
971
+ const triggerPlayPauseEventsWhenReady = (willAutoPlay: boolean) => {
972
+ if (playPauseEventsCanceller !== null) {
973
+ playPauseEventsCanceller.cancel(); // cancel previous logic
974
+ playPauseEventsCanceller = null;
975
+ }
976
+ playerStateRef.onUpdate((val, stopListeningToStateUpdates) => {
977
+ if (!isLoadedState(val)) {
978
+ return; // content not loaded yet: no event
979
+ }
980
+ stopListeningToStateUpdates();
981
+ if (playPauseEventsCanceller !== null) {
982
+ playPauseEventsCanceller.cancel();
983
+ }
984
+ playPauseEventsCanceller = new TaskCanceller();
985
+ playPauseEventsCanceller.linkToSignal(currentContentCanceller.signal);
986
+ if (willAutoPlay !== !videoElement.paused) {
987
+ // paused status is not at the expected value on load: emit event
988
+ if (videoElement.paused) {
989
+ this.trigger("pause", null);
990
+ } else {
991
+ this.trigger("play", null);
992
+ }
993
+ }
994
+ emitPlayPauseEvents(videoElement,
995
+ () => this.trigger("play", null),
996
+ () => this.trigger("pause", null),
997
+ currentContentCanceller.signal);
998
+ }, { emitCurrentValue: false, clearSignal: currentContentCanceller.signal });
999
+ };
1000
+
1001
+ triggerPlayPauseEventsWhenReady(autoPlay);
1002
+ initializer.addEventListener("reloadingMediaSource", (payload) => {
1003
+ triggerPlayPauseEventsWhenReady(payload.autoPlay);
1004
+ });
1005
+
949
1006
  /**
950
1007
  * `TaskCanceller` allowing to stop emitting `"seeking"` and `"seeked"`
951
1008
  * events.
@@ -1109,6 +1166,42 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1109
1166
  return this.state;
1110
1167
  }
1111
1168
 
1169
+ /**
1170
+ * Returns true if a content is loaded.
1171
+ * @returns {Boolean} - `true` if a content is loaded, `false` otherwise.
1172
+ */
1173
+ isContentLoaded() : boolean {
1174
+ return !arrayIncludes(["LOADING", "RELOADING", "STOPPED"], this.state);
1175
+ }
1176
+
1177
+ /**
1178
+ * Returns true if the player is buffering.
1179
+ * @returns {Boolean} - `true` if the player is buffering, `false` otherwise.
1180
+ */
1181
+ isBuffering() : boolean {
1182
+ return arrayIncludes(["BUFFERING", "SEEKING", "LOADING", "RELOADING"], this.state);
1183
+ }
1184
+
1185
+ /**
1186
+ * Returns the play/pause status of the player :
1187
+ * - when `LOADING` or `RELOADING`, returns the scheduled play/pause condition
1188
+ * for when loading is over,
1189
+ * - in other states, returns the `<video>` element .paused value,
1190
+ * - if the player is disposed, returns `true`.
1191
+ * @returns {Boolean} - `true` if the player is paused or will be after loading,
1192
+ * `false` otherwise.
1193
+ */
1194
+ isPaused() : boolean {
1195
+ if (this.videoElement) {
1196
+ if (arrayIncludes(["LOADING", "RELOADING"], this.state)) {
1197
+ return !this._priv_lastAutoPlay;
1198
+ } else {
1199
+ return this.videoElement.paused;
1200
+ }
1201
+ }
1202
+ return true;
1203
+ }
1204
+
1112
1205
  /**
1113
1206
  * Returns true if both:
1114
1207
  * - a content is loaded
@@ -1288,6 +1381,15 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1288
1381
  return this.videoElement.currentTime;
1289
1382
  }
1290
1383
 
1384
+ /**
1385
+ * Returns the last stored content position, in seconds.
1386
+ *
1387
+ * @returns {number|undefined}
1388
+ */
1389
+ getLastStoredContentPosition() : number|undefined {
1390
+ return this._priv_reloadingMetadata.reloadPosition;
1391
+ }
1392
+
1291
1393
  /**
1292
1394
  * Returns the current playback rate at which the video plays.
1293
1395
  * @returns {Number}
@@ -2986,7 +3088,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2986
3088
  return mediaElementTrackChoiceManager;
2987
3089
  }
2988
3090
  }
2989
- Player.version = /* PLAYER_VERSION */"3.31.0-dev.2023052200";
3091
+ Player.version = /* PLAYER_VERSION */"3.31.0";
2990
3092
 
2991
3093
  /** Every events sent by the RxPlayer's public API. */
2992
3094
  interface IPublicAPIEvent {
@@ -3011,6 +3113,8 @@ interface IPublicAPIEvent {
3011
3113
  availableTextTracksChange : IAvailableTextTrack[];
3012
3114
  availableVideoTracksChange : IAvailableVideoTrack[];
3013
3115
  decipherabilityUpdate : IDecipherabilityUpdateContent[];
3116
+ play: null;
3117
+ pause: null;
3014
3118
  seeking : null;
3015
3119
  seeked : null;
3016
3120
  streamEvent : IStreamEvent;
@@ -70,6 +70,32 @@ export function emitSeekEvents(
70
70
  }, { includeLastObservation: true, clearSignal: cancelSignal });
71
71
  }
72
72
 
73
+ /**
74
+ * @param {HTMLMediaElement} mediaElement
75
+ * @param {function} onPlay - Callback called when a play operation has started
76
+ * on `mediaElement`.
77
+ * @param {function} onPause - Callback called when a pause operation has
78
+ * started on `mediaElement`.
79
+ * @param {Object} cancelSignal - When triggered, stop calling callbacks and
80
+ * remove all listeners this function has registered.
81
+ */
82
+ export function emitPlayPauseEvents(
83
+ mediaElement : HTMLMediaElement | null,
84
+ onPlay: () => void,
85
+ onPause: () => void,
86
+ cancelSignal : CancellationSignal
87
+ ) : void {
88
+ if (cancelSignal.isCancelled() || mediaElement === null) {
89
+ return ;
90
+ }
91
+ mediaElement.addEventListener("play", onPlay);
92
+ mediaElement.addEventListener("pause", onPause);
93
+ cancelSignal.register(() => {
94
+ mediaElement.removeEventListener("play", onPlay);
95
+ mediaElement.removeEventListener("pause", onPause);
96
+ });
97
+ }
98
+
73
99
  /** Player state dictionnary. */
74
100
  export const enum PLAYER_STATES {
75
101
  STOPPED = "STOPPED",
@@ -111,7 +111,12 @@ export default function SessionEventsListener(
111
111
  if (isNullOrUndefined(licenseObject)) {
112
112
  log.info("DRM: No license given, skipping session.update");
113
113
  } else {
114
- return updateSessionWithMessage(session, licenseObject);
114
+ try {
115
+ return updateSessionWithMessage(session, licenseObject);
116
+ } catch (err) {
117
+ manualCanceller.cancel();
118
+ callbacks.onError(err);
119
+ }
115
120
  }
116
121
  })
117
122
  .catch((err : unknown) => {
@@ -356,7 +356,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
356
356
  if (initCanceller.isUsed()) {
357
357
  return;
358
358
  }
359
- triggerEvent("reloadingMediaSource", null);
359
+ triggerEvent("reloadingMediaSource", reloadOrder);
360
360
  if (initCanceller.isUsed()) {
361
361
  return;
362
362
  }
@@ -117,7 +117,15 @@ export interface IContentInitializerEvents {
117
117
  * Event sent when we're starting attach a new MediaSource to the media element
118
118
  * (after removing the previous one).
119
119
  */
120
- reloadingMediaSource: null;
120
+ reloadingMediaSource: {
121
+ /** The position we're reloading at, in seconds. */
122
+ position: number;
123
+ /**
124
+ * If `true`, we'll play directly after finishing the reloading operation.
125
+ * If `false`, we'll be paused after it.
126
+ */
127
+ autoPlay: boolean;
128
+ };
121
129
  /** Event sent after the player stalled. */
122
130
  stalled : IStallingSituation;
123
131
  /** Event sent when the player goes out of a stalling situation. */