@npo/player 1.26.0 → 1.27.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 (180) 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 +52 -69
  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/utils.js +4 -1
  20. package/lib/services/drmHandlers/utils.test.js +7 -0
  21. package/lib/services/drmHandlers/verifydrm.js +5 -6
  22. package/lib/services/drmHandlers/verifydrm.test.js +28 -5
  23. package/lib/services/errors/errorBackground.test.js +48 -0
  24. package/lib/services/errors/errorHandler.d.ts +3 -0
  25. package/lib/services/errors/errorHandler.js +46 -0
  26. package/lib/services/errors/errorText.d.ts +2 -0
  27. package/lib/services/errors/errorText.js +59 -0
  28. package/lib/services/errors/errorText.test.js +66 -0
  29. package/lib/services/keyboardHandlers/resolvekeypress.js +21 -17
  30. package/lib/services/localStorageHandlers/localStorageHandlers.js +5 -2
  31. package/lib/services/localStorageHandlers/localStorageHandlers.test.js +0 -1
  32. package/lib/services/nicamHandlers/nicamhandler.js +0 -4
  33. package/lib/services/nicamHandlers/nicamhandler.test.js +0 -5
  34. package/lib/services/npoPlayerAPI/contants.d.ts +20 -0
  35. package/lib/services/npoPlayerAPI/contants.js +6 -0
  36. package/lib/services/npoPlayerAPI/npoPlayerAPI.d.ts +4 -1
  37. package/lib/services/npoPlayerAPI/npoPlayerAPI.js +31 -5
  38. package/lib/services/npoPlayerAPI/npoPlayerAPI.test.js +11 -0
  39. package/lib/services/services.d.ts +8 -2
  40. package/lib/services/services.js +28 -2
  41. package/lib/services/streamFetchHandler/fetchStream.d.ts +2 -0
  42. package/lib/services/streamFetchHandler/fetchStream.js +48 -0
  43. package/lib/services/streamFetchHandler/fetchstream.test.js +70 -0
  44. package/lib/services/trackingHandlers/eventBinding.d.ts +2 -0
  45. package/lib/{js/tracking/handlers/eventbinding.js → services/trackingHandlers/eventBinding.js} +16 -14
  46. package/lib/services/trackingHandlers/eventBinding.test.js +89 -0
  47. package/lib/services/trackingHandlers/eventLogging.d.ts +2 -0
  48. package/lib/{js/tracking/handlers/eventlogging.js → services/trackingHandlers/eventLogging.js} +18 -7
  49. package/lib/services/trackingHandlers/eventLogging.test.js +63 -0
  50. package/lib/services/trackingHandlers/index.d.ts +3 -0
  51. package/lib/services/trackingHandlers/index.js +3 -0
  52. package/lib/services/trackingHandlers/playerTrackerInit.d.ts +2 -0
  53. package/lib/{js/tracking/handlers/playertrackerinit.js → services/trackingHandlers/playerTrackerInit.js} +6 -3
  54. package/lib/services/trackingHandlers/playerTrackerInit.test.js +75 -0
  55. package/lib/services/trackingHandlers/playerTrackerStart.d.ts +2 -0
  56. package/lib/services/trackingHandlers/playerTrackerStart.js +25 -0
  57. package/lib/services/trackingHandlers/playerTrackerStart.test.js +59 -0
  58. package/lib/services/trackingHandlers/streamTrackerInit.d.ts +3 -0
  59. package/lib/services/trackingHandlers/streamTrackerInit.js +27 -0
  60. package/lib/services/trackingHandlers/streamTrackerInit.test.js +84 -0
  61. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.d.ts +2 -2
  62. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.js +4 -6
  63. package/lib/services/verticalVideoHandlers/handleVerticalVideoControls.test.js +4 -17
  64. package/lib/services/verticalVideoHandlers/handleVerticalVideoSettings.d.ts +2 -0
  65. package/lib/services/verticalVideoHandlers/handleVerticalVideoSettings.js +26 -0
  66. package/lib/services/verticalVideoHandlers/handleVerticalVideoSettings.test.js +82 -0
  67. package/lib/src/js/playeractions/playeractions.d.ts +0 -1
  68. package/lib/src/js/utilities/utilities.prid.d.ts +6 -0
  69. package/lib/src/js/utilities/utilities.prid.test.d.ts +1 -0
  70. package/lib/src/npoplayer.d.ts +1 -1
  71. package/lib/src/services/errors/errorBackground.test.d.ts +1 -0
  72. package/lib/src/services/errors/errorHandler.d.ts +3 -0
  73. package/lib/src/services/errors/errorText.d.ts +2 -0
  74. package/lib/src/services/errors/errorText.test.d.ts +1 -0
  75. package/lib/src/services/npoPlayerAPI/contants.d.ts +20 -0
  76. package/lib/src/services/npoPlayerAPI/npoPlayerAPI.d.ts +4 -1
  77. package/lib/src/services/services.d.ts +8 -2
  78. package/lib/src/services/streamFetchHandler/fetchStream.d.ts +2 -0
  79. package/lib/src/services/streamFetchHandler/fetchstream.test.d.ts +1 -0
  80. package/lib/src/services/trackingHandlers/eventBinding.d.ts +2 -0
  81. package/lib/src/services/trackingHandlers/eventBinding.test.d.ts +1 -0
  82. package/lib/src/services/trackingHandlers/eventLogging.d.ts +2 -0
  83. package/lib/src/services/trackingHandlers/eventLogging.test.d.ts +1 -0
  84. package/lib/src/services/trackingHandlers/index.d.ts +3 -0
  85. package/lib/src/services/trackingHandlers/playerTrackerInit.d.ts +2 -0
  86. package/lib/src/services/trackingHandlers/playerTrackerInit.test.d.ts +1 -0
  87. package/lib/src/services/trackingHandlers/playerTrackerStart.d.ts +2 -0
  88. package/lib/src/services/trackingHandlers/playerTrackerStart.test.d.ts +1 -0
  89. package/lib/src/services/trackingHandlers/streamTrackerInit.d.ts +3 -0
  90. package/lib/src/services/trackingHandlers/streamTrackerInit.test.d.ts +1 -0
  91. package/lib/src/services/verticalVideoHandlers/handleVerticalVideoControls.d.ts +2 -2
  92. package/lib/src/services/verticalVideoHandlers/handleVerticalVideoSettings.d.ts +2 -0
  93. package/lib/src/services/verticalVideoHandlers/handleVerticalVideoSettings.test.d.ts +1 -0
  94. package/lib/src/types/interfaces.d.ts +16 -2
  95. package/lib/src/ui/components/seekbar.d.ts +2 -1
  96. package/lib/src/ui/components/verticalvideo/controlbar.d.ts +3 -3
  97. package/lib/src/ui/components/verticalvideo/settingspanel.d.ts +2 -0
  98. package/lib/tests/mocks/mockNpoplayer.js +1 -1
  99. package/lib/tests/mocks/mockStreamObject.d.ts +2 -0
  100. package/lib/tests/mocks/mockStreamObject.js +21 -0
  101. package/lib/tests/mocks/playerContextMock.d.ts +1 -0
  102. package/lib/tests/mocks/playerContextMock.js +5 -1
  103. package/lib/types/interfaces.d.ts +16 -2
  104. package/lib/ui/components/audio/controlbar.js +2 -1
  105. package/lib/ui/components/controlbar.js +1 -1
  106. package/lib/ui/components/seekbar.d.ts +2 -1
  107. package/lib/ui/components/seekbar.js +2 -2
  108. package/lib/ui/components/settingspanel.js +8 -12
  109. package/lib/ui/components/verticalvideo/controlbar.d.ts +3 -3
  110. package/lib/ui/components/verticalvideo/controlbar.js +14 -11
  111. package/lib/ui/components/verticalvideo/settingspanel.d.ts +2 -0
  112. package/lib/ui/components/verticalvideo/settingspanel.js +21 -0
  113. package/lib/ui/handlers/domhandlers.test.js +6 -26
  114. package/lib/ui/nativemobileuifactory.js +1 -1
  115. package/lib/ui/uicontainer.js +5 -4
  116. package/lib/ui/uicontainer.test.js +4 -1
  117. package/package.json +1 -1
  118. package/src/style/components/_advert.scss +3 -3
  119. package/src/style/components/_buffering.scss +14 -8
  120. package/src/style/components/_error.scss +44 -1
  121. package/src/style/components/_hugeplaybacktogglebutton.scss +1 -0
  122. package/src/style/components/_metadata.scss +13 -12
  123. package/src/style/components/_nicam.scss +11 -6
  124. package/src/style/components/_playnext.scss +1 -1
  125. package/src/style/components/_replay.scss +1 -6
  126. package/src/style/components/_settingspanel.scss +74 -79
  127. package/src/style/components/audio/_bottombar.scss +7 -9
  128. package/src/style/components/audio/_errors.scss +1 -1
  129. package/src/style/components/audio/_metadata.scss +14 -8
  130. package/src/style/components/audio/_playbutton.scss +1 -0
  131. package/src/style/components/audio/_topbar.scss +12 -2
  132. package/src/style/components/audio/_volumeslider.scss +3 -3
  133. package/src/style/components/vertical-video/_bottombar.scss +42 -1
  134. package/src/style/components/vertical-video/_hugeplaybacktogglebutton.scss +24 -4
  135. package/src/style/components/vertical-video/_settingspanel.scss +20 -3
  136. package/src/style/components/vertical-video/_topbar.scss +47 -6
  137. package/src/style/npoplayer.css +91 -57
  138. package/src/style/npoplayer.scss +4 -7
  139. package/src/style/variants/_player-base.scss +10 -2
  140. package/src/style/variants/_player-large.scss +4 -0
  141. package/src/style/variants/_player-medium.scss +4 -1
  142. package/src/style/variants/_player-small.scss +15 -1
  143. package/src/style/vars/_fonts.scss +13 -7
  144. package/src/style/vars/_z-index.scss +1 -0
  145. package/lib/js/api/getstreamobject.d.ts +0 -3
  146. package/lib/js/api/getstreamobject.js +0 -38
  147. package/lib/js/api/getstreamobject.test.js +0 -48
  148. package/lib/js/playeractions/customerrors.test.js +0 -51
  149. package/lib/js/playeractions/handlers/customerrors.d.ts +0 -50
  150. package/lib/js/playeractions/handlers/customerrors.js +0 -56
  151. package/lib/js/playeractions/handlers/error.d.ts +0 -3
  152. package/lib/js/playeractions/handlers/error.js +0 -14
  153. package/lib/js/playeractions/handlers/error.test.js +0 -44
  154. package/lib/js/tracking/handlers/eventbinding.d.ts +0 -3
  155. package/lib/js/tracking/handlers/eventlogging.d.ts +0 -2
  156. package/lib/js/tracking/handlers/eventlogging.test.js +0 -46
  157. package/lib/js/tracking/handlers/playertrackerinit.d.ts +0 -4
  158. package/lib/js/tracking/handlers/playertrackerinit.test.js +0 -74
  159. package/lib/js/tracking/handlers/playertrackerstart.d.ts +0 -1
  160. package/lib/js/tracking/handlers/playertrackerstart.js +0 -25
  161. package/lib/js/tracking/playertracker.d.ts +0 -4
  162. package/lib/js/tracking/playertracker.js +0 -4
  163. package/lib/src/js/api/getstreamobject.d.ts +0 -3
  164. package/lib/src/js/playeractions/handlers/customerrors.d.ts +0 -50
  165. package/lib/src/js/playeractions/handlers/error.d.ts +0 -3
  166. package/lib/src/js/tracking/handlers/eventbinding.d.ts +0 -3
  167. package/lib/src/js/tracking/handlers/eventlogging.d.ts +0 -2
  168. package/lib/src/js/tracking/handlers/playertrackerinit.d.ts +0 -4
  169. package/lib/src/js/tracking/handlers/playertrackerstart.d.ts +0 -1
  170. package/lib/src/js/tracking/playertracker.d.ts +0 -4
  171. /package/lib/js/{api/getstreamobject.test.d.ts → utilities/utilities.prid.test.d.ts} +0 -0
  172. /package/lib/{js/playeractions/customerrors.test.d.ts → services/errors/errorBackground.test.d.ts} +0 -0
  173. /package/lib/{js/playeractions/handlers/error.test.d.ts → services/errors/errorText.test.d.ts} +0 -0
  174. /package/lib/{js/tracking/handlers/eventlogging.test.d.ts → services/streamFetchHandler/fetchstream.test.d.ts} +0 -0
  175. /package/lib/{js/tracking/handlers/playertrackerinit.test.d.ts → services/trackingHandlers/eventBinding.test.d.ts} +0 -0
  176. /package/lib/{src/js/api/getstreamobject.test.d.ts → services/trackingHandlers/eventLogging.test.d.ts} +0 -0
  177. /package/lib/{src/js/playeractions/customerrors.test.d.ts → services/trackingHandlers/playerTrackerInit.test.d.ts} +0 -0
  178. /package/lib/{src/js/playeractions/handlers/error.test.d.ts → services/trackingHandlers/playerTrackerStart.test.d.ts} +0 -0
  179. /package/lib/{src/js/tracking/handlers/eventlogging.test.d.ts → services/trackingHandlers/streamTrackerInit.test.d.ts} +0 -0
  180. /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,33 @@ 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
+ if (this.npoTag) {
99
+ this.npoTag.pageTracker = this.npoTagPageTracker;
100
+ }
101
+ }
112
102
  if (sourceIsUrl && sourceIsMedia) {
103
+ _streamObject = emptyStreamObject;
104
+ this.streamObject = _streamObject;
113
105
  this.sourceConfig = {
114
106
  ...options.sourceConfig,
115
107
  progressive: source,
@@ -120,9 +112,13 @@ export default class NpoPlayer {
120
112
  customData5: this.version
121
113
  }
122
114
  };
123
- await this.playerContext.player.createUIManager(this.playerContext, this.variant);
115
+ void this.playerContext.player.createUIManager(this.playerContext, this.variant);
124
116
  await this.playerContext.player?.load(this.sourceConfig);
125
- getDurationAndStartPlayerTracker();
117
+ this.npoPlayerServices.startPlayerTracker({
118
+ playerContext: this.playerContext,
119
+ source: source,
120
+ duration: undefined
121
+ });
126
122
  }
127
123
  else if (sourceIsJWTToken) {
128
124
  this.jwt = source;
@@ -143,58 +139,47 @@ export default class NpoPlayer {
143
139
  }
144
140
  }
145
141
  };
146
- await this.playerContext.player.createUIManager(this.playerContext, this.variant);
142
+ void this.playerContext.player.createUIManager(this.playerContext, this.variant);
147
143
  try {
148
- _streamObject = await getStreamObject(this, payload);
144
+ _streamObject = await this.npoPlayerServices.fetchStream(this.playerContext, payload);
149
145
  this.streamObject = _streamObject;
150
146
  }
151
- catch (error) {
152
- this.doError('Het is niet gelukt de stream op te halen. \n' + error);
147
+ catch {
153
148
  this.player.pause();
154
149
  return;
155
150
  }
156
151
  if (this.streamObject?.stream == undefined)
157
152
  return;
158
153
  const drmType = this.streamObject.stream.drmType ?? undefined;
154
+ if (drmType !== undefined && drmType !== profile.drm) {
155
+ await this.npoPlayerServices.handleError(this.playerContext, 2007);
156
+ return;
157
+ }
159
158
  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
159
  await this.npoPlayerServices.verifyDRM(this.playerContext, payload);
161
160
  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
- }
168
- const initAndStartTracker = () => {
169
- if (this.player === null)
170
- return;
171
- this.player?.off(PlayerEvent.AdBreakFinished, initAndStartTracker);
172
- this.player?.off(PlayerEvent.AdError, initAndStartTracker);
173
- streamDuration =
174
- streamDuration == undefined
175
- ? getDurationAndStartPlayerTracker()
176
- : startPlayerTracker(this, getStreamDurationInSeconds({ duration: streamDuration, durationIsInMs: true }), this.version, streamPrid);
177
- };
161
+ this.hidePlayNextScreen();
162
+ const streamDuration = _streamObject.metadata.duration;
163
+ this.npoPlayerServices.startPlayerTracker({
164
+ playerContext: this.playerContext,
165
+ source: source,
166
+ duration: streamDuration ? Number(streamDuration) : undefined
167
+ });
178
168
  if (this.variant === NpoPlayerUIVariants.DEFAULT &&
179
169
  this.streamObject.metadata.hasPreroll === 'true' &&
180
170
  this.streamObject.assets.preroll) {
181
- this.player.on(PlayerEvent.AdBreakFinished, initAndStartTracker);
182
- this.player.on(PlayerEvent.AdError, initAndStartTracker);
183
171
  await this.npoPlayerServices.schedulePreRolls(this.playerContext);
184
172
  }
185
- else {
186
- initAndStartTracker();
187
- }
188
173
  }
189
174
  else {
190
- this.doError(`Het is niet gelukt de stream op te halen: \n Input is geen valide token of media object.`, 500);
175
+ await this.npoPlayerServices.handleError(this.playerContext, 500);
191
176
  }
192
177
  this.npoPlayerServices.handleStreamOptions(this.playerContext);
193
178
  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);
179
+ if (this.variant === NpoPlayerUIVariants.VERTICAL) {
180
+ this.npoPlayerServices.handleVerticalVideoControls(this.playerContext);
181
+ this.npoPlayerServices.handleVerticalVideoSettings(this.playerContext);
182
+ }
198
183
  this.player.on(PlayerEvent.Seek, () => {
199
184
  removeReplayClass(this.player);
200
185
  });
@@ -214,14 +199,6 @@ export default class NpoPlayer {
214
199
  }.bind(this);
215
200
  this.player.on(PlayerEvent.Ready, setLiveOffsetListener);
216
201
  }
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
202
  async play() {
226
203
  await this.playerContext?.player.play();
227
204
  }
@@ -300,6 +277,8 @@ export default class NpoPlayer {
300
277
  this.streamOptions.playNext?.cancelCallback?.();
301
278
  }
302
279
  hidePlayNextScreen() {
280
+ if (!this.isShowingPlayNextScreen)
281
+ return;
303
282
  this.player?.off(PlayerEvent.Paused, () => {
304
283
  this.hidePlayNextScreen();
305
284
  });
@@ -316,12 +295,16 @@ export default class NpoPlayer {
316
295
  if (this.npoTag != undefined) {
317
296
  clearInterval(this.npoTag?.heartbeatInterval);
318
297
  }
319
- logEvent(this, 'stop');
320
298
  if (this.playerContext) {
299
+ this.npoPlayerServices.logEvent({
300
+ playerContext: this.playerContext,
301
+ event: 'stop'
302
+ });
321
303
  this.npoPlayerServices.removeUivisiblityHandlers(this.playerContext);
322
304
  }
323
305
  this.uiManager?.release();
324
306
  this.uiManager = undefined;
307
+ this.container.removeEventListener('keydown', this.keydownHandler, true);
325
308
  await this.player?.destroy();
326
309
  return true;
327
310
  }
@@ -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.1",
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: undefined };
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
  });
@@ -8,7 +8,10 @@ export function isSupportedOpenDRM(drm) {
8
8
  export function getBestDRMForDash(drmInfo) {
9
9
  for (const drm of drmInfo.supportedDRM) {
10
10
  if (isSupportedOpenDRM(drm) && !drmInfo.preferredDRM) {
11
- return 'widevine';
11
+ if (drm === 'com.microsoft.playready') {
12
+ return DRM_TYPES.PLAYREADY;
13
+ }
14
+ return DRM_TYPES.WIDEVINE;
12
15
  }
13
16
  if (isSupportedAppleDRM(drm) && drmInfo.preferredDRM === DRM_TYPES.FAIRPLAY) {
14
17
  return DRM_TYPES.FAIRPLAY;
@@ -33,6 +33,13 @@ describe('DRM Functions', () => {
33
33
  };
34
34
  expect(getBestDRMForDash(drmInfo)).toBe('widevine');
35
35
  });
36
+ it('should return playready for supported open DRM and no preferred DRM', () => {
37
+ const drmInfo = {
38
+ supportedDRM: ['com.microsoft.playready'],
39
+ preferredDRM: ''
40
+ };
41
+ expect(getBestDRMForDash(drmInfo)).toBe('playready');
42
+ });
36
43
  it('should return fairplay for supported Apple DRM and preferred DRM is fairplay', () => {
37
44
  const drmInfo = {
38
45
  supportedDRM: [appleFps1],
@@ -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
  }