@npo/player 1.18.4 → 1.20.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.
Files changed (67) hide show
  1. package/README.md +4 -1
  2. package/lib/js/ads/ster.js +2 -0
  3. package/lib/js/drm/handlers/decideprofile.js +17 -17
  4. package/lib/js/drm/handlers/decideprofile.test.d.ts +1 -0
  5. package/lib/js/drm/handlers/decideprofile.test.js +33 -0
  6. package/lib/js/fragments/removefragments.d.ts +1 -2
  7. package/lib/js/fragments/removefragments.js +1 -5
  8. package/lib/js/fragments/removefragments.test.d.ts +1 -0
  9. package/lib/js/fragments/removefragments.test.js +26 -0
  10. package/lib/js/fragments/setfragments.d.ts +1 -1
  11. package/lib/js/fragments/setfragments.js +19 -13
  12. package/lib/js/fragments/setfragments.test.d.ts +1 -0
  13. package/lib/js/fragments/setfragments.test.js +72 -0
  14. package/lib/js/playeractions/handlers/handleoffsets.js +8 -5
  15. package/lib/js/playeractions/handlers/processsourceconfig.js +13 -7
  16. package/lib/js/playeractions/handlers/removereplayclass.d.ts +2 -0
  17. package/lib/js/playeractions/handlers/removereplayclass.js +11 -0
  18. package/lib/js/playeractions/handlers/removereplayclass.test.d.ts +1 -0
  19. package/lib/js/playeractions/handlers/removereplayclass.test.js +28 -0
  20. package/lib/js/playeractions/handlers/resolvekeypress.test.d.ts +1 -0
  21. package/lib/js/playeractions/handlers/resolvekeypress.test.js +80 -0
  22. package/lib/js/tracking/handlers/eventlogging.test.d.ts +1 -0
  23. package/lib/js/tracking/handlers/eventlogging.test.js +46 -0
  24. package/lib/js/ui/components/controlbar.js +2 -1
  25. package/lib/js/ui/components/nativemobile/buttons.js +0 -1
  26. package/lib/js/ui/components/settingspanel.js +4 -0
  27. package/lib/js/ui/handlers/listboxhandlers.js +1 -0
  28. package/lib/js/ui/handlers/nicamhandler.d.ts +3 -0
  29. package/lib/js/ui/handlers/nicamhandler.js +16 -0
  30. package/lib/js/ui/handlers/streamhandler.js +1 -1
  31. package/lib/js/ui/nativemobileuicontainer.js +2 -2
  32. package/lib/js/ui/nativemobileuifactory.js +18 -26
  33. package/lib/js/ui/uicontainer.d.ts +1 -0
  34. package/lib/js/ui/uicontainer.js +8 -0
  35. package/lib/js/utilities/utilities.js +3 -4
  36. package/lib/npoplayer.d.ts +4 -2
  37. package/lib/npoplayer.js +117 -36
  38. package/lib/package.json +7 -7
  39. package/lib/src/js/drm/handlers/decideprofile.test.d.ts +1 -0
  40. package/lib/src/js/fragments/removefragments.d.ts +1 -2
  41. package/lib/src/js/fragments/removefragments.test.d.ts +1 -0
  42. package/lib/src/js/fragments/setfragments.d.ts +1 -1
  43. package/lib/src/js/fragments/setfragments.test.d.ts +1 -0
  44. package/lib/src/js/playeractions/handlers/removereplayclass.d.ts +2 -0
  45. package/lib/src/js/playeractions/handlers/removereplayclass.test.d.ts +1 -0
  46. package/lib/src/js/playeractions/handlers/resolvekeypress.test.d.ts +1 -0
  47. package/lib/src/js/tracking/handlers/eventlogging.test.d.ts +1 -0
  48. package/lib/src/js/ui/handlers/nicamhandler.d.ts +3 -0
  49. package/lib/src/js/ui/uicontainer.d.ts +1 -0
  50. package/lib/src/npoplayer.d.ts +4 -2
  51. package/lib/src/types/interfaces.d.ts +10 -0
  52. package/lib/tests/mocks/mockNpoplayer.d.ts +2 -0
  53. package/lib/tests/mocks/mockNpoplayer.js +115 -0
  54. package/lib/types/interfaces.d.ts +10 -0
  55. package/package.json +7 -7
  56. package/src/scss/components/_advert.scss +5 -5
  57. package/src/scss/components/_hugeplaybacktogglebutton.scss +1 -0
  58. package/src/scss/components/_metadata.scss +17 -0
  59. package/src/scss/components/_nicam.scss +20 -0
  60. package/src/scss/components/_replay.scss +2 -2
  61. package/src/scss/components/_seekbarthumbnail.scss +0 -1
  62. package/src/scss/components/_subtitles.scss +1 -1
  63. package/src/scss/npoplayer.css +39 -34
  64. package/src/scss/variants/_player-base.scss +7 -2
  65. package/lib/js/ui/handlers/playnexthandlers.d.ts +0 -0
  66. package/lib/js/ui/handlers/playnexthandlers.js +0 -1
  67. package/lib/src/js/ui/handlers/playnexthandlers.d.ts +0 -0
@@ -1,3 +1,4 @@
1
+ import { PlayerEvent } from 'bitmovin-player';
1
2
  export function processNicam(streamObject, nicamElement, streamOptions) {
2
3
  const effectiveMetadata = streamOptions ? { ...streamObject.metadata, ...streamOptions } : streamObject.metadata;
3
4
  if (nicamElement) {
@@ -24,3 +25,18 @@ export function addNicamIcon(character, nicamElement) {
24
25
  span.classList.add('nicam-icon', iconClass);
25
26
  nicamElement.appendChild(span);
26
27
  }
28
+ export function showNicamAfterUiDelay(player, uiManager) {
29
+ player.off(PlayerEvent.Play, () => { showNicamAfterUiDelay(player, uiManager); });
30
+ if (uiManager === null)
31
+ return;
32
+ const playerContainer = player.getContainer().querySelector('.bmpui-npo-player.bmpui-layout-max-width-400') || player.getContainer().querySelector('.bmpui-npo-player.bmpui-layout-max-width-600');
33
+ if (!playerContainer)
34
+ return;
35
+ uiManager.activeUi.onControlsHide.subscribeOnce(() => {
36
+ const className = 'show-nicam';
37
+ playerContainer.classList.add(className);
38
+ setTimeout(() => {
39
+ playerContainer.classList.remove(className);
40
+ }, 3000);
41
+ });
42
+ }
@@ -10,7 +10,7 @@ export function processStream(streamObject, container, streamOptions, player, ui
10
10
  const section = {
11
11
  start: seg.inpoint,
12
12
  end: seg.outpoint,
13
- title: sourceConfig.title || '',
13
+ title: sourceConfig.title || streamObject.metadata.title || '',
14
14
  };
15
15
  setFragments(player, uiManager, { sections: [section] });
16
16
  }
@@ -10,6 +10,7 @@ const defaultData = {
10
10
  defaultActionRequired: true,
11
11
  };
12
12
  export function sendCustomMessage(message, data) {
13
+ console.log('sendCustomMessage', message, data);
13
14
  try {
14
15
  if (data !== undefined) {
15
16
  return JSON.parse(window.bitmovin.customMessageHandler.sendSynchronous(message, JSON.stringify(data)));
@@ -18,8 +19,7 @@ export function sendCustomMessage(message, data) {
18
19
  return JSON.parse(window.bitmovin.customMessageHandler.sendSynchronous(message));
19
20
  }
20
21
  }
21
- catch (e) {
22
- console.warn(`Custom message ${message} may not be send, as it returned no valid data (JSON as a string)`, e);
22
+ catch {
23
23
  return defaultData;
24
24
  }
25
25
  }
@@ -17,30 +17,31 @@ export function nativeMobileUiFactory(player, config = {}) {
17
17
  const mobileUIManager = new UIManager(player, nativeMobileUIContainer(player), uiConfig);
18
18
  let _streamObject = {};
19
19
  const removeFinishedClass = () => {
20
- const playerFinishedElement = document.querySelector('.bmpui-npo-player.bmpui-player-state-finished');
20
+ player.off(PlayerEvent.Seeked, removeFinishedClass);
21
+ const playerFinishedElement = player.getContainer().querySelector('.bmpui-npo-player.bmpui-player-state-finished');
21
22
  if (playerFinishedElement) {
22
23
  playerFinishedElement.classList.remove('bmpui-player-state-finished');
23
24
  }
24
25
  };
25
26
  const clearUI = () => {
26
- const nicamElement = document.querySelector('#ui-container .bmpui-nicam');
27
+ const nicamElement = player.getContainer().querySelector('.bmpui-nicam');
27
28
  if (nicamElement) {
28
29
  nicamElement.innerHTML = '';
29
30
  }
30
- document.querySelector('.bmpui-npo-player')?.classList.remove('livestream-dvr');
31
- const element = document.querySelector('.bmpui-npo-player .bmpui-seekbar-label-title');
31
+ player.getContainer().querySelector('.bmpui-npo-player')?.classList.remove('livestream-dvr');
32
+ const element = player.getContainer().querySelector('.bmpui-seekbar-label-title');
32
33
  if (element) {
33
34
  element.textContent = '';
34
35
  }
35
- removeFragments(player, mobileUIManager);
36
+ removeFragments(mobileUIManager);
36
37
  };
37
38
  const updateUI = () => {
38
39
  if (_streamObject.metadata?.ageRating) {
39
- processNicam(_streamObject, document.querySelector('#ui-container .bmpui-nicam'), null);
40
+ processNicam(_streamObject, player.getContainer().querySelector('.bmpui-nicam'), null);
40
41
  }
41
42
  const isLiveStream = _streamObject.stream?.isLiveStream && _streamObject.stream?.hasDvrWindow;
42
43
  if (isLiveStream) {
43
- document.querySelector('.bmpui-npo-player')?.classList.add('livestream-dvr');
44
+ player.getContainer().querySelector('.bmpui-npo-player')?.classList.add('livestream-dvr');
44
45
  }
45
46
  if (_streamObject.segment) {
46
47
  const seg = _streamObject.segment;
@@ -54,7 +55,7 @@ export function nativeMobileUiFactory(player, config = {}) {
54
55
  else {
55
56
  addFragments(player, mobileUIManager, null);
56
57
  }
57
- const element = document.querySelector('.bmpui-npo-player .bmpui-seekbar-label-title');
58
+ const element = player.getContainer().querySelector('.bmpui-seekbar-label-title');
58
59
  if (element) {
59
60
  element.textContent = _streamObject.title || '';
60
61
  }
@@ -69,27 +70,18 @@ export function nativeMobileUiFactory(player, config = {}) {
69
70
  });
70
71
  }
71
72
  const addEventListeners = () => {
72
- const playbackToggleElement = document.getElementById('native-mobile-playbacktoggleoverlay');
73
+ player.off(PlayerEvent.Ready, addEventListeners);
74
+ sendCustomMessage(CustomMessages.JAVASCRIPT_READY);
75
+ const playbackToggleElement = player.getContainer().querySelector('#native-mobile-playbacktoggleoverlay');
73
76
  if (playbackToggleElement) {
74
- const playbackToggleButton = playbackToggleElement.querySelector('button');
75
- playbackToggleElement.addEventListener('click', () => {
76
- const isPlaying = playbackToggleButton?.getAttribute('aria-pressed') === 'true';
77
- sendCustomMessage(CustomMessages.TOGGLE_PLAY_PAUSE, { isChecked: !isPlaying });
77
+ playbackToggleElement.addEventListener('touchend', (event) => {
78
+ event.preventDefault();
79
+ player?.play();
80
+ sendCustomMessage(CustomMessages.TOGGLE_PLAY_PAUSE, { isChecked: true });
78
81
  });
79
82
  }
80
83
  };
81
- player.off(PlayerEvent.Seeked, () => {
82
- removeFinishedClass();
83
- });
84
- player.on(PlayerEvent.Seeked, () => {
85
- removeFinishedClass();
86
- });
87
- player.off(PlayerEvent.Ready, () => {
88
- sendCustomMessage(CustomMessages.JAVASCRIPT_READY);
89
- });
90
- player.on(PlayerEvent.Ready, () => {
91
- addEventListeners();
92
- sendCustomMessage(CustomMessages.JAVASCRIPT_READY);
93
- });
84
+ player.on(PlayerEvent.Seeked, removeFinishedClass);
85
+ player.on(PlayerEvent.Ready, addEventListeners);
94
86
  return mobileUIManager;
95
87
  }
@@ -3,3 +3,4 @@ import { UIContainer } from 'bitmovin-player-ui';
3
3
  import { PlayerAPI } from 'bitmovin-player';
4
4
  import { NpoPlayerUIVariants } from '../../types/interfaces';
5
5
  export declare function createUIContainer(npoplayer: NpoPlayer, player: PlayerAPI, variant: NpoPlayerUIVariants, container: HTMLElement): UIContainer;
6
+ export declare function removeUIContainer(container: HTMLElement): void;
@@ -77,3 +77,11 @@ export function createUIContainer(npoplayer, player, variant, container) {
77
77
  hideDelay: uiDelay
78
78
  });
79
79
  }
80
+ export function removeUIContainer(container) {
81
+ for (let i = 0; i < container.attributes.length; i += 1) {
82
+ const attributeName = container.attributes[i].name;
83
+ if (attributeName.startsWith('data-')) {
84
+ container.removeAttribute(attributeName);
85
+ }
86
+ }
87
+ }
@@ -22,10 +22,9 @@ export function localize(key) {
22
22
  return i18n.getLocalizer(key);
23
23
  }
24
24
  export function validateStreamLength(duration, seconds = false) {
25
- if (seconds) {
26
- return Number(duration);
27
- }
28
- return duration !== undefined ? msToSeconds(Number(duration)) : -1;
25
+ if (duration === undefined || duration === Infinity)
26
+ return -1;
27
+ return seconds ? (isFinite(+duration) ? Number(duration) : -1) : msToSeconds(Number(duration));
29
28
  }
30
29
  export function msToSeconds(ms) {
31
30
  return ms / 1000;
@@ -32,6 +32,8 @@ export default class NpoPlayer {
32
32
  createUIManager(player: PlayerAPI, variant: NpoPlayerUIVariants): Promise<void>;
33
33
  doError(input: any, status?: number): void;
34
34
  keyPress(e: KeyboardEvent): void;
35
+ play(): void;
36
+ pause(): void;
35
37
  setVolume(volume: number): void;
36
38
  increaseVolume(): void;
37
39
  decreaseVolume(): void;
@@ -42,7 +44,7 @@ export default class NpoPlayer {
42
44
  cancelPlayNextScreen(): void;
43
45
  hidePlayNextScreen(): void;
44
46
  doPlayNext(): void;
45
- destroy(): boolean;
46
- unload(): boolean;
47
+ destroy(asyncMode?: boolean): Promise<boolean> | boolean;
48
+ unload(asyncMode?: boolean): Promise<boolean> | boolean;
47
49
  printVersion(): void;
48
50
  }
package/lib/npoplayer.js CHANGED
@@ -9,7 +9,7 @@ import { getStreamObject } from './js/api/getstreamobject';
9
9
  import { printVersion } from './js/utilities/printversion';
10
10
  import { UIManager } from 'bitmovin-player-ui';
11
11
  import { customSpecificErrorMessageOverlayConfig } from './js/playeractions/handlers/customerrors';
12
- import { createUIContainer } from './js/ui/uicontainer';
12
+ import { createUIContainer, removeUIContainer } from './js/ui/uicontainer';
13
13
  import { addAccessibilityAttributes } from './js/ui/handlers/accessibilityhandler';
14
14
  import { LogEmitter } from './types/classes';
15
15
  import { handlePreRolls } from './js/ads/ster';
@@ -18,6 +18,8 @@ import { NpoPlayerUIVariants } from './types/interfaces';
18
18
  import { nativeMobileUiFactory } from './js/ui/nativemobileuifactory';
19
19
  import { setupMediaSessionActionHandlers } from './js/playeractions/handlers/mediasessionactions';
20
20
  import { processStream } from './js/ui/handlers/streamhandler';
21
+ import { removeReplayClass } from './js/playeractions/handlers/removereplayclass';
22
+ import { showNicamAfterUiDelay } from './js/ui/handlers/nicamhandler';
21
23
  export { NpoPlayerUIVariants, };
22
24
  export default class NpoPlayer {
23
25
  constructor(_container, _playerConfig, _npotag, _npotaginstance, _variant) {
@@ -55,6 +57,13 @@ export default class NpoPlayer {
55
57
  this.container.addEventListener('keydown', e => {
56
58
  this.keyPress(e);
57
59
  }, true);
60
+ const playbackToggleElement = this.container.querySelector('.bmpui-ui-playbacktoggle-overlay');
61
+ if (playbackToggleElement) {
62
+ playbackToggleElement.addEventListener('touchend', (event) => {
63
+ event.preventDefault();
64
+ this.player?.play();
65
+ });
66
+ }
58
67
  }
59
68
  async loadStream(source, options = {}) {
60
69
  this.streamOptions = options;
@@ -70,13 +79,21 @@ export default class NpoPlayer {
70
79
  const isUrl = utility.isUrl(source);
71
80
  const isMedia = await utility.isMediaUrl(source);
72
81
  const isJWTToken = utility.isJWTToken(source);
82
+ const getDurationAndStartPlayerTracker = function () {
83
+ if (this.player === null)
84
+ return;
85
+ let prid = (utility.isJWTToken(source) ? this.streamObject?.metadata.prid : source) || 'unknown';
86
+ this.player.off(PlayerEvent.SourceLoaded, getDurationAndStartPlayerTracker);
87
+ let duration = this.player ? this.player.getDuration() : undefined;
88
+ startPlayerTracker(this, utility.validateStreamLength(duration, true), this.version, prid);
89
+ }.bind(this);
73
90
  if (isUrl && isMedia) {
74
91
  this.sourceConfig = {
75
92
  ...options.sourceConfig,
76
93
  'progressive': source
77
94
  };
78
95
  this.player?.load(this.sourceConfig);
79
- startPlayerTracker(this, utility.validateStreamLength(this.player?.getDuration(), true), this.version, source);
96
+ getDurationAndStartPlayerTracker();
80
97
  }
81
98
  else if (isJWTToken) {
82
99
  this.jwt = source;
@@ -86,7 +103,17 @@ export default class NpoPlayer {
86
103
  const payload = {
87
104
  baseURL: endpoint,
88
105
  jwt: source,
89
- data: { profileName: profile.profileName, drmType: profile.drm, referrerUrl: window.location.href }
106
+ data: {
107
+ profileName: profile.profileName,
108
+ drmType: profile.drm,
109
+ referrerUrl: window.location.href,
110
+ ster: {
111
+ identifier: options?.ster?.identifier ?? 'npo-app-desktop',
112
+ deviceType: options?.ster?.deviceType ?? 4,
113
+ site: options?.ster?.site ?? 'npo',
114
+ player: 'web'
115
+ }
116
+ }
90
117
  };
91
118
  try {
92
119
  _streamObject = await getStreamObject(this, payload);
@@ -107,7 +134,14 @@ export default class NpoPlayer {
107
134
  await drm.verifyDRM(this, this.player, payload);
108
135
  setupMediaSessionActionHandlers(this.player, this.sourceConfig, _streamObject);
109
136
  logEvent(this, 'load');
110
- startPlayerTracker(this, utility.validateStreamLength(_streamObject.metadata.duration, false), this.version, _streamObject.metadata.prid);
137
+ let streamDuration = _streamObject.metadata.duration;
138
+ let streamPrid = this.streamObject.metadata.prid || _streamObject.metadata.prid;
139
+ streamDuration = streamDuration == null
140
+ ? this.player.on(PlayerEvent.SourceLoaded, getDurationAndStartPlayerTracker)
141
+ : startPlayerTracker(this, utility.validateStreamLength(streamDuration, false), this.version, streamPrid);
142
+ if (this.isShowingPlayNextScreen) {
143
+ this.hidePlayNextScreen();
144
+ }
111
145
  if (this.variant !== NpoPlayerUIVariants.AUDIO) {
112
146
  await handlePreRolls(this.player, _streamObject, this);
113
147
  }
@@ -115,15 +149,23 @@ export default class NpoPlayer {
115
149
  else {
116
150
  this.doError(`Het is niet gelukt de stream op te halen: \n Input is geen valide token of media object.`, 500);
117
151
  }
118
- this.player.off(PlayerEvent.SourceLoaded, () => { void addAccessibilityAttributes(this.container, this.streamObject.metadata); });
119
- this.player.on(PlayerEvent.SourceLoaded, () => { void addAccessibilityAttributes(this.container, this.streamObject.metadata); });
120
- this.player.off(PlayerEvent.AdBreakFinished, () => { void addAccessibilityAttributes(this.container, this.streamObject.metadata); });
121
- this.player.on(PlayerEvent.AdBreakFinished, () => { void addAccessibilityAttributes(this.container, this.streamObject.metadata); });
152
+ const triggerAddAccessibilityAttributes = () => {
153
+ this.player?.off(PlayerEvent.SourceLoaded, triggerAddAccessibilityAttributes);
154
+ this.player?.off(PlayerEvent.AdBreakFinished, triggerAddAccessibilityAttributes);
155
+ this.player?.off(PlayerEvent.AdError, triggerAddAccessibilityAttributes);
156
+ void addAccessibilityAttributes(this.container, this.streamObject.metadata);
157
+ };
158
+ this.player.on(PlayerEvent.SourceLoaded, triggerAddAccessibilityAttributes);
159
+ this.player.on(PlayerEvent.AdBreakFinished, triggerAddAccessibilityAttributes);
160
+ this.player.on(PlayerEvent.AdError, triggerAddAccessibilityAttributes);
122
161
  if (this.sourceConfig?.metadata) {
123
162
  this.sourceConfig.metadata.jwt = source;
124
163
  this.sourceConfig.metadata.streamLinkAsJsonString = JSON.stringify(this.streamObject);
125
164
  this.sourceConfig.metadata.npoTagSession = String(this.npoTag?.npoTagInstance?.getSerializedSessionInfo());
126
165
  }
166
+ this.player.on(PlayerEvent.Seek, () => {
167
+ removeReplayClass(this.player);
168
+ });
127
169
  if (options?.playNext?.showPlayNext === true) {
128
170
  this.player.on(PlayerEvent.TimeChanged, () => {
129
171
  this.showPlayNextScreen();
@@ -132,6 +174,7 @@ export default class NpoPlayer {
132
174
  this.hidePlayNextScreen();
133
175
  });
134
176
  }
177
+ this.player.on(PlayerEvent.Play, () => { showNicamAfterUiDelay(this.player, this.uiManager); });
135
178
  if (options?.startOffset != null)
136
179
  playerAction.handleStartOffset(this.player, options.startOffset);
137
180
  const setLiveOffsetListener = function () {
@@ -143,15 +186,15 @@ export default class NpoPlayer {
143
186
  this.player.on(PlayerEvent.SourceLoaded, setLiveOffsetListener);
144
187
  }
145
188
  async createUIManager(player, variant) {
146
- const uiConfig = {
147
- errorMessages: customSpecificErrorMessageOverlayConfig,
148
- disableAutoHideWhenHovered: true
149
- };
150
- if (!this.uiManager || variant !== this.variant) {
189
+ if (this.uiManager === null || variant !== this.variant) {
190
+ const uiConfig = {
191
+ errorMessages: customSpecificErrorMessageOverlayConfig,
192
+ disableAutoHideWhenHovered: true
193
+ };
151
194
  this.uiManager = new UIManager(player, createUIContainer(this, player, variant, this.container), uiConfig);
152
195
  this.variant = variant;
153
196
  }
154
- if (this.uiManager) {
197
+ else {
155
198
  processStream(this.streamObject, this.container, this.streamOptions, player, this.uiManager, this.sourceConfig);
156
199
  }
157
200
  }
@@ -170,6 +213,16 @@ export default class NpoPlayer {
170
213
  return;
171
214
  playerAction.resolveKeyPress(this.player, this, e);
172
215
  }
216
+ play() {
217
+ if (this.player == null)
218
+ return;
219
+ this.player.play();
220
+ }
221
+ pause() {
222
+ if (this.player == null)
223
+ return;
224
+ this.player.pause();
225
+ }
173
226
  setVolume(volume) {
174
227
  if (this.player == null)
175
228
  return;
@@ -211,7 +264,7 @@ export default class NpoPlayer {
211
264
  playerAction.shiftToProgramStart(this.player, this.streamOptions.liveProgramTime);
212
265
  }
213
266
  showPlayNextScreen() {
214
- if (this.player == null || !this.streamOptions.playNext?.showPlayNext)
267
+ if (this.player == null || !this.streamOptions.playNext?.showPlayNext || this.adBreakActive)
215
268
  return;
216
269
  this.player.off(PlayerEvent.TimeChanged, () => {
217
270
  this.showPlayNextScreen();
@@ -245,33 +298,61 @@ export default class NpoPlayer {
245
298
  this.hidePlayNextScreen();
246
299
  void this.streamOptions.playNext?.proceedCallback?.();
247
300
  }
248
- destroy() {
249
- try {
250
- if (this.npoTag != null) {
251
- clearInterval(this.npoTag.heartbeatInterval);
301
+ destroy(asyncMode = false) {
302
+ const destroyLogic = async () => {
303
+ try {
304
+ if (this.npoTag != null) {
305
+ clearInterval(this.npoTag.heartbeatInterval);
306
+ }
307
+ logEvent(this, 'stop');
308
+ const playerContainer = this.player?.getContainer();
309
+ if (playerContainer)
310
+ removeUIContainer(playerContainer);
311
+ this.uiManager?.release();
312
+ this.uiManager = null;
313
+ await this.player?.destroy();
314
+ return true;
252
315
  }
253
- logEvent(this, 'stop');
254
- this.uiManager?.release();
255
- this.uiManager = null;
256
- void this.player?.destroy();
257
- return true;
316
+ catch (e) {
317
+ console.error('Error destroying player:', e);
318
+ return false;
319
+ }
320
+ };
321
+ if (asyncMode) {
322
+ return destroyLogic().then(() => true).catch(() => false);
258
323
  }
259
- catch (e) {
260
- console.error('NPO Player is al destroyed', e);
261
- return false;
324
+ else {
325
+ return destroyLogic();
262
326
  }
263
327
  }
264
- unload() {
265
- try {
266
- if (this.npoTag != null) {
267
- clearInterval(this.npoTag.heartbeatInterval);
328
+ unload(asyncMode = false) {
329
+ const unloadLogic = async () => {
330
+ try {
331
+ if (this.npoTag != null) {
332
+ clearInterval(this.npoTag.heartbeatInterval);
333
+ }
334
+ const playerContainer = this.player?.getContainer();
335
+ if (playerContainer)
336
+ removeUIContainer(playerContainer);
337
+ this.hidePlayNextScreen();
338
+ await this.player?.unload();
339
+ return true;
268
340
  }
269
- void this.player?.unload();
270
- return true;
341
+ catch (e) {
342
+ console.error('Error unloading player:', e);
343
+ return false;
344
+ }
345
+ };
346
+ if (asyncMode) {
347
+ return unloadLogic();
271
348
  }
272
- catch (e) {
273
- console.error('NPO Player is al unloaded', e);
274
- return false;
349
+ else {
350
+ try {
351
+ return unloadLogic();
352
+ }
353
+ catch (error) {
354
+ return false;
355
+ }
275
356
  }
276
357
  }
277
358
  printVersion() {
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npo/player",
3
- "version": "1.18.4",
3
+ "version": "1.20.0",
4
4
  "description": "NPO Player",
5
5
  "author": "Publieke Omroep <player@npo.nl>",
6
6
  "contributors": [
@@ -9,7 +9,7 @@
9
9
  "Arjan Kruithof"
10
10
  ],
11
11
  "engines": {
12
- "node": "^18.16.0"
12
+ "node": "^18 || ^20"
13
13
  },
14
14
  "license": "ISC",
15
15
  "main": "./lib/npoplayer",
@@ -24,7 +24,7 @@
24
24
  "bundle": "npm run lint && bash build-lib.sh && npm run build:scss && npm run build:cdn",
25
25
  "build:ts": "bash build-lib.sh",
26
26
  "test": "jest",
27
- "test:ci": "node_modules/.bin/jest",
27
+ "test:ci": "jest --collectCoverage --testResultsProcessor=jest-sonar-reporter",
28
28
  "version:dev": "npm version prerelease --preid=dev --no-git-tag-version"
29
29
  },
30
30
  "files": [
@@ -55,7 +55,7 @@
55
55
  "eslint-plugin-import": "^2.27.5",
56
56
  "eslint-plugin-n": "^15.7.0",
57
57
  "eslint-plugin-promise": "^6.1.1",
58
- "eslint-plugin-sonarjs": "^0.19.0",
58
+ "eslint-plugin-sonarjs": "^0.23.0",
59
59
  "fs-extra": "^11.1.1",
60
60
  "jest": "^28.1.3",
61
61
  "jest-environment-jsdom": "^29.4.1",
@@ -81,9 +81,9 @@
81
81
  "webpack-dev-server": "^4.11.1"
82
82
  },
83
83
  "dependencies": {
84
- "@npotag/tag": "^3.0.0",
85
- "bitmovin-player": "8.125.0",
86
- "bitmovin-player-ui": "^3.48.0"
84
+ "@npotag/tag": "^3.0.1",
85
+ "bitmovin-player": "8.151.0",
86
+ "bitmovin-player-ui": "3.54.0"
87
87
  },
88
88
  "browserslist": [
89
89
  "defaults",
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,2 @@
1
- import { PlayerAPI } from 'bitmovin-player';
2
1
  import { UIManager } from 'bitmovin-player-ui';
3
- export declare function removeFragments(player: PlayerAPI, uiManager: UIManager): Promise<void>;
2
+ export declare function removeFragments(uiManager: UIManager): void;
@@ -0,0 +1 @@
1
+ export {};
@@ -3,4 +3,4 @@ import { type Fragments } from '../../types/interfaces';
3
3
  import { UIManager } from 'bitmovin-player-ui';
4
4
  export declare const checkFunction: () => (e: any) => void;
5
5
  export declare const seekFunction: () => (e: any) => void;
6
- export declare function setFragments(player: PlayerAPI, uiManager: UIManager, fragments: Fragments): Promise<void>;
6
+ export declare function setFragments(player: PlayerAPI, uiManager: UIManager, fragments: Fragments): void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { PlayerAPI } from 'bitmovin-player';
2
+ export declare function removeReplayClass(player: PlayerAPI | null): void;
@@ -1,3 +1,6 @@
1
+ import { PlayerAPI } from 'bitmovin-player';
2
+ import { UIManager } from 'bitmovin-player-ui';
1
3
  import { StreamObject, StreamOptions } from 'types/interfaces';
2
4
  export declare function processNicam(streamObject: StreamObject, nicamElement: HTMLElement | null, streamOptions: StreamOptions | null): void;
3
5
  export declare function addNicamIcon(character: string, nicamElement: Element): void;
6
+ export declare function showNicamAfterUiDelay(player: PlayerAPI, uiManager: UIManager | null): void;
@@ -3,3 +3,4 @@ import { UIContainer } from 'bitmovin-player-ui';
3
3
  import { PlayerAPI } from 'bitmovin-player';
4
4
  import { NpoPlayerUIVariants } from '../../types/interfaces';
5
5
  export declare function createUIContainer(npoplayer: NpoPlayer, player: PlayerAPI, variant: NpoPlayerUIVariants, container: HTMLElement): UIContainer;
6
+ export declare function removeUIContainer(container: HTMLElement): void;
@@ -32,6 +32,8 @@ export default class NpoPlayer {
32
32
  createUIManager(player: PlayerAPI, variant: NpoPlayerUIVariants): Promise<void>;
33
33
  doError(input: any, status?: number): void;
34
34
  keyPress(e: KeyboardEvent): void;
35
+ play(): void;
36
+ pause(): void;
35
37
  setVolume(volume: number): void;
36
38
  increaseVolume(): void;
37
39
  decreaseVolume(): void;
@@ -42,7 +44,7 @@ export default class NpoPlayer {
42
44
  cancelPlayNextScreen(): void;
43
45
  hidePlayNextScreen(): void;
44
46
  doPlayNext(): void;
45
- destroy(): boolean;
46
- unload(): boolean;
47
+ destroy(asyncMode?: boolean): Promise<boolean> | boolean;
48
+ unload(asyncMode?: boolean): Promise<boolean> | boolean;
47
49
  printVersion(): void;
48
50
  }
@@ -15,6 +15,15 @@ export interface ApiPayload {
15
15
  baseURL: string;
16
16
  jwt: string;
17
17
  data: Record<string, unknown>;
18
+ ster?: SterData;
19
+ }
20
+ interface SterData {
21
+ identifier: string;
22
+ site?: string;
23
+ deviceType?: number;
24
+ os?: string;
25
+ osVersion?: string;
26
+ player?: string;
18
27
  }
19
28
  export interface DRMProfile {
20
29
  profileName: string;
@@ -86,6 +95,7 @@ export interface StreamOptions {
86
95
  ageRating?: string;
87
96
  nicam?: string[] | null;
88
97
  playNext?: PlayNext;
98
+ ster?: SterData;
89
99
  }
90
100
  export interface UIComponents {
91
101
  errorMessageOverlay?: ErrorMessageOverlay;
@@ -0,0 +1,2 @@
1
+ import type NpoPlayer from '../../src/npoplayer';
2
+ export declare const mockNpoPlayer: NpoPlayer;