@npo/player 1.26.0 → 1.27.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 (175) hide show
  1. package/README.md +1 -1
  2. package/lib/js/playeractions/handlers/mediasessionactions.js +2 -2
  3. package/lib/js/playeractions/playeractions.d.ts +0 -1
  4. package/lib/js/playeractions/playeractions.js +0 -1
  5. package/lib/js/utilities/utilities.prid.d.ts +6 -0
  6. package/lib/js/utilities/utilities.prid.js +8 -0
  7. package/lib/js/utilities/utilities.prid.test.js +46 -0
  8. package/lib/npoplayer.d.ts +1 -1
  9. package/lib/npoplayer.js +50 -59
  10. package/lib/npoplayer.test.js +2 -2
  11. package/lib/package.json +1 -1
  12. package/lib/services/a11y/setup.test.js +0 -1
  13. package/lib/services/advertHandlers/discardAdBreak.js +2 -0
  14. package/lib/services/advertHandlers/discardAdBreak.test.js +6 -1
  15. package/lib/services/advertHandlers/handlePreRolls.js +2 -4
  16. package/lib/services/advertHandlers/handlePrerolls.test.js +62 -1
  17. package/lib/services/drmHandlers/decideprofile.js +7 -1
  18. package/lib/services/drmHandlers/decideprofile.test.js +4 -0
  19. package/lib/services/drmHandlers/verifydrm.js +5 -6
  20. package/lib/services/drmHandlers/verifydrm.test.js +28 -5
  21. package/lib/services/errors/errorBackground.test.js +48 -0
  22. package/lib/services/errors/errorHandler.d.ts +3 -0
  23. package/lib/services/errors/errorHandler.js +45 -0
  24. package/lib/services/errors/errorText.d.ts +2 -0
  25. package/lib/services/errors/errorText.js +59 -0
  26. package/lib/services/errors/errorText.test.js +66 -0
  27. package/lib/services/keyboardHandlers/resolvekeypress.js +21 -17
  28. package/lib/services/localStorageHandlers/localStorageHandlers.js +5 -2
  29. package/lib/services/localStorageHandlers/localStorageHandlers.test.js +0 -1
  30. package/lib/services/nicamHandlers/nicamhandler.js +0 -4
  31. package/lib/services/nicamHandlers/nicamhandler.test.js +0 -5
  32. package/lib/services/npoPlayerAPI/contants.d.ts +20 -0
  33. package/lib/services/npoPlayerAPI/contants.js +6 -0
  34. package/lib/services/npoPlayerAPI/npoPlayerAPI.d.ts +4 -1
  35. package/lib/services/npoPlayerAPI/npoPlayerAPI.js +29 -5
  36. package/lib/services/npoPlayerAPI/npoPlayerAPI.test.js +11 -0
  37. package/lib/services/services.d.ts +8 -2
  38. package/lib/services/services.js +28 -2
  39. package/lib/services/streamFetchHandler/fetchStream.d.ts +2 -0
  40. package/lib/services/streamFetchHandler/fetchStream.js +48 -0
  41. package/lib/services/streamFetchHandler/fetchstream.test.js +70 -0
  42. package/lib/services/trackingHandlers/eventBinding.d.ts +2 -0
  43. package/lib/{js/tracking/handlers/eventbinding.js → services/trackingHandlers/eventBinding.js} +16 -14
  44. package/lib/services/trackingHandlers/eventBinding.test.js +89 -0
  45. package/lib/services/trackingHandlers/eventLogging.d.ts +2 -0
  46. package/lib/{js/tracking/handlers/eventlogging.js → services/trackingHandlers/eventLogging.js} +18 -7
  47. package/lib/services/trackingHandlers/eventLogging.test.js +63 -0
  48. package/lib/services/trackingHandlers/index.d.ts +3 -0
  49. package/lib/services/trackingHandlers/index.js +3 -0
  50. package/lib/services/trackingHandlers/playerTrackerInit.d.ts +2 -0
  51. package/lib/{js/tracking/handlers/playertrackerinit.js → services/trackingHandlers/playerTrackerInit.js} +6 -3
  52. package/lib/services/trackingHandlers/playerTrackerInit.test.js +75 -0
  53. package/lib/services/trackingHandlers/playerTrackerStart.d.ts +2 -0
  54. package/lib/services/trackingHandlers/playerTrackerStart.js +25 -0
  55. package/lib/services/trackingHandlers/playerTrackerStart.test.js +59 -0
  56. package/lib/services/trackingHandlers/streamTrackerInit.d.ts +3 -0
  57. package/lib/services/trackingHandlers/streamTrackerInit.js +27 -0
  58. package/lib/services/trackingHandlers/streamTrackerInit.test.js +84 -0
  59. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.d.ts +2 -2
  60. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.js +4 -6
  61. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.test.js +4 -17
  62. package/lib/services/verticalVideoHandlers/handleVerticalVideoSettings.d.ts +2 -0
  63. package/lib/services/verticalVideoHandlers/handleVerticalVideoSettings.js +26 -0
  64. package/lib/services/verticalVideoHandlers/handleVerticalVideoSettings.test.js +82 -0
  65. package/lib/src/js/playeractions/playeractions.d.ts +0 -1
  66. package/lib/src/js/utilities/utilities.prid.d.ts +6 -0
  67. package/lib/src/js/utilities/utilities.prid.test.d.ts +1 -0
  68. package/lib/src/npoplayer.d.ts +1 -1
  69. package/lib/src/services/errors/errorBackground.test.d.ts +1 -0
  70. package/lib/src/services/errors/errorHandler.d.ts +3 -0
  71. package/lib/src/services/errors/errorText.d.ts +2 -0
  72. package/lib/src/services/errors/errorText.test.d.ts +1 -0
  73. package/lib/src/services/npoPlayerAPI/contants.d.ts +20 -0
  74. package/lib/src/services/npoPlayerAPI/npoPlayerAPI.d.ts +4 -1
  75. package/lib/src/services/services.d.ts +8 -2
  76. package/lib/src/services/streamFetchHandler/fetchStream.d.ts +2 -0
  77. package/lib/src/services/streamFetchHandler/fetchstream.test.d.ts +1 -0
  78. package/lib/src/services/trackingHandlers/eventBinding.d.ts +2 -0
  79. package/lib/src/services/trackingHandlers/eventBinding.test.d.ts +1 -0
  80. package/lib/src/services/trackingHandlers/eventLogging.d.ts +2 -0
  81. package/lib/src/services/trackingHandlers/eventLogging.test.d.ts +1 -0
  82. package/lib/src/services/trackingHandlers/index.d.ts +3 -0
  83. package/lib/src/services/trackingHandlers/playerTrackerInit.d.ts +2 -0
  84. package/lib/src/services/trackingHandlers/playerTrackerInit.test.d.ts +1 -0
  85. package/lib/src/services/trackingHandlers/playerTrackerStart.d.ts +2 -0
  86. package/lib/src/services/trackingHandlers/playerTrackerStart.test.d.ts +1 -0
  87. package/lib/src/services/trackingHandlers/streamTrackerInit.d.ts +3 -0
  88. package/lib/src/services/trackingHandlers/streamTrackerInit.test.d.ts +1 -0
  89. package/lib/src/services/verticalVideoHandlers/handleVerticalVideoControls.d.ts +2 -2
  90. package/lib/src/services/verticalVideoHandlers/handleVerticalVideoSettings.d.ts +2 -0
  91. package/lib/src/services/verticalVideoHandlers/handleVerticalVideoSettings.test.d.ts +1 -0
  92. package/lib/src/types/interfaces.d.ts +15 -1
  93. package/lib/src/ui/components/seekbar.d.ts +2 -1
  94. package/lib/src/ui/components/verticalvideo/controlbar.d.ts +3 -3
  95. package/lib/src/ui/components/verticalvideo/settingspanel.d.ts +2 -0
  96. package/lib/tests/mocks/mockNpoplayer.js +1 -1
  97. package/lib/tests/mocks/mockStreamObject.d.ts +2 -0
  98. package/lib/tests/mocks/mockStreamObject.js +21 -0
  99. package/lib/tests/mocks/playerContextMock.d.ts +1 -0
  100. package/lib/tests/mocks/playerContextMock.js +5 -1
  101. package/lib/types/interfaces.d.ts +15 -1
  102. package/lib/ui/components/audio/controlbar.js +2 -1
  103. package/lib/ui/components/controlbar.js +1 -1
  104. package/lib/ui/components/seekbar.d.ts +2 -1
  105. package/lib/ui/components/seekbar.js +2 -2
  106. package/lib/ui/components/settingspanel.js +1 -3
  107. package/lib/ui/components/verticalvideo/controlbar.d.ts +3 -3
  108. package/lib/ui/components/verticalvideo/controlbar.js +14 -11
  109. package/lib/ui/components/verticalvideo/settingspanel.d.ts +2 -0
  110. package/lib/ui/components/verticalvideo/settingspanel.js +21 -0
  111. package/lib/ui/handlers/domhandlers.test.js +6 -26
  112. package/lib/ui/nativemobileuifactory.js +1 -1
  113. package/lib/ui/uicontainer.js +4 -1
  114. package/lib/ui/uicontainer.test.js +4 -1
  115. package/package.json +1 -1
  116. package/src/style/components/_advert.scss +3 -3
  117. package/src/style/components/_buffering.scss +14 -8
  118. package/src/style/components/_error.scss +44 -1
  119. package/src/style/components/_hugeplaybacktogglebutton.scss +1 -0
  120. package/src/style/components/_metadata.scss +13 -12
  121. package/src/style/components/_nicam.scss +11 -6
  122. package/src/style/components/_playnext.scss +1 -1
  123. package/src/style/components/_replay.scss +1 -6
  124. package/src/style/components/_settingspanel.scss +74 -79
  125. package/src/style/components/audio/_errors.scss +1 -1
  126. package/src/style/components/audio/_metadata.scss +14 -8
  127. package/src/style/components/audio/_playbutton.scss +1 -0
  128. package/src/style/components/audio/_topbar.scss +10 -2
  129. package/src/style/components/audio/_volumeslider.scss +3 -3
  130. package/src/style/components/vertical-video/_bottombar.scss +42 -1
  131. package/src/style/components/vertical-video/_hugeplaybacktogglebutton.scss +24 -4
  132. package/src/style/components/vertical-video/_settingspanel.scss +20 -3
  133. package/src/style/components/vertical-video/_topbar.scss +47 -6
  134. package/src/style/npoplayer.css +86 -53
  135. package/src/style/npoplayer.scss +4 -7
  136. package/src/style/variants/_player-base.scss +10 -2
  137. package/src/style/variants/_player-small.scss +15 -1
  138. package/src/style/vars/_fonts.scss +13 -7
  139. package/src/style/vars/_z-index.scss +1 -0
  140. package/lib/js/api/getstreamobject.d.ts +0 -3
  141. package/lib/js/api/getstreamobject.js +0 -38
  142. package/lib/js/api/getstreamobject.test.js +0 -48
  143. package/lib/js/playeractions/customerrors.test.js +0 -51
  144. package/lib/js/playeractions/handlers/customerrors.d.ts +0 -50
  145. package/lib/js/playeractions/handlers/customerrors.js +0 -56
  146. package/lib/js/playeractions/handlers/error.d.ts +0 -3
  147. package/lib/js/playeractions/handlers/error.js +0 -14
  148. package/lib/js/playeractions/handlers/error.test.js +0 -44
  149. package/lib/js/tracking/handlers/eventbinding.d.ts +0 -3
  150. package/lib/js/tracking/handlers/eventlogging.d.ts +0 -2
  151. package/lib/js/tracking/handlers/eventlogging.test.js +0 -46
  152. package/lib/js/tracking/handlers/playertrackerinit.d.ts +0 -4
  153. package/lib/js/tracking/handlers/playertrackerinit.test.js +0 -74
  154. package/lib/js/tracking/handlers/playertrackerstart.d.ts +0 -1
  155. package/lib/js/tracking/handlers/playertrackerstart.js +0 -25
  156. package/lib/js/tracking/playertracker.d.ts +0 -4
  157. package/lib/js/tracking/playertracker.js +0 -4
  158. package/lib/src/js/api/getstreamobject.d.ts +0 -3
  159. package/lib/src/js/playeractions/handlers/customerrors.d.ts +0 -50
  160. package/lib/src/js/playeractions/handlers/error.d.ts +0 -3
  161. package/lib/src/js/tracking/handlers/eventbinding.d.ts +0 -3
  162. package/lib/src/js/tracking/handlers/eventlogging.d.ts +0 -2
  163. package/lib/src/js/tracking/handlers/playertrackerinit.d.ts +0 -4
  164. package/lib/src/js/tracking/handlers/playertrackerstart.d.ts +0 -1
  165. package/lib/src/js/tracking/playertracker.d.ts +0 -4
  166. /package/lib/js/{api/getstreamobject.test.d.ts → utilities/utilities.prid.test.d.ts} +0 -0
  167. /package/lib/{js/playeractions/customerrors.test.d.ts → services/errors/errorBackground.test.d.ts} +0 -0
  168. /package/lib/{js/playeractions/handlers/error.test.d.ts → services/errors/errorText.test.d.ts} +0 -0
  169. /package/lib/{js/tracking/handlers/eventlogging.test.d.ts → services/streamFetchHandler/fetchstream.test.d.ts} +0 -0
  170. /package/lib/{js/tracking/handlers/playertrackerinit.test.d.ts → services/trackingHandlers/eventBinding.test.d.ts} +0 -0
  171. /package/lib/{src/js/api/getstreamobject.test.d.ts → services/trackingHandlers/eventLogging.test.d.ts} +0 -0
  172. /package/lib/{src/js/playeractions/customerrors.test.d.ts → services/trackingHandlers/playerTrackerInit.test.d.ts} +0 -0
  173. /package/lib/{src/js/playeractions/handlers/error.test.d.ts → services/trackingHandlers/playerTrackerStart.test.d.ts} +0 -0
  174. /package/lib/{src/js/tracking/handlers/eventlogging.test.d.ts → services/trackingHandlers/streamTrackerInit.test.d.ts} +0 -0
  175. /package/lib/{src/js/tracking/handlers/playertrackerinit.test.d.ts → services/verticalVideoHandlers/handleVerticalVideoSettings.test.d.ts} +0 -0
package/README.md CHANGED
@@ -11,7 +11,7 @@ Extensive and up-to-date documentation is available at https://docs.npoplayer.nl
11
11
  Code quality is analysed by SonarCloud. The project can be found at https://sonarcloud.io/project/overview?id=NPOstart_npo-player
12
12
 
13
13
  # Changelog
14
- Current version: v1.26.0
14
+ Current version: v1.27.0
15
15
 
16
16
  The changelog is available at https://docs.npoplayer.nl/implementation/web/changelog/
17
17
 
@@ -91,7 +91,7 @@ export function setupMediaSessionActionHandlers(player, sourceConfig, _streamObj
91
91
  },
92
92
  {
93
93
  action: 'play',
94
- handler: () => player.play()
94
+ handler: () => void player.play()
95
95
  },
96
96
  {
97
97
  action: 'pause',
@@ -99,7 +99,7 @@ export function setupMediaSessionActionHandlers(player, sourceConfig, _streamObj
99
99
  },
100
100
  {
101
101
  action: 'stop',
102
- handler: () => player.unload()
102
+ handler: () => void player.unload()
103
103
  }
104
104
  ];
105
105
  for (const { action, handler } of actionHandlers) {
@@ -1,4 +1,3 @@
1
1
  export { handleStartOffset, handleLiveOffsetLogic, shiftToProgramStart } from './handlers/handleoffsets';
2
- export { handlePlayerError } from './handlers/error';
3
2
  export { processPlayerConfig } from './handlers/processplayerconfig';
4
3
  export { processSourceConfig } from './handlers/processsourceconfig';
@@ -1,4 +1,3 @@
1
1
  export { handleStartOffset, handleLiveOffsetLogic, shiftToProgramStart } from './handlers/handleoffsets';
2
- export { handlePlayerError } from './handlers/error';
3
2
  export { processPlayerConfig } from './handlers/processplayerconfig';
4
3
  export { processSourceConfig } from './handlers/processsourceconfig';
@@ -0,0 +1,6 @@
1
+ import { SourceConfig, StreamObject } from 'types/interfaces';
2
+ export declare function getAnalyticsPrid(params: {
3
+ source: string;
4
+ sourceConfig?: SourceConfig;
5
+ streamObject?: StreamObject;
6
+ }): string;
@@ -0,0 +1,8 @@
1
+ import { isJWTToken } from './utilities.jwt';
2
+ export function getAnalyticsPrid(params) {
3
+ const { source, sourceConfig, streamObject } = params;
4
+ if (isJWTToken(source)) {
5
+ return streamObject?.metadata?.prid ?? '';
6
+ }
7
+ return sourceConfig?.metadata?.prid ?? source;
8
+ }
@@ -0,0 +1,46 @@
1
+ import { mockStreamObject } from '../../../tests/mocks/mockStreamObject';
2
+ import { getAnalyticsPrid } from './utilities.prid';
3
+ describe('getAnalyticsPrid', () => {
4
+ it('should return the prid from streamObject if the source is a JWT token', () => {
5
+ const result = getAnalyticsPrid({
6
+ source: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
7
+ sourceConfig: undefined,
8
+ streamObject: {
9
+ ...mockStreamObject,
10
+ metadata: {
11
+ ...mockStreamObject.metadata,
12
+ prid: '123'
13
+ }
14
+ }
15
+ });
16
+ expect(result).toBe('123');
17
+ });
18
+ it('should return an empty string if the source is a JWT token but no prid is present in streamObject', () => {
19
+ const result = getAnalyticsPrid({
20
+ source: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
21
+ sourceConfig: undefined,
22
+ streamObject: {
23
+ ...mockStreamObject
24
+ }
25
+ });
26
+ expect(result).toBe('');
27
+ });
28
+ it('should return the prid from sourceConfig if the source is a media file URL', () => {
29
+ const result = getAnalyticsPrid({
30
+ source: 'https://example.com/media/video.mp4',
31
+ sourceConfig: { metadata: { prid: '456' } },
32
+ streamObject: undefined
33
+ });
34
+ expect(result).toBe('456');
35
+ });
36
+ it('should return the source (media file URL) if no prid is found', () => {
37
+ const result = getAnalyticsPrid({
38
+ source: 'https://example.com/media/audio.mp3',
39
+ sourceConfig: undefined,
40
+ streamObject: {
41
+ ...mockStreamObject
42
+ }
43
+ });
44
+ expect(result).toBe('https://example.com/media/audio.mp3');
45
+ });
46
+ });
@@ -41,9 +41,9 @@ export default class NpoPlayer {
41
41
  variant?: NpoPlayerUIVariants;
42
42
  npoTagPageTracker?: PageTracker | undefined;
43
43
  }, playerConfig?: PlayerConfig | undefined, npoTag?: InitialisationProps | undefined, npoTagInstance?: NPOTag | undefined, variant?: NpoPlayerUIVariants, npoTagPageTracker?: PageTracker | undefined);
44
+ keydownHandler: (e: KeyboardEvent) => void;
44
45
  initPlayer(_container: HTMLElement, playerConfig: PlayerConfig): void;
45
46
  loadStream(source: string, options?: StreamOptions): Promise<void>;
46
- doError(input: any, status?: number): void;
47
47
  play(): Promise<void>;
48
48
  pause(): void;
49
49
  setVolume(volume: number): void;
package/lib/npoplayer.js CHANGED
@@ -1,15 +1,12 @@
1
1
  import { NpoPlayerServices } from './services/services';
2
2
  import { Player, PlayerEvent } from 'bitmovin-player';
3
3
  import AdsModuleBM from 'bitmovin-player/modules/bitmovinplayer-advertising-bitmovin';
4
- import { logEvent, initPlayerTracker, startPlayerTracker } from './js/tracking/playertracker';
5
4
  import { hidePlayNextScreen, showPlayNextScreenIfNeeded } from './ui/handlers/playnextscreen';
6
5
  import * as playerAction from './js/playeractions/playeractions';
7
6
  import { getModuleExport } from './js/utilities/utilities.module';
8
- import { getStreamObject } from './js/api/getstreamobject';
9
7
  import { isJWTToken } from './js/utilities/utilities.jwt';
10
8
  import { isMediaUrl, isUrl } from './js/utilities/utilities.url';
11
9
  import { logVersion } from './js/utilities/utilities.version';
12
- import { getStreamDurationInSeconds } from './js/utilities/utilities.stream';
13
10
  import { LogEmitter } from './types/classes';
14
11
  import pkg from '../package.json';
15
12
  import { NpoPlayerUIVariants } from './types/interfaces';
@@ -18,16 +15,12 @@ import { setupMediaSessionActionHandlers } from './js/playeractions/handlers/med
18
15
  import { removeReplayClass } from './js/playeractions/handlers/removereplayclass';
19
16
  import { updateLiveMarkers } from './js/markers/updateLiveMarkers';
20
17
  import { NpoPlayerAPI } from './services/npoPlayerAPI/npoPlayerAPI';
18
+ import { emptyStreamObject } from './services/npoPlayerAPI/contants';
21
19
  export { NpoPlayerUIVariants };
22
20
  export default class NpoPlayer {
23
21
  constructor(container, playerConfig, npoTag, npoTagInstance, variant, npoTagPageTracker) {
24
22
  this.sourceConfig = {};
25
- this.streamObject = {
26
- stream: { drmType: '', streamProfile: '', streamURL: '', avType: '', sourceProfile: '' },
27
- metadata: { title: '', description: '' },
28
- assets: { scrubbingThumbnail: '', subtitles: [] },
29
- user: { type: 'anonymous' }
30
- };
23
+ this.streamObject = emptyStreamObject;
31
24
  this.player = undefined;
32
25
  this.uiManager = undefined;
33
26
  this.npoTagInitialisation = undefined;
@@ -47,6 +40,11 @@ export default class NpoPlayer {
47
40
  this.playerContext = undefined;
48
41
  this.npoPlayerServices = new NpoPlayerServices();
49
42
  this.eventListeners = undefined;
43
+ this.keydownHandler = (e) => {
44
+ if (!this.playerContext)
45
+ return;
46
+ this.npoPlayerServices.keyboardHandler(this.playerContext, e);
47
+ };
50
48
  if (typeof container === 'object' && !(container instanceof HTMLElement)) {
51
49
  const params = container;
52
50
  this.container = params.container;
@@ -69,9 +67,6 @@ export default class NpoPlayer {
69
67
  console.error('Player configuration is missing. Ensure that playerConfig is properly initialized before using the player.');
70
68
  return;
71
69
  }
72
- if (this.npoTagInstance) {
73
- initPlayerTracker(this, this.npoTagInitialisation, this.npoTagInstance, this.npoTagPageTracker);
74
- }
75
70
  this.initPlayer(this.container, this.playerConfig);
76
71
  }
77
72
  initPlayer(_container, playerConfig) {
@@ -80,36 +75,30 @@ export default class NpoPlayer {
80
75
  Player.addModule(getModuleExport(AdsModuleBM));
81
76
  const npoPlayerAPI = new NpoPlayerAPI(this.player);
82
77
  this.playerContext = { player: npoPlayerAPI, npoPlayer: this };
78
+ this.npoPlayerServices.initPlayerTracker(this.playerContext);
83
79
  this.npoPlayerServices.handleUserPrefs(this.playerContext);
84
80
  this.npoPlayerServices.setAccessibilityAttributes(this.playerContext);
85
81
  this.npoPlayerServices.handlePreferences(this.playerContext);
86
- this.container.addEventListener('keydown', (e) => {
87
- if (!this.playerContext)
88
- return;
89
- this.npoPlayerServices.keyboardHandler(this.playerContext, e);
90
- }, true);
82
+ this.container.addEventListener('keydown', this.keydownHandler, true);
91
83
  }
92
84
  async loadStream(source, options = {}) {
93
- if (!this.playerContext)
94
- return;
95
- this.streamOptions = options;
96
- if (!this.player) {
85
+ if (!this.playerContext || !this.player) {
97
86
  console.error('Er is nog geen player geladen.');
98
87
  return;
99
88
  }
89
+ this.streamOptions = options;
90
+ this.playerContext.player.removeClassFromNpoPlayerElement('npo-player-error');
100
91
  this.npoPlayerServices.discardAdBreak(this.playerContext);
101
92
  let _streamObject;
102
93
  const sourceIsUrl = isUrl(source);
103
94
  const sourceIsMedia = await isMediaUrl(source);
104
95
  const sourceIsJWTToken = isJWTToken(source);
105
- const getDurationAndStartPlayerTracker = function () {
106
- if (this.player === null)
107
- return;
108
- const prid = (isJWTToken(source) ? this.streamObject?.metadata.prid : source) || 'unknown';
109
- const duration = this.player ? this.player.getDuration() : undefined;
110
- startPlayerTracker(this, getStreamDurationInSeconds({ duration: duration }), this.version, prid);
111
- }.bind(this);
96
+ if (this.streamOptions.npoTagPageTracker) {
97
+ this.npoTagPageTracker = this.streamOptions.npoTagPageTracker;
98
+ }
112
99
  if (sourceIsUrl && sourceIsMedia) {
100
+ _streamObject = emptyStreamObject;
101
+ this.streamObject = _streamObject;
113
102
  this.sourceConfig = {
114
103
  ...options.sourceConfig,
115
104
  progressive: source,
@@ -120,9 +109,13 @@ export default class NpoPlayer {
120
109
  customData5: this.version
121
110
  }
122
111
  };
123
- await this.playerContext.player.createUIManager(this.playerContext, this.variant);
112
+ void this.playerContext.player.createUIManager(this.playerContext, this.variant);
124
113
  await this.playerContext.player?.load(this.sourceConfig);
125
- getDurationAndStartPlayerTracker();
114
+ this.npoPlayerServices.startPlayerTracker({
115
+ playerContext: this.playerContext,
116
+ source: source,
117
+ duration: undefined
118
+ });
126
119
  }
127
120
  else if (sourceIsJWTToken) {
128
121
  this.jwt = source;
@@ -143,37 +136,37 @@ export default class NpoPlayer {
143
136
  }
144
137
  }
145
138
  };
146
- await this.playerContext.player.createUIManager(this.playerContext, this.variant);
139
+ void this.playerContext.player.createUIManager(this.playerContext, this.variant);
147
140
  try {
148
- _streamObject = await getStreamObject(this, payload);
141
+ _streamObject = await this.npoPlayerServices.fetchStream(this.playerContext, payload);
149
142
  this.streamObject = _streamObject;
150
143
  }
151
- catch (error) {
152
- this.doError('Het is niet gelukt de stream op te halen. \n' + error);
144
+ catch {
153
145
  this.player.pause();
154
146
  return;
155
147
  }
156
148
  if (this.streamObject?.stream == undefined)
157
149
  return;
158
150
  const drmType = this.streamObject.stream.drmType ?? undefined;
151
+ if (drmType !== undefined && drmType !== profile.drm) {
152
+ await this.npoPlayerServices.handleError(this.playerContext, 2007);
153
+ return;
154
+ }
159
155
  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);
160
156
  await this.npoPlayerServices.verifyDRM(this.playerContext, payload);
161
157
  setupMediaSessionActionHandlers(this.player, this.sourceConfig, _streamObject);
162
- logEvent(this, 'load');
163
- let streamDuration = _streamObject.metadata.duration;
164
- const streamPrid = this.streamObject.metadata.prid || _streamObject.metadata.prid;
165
- if (this.isShowingPlayNextScreen) {
166
- this.hidePlayNextScreen();
167
- }
158
+ this.hidePlayNextScreen();
159
+ const streamDuration = _streamObject.metadata.duration;
168
160
  const initAndStartTracker = () => {
169
- if (this.player === null)
161
+ if (!this.playerContext || !this.player)
170
162
  return;
171
163
  this.player?.off(PlayerEvent.AdBreakFinished, initAndStartTracker);
172
164
  this.player?.off(PlayerEvent.AdError, initAndStartTracker);
173
- streamDuration =
174
- streamDuration == undefined
175
- ? getDurationAndStartPlayerTracker()
176
- : startPlayerTracker(this, getStreamDurationInSeconds({ duration: streamDuration, durationIsInMs: true }), this.version, streamPrid);
165
+ this.npoPlayerServices.startPlayerTracker({
166
+ playerContext: this.playerContext,
167
+ source: source,
168
+ duration: streamDuration ? Number(streamDuration) : undefined
169
+ });
177
170
  };
178
171
  if (this.variant === NpoPlayerUIVariants.DEFAULT &&
179
172
  this.streamObject.metadata.hasPreroll === 'true' &&
@@ -187,14 +180,14 @@ export default class NpoPlayer {
187
180
  }
188
181
  }
189
182
  else {
190
- this.doError(`Het is niet gelukt de stream op te halen: \n Input is geen valide token of media object.`, 500);
183
+ await this.npoPlayerServices.handleError(this.playerContext, 500);
191
184
  }
192
185
  this.npoPlayerServices.handleStreamOptions(this.playerContext);
193
186
  this.npoPlayerServices.setupNicamKijkwijzerIcons(this.playerContext);
194
- this.player.on(PlayerEvent.Ready, () => {
195
- this.npoPlayerServices.showNicamAfterUiDelay(this.playerContext);
196
- });
197
- this.npoPlayerServices.handleVerticalVideoControls(this.playerContext, this.variant);
187
+ if (this.variant === NpoPlayerUIVariants.VERTICAL) {
188
+ this.npoPlayerServices.handleVerticalVideoControls(this.playerContext);
189
+ this.npoPlayerServices.handleVerticalVideoSettings(this.playerContext);
190
+ }
198
191
  this.player.on(PlayerEvent.Seek, () => {
199
192
  removeReplayClass(this.player);
200
193
  });
@@ -214,14 +207,6 @@ export default class NpoPlayer {
214
207
  }.bind(this);
215
208
  this.player.on(PlayerEvent.Ready, setLiveOffsetListener);
216
209
  }
217
- doError(input, status) {
218
- if (!this.player)
219
- return;
220
- if (status) {
221
- this.logEmitter.emit('logError', status);
222
- }
223
- playerAction.handlePlayerError(this.player, this.uiComponents, input);
224
- }
225
210
  async play() {
226
211
  await this.playerContext?.player.play();
227
212
  }
@@ -300,6 +285,8 @@ export default class NpoPlayer {
300
285
  this.streamOptions.playNext?.cancelCallback?.();
301
286
  }
302
287
  hidePlayNextScreen() {
288
+ if (!this.isShowingPlayNextScreen)
289
+ return;
303
290
  this.player?.off(PlayerEvent.Paused, () => {
304
291
  this.hidePlayNextScreen();
305
292
  });
@@ -316,12 +303,16 @@ export default class NpoPlayer {
316
303
  if (this.npoTag != undefined) {
317
304
  clearInterval(this.npoTag?.heartbeatInterval);
318
305
  }
319
- logEvent(this, 'stop');
320
306
  if (this.playerContext) {
307
+ this.npoPlayerServices.logEvent({
308
+ playerContext: this.playerContext,
309
+ event: 'stop'
310
+ });
321
311
  this.npoPlayerServices.removeUivisiblityHandlers(this.playerContext);
322
312
  }
323
313
  this.uiManager?.release();
324
314
  this.uiManager = undefined;
315
+ this.container.removeEventListener('keydown', this.keydownHandler, true);
325
316
  await this.player?.destroy();
326
317
  return true;
327
318
  }
@@ -7,12 +7,12 @@ jest.mock('./services/streamoptionsHandlers/streamOptionsHandler', () => ({
7
7
  handleStreamOptions: jest.fn()
8
8
  }));
9
9
  let player;
10
- test('player init with parameters', async () => {
10
+ test('player init with parameters', () => {
11
11
  player = new NpoPlayer(div, testPlayerConfig);
12
12
  expect(player).toBeInstanceOf(NpoPlayer);
13
13
  expect(player.playerConfig?.key).toBe(testPlayerConfig.key);
14
14
  });
15
- test('player init with parameters', async () => {
15
+ test('player init with parameters', () => {
16
16
  player = new NpoPlayer({
17
17
  container: div,
18
18
  playerConfig: testPlayerConfig
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npo/player",
3
- "version": "1.26.0",
3
+ "version": "1.27.0",
4
4
  "description": "NPO Player",
5
5
  "author": "Publieke Omroep <player@npo.nl>",
6
6
  "contributors": [
@@ -41,7 +41,6 @@ describe('setupAccessibilityAttributes', () => {
41
41
  playerConfig: {},
42
42
  initPlayer: () => { },
43
43
  loadStream: () => Promise.resolve(),
44
- doError: () => { },
45
44
  play: async () => { },
46
45
  pause: () => { },
47
46
  setVolume: () => { },
@@ -1,8 +1,10 @@
1
+ import { NpoPlayerUIVariants } from '../../types/interfaces';
1
2
  export function discardAdBreak(playerContext) {
2
3
  const { npoPlayer, player } = playerContext;
3
4
  const { adBreakActive } = npoPlayer;
4
5
  if (!player || adBreakActive === false)
5
6
  return;
7
+ void playerContext.player.createUIManager(playerContext, NpoPlayerUIVariants.DEFAULT);
6
8
  const activeAdBreak = player.getActiveAdBreak();
7
9
  if (activeAdBreak?.id) {
8
10
  player.playerAPI.ads.discardAdBreak(activeAdBreak.id);
@@ -1,4 +1,5 @@
1
1
  import createPlayerContextMock, { createMockNpoPlayer, createMockNpoPlayerAPI } from '../../../tests/mocks/playerContextMock';
2
+ import { NpoPlayerUIVariants } from '../../types/interfaces';
2
3
  import { discardAdBreak } from './discardAdBreak';
3
4
  describe('discardAdBreak', () => {
4
5
  let mockNpoPlayerAPI;
@@ -34,11 +35,13 @@ describe('discardAdBreak', () => {
34
35
  expect(mockNpoPlayerAPI.getActiveAdBreak).not.toHaveBeenCalled();
35
36
  expect(mockNpoPlayerAPI.playerAPI.ads.discardAdBreak).not.toHaveBeenCalled();
36
37
  expect(npoPlayerMock.adBreakActive).toBe(false);
38
+ expect(playerContextMock.player.createUIManager).not.toHaveBeenCalled();
37
39
  });
38
40
  it('should discard active ad break if adBreakActive is true and ad break exists', () => {
39
41
  mockNpoPlayerAPI = createMockNpoPlayerAPI({
40
42
  getActiveAdBreak: jest.fn().mockReturnValue(activeAdBreakMock),
41
- discardAdBreak: jest.fn()
43
+ discardAdBreak: jest.fn(),
44
+ variant: NpoPlayerUIVariants.AD
42
45
  });
43
46
  npoPlayerMock = createMockNpoPlayer({
44
47
  adBreakActive: true
@@ -51,6 +54,7 @@ describe('discardAdBreak', () => {
51
54
  expect(mockNpoPlayerAPI.getActiveAdBreak).toHaveBeenCalled();
52
55
  expect(mockNpoPlayerAPI.playerAPI.ads.discardAdBreak).toHaveBeenCalledWith(activeAdBreakMock.id);
53
56
  expect(npoPlayerMock.adBreakActive).toBe(false);
57
+ expect(playerContextMock.player.createUIManager).toHaveBeenCalledWith(playerContextMock, NpoPlayerUIVariants.DEFAULT);
54
58
  });
55
59
  it('should not discard ad break if active ad break does not exist', () => {
56
60
  mockNpoPlayerAPI = createMockNpoPlayerAPI({
@@ -68,5 +72,6 @@ describe('discardAdBreak', () => {
68
72
  expect(mockNpoPlayerAPI.getActiveAdBreak).toHaveBeenCalled();
69
73
  expect(mockNpoPlayerAPI.playerAPI.ads.discardAdBreak).not.toHaveBeenCalled();
70
74
  expect(npoPlayerMock.adBreakActive).toBe(false);
75
+ expect(playerContextMock.player.createUIManager).toHaveBeenCalledWith(playerContextMock, NpoPlayerUIVariants.DEFAULT);
71
76
  });
72
77
  });
@@ -26,7 +26,7 @@ export async function handlePreRolls(playerContext) {
26
26
  await playerContext.player.scheduleAds(advertConfig);
27
27
  }
28
28
  playerContext.player.on(NpoPlayerEvent.Ready, handleReady);
29
- async function handlePlay() {
29
+ function handlePlay() {
30
30
  playerContext.player.off(NpoPlayerEvent.Play, handlePlay);
31
31
  attemptSetAdUi();
32
32
  }
@@ -37,9 +37,7 @@ export async function handlePreRolls(playerContext) {
37
37
  return false;
38
38
  playerContext.npoPlayer.uiManager?.release();
39
39
  playerContext.npoPlayer.uiManager = undefined;
40
- if (playerContext.npoPlayer.playerContext) {
41
- void playerContext.player.createUIManager(playerContext, NpoPlayerUIVariants.AD);
42
- }
40
+ void playerContext.player.createUIManager(playerContext, NpoPlayerUIVariants.AD);
43
41
  adUiSet = true;
44
42
  const adButton = playerContext.npoPlayer.uiComponents.adbutton;
45
43
  const currentAd = activeAdBreak.ads[0];
@@ -1,4 +1,5 @@
1
1
  import { handlePreRolls } from './handlePreRolls';
2
+ import { PlayerEvent } from 'bitmovin-player/modules/bitmovinplayer-core';
2
3
  import { createMockNpoPlayer, createPlayerContextMock } from '../../../tests/mocks/playerContextMock';
3
4
  jest.mock('bitmovin-player');
4
5
  jest.mock('../../npoplayer');
@@ -11,7 +12,8 @@ describe('handlePreRolls', () => {
11
12
  getActiveAdBreak: jest.fn()
12
13
  },
13
14
  on: jest.fn(),
14
- off: jest.fn()
15
+ off: jest.fn(),
16
+ scheduleAds: jest.fn()
15
17
  };
16
18
  });
17
19
  it('should not schedule ads when hasPreroll is false', async () => {
@@ -49,4 +51,63 @@ describe('handlePreRolls', () => {
49
51
  afterEach(() => {
50
52
  jest.clearAllMocks();
51
53
  });
54
+ it('should schedule ads when hasPreroll is true and preroll URL exists', async () => {
55
+ await new Promise((resolve) => setTimeout(resolve, 0));
56
+ const npoPlayerMock = createMockNpoPlayer({
57
+ streamObject: {
58
+ metadata: {
59
+ hasPreroll: 'true'
60
+ },
61
+ assets: {
62
+ preroll: 'sample_url'
63
+ }
64
+ }
65
+ });
66
+ const playerContextMock = createPlayerContextMock({
67
+ npoPlayer: npoPlayerMock,
68
+ player: mockPlayerAPI
69
+ });
70
+ const readyCallback = jest.fn();
71
+ mockPlayerAPI.on.mockImplementation((event, callback) => {
72
+ if (event === PlayerEvent.Ready) {
73
+ readyCallback.mockImplementation(callback);
74
+ }
75
+ });
76
+ void handlePreRolls(playerContextMock);
77
+ readyCallback();
78
+ expect(playerContextMock.player.scheduleAds).toHaveBeenCalledWith({
79
+ tag: {
80
+ url: 'sample_url',
81
+ type: 'vast'
82
+ },
83
+ id: 'Ad',
84
+ position: 'pre'
85
+ });
86
+ });
87
+ it('should cleanup UI and resolve when AdBreakFinished event occurs', async () => {
88
+ const npoPlayerMock = createMockNpoPlayer({
89
+ streamObject: {
90
+ metadata: {
91
+ hasPreroll: 'true'
92
+ },
93
+ assets: {
94
+ preroll: 'sample_url'
95
+ }
96
+ }
97
+ });
98
+ const playerContextMock = createPlayerContextMock({
99
+ npoPlayer: npoPlayerMock,
100
+ player: mockPlayerAPI
101
+ });
102
+ const adBreakFinishedHandler = jest.fn();
103
+ mockPlayerAPI.on.mockImplementation((event, handler) => {
104
+ if (event === PlayerEvent.AdBreakFinished) {
105
+ adBreakFinishedHandler.mockImplementation(handler);
106
+ }
107
+ });
108
+ const handlePromise = handlePreRolls(playerContextMock);
109
+ adBreakFinishedHandler();
110
+ await handlePromise;
111
+ expect(playerContextMock.npoPlayer.uiManager).toBeUndefined();
112
+ });
52
113
  });
@@ -15,7 +15,7 @@ export async function decideProfile(playerContext, preferredDRM = '') {
15
15
  }
16
16
  const { bestWithDRM, bestDRM } = determineBestWithDRM(supportedTech, { supportedDRM, preferredDRM });
17
17
  const bestWithoutDRM = determineBestWithoutDRM(supportedTech);
18
- return bestWithDRM ? { profileName: bestWithDRM, drm: bestDRM } : { profileName: bestWithoutDRM, drm: bestDRM };
18
+ return bestWithDRM ? { profileName: bestWithDRM, drm: bestDRM } : { profileName: bestWithoutDRM, drm: '' };
19
19
  }
20
20
  function determineBestWithDRM(supportedTech, drmInfo) {
21
21
  let bestWithDRM = '';
@@ -33,6 +33,12 @@ function determineBestWithDRM(supportedTech, drmInfo) {
33
33
  continue;
34
34
  if (techMap[tech.streaming]) {
35
35
  const { drm, stream } = techMap[tech.streaming];
36
+ if ((drmInfo.preferredDRM === DRM_TYPES.PLAYREADY || drmInfo.supportedDRM.includes(DRM_TYPES.PLAYREADY)) &&
37
+ tech.streaming === STREAM_TYPES.DASH) {
38
+ bestWithDRM = STREAM_TYPES.DASH;
39
+ bestDRM = DRM_TYPES.PLAYREADY;
40
+ break;
41
+ }
36
42
  if (drmInfo.preferredDRM !== drm) {
37
43
  bestDRM =
38
44
  tech.streaming === STREAM_TYPES.DASH ? getBestDRMForDash(drmInfo) : getBestDRMForHls(drmInfo);
@@ -31,4 +31,8 @@ describe('decideProfile', () => {
31
31
  const result = await decideProfile(mockPlayerContext, DRM_TYPES.FAIRPLAY);
32
32
  expect(result).toEqual({ profileName: STREAM_TYPES.HLS, drm: DRM_TYPES.FAIRPLAY });
33
33
  });
34
+ it('should return profile with dash streaming and playready DRM when preferred DRM is PlayReady', async () => {
35
+ const result = await decideProfile(mockPlayerContext, DRM_TYPES.PLAYREADY);
36
+ expect(result).toEqual({ profileName: STREAM_TYPES.DASH, drm: DRM_TYPES.PLAYREADY });
37
+ });
34
38
  });
@@ -1,5 +1,4 @@
1
1
  import { convertJwtToBase64, convertBase64ToObject } from '../../js/utilities/utilities.jwt';
2
- import { getStreamObject } from '../../js/api/getstreamobject';
3
2
  export async function verifyDRM(playerContext, payload) {
4
3
  try {
5
4
  const { player, npoPlayer: { streamObject, sourceConfig } } = playerContext;
@@ -29,12 +28,12 @@ function getDRMTokenTimestamp(drmToken) {
29
28
  return convertBase64ToObject(drmBase64).iat;
30
29
  }
31
30
  async function refreshDRMToken(playerContext, payload) {
32
- const backupStreamObject = await getStreamObject(playerContext.npoPlayer, payload);
33
- const newDrmToken = backupStreamObject.stream.drmToken;
31
+ const { npoPlayer: { npoPlayerServices, streamObject, sourceConfig } } = playerContext;
32
+ const { stream: { drmToken: newDrmToken } } = await npoPlayerServices.fetchStream(playerContext, payload);
34
33
  const clonedStreamObject = {
35
- ...playerContext.npoPlayer.streamObject,
36
- stream: { ...playerContext.npoPlayer.streamObject.stream, drmToken: newDrmToken }
34
+ ...streamObject,
35
+ stream: { ...streamObject.stream, drmToken: newDrmToken }
37
36
  };
38
37
  playerContext.npoPlayer.streamObject = clonedStreamObject;
39
- await loadPlayer(playerContext, playerContext.npoPlayer.sourceConfig);
38
+ await loadPlayer(playerContext, sourceConfig);
40
39
  }
@@ -1,23 +1,41 @@
1
1
  import { verifyDRM } from './verifydrm';
2
2
  import { mockNpoPlayer } from '../../../tests/mocks/mockNpoplayer';
3
3
  import { convertBase64ToObject, convertJwtToBase64 } from '../../js/utilities/utilities.jwt';
4
- import { getStreamObject } from '../../js/api/getstreamobject';
5
4
  jest.mock('../../js/utilities/utilities.jwt', () => ({
6
5
  convertBase64ToObject: jest.fn(),
7
6
  convertJwtToBase64: jest.fn()
8
7
  }));
9
- jest.mock('../../js/api/getstreamobject', () => ({
10
- getStreamObject: jest.fn()
8
+ jest.mock('../streamFetchHandler/fetchStream', () => ({
9
+ fetchStream: jest.fn()
11
10
  }));
12
11
  let mockPlayer;
13
12
  const mockNpoplayer = mockNpoPlayer;
14
13
  let mockPlayerContext;
14
+ function createMockStreamObject(overrides = {}) {
15
+ return {
16
+ stream: {
17
+ avType: '',
18
+ drmType: '',
19
+ streamProfile: '',
20
+ sourceProfile: '',
21
+ streamURL: '',
22
+ drmToken: 'newToken'
23
+ },
24
+ metadata: { description: 'default description', title: 'default title' },
25
+ assets: { scrubbingThumbnail: 'thumbnail.jpg', subtitles: [] },
26
+ user: { type: 'anonymous' },
27
+ ...overrides
28
+ };
29
+ }
15
30
  beforeEach(() => {
16
31
  jest.clearAllMocks();
17
32
  mockPlayer = {
18
33
  streamObject: { stream: { drmToken: undefined } },
19
34
  load: jest.fn()
20
35
  };
36
+ mockNpoplayer.npoPlayerServices = {
37
+ fetchStream: jest.fn()
38
+ };
21
39
  mockPlayerContext = { player: mockPlayer, npoPlayer: mockNpoplayer };
22
40
  });
23
41
  afterEach(() => {
@@ -66,10 +84,15 @@ describe('Test DRM verification', () => {
66
84
  jest.spyOn(global.Date, 'now').mockImplementation(() => currentTime * 1000);
67
85
  convertJwtToBase64.mockReturnValue(JSON.stringify({ iat: drmJsonTimestamp }));
68
86
  convertBase64ToObject.mockReturnValue({ iat: drmJsonTimestamp });
69
- getStreamObject.mockResolvedValue({ stream: { drmToken: 'newToken' } });
87
+ jest.spyOn(mockNpoplayer.npoPlayerServices, 'fetchStream').mockResolvedValue(createMockStreamObject({
88
+ stream: {
89
+ ...createMockStreamObject().stream,
90
+ drmToken: 'overriddenToken'
91
+ }
92
+ }));
70
93
  jest.spyOn(mockPlayerContext.player, 'load').mockResolvedValue(undefined);
71
94
  await verifyDRM(mockPlayerContext, payload);
72
- expect(getStreamObject).toHaveBeenCalledWith(mockPlayerContext.npoPlayer, payload);
95
+ expect(mockNpoplayer.npoPlayerServices.fetchStream).toHaveBeenCalledWith(mockPlayerContext, payload);
73
96
  expect(mockPlayerContext.player.load).toHaveBeenCalledWith(sourceConfig);
74
97
  });
75
98
  it('should call loadPlayer when the drmToken is null', async () => {