@npo/player 1.23.1 → 1.24.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 (140) 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 +27 -46
  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/handlePreRolls.d.ts +2 -0
  22. package/lib/services/advertHandlers/handlePreRolls.js +132 -0
  23. package/lib/services/advertHandlers/handlePrerolls.test.js +52 -0
  24. package/lib/services/cdnProviders/cdnProviders.d.ts +4 -0
  25. package/lib/services/cdnProviders/cdnProviders.js +22 -0
  26. package/lib/services/cdnProviders/cndProviders.test.js +22 -0
  27. package/lib/services/eventListenerHandlers/removeEventListeners.d.ts +1 -1
  28. package/lib/services/eventListenerHandlers/removeEventListeners.js +4 -4
  29. package/lib/services/eventListenerHandlers/removeEventListeners.test.js +28 -13
  30. package/lib/services/liveStreamHandlers/handleLiveStreamControls.d.ts +1 -1
  31. package/lib/services/liveStreamHandlers/handleLiveStreamControls.js +3 -3
  32. package/lib/services/liveStreamHandlers/handleLiveStreamControls.test.d.ts +2 -0
  33. package/lib/services/liveStreamHandlers/handleLiveStreamControls.test.js +65 -0
  34. package/lib/services/localStorageHandlers/localStorageHandlers.d.ts +1 -1
  35. package/lib/services/localStorageHandlers/localStorageHandlers.test.js +0 -1
  36. package/lib/services/npoPlayerAPI/contants.d.ts +5 -0
  37. package/lib/services/npoPlayerAPI/contants.js +5 -0
  38. package/lib/services/npoPlayerAPI/npoPlayerAPI.d.ts +16 -4
  39. package/lib/services/npoPlayerAPI/npoPlayerAPI.js +65 -3
  40. package/lib/services/npoPlayerAPI/npoPlayerAPI.test.js +66 -1
  41. package/lib/services/segmentHandlers/addSegmentEventListeners.js +3 -3
  42. package/lib/services/segmentHandlers/addSegmentEventListeners.test.js +10 -9
  43. package/lib/services/segmentHandlers/convertFragmentToSegment.d.ts +1 -1
  44. package/lib/services/segmentHandlers/handleSegmentSeek.d.ts +1 -1
  45. package/lib/services/segmentHandlers/handleSegmentSeek.test.js +4 -4
  46. package/lib/services/segmentHandlers/handleSegmentTimeChanged.d.ts +1 -1
  47. package/lib/services/segmentHandlers/handleSegmentTimeChanged.test.js +33 -24
  48. package/lib/services/segmentHandlers/initSegment.d.ts +1 -1
  49. package/lib/services/segmentHandlers/initSegment.test.js +3 -1
  50. package/lib/services/segmentHandlers/setSegmentMarkers.d.ts +1 -1
  51. package/lib/services/services.d.ts +3 -1
  52. package/lib/services/services.js +8 -0
  53. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.d.ts +2 -0
  54. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.js +8 -0
  55. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.test.d.ts +1 -0
  56. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.test.js +36 -0
  57. package/lib/src/js/playeractions/handlers/handleoffsets.d.ts +4 -6
  58. package/lib/src/js/tracking/handlers/eventbinding.d.ts +1 -1
  59. package/lib/src/js/tracking/handlers/eventlogging.d.ts +1 -1
  60. package/lib/src/npoplayer.d.ts +1 -2
  61. package/lib/src/services/advertHandlers/handlePreRolls.d.ts +2 -0
  62. package/lib/src/services/advertHandlers/handlePrerolls.test.d.ts +1 -0
  63. package/lib/src/services/cdnProviders/cdnProviders.d.ts +4 -0
  64. package/lib/src/services/cdnProviders/cndProviders.test.d.ts +1 -0
  65. package/lib/src/services/eventListenerHandlers/removeEventListeners.d.ts +1 -1
  66. package/lib/src/services/liveStreamHandlers/handleLiveStreamControls.d.ts +1 -1
  67. package/lib/src/services/liveStreamHandlers/handleLiveStreamControls.test.d.ts +2 -0
  68. package/lib/src/services/localStorageHandlers/localStorageHandlers.d.ts +1 -1
  69. package/lib/src/services/npoPlayerAPI/contants.d.ts +5 -0
  70. package/lib/src/services/npoPlayerAPI/npoPlayerAPI.d.ts +16 -4
  71. package/lib/src/services/segmentHandlers/convertFragmentToSegment.d.ts +1 -1
  72. package/lib/src/services/segmentHandlers/handleSegmentSeek.d.ts +1 -1
  73. package/lib/src/services/segmentHandlers/handleSegmentTimeChanged.d.ts +1 -1
  74. package/lib/src/services/segmentHandlers/initSegment.d.ts +1 -1
  75. package/lib/src/services/segmentHandlers/setSegmentMarkers.d.ts +1 -1
  76. package/lib/src/services/services.d.ts +3 -1
  77. package/lib/src/services/verticalVideoHandlers/handleVerticalVideoControls.d.ts +2 -0
  78. package/lib/src/services/verticalVideoHandlers/handleVerticalVideoControls.test.d.ts +1 -0
  79. package/lib/src/types/events.d.ts +8 -1
  80. package/lib/src/types/interfaces.d.ts +8 -12
  81. package/lib/src/ui/components/audio/controlbar.d.ts +1 -1
  82. package/lib/src/ui/components/controlbar.d.ts +1 -1
  83. package/lib/src/ui/components/seekbar.d.ts +1 -1
  84. package/lib/src/ui/components/settingspanel.d.ts +1 -1
  85. package/lib/src/ui/components/verticalvideo/controlbar.d.ts +3 -0
  86. package/lib/src/ui/handlers/streamhandler.d.ts +2 -4
  87. package/lib/src/ui/uicontainer.d.ts +2 -2
  88. package/lib/tests/mocks/mockNpoplayer.js +0 -1
  89. package/lib/tests/mocks/playerContextMock.d.ts +67 -0
  90. package/lib/tests/mocks/playerContextMock.js +117 -0
  91. package/lib/types/events.d.ts +8 -1
  92. package/lib/types/events.js +184 -1
  93. package/lib/types/interfaces.d.ts +8 -12
  94. package/lib/types/interfaces.js +1 -0
  95. package/lib/ui/components/audio/controlbar.d.ts +1 -1
  96. package/lib/ui/components/audio/controlbar.js +6 -6
  97. package/lib/ui/components/controlbar.d.ts +1 -1
  98. package/lib/ui/components/controlbar.js +13 -11
  99. package/lib/ui/components/nativemobile/controlbar.js +0 -1
  100. package/lib/ui/components/seekbar.js +1 -1
  101. package/lib/ui/components/settingspanel.d.ts +1 -1
  102. package/lib/ui/components/settingspanel.js +68 -84
  103. package/lib/ui/components/topbar.js +6 -3
  104. package/lib/ui/components/verticalvideo/controlbar.d.ts +3 -0
  105. package/lib/ui/components/verticalvideo/controlbar.js +34 -0
  106. package/lib/ui/handlers/streamhandler.d.ts +2 -4
  107. package/lib/ui/handlers/streamhandler.js +16 -7
  108. package/lib/ui/nativemobileuifactory.js +2 -0
  109. package/lib/ui/uicontainer.d.ts +2 -2
  110. package/lib/ui/uicontainer.js +35 -29
  111. package/package.json +5 -5
  112. package/src/style/components/_settingspanel.scss +35 -3
  113. package/src/style/components/_subtitles.scss +29 -25
  114. package/src/style/components/_textbuttons.scss +2 -2
  115. package/src/style/components/_volumeslider.scss +1 -0
  116. package/src/style/components/vertical-video/_bottombar.scss +19 -0
  117. package/src/style/components/vertical-video/_buttons.scss +23 -0
  118. package/src/style/components/vertical-video/_hugeplaybacktogglebutton.scss +14 -0
  119. package/src/style/components/vertical-video/_seekbar.scss +19 -0
  120. package/src/style/components/vertical-video/_settingsbutton.scss +7 -0
  121. package/src/style/components/vertical-video/_settingspanel.scss +14 -0
  122. package/src/style/components/vertical-video/_shortvideo.scss +14 -0
  123. package/src/style/components/vertical-video/_subtitles.scss +3 -0
  124. package/src/style/components/vertical-video/_topbar.scss +17 -0
  125. package/src/style/components/vertical-video/_volumeslider.scss +9 -0
  126. package/src/style/npoplayer.css +74 -34
  127. package/src/style/npoplayer.scss +2 -1
  128. package/src/style/variants/_player-small.scss +18 -10
  129. package/src/style/variants/_player-vertical.scss +23 -0
  130. package/lib/js/ads/ster.d.ts +0 -4
  131. package/lib/js/ads/ster.js +0 -126
  132. package/lib/js/ads/ster.test.js +0 -63
  133. package/lib/js/cdnproviders.d.ts +0 -1
  134. package/lib/js/cdnproviders.js +0 -16
  135. package/lib/src/js/ads/ster.d.ts +0 -4
  136. package/lib/src/js/cdnproviders.d.ts +0 -1
  137. package/lib/tests/mocks/mockPlayerContext.d.ts +0 -2
  138. package/lib/tests/mocks/mockPlayerContext.js +0 -40
  139. /package/lib/{js/ads/ster.test.d.ts → services/advertHandlers/handlePrerolls.test.d.ts} +0 -0
  140. /package/lib/{src/js/ads/ster.test.d.ts → services/cdnProviders/cndProviders.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,15 +10,12 @@ 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';
@@ -55,14 +51,17 @@ export default class NpoPlayer {
55
51
  this.container = _container;
56
52
  this.variant = _variant || NpoPlayerUIVariants.DEFAULT;
57
53
  initPlayerTracker(this, _npotag, _npotaginstance);
58
- this.initPlayer(this.container, _playerConfig, this.variant);
54
+ this.initPlayer(this.container, _playerConfig);
59
55
  }
60
- initPlayer(_container, playerConfig, variant) {
56
+ initPlayer(_container, playerConfig) {
61
57
  const processedPlayerConfig = playerAction.processPlayerConfig(this, playerConfig);
62
- Player.addModule(getModuleExport(AdsModuleBM));
63
- this.userPreferences = this.npoplayerServices.getStoredUserPrefs();
64
58
  this.player = new Player(_container, processedPlayerConfig);
59
+ Player.addModule(getModuleExport(AdsModuleBM));
65
60
  const npoPlayerAPI = new NpoPlayerAPI(this.player);
61
+ this.playerContext = { player: npoPlayerAPI, npoplayer: this };
62
+ this.userPreferences = this.npoplayerServices.getStoredUserPrefs();
63
+ this.npoplayerServices.handleUserPrefs(this.playerContext);
64
+ this.npoplayerServices.setAccessibilityAttributes(this.playerContext);
66
65
  window.addEventListener('beforeunload', () => {
67
66
  if (this.player) {
68
67
  this.npoplayerServices.setStoredUserPrefs(LocalStorageValues.SUBTITLES_ENABLED, npoPlayerAPI.areSubtitlesEnabled().toString());
@@ -70,10 +69,6 @@ export default class NpoPlayer {
70
69
  this.npoplayerServices.setStoredUserPrefs(LocalStorageValues.IS_MUTED, npoPlayerAPI.isMuted().toString());
71
70
  }
72
71
  });
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
72
  this.container.addEventListener('keydown', (e) => {
78
73
  if (!this.playerContext)
79
74
  return;
@@ -89,8 +84,8 @@ export default class NpoPlayer {
89
84
  return;
90
85
  }
91
86
  if (this.adBreakActive) {
92
- console.error('Je kunt geen nieuwe content laden tijdens een reclameblok.');
93
- return;
87
+ const currentAd = this.player.ads.getActiveAdBreak().id;
88
+ this.player.ads.discardAdBreak(currentAd);
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) {
@@ -163,28 +158,28 @@ export default class NpoPlayer {
163
158
  const initAndStartTracker = () => {
164
159
  if (this.player === null)
165
160
  return;
166
- this.player?.off(PlayerEvent.SourceLoaded, initAndStartTracker);
167
161
  this.player?.off(PlayerEvent.AdBreakFinished, initAndStartTracker);
168
162
  this.player?.off(PlayerEvent.AdError, initAndStartTracker);
169
163
  streamDuration =
170
164
  streamDuration == undefined
171
- ? this.player?.on(PlayerEvent.Ready, getDurationAndStartPlayerTracker)
165
+ ? getDurationAndStartPlayerTracker()
172
166
  : startPlayerTracker(this, getStreamDurationInSeconds({ duration: streamDuration, durationIsInMs: true }), this.version, streamPrid);
173
167
  };
174
- if (this.variant !== NpoPlayerUIVariants.AUDIO &&
168
+ if (this.variant === NpoPlayerUIVariants.DEFAULT &&
175
169
  this.streamObject.metadata.hasPreroll === 'true' &&
176
170
  this.streamObject.assets.preroll) {
177
171
  this.player.on(PlayerEvent.AdBreakFinished, initAndStartTracker);
178
172
  this.player.on(PlayerEvent.AdError, initAndStartTracker);
179
- await handlePreRolls(this.player, this.streamObject, this);
173
+ await handlePreRolls(this.playerContext);
180
174
  }
181
175
  else {
182
- this.player.on(PlayerEvent.Ready, initAndStartTracker);
176
+ initAndStartTracker();
183
177
  }
184
178
  }
185
179
  else {
186
180
  this.doError(`Het is niet gelukt de stream op te halen: \n Input is geen valide token of media object.`, 500);
187
181
  }
182
+ this.npoplayerServices.handleVerticalVideoControls(this.playerContext, this.variant);
188
183
  if (this.sourceConfig?.metadata) {
189
184
  this.sourceConfig.metadata.jwt = source;
190
185
  this.sourceConfig.metadata.streamLinkAsJsonString = JSON.stringify(this.streamObject);
@@ -205,26 +200,12 @@ export default class NpoPlayer {
205
200
  showNicamAfterUiDelay(this.player, this.uiManager);
206
201
  });
207
202
  const setLiveOffsetListener = function () {
208
- if (this.player === null)
203
+ if (this.playerContext === undefined)
209
204
  return;
210
205
  this.player?.off(PlayerEvent.SourceLoaded, setLiveOffsetListener);
211
- playerAction.handleLiveOffsetLogic(this, this.player, options);
206
+ playerAction.handleLiveOffsetLogic(this.playerContext, options);
212
207
  }.bind(this);
213
- this.player.on(PlayerEvent.Ready, setLiveOffsetListener);
214
- }
215
- async createUIManager(player, playerContext, variant) {
216
- if (this.uiManager === undefined || variant !== this.variant) {
217
- const uiConfig = {
218
- errorMessages: customSpecificErrorMessageOverlayConfig,
219
- disableAutoHideWhenHovered: true,
220
- seekbarSnappingEnabled: false
221
- };
222
- this.uiManager = new UIManager(player, createUIContainer(this, player, variant, this.container), uiConfig);
223
- this.variant = variant;
224
- }
225
- else {
226
- processStream(this.streamObject, this.container, this.streamOptions, player, this.uiManager, this.sourceConfig, playerContext);
227
- }
208
+ this.player.on(PlayerEvent.SourceLoaded, setLiveOffsetListener);
228
209
  }
229
210
  doError(input, status) {
230
211
  if (this.player == undefined)
@@ -241,19 +222,19 @@ export default class NpoPlayer {
241
222
  this.playerContext?.player.pause();
242
223
  }
243
224
  setVolume(volume) {
244
- if (this.player == undefined)
225
+ if (this.playerContext === undefined)
245
226
  return;
246
- this.player.setVolume(volume);
227
+ this.playerContext.player.setVolume(volume);
247
228
  }
248
229
  increaseVolume() {
249
- if (this.player == undefined)
230
+ if (this.playerContext === undefined)
250
231
  return;
251
- this.setVolume(this.player.getVolume() + 10);
232
+ this.playerContext.player.increaseVolume();
252
233
  }
253
234
  decreaseVolume() {
254
- if (this.player == undefined)
235
+ if (this.playerContext === undefined)
255
236
  return;
256
- this.setVolume(this.player.getVolume() - 10);
237
+ this.playerContext.player.decreaseVolume();
257
238
  }
258
239
  goForward(seconds) {
259
240
  if (this.player == undefined)
@@ -276,9 +257,9 @@ export default class NpoPlayer {
276
257
  }
277
258
  }
278
259
  watchFromStart() {
279
- if (this.player == undefined)
260
+ if (this.playerContext == undefined)
280
261
  return;
281
- playerAction.shiftToProgramStart(this.player, this.streamOptions.liveProgramTime);
262
+ playerAction.shiftToProgramStart(this.playerContext, this.streamOptions.liveProgramTime);
282
263
  }
283
264
  showPlayNextScreen() {
284
265
  if (this.player == undefined || !this.streamOptions.playNext?.showPlayNext || this.adBreakActive)
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npo/player",
3
- "version": "1.23.1",
3
+ "version": "1.24.0",
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 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,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) {
@@ -1,27 +1,42 @@
1
- import { PlayerEvent } from 'bitmovin-player';
2
- import { mockPlayerContext } from '../../../tests/mocks/mockPlayerContext';
1
+ import { NpoPlayerEvent } from '../../types/events';
3
2
  import { removeEventListeners } from './removeEventListeners';
3
+ import createPlayerContextMock from '../../../tests/mocks/playerContextMock';
4
4
  describe('removeEventListeners', () => {
5
- let context;
5
+ let mockPlayerContext;
6
6
  beforeEach(() => {
7
- context = { ...mockPlayerContext };
8
7
  jest.clearAllMocks();
8
+ mockPlayerContext = createPlayerContextMock();
9
9
  });
10
10
  it('should remove TimeChanged event listener if exists', () => {
11
- removeEventListeners(context);
12
- if (context.npoplayer.eventListeners) {
13
- expect(context.player.off).toHaveBeenCalledWith(PlayerEvent.TimeChanged, context.npoplayer.eventListeners.segmentHandleTimeChangedCallback);
14
- expect(context.player.off).toHaveBeenCalledWith(PlayerEvent.TimeChanged, context.npoplayer.eventListeners.liveStreamHandleTimeChangedCallback);
11
+ mockPlayerContext.npoplayer.eventListeners = {
12
+ segmentHandleTimeChangedCallback: jest.fn(),
13
+ liveStreamHandleTimeChangedCallback: jest.fn(),
14
+ segmentSeekFunctionCallback: jest.fn()
15
+ };
16
+ removeEventListeners(mockPlayerContext);
17
+ if (mockPlayerContext.npoplayer.eventListeners) {
18
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.TimeChanged, mockPlayerContext.npoplayer.eventListeners.segmentHandleTimeChangedCallback);
19
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.TimeChanged, mockPlayerContext.npoplayer.eventListeners.liveStreamHandleTimeChangedCallback);
15
20
  }
16
21
  });
17
22
  it('should remove Seek event listener if exists', () => {
18
- removeEventListeners(context);
19
- if (context.npoplayer.eventListeners) {
20
- expect(context.player.off).toHaveBeenCalledWith(PlayerEvent.Seek, context.npoplayer.eventListeners.segmentSeekFunctionCallback);
23
+ mockPlayerContext.npoplayer.eventListeners = {
24
+ segmentHandleTimeChangedCallback: jest.fn(),
25
+ liveStreamHandleTimeChangedCallback: jest.fn(),
26
+ segmentSeekFunctionCallback: jest.fn()
27
+ };
28
+ removeEventListeners(mockPlayerContext);
29
+ if (mockPlayerContext.npoplayer.eventListeners) {
30
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.Seek, mockPlayerContext.npoplayer.eventListeners.segmentSeekFunctionCallback);
21
31
  }
22
32
  });
23
33
  it('should reset EventListeners in playerContext', () => {
24
- removeEventListeners(context);
25
- expect(context.npoplayer.eventListeners).toBeUndefined();
34
+ mockPlayerContext.npoplayer.eventListeners = {
35
+ segmentHandleTimeChangedCallback: jest.fn(),
36
+ liveStreamHandleTimeChangedCallback: jest.fn(),
37
+ segmentSeekFunctionCallback: jest.fn()
38
+ };
39
+ removeEventListeners(mockPlayerContext);
40
+ expect(mockPlayerContext.npoplayer.eventListeners).toBeUndefined();
26
41
  });
27
42
  });
@@ -1,4 +1,4 @@
1
- import { PlayerContext } from 'types/interfaces';
1
+ import { PlayerContext } from '../../types/interfaces';
2
2
  export declare const handleLiveStreamControls: (playerContext: PlayerContext) => void;
3
3
  export declare function updateForwardButtonState(player: any): void;
4
4
  export declare function toggleForwardButtons(forwardButtons: NodeListOf<Element>, timeShift: number): void;
@@ -1,8 +1,8 @@
1
- import { PlayerEvent } from 'bitmovin-player';
1
+ import { NpoPlayerEvent } from '../../types/events';
2
2
  export const handleLiveStreamControls = (playerContext) => {
3
3
  const liveStreamHandleTimeChangedCallback = () => updateForwardButtonState(playerContext.player);
4
4
  const { player, npoplayer } = playerContext;
5
- player.on(PlayerEvent.TimeChanged, liveStreamHandleTimeChangedCallback);
5
+ player.on(NpoPlayerEvent.TimeChanged, liveStreamHandleTimeChangedCallback);
6
6
  npoplayer.eventListeners = {
7
7
  liveStreamHandleTimeChangedCallback
8
8
  };
@@ -10,7 +10,7 @@ export const handleLiveStreamControls = (playerContext) => {
10
10
  export function updateForwardButtonState(player) {
11
11
  const timeShift = player?.getTimeShift();
12
12
  const forwardButtons = player?.getContainer().querySelectorAll('.bmpui-ui-forwardbutton');
13
- player.off(PlayerEvent.TimeChanged, () => {
13
+ player.off(NpoPlayerEvent.TimeChanged, () => {
14
14
  updateForwardButtonState(player);
15
15
  });
16
16
  if (forwardButtons && timeShift !== undefined) {
@@ -0,0 +1,2 @@
1
+ import '@testing-library/jest-dom';
2
+ export declare const TIME_SHIFT_THRESHOLD = -10;