@npo/player 1.27.7 → 1.28.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 (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 +81 -28
  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 +40 -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 +99 -0
  168. package/src/style/components/_nicam.scss +6 -2
  169. package/src/style/components/_seekbarthumbnail.scss +5 -0
  170. package/src/style/components/_settingspanel.scss +1 -1
  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 +166 -83
  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 +3 -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,34 +156,82 @@ 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
- const streamDuration = _streamObject.metadata.duration;
174
+ }
175
+ else {
176
+ await this.npoPlayerServices.handleError({ playerContext: this.playerContext, status: 500 });
177
+ }
178
+ const streamDuration = this.streamObject.metadata.duration;
179
+ const initAndStartTracker = () => {
180
+ if (!this.playerContext || !this.player)
181
+ return;
182
+ this.player?.off(PlayerEvent.AdBreakFinished, initAndStartTracker);
183
+ this.player?.off(PlayerEvent.AdError, initAndStartTracker);
164
184
  this.npoPlayerServices.startPlayerTracker({
165
185
  playerContext: this.playerContext,
166
186
  source: source,
167
187
  duration: streamDuration ? Number(streamDuration) : undefined
168
188
  });
169
- }
170
- else {
171
- await this.npoPlayerServices.handleError(this.playerContext, 500);
172
- }
189
+ };
173
190
  this.npoPlayerServices.setupNicamKijkwijzerIcons(this.playerContext);
174
- setupMediaSessionActionHandlers(this.player, this.sourceConfig, this.streamObject);
191
+ setupMediaSessionActionHandlers({
192
+ player: this.player,
193
+ sourceConfig: this.sourceConfig,
194
+ streamObject: this.streamObject
195
+ });
175
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
+ }
176
202
  if (this.variant === NpoPlayerUIVariants.DEFAULT &&
177
203
  this.streamObject.metadata.hasPreroll === 'true' &&
178
204
  this.streamObject.assets.preroll) {
205
+ this.player.on(PlayerEvent.AdBreakFinished, initAndStartTracker);
206
+ this.player.on(PlayerEvent.AdError, initAndStartTracker);
179
207
  await this.npoPlayerServices.schedulePreRolls(this.playerContext);
180
208
  }
209
+ else {
210
+ initAndStartTracker();
211
+ }
181
212
  if (this.variant === NpoPlayerUIVariants.VERTICAL) {
182
213
  this.npoPlayerServices.handleVerticalVideoControls(this.playerContext);
183
214
  this.npoPlayerServices.handleVerticalVideoSettings(this.playerContext);
184
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
+ }
185
235
  this.player.on(PlayerEvent.Seek, () => {
186
236
  removeReplayClass(this.player);
187
237
  });
@@ -254,14 +304,18 @@ export default class NpoPlayer {
254
304
  this.showPlayNextScreen();
255
305
  });
256
306
  const videoDuration = this.player.getDuration();
257
- const overlayDuration = this.streamOptions.playNext?.duration || 10;
258
- const offset = this.streamOptions.playNext?.offset || overlayDuration;
307
+ const overlayDuration = this.streamOptions.playNext?.duration ?? 10;
308
+ const offset = this.streamOptions.playNext?.offset ?? overlayDuration;
259
309
  const showPlayNextAt = videoDuration - offset;
260
310
  if (!this.isShowingPlayNextScreen &&
261
311
  this.player.getCurrentTime() > showPlayNextAt &&
262
312
  !this.canceledPlayNextScreen) {
263
313
  this.isShowingPlayNextScreen = true;
264
- showPlayNextScreenIfNeeded(overlayDuration, this.container, this.streamOptions.playNext?.proceedCallback);
314
+ showPlayNextScreenIfNeeded({
315
+ overlayDuration,
316
+ container: this.container,
317
+ proceedCallBack: this.streamOptions.playNext?.proceedCallback
318
+ });
265
319
  }
266
320
  else if (this.isShowingPlayNextScreen && this.player.getCurrentTime() < showPlayNextAt) {
267
321
  this.isShowingPlayNextScreen = false;
@@ -271,7 +325,12 @@ export default class NpoPlayer {
271
325
  updateMarkers(timeLineMarkers) {
272
326
  if (!(this.player && this.uiManager && this.streamObject.stream.isLiveStream))
273
327
  return;
274
- 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
+ });
275
334
  }
276
335
  cancelPlayNextScreen() {
277
336
  this.canceledPlayNextScreen = true;
@@ -327,12 +386,14 @@ export default class NpoPlayer {
327
386
  if (this.npoTag != undefined) {
328
387
  clearInterval(this.npoTag.heartbeatInterval);
329
388
  }
330
- this.pause();
331
389
  if (this.playerContext) {
332
390
  this.npoPlayerServices.removeEventListeners(this.playerContext);
333
391
  this.npoPlayerServices.discardAdBreak(this.playerContext);
392
+ this.npoPlayerServices.clearInfoPanel(this.playerContext);
334
393
  }
394
+ this.chapterHandler?.reset();
335
395
  this.hidePlayNextScreen();
396
+ this.sourceConfig = {};
336
397
  await this.player?.unload();
337
398
  return true;
338
399
  }
@@ -357,11 +418,3 @@ export default class NpoPlayer {
357
418
  logVersion(this.version);
358
419
  }
359
420
  }
360
- ;
361
- window.bitmovin.playerui = function () {
362
- };
363
- window.bitmovin.playerui.UIFactory = {
364
- buildDefaultSmallScreenUI: function (player, config = {}) {
365
- return nativeMobileUiFactory(player, config);
366
- }
367
- };
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npo/player",
3
- "version": "1.27.7",
3
+ "version": "1.28.0",
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;