@npo/player 1.23.2 → 1.24.1

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 (146) hide show
  1. package/README.md +1 -1
  2. package/lib/js/playeractions/handlers/error.js +2 -2
  3. package/lib/js/playeractions/handlers/error.test.js +2 -2
  4. package/lib/js/playeractions/handlers/handleoffsets.d.ts +4 -6
  5. package/lib/js/playeractions/handlers/handleoffsets.js +16 -17
  6. package/lib/js/playeractions/handlers/handleoffsets.test.js +73 -29
  7. package/lib/js/playeractions/handlers/mediasessionactions.js +24 -12
  8. package/lib/js/playeractions/handlers/processsourceconfig.js +16 -3
  9. package/lib/js/tracking/handlers/eventbinding.d.ts +1 -1
  10. package/lib/js/tracking/handlers/eventbinding.js +17 -23
  11. package/lib/js/tracking/handlers/eventlogging.d.ts +1 -1
  12. package/lib/js/tracking/handlers/eventlogging.js +18 -18
  13. package/lib/js/tracking/handlers/eventlogging.test.js +24 -24
  14. package/lib/js/tracking/handlers/playertrackerstart.js +4 -2
  15. package/lib/lang/nl.json +3 -1
  16. package/lib/npoplayer.d.ts +1 -2
  17. package/lib/npoplayer.js +30 -44
  18. package/lib/package.json +5 -5
  19. package/lib/services/a11y/setup.js +2 -2
  20. package/lib/services/a11y/setup.test.js +7 -8
  21. package/lib/services/advertHandlers/discardAdBreak.d.ts +2 -0
  22. package/lib/services/advertHandlers/discardAdBreak.js +11 -0
  23. package/lib/services/advertHandlers/discardAdBreak.test.js +60 -0
  24. package/lib/services/advertHandlers/handlePreRolls.d.ts +2 -0
  25. package/lib/services/advertHandlers/handlePreRolls.js +132 -0
  26. package/lib/services/advertHandlers/handlePrerolls.test.js +52 -0
  27. package/lib/services/cdnProviders/cdnProviders.d.ts +4 -0
  28. package/lib/services/cdnProviders/cdnProviders.js +22 -0
  29. package/lib/services/cdnProviders/cndProviders.test.d.ts +1 -0
  30. package/lib/services/cdnProviders/cndProviders.test.js +22 -0
  31. package/lib/services/eventListenerHandlers/removeEventListeners.d.ts +1 -1
  32. package/lib/services/eventListenerHandlers/removeEventListeners.js +4 -4
  33. package/lib/services/eventListenerHandlers/removeEventListeners.test.js +28 -13
  34. package/lib/services/liveStreamHandlers/handleLiveStreamControls.d.ts +1 -1
  35. package/lib/services/liveStreamHandlers/handleLiveStreamControls.js +3 -3
  36. package/lib/services/liveStreamHandlers/handleLiveStreamControls.test.d.ts +2 -0
  37. package/lib/services/liveStreamHandlers/handleLiveStreamControls.test.js +65 -0
  38. package/lib/services/localStorageHandlers/localStorageHandlers.d.ts +1 -1
  39. package/lib/services/localStorageHandlers/localStorageHandlers.test.js +0 -1
  40. package/lib/services/npoPlayerAPI/contants.d.ts +5 -0
  41. package/lib/services/npoPlayerAPI/contants.js +5 -0
  42. package/lib/services/npoPlayerAPI/npoPlayerAPI.d.ts +17 -4
  43. package/lib/services/npoPlayerAPI/npoPlayerAPI.js +68 -3
  44. package/lib/services/npoPlayerAPI/npoPlayerAPI.test.js +66 -1
  45. package/lib/services/segmentHandlers/addSegmentEventListeners.js +3 -3
  46. package/lib/services/segmentHandlers/addSegmentEventListeners.test.js +10 -9
  47. package/lib/services/segmentHandlers/convertFragmentToSegment.d.ts +1 -1
  48. package/lib/services/segmentHandlers/handleSegmentSeek.d.ts +1 -1
  49. package/lib/services/segmentHandlers/handleSegmentSeek.test.js +4 -4
  50. package/lib/services/segmentHandlers/handleSegmentTimeChanged.d.ts +1 -1
  51. package/lib/services/segmentHandlers/handleSegmentTimeChanged.test.js +33 -24
  52. package/lib/services/segmentHandlers/initSegment.d.ts +1 -1
  53. package/lib/services/segmentHandlers/initSegment.test.js +3 -1
  54. package/lib/services/segmentHandlers/setSegmentMarkers.d.ts +1 -1
  55. package/lib/services/services.d.ts +3 -1
  56. package/lib/services/services.js +8 -0
  57. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.d.ts +2 -0
  58. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.js +8 -0
  59. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.test.d.ts +1 -0
  60. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.test.js +36 -0
  61. package/lib/src/js/playeractions/handlers/handleoffsets.d.ts +4 -6
  62. package/lib/src/js/tracking/handlers/eventbinding.d.ts +1 -1
  63. package/lib/src/js/tracking/handlers/eventlogging.d.ts +1 -1
  64. package/lib/src/npoplayer.d.ts +1 -2
  65. package/lib/src/services/advertHandlers/discardAdBreak.d.ts +2 -0
  66. package/lib/src/services/advertHandlers/discardAdBreak.test.d.ts +1 -0
  67. package/lib/src/services/advertHandlers/handlePreRolls.d.ts +2 -0
  68. package/lib/src/services/advertHandlers/handlePrerolls.test.d.ts +1 -0
  69. package/lib/src/services/cdnProviders/cdnProviders.d.ts +4 -0
  70. package/lib/src/services/cdnProviders/cndProviders.test.d.ts +1 -0
  71. package/lib/src/services/eventListenerHandlers/removeEventListeners.d.ts +1 -1
  72. package/lib/src/services/liveStreamHandlers/handleLiveStreamControls.d.ts +1 -1
  73. package/lib/src/services/liveStreamHandlers/handleLiveStreamControls.test.d.ts +2 -0
  74. package/lib/src/services/localStorageHandlers/localStorageHandlers.d.ts +1 -1
  75. package/lib/src/services/npoPlayerAPI/contants.d.ts +5 -0
  76. package/lib/src/services/npoPlayerAPI/npoPlayerAPI.d.ts +17 -4
  77. package/lib/src/services/segmentHandlers/convertFragmentToSegment.d.ts +1 -1
  78. package/lib/src/services/segmentHandlers/handleSegmentSeek.d.ts +1 -1
  79. package/lib/src/services/segmentHandlers/handleSegmentTimeChanged.d.ts +1 -1
  80. package/lib/src/services/segmentHandlers/initSegment.d.ts +1 -1
  81. package/lib/src/services/segmentHandlers/setSegmentMarkers.d.ts +1 -1
  82. package/lib/src/services/services.d.ts +3 -1
  83. package/lib/src/services/verticalVideoHandlers/handleVerticalVideoControls.d.ts +2 -0
  84. package/lib/src/services/verticalVideoHandlers/handleVerticalVideoControls.test.d.ts +1 -0
  85. package/lib/src/types/events.d.ts +8 -1
  86. package/lib/src/types/interfaces.d.ts +8 -12
  87. package/lib/src/ui/components/audio/controlbar.d.ts +1 -1
  88. package/lib/src/ui/components/controlbar.d.ts +1 -1
  89. package/lib/src/ui/components/seekbar.d.ts +1 -1
  90. package/lib/src/ui/components/settingspanel.d.ts +1 -1
  91. package/lib/src/ui/components/verticalvideo/controlbar.d.ts +3 -0
  92. package/lib/src/ui/handlers/streamhandler.d.ts +2 -4
  93. package/lib/src/ui/uicontainer.d.ts +2 -2
  94. package/lib/tests/mocks/mockNpoplayer.js +0 -1
  95. package/lib/tests/mocks/playerContextMock.d.ts +67 -0
  96. package/lib/tests/mocks/playerContextMock.js +118 -0
  97. package/lib/types/events.d.ts +8 -1
  98. package/lib/types/events.js +184 -1
  99. package/lib/types/interfaces.d.ts +8 -12
  100. package/lib/types/interfaces.js +1 -0
  101. package/lib/ui/components/audio/controlbar.d.ts +1 -1
  102. package/lib/ui/components/audio/controlbar.js +6 -6
  103. package/lib/ui/components/controlbar.d.ts +1 -1
  104. package/lib/ui/components/controlbar.js +13 -11
  105. package/lib/ui/components/nativemobile/controlbar.js +0 -1
  106. package/lib/ui/components/seekbar.js +1 -1
  107. package/lib/ui/components/settingspanel.d.ts +1 -1
  108. package/lib/ui/components/settingspanel.js +68 -84
  109. package/lib/ui/components/topbar.js +6 -3
  110. package/lib/ui/components/verticalvideo/controlbar.d.ts +3 -0
  111. package/lib/ui/components/verticalvideo/controlbar.js +34 -0
  112. package/lib/ui/handlers/streamhandler.d.ts +2 -4
  113. package/lib/ui/handlers/streamhandler.js +16 -7
  114. package/lib/ui/nativemobileuifactory.js +2 -0
  115. package/lib/ui/uicontainer.d.ts +2 -2
  116. package/lib/ui/uicontainer.js +35 -29
  117. package/package.json +5 -5
  118. package/src/style/components/_settingspanel.scss +35 -3
  119. package/src/style/components/_subtitles.scss +29 -25
  120. package/src/style/components/_textbuttons.scss +2 -2
  121. package/src/style/components/_volumeslider.scss +1 -0
  122. package/src/style/components/vertical-video/_bottombar.scss +19 -0
  123. package/src/style/components/vertical-video/_buttons.scss +23 -0
  124. package/src/style/components/vertical-video/_hugeplaybacktogglebutton.scss +14 -0
  125. package/src/style/components/vertical-video/_seekbar.scss +19 -0
  126. package/src/style/components/vertical-video/_settingsbutton.scss +7 -0
  127. package/src/style/components/vertical-video/_settingspanel.scss +14 -0
  128. package/src/style/components/vertical-video/_shortvideo.scss +14 -0
  129. package/src/style/components/vertical-video/_subtitles.scss +3 -0
  130. package/src/style/components/vertical-video/_topbar.scss +17 -0
  131. package/src/style/components/vertical-video/_volumeslider.scss +9 -0
  132. package/src/style/npoplayer.css +74 -34
  133. package/src/style/npoplayer.scss +2 -1
  134. package/src/style/variants/_player-small.scss +18 -10
  135. package/src/style/variants/_player-vertical.scss +23 -0
  136. package/lib/js/ads/ster.d.ts +0 -4
  137. package/lib/js/ads/ster.js +0 -126
  138. package/lib/js/ads/ster.test.js +0 -63
  139. package/lib/js/cdnproviders.d.ts +0 -1
  140. package/lib/js/cdnproviders.js +0 -16
  141. package/lib/src/js/ads/ster.d.ts +0 -4
  142. package/lib/src/js/cdnproviders.d.ts +0 -1
  143. package/lib/tests/mocks/mockPlayerContext.d.ts +0 -2
  144. package/lib/tests/mocks/mockPlayerContext.js +0 -40
  145. /package/lib/{js/ads/ster.test.d.ts → services/advertHandlers/discardAdBreak.test.d.ts} +0 -0
  146. /package/lib/{src/js/ads/ster.test.d.ts → services/advertHandlers/handlePrerolls.test.d.ts} +0 -0
package/lib/npoplayer.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { NpoPlayerServices } from './services/services';
2
2
  import { Player, PlayerEvent } from 'bitmovin-player';
3
- import { UIManager } from 'bitmovin-player-ui';
4
3
  import AdsModuleBM from 'bitmovin-player/modules/bitmovinplayer-advertising-bitmovin';
5
4
  import { logEvent, initPlayerTracker, startPlayerTracker } from './js/tracking/playertracker';
6
5
  import { hidePlayNextScreen, showPlayNextScreenIfNeeded } from './ui/handlers/playnextscreen';
@@ -11,19 +10,17 @@ import { isJWTToken } from './js/utilities/utilities.jwt';
11
10
  import { isMediaUrl, isUrl } from './js/utilities/utilities.url';
12
11
  import { logVersion } from './js/utilities/utilities.version';
13
12
  import { getStreamDurationInSeconds } from './js/utilities/utilities.stream';
14
- import { customSpecificErrorMessageOverlayConfig } from './js/playeractions/handlers/customerrors';
15
- import { createUIContainer } from './ui/uicontainer';
16
13
  import { LogEmitter } from './types/classes';
17
- import { handlePreRolls } from './js/ads/ster';
14
+ import { handlePreRolls } from './services/advertHandlers/handlePreRolls';
18
15
  import pkg from '../package.json';
19
16
  import { NpoPlayerUIVariants, LocalStorageValues } from './types/interfaces';
20
17
  import { nativeMobileUiFactory } from './ui/nativemobileuifactory';
21
18
  import { setupMediaSessionActionHandlers } from './js/playeractions/handlers/mediasessionactions';
22
- import { processStream } from './ui/handlers/streamhandler';
23
19
  import { removeReplayClass } from './js/playeractions/handlers/removereplayclass';
24
20
  import { showNicamAfterUiDelay } from './ui/handlers/nicamhandler';
25
21
  import { updateLiveMarkers } from './js/markers/updateLiveMarkers';
26
22
  import { NpoPlayerAPI } from './services/npoPlayerAPI/npoPlayerAPI';
23
+ import { discardAdBreak } from './services/advertHandlers/discardAdBreak';
27
24
  export { NpoPlayerUIVariants };
28
25
  export default class NpoPlayer {
29
26
  constructor(_container, _playerConfig, _npotag, _npotaginstance, _variant) {
@@ -55,14 +52,17 @@ export default class NpoPlayer {
55
52
  this.container = _container;
56
53
  this.variant = _variant || NpoPlayerUIVariants.DEFAULT;
57
54
  initPlayerTracker(this, _npotag, _npotaginstance);
58
- this.initPlayer(this.container, _playerConfig, this.variant);
55
+ this.initPlayer(this.container, _playerConfig);
59
56
  }
60
- initPlayer(_container, playerConfig, variant) {
57
+ initPlayer(_container, playerConfig) {
61
58
  const processedPlayerConfig = playerAction.processPlayerConfig(this, playerConfig);
62
- Player.addModule(getModuleExport(AdsModuleBM));
63
- this.userPreferences = this.npoplayerServices.getStoredUserPrefs();
64
59
  this.player = new Player(_container, processedPlayerConfig);
60
+ Player.addModule(getModuleExport(AdsModuleBM));
65
61
  const npoPlayerAPI = new NpoPlayerAPI(this.player);
62
+ this.playerContext = { player: npoPlayerAPI, npoplayer: this };
63
+ this.userPreferences = this.npoplayerServices.getStoredUserPrefs();
64
+ this.npoplayerServices.handleUserPrefs(this.playerContext);
65
+ this.npoplayerServices.setAccessibilityAttributes(this.playerContext);
66
66
  window.addEventListener('beforeunload', () => {
67
67
  if (this.player) {
68
68
  this.npoplayerServices.setStoredUserPrefs(LocalStorageValues.SUBTITLES_ENABLED, npoPlayerAPI.areSubtitlesEnabled().toString());
@@ -70,10 +70,6 @@ export default class NpoPlayer {
70
70
  this.npoplayerServices.setStoredUserPrefs(LocalStorageValues.IS_MUTED, npoPlayerAPI.isMuted().toString());
71
71
  }
72
72
  });
73
- this.playerContext = { player: npoPlayerAPI, npoplayer: this };
74
- this.createUIManager(this.player, this.playerContext, variant);
75
- this.npoplayerServices.handleUserPrefs(this.playerContext);
76
- this.npoplayerServices.setAccessibilityAttributes(this.playerContext);
77
73
  this.container.addEventListener('keydown', (e) => {
78
74
  if (!this.playerContext)
79
75
  return;
@@ -88,9 +84,8 @@ export default class NpoPlayer {
88
84
  console.error('Er is nog geen player geladen.');
89
85
  return;
90
86
  }
91
- if (this.adBreakActive) {
92
- console.error('Je kunt geen nieuwe content laden tijdens een reclameblok.');
93
- return;
87
+ if (this.playerContext) {
88
+ discardAdBreak(this.playerContext);
94
89
  }
95
90
  let _streamObject;
96
91
  const sourceIsUrl = isUrl(source);
@@ -140,7 +135,7 @@ export default class NpoPlayer {
140
135
  _streamObject = await getStreamObject(this, payload);
141
136
  this.streamObject = _streamObject;
142
137
  if (_streamObject) {
143
- await this.createUIManager(this.player, this.playerContext, this.variant);
138
+ await this.playerContext.player.createUIManager(this.playerContext, this.variant);
144
139
  }
145
140
  }
146
141
  catch (error) {
@@ -170,12 +165,12 @@ export default class NpoPlayer {
170
165
  ? getDurationAndStartPlayerTracker()
171
166
  : startPlayerTracker(this, getStreamDurationInSeconds({ duration: streamDuration, durationIsInMs: true }), this.version, streamPrid);
172
167
  };
173
- if (this.variant !== NpoPlayerUIVariants.AUDIO &&
168
+ if (this.variant === NpoPlayerUIVariants.DEFAULT &&
174
169
  this.streamObject.metadata.hasPreroll === 'true' &&
175
170
  this.streamObject.assets.preroll) {
176
171
  this.player.on(PlayerEvent.AdBreakFinished, initAndStartTracker);
177
172
  this.player.on(PlayerEvent.AdError, initAndStartTracker);
178
- await handlePreRolls(this.player, this.streamObject, this);
173
+ await handlePreRolls(this.playerContext);
179
174
  }
180
175
  else {
181
176
  initAndStartTracker();
@@ -184,6 +179,7 @@ export default class NpoPlayer {
184
179
  else {
185
180
  this.doError(`Het is niet gelukt de stream op te halen: \n Input is geen valide token of media object.`, 500);
186
181
  }
182
+ this.npoplayerServices.handleVerticalVideoControls(this.playerContext, this.variant);
187
183
  if (this.sourceConfig?.metadata) {
188
184
  this.sourceConfig.metadata.jwt = source;
189
185
  this.sourceConfig.metadata.streamLinkAsJsonString = JSON.stringify(this.streamObject);
@@ -204,26 +200,12 @@ export default class NpoPlayer {
204
200
  showNicamAfterUiDelay(this.player, this.uiManager);
205
201
  });
206
202
  const setLiveOffsetListener = function () {
207
- if (this.player === null)
203
+ if (this.playerContext === undefined)
208
204
  return;
209
205
  this.player?.off(PlayerEvent.SourceLoaded, setLiveOffsetListener);
210
- playerAction.handleLiveOffsetLogic(this, this.player, options);
206
+ playerAction.handleLiveOffsetLogic(this.playerContext, options);
211
207
  }.bind(this);
212
- this.player.on(PlayerEvent.Ready, setLiveOffsetListener);
213
- }
214
- async createUIManager(player, playerContext, variant) {
215
- if (this.uiManager === undefined || variant !== this.variant) {
216
- const uiConfig = {
217
- errorMessages: customSpecificErrorMessageOverlayConfig,
218
- disableAutoHideWhenHovered: true,
219
- seekbarSnappingEnabled: false
220
- };
221
- this.uiManager = new UIManager(player, createUIContainer(this, player, variant, this.container), uiConfig);
222
- this.variant = variant;
223
- }
224
- else {
225
- processStream(this.streamObject, this.container, this.streamOptions, player, this.uiManager, this.sourceConfig, playerContext);
226
- }
208
+ this.player.on(PlayerEvent.SourceLoaded, setLiveOffsetListener);
227
209
  }
228
210
  doError(input, status) {
229
211
  if (this.player == undefined)
@@ -240,19 +222,19 @@ export default class NpoPlayer {
240
222
  this.playerContext?.player.pause();
241
223
  }
242
224
  setVolume(volume) {
243
- if (this.player == undefined)
225
+ if (this.playerContext === undefined)
244
226
  return;
245
- this.player.setVolume(volume);
227
+ this.playerContext.player.setVolume(volume);
246
228
  }
247
229
  increaseVolume() {
248
- if (this.player == undefined)
230
+ if (this.playerContext === undefined)
249
231
  return;
250
- this.setVolume(this.player.getVolume() + 10);
232
+ this.playerContext.player.increaseVolume();
251
233
  }
252
234
  decreaseVolume() {
253
- if (this.player == undefined)
235
+ if (this.playerContext === undefined)
254
236
  return;
255
- this.setVolume(this.player.getVolume() - 10);
237
+ this.playerContext.player.decreaseVolume();
256
238
  }
257
239
  goForward(seconds) {
258
240
  if (this.player == undefined)
@@ -275,9 +257,9 @@ export default class NpoPlayer {
275
257
  }
276
258
  }
277
259
  watchFromStart() {
278
- if (this.player == undefined)
260
+ if (this.playerContext == undefined)
279
261
  return;
280
- playerAction.shiftToProgramStart(this.player, this.streamOptions.liveProgramTime);
262
+ playerAction.shiftToProgramStart(this.playerContext, this.streamOptions.liveProgramTime);
281
263
  }
282
264
  showPlayNextScreen() {
283
265
  if (this.player == undefined || !this.streamOptions.playNext?.showPlayNext || this.adBreakActive)
@@ -315,6 +297,7 @@ export default class NpoPlayer {
315
297
  this.hidePlayNextScreen();
316
298
  });
317
299
  this.isShowingPlayNextScreen = false;
300
+ this.canceledPlayNextScreen = false;
318
301
  hidePlayNextScreen(this.container);
319
302
  }
320
303
  doPlayNext() {
@@ -350,6 +333,9 @@ export default class NpoPlayer {
350
333
  if (this.npoTag != undefined) {
351
334
  clearInterval(this.npoTag.heartbeatInterval);
352
335
  }
336
+ if (this.playerContext) {
337
+ discardAdBreak(this.playerContext);
338
+ }
353
339
  this.hidePlayNextScreen();
354
340
  await this.player?.unload();
355
341
  return true;
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npo/player",
3
- "version": "1.23.2",
3
+ "version": "1.24.1",
4
4
  "description": "NPO Player",
5
5
  "author": "Publieke Omroep <player@npo.nl>",
6
6
  "contributors": [
@@ -16,7 +16,7 @@
16
16
  "types": "./lib/npoplayer.d.ts",
17
17
  "scripts": {
18
18
  "build": "webpack --hot",
19
- "build:scss": "npm run build:web-styles && npm run build:mobile-styles && mkdir -p dist && cp src/style/*.css dist && mkdir src/scss && cp src/style/*.css.map dist && cp src/style/*.css src/scss && cp src/style/*.css.map src/scss",
19
+ "build:scss": "npm run build:web-styles && npm run build:mobile-styles && mkdir -p dist && cp src/style/*.css dist && cp src/style/*.css.map dist && mkdir -p src/scss && cp src/style/*.css src/scss && cp src/style/*.css.map src/scss",
20
20
  "build:web-styles": "sass src/style/npoplayer.scss:src/style/npoplayer.css --style=compressed && postcss src/style/npoplayer.css --use autoprefixer postcss-import -o src/style/npoplayer.css",
21
21
  "build:mobile-styles": "sass src/style/npoplayer-mobile.scss:src/style/npoplayer-mobile.css --style=compressed && postcss src/style/npoplayer-mobile.css --use autoprefixer postcss-import -o src/style/npoplayer-mobile.css",
22
22
  "build:cdn": "npm run build && bash build-cdn.sh",
@@ -89,9 +89,9 @@
89
89
  "webpack-dev-server": "^4.11.1"
90
90
  },
91
91
  "dependencies": {
92
- "@npotag/tag": "3.1.2",
93
- "bitmovin-player": "8.166.0",
94
- "bitmovin-player-ui": "3.64.0"
92
+ "@npotag/tag": "3.2.1",
93
+ "bitmovin-player": "8.173.0",
94
+ "bitmovin-player-ui": "3.67.0"
95
95
  },
96
96
  "browserslist": [
97
97
  "defaults",
@@ -1,8 +1,8 @@
1
- import { PlayerEvent } from 'bitmovin-player';
1
+ import { NpoPlayerEvent } from '../../types/events';
2
2
  import { addAccessibilityAttributes } from '../../ui/handlers/accessibilityhandler';
3
3
  export function setupAccessibilityAttributes(playerContext) {
4
4
  const { player, npoplayer } = playerContext;
5
- const events = [PlayerEvent.SourceLoaded, PlayerEvent.AdBreakFinished, PlayerEvent.AdError];
5
+ const events = [NpoPlayerEvent.SourceLoaded, NpoPlayerEvent.AdBreakFinished, NpoPlayerEvent.AdError];
6
6
  const triggerAddAccessibilityAttributes = () => {
7
7
  for (const event of events) {
8
8
  player?.off(event, triggerAddAccessibilityAttributes);
@@ -1,4 +1,4 @@
1
- import { PlayerEvent } from 'bitmovin-player';
1
+ import { NpoPlayerEvent } from '../../types/events';
2
2
  import { addAccessibilityAttributes } from '../../ui/handlers/accessibilityhandler';
3
3
  import { setupAccessibilityAttributes } from './setup';
4
4
  import { mockNpoPlayer } from '../../../tests/mocks/mockNpoplayer';
@@ -44,7 +44,6 @@ describe('setupAccessibilityAttributes', () => {
44
44
  playerConfig: {},
45
45
  initPlayer: () => { },
46
46
  loadStream: () => Promise.resolve(),
47
- createUIManager: async () => { },
48
47
  doError: () => { },
49
48
  play: async () => { },
50
49
  pause: () => { },
@@ -87,18 +86,18 @@ describe('setupAccessibilityAttributes', () => {
87
86
  it('should set up event listeners for specified events', () => {
88
87
  setupAccessibilityAttributes(playerContext);
89
88
  expect(mockAddEventListener).toHaveBeenCalledTimes(3);
90
- expect(mockAddEventListener).toHaveBeenCalledWith(PlayerEvent.SourceLoaded, expect.any(Function));
91
- expect(mockAddEventListener).toHaveBeenCalledWith(PlayerEvent.AdBreakFinished, expect.any(Function));
92
- expect(mockAddEventListener).toHaveBeenCalledWith(PlayerEvent.AdError, expect.any(Function));
89
+ expect(mockAddEventListener).toHaveBeenCalledWith(NpoPlayerEvent.SourceLoaded, expect.any(Function));
90
+ expect(mockAddEventListener).toHaveBeenCalledWith(NpoPlayerEvent.AdBreakFinished, expect.any(Function));
91
+ expect(mockAddEventListener).toHaveBeenCalledWith(NpoPlayerEvent.AdError, expect.any(Function));
93
92
  });
94
93
  it('should remove event listeners after they are triggered', () => {
95
94
  setupAccessibilityAttributes(playerContext);
96
95
  const triggerFunction = mockAddEventListener.mock.calls[0][1];
97
96
  triggerFunction();
98
97
  expect(mockRemoveEventListener).toHaveBeenCalledTimes(3);
99
- expect(mockRemoveEventListener).toHaveBeenCalledWith(PlayerEvent.SourceLoaded, triggerFunction);
100
- expect(mockRemoveEventListener).toHaveBeenCalledWith(PlayerEvent.AdBreakFinished, triggerFunction);
101
- expect(mockRemoveEventListener).toHaveBeenCalledWith(PlayerEvent.AdError, triggerFunction);
98
+ expect(mockRemoveEventListener).toHaveBeenCalledWith(NpoPlayerEvent.SourceLoaded, triggerFunction);
99
+ expect(mockRemoveEventListener).toHaveBeenCalledWith(NpoPlayerEvent.AdBreakFinished, triggerFunction);
100
+ expect(mockRemoveEventListener).toHaveBeenCalledWith(NpoPlayerEvent.AdError, triggerFunction);
102
101
  });
103
102
  it('should call addAccessibilityAttributes with the correct parameters', () => {
104
103
  setupAccessibilityAttributes(playerContext);
@@ -0,0 +1,2 @@
1
+ import { PlayerContext } from '../../types/interfaces';
2
+ export declare function discardAdBreak(playerContext: PlayerContext): void;
@@ -0,0 +1,11 @@
1
+ export function discardAdBreak(playerContext) {
2
+ const { npoplayer, player } = playerContext;
3
+ const { adBreakActive } = npoplayer;
4
+ if (!player || adBreakActive === false)
5
+ return;
6
+ const activeAdBreak = player.getActiveAdBreak();
7
+ if (activeAdBreak?.id) {
8
+ player.discardAdBreak(activeAdBreak.id);
9
+ }
10
+ npoplayer.adBreakActive = false;
11
+ }
@@ -0,0 +1,60 @@
1
+ import createPlayerContextMock, { createMockNpoPlayer, createMockNpoPlayerAPI } from '../../../tests/mocks/playerContextMock';
2
+ import { discardAdBreak } from './discardAdBreak';
3
+ describe('discardAdBreak', () => {
4
+ let mockNpoPlayerAPI;
5
+ let npoplayerMock;
6
+ const activeAdBreakMock = { id: 'ad-break-id' };
7
+ afterEach(() => {
8
+ jest.resetAllMocks();
9
+ });
10
+ it('should not discard ad break if adBreakActive is false', () => {
11
+ mockNpoPlayerAPI = createMockNpoPlayerAPI({
12
+ getActiveAdBreak: jest.fn().mockReturnValue(undefined),
13
+ discardAdBreak: jest.fn()
14
+ });
15
+ npoplayerMock = createMockNpoPlayer({
16
+ adBreakActive: false
17
+ });
18
+ const playerContextMock = createPlayerContextMock({
19
+ player: mockNpoPlayerAPI,
20
+ npoplayer: npoplayerMock
21
+ });
22
+ discardAdBreak(playerContextMock);
23
+ expect(mockNpoPlayerAPI.getActiveAdBreak).not.toHaveBeenCalled();
24
+ expect(mockNpoPlayerAPI.discardAdBreak).not.toHaveBeenCalled();
25
+ });
26
+ it('should discard active ad break if adBreakActive is true and ad break exists', () => {
27
+ mockNpoPlayerAPI = createMockNpoPlayerAPI({
28
+ getActiveAdBreak: jest.fn().mockReturnValue(activeAdBreakMock),
29
+ discardAdBreak: jest.fn()
30
+ });
31
+ npoplayerMock = createMockNpoPlayer({
32
+ adBreakActive: true
33
+ });
34
+ const playerContextMock = createPlayerContextMock({
35
+ player: mockNpoPlayerAPI,
36
+ npoplayer: npoplayerMock
37
+ });
38
+ discardAdBreak(playerContextMock);
39
+ expect(mockNpoPlayerAPI.getActiveAdBreak).toHaveBeenCalled();
40
+ expect(mockNpoPlayerAPI.discardAdBreak).toHaveBeenCalledWith(activeAdBreakMock.id);
41
+ expect(npoplayerMock.adBreakActive).toBe(false);
42
+ });
43
+ it('should not discard ad break if active ad break does not exist', () => {
44
+ mockNpoPlayerAPI = createMockNpoPlayerAPI({
45
+ getActiveAdBreak: jest.fn().mockReturnValue(undefined),
46
+ discardAdBreak: jest.fn()
47
+ });
48
+ npoplayerMock = createMockNpoPlayer({
49
+ adBreakActive: true
50
+ });
51
+ const playerContextMock = createPlayerContextMock({
52
+ player: mockNpoPlayerAPI,
53
+ npoplayer: npoplayerMock
54
+ });
55
+ discardAdBreak(playerContextMock);
56
+ expect(mockNpoPlayerAPI.getActiveAdBreak).toHaveBeenCalled();
57
+ expect(mockNpoPlayerAPI.discardAdBreak).not.toHaveBeenCalled();
58
+ expect(npoplayerMock.adBreakActive).toBe(false);
59
+ });
60
+ });
@@ -0,0 +1,2 @@
1
+ import { PlayerContext } from '../../types/interfaces';
2
+ export declare function handlePreRolls(playerContext: PlayerContext): Promise<void>;
@@ -0,0 +1,132 @@
1
+ import { AdTagType } from 'bitmovin-player';
2
+ import { NpoPlayerUIVariants } from '../../types/interfaces';
3
+ import { NpoPlayerEvent } from '../../types/events';
4
+ export async function handlePreRolls(playerContext) {
5
+ return new Promise((resolve) => {
6
+ if (playerContext.npoplayer.streamObject.metadata.hasPreroll == 'false' ||
7
+ playerContext.npoplayer.streamObject.assets.preroll == undefined) {
8
+ resolve();
9
+ return;
10
+ }
11
+ const prerollUrl = playerContext.npoplayer.streamObject.assets.preroll;
12
+ let adIndex = 0;
13
+ let totalAds = 0;
14
+ let currentClickListener;
15
+ let adUiSet = false;
16
+ async function handleReady() {
17
+ playerContext.player.off(NpoPlayerEvent.Ready, handleReady);
18
+ const advertConfig = {
19
+ tag: {
20
+ url: String(prerollUrl),
21
+ type: AdTagType.VAST
22
+ },
23
+ id: 'Ad',
24
+ position: 'pre'
25
+ };
26
+ await playerContext.player.scheduleAds(advertConfig);
27
+ }
28
+ playerContext.player.on(NpoPlayerEvent.Ready, handleReady);
29
+ async function handlePlay() {
30
+ playerContext.player.off(NpoPlayerEvent.Play, handlePlay);
31
+ attemptSetAdUi();
32
+ }
33
+ playerContext.player.on(NpoPlayerEvent.Play, handlePlay);
34
+ function setAdUi() {
35
+ const activeAdBreak = playerContext.player.getActiveAdBreak();
36
+ if (!activeAdBreak || !activeAdBreak.ads)
37
+ return false;
38
+ playerContext.npoplayer.uiManager?.release();
39
+ playerContext.npoplayer.uiManager = undefined;
40
+ if (playerContext.npoplayer.playerContext) {
41
+ playerContext.player.createUIManager(playerContext, NpoPlayerUIVariants.AD);
42
+ }
43
+ adUiSet = true;
44
+ const adButton = playerContext.npoplayer.uiComponents.adbutton;
45
+ const currentAd = activeAdBreak.ads[0];
46
+ adIndex = 1;
47
+ totalAds = activeAdBreak.ads.length;
48
+ if (adButton) {
49
+ adButton.show();
50
+ adClickHandler(currentAd, adButton);
51
+ }
52
+ playerContext.npoplayer.uiComponents.adlabel?.setText(`Advertentie 1 van ${activeAdBreak.ads.length}`);
53
+ return true;
54
+ }
55
+ function attemptSetAdUi(attemptsLeft = 10) {
56
+ if (attemptsLeft <= 0)
57
+ return;
58
+ if (!adUiSet) {
59
+ setAdUi();
60
+ setTimeout(() => {
61
+ attemptSetAdUi(attemptsLeft - 1);
62
+ }, 200);
63
+ }
64
+ }
65
+ function handleAdStarted() {
66
+ playerContext.player.off(NpoPlayerEvent.AdStarted, handleAdStarted);
67
+ const activeAdBreak = playerContext.player.getActiveAdBreak();
68
+ if (!activeAdBreak || !activeAdBreak.ads) {
69
+ console.error('No active ad break data found');
70
+ return;
71
+ }
72
+ if (!adUiSet)
73
+ attemptSetAdUi();
74
+ playerContext.npoplayer.adBreakActive = true;
75
+ }
76
+ playerContext.player.on(NpoPlayerEvent.AdStarted, handleAdStarted);
77
+ function adClickHandler(ad, button) {
78
+ const clickThroughCallback = ad.clickThroughUrlOpened;
79
+ if (currentClickListener)
80
+ button.onClick.unsubscribe(currentClickListener);
81
+ currentClickListener = () => {
82
+ try {
83
+ window.open(ad.clickThroughUrl, '_blank');
84
+ playerContext.player.pause();
85
+ if (clickThroughCallback)
86
+ clickThroughCallback();
87
+ }
88
+ catch (error) {
89
+ console.log(error);
90
+ }
91
+ };
92
+ button.onClick.subscribe(currentClickListener);
93
+ }
94
+ function handleAdFinished() {
95
+ playerContext.player.off(NpoPlayerEvent.AdFinished, handleAdFinished);
96
+ const activeAdBreak = playerContext.player.getActiveAdBreak();
97
+ if (!activeAdBreak || !activeAdBreak.ads) {
98
+ console.error('No active ad break data found');
99
+ return;
100
+ }
101
+ if (adIndex >= totalAds) {
102
+ handleAdBreakFinished();
103
+ return;
104
+ }
105
+ const adButton = playerContext.npoplayer.uiComponents.adbutton;
106
+ const nextAd = activeAdBreak.ads[adIndex];
107
+ adIndex += 1;
108
+ if (adButton) {
109
+ adButton.show();
110
+ adClickHandler(nextAd, adButton);
111
+ }
112
+ playerContext.npoplayer.uiComponents.adlabel?.setText(`Advertentie ${adIndex} van ${activeAdBreak.ads.length}`);
113
+ }
114
+ playerContext.player.on(NpoPlayerEvent.AdFinished, handleAdFinished);
115
+ function handleAdBreakFinished() {
116
+ playerContext.player.off(NpoPlayerEvent.AdBreakFinished, handleAdBreakFinished);
117
+ playerContext.player.off(NpoPlayerEvent.AdError, handleAdBreakFinished);
118
+ playerContext.npoplayer.adBreakActive = false;
119
+ playerContext.npoplayer.uiComponents.adbutton?.hide();
120
+ playerContext.npoplayer.uiComponents.adlabel?.hide();
121
+ playerContext.npoplayer.uiManager?.release();
122
+ playerContext.npoplayer.uiManager = undefined;
123
+ if (playerContext.npoplayer.playerContext) {
124
+ playerContext.player.createUIManager(playerContext, NpoPlayerUIVariants.DEFAULT);
125
+ }
126
+ adUiSet = false;
127
+ resolve();
128
+ }
129
+ playerContext.player.on(NpoPlayerEvent.AdBreakFinished, handleAdBreakFinished);
130
+ playerContext.player.on(NpoPlayerEvent.AdError, handleAdBreakFinished);
131
+ });
132
+ }
@@ -0,0 +1,52 @@
1
+ import { handlePreRolls } from './handlePreRolls';
2
+ import { createMockNpoPlayer, createPlayerContextMock } from '../../../tests/mocks/playerContextMock';
3
+ jest.mock('bitmovin-player');
4
+ jest.mock('../../npoplayer');
5
+ describe('handlePreRolls', () => {
6
+ let mockPlayerAPI;
7
+ beforeEach(() => {
8
+ mockPlayerAPI = {
9
+ ads: {
10
+ schedule: jest.fn().mockResolvedValue(true),
11
+ getActiveAdBreak: jest.fn()
12
+ },
13
+ on: jest.fn(),
14
+ off: jest.fn()
15
+ };
16
+ });
17
+ it('should not schedule ads when hasPreroll is false', async () => {
18
+ const npoplayerMock = createMockNpoPlayer({
19
+ streamObject: {
20
+ metadata: {
21
+ hasPreroll: 'false'
22
+ },
23
+ assets: {
24
+ preroll: 'sample_url'
25
+ }
26
+ }
27
+ });
28
+ const playerContextMock = createPlayerContextMock({
29
+ npoplayer: npoplayerMock
30
+ });
31
+ await handlePreRolls(playerContextMock);
32
+ expect(mockPlayerAPI.ads.schedule).not.toHaveBeenCalled();
33
+ });
34
+ it('should not schedule ads if preroll URL is missing', async () => {
35
+ const npoplayerMock = createMockNpoPlayer({
36
+ streamObject: {
37
+ metadata: {
38
+ hasPreroll: 'true'
39
+ },
40
+ assets: {}
41
+ }
42
+ });
43
+ const playerContextMock = createPlayerContextMock({
44
+ npoplayer: npoplayerMock
45
+ });
46
+ await handlePreRolls(playerContextMock);
47
+ expect(mockPlayerAPI.ads.schedule).not.toHaveBeenCalled();
48
+ });
49
+ afterEach(() => {
50
+ jest.clearAllMocks();
51
+ });
52
+ });
@@ -0,0 +1,4 @@
1
+ export type CdnProvider = 'NEP-FASTLY' | 'NEP' | 'NEP-LSW' | 'EUROVISION' | 'KPN' | 'AKEMAI' | 'OMROEP-ICECAST' | 'OMROEP-CONTENT' | 'OMROEP-PODCAST' | 'OMROEP-VIDEO' | 'OMROEP-DOWNLOAD' | 'NOS' | 'NPOAUDIO';
2
+ export declare function createImmutableMap(entries: [string, CdnProvider][]): ReadonlyMap<string, CdnProvider>;
3
+ export declare const npoCdnProvidersData: [string, CdnProvider][];
4
+ export declare const npoCdnProviders: ReadonlyMap<string, CdnProvider>;
@@ -0,0 +1,22 @@
1
+ export function createImmutableMap(entries) {
2
+ const map = new Map(entries);
3
+ return Object.freeze(map);
4
+ }
5
+ export const npoCdnProvidersData = [
6
+ ['nep.global.ssl.fastly.net', 'NEP-FASTLY'],
7
+ ['npo-fsly.cdn.streamgate.io', 'NEP-FASTLY'],
8
+ ['cdn.streamgate.nl', 'NEP'],
9
+ ['pr.lswcdn.net', 'NEP-LSW'],
10
+ ['cdn.eurovisioncdn.net', 'EUROVISION'],
11
+ ['npo.prd.cdn.bcms.kpn.com', 'KPN'],
12
+ ['cdn.bcms.kpn.com', 'KPN'],
13
+ ['Akamai', 'AKEMAI'],
14
+ ['icecast.omroep.nl', 'OMROEP-ICECAST'],
15
+ ['content.omroep.nl', 'OMROEP-CONTENT'],
16
+ ['podcast.npo.nl', 'OMROEP-PODCAST'],
17
+ ['video.omroep.nl', 'OMROEP-VIDEO'],
18
+ ['download.omroep.nl', 'OMROEP-DOWNLOAD'],
19
+ ['download.nos.nl', 'NOS'],
20
+ ['entry.cdn.npoaudio.nl', 'NPOAUDIO']
21
+ ];
22
+ export const npoCdnProviders = createImmutableMap(npoCdnProvidersData);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ import { createImmutableMap, npoCdnProviders, npoCdnProvidersData } from './cdnProviders';
2
+ describe('createImmutableMap function', () => {
3
+ it('should create an immutable map with correct entries', () => {
4
+ const testEntries = [
5
+ ['test1.com', 'AKEMAI'],
6
+ ['test2.com', 'EUROVISION'],
7
+ ['test3.com', 'KPN']
8
+ ];
9
+ const immutableMap = createImmutableMap(testEntries);
10
+ for (const [key, value] of testEntries) {
11
+ expect(immutableMap.get(key)).toBe(value);
12
+ }
13
+ });
14
+ });
15
+ describe('npoCdnProviders map', () => {
16
+ it('should match the expected contents', () => {
17
+ expect(npoCdnProviders.size).toBe(npoCdnProvidersData.length);
18
+ for (const [key, value] of npoCdnProvidersData) {
19
+ expect(npoCdnProviders.get(key)).toBe(value);
20
+ }
21
+ });
22
+ });
@@ -1,2 +1,2 @@
1
- import { PlayerContext } from 'types/interfaces';
1
+ import { PlayerContext } from '../../types/interfaces';
2
2
  export declare const removeEventListeners: (playerContext: PlayerContext) => void;
@@ -1,12 +1,12 @@
1
- import { PlayerEvent } from 'bitmovin-player';
1
+ import { NpoPlayerEvent } from '../../types/events';
2
2
  export const removeEventListeners = (playerContext) => {
3
3
  const { eventListeners } = playerContext.npoplayer;
4
4
  if (eventListeners) {
5
5
  const { segmentHandleTimeChangedCallback, segmentSeekFunctionCallback, liveStreamHandleTimeChangedCallback } = eventListeners;
6
6
  const eventMapping = [
7
- { event: PlayerEvent.TimeChanged, callback: liveStreamHandleTimeChangedCallback },
8
- { event: PlayerEvent.TimeChanged, callback: segmentHandleTimeChangedCallback },
9
- { event: PlayerEvent.Seek, callback: segmentSeekFunctionCallback }
7
+ { event: NpoPlayerEvent.TimeChanged, callback: liveStreamHandleTimeChangedCallback },
8
+ { event: NpoPlayerEvent.TimeChanged, callback: segmentHandleTimeChangedCallback },
9
+ { event: NpoPlayerEvent.Seek, callback: segmentSeekFunctionCallback }
10
10
  ];
11
11
  for (const { event, callback } of eventMapping) {
12
12
  if (callback) {