@npo/player 1.27.8 → 1.28.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 (214) hide show
  1. package/README.md +4 -7
  2. package/lib/js/markers/updateLiveMarkers.d.ts +7 -2
  3. package/lib/js/markers/updateLiveMarkers.js +11 -4
  4. package/lib/js/markers/updateLiveMarkers.test.js +13 -3
  5. package/lib/js/playeractions/handlers/handleoffsets.test.js +1 -1
  6. package/lib/js/playeractions/handlers/mediasessionactions.d.ts +7 -2
  7. package/lib/js/playeractions/handlers/mediasessionactions.js +7 -7
  8. package/lib/js/playeractions/handlers/processplayerconfig.d.ts +1 -1
  9. package/lib/js/playeractions/handlers/processplayerconfig.js +1 -1
  10. package/lib/js/playeractions/handlers/processplayerconfig.test.js +1 -1
  11. package/lib/js/playeractions/handlers/processsourceconfig.d.ts +11 -2
  12. package/lib/js/playeractions/handlers/processsourceconfig.js +35 -18
  13. package/lib/js/playeractions/handlers/removereplayclass.d.ts +1 -1
  14. package/lib/js/playeractions/handlers/removereplayclass.js +1 -1
  15. package/lib/js/playeractions/handlers/removereplayclass.test.js +1 -1
  16. package/lib/js/settings/localization.d.ts +1 -0
  17. package/lib/js/utilities/utilities.jwt.d.ts +2 -0
  18. package/lib/js/utilities/utilities.jwt.js +22 -0
  19. package/lib/js/utilities/utilities.stream.js +11 -2
  20. package/lib/js/utilities/utilities.url.js +2 -2
  21. package/lib/js/utilities/utilities.version.js +1 -1
  22. package/lib/js/utilities/utilities.version.test.js +1 -1
  23. package/lib/lang/nl.json +2 -1
  24. package/lib/npoplayer.d.ts +6 -5
  25. package/lib/npoplayer.js +66 -23
  26. package/lib/package.json +7 -9
  27. package/lib/services/a11y/setup.test.js +2 -4
  28. package/lib/services/advertHandlers/handlePreRolls.js +3 -4
  29. package/lib/services/advertHandlers/handlePrerolls.test.js +2 -3
  30. package/lib/services/chapterHandlers/chapterHandler.d.ts +35 -0
  31. package/lib/services/chapterHandlers/chapterHandler.js +230 -0
  32. package/lib/services/errors/errorBackground.test.js +1 -1
  33. package/lib/services/errors/errorHandler.d.ts +4 -2
  34. package/lib/services/errors/errorHandler.js +27 -14
  35. package/lib/services/errors/errorHandler.test.js +148 -0
  36. package/lib/services/errors/errorRetryHandler.d.ts +2 -0
  37. package/lib/services/errors/errorRetryHandler.js +14 -0
  38. package/lib/services/errors/errorRetryHandler.test.d.ts +1 -0
  39. package/lib/services/errors/errorRetryHandler.test.js +62 -0
  40. package/lib/services/errors/errorText.js +8 -5
  41. package/lib/services/errors/errorText.test.js +3 -3
  42. package/lib/services/infoPanel/infoPanel.d.ts +3 -0
  43. package/lib/services/infoPanel/infoPanel.js +35 -0
  44. package/lib/services/infoPanel/infoPanel.test.d.ts +1 -0
  45. package/lib/services/infoPanel/infoPanel.test.js +70 -0
  46. package/lib/services/keyboardHandlers/resolvekeypress.js +11 -1
  47. package/lib/services/keyboardHandlers/resolvekeypress.test.js +6 -2
  48. package/lib/services/nicamHandlers/nicamhandler.d.ts +1 -0
  49. package/lib/services/nicamHandlers/nicamhandler.js +2 -1
  50. package/lib/services/nicamHandlers/nicamhandler.test.js +2 -2
  51. package/lib/services/npoPlayerAPI/npoPlayerAPI.d.ts +8 -2
  52. package/lib/services/npoPlayerAPI/npoPlayerAPI.js +25 -12
  53. package/lib/services/npoPlayerAPI/playerModules.d.ts +1 -0
  54. package/lib/services/npoPlayerAPI/playerModules.js +46 -0
  55. package/lib/services/preferences/handlePreferences.js +9 -4
  56. package/lib/services/preferences/handlePreferences.test.js +53 -17
  57. package/lib/services/segmentHandlers/addSegmentEventListeners.test.js +1 -1
  58. package/lib/services/segmentHandlers/isSegmentInBounds.d.ts +2 -0
  59. package/lib/services/segmentHandlers/isSegmentInBounds.js +5 -0
  60. package/lib/services/segmentHandlers/isSegmentInBounds.test.d.ts +1 -0
  61. package/lib/services/segmentHandlers/isSegmentInBounds.test.js +27 -0
  62. package/lib/services/services.d.ts +5 -2
  63. package/lib/services/services.js +15 -2
  64. package/lib/services/streamFetchHandler/fetchStream.js +5 -4
  65. package/lib/services/streamFetchHandler/fetchstream.test.js +25 -3
  66. package/lib/services/streamoptionsHandlers/streamOptionsHandler.js +11 -9
  67. package/lib/services/streamoptionsHandlers/streamOptionsHandler.test.js +14 -2
  68. package/lib/services/trackingHandlers/eventBinding.js +48 -33
  69. package/lib/services/trackingHandlers/eventBinding.test.js +57 -1
  70. package/lib/services/trackingHandlers/playerTrackerStart.js +1 -1
  71. package/lib/services/uiHandlers/uiVisiblityHandler.js +6 -0
  72. package/lib/services/verticalVideoHandlers/handleVerticalVideoSettings.test.js +0 -9
  73. package/lib/src/js/markers/updateLiveMarkers.d.ts +7 -2
  74. package/lib/src/js/playeractions/handlers/mediasessionactions.d.ts +7 -2
  75. package/lib/src/js/playeractions/handlers/processplayerconfig.d.ts +1 -1
  76. package/lib/src/js/playeractions/handlers/processsourceconfig.d.ts +11 -2
  77. package/lib/src/js/playeractions/handlers/removereplayclass.d.ts +1 -1
  78. package/lib/src/js/settings/localization.d.ts +1 -0
  79. package/lib/src/js/utilities/utilities.jwt.d.ts +2 -0
  80. package/lib/src/npoplayer.d.ts +6 -5
  81. package/lib/src/services/chapterHandlers/chapterHandler.d.ts +35 -0
  82. package/lib/src/services/errors/errorHandler.d.ts +4 -2
  83. package/lib/src/services/errors/errorRetryHandler.d.ts +2 -0
  84. package/lib/src/services/errors/errorRetryHandler.test.d.ts +1 -0
  85. package/lib/src/services/infoPanel/infoPanel.d.ts +3 -0
  86. package/lib/src/services/infoPanel/infoPanel.test.d.ts +1 -0
  87. package/lib/src/services/nicamHandlers/nicamhandler.d.ts +1 -0
  88. package/lib/src/services/npoPlayerAPI/npoPlayerAPI.d.ts +8 -2
  89. package/lib/src/services/npoPlayerAPI/playerModules.d.ts +1 -0
  90. package/lib/src/services/segmentHandlers/isSegmentInBounds.d.ts +2 -0
  91. package/lib/src/services/segmentHandlers/isSegmentInBounds.test.d.ts +1 -0
  92. package/lib/src/services/services.d.ts +5 -2
  93. package/lib/src/types/events.d.ts +1 -1
  94. package/lib/src/types/interfaces.d.ts +39 -43
  95. package/lib/src/ui/components/adbutton.d.ts +3 -6
  96. package/lib/src/ui/components/adlabel.d.ts +3 -6
  97. package/lib/src/ui/components/audio/controlbar.d.ts +2 -2
  98. package/lib/src/ui/components/buttons.d.ts +18 -11
  99. package/lib/src/ui/components/ctabar.d.ts +4 -10
  100. package/lib/src/ui/components/infopanel/infopanel.d.ts +3 -0
  101. package/lib/src/ui/components/infopanel/togglebutton.d.ts +3 -0
  102. package/lib/src/ui/components/playnext.d.ts +3 -9
  103. package/lib/src/ui/components/seekbar.d.ts +2 -3
  104. package/lib/src/ui/components/settingspanel.d.ts +2 -3
  105. package/lib/src/ui/components/titlebar.d.ts +2 -2
  106. package/lib/src/ui/handlers/domhandlers.d.ts +2 -2
  107. package/lib/src/ui/handlers/listboxhandlers.d.ts +18 -4
  108. package/lib/src/ui/handlers/playnextscreen.d.ts +6 -2
  109. package/lib/src/ui/uicontainer.d.ts +1 -2
  110. package/lib/tests/jest.setup-files-after-env.d.ts +0 -0
  111. package/lib/tests/jest.setup-files-after-env.js +6 -0
  112. package/lib/tests/jest.setup-tests.d.ts +1 -0
  113. package/lib/tests/jest.setup-tests.js +2 -0
  114. package/lib/tests/mocks/mockNpoplayer.js +27 -44
  115. package/lib/tests/mocks/mockPlayerAPI.d.ts +1 -1
  116. package/lib/tests/mocks/mockPlayerAPI.js +15 -1
  117. package/lib/tests/mocks/mockPlayerUi.d.ts +64 -0
  118. package/lib/tests/mocks/mockPlayerUi.js +251 -0
  119. package/lib/tests/mocks/playerContextMock.d.ts +3 -66
  120. package/lib/tests/mocks/playerContextMock.js +34 -7
  121. package/lib/types/events.d.ts +1 -1
  122. package/lib/types/events.js +1 -1
  123. package/lib/types/interfaces.d.ts +39 -43
  124. package/lib/types/interfaces.js +0 -27
  125. package/lib/ui/components/adbutton.d.ts +3 -6
  126. package/lib/ui/components/adbutton.js +2 -1
  127. package/lib/ui/components/adlabel.d.ts +3 -6
  128. package/lib/ui/components/adlabel.js +2 -1
  129. package/lib/ui/components/audio/controlbar.d.ts +2 -2
  130. package/lib/ui/components/audio/controlbar.js +6 -6
  131. package/lib/ui/components/buttons.d.ts +18 -11
  132. package/lib/ui/components/buttons.js +44 -26
  133. package/lib/ui/components/controlbar.js +13 -9
  134. package/lib/ui/components/ctabar.d.ts +4 -10
  135. package/lib/ui/components/ctabar.js +8 -7
  136. package/lib/ui/components/infopanel/infopanel.d.ts +3 -0
  137. package/lib/ui/components/infopanel/infopanel.js +42 -0
  138. package/lib/ui/components/infopanel/togglebutton.d.ts +3 -0
  139. package/lib/ui/components/infopanel/togglebutton.js +18 -0
  140. package/lib/ui/components/playnext.d.ts +3 -9
  141. package/lib/ui/components/playnext.js +3 -3
  142. package/lib/ui/components/seekbar.d.ts +2 -3
  143. package/lib/ui/components/seekbar.js +7 -3
  144. package/lib/ui/components/settingspanel.d.ts +2 -3
  145. package/lib/ui/components/settingspanel.js +79 -53
  146. package/lib/ui/components/titlebar.d.ts +2 -2
  147. package/lib/ui/components/titlebar.js +2 -1
  148. package/lib/ui/components/topbar.js +8 -5
  149. package/lib/ui/components/verticalvideo/controlbar.js +5 -2
  150. package/lib/ui/components/verticalvideo/settingspanel.js +9 -5
  151. package/lib/ui/handlers/accessibilityhandler.js +11 -22
  152. package/lib/ui/handlers/accessibilityhandler.test.js +25 -34
  153. package/lib/ui/handlers/domhandlers.d.ts +2 -2
  154. package/lib/ui/handlers/listboxhandlers.d.ts +18 -4
  155. package/lib/ui/handlers/listboxhandlers.js +5 -3
  156. package/lib/ui/handlers/playnextscreen.d.ts +6 -2
  157. package/lib/ui/handlers/playnextscreen.js +4 -4
  158. package/lib/ui/handlers/playnextscreen.test.js +15 -3
  159. package/lib/ui/uicontainer.d.ts +1 -2
  160. package/lib/ui/uicontainer.js +24 -8
  161. package/lib/ui/uicontainer.test.js +6 -5
  162. package/package.json +7 -9
  163. package/src/style/components/_advert.scss +0 -4
  164. package/src/style/components/_buffering.scss +18 -22
  165. package/src/style/components/_controlbars.scss +0 -4
  166. package/src/style/components/_icons.scss +18 -4
  167. package/src/style/components/_infopanel.scss +105 -0
  168. package/src/style/components/_nicam.scss +8 -4
  169. package/src/style/components/_seekbarthumbnail.scss +5 -0
  170. package/src/style/components/_settingspanel.scss +2 -2
  171. package/src/style/components/_subtitles.scss +0 -1
  172. package/src/style/components/_textbuttons.scss +30 -1
  173. package/src/style/components/_volumeslider.scss +0 -1
  174. package/src/style/components/audio/_bottombar.scss +6 -0
  175. package/src/style/components/audio/_metadata.scss +25 -9
  176. package/src/style/components/audio/_topbar.scss +6 -1
  177. package/src/style/npoplayer.css +170 -85
  178. package/src/style/npoplayer.scss +10 -2
  179. package/src/style/variants/_player-audio-only.scss +32 -0
  180. package/src/style/variants/_player-base.scss +5 -1
  181. package/src/style/variants/_player-large.scss +11 -3
  182. package/src/style/variants/_player-medium.scss +5 -1
  183. package/src/style/variants/_player-small.scss +6 -1
  184. package/src/style/vars/_colors.scss +1 -1
  185. package/src/style/vars/_icons.scss +5 -4
  186. package/src/style/vars/_z-index.scss +1 -1
  187. package/lib/npoplayer-bridge.test.js +0 -24
  188. package/lib/src/ui/components/nativemobile/buttons.d.ts +0 -30
  189. package/lib/src/ui/components/nativemobile/controlbar.d.ts +0 -3
  190. package/lib/src/ui/components/nativemobile/ctabar.d.ts +0 -11
  191. package/lib/src/ui/components/nativemobile/titlebar.d.ts +0 -2
  192. package/lib/src/ui/components/nativemobile/topbar.d.ts +0 -3
  193. package/lib/src/ui/nativemobileuicontainer.d.ts +0 -5
  194. package/lib/src/ui/nativemobileuifactory.d.ts +0 -3
  195. package/lib/src/ui/nativemobileuifactory.test.d.ts +0 -1
  196. package/lib/ui/components/nativemobile/buttons.d.ts +0 -30
  197. package/lib/ui/components/nativemobile/buttons.js +0 -233
  198. package/lib/ui/components/nativemobile/controlbar.d.ts +0 -3
  199. package/lib/ui/components/nativemobile/controlbar.js +0 -43
  200. package/lib/ui/components/nativemobile/ctabar.d.ts +0 -11
  201. package/lib/ui/components/nativemobile/ctabar.js +0 -10
  202. package/lib/ui/components/nativemobile/titlebar.d.ts +0 -2
  203. package/lib/ui/components/nativemobile/titlebar.js +0 -7
  204. package/lib/ui/components/nativemobile/topbar.d.ts +0 -3
  205. package/lib/ui/components/nativemobile/topbar.js +0 -23
  206. package/lib/ui/nativemobileuicontainer.d.ts +0 -5
  207. package/lib/ui/nativemobileuicontainer.js +0 -42
  208. package/lib/ui/nativemobileuifactory.d.ts +0 -3
  209. package/lib/ui/nativemobileuifactory.js +0 -112
  210. package/lib/ui/nativemobileuifactory.test.d.ts +0 -1
  211. package/lib/ui/nativemobileuifactory.test.js +0 -64
  212. package/src/style/variants/_player-native-mobile.scss +0 -13
  213. /package/lib/{npoplayer-bridge.test.d.ts → services/errors/errorHandler.test.d.ts} +0 -0
  214. /package/lib/src/{npoplayer-bridge.test.d.ts → services/errors/errorHandler.test.d.ts} +0 -0
package/lib/npoplayer.js CHANGED
@@ -1,21 +1,20 @@
1
1
  import { NpoPlayerServices } from './services/services';
2
- import { Player, PlayerEvent } from 'bitmovin-player';
3
- import AdsModuleBM from 'bitmovin-player/modules/bitmovinplayer-advertising-bitmovin';
2
+ import { Player, PlayerEvent } from 'bitmovin-player/modules/bitmovinplayer-core';
4
3
  import { hidePlayNextScreen, showPlayNextScreenIfNeeded } from './ui/handlers/playnextscreen';
5
4
  import * as playerAction from './js/playeractions/playeractions';
6
- import { getModuleExport } from './js/utilities/utilities.module';
7
5
  import { isJWTToken } from './js/utilities/utilities.jwt';
8
6
  import { isMediaUrl, isUrl } from './js/utilities/utilities.url';
9
7
  import { logVersion } from './js/utilities/utilities.version';
10
8
  import { LogEmitter } from './types/classes';
11
9
  import pkg from '../package.json';
12
10
  import { NpoPlayerUIVariants } from './types/interfaces';
13
- import { nativeMobileUiFactory } from './ui/nativemobileuifactory';
14
11
  import { setupMediaSessionActionHandlers } from './js/playeractions/handlers/mediasessionactions';
15
12
  import { removeReplayClass } from './js/playeractions/handlers/removereplayclass';
16
13
  import { updateLiveMarkers } from './js/markers/updateLiveMarkers';
17
14
  import { NpoPlayerAPI } from './services/npoPlayerAPI/npoPlayerAPI';
15
+ import { playerModules } from './services/npoPlayerAPI/playerModules';
18
16
  import { emptyStreamObject } from './services/npoPlayerAPI/contants';
17
+ import { ChapterHandler, defaultSkippableChapterTypes } from './services/chapterHandlers/chapterHandler';
19
18
  export { NpoPlayerUIVariants };
20
19
  export default class NpoPlayer {
21
20
  constructor(container, playerConfig, npoTag, npoTagInstance, variant, npoTagPageTracker) {
@@ -40,6 +39,7 @@ export default class NpoPlayer {
40
39
  this.playerContext = undefined;
41
40
  this.npoPlayerServices = new NpoPlayerServices();
42
41
  this.eventListeners = undefined;
42
+ this.chapterHandler = undefined;
43
43
  this.keydownHandler = (e) => {
44
44
  if (!this.playerContext)
45
45
  return;
@@ -51,7 +51,7 @@ export default class NpoPlayer {
51
51
  this.playerConfig = params.playerConfig;
52
52
  this.npoTagInitialisation = params.npoTag || undefined;
53
53
  this.npoTagInstance = params.npoTagInstance;
54
- this.variant = params.variant || NpoPlayerUIVariants.DEFAULT;
54
+ this.variant = params.variant ?? NpoPlayerUIVariants.DEFAULT;
55
55
  this.npoTagPageTracker = params.npoTagPageTracker || undefined;
56
56
  }
57
57
  else {
@@ -59,7 +59,7 @@ export default class NpoPlayer {
59
59
  this.playerConfig = playerConfig || undefined;
60
60
  this.npoTagInitialisation = npoTag || undefined;
61
61
  this.npoTagInstance = npoTagInstance;
62
- this.variant = variant || NpoPlayerUIVariants.DEFAULT;
62
+ this.variant = variant ?? NpoPlayerUIVariants.DEFAULT;
63
63
  this.npoTagPageTracker = npoTagPageTracker || undefined;
64
64
  console.warn('As of NPO Player version 1.25, parameter-based initialization has been deprecated in favor of object-based initialization. This feature will be removed in version 2.0. Please update your implementation to ensure compatibility. For more details, refer to the documentation at: https://docs.npoplayer.nl/implementation/web/player.');
65
65
  }
@@ -71,8 +71,10 @@ export default class NpoPlayer {
71
71
  }
72
72
  initPlayer(_container, playerConfig) {
73
73
  const processedPlayerConfig = playerAction.processPlayerConfig(this, playerConfig);
74
+ for (const module of playerModules) {
75
+ Player.addModule(module);
76
+ }
74
77
  this.player = new Player(_container, processedPlayerConfig);
75
- Player.addModule(getModuleExport(AdsModuleBM));
76
78
  const npoPlayerAPI = new NpoPlayerAPI(this.player);
77
79
  this.playerContext = { player: npoPlayerAPI, npoPlayer: this };
78
80
  this.npoPlayerServices.initPlayerTracker(this.playerContext);
@@ -154,15 +156,24 @@ export default class NpoPlayer {
154
156
  return;
155
157
  const drmType = this.streamObject.stream.drmType ?? undefined;
156
158
  if (drmType !== undefined && drmType !== profile.drm) {
157
- await this.npoPlayerServices.handleError(this.playerContext, 2007);
159
+ await this.npoPlayerServices.handleError({ playerContext: this.playerContext, status: 2007 });
158
160
  return;
159
161
  }
160
- this.sourceConfig = await playerAction.processSourceConfig(this.npoPlayerServices, source, options.sourceConfig ?? {}, this.streamObject, drmType && drmType.length > 0 ? profile.drm : undefined, this.streamOptions, this.version, this.npoTag?.npoTagInstance);
162
+ this.sourceConfig = await playerAction.processSourceConfig({
163
+ npoPlayerServices: this.npoPlayerServices,
164
+ source,
165
+ _sourceConfig: options.sourceConfig ?? {},
166
+ _streamObject: this.streamObject,
167
+ drm: drmType && drmType.length > 0 ? profile.drm : undefined,
168
+ streamOptions: this.streamOptions,
169
+ version: this.version,
170
+ npoTagInstance: this.npoTag?.npoTagInstance
171
+ });
161
172
  this.npoPlayerServices.handleStreamOptions(this.playerContext);
162
173
  await this.npoPlayerServices.verifyDRM(this.playerContext, payload);
163
174
  }
164
175
  else {
165
- await this.npoPlayerServices.handleError(this.playerContext, 500);
176
+ await this.npoPlayerServices.handleError({ playerContext: this.playerContext, status: 500 });
166
177
  }
167
178
  const streamDuration = this.streamObject.metadata.duration;
168
179
  const initAndStartTracker = () => {
@@ -177,8 +188,17 @@ export default class NpoPlayer {
177
188
  });
178
189
  };
179
190
  this.npoPlayerServices.setupNicamKijkwijzerIcons(this.playerContext);
180
- setupMediaSessionActionHandlers(this.player, this.sourceConfig, this.streamObject);
191
+ setupMediaSessionActionHandlers({
192
+ player: this.player,
193
+ sourceConfig: this.sourceConfig,
194
+ streamObject: this.streamObject
195
+ });
181
196
  this.hidePlayNextScreen();
197
+ if (!this.npoPlayerServices.isSegmentInBounds(this.playerContext)) {
198
+ await this.npoPlayerServices.handleError({ playerContext: this.playerContext, status: 422 });
199
+ await this.player.unload();
200
+ return;
201
+ }
182
202
  if (this.variant === NpoPlayerUIVariants.DEFAULT &&
183
203
  this.streamObject.metadata.hasPreroll === 'true' &&
184
204
  this.streamObject.assets.preroll) {
@@ -193,6 +213,25 @@ export default class NpoPlayer {
193
213
  this.npoPlayerServices.handleVerticalVideoControls(this.playerContext);
194
214
  this.npoPlayerServices.handleVerticalVideoSettings(this.playerContext);
195
215
  }
216
+ if (this.streamObject?.metadata?.chapters) {
217
+ const chapters = this.streamObject.metadata.chapters;
218
+ if (!chapters || !this.playerContext)
219
+ return;
220
+ this.chapterHandler = new ChapterHandler(this.playerContext, {
221
+ chapters,
222
+ showSkipButtons: true,
223
+ skippableTypes: this.streamOptions.skippableChapters ?? defaultSkippableChapterTypes
224
+ });
225
+ this.player.on(PlayerEvent.TimeChanged, () => {
226
+ this.chapterHandler?.handleTimeChange();
227
+ });
228
+ this.player.on(PlayerEvent.Paused, () => {
229
+ this.chapterHandler?.handlePause();
230
+ });
231
+ this.player.on(PlayerEvent.Play, () => {
232
+ this.chapterHandler?.handlePlay();
233
+ });
234
+ }
196
235
  this.player.on(PlayerEvent.Seek, () => {
197
236
  removeReplayClass(this.player);
198
237
  });
@@ -265,14 +304,18 @@ export default class NpoPlayer {
265
304
  this.showPlayNextScreen();
266
305
  });
267
306
  const videoDuration = this.player.getDuration();
268
- const overlayDuration = this.streamOptions.playNext?.duration || 10;
269
- const offset = this.streamOptions.playNext?.offset || overlayDuration;
307
+ const overlayDuration = this.streamOptions.playNext?.duration ?? 10;
308
+ const offset = this.streamOptions.playNext?.offset ?? overlayDuration;
270
309
  const showPlayNextAt = videoDuration - offset;
271
310
  if (!this.isShowingPlayNextScreen &&
272
311
  this.player.getCurrentTime() > showPlayNextAt &&
273
312
  !this.canceledPlayNextScreen) {
274
313
  this.isShowingPlayNextScreen = true;
275
- showPlayNextScreenIfNeeded(overlayDuration, this.container, this.streamOptions.playNext?.proceedCallback);
314
+ showPlayNextScreenIfNeeded({
315
+ overlayDuration,
316
+ container: this.container,
317
+ proceedCallBack: this.streamOptions.playNext?.proceedCallback
318
+ });
276
319
  }
277
320
  else if (this.isShowingPlayNextScreen && this.player.getCurrentTime() < showPlayNextAt) {
278
321
  this.isShowingPlayNextScreen = false;
@@ -282,7 +325,12 @@ export default class NpoPlayer {
282
325
  updateMarkers(timeLineMarkers) {
283
326
  if (!(this.player && this.uiManager && this.streamObject.stream.isLiveStream))
284
327
  return;
285
- updateLiveMarkers(timeLineMarkers, this.player, this.uiManager, this.streamOptions.autoFillTimeLineMarkerDuration ?? true);
328
+ updateLiveMarkers({
329
+ timeLineMarkers,
330
+ player: this.player,
331
+ uiManager: this.uiManager,
332
+ autoFillTimeLineMarkerDuration: this.streamOptions.autoFillTimeLineMarkerDuration ?? true
333
+ });
286
334
  }
287
335
  cancelPlayNextScreen() {
288
336
  this.canceledPlayNextScreen = true;
@@ -341,8 +389,11 @@ export default class NpoPlayer {
341
389
  if (this.playerContext) {
342
390
  this.npoPlayerServices.removeEventListeners(this.playerContext);
343
391
  this.npoPlayerServices.discardAdBreak(this.playerContext);
392
+ this.npoPlayerServices.clearInfoPanel(this.playerContext);
344
393
  }
394
+ this.chapterHandler?.reset();
345
395
  this.hidePlayNextScreen();
396
+ this.sourceConfig = {};
346
397
  await this.player?.unload();
347
398
  return true;
348
399
  }
@@ -367,11 +418,3 @@ export default class NpoPlayer {
367
418
  logVersion(this.version);
368
419
  }
369
420
  }
370
- ;
371
- window.bitmovin.playerui = function () {
372
- };
373
- window.bitmovin.playerui.UIFactory = {
374
- buildDefaultSmallScreenUI: function (player, config = {}) {
375
- return nativeMobileUiFactory(player, config);
376
- }
377
- };
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npo/player",
3
- "version": "1.27.8",
3
+ "version": "1.28.1",
4
4
  "description": "NPO Player",
5
5
  "author": "Publieke Omroep <player@npo.nl>",
6
6
  "contributors": [
@@ -16,17 +16,14 @@
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 && cp src/style/*.css.map dist && mkdir -p src/scss && cp src/style/*.css src/scss && cp src/style/*.css.map src/scss",
19
+ "build:scss": "npm run build:web-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
- "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
21
  "build:cdn": "npm run build && bash build-cdn.sh",
23
22
  "lint": "eslint src --fix",
24
23
  "bundle": "npm run lint && bash build-lib.sh && npm run build:scss && npm run build:cdn",
25
24
  "build:ts": "bash build-lib.sh",
26
25
  "test": "jest",
27
- "test:ci": "jest --collectCoverage --testResultsProcessor=jest-sonar-reporter",
28
- "version:dev": "npm version prerelease --preid=dev --no-git-tag-version",
29
- "prepublishOnly": "aws codeartifact login --tool npm --namespace @npo-player --repository npo-player --domain npoplayer $([ -z \"$TF_BUILD\" ] && echo \" --profile codeartifact\")"
26
+ "version:dev": "npm version prerelease --preid=dev --no-git-tag-version"
30
27
  },
31
28
  "files": [
32
29
  "LICENSE",
@@ -67,7 +64,8 @@
67
64
  "jest": "^28.1.3",
68
65
  "jest-environment-jsdom": "^29.4.1",
69
66
  "jest-fetch-mock": "^3.0.3",
70
- "jest-sonar-reporter": "2.0.0",
67
+ "jest-junit": "^16.0.0",
68
+ "jest-sonar-reporter": "^2.0.0",
71
69
  "jest-transform-css": "^6.0.1",
72
70
  "mini-css-extract-plugin": "^2.7.2",
73
71
  "node-fetch": "^3.3.0",
@@ -90,8 +88,8 @@
90
88
  },
91
89
  "dependencies": {
92
90
  "@npotag/tag": "3.2.4",
93
- "bitmovin-player": "^8.166.0",
94
- "bitmovin-player-ui": "3.67.0"
91
+ "bitmovin-player": "^8.228.0",
92
+ "bitmovin-player-ui": "^3.102.0"
95
93
  },
96
94
  "browserslist": [
97
95
  "defaults",
@@ -2,7 +2,7 @@ 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';
5
- jest.mock('bitmovin-player', () => ({
5
+ jest.mock('bitmovin-player/modules/bitmovinplayer-core', () => ({
6
6
  PlayerEvent: {
7
7
  SourceLoaded: 'sourceLoaded',
8
8
  AdBreakFinished: 'adBreakFinished',
@@ -55,9 +55,7 @@ describe('setupAccessibilityAttributes', () => {
55
55
  hidePlayNextScreen: () => { },
56
56
  doPlayNext: () => { },
57
57
  destroy: () => false,
58
- unload: () => {
59
- return true;
60
- },
58
+ unload: () => true,
61
59
  printVersion: () => { }
62
60
  }
63
61
  };
@@ -1,4 +1,3 @@
1
- import { AdTagType } from 'bitmovin-player';
2
1
  import { NpoPlayerUIVariants } from '../../types/interfaces';
3
2
  import { NpoPlayerEvent } from '../../types/events';
4
3
  export async function handlePreRolls(playerContext) {
@@ -18,7 +17,7 @@ export async function handlePreRolls(playerContext) {
18
17
  const advertConfig = {
19
18
  tag: {
20
19
  url: String(prerollUrl),
21
- type: AdTagType.VAST
20
+ type: 'vast'
22
21
  },
23
22
  id: 'Ad',
24
23
  position: 'pre'
@@ -33,7 +32,7 @@ export async function handlePreRolls(playerContext) {
33
32
  playerContext.player.on(NpoPlayerEvent.Play, handlePlay);
34
33
  function setAdUi() {
35
34
  const activeAdBreak = playerContext.player.getActiveAdBreak();
36
- if (!activeAdBreak || !activeAdBreak.ads)
35
+ if (!activeAdBreak?.ads)
37
36
  return false;
38
37
  playerContext.npoPlayer.uiManager?.release();
39
38
  playerContext.npoPlayer.uiManager = undefined;
@@ -92,7 +91,7 @@ export async function handlePreRolls(playerContext) {
92
91
  function handleAdFinished() {
93
92
  playerContext.player.off(NpoPlayerEvent.AdFinished, handleAdFinished);
94
93
  const activeAdBreak = playerContext.player.getActiveAdBreak();
95
- if (!activeAdBreak || !activeAdBreak.ads) {
94
+ if (!activeAdBreak?.ads) {
96
95
  console.error('No active ad break data found');
97
96
  return;
98
97
  }
@@ -1,8 +1,7 @@
1
1
  import { handlePreRolls } from './handlePreRolls';
2
2
  import { PlayerEvent } from 'bitmovin-player/modules/bitmovinplayer-core';
3
3
  import { createMockNpoPlayer, createPlayerContextMock } from '../../../tests/mocks/playerContextMock';
4
- jest.mock('bitmovin-player');
5
- jest.mock('../../npoplayer');
4
+ import { AdTagType } from 'bitmovin-player/modules/bitmovinplayer-advertising-core';
6
5
  describe('handlePreRolls', () => {
7
6
  let mockPlayerAPI;
8
7
  beforeEach(() => {
@@ -78,7 +77,7 @@ describe('handlePreRolls', () => {
78
77
  expect(playerContextMock.player.scheduleAds).toHaveBeenCalledWith({
79
78
  tag: {
80
79
  url: 'sample_url',
81
- type: 'vast'
80
+ type: AdTagType.VAST
82
81
  },
83
82
  id: 'Ad',
84
83
  position: 'pre'
@@ -0,0 +1,35 @@
1
+ import { Chapter, PlayerContext } from '../../types/interfaces';
2
+ export declare const defaultSkippableChapterTypes: string[];
3
+ export interface ChapterHandlerOptions {
4
+ chapters: Chapter[];
5
+ showSkipButtons?: boolean;
6
+ skippableTypes?: string[];
7
+ }
8
+ export declare class ChapterHandler {
9
+ private readonly playerContext;
10
+ private readonly defaultSkippableTypes;
11
+ private readonly options;
12
+ private lastShownFrom?;
13
+ private hideTimeout?;
14
+ private controlsVisible;
15
+ private currentChapter?;
16
+ private isForceShowing;
17
+ private isPaused;
18
+ private pausedTimeRemaining?;
19
+ constructor(playerContext: PlayerContext, options: ChapterHandlerOptions);
20
+ getCurrentChapter(currentTimeMs: number): Chapter | undefined;
21
+ isChapterSkippable(chapter: Chapter): boolean;
22
+ handleTimeChange(): void;
23
+ reset(): void;
24
+ handlePause(): void;
25
+ handlePlay(): void;
26
+ setControlsVisibility(visible: boolean): void;
27
+ private setupSkipButtonHandler;
28
+ private skipCurrentChapter;
29
+ private updateSkipButtonVisibility;
30
+ private startFadeOut;
31
+ private forceShowButton;
32
+ private getCurrentControlsVisibility;
33
+ private startHideTimeout;
34
+ private clearHideTimeout;
35
+ }
@@ -0,0 +1,230 @@
1
+ export const defaultSkippableChapterTypes = ['INTRO', 'RECAP'];
2
+ const TEXT_BUTTON_BAR_SELECTOR = '.bmpui-controlbar-textbuttons';
3
+ const FADING_OUT_CLASS = 'fading-out';
4
+ const FADING_IN_CLASS = 'fading-in';
5
+ export class ChapterHandler {
6
+ constructor(playerContext, options) {
7
+ this.defaultSkippableTypes = defaultSkippableChapterTypes;
8
+ this.controlsVisible = undefined;
9
+ this.isForceShowing = false;
10
+ this.isPaused = false;
11
+ this.playerContext = playerContext;
12
+ this.options = { showSkipButtons: true, skippableTypes: this.defaultSkippableTypes, ...options };
13
+ this.setupSkipButtonHandler();
14
+ }
15
+ getCurrentChapter(currentTimeMs) {
16
+ return this.options.chapters.find((c) => currentTimeMs >= c.from && currentTimeMs < c.until);
17
+ }
18
+ isChapterSkippable(chapter) {
19
+ if (chapter.type === 'IDENT')
20
+ return false;
21
+ return !!this.options.skippableTypes?.includes(chapter.type);
22
+ }
23
+ handleTimeChange() {
24
+ const { player } = this.playerContext;
25
+ if (!player || this.options.chapters.length === 0 || !this.options.showSkipButtons)
26
+ return;
27
+ const tMs = player.getCurrentTime() * 1000;
28
+ const ch = this.getCurrentChapter(tMs);
29
+ this.currentChapter = ch;
30
+ if (ch && this.isChapterSkippable(ch)) {
31
+ if (this.lastShownFrom === undefined || this.lastShownFrom !== ch.from) {
32
+ this.lastShownFrom = ch.from;
33
+ this.clearHideTimeout();
34
+ this.isForceShowing = true;
35
+ this.controlsVisible ?? (this.controlsVisible = this.getCurrentControlsVisibility());
36
+ this.forceShowButton(true);
37
+ if (!this.isPaused && !this.controlsVisible) {
38
+ this.startHideTimeout();
39
+ }
40
+ }
41
+ else if (!this.isForceShowing) {
42
+ this.updateSkipButtonVisibility();
43
+ }
44
+ }
45
+ else {
46
+ if (this.lastShownFrom !== undefined) {
47
+ this.clearHideTimeout();
48
+ this.lastShownFrom = undefined;
49
+ }
50
+ if (this.isForceShowing) {
51
+ this.isForceShowing = false;
52
+ this.forceShowButton(false);
53
+ }
54
+ this.updateSkipButtonVisibility();
55
+ }
56
+ }
57
+ reset() {
58
+ this.clearHideTimeout();
59
+ this.lastShownFrom = undefined;
60
+ this.currentChapter = undefined;
61
+ this.isForceShowing = false;
62
+ this.isPaused = false;
63
+ this.pausedTimeRemaining = undefined;
64
+ this.controlsVisible = undefined;
65
+ this.forceShowButton(false);
66
+ this.updateSkipButtonVisibility();
67
+ }
68
+ handlePause() {
69
+ if (this.hideTimeout && this.isForceShowing) {
70
+ this.isPaused = true;
71
+ this.clearHideTimeout();
72
+ }
73
+ }
74
+ handlePlay() {
75
+ if (this.isPaused && this.isForceShowing) {
76
+ this.isPaused = false;
77
+ if (!this.controlsVisible) {
78
+ this.startHideTimeout();
79
+ }
80
+ }
81
+ }
82
+ setControlsVisibility(visible) {
83
+ const wasVisible = this.controlsVisible;
84
+ this.controlsVisible = visible;
85
+ if (visible && !wasVisible && this.isForceShowing) {
86
+ this.clearHideTimeout();
87
+ }
88
+ else if (!visible && wasVisible && this.isForceShowing && !this.isPaused) {
89
+ this.clearHideTimeout();
90
+ this.startHideTimeout();
91
+ }
92
+ if (!this.isForceShowing) {
93
+ this.updateSkipButtonVisibility();
94
+ }
95
+ }
96
+ setupSkipButtonHandler() {
97
+ const { skipChapterButton } = this.playerContext.npoPlayer.uiComponents;
98
+ if (!skipChapterButton)
99
+ return;
100
+ skipChapterButton.onClick.subscribe(() => {
101
+ this.skipCurrentChapter();
102
+ });
103
+ }
104
+ skipCurrentChapter() {
105
+ if (!this.currentChapter)
106
+ return;
107
+ const { player } = this.playerContext;
108
+ player.seek(this.currentChapter.until / 1000);
109
+ this.clearHideTimeout();
110
+ this.lastShownFrom = undefined;
111
+ if (this.isForceShowing) {
112
+ this.isForceShowing = false;
113
+ this.forceShowButton(false);
114
+ }
115
+ this.updateSkipButtonVisibility();
116
+ }
117
+ updateSkipButtonVisibility() {
118
+ const { skipChapterButton } = this.playerContext.npoPlayer.uiComponents;
119
+ if (!skipChapterButton)
120
+ return;
121
+ const shouldShow = this.currentChapter && this.isChapterSkippable(this.currentChapter) && this.controlsVisible;
122
+ if (shouldShow && this.currentChapter) {
123
+ skipChapterButton.setText(`${this.currentChapter.title}`);
124
+ skipChapterButton.show();
125
+ }
126
+ else {
127
+ skipChapterButton.hide();
128
+ }
129
+ }
130
+ startFadeOut() {
131
+ if (!this.isForceShowing || !this.currentChapter)
132
+ return;
133
+ if (this.controlsVisible || this.isPaused)
134
+ return;
135
+ const { skipChapterButton } = this.playerContext.npoPlayer.uiComponents;
136
+ if (!skipChapterButton)
137
+ return;
138
+ const buttonElement = skipChapterButton.getDomElement();
139
+ const playerContainer = this.playerContext.player.getNpoPlayerElement();
140
+ const ctaBar = playerContainer?.querySelector(TEXT_BUTTON_BAR_SELECTOR);
141
+ if (buttonElement) {
142
+ buttonElement.removeClass(FADING_IN_CLASS);
143
+ buttonElement.addClass(FADING_OUT_CLASS);
144
+ }
145
+ if (ctaBar) {
146
+ ctaBar.classList.remove(FADING_IN_CLASS);
147
+ ctaBar.classList.add(FADING_OUT_CLASS);
148
+ }
149
+ setTimeout(() => {
150
+ this.isForceShowing = false;
151
+ this.forceShowButton(false);
152
+ this.updateSkipButtonVisibility();
153
+ }, 400);
154
+ }
155
+ forceShowButton(forceVisible) {
156
+ const { skipChapterButton } = this.playerContext.npoPlayer.uiComponents;
157
+ if (!skipChapterButton || !this.currentChapter)
158
+ return;
159
+ if (forceVisible && !this.isChapterSkippable(this.currentChapter)) {
160
+ return;
161
+ }
162
+ const playerContainer = this.playerContext.player.getNpoPlayerElement();
163
+ if (forceVisible) {
164
+ skipChapterButton.setText(`${this.currentChapter.title}`);
165
+ skipChapterButton.show();
166
+ const buttonElement = skipChapterButton.getDomElement();
167
+ if (buttonElement) {
168
+ buttonElement.addClass('force-visible-skip-button');
169
+ buttonElement.removeClass(FADING_OUT_CLASS);
170
+ buttonElement.addClass(FADING_IN_CLASS);
171
+ }
172
+ const ctaBar = playerContainer?.querySelector(TEXT_BUTTON_BAR_SELECTOR);
173
+ if (ctaBar) {
174
+ ctaBar.classList.add('force-visible-container');
175
+ ctaBar.classList.remove(FADING_OUT_CLASS);
176
+ ctaBar.classList.add(FADING_IN_CLASS);
177
+ }
178
+ setTimeout(() => {
179
+ if (buttonElement) {
180
+ buttonElement.removeClass(FADING_IN_CLASS);
181
+ }
182
+ if (ctaBar) {
183
+ ctaBar.classList.remove(FADING_IN_CLASS);
184
+ }
185
+ }, 50);
186
+ }
187
+ else {
188
+ const buttonElement = skipChapterButton.getDomElement();
189
+ if (buttonElement) {
190
+ buttonElement.removeClass('force-visible-skip-button');
191
+ buttonElement.removeClass(FADING_OUT_CLASS);
192
+ buttonElement.removeClass(FADING_IN_CLASS);
193
+ }
194
+ const ctaBar = playerContainer?.querySelector(TEXT_BUTTON_BAR_SELECTOR);
195
+ if (ctaBar) {
196
+ ctaBar.classList.remove('force-visible-container');
197
+ ctaBar.classList.remove(FADING_OUT_CLASS);
198
+ ctaBar.classList.remove(FADING_IN_CLASS);
199
+ }
200
+ }
201
+ }
202
+ getCurrentControlsVisibility() {
203
+ const { uiManager } = this.playerContext.npoPlayer;
204
+ if (!uiManager?.activeUi)
205
+ return true;
206
+ try {
207
+ const uiContainer = uiManager.activeUi.getUI();
208
+ if (!uiContainer)
209
+ return true;
210
+ const containerElement = uiContainer.getDomElement();
211
+ if (!containerElement)
212
+ return true;
213
+ return !containerElement.hasClass('bmpui-controls-hidden');
214
+ }
215
+ catch {
216
+ return true;
217
+ }
218
+ }
219
+ startHideTimeout() {
220
+ this.hideTimeout = window.setTimeout(() => {
221
+ this.startFadeOut();
222
+ }, 7400);
223
+ }
224
+ clearHideTimeout() {
225
+ if (this.hideTimeout) {
226
+ window.clearTimeout(this.hideTimeout);
227
+ this.hideTimeout = undefined;
228
+ }
229
+ }
230
+ }
@@ -14,7 +14,7 @@ describe('setErrorBackground', () => {
14
14
  }
15
15
  };
16
16
  setErrorBackground(mockPlayerContext);
17
- expect(mockPlayerContainer.style.getPropertyValue('--npo-player-errormessage-background-image')).toBe(posterUrl);
17
+ expect(mockPlayerContainer.style.getPropertyValue('--npo-player-errormessage-background-image')).toBe(`url(${posterUrl})`);
18
18
  });
19
19
  it('sets the error background image to "none" if poster is not provided', () => {
20
20
  const mockPlayerContainer = document.createElement('div');
@@ -1,3 +1,5 @@
1
- import { PlayerContext } from '../../types/interfaces';
2
- export declare function handlePlayerError(playerContext: PlayerContext, status: number): Promise<void>;
1
+ import { LogErrorParams, PlayerContext } from '../../types/interfaces';
2
+ export declare function handlePlayerError({ playerContext, status, context }: LogErrorParams): Promise<void>;
3
+ export declare function generateErrorMessage(status: number, shouldRetry: boolean): string;
4
+ export declare function setupRetryHandler(playerContext: PlayerContext, errorContainerClass: string): void;
3
5
  export declare function setErrorBackground(playerContext: PlayerContext): void;