rx-player 4.5.0-dev.2026041501 → 4.5.0-dev.2026043000

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. package/CHANGELOG.md +4 -1
  2. package/VERSION +1 -1
  3. package/dist/commonjs/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  4. package/dist/commonjs/__GENERATED_CODE/embedded_worker.js +1 -1
  5. package/dist/commonjs/core/entry/content_time_boundaries_observer.d.ts.map +1 -1
  6. package/dist/commonjs/core/entry/content_time_boundaries_observer.js +18 -4
  7. package/dist/commonjs/errors/index.d.ts +2 -2
  8. package/dist/commonjs/errors/index.d.ts.map +1 -1
  9. package/dist/commonjs/errors/index.js +2 -1
  10. package/dist/commonjs/errors/media_error.d.ts +19 -4
  11. package/dist/commonjs/errors/media_error.d.ts.map +1 -1
  12. package/dist/commonjs/errors/media_error.js +29 -0
  13. package/dist/commonjs/main_thread/api/public_api.js +2 -2
  14. package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  15. package/dist/commonjs/main_thread/init/media_source_content_initializer.js +1 -4
  16. package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  17. package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
  18. package/dist/es2017/core/entry/content_time_boundaries_observer.d.ts.map +1 -1
  19. package/dist/es2017/core/entry/content_time_boundaries_observer.js +18 -4
  20. package/dist/es2017/errors/index.d.ts +2 -2
  21. package/dist/es2017/errors/index.d.ts.map +1 -1
  22. package/dist/es2017/errors/index.js +2 -2
  23. package/dist/es2017/errors/media_error.d.ts +19 -4
  24. package/dist/es2017/errors/media_error.d.ts.map +1 -1
  25. package/dist/es2017/errors/media_error.js +28 -0
  26. package/dist/es2017/main_thread/api/public_api.js +2 -2
  27. package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  28. package/dist/es2017/main_thread/init/media_source_content_initializer.js +2 -5
  29. package/dist/worker.js +5 -5
  30. package/package.json +2 -2
  31. package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
  32. package/src/core/entry/__tests__/content_time_boundaries_observer.test.ts +5 -0
  33. package/src/core/entry/content_time_boundaries_observer.ts +18 -4
  34. package/src/errors/__tests__/media_error.test.ts +106 -3
  35. package/src/errors/index.ts +2 -1
  36. package/src/errors/media_error.ts +63 -7
  37. package/src/main_thread/api/public_api.ts +2 -2
  38. package/src/main_thread/init/media_source_content_initializer.ts +3 -5
@@ -143,6 +143,11 @@ describe("ContentTimeBoundariesObserver", () => {
143
143
  expect(mockTrigger).toHaveBeenCalledWith("warning", expect.any(Object));
144
144
  const warning = mockTrigger.mock.calls[0][1];
145
145
  expect(warning.code).toBe("MEDIA_TIME_BEFORE_MANIFEST");
146
+ expect(warning.timeInfo).toEqual({
147
+ position: 5,
148
+ minPosition: 10,
149
+ maxPosition: 100,
150
+ });
146
151
  });
147
152
 
148
153
  it("should trigger warning when position is after manifest maximum", async () => {
@@ -113,20 +113,34 @@ export default class ContentTimeBoundariesObserver extends EventEmitter<IContent
113
113
  playbackObserver.listen(
114
114
  ({ position }) => {
115
115
  const wantedPosition = position.getWanted();
116
- if (wantedPosition < manifest.getMinimumSafePosition()) {
116
+ const minimumPosition = manifest.getMinimumSafePosition();
117
+ const maximumPosition = maximumPositionCalculator.getMaximumAvailablePosition();
118
+ if (wantedPosition < minimumPosition) {
117
119
  const warning = new MediaError(
118
120
  "MEDIA_TIME_BEFORE_MANIFEST",
119
121
  "The current position is behind the " +
120
122
  "earliest time announced in the Manifest.",
123
+ {
124
+ timeInfo: {
125
+ position: wantedPosition,
126
+ minPosition: minimumPosition,
127
+ maxPosition: maximumPosition,
128
+ },
129
+ },
121
130
  );
122
131
  this.trigger("warning", warning);
123
- } else if (
124
- wantedPosition > maximumPositionCalculator.getMaximumAvailablePosition()
125
- ) {
132
+ } else if (wantedPosition > maximumPosition) {
126
133
  const warning = new MediaError(
127
134
  "MEDIA_TIME_AFTER_MANIFEST",
128
135
  "The current position is after the latest " +
129
136
  "time announced in the Manifest.",
137
+ {
138
+ timeInfo: {
139
+ position: wantedPosition,
140
+ minPosition: minimumPosition,
141
+ maxPosition: maximumPosition,
142
+ },
143
+ },
130
144
  );
131
145
  this.trigger("warning", warning);
132
146
  }
@@ -1,10 +1,13 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import MediaError from "../media_error";
2
+ import type { ISerializedMediaError } from "../media_error";
3
+ import MediaError, { deserializeMediaError } from "../media_error";
3
4
 
4
5
  describe("errors - MediaError", () => {
5
6
  it("should format a MediaError", () => {
6
7
  const reason = "test";
7
- const mediaError = new MediaError("MEDIA_TIME_BEFORE_MANIFEST", reason);
8
+ const mediaError = new MediaError("MEDIA_TIME_BEFORE_MANIFEST", reason, {
9
+ timeInfo: { position: 5, minPosition: 10, maxPosition: 100 },
10
+ });
8
11
  expect(mediaError).toBeInstanceOf(Error);
9
12
  expect(mediaError.name).toBe("MediaError");
10
13
  expect(mediaError.type).toBe("MEDIA_ERROR");
@@ -15,7 +18,9 @@ describe("errors - MediaError", () => {
15
18
 
16
19
  it("should be able to set it as fatal", () => {
17
20
  const reason = "test";
18
- const mediaError = new MediaError("MEDIA_TIME_AFTER_MANIFEST", reason);
21
+ const mediaError = new MediaError("MEDIA_TIME_AFTER_MANIFEST", reason, {
22
+ timeInfo: { position: 5, minPosition: 10, maxPosition: 100 },
23
+ });
19
24
  mediaError.fatal = true;
20
25
  expect(mediaError).toBeInstanceOf(Error);
21
26
  expect(mediaError.name).toBe("MediaError");
@@ -36,4 +41,102 @@ describe("errors - MediaError", () => {
36
41
  expect(mediaError.fatal).toBe(true);
37
42
  expect(mediaError.message).toBe("MEDIA_ERR_NETWORK: test");
38
43
  });
44
+
45
+ it("should expose and serialize custom position metadata", () => {
46
+ const mediaError = new MediaError("MEDIA_TIME_BEFORE_MANIFEST", "test", {
47
+ timeInfo: {
48
+ position: 3,
49
+ minPosition: 10,
50
+ maxPosition: 20,
51
+ },
52
+ });
53
+
54
+ expect(mediaError.timeInfo).toEqual({
55
+ position: 3,
56
+ minPosition: 10,
57
+ maxPosition: 20,
58
+ });
59
+ expect(mediaError.serialize()).toEqual({
60
+ isSerializedError: true,
61
+ name: "MediaError",
62
+ code: "MEDIA_TIME_BEFORE_MANIFEST",
63
+ reason: "test",
64
+ tracks: undefined,
65
+ timeInfo: {
66
+ position: 3,
67
+ minPosition: 10,
68
+ maxPosition: 20,
69
+ },
70
+ });
71
+ });
72
+
73
+ it("should deserialize a serialized MediaError with timeInfo", () => {
74
+ const serializedError: ISerializedMediaError = {
75
+ isSerializedError: true,
76
+ name: "MediaError",
77
+ code: "MEDIA_TIME_BEFORE_MANIFEST",
78
+ reason: "test",
79
+ tracks: undefined,
80
+ timeInfo: {
81
+ position: 3,
82
+ minPosition: 10,
83
+ maxPosition: 20,
84
+ },
85
+ };
86
+
87
+ const mediaError = deserializeMediaError(serializedError);
88
+
89
+ expect(mediaError).toBeInstanceOf(MediaError);
90
+ expect(mediaError.code).toBe("MEDIA_TIME_BEFORE_MANIFEST");
91
+ expect(mediaError.message).toBe("MEDIA_TIME_BEFORE_MANIFEST: test");
92
+ expect(mediaError.timeInfo).toEqual(serializedError.timeInfo);
93
+ });
94
+
95
+ it("should deserialize a serialized MediaError with tracks", () => {
96
+ const serializedError: ISerializedMediaError = {
97
+ isSerializedError: true,
98
+ name: "MediaError",
99
+ code: "BUFFER_APPEND_ERROR",
100
+ reason: "test",
101
+ tracks: [
102
+ {
103
+ type: "audio",
104
+ track: {
105
+ id: "fra1",
106
+ audioDescription: false,
107
+ language: "fra",
108
+ normalized: "fra",
109
+ representations: [],
110
+ },
111
+ },
112
+ ],
113
+ timeInfo: undefined,
114
+ };
115
+
116
+ const mediaError = deserializeMediaError(serializedError);
117
+
118
+ expect(mediaError).toBeInstanceOf(MediaError);
119
+ expect(mediaError.code).toBe("BUFFER_APPEND_ERROR");
120
+ expect(mediaError.message).toBe("BUFFER_APPEND_ERROR: test");
121
+ expect(mediaError.tracksInfo).toEqual(serializedError.tracks);
122
+ });
123
+
124
+ it("should deserialize a serialized MediaError without metadata", () => {
125
+ const serializedError = {
126
+ isSerializedError: true as const,
127
+ name: "MediaError" as const,
128
+ code: "MEDIA_ERR_NETWORK" as const,
129
+ reason: "test",
130
+ tracks: undefined,
131
+ timeInfo: undefined,
132
+ };
133
+
134
+ const mediaError = deserializeMediaError(serializedError);
135
+
136
+ expect(mediaError).toBeInstanceOf(MediaError);
137
+ expect(mediaError.code).toBe("MEDIA_ERR_NETWORK");
138
+ expect(mediaError.message).toBe("MEDIA_ERR_NETWORK: test");
139
+ expect(mediaError.timeInfo).toBeUndefined();
140
+ expect(mediaError.tracksInfo).toBeUndefined();
141
+ });
39
142
  });
@@ -22,7 +22,7 @@ import { ErrorCodes, ErrorTypes, NetworkErrorTypes } from "./error_codes";
22
22
  import formatError from "./format_error";
23
23
  import isKnownError from "./is_known_error";
24
24
  import type { ISerializedMediaError } from "./media_error";
25
- import MediaError from "./media_error";
25
+ import MediaError, { deserializeMediaError } from "./media_error";
26
26
  import type { ISerializedNetworkError } from "./network_error";
27
27
  import NetworkError from "./network_error";
28
28
  import type { ISerializedOtherError } from "./other_error";
@@ -46,6 +46,7 @@ export {
46
46
  ErrorTypes,
47
47
  formatError,
48
48
  MediaError,
49
+ deserializeMediaError,
49
50
  NetworkError,
50
51
  OtherError,
51
52
  NetworkErrorTypes,
@@ -15,16 +15,27 @@
15
15
  */
16
16
 
17
17
  import type { ITaggedTrack } from "../manifest";
18
+ import assert from "../utils/assert";
18
19
  import type { IMediaErrorCode } from "./error_codes";
19
20
  import { ErrorTypes } from "./error_codes";
20
21
  import errorMessage from "./error_message";
21
22
 
22
- type ICodeWithAdaptationType =
23
+ export type ICodeWithAdaptationType =
23
24
  | "BUFFER_APPEND_ERROR"
24
25
  | "BUFFER_FULL_ERROR"
25
26
  | "NO_PLAYABLE_REPRESENTATION"
26
27
  | "MANIFEST_INCOMPATIBLE_CODECS_ERROR";
27
28
 
29
+ export type ICodeWithManifestPositionError =
30
+ | "MEDIA_TIME_BEFORE_MANIFEST"
31
+ | "MEDIA_TIME_AFTER_MANIFEST";
32
+
33
+ interface ITimeInfo {
34
+ position: number;
35
+ minPosition: number;
36
+ maxPosition: number;
37
+ }
38
+
28
39
  /**
29
40
  * Error linked to the media Playback.
30
41
  *
@@ -36,6 +47,7 @@ export default class MediaError extends Error {
36
47
  public readonly type: "MEDIA_ERROR";
37
48
  public readonly code: IMediaErrorCode;
38
49
  public readonly tracksInfo: ITaggedTrack[] | undefined;
50
+ public readonly timeInfo: ITimeInfo | undefined;
39
51
  public fatal: boolean;
40
52
  private _originalMessage: string;
41
53
 
@@ -52,13 +64,18 @@ export default class MediaError extends Error {
52
64
  },
53
65
  );
54
66
  constructor(
55
- code: Exclude<IMediaErrorCode, ICodeWithAdaptationType>,
67
+ code: ICodeWithManifestPositionError,
68
+ reason: string,
69
+ context: {
70
+ timeInfo: ITimeInfo;
71
+ },
72
+ );
73
+ constructor(
74
+ code: Exclude<
75
+ IMediaErrorCode,
76
+ ICodeWithAdaptationType | ICodeWithManifestPositionError
77
+ >,
56
78
  reason: string,
57
- context?:
58
- | {
59
- tracks?: undefined;
60
- }
61
- | undefined,
62
79
  );
63
80
  constructor(
64
81
  code: IMediaErrorCode,
@@ -66,6 +83,7 @@ export default class MediaError extends Error {
66
83
  context?:
67
84
  | {
68
85
  tracks?: ITaggedTrack[] | undefined;
86
+ timeInfo?: ITimeInfo;
69
87
  }
70
88
  | undefined,
71
89
  ) {
@@ -82,6 +100,9 @@ export default class MediaError extends Error {
82
100
  if (context?.tracks !== undefined && context?.tracks.length > 0) {
83
101
  this.tracksInfo = context.tracks;
84
102
  }
103
+ if (context?.timeInfo !== undefined) {
104
+ this.timeInfo = context.timeInfo;
105
+ }
85
106
  }
86
107
 
87
108
  /**
@@ -97,10 +118,44 @@ export default class MediaError extends Error {
97
118
  code: this.code,
98
119
  reason: this._originalMessage,
99
120
  tracks: this.tracksInfo,
121
+ timeInfo: this.timeInfo,
100
122
  };
101
123
  }
102
124
  }
103
125
 
126
+ /**
127
+ * Re-create a `MediaError` from a serialized representation.
128
+ * @param {Object} serializedMediaError
129
+ * @returns {MediaError}
130
+ */
131
+ export function deserializeMediaError(
132
+ serializedMediaError: ISerializedMediaError,
133
+ ): MediaError {
134
+ if (
135
+ serializedMediaError.code === "MEDIA_TIME_BEFORE_MANIFEST" ||
136
+ serializedMediaError.code === "MEDIA_TIME_AFTER_MANIFEST"
137
+ ) {
138
+ assert(
139
+ serializedMediaError.timeInfo !== undefined,
140
+ `The MediaError with code ${serializedMediaError.code} does not provide "timeinfo"`,
141
+ );
142
+ return new MediaError(serializedMediaError.code, serializedMediaError.reason, {
143
+ timeInfo: serializedMediaError.timeInfo,
144
+ });
145
+ }
146
+ if (
147
+ serializedMediaError.code === "BUFFER_APPEND_ERROR" ||
148
+ serializedMediaError.code === "BUFFER_FULL_ERROR" ||
149
+ serializedMediaError.code === "NO_PLAYABLE_REPRESENTATION" ||
150
+ serializedMediaError.code === "MANIFEST_INCOMPATIBLE_CODECS_ERROR"
151
+ ) {
152
+ return new MediaError(serializedMediaError.code, serializedMediaError.reason, {
153
+ tracks: serializedMediaError.tracks,
154
+ });
155
+ }
156
+ return new MediaError(serializedMediaError.code, serializedMediaError.reason);
157
+ }
158
+
104
159
  /** Serializable object which allows to create a `MediaError` later. */
105
160
  export interface ISerializedMediaError {
106
161
  isSerializedError: true;
@@ -108,4 +163,5 @@ export interface ISerializedMediaError {
108
163
  code: IMediaErrorCode;
109
164
  reason: string;
110
165
  tracks: ITaggedTrack[] | undefined;
166
+ timeInfo?: ITimeInfo | undefined;
111
167
  }
@@ -437,7 +437,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
437
437
  // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
438
438
  videoElement.preload = "auto";
439
439
 
440
- this.version = /* PLAYER_VERSION */ "4.5.0-dev.2026041501";
440
+ this.version = /* PLAYER_VERSION */ "4.5.0-dev.2026043000";
441
441
  this.log = log;
442
442
  this.state = "STOPPED";
443
443
  this.videoElement = videoElement;
@@ -3837,7 +3837,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
3837
3837
  return true;
3838
3838
  }
3839
3839
  }
3840
- Player.version = /* PLAYER_VERSION */ "4.5.0-dev.2026041501";
3840
+ Player.version = /* PLAYER_VERSION */ "4.5.0-dev.2026043000";
3841
3841
 
3842
3842
  /** Every events sent by the RxPlayer's public API. */
3843
3843
  interface IPublicAPIEvent {
@@ -15,9 +15,10 @@ import type {
15
15
  ISentLogValue,
16
16
  } from "../../core/types";
17
17
  import { CoreMessageType } from "../../core/types";
18
+ import type { MediaError } from "../../errors";
18
19
  import {
20
+ deserializeMediaError,
19
21
  EncryptedMediaError,
20
- MediaError,
21
22
  NetworkError,
22
23
  OtherError,
23
24
  SourceBufferError,
@@ -2292,10 +2293,7 @@ function formatCoreError(sentError: ISentError): IPlayerError {
2292
2293
  ),
2293
2294
  );
2294
2295
  case "MediaError":
2295
- // eslint-disable-next-line
2296
- return new MediaError(sentError.code as any, sentError.reason, {
2297
- tracks: sentError.tracks,
2298
- });
2296
+ return deserializeMediaError(sentError);
2299
2297
  case "EncryptedMediaError":
2300
2298
  // We assume that everything have already been checked Worker-side here
2301
2299
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument