@codercms/web-player 0.0.37 → 0.0.41

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 (75) hide show
  1. package/dist/core/AudioCtx/AudioContext.d.ts +2 -0
  2. package/dist/core/AudioCtx/AudioContext.js +92 -0
  3. package/dist/core/AudioCtx/AudioContext.js.map +1 -0
  4. package/dist/core/Loaders/BaseLoader.d.ts +17 -0
  5. package/dist/core/Loaders/BaseLoader.js +50 -0
  6. package/dist/core/Loaders/BaseLoader.js.map +1 -0
  7. package/dist/core/Loaders/HLSLoader.d.ts +17 -0
  8. package/dist/core/Loaders/HLSLoader.js +313 -0
  9. package/dist/core/Loaders/HLSLoader.js.map +1 -0
  10. package/dist/core/{LoaderInterface.d.ts → Loaders/LoaderInterface.d.ts} +11 -3
  11. package/dist/core/{LoaderInterface.js → Loaders/LoaderInterface.js} +6 -0
  12. package/dist/core/{LoaderInterface.js.map → Loaders/LoaderInterface.js.map} +1 -1
  13. package/dist/core/Loaders/NativeAudioTrackList.d.ts +39 -0
  14. package/dist/core/Loaders/NativeAudioTrackList.js.map +1 -0
  15. package/dist/core/Loaders/NativeLoader.d.ts +16 -0
  16. package/dist/core/Loaders/NativeLoader.js +114 -0
  17. package/dist/core/Loaders/NativeLoader.js.map +1 -0
  18. package/dist/core/Playlist.d.ts +4 -3
  19. package/dist/core/Playlist.js.map +1 -1
  20. package/dist/core/PlaylistPlayer.d.ts +3 -2
  21. package/dist/core/PlaylistPlayer.js +5 -3
  22. package/dist/core/PlaylistPlayer.js.map +1 -1
  23. package/dist/core/VideoPlayer.d.ts +19 -191
  24. package/dist/core/VideoPlayer.js +92 -594
  25. package/dist/core/VideoPlayer.js.map +1 -1
  26. package/dist/core/VideoPlayerEvents.d.ts +74 -0
  27. package/dist/core/VideoPlayerEvents.js +40 -0
  28. package/dist/core/VideoPlayerEvents.js.map +1 -0
  29. package/dist/core/VideoPlayerMediaLoadManager.d.ts +50 -0
  30. package/dist/core/VideoPlayerMediaLoadManager.js +269 -0
  31. package/dist/core/VideoPlayerMediaLoadManager.js.map +1 -0
  32. package/dist/core/VideoPlayerMediaState.d.ts +39 -0
  33. package/dist/core/VideoPlayerMediaState.js +161 -0
  34. package/dist/core/VideoPlayerMediaState.js.map +1 -0
  35. package/dist/core/VideoPlayerMediaStateProxy.d.ts +17 -0
  36. package/dist/core/VideoPlayerMediaStateProxy.js +42 -0
  37. package/dist/core/VideoPlayerMediaStateProxy.js.map +1 -0
  38. package/dist/core/svelte/VideoPlayerMediaStateSvelteStore.d.ts +58 -0
  39. package/dist/core/svelte/VideoPlayerMediaStateSvelteStore.js +146 -0
  40. package/dist/core/svelte/VideoPlayerMediaStateSvelteStore.js.map +1 -0
  41. package/dist/core/types/BufferedPart.d.ts +4 -0
  42. package/dist/core/types/BufferedPart.js +1 -0
  43. package/dist/core/types/BufferedPart.js.map +1 -0
  44. package/dist/core/types/PlayerLoadParams.d.ts +11 -0
  45. package/dist/core/types/PlayerLoadParams.js +1 -0
  46. package/dist/core/types/PlayerLoadParams.js.map +1 -0
  47. package/dist/core/types/QualityLevel.js +1 -0
  48. package/dist/core/types/index.d.ts +4 -0
  49. package/dist/core/types/index.js +1 -0
  50. package/dist/core/types/index.js.map +1 -0
  51. package/dist/index.d.ts +9 -4
  52. package/dist/index.js +9 -4
  53. package/dist/ui/LoadingIndicator.svelte +35 -16
  54. package/dist/ui/LoadingIndicator.svelte.d.ts +1 -1
  55. package/dist/ui/ProgressBar.svelte +7 -8
  56. package/dist/ui/ProgressBar.svelte.d.ts +1 -1
  57. package/package.json +1 -1
  58. package/dist/HybridDoublyLinkedList.d.ts +0 -20
  59. package/dist/HybridDoublyLinkedList.js +0 -90
  60. package/dist/HybridDoublyLinkedList.js.map +0 -1
  61. package/dist/HybridLinkedList.d.ts +0 -18
  62. package/dist/HybridLinkedList.js +0 -90
  63. package/dist/HybridLinkedList.js.map +0 -1
  64. package/dist/core/HLSLoader.d.ts +0 -19
  65. package/dist/core/HLSLoader.js +0 -196
  66. package/dist/core/HLSLoader.js.map +0 -1
  67. package/dist/core/NativeLoader.d.ts +0 -14
  68. package/dist/core/NativeLoader.js +0 -66
  69. package/dist/core/NativeLoader.js.map +0 -1
  70. /package/dist/core/{AudioTrack.js → Loaders/NativeAudioTrackList.js} +0 -0
  71. /package/dist/core/{AudioTrack.d.ts → types/AudioTrack.d.ts} +0 -0
  72. /package/dist/core/{QualityLevel.js → types/AudioTrack.js} +0 -0
  73. /package/dist/core/{AudioTrack.js.map → types/AudioTrack.js.map} +0 -0
  74. /package/dist/core/{QualityLevel.d.ts → types/QualityLevel.d.ts} +0 -0
  75. /package/dist/core/{QualityLevel.js.map → types/QualityLevel.js.map} +0 -0
@@ -1,124 +1,30 @@
1
1
  import { EventEmitter } from 'eventemitter3';
2
- import { readonly, writable } from 'svelte/store';
3
- import { HLSLoader, isHlsJsSupported } from './HLSLoader.js';
4
- import { NativeLoader } from './NativeLoader.js';
5
- import { LoaderEvents } from './LoaderInterface.js';
6
- import { DOMEventRegistry } from './DOMEventRegistry.js';
7
2
  import screenfull from 'screenfull';
8
3
  import { BROWSER } from 'esm-env';
4
+ import { HLSLoader, IS_HLS_JS_SUPPORTED } from './Loaders/HLSLoader.js';
5
+ import { NativeLoader } from './Loaders/NativeLoader.js';
6
+ import { DOMEventRegistry } from './DOMEventRegistry.js';
7
+ import { PlayerEvents } from './VideoPlayerEvents.js';
8
+ import { VideoPlayerMediaState } from './VideoPlayerMediaState.js';
9
9
  import { IS_IOS, IS_IPAD_NEW } from '../ui/utils/ios.js';
10
- export var PlayerEvents;
11
- (function (PlayerEvents) {
12
- PlayerEvents["Play"] = "player.play";
13
- PlayerEvents["Pause"] = "player.pause";
14
- PlayerEvents["Ended"] = "player.ended";
15
- PlayerEvents["Error"] = "player.error";
16
- PlayerEvents["Playing"] = "player.playing";
17
- PlayerEvents["Seeking"] = "player.seeking";
18
- PlayerEvents["Seeked"] = "player.seeked";
19
- PlayerEvents["MediaElementAttached"] = "player.mediaElementAttached";
20
- PlayerEvents["MediaElementDetached"] = "player.mediaElementDetached";
21
- PlayerEvents["ContainerElementAttached"] = "player.containerElementAttached";
22
- PlayerEvents["ContainerElementDetached"] = "player.containerElementDetached";
23
- PlayerEvents["FullScreenEnter"] = "player.fullscreenEnter";
24
- PlayerEvents["FullScreenExit"] = "player.fullscreenExit";
25
- PlayerEvents["FullScreenToggle"] = "player.fullscreenToggle";
26
- // LoadingStarted happens when player's load method called
27
- PlayerEvents["LoadingStarted"] = "player.loadingStarted";
28
- // LoadingMetadataEnded happens when loader process has been completed
29
- PlayerEvents["LoadingMetadataEnded"] = "player.loadingMetadataEnded";
30
- // LoadingMetadataFailed happens when loader fails to load video
31
- PlayerEvents["LoadingMetadataFailed"] = "player.loadingMedataFailed";
32
- // LoadingEnded happens when metadata loading is done and there's also initial video content fetched
33
- PlayerEvents["LoadingEnded"] = "player.loadingEnded";
34
- PlayerEvents["BufferingStarted"] = "player.bufferingStarted";
35
- PlayerEvents["BufferingEnded"] = "player.bufferingEnded";
36
- PlayerEvents["PlaybackBufferingProgress"] = "player.bufferingProgress";
37
- PlayerEvents["PlaybackDurationChange"] = "player.durationChange";
38
- PlayerEvents["PlaybackTimeUpdate"] = "player.timeUpdate";
39
- PlayerEvents["PlaybackRateChange"] = "player.playbackRateChange";
40
- PlayerEvents["VolumeChange"] = "player.volumeChange";
41
- PlayerEvents["MuteChange"] = "player.muteChange";
42
- PlayerEvents["VolumeStateChange"] = "player.volumeStateChange";
43
- PlayerEvents["QualityLevelsUpdated"] = "player.qualityLevelsUpdated";
44
- PlayerEvents["QualityLevelSwitching"] = "player.qualityLevelSwitching";
45
- PlayerEvents["QualityLevelSwitched"] = "player.qualityLevelSwitched";
46
- PlayerEvents["AudioTracksUpdated"] = "player.audioTracksUpdated";
47
- PlayerEvents["AudioTrackSwitching"] = "player.audioTrackSwitching";
48
- PlayerEvents["AudioTrackSwitched"] = "player.audioTrackSwitched";
49
- })(PlayerEvents || (PlayerEvents = {}));
50
- export var Loader;
51
- (function (Loader) {
52
- Loader[Loader["Native"] = 0] = "Native";
53
- Loader[Loader["HLS"] = 1] = "HLS";
54
- Loader[Loader["DASH"] = 2] = "DASH";
55
- })(Loader || (Loader = {}));
56
- async function getLoadUrl(paramsUrl, signal) {
57
- let url;
58
- if (typeof paramsUrl === 'string') {
59
- url = paramsUrl;
60
- }
61
- else if (typeof paramsUrl === 'function') {
62
- url = await paramsUrl(signal);
63
- }
64
- else {
65
- url = await paramsUrl;
66
- }
67
- if (!url || !url.length) {
68
- throw Error('Bad load url');
69
- }
70
- return url;
71
- }
72
- export var VideoLoadingState;
73
- (function (VideoLoadingState) {
74
- VideoLoadingState[VideoLoadingState["LoadingMetadata"] = 0] = "LoadingMetadata";
75
- VideoLoadingState[VideoLoadingState["LoadingData"] = 1] = "LoadingData";
76
- VideoLoadingState[VideoLoadingState["LoadingDone"] = 2] = "LoadingDone";
77
- VideoLoadingState[VideoLoadingState["LoadingFailed"] = 3] = "LoadingFailed";
78
- })(VideoLoadingState || (VideoLoadingState = {}));
10
+ import { VideoLoadingState, VideoPlayerMediaLoadManager } from './VideoPlayerMediaLoadManager.js';
11
+ import { VideoPlayerMediaStateProxy } from './VideoPlayerMediaStateProxy.js';
12
+ import { VideoPlayerMediaStateSvelteStore } from './svelte/VideoPlayerMediaStateSvelteStore.js';
13
+ export { IS_AUDIO_CONTEXT_SUPPORTED, IS_AUDIO_GAIN_SUPPORTED } from './AudioCtx/AudioContext.js';
14
+ export const IS_VOLUME_CHANGE_ALLOWED = BROWSER &&
15
+ Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'volume')?.set !== undefined;
16
+ export const IS_FULLSCREEN_ALLOWED = BROWSER && screenfull.isEnabled;
79
17
  export class VideoPlayer {
80
- static _isFullScreenAllowed = screenfull.isEnabled;
81
18
  _el;
82
19
  _containerEl;
83
20
  elEventsRegistry;
84
21
  _cfg;
85
22
  emitter = new EventEmitter();
86
- loadAbortController = null;
87
- currentLoader = null;
88
- hlsLoader = null;
89
- nativeLoader = null;
90
- _loadingState = VideoLoadingState.LoadingDone;
91
- // _isBuffering indicates is there and buffering process which block playback
92
- _isBuffering = false;
93
- // _isLoading flag indicates combined state of buffering + video loading progress
94
- _isLoading = false;
23
+ _mediaState = new VideoPlayerMediaState(this.emitter);
24
+ _mediaStateProxy = null;
25
+ _mediaStateSvelteStore = null;
26
+ _loadingMgr = new VideoPlayerMediaLoadManager(this.emitter, this._mediaState);
95
27
  _isFullScreen = false;
96
- _paused = true;
97
- _duration = 0;
98
- _currentTime = 0;
99
- _playbackRate = 0;
100
- _muted = false;
101
- _volume = 1;
102
- _buffered = [];
103
- _isAutoQuality = true;
104
- _currentQualityLevel = null;
105
- _qualityLevels = [];
106
- _currentAudioTrack = null;
107
- _audioTracks = [];
108
- _isFullScreenStore = writable(this._isFullScreen);
109
- _isLoadingStore = writable(this._isLoading);
110
- _pausedStore = writable(this._paused);
111
- _durationStore = writable(this._duration);
112
- _currentTimeStore = writable(this._currentTime);
113
- _playbackRateStore = writable(this._playbackRate);
114
- _mutedStore = writable(this._muted);
115
- _volumeStore = writable(this._volume);
116
- _bufferedStore = writable(this._buffered);
117
- _isAutoQualityStore = writable(this._isAutoQuality);
118
- _currentQualityLevelStore = writable(this._currentQualityLevel);
119
- _qualityLevelsStore = writable(this._qualityLevels);
120
- _currentAudioTrackStore = writable(this._currentAudioTrack);
121
- _audioTracksStore = writable(this._audioTracks);
122
28
  constructor(el, cfg) {
123
29
  this._cfg = cfg ?? {};
124
30
  // Do nothing in SSR environment
@@ -128,27 +34,22 @@ export class VideoPlayer {
128
34
  if (el) {
129
35
  this.attachEl(el);
130
36
  }
131
- if (VideoPlayer._isFullScreenAllowed) {
37
+ if (IS_FULLSCREEN_ALLOWED) {
132
38
  screenfull.on('change', this._onFullScreenChange.bind(this));
133
39
  }
134
- this.emitter.on(LoaderEvents.QualityLevelsUpdated, this._onLoaderQualityLevelsUpdated.bind(this));
135
- this.emitter.on(LoaderEvents.QualityLevelSwitching, this._onLoaderQualityLevelSwitching.bind(this));
136
- this.emitter.on(LoaderEvents.QualityLevelSwitched, this._onLoaderQualityLevelSwitched.bind(this));
137
- this.emitter.on(LoaderEvents.AudioTracksUpdated, this._onLoaderAudioTracksUpdated.bind(this));
138
- this.emitter.on(LoaderEvents.AudioTrackSwitching, this._onLoaderAudioTrackSwitching.bind(this));
139
- this.emitter.on(LoaderEvents.AudioTrackSwitched, this._onLoaderAudioTrackSwitched.bind(this));
140
40
  }
141
41
  _registerVideoEvents(el) {
142
42
  this.elEventsRegistry?.unsubscribeAll();
143
43
  this.elEventsRegistry = new DOMEventRegistry(el);
144
44
  this.elEventsRegistry.subscribe('play', () => {
145
- this._updatePaused(el.paused);
45
+ this._mediaState.updatePaused(el.paused);
146
46
  });
147
47
  this.elEventsRegistry.subscribe('pause', () => {
148
- this._updatePaused(el.paused);
48
+ this._mediaState.updatePaused(el.paused);
149
49
  });
150
50
  this.elEventsRegistry.subscribe('playing', () => {
151
- this._onBufferingEnded();
51
+ this._mediaState.updatePaused(el.paused);
52
+ this._mediaState.updateBuffering(false);
152
53
  this.emitter.emit(PlayerEvents.Playing);
153
54
  });
154
55
  this.elEventsRegistry.subscribe('seeking', () => {
@@ -158,220 +59,46 @@ export class VideoPlayer {
158
59
  this.emitter.emit(PlayerEvents.Seeked);
159
60
  });
160
61
  this.elEventsRegistry.subscribe('waiting', () => {
161
- this._onBufferingStarted();
62
+ this._mediaState.updateBuffering(true);
162
63
  });
64
+ // this.elEventsRegistry.subscribe('stalled', () => {
65
+ // this._mediaState.updateBuffering(true);
66
+ // });
163
67
  this.elEventsRegistry.subscribe('ended', () => {
164
- this._onBufferingEnded();
68
+ this._mediaState.updateBuffering(false);
165
69
  this.emitter.emit(PlayerEvents.Ended);
166
70
  });
167
71
  this.elEventsRegistry.subscribe('progress', () => {
168
- this._onProgress(el);
72
+ this._mediaState.updateBufferedFromTimeRanges(el.buffered);
169
73
  });
170
74
  this.elEventsRegistry.subscribe('canplaythrough', () => {
171
- this._onBufferingEnded();
75
+ this._mediaState.updateBuffering(false);
172
76
  });
173
77
  this.elEventsRegistry.subscribe('durationchange', () => {
174
- let newDur = el.duration;
175
- if (!isFinite(newDur) || isNaN(newDur)) {
176
- newDur = 0;
177
- }
178
- if (this._duration !== newDur) {
179
- this._updateDuration(newDur);
180
- }
78
+ this._mediaState.updateDuration(el.duration);
181
79
  });
182
80
  this.elEventsRegistry.subscribe('timeupdate', () => {
183
- let newTime = el.currentTime;
184
- if (!isFinite(newTime) || isNaN(newTime)) {
185
- newTime = 0;
186
- }
187
- if (this._currentTime !== newTime) {
188
- this._updateCurrentTime(newTime);
189
- }
81
+ this._mediaState.updateCurrentTime(el.currentTime);
190
82
  // Buffered parts may not be dispatched in on progress event
191
83
  // so take care that there is always fresh buffered parts
192
- this._onProgress(el);
84
+ this._mediaState.updateBufferedFromTimeRanges(el.buffered);
193
85
  });
194
86
  this.elEventsRegistry.subscribe('ratechange', () => {
195
- const newRate = el.playbackRate;
196
- if (this._playbackRate !== newRate) {
197
- this._updatePlaybackRate(newRate);
198
- }
87
+ this._mediaState.updatePlaybackRate(el.playbackRate);
199
88
  });
200
89
  this.elEventsRegistry.subscribe('volumechange', () => {
201
- const newVol = el.volume;
202
- const newMuted = el.muted;
203
- const isVolChanged = this._volume !== newVol;
204
- const isMuteChanged = this._muted !== newMuted;
205
- if (isVolChanged) {
206
- this._updateVolume(newVol);
207
- }
208
- if (isMuteChanged) {
209
- this._updateMuted(newMuted);
210
- }
211
- if (isVolChanged || isMuteChanged) {
212
- this.emitter.emit(PlayerEvents.VolumeStateChange, newVol, newMuted);
213
- }
90
+ this._mediaState.updateVolume(el.volume, el.muted);
214
91
  });
215
92
  // this.elEventsRegistry.subscribe('emptied', () => {
216
93
  // this._resetState();
217
94
  // });
218
95
  this.elEventsRegistry.subscribe('error', (ev) => {
219
- this.emitter.emit(PlayerEvents.Error, ev.error ?? 'unknown playback error');
96
+ this.emitter.emit(PlayerEvents.Error, ev.error ?? new Error('Unknown playback error'));
220
97
  });
221
98
  }
222
99
  _unregisterVideoElementEvents() {
223
100
  this.elEventsRegistry?.unsubscribeAll();
224
101
  }
225
- _onBufferingStarted() {
226
- if (!this._isBuffering) {
227
- this._isBuffering = true;
228
- this._calcIsLoadingState();
229
- this.emitter.emit(PlayerEvents.BufferingStarted);
230
- }
231
- }
232
- _onBufferingEnded() {
233
- if (this._isBuffering) {
234
- this._isBuffering = false;
235
- if (this._loadingState === VideoLoadingState.LoadingData) {
236
- this._loadingState = VideoLoadingState.LoadingDone;
237
- this.emitter.emit(PlayerEvents.LoadingEnded);
238
- }
239
- this._calcIsLoadingState();
240
- this.emitter.emit(PlayerEvents.BufferingEnded);
241
- }
242
- }
243
- _calcIsLoadingState() {
244
- const isFinishedVideoLoading = this._loadingState === VideoLoadingState.LoadingDone ||
245
- this._loadingState === VideoLoadingState.LoadingFailed;
246
- this._isLoading = !isFinishedVideoLoading || this._isBuffering;
247
- this._isLoadingStore.set(this._isLoading);
248
- }
249
- _onProgress(el) {
250
- const newBuf = el.buffered;
251
- const parts = [];
252
- if (newBuf.length !== this._buffered.length) {
253
- for (let i = 0; i < newBuf.length; i++) {
254
- parts.push({ start: newBuf.start(i), end: newBuf.end(i) });
255
- }
256
- this._updateBufferedParts(parts);
257
- return;
258
- }
259
- let isChanged = false;
260
- for (let i = 0; i < newBuf.length; i++) {
261
- const start = newBuf.start(i);
262
- const end = newBuf.end(i);
263
- isChanged = start !== this._buffered[i].start || end !== this._buffered[i].end;
264
- parts.push({ start, end });
265
- }
266
- if (!isChanged) {
267
- return;
268
- }
269
- this._updateBufferedParts(parts);
270
- }
271
- _resetState(newLoadingState) {
272
- this._updateAudioTracks([]);
273
- this._updateQualityLevels([]);
274
- this._updateDuration(0);
275
- this._updateCurrentTime(0);
276
- this._updateBufferedParts([]);
277
- this._currentQualityLevel = null;
278
- this._currentQualityLevelStore.set(null);
279
- this._currentAudioTrack = null;
280
- this._currentAudioTrackStore.set(null);
281
- if (newLoadingState !== undefined) {
282
- this._loadingState = newLoadingState;
283
- }
284
- else if (this._loadingState !== VideoLoadingState.LoadingDone &&
285
- this._loadingState !== VideoLoadingState.LoadingFailed) {
286
- this._loadingState = VideoLoadingState.LoadingDone;
287
- }
288
- this._onBufferingEnded();
289
- this._calcIsLoadingState();
290
- }
291
- _refreshState() {
292
- const el = this.el;
293
- let currTime = el.currentTime;
294
- if (isNaN(currTime) || !isFinite(currTime)) {
295
- currTime = 0;
296
- }
297
- let duration = el.duration;
298
- if (isNaN(duration) || !isFinite(duration)) {
299
- duration = 0;
300
- }
301
- if (el.paused !== this._paused) {
302
- this._updatePaused(el.paused);
303
- }
304
- if (el.muted !== this._muted) {
305
- this._updateMuted(el.muted);
306
- }
307
- if (el.volume !== this._volume) {
308
- this._updateVolume(el.volume);
309
- }
310
- if (currTime !== this._currentTime) {
311
- this._updateCurrentTime(currTime);
312
- }
313
- if (duration !== this._duration) {
314
- this._updateDuration(duration);
315
- }
316
- if (el.playbackRate !== this._playbackRate) {
317
- this._updatePlaybackRate(el.playbackRate);
318
- }
319
- this._onProgress(el);
320
- }
321
- _onLoaderQualityLevelsUpdated(newLevels) {
322
- // console.log('Quality levels updated', newLevels);
323
- this._updateQualityLevels(newLevels);
324
- }
325
- _onLoaderQualityLevelSwitching(newLevelId, isAutoLevel) {
326
- const newLevel = this._qualityLevels.find((lvl) => lvl.id === newLevelId);
327
- if (!newLevel) {
328
- console.error(`Quality level switch to unknown level`, {
329
- newLevelId,
330
- levels: this._qualityLevels
331
- });
332
- return;
333
- }
334
- this._isAutoQuality = isAutoLevel;
335
- this.emitter.emit(PlayerEvents.QualityLevelSwitching, newLevel, isAutoLevel);
336
- }
337
- _onLoaderQualityLevelSwitched(newLevelId, isAutoLevel) {
338
- const newLevel = this._qualityLevels.find((lvl) => lvl.id === newLevelId);
339
- if (!newLevel) {
340
- console.error(`Quality level switch to unknown level`, {
341
- newLevelId,
342
- levels: this._qualityLevels
343
- });
344
- return;
345
- }
346
- this._updateQualityLevel(newLevel, isAutoLevel);
347
- }
348
- _onLoaderAudioTracksUpdated(newTracks) {
349
- // console.log('Audio tracks updated', newTracks);
350
- this._updateAudioTracks(newTracks);
351
- }
352
- _onLoaderAudioTrackSwitching(newTrackId) {
353
- const newTrack = this._audioTracks.find((lvl) => lvl.id === newTrackId);
354
- if (!newTrack) {
355
- console.error(`Audio track switch to unknown track`, {
356
- newTrackId,
357
- tracks: this._audioTracks
358
- });
359
- return;
360
- }
361
- this.emitter.emit(PlayerEvents.AudioTrackSwitching, newTrack);
362
- }
363
- _onLoaderAudioTrackSwitched(newTrackId) {
364
- const newTrack = this._audioTracks.find((lvl) => lvl.id === newTrackId);
365
- if (!newTrack) {
366
- console.error(`Audio track switch to unknown track`, {
367
- newTrackId,
368
- tracks: this._audioTracks
369
- });
370
- return;
371
- }
372
- this._updateCurrentAudioTrack(newTrack);
373
- this.emitter.emit(PlayerEvents.AudioTrackSwitching, newTrack);
374
- }
375
102
  _onFullScreenChange = () => {
376
103
  const newFullScreen = screenfull.isFullscreen;
377
104
  if (newFullScreen !== this._isFullScreen) {
@@ -380,7 +107,7 @@ export class VideoPlayer {
380
107
  };
381
108
  _updateIsFullScreen(isFullScreen) {
382
109
  this._isFullScreen = isFullScreen;
383
- this._isFullScreenStore.set(isFullScreen);
110
+ // this._isFullScreenStore.set(isFullScreen);
384
111
  if (isFullScreen) {
385
112
  this.emitter.emit(PlayerEvents.FullScreenEnter);
386
113
  }
@@ -389,68 +116,6 @@ export class VideoPlayer {
389
116
  }
390
117
  this.emitter.emit(PlayerEvents.FullScreenToggle, isFullScreen);
391
118
  }
392
- _updateBufferedParts(buffered) {
393
- this._buffered = buffered;
394
- this._bufferedStore.set(buffered);
395
- this.emitter.emit(PlayerEvents.PlaybackBufferingProgress, buffered);
396
- }
397
- _updateDuration(duration) {
398
- this._duration = duration;
399
- this._durationStore.set(duration);
400
- this.emitter.emit(PlayerEvents.PlaybackDurationChange, duration);
401
- }
402
- _updateCurrentTime(currentTime) {
403
- this._currentTime = currentTime;
404
- this._currentTimeStore.set(currentTime);
405
- this.emitter.emit(PlayerEvents.PlaybackTimeUpdate, currentTime);
406
- }
407
- _updatePlaybackRate(rate) {
408
- this._playbackRate = rate;
409
- this._playbackRateStore.set(rate);
410
- this.emitter.emit(PlayerEvents.PlaybackRateChange, rate);
411
- }
412
- _updateVolume(volume) {
413
- this._volume = volume;
414
- this._volumeStore.set(volume);
415
- this.emitter.emit(PlayerEvents.VolumeChange, volume);
416
- }
417
- _updateMuted(muted) {
418
- this._muted = muted;
419
- this._mutedStore.set(muted);
420
- this.emitter.emit(PlayerEvents.MuteChange, muted);
421
- }
422
- _updatePaused(paused) {
423
- this._paused = paused;
424
- this._pausedStore.set(paused);
425
- if (!paused) {
426
- this.emitter.emit(PlayerEvents.Play);
427
- }
428
- else {
429
- this.emitter.emit(PlayerEvents.Pause);
430
- }
431
- }
432
- _updateQualityLevels(levels) {
433
- this._qualityLevels = levels;
434
- this._qualityLevelsStore.set(levels);
435
- this.emitter.emit(PlayerEvents.QualityLevelsUpdated, levels);
436
- }
437
- _updateQualityLevel(level, isAutoQuality) {
438
- this._currentQualityLevel = level;
439
- this._currentQualityLevelStore.set(level);
440
- this._isAutoQuality = isAutoQuality;
441
- this._isAutoQualityStore.set(isAutoQuality);
442
- this.emitter.emit(PlayerEvents.QualityLevelSwitched, level, isAutoQuality);
443
- }
444
- _updateAudioTracks(audioTracks) {
445
- this._audioTracks = audioTracks;
446
- this._audioTracksStore.set(audioTracks);
447
- this.emitter.emit(PlayerEvents.AudioTracksUpdated, audioTracks);
448
- }
449
- _updateCurrentAudioTrack(audioTrack) {
450
- this._currentAudioTrack = audioTrack;
451
- this._currentAudioTrackStore.set(audioTrack);
452
- this.emitter.emit(PlayerEvents.AudioTrackSwitched, audioTrack);
453
- }
454
119
  attachEl(el) {
455
120
  if (this._el === el) {
456
121
  return;
@@ -459,15 +124,15 @@ export class VideoPlayer {
459
124
  this.detachEl();
460
125
  }
461
126
  this._el = el;
462
- this._refreshState();
127
+ this._mediaState.syncFromEl(el);
463
128
  this._registerVideoEvents(el);
464
129
  // Setup HLS.js loader
465
- if (isHlsJsSupported()) {
466
- this.hlsLoader = new HLSLoader(this.emitter, this._el, this._cfg.hlsConfig ?? {
130
+ if (IS_HLS_JS_SUPPORTED) {
131
+ this._loadingMgr.setHlsLoader(new HLSLoader(el, this._cfg.hlsConfig ?? {
467
132
  progressive: true
468
- });
133
+ }));
469
134
  }
470
- this.nativeLoader = new NativeLoader(this.emitter, this.el);
135
+ this._loadingMgr.setNativeLoader(new NativeLoader(this._el));
471
136
  this.emitter.emit(PlayerEvents.MediaElementAttached, el);
472
137
  }
473
138
  attachContainerEl(el) {
@@ -487,6 +152,7 @@ export class VideoPlayer {
487
152
  this._unregisterVideoElementEvents();
488
153
  const el = this._el;
489
154
  this._el = undefined;
155
+ this._loadingMgr.destroy();
490
156
  this.emitter.emit(PlayerEvents.MediaElementDetached, el);
491
157
  }
492
158
  detachContainerEl() {
@@ -498,133 +164,27 @@ export class VideoPlayer {
498
164
  this.emitter.emit(PlayerEvents.ContainerElementDetached, el);
499
165
  }
500
166
  destroy() {
167
+ if (this._mediaStateSvelteStore) {
168
+ this._mediaStateSvelteStore.destroy();
169
+ }
501
170
  this.detachEl();
502
171
  this.detachContainerEl();
503
- this.emitter.off(LoaderEvents.QualityLevelsUpdated, this._onLoaderQualityLevelsUpdated.bind(this));
504
- this.emitter.off(LoaderEvents.QualityLevelSwitching, this._onLoaderQualityLevelSwitching.bind(this));
505
- this.emitter.off(LoaderEvents.QualityLevelSwitched, this._onLoaderQualityLevelSwitched.bind(this));
506
- this.emitter.off(LoaderEvents.AudioTracksUpdated, this._onLoaderAudioTracksUpdated.bind(this));
507
- this.emitter.off(LoaderEvents.AudioTrackSwitching, this._onLoaderAudioTrackSwitching.bind(this));
508
- this.emitter.off(LoaderEvents.AudioTrackSwitched, this._onLoaderAudioTrackSwitched.bind(this));
509
- if (this.currentLoader) {
510
- this.currentLoader.stop();
511
- this.currentLoader = null;
512
- this.hlsLoader = null;
513
- }
514
- if (VideoPlayer._isFullScreenAllowed) {
172
+ if (IS_FULLSCREEN_ALLOWED) {
515
173
  screenfull.off('change', this._onFullScreenChange.bind(this));
516
174
  }
517
175
  }
518
176
  stop() {
519
- if (this.currentLoader) {
520
- if (this.loadAbortController) {
521
- this.loadAbortController.abort('Player stop called');
522
- this.loadAbortController = null;
523
- }
524
- if (!this.el.paused) {
525
- this.el.pause();
526
- this._paused = true;
527
- this._pausedStore.set(true);
528
- }
529
- try {
530
- this.currentLoader.stop();
531
- }
532
- catch (e) {
533
- // Ignore any loader stopping error
534
- console.log('loader error during stop', e);
535
- }
536
- this._resetState();
537
- }
538
- }
539
- _getLoaderByType(loaderType) {
540
- let loader = null;
541
- switch (loaderType) {
542
- case Loader.Native:
543
- loader = this.nativeLoader;
544
- break;
545
- case Loader.HLS:
546
- loader = this.hlsLoader ?? this.nativeLoader;
547
- break;
548
- case Loader.DASH:
549
- loader = this.nativeLoader;
550
- break;
551
- default:
552
- throw Error('Unknown loader selected');
553
- }
554
- if (!loader) {
555
- throw Error('No suitable loader found');
556
- }
557
- return loader;
558
- }
559
- _getLoaderFromUrl(url) {
560
- let loader = null;
561
- const urlParsed = new URL(url);
562
- const path = urlParsed.pathname;
563
- if (path.endsWith('.m3u8')) {
564
- // HLS
565
- loader = this.hlsLoader ?? this.nativeLoader;
566
- }
567
- else if (path.endsWith('.mpd')) {
568
- // DASH
569
- loader = this.nativeLoader;
570
- }
571
- else {
572
- // For everything else native loader should be used
573
- loader = this.nativeLoader;
574
- }
575
- if (!loader) {
576
- throw Error('No suitable loader found');
577
- }
578
- return loader;
177
+ this._loadingMgr.stop();
178
+ this._mediaState.resetForLoadingStart();
579
179
  }
580
180
  async load(params) {
581
- if (!this.el) {
582
- return;
583
- }
584
- let neededLoader = null;
585
- if (params.loader !== undefined) {
586
- neededLoader = this._getLoaderByType(params.loader);
587
- }
588
- // Unsubscribe from video events before loading
181
+ const el = this.el;
182
+ this.emitter.emit(PlayerEvents.BeforeLoading, params);
589
183
  this._unregisterVideoElementEvents();
590
- const abortController = new AbortController();
591
- this.loadAbortController?.abort('Player load called before previous load is done');
592
- this.loadAbortController = abortController;
593
- this._resetState(VideoLoadingState.LoadingMetadata);
594
- this._calcIsLoadingState();
595
- this.emitter.emit(PlayerEvents.LoadingStarted, params);
596
- try {
597
- const loadUrl = await getLoadUrl(params.url, this.loadAbortController.signal);
598
- // Detect needed loader based on load url
599
- if (neededLoader === null) {
600
- neededLoader = this._getLoaderFromUrl(loadUrl);
601
- }
602
- if (this.currentLoader && this.currentLoader !== neededLoader) {
603
- this.currentLoader.stop();
604
- this.currentLoader = neededLoader;
605
- }
606
- // Subscribe to video events before loader load call, because it will produce HTML events
607
- this._registerVideoEvents(this.el);
608
- await neededLoader.load({
609
- abortSignal: abortController.signal,
610
- url: loadUrl,
611
- startLevel: params.startLevel,
612
- startPosition: params.startPosition,
613
- audioTrack: params.audioTrack
614
- });
615
- }
616
- catch (e) {
617
- this.loadAbortController = null;
618
- this._loadingState = VideoLoadingState.LoadingFailed;
619
- this._calcIsLoadingState();
620
- this.emitter.emit(PlayerEvents.LoadingMetadataFailed, e);
621
- throw e;
622
- }
623
- this.loadAbortController = null;
624
- this._loadingState = VideoLoadingState.LoadingData;
625
- this._onBufferingStarted();
626
- this._calcIsLoadingState();
627
- this.emitter.emit(PlayerEvents.LoadingMetadataEnded);
184
+ this._mediaState.resetForLoadingStart();
185
+ await this._loadingMgr.load(params);
186
+ this._registerVideoEvents(el);
187
+ this._mediaState.syncFromEl(el);
628
188
  if (params.playAfterLoad) {
629
189
  await this.play();
630
190
  }
@@ -658,18 +218,18 @@ export class VideoPlayer {
658
218
  if (pos < 0) {
659
219
  pos = 0;
660
220
  }
661
- else if (pos > this._duration) {
662
- pos = this._duration;
221
+ else if (pos > this._mediaState.duration) {
222
+ pos = this._mediaState.duration;
663
223
  }
664
- this._updateCurrentTime(pos);
224
+ this._mediaState.updateCurrentTime(pos);
665
225
  this._el.currentTime = pos;
666
226
  }
667
227
  seekForward(step) {
668
- const newCurrentTime = this._currentTime + step;
228
+ const newCurrentTime = this._mediaState.currentTime + step;
669
229
  this.seekTo(newCurrentTime);
670
230
  }
671
231
  seekBackward(step) {
672
- const newCurrentTime = this._currentTime - step;
232
+ const newCurrentTime = this._mediaState.currentTime - step;
673
233
  this.seekTo(newCurrentTime);
674
234
  }
675
235
  setVolume(vol) {
@@ -687,6 +247,20 @@ export class VideoPlayer {
687
247
  }
688
248
  this._el.muted = muted;
689
249
  }
250
+ // setVolumeGain(gain: number) {
251
+ // if (!this._el) {
252
+ // throw Error('Player is not attached to media element');
253
+ // }
254
+ //
255
+ // if (!this._audioGain) {
256
+ // if (!IS_AUDIO_GAIN_SUPPORTED) {
257
+ // console.error('Audio gain is not supported by the browser');
258
+ // return;
259
+ // }
260
+ //
261
+ // this._audioGain = createGainNode();
262
+ // }
263
+ // }
690
264
  setPlaybackRate(rate) {
691
265
  if (!this._el) {
692
266
  throw Error('Player is not attached to media element');
@@ -699,9 +273,6 @@ export class VideoPlayer {
699
273
  }
700
274
  this._el.muted = !this._el.muted;
701
275
  }
702
- static isFullScreenAllowed() {
703
- return VideoPlayer._isFullScreenAllowed;
704
- }
705
276
  async toggleFullScreen() {
706
277
  // On iOS only native full screen supported
707
278
  if (IS_IOS && !IS_IPAD_NEW) {
@@ -719,30 +290,33 @@ export class VideoPlayer {
719
290
  return await screenfull.toggle(this._el);
720
291
  }
721
292
  setQualityLevel(id, graceful) {
722
- const found = this._qualityLevels.find((lvl) => lvl.id === id);
293
+ const found = this._mediaState.findQualityLevelById(id);
723
294
  if (!found) {
724
295
  throw Error(`Quality level not found: ${id}`);
725
296
  }
726
- if (!this.currentLoader) {
297
+ const loader = this._loadingMgr.getCurrentLoader();
298
+ if (!loader) {
727
299
  throw Error('Loader is not set');
728
300
  }
729
- this.currentLoader.switchQualityLevel(id, graceful);
301
+ loader.switchQualityLevel(id, graceful);
730
302
  }
731
303
  setAutoQualityLevel(graceful) {
732
- if (!this.currentLoader) {
304
+ const loader = this._loadingMgr.getCurrentLoader();
305
+ if (!loader) {
733
306
  throw Error('Loader is not set');
734
307
  }
735
- this.currentLoader.switchQualityLevel(this.currentLoader.getAutoQualityLevelId(), graceful);
308
+ loader.switchQualityLevel(loader.getAutoQualityLevelId(), graceful);
736
309
  }
737
310
  setAudioTrack(trackId, graceful) {
738
- const found = this._audioTracks.find((track) => track.id === trackId);
311
+ const found = this._mediaState.findAudioTrackById(trackId);
739
312
  if (!found) {
740
313
  throw Error(`Audio track not found: ${trackId}`);
741
314
  }
742
- if (!this.currentLoader) {
315
+ const loader = this._loadingMgr.getCurrentLoader();
316
+ if (!loader) {
743
317
  throw Error('Loader is not set');
744
318
  }
745
- this.currentLoader.switchAudioTrack(trackId, graceful);
319
+ loader.switchAudioTrack(trackId, graceful);
746
320
  }
747
321
  subscribe(ev, cb) {
748
322
  this.emitter.on(ev, cb);
@@ -765,98 +339,22 @@ export class VideoPlayer {
765
339
  }
766
340
  return this._containerEl;
767
341
  }
768
- get loader() {
769
- return this.currentLoader;
770
- }
771
342
  get isFullScreen() {
772
343
  return this._isFullScreen;
773
344
  }
774
- get loadingState() {
775
- return this._loadingState;
776
- }
777
- get isBuffering() {
778
- return this._isBuffering;
345
+ get loader() {
346
+ return this._loadingMgr.getCurrentLoader();
779
347
  }
780
- // isLoading indicates is there any buffering process or metadata loading
781
348
  get isLoading() {
782
- return this._isLoading;
783
- }
784
- get qualityLevels() {
785
- return this._qualityLevels;
786
- }
787
- get isAutoQuality() {
788
- return this._isAutoQuality;
789
- }
790
- get qualityLevel() {
791
- return this._currentQualityLevel;
792
- }
793
- get audioTracks() {
794
- return this._audioTracks;
795
- }
796
- get audioTrack() {
797
- return this._currentAudioTrack;
798
- }
799
- get duration() {
800
- return this._duration;
801
- }
802
- get paused() {
803
- return this._paused;
804
- }
805
- get currentTime() {
806
- return this._currentTime;
807
- }
808
- get buffered() {
809
- return this._buffered;
810
- }
811
- get playbackRate() {
812
- return this._playbackRate;
813
- }
814
- get volume() {
815
- return this._volume;
349
+ return this._loadingMgr.getLoadingState() === VideoLoadingState.Loading;
816
350
  }
817
- get muted() {
818
- return this._muted;
819
- }
820
- get pausedStore() {
821
- return readonly(this._pausedStore);
822
- }
823
- get isFullScreenStore() {
824
- return readonly(this._isFullScreenStore);
825
- }
826
- get isLoadingStore() {
827
- return readonly(this._isLoadingStore);
828
- }
829
- get qualityLevelsStore() {
830
- return readonly(this._qualityLevelsStore);
831
- }
832
- get isAutoQualityStore() {
833
- return readonly(this._isAutoQualityStore);
834
- }
835
- get qualityLevelStore() {
836
- return readonly(this._currentQualityLevelStore);
837
- }
838
- get audioTracksStore() {
839
- return readonly(this._audioTracksStore);
840
- }
841
- get audioTrackStore() {
842
- return readonly(this._currentAudioTrackStore);
843
- }
844
- get durationStore() {
845
- return readonly(this._durationStore);
846
- }
847
- get currentTimeStore() {
848
- return readonly(this._currentTimeStore);
849
- }
850
- get bufferedStore() {
851
- return readonly(this._bufferedStore);
852
- }
853
- get playbackRateStore() {
854
- return readonly(this._playbackRateStore);
351
+ get loadingState() {
352
+ return this._loadingMgr.getLoadingState();
855
353
  }
856
- get volumeStore() {
857
- return readonly(this._volumeStore);
354
+ get mediaState() {
355
+ return (this._mediaStateProxy ??= new VideoPlayerMediaStateProxy(this._mediaState));
858
356
  }
859
- get mutedStore() {
860
- return readonly(this._mutedStore);
357
+ get mediaStateSvelteStore() {
358
+ return (this._mediaStateSvelteStore ??= new VideoPlayerMediaStateSvelteStore(this));
861
359
  }
862
360
  }