@npo/player 1.24.7 → 1.26.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 (170) hide show
  1. package/README.md +1 -1
  2. package/lib/js/api/getstreamobject.d.ts +1 -1
  3. package/lib/js/api/getstreamobject.js +4 -4
  4. package/lib/js/api/getstreamobject.test.js +13 -13
  5. package/lib/js/playeractions/handlers/handleoffsets.js +9 -7
  6. package/lib/js/playeractions/handlers/handleoffsets.test.js +1 -1
  7. package/lib/js/playeractions/handlers/processplayerconfig.d.ts +1 -1
  8. package/lib/js/playeractions/handlers/processplayerconfig.js +18 -4
  9. package/lib/js/playeractions/handlers/processplayerconfig.test.js +116 -0
  10. package/lib/js/playeractions/handlers/processsourceconfig.d.ts +3 -1
  11. package/lib/js/playeractions/handlers/processsourceconfig.js +20 -10
  12. package/lib/js/playeractions/handlers/processsourceconfig.test.js +25 -0
  13. package/lib/js/settings/localization.d.ts +76 -1
  14. package/lib/js/settings/localization.js +2 -2
  15. package/lib/js/tracking/handlers/eventbinding.js +25 -17
  16. package/lib/js/tracking/handlers/eventlogging.js +3 -8
  17. package/lib/js/tracking/handlers/playertrackerinit.d.ts +2 -2
  18. package/lib/js/tracking/handlers/playertrackerinit.js +8 -6
  19. package/lib/js/tracking/handlers/playertrackerinit.test.d.ts +1 -0
  20. package/lib/js/tracking/handlers/playertrackerinit.test.js +74 -0
  21. package/lib/js/tracking/handlers/playertrackerstart.js +1 -1
  22. package/lib/js/utilities/utilities.element.d.ts +6 -0
  23. package/lib/js/utilities/utilities.element.js +10 -0
  24. package/lib/js/utilities/utilities.element.test.js +18 -5
  25. package/lib/lang/nl.json +1 -1
  26. package/lib/lang/subtitleLabels/nl.json +14 -0
  27. package/lib/npoplayer.d.ts +15 -6
  28. package/lib/npoplayer.js +57 -50
  29. package/lib/npoplayer.test.js +12 -4
  30. package/lib/package.json +3 -3
  31. package/lib/services/a11y/setup.js +2 -5
  32. package/lib/services/a11y/setup.test.js +2 -19
  33. package/lib/services/advertHandlers/discardAdBreak.js +4 -4
  34. package/lib/services/advertHandlers/discardAdBreak.test.js +25 -13
  35. package/lib/services/advertHandlers/handlePreRolls.js +19 -19
  36. package/lib/services/advertHandlers/handlePrerolls.test.js +4 -4
  37. package/lib/services/cdnProviders/cdnProviders.js +4 -2
  38. package/lib/services/drmHandlers/decideprofile.js +1 -1
  39. package/lib/services/drmHandlers/decideprofile.test.js +1 -1
  40. package/lib/services/drmHandlers/verifydrm.js +6 -6
  41. package/lib/services/drmHandlers/verifydrm.test.js +5 -5
  42. package/lib/services/eventListenerHandlers/removeEventListeners.js +2 -2
  43. package/lib/services/eventListenerHandlers/removeEventListeners.test.js +9 -9
  44. package/lib/services/keyboardHandlers/resolvekeypress.js +5 -5
  45. package/lib/services/keyboardHandlers/resolvekeypress.test.js +1 -1
  46. package/lib/services/liveStreamHandlers/handleLiveStreamControls.js +2 -2
  47. package/lib/services/liveStreamHandlers/handleLiveStreamControls.test.js +2 -2
  48. package/lib/services/localStorageHandlers/localStorageHandlers.js +2 -2
  49. package/lib/services/localStorageHandlers/localStorageHandlers.test.js +2 -5
  50. package/lib/services/nicamHandlers/nicamhandler.d.ts +5 -0
  51. package/lib/{ui/handlers → services/nicamHandlers}/nicamhandler.js +24 -15
  52. package/lib/services/nicamHandlers/nicamhandler.test.js +132 -0
  53. package/lib/services/npoPlayerAPI/npoPlayerAPI.d.ts +3 -2
  54. package/lib/services/npoPlayerAPI/npoPlayerAPI.js +19 -23
  55. package/lib/services/preferences/handlePreferences.d.ts +2 -0
  56. package/lib/services/preferences/handlePreferences.js +42 -0
  57. package/lib/services/preferences/handlePreferences.test.d.ts +1 -0
  58. package/lib/services/preferences/handlePreferences.test.js +102 -0
  59. package/lib/services/segmentHandlers/addSegmentEventListeners.js +2 -2
  60. package/lib/services/segmentHandlers/addSegmentEventListeners.test.js +1 -1
  61. package/lib/services/segmentHandlers/setSegmentMarkers.js +1 -1
  62. package/lib/services/services.d.ts +7 -0
  63. package/lib/services/services.js +26 -0
  64. package/lib/services/streamoptionsHandlers/streamOptionsHandler.d.ts +6 -0
  65. package/lib/services/streamoptionsHandlers/streamOptionsHandler.js +78 -0
  66. package/lib/services/streamoptionsHandlers/streamOptionsHandler.test.js +187 -0
  67. package/lib/services/uiHandlers/uiVisiblityHandler.d.ts +3 -0
  68. package/lib/services/uiHandlers/uiVisiblityHandler.js +26 -0
  69. package/lib/services/uiHandlers/uiVisiblityHandler.test.d.ts +1 -0
  70. package/lib/services/uiHandlers/uiVisiblityHandler.test.js +62 -0
  71. package/lib/src/js/api/getstreamobject.d.ts +1 -1
  72. package/lib/src/js/playeractions/handlers/processplayerconfig.d.ts +1 -1
  73. package/lib/src/js/playeractions/handlers/processplayerconfig.test.d.ts +1 -0
  74. package/lib/src/js/playeractions/handlers/processsourceconfig.d.ts +3 -1
  75. package/lib/src/js/playeractions/handlers/processsourceconfig.test.d.ts +1 -0
  76. package/lib/src/js/settings/localization.d.ts +76 -1
  77. package/lib/src/js/tracking/handlers/playertrackerinit.d.ts +2 -2
  78. package/lib/src/js/tracking/handlers/playertrackerinit.test.d.ts +1 -0
  79. package/lib/src/js/utilities/utilities.element.d.ts +6 -0
  80. package/lib/src/npoplayer.d.ts +15 -6
  81. package/lib/src/services/nicamHandlers/nicamhandler.d.ts +5 -0
  82. package/lib/src/services/nicamHandlers/nicamhandler.test.d.ts +1 -0
  83. package/lib/src/services/npoPlayerAPI/npoPlayerAPI.d.ts +3 -2
  84. package/lib/src/services/preferences/handlePreferences.d.ts +2 -0
  85. package/lib/src/services/preferences/handlePreferences.test.d.ts +1 -0
  86. package/lib/src/services/services.d.ts +7 -0
  87. package/lib/src/services/streamoptionsHandlers/streamOptionsHandler.d.ts +6 -0
  88. package/lib/src/services/streamoptionsHandlers/streamOptionsHandler.test.d.ts +1 -0
  89. package/lib/src/services/uiHandlers/uiVisiblityHandler.d.ts +3 -0
  90. package/lib/src/services/uiHandlers/uiVisiblityHandler.test.d.ts +1 -0
  91. package/lib/src/types/classes.d.ts +6 -0
  92. package/lib/src/types/interfaces.d.ts +25 -3
  93. package/lib/src/ui/components/adbutton.d.ts +1 -1
  94. package/lib/src/ui/components/adlabel.d.ts +1 -1
  95. package/lib/src/ui/components/buttons.d.ts +10 -17
  96. package/lib/src/ui/components/controlbar.d.ts +2 -2
  97. package/lib/src/ui/components/ctabar.d.ts +1 -1
  98. package/lib/src/ui/components/nativemobile/buttons.d.ts +8 -0
  99. package/lib/src/ui/components/playnext.d.ts +1 -3
  100. package/lib/src/ui/components/seekbar.d.ts +1 -1
  101. package/lib/src/ui/components/titlebar.d.ts +1 -1
  102. package/lib/src/ui/components/topbar.d.ts +2 -2
  103. package/lib/src/ui/components/verticalvideo/controlbar.d.ts +2 -2
  104. package/lib/src/ui/handlers/playnextscreen.test.d.ts +1 -0
  105. package/lib/src/ui/handlers/timecontrolhandlers.d.ts +3 -3
  106. package/lib/src/ui/uicontainer.d.ts +2 -3
  107. package/lib/src/ui/uicontainer.test.d.ts +1 -0
  108. package/lib/tests/mocks/mockLogEmitter.d.ts +2 -0
  109. package/lib/tests/mocks/mockLogEmitter.js +20 -0
  110. package/lib/tests/mocks/mockNpoplayer.js +4 -2
  111. package/lib/tests/mocks/playerContextMock.d.ts +5 -3
  112. package/lib/tests/mocks/playerContextMock.js +8 -5
  113. package/lib/types/classes.d.ts +6 -0
  114. package/lib/types/classes.js +12 -0
  115. package/lib/types/interfaces.d.ts +25 -3
  116. package/lib/types/interfaces.js +1 -0
  117. package/lib/ui/components/adbutton.d.ts +1 -1
  118. package/lib/ui/components/adbutton.js +3 -3
  119. package/lib/ui/components/adlabel.d.ts +1 -1
  120. package/lib/ui/components/adlabel.js +3 -3
  121. package/lib/ui/components/buttons.d.ts +10 -17
  122. package/lib/ui/components/buttons.js +44 -27
  123. package/lib/ui/components/controlbar.d.ts +2 -2
  124. package/lib/ui/components/controlbar.js +20 -32
  125. package/lib/ui/components/ctabar.d.ts +1 -1
  126. package/lib/ui/components/ctabar.js +4 -4
  127. package/lib/ui/components/nativemobile/buttons.d.ts +8 -0
  128. package/lib/ui/components/nativemobile/buttons.js +41 -1
  129. package/lib/ui/components/nativemobile/controlbar.js +1 -2
  130. package/lib/ui/components/nativemobile/topbar.js +2 -2
  131. package/lib/ui/components/playnext.d.ts +1 -3
  132. package/lib/ui/components/playnext.js +3 -3
  133. package/lib/ui/components/seekbar.d.ts +1 -1
  134. package/lib/ui/components/seekbar.js +5 -3
  135. package/lib/ui/components/settingspanel.js +9 -9
  136. package/lib/ui/components/titlebar.d.ts +1 -1
  137. package/lib/ui/components/titlebar.js +2 -2
  138. package/lib/ui/components/topbar.d.ts +2 -2
  139. package/lib/ui/components/topbar.js +8 -17
  140. package/lib/ui/components/verticalvideo/controlbar.d.ts +2 -2
  141. package/lib/ui/components/verticalvideo/controlbar.js +4 -4
  142. package/lib/ui/handlers/playnextscreen.test.d.ts +1 -0
  143. package/lib/ui/handlers/timecontrolhandlers.d.ts +3 -3
  144. package/lib/ui/nativemobileuifactory.js +20 -24
  145. package/lib/ui/nativemobileuifactory.test.js +2 -6
  146. package/lib/ui/uicontainer.d.ts +2 -3
  147. package/lib/ui/uicontainer.js +13 -30
  148. package/lib/ui/uicontainer.test.d.ts +1 -0
  149. package/lib/ui/uicontainer.test.js +80 -0
  150. package/package.json +3 -3
  151. package/src/style/components/_advert.scss +0 -9
  152. package/src/style/components/_icons.scss +5 -0
  153. package/src/style/components/_nicam.scss +5 -4
  154. package/src/style/components/_settingspanel.scss +48 -17
  155. package/src/style/components/vertical-video/_settingspanel.scss +1 -1
  156. package/src/style/npoplayer.css +26 -20
  157. package/src/style/variants/_player-base.scss +4 -0
  158. package/src/style/variants/_player-large.scss +5 -1
  159. package/src/style/variants/_player-small.scss +11 -8
  160. package/lib/src/ui/handlers/nicamhandler.d.ts +0 -6
  161. package/lib/src/ui/handlers/streamhandler.d.ts +0 -2
  162. package/lib/ui/handlers/nicamhandler.d.ts +0 -6
  163. package/lib/ui/handlers/nicamhandler.test.js +0 -36
  164. package/lib/ui/handlers/streamhandler.d.ts +0 -2
  165. package/lib/ui/handlers/streamhandler.js +0 -60
  166. /package/lib/{src/ui/handlers/playnextstreen.test.d.ts → js/playeractions/handlers/processplayerconfig.test.d.ts} +0 -0
  167. /package/lib/{ui/handlers/playnextstreen.test.d.ts → js/playeractions/handlers/processsourceconfig.test.d.ts} +0 -0
  168. /package/lib/{src/ui/handlers → services/nicamHandlers}/nicamhandler.test.d.ts +0 -0
  169. /package/lib/{ui/handlers/nicamhandler.test.d.ts → services/streamoptionsHandlers/streamOptionsHandler.test.d.ts} +0 -0
  170. /package/lib/ui/handlers/{playnextstreen.test.js → playnextscreen.test.js} +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.24.0
14
+ Current version: v1.26.0
15
15
 
16
16
  The changelog is available at https://docs.npoplayer.nl/implementation/web/changelog/
17
17
 
@@ -1,3 +1,3 @@
1
1
  import { type ApiPayload } from '../../types/interfaces';
2
2
  import type NpoPlayer from '../../npoplayer';
3
- export declare function getStreamObject(npoplayer: NpoPlayer, payload: ApiPayload): Promise<any>;
3
+ export declare function getStreamObject(npoPlayer: NpoPlayer, payload: ApiPayload): Promise<any>;
@@ -1,5 +1,5 @@
1
1
  import { removeTrailingSlash } from '../utilities/utilities.url';
2
- export async function getStreamObject(npoplayer, payload) {
2
+ export async function getStreamObject(npoPlayer, payload) {
3
3
  try {
4
4
  const controller = new AbortController();
5
5
  const { signal } = controller;
@@ -22,16 +22,16 @@ export async function getStreamObject(npoplayer, payload) {
22
22
  return streamApiResponse;
23
23
  }
24
24
  else {
25
- npoplayer.doError(`Het is niet gelukt de stream op te halen: \n ${streamApiResponse.body}`, response.status);
25
+ npoPlayer.doError('Het is niet gelukt de stream op te halen.', response.status);
26
26
  }
27
27
  }
28
28
  catch (error) {
29
29
  if (error instanceof Error) {
30
30
  if (error.message === 'AbortError') {
31
- npoplayer.doError('Het is niet gelukt de stream op te halen: \n Request timed out', 408);
31
+ npoPlayer.doError('Het ophalen van de stream duurde te lang.', 408);
32
32
  }
33
33
  else {
34
- npoplayer.doError(`Het is niet gelukt de stream op te halen: \n ${error.message}`, 500);
34
+ npoPlayer.doError(`Het is niet gelukt de stream op te halen: \n ${error.message}`, 500);
35
35
  }
36
36
  }
37
37
  }
@@ -2,18 +2,18 @@ import { getStreamObject } from './getstreamobject';
2
2
  import NpoPlayer from '../../npoplayer';
3
3
  import fetchMock from 'jest-fetch-mock';
4
4
  fetchMock.enableMocks();
5
- jest.mock('../../ui/handlers/streamhandler', () => ({
6
- processStream: jest.fn()
5
+ jest.mock('../../services/streamoptionsHandlers/streamOptionsHandler', () => ({
6
+ handleStreamOptions: jest.fn()
7
7
  }));
8
8
  describe('getStreamObject', () => {
9
- let npoplayer;
9
+ let npoPlayer;
10
10
  let apiPayload;
11
11
  const testPlayerConfig = {
12
12
  key: 'dummy key'
13
13
  };
14
14
  const div = document.createElement('div');
15
15
  beforeEach(() => {
16
- npoplayer = new NpoPlayer(div, testPlayerConfig);
16
+ npoPlayer = new NpoPlayer(div, testPlayerConfig);
17
17
  apiPayload = {
18
18
  baseURL: 'http://example.com',
19
19
  jwt: 'someToken',
@@ -24,25 +24,25 @@ describe('getStreamObject', () => {
24
24
  });
25
25
  it('should handle successful fetch', async () => {
26
26
  fetchMock.mockResponseOnce(JSON.stringify({ foo: 'bar' }));
27
- const result = await getStreamObject(npoplayer, apiPayload);
27
+ const result = await getStreamObject(npoPlayer, apiPayload);
28
28
  expect(result).toEqual({ foo: 'bar' });
29
29
  });
30
30
  it('should handle fetch with bad response', async () => {
31
31
  fetchMock.mockResponseOnce(JSON.stringify({ body: 'Bad Request' }), { status: 400 });
32
- const doErrorSpy = jest.spyOn(npoplayer, 'doError');
33
- await getStreamObject(npoplayer, apiPayload);
34
- expect(doErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Het is niet gelukt de stream op te halen'), 400);
32
+ const doErrorSpy = jest.spyOn(npoPlayer, 'doError');
33
+ await getStreamObject(npoPlayer, apiPayload);
34
+ expect(doErrorSpy).toHaveBeenCalledWith(expect.stringMatching('Het is niet gelukt de stream op te halen.'), 400);
35
35
  });
36
36
  it('should handle fetch timeout', async () => {
37
37
  fetchMock.mockRejectOnce(new Error('AbortError'));
38
- const doErrorSpy = jest.spyOn(npoplayer, 'doError');
39
- await getStreamObject(npoplayer, apiPayload);
40
- expect(doErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Request timed out'), 408);
38
+ const doErrorSpy = jest.spyOn(npoPlayer, 'doError');
39
+ await getStreamObject(npoPlayer, apiPayload);
40
+ expect(doErrorSpy).toHaveBeenCalledWith(expect.stringMatching('Het ophalen van de stream duurde te lang.'), 408);
41
41
  });
42
42
  it('should handle fetch with other errors', async () => {
43
43
  fetchMock.mockRejectOnce(new Error('SomeError'));
44
- const doErrorSpy = jest.spyOn(npoplayer, 'doError');
45
- await getStreamObject(npoplayer, apiPayload);
44
+ const doErrorSpy = jest.spyOn(npoPlayer, 'doError');
45
+ await getStreamObject(npoPlayer, apiPayload);
46
46
  expect(doErrorSpy).toHaveBeenCalledWith(expect.stringContaining('SomeError'), 500);
47
47
  });
48
48
  });
@@ -8,11 +8,13 @@ export function handleStartOffset(playerContext, offset) {
8
8
  return;
9
9
  }
10
10
  const seek = () => {
11
+ player.off(NpoPlayerEvent.Ready, seek);
11
12
  player.off(NpoPlayerEvent.SourceLoaded, seek);
12
13
  player.off(NpoPlayerEvent.AdBreakFinished, seek);
13
14
  player.off(NpoPlayerEvent.AdError, seek);
14
15
  player.seek(offset);
15
16
  };
17
+ player.on(NpoPlayerEvent.Ready, seek);
16
18
  player.on(NpoPlayerEvent.SourceLoaded, seek);
17
19
  player.on(NpoPlayerEvent.AdBreakFinished, seek);
18
20
  player.on(NpoPlayerEvent.AdError, seek);
@@ -28,22 +30,22 @@ export function shiftToProgramStart(playerContext, timestamp) {
28
30
  player.timeShift(timestamp);
29
31
  }
30
32
  export function handleLiveOffsetLogic(playerContext, options) {
31
- const { player, npoplayer } = playerContext;
32
- if (npoplayer.uiComponents.watchFromStartButton == undefined)
33
+ const { player, npoPlayer } = playerContext;
34
+ if (npoPlayer.uiComponents.watchFromStartButton == undefined)
33
35
  return;
34
36
  if (options?.liveProgramTime == undefined) {
35
- npoplayer.uiComponents.watchFromStartButton.hide();
37
+ npoPlayer.uiComponents.watchFromStartButton.hide();
36
38
  return;
37
39
  }
38
- npoplayer.streamOptions.liveProgramTime = options.liveProgramTime;
40
+ npoPlayer.streamOptions.liveProgramTime = options.liveProgramTime;
39
41
  if (player.isLive() && options.liveProgramTime > 0) {
40
- npoplayer.uiComponents.watchFromStartButton.show();
42
+ npoPlayer.uiComponents.watchFromStartButton.show();
41
43
  }
42
44
  else {
43
- npoplayer.uiComponents.watchFromStartButton.hide();
45
+ npoPlayer.uiComponents.watchFromStartButton.hide();
44
46
  }
45
47
  if (options.liveOffset !== null && options.liveOffset !== false && options.liveProgramTime > 0) {
46
- npoplayer.uiComponents.watchFromStartButton.hide();
48
+ npoPlayer.uiComponents.watchFromStartButton.hide();
47
49
  shiftToProgramStart(playerContext, options.liveProgramTime);
48
50
  }
49
51
  }
@@ -39,7 +39,7 @@ describe('Test offset handling', () => {
39
39
  seek: seekMock,
40
40
  off: jest.fn(),
41
41
  on: jest.fn((event, callback) => {
42
- if (event === PlayerEvent.SourceLoaded) {
42
+ if (event === PlayerEvent.Ready) {
43
43
  callback();
44
44
  }
45
45
  })
@@ -1,3 +1,3 @@
1
1
  import { type PlayerConfig } from 'bitmovin-player';
2
2
  import NpoPlayer from 'npoplayer';
3
- export declare function processPlayerConfig(npoplayer: NpoPlayer, playerConfig: PlayerConfig): PlayerConfig;
3
+ export declare function processPlayerConfig(npoPlayer: NpoPlayer, playerConfig: PlayerConfig): PlayerConfig;
@@ -1,6 +1,6 @@
1
1
  import { LogLevel } from 'bitmovin-player';
2
2
  import { isSafari } from '../../utilities/utilities.user-agent';
3
- export function processPlayerConfig(npoplayer, playerConfig) {
3
+ export function processPlayerConfig(npoPlayer, playerConfig) {
4
4
  const processedConfig = playerConfig;
5
5
  if (isSafari()) {
6
6
  processedConfig.playback = {
@@ -17,9 +17,9 @@ export function processPlayerConfig(npoplayer, playerConfig) {
17
17
  receiverVersion: 'v3',
18
18
  messageNamespace: 'urn:x-cast:com.bitmovin.player.caf',
19
19
  customReceiverConfig: {
20
- drm: npoplayer.drmProfile.drm,
21
- profileName: npoplayer.drmProfile.profileName,
22
- jwt: npoplayer.jwt
20
+ drm: npoPlayer.drmProfile.drm,
21
+ profileName: npoPlayer.drmProfile.profileName,
22
+ jwt: npoPlayer.jwt
23
23
  }
24
24
  };
25
25
  const userRemoteControlConfig = processedConfig.remotecontrol || defaultRemoteControlConfig;
@@ -32,6 +32,20 @@ export function processPlayerConfig(npoplayer, playerConfig) {
32
32
  }
33
33
  };
34
34
  processedConfig.remotecontrol = mergedRemoteControlConfig;
35
+ if (processedConfig.cast) {
36
+ console.warn("'cast' is deprecated in playerConfig. Please use 'remotecontrol' with 'GoogleCastRemoteControlConfig' instead.");
37
+ processedConfig.remotecontrol = {
38
+ ...processedConfig.remotecontrol,
39
+ customReceiverConfig: {
40
+ ...processedConfig.remotecontrol.customReceiverConfig,
41
+ ...processedConfig.cast,
42
+ enable: processedConfig.remotecontrol.customReceiverConfig?.enable ??
43
+ (typeof processedConfig.cast?.enable === 'boolean'
44
+ ? processedConfig.cast.enable.toString()
45
+ : processedConfig.cast?.enable)
46
+ }
47
+ };
48
+ }
35
49
  if (!processedConfig.logs)
36
50
  processedConfig.logs = {
37
51
  level: LogLevel.ERROR
@@ -0,0 +1,116 @@
1
+ import { LogLevel } from 'bitmovin-player';
2
+ import { isSafari } from '../../utilities/utilities.user-agent';
3
+ import { processPlayerConfig } from './processplayerconfig';
4
+ jest.mock('../../utilities/utilities.user-agent', () => ({
5
+ isSafari: jest.fn()
6
+ }));
7
+ const mockNpoPlayer = {
8
+ drmProfile: {
9
+ drm: 'mockDrm',
10
+ profileName: 'mockProfileName'
11
+ },
12
+ jwt: 'mockJwt'
13
+ };
14
+ describe('processPlayerConfig', () => {
15
+ let playerConfig;
16
+ beforeEach(() => {
17
+ playerConfig = {
18
+ key: 'dummy-key'
19
+ };
20
+ });
21
+ test('should disable autoplay and fix DRM issues on Safari', () => {
22
+ ;
23
+ isSafari.mockReturnValue(true);
24
+ const processedConfig = processPlayerConfig(mockNpoPlayer, playerConfig);
25
+ expect(processedConfig.playback?.autoplay).toBe(false);
26
+ expect(processedConfig.tweaks?.fairplay_ignore_duplicate_init_data_key_errors).toBe(true);
27
+ });
28
+ test('should always set bitmovin logging to false', () => {
29
+ playerConfig.logs = { bitmovin: true };
30
+ const processedConfig = processPlayerConfig(mockNpoPlayer, playerConfig);
31
+ expect(processedConfig.logs?.bitmovin).toEqual(false);
32
+ });
33
+ test('make sure there is an (empty) advertising object', () => {
34
+ const processedConfig = processPlayerConfig(mockNpoPlayer, playerConfig);
35
+ expect(processedConfig.advertising).toEqual({});
36
+ });
37
+ test('should set default remoteControlConfig if none is provided', () => {
38
+ const processedConfig = processPlayerConfig(mockNpoPlayer, playerConfig);
39
+ expect(processedConfig.remotecontrol).toEqual({
40
+ type: 'googlecast',
41
+ receiverApplicationId: 'CC38EB17',
42
+ receiverVersion: 'v3',
43
+ messageNamespace: 'urn:x-cast:com.bitmovin.player.caf',
44
+ customReceiverConfig: {
45
+ drm: 'mockDrm',
46
+ profileName: 'mockProfileName',
47
+ jwt: 'mockJwt'
48
+ }
49
+ });
50
+ });
51
+ test('should merge default remoteControlConfig with user-defined remoteControlConfig', () => {
52
+ playerConfig.remotecontrol = {
53
+ type: 'googlecast',
54
+ receiverApplicationId: 'user-receiver-application-id',
55
+ customReceiverConfig: {
56
+ primaryColor: 'rgb(255, 0, 0)',
57
+ enable: 'false'
58
+ }
59
+ };
60
+ const processedConfig = processPlayerConfig(mockNpoPlayer, playerConfig);
61
+ expect(processedConfig.remotecontrol).toEqual({
62
+ type: 'googlecast',
63
+ receiverApplicationId: 'user-receiver-application-id',
64
+ receiverVersion: 'v3',
65
+ messageNamespace: 'urn:x-cast:com.bitmovin.player.caf',
66
+ customReceiverConfig: {
67
+ drm: 'mockDrm',
68
+ profileName: 'mockProfileName',
69
+ jwt: 'mockJwt',
70
+ primaryColor: 'rgb(255, 0, 0)',
71
+ enable: 'false'
72
+ }
73
+ });
74
+ });
75
+ test('should convert deprecated cast configuration', () => {
76
+ playerConfig.cast = {
77
+ enable: false
78
+ };
79
+ const processedConfig = processPlayerConfig(mockNpoPlayer, playerConfig);
80
+ expect(processedConfig.remotecontrol?.customReceiverConfig?.enable).toBe('false');
81
+ });
82
+ test('should prefer customReceiverConfig over cast property', () => {
83
+ playerConfig = {
84
+ ...playerConfig,
85
+ cast: {
86
+ enable: false
87
+ }
88
+ };
89
+ playerConfig.remotecontrol = {
90
+ type: 'googlecast',
91
+ customReceiverConfig: {
92
+ enable: 'true'
93
+ }
94
+ };
95
+ const processedConfig = processPlayerConfig(mockNpoPlayer, playerConfig);
96
+ expect(processedConfig.remotecontrol?.customReceiverConfig?.enable).toBe('true');
97
+ });
98
+ test('should set default log level if logs are undefined', () => {
99
+ delete playerConfig.logs;
100
+ const processedConfig = processPlayerConfig(mockNpoPlayer, playerConfig);
101
+ expect(processedConfig.logs).toEqual({
102
+ bitmovin: false,
103
+ level: LogLevel.ERROR
104
+ });
105
+ });
106
+ test('should not set default log level if logs are defined', () => {
107
+ playerConfig.logs = {
108
+ level: LogLevel.WARN
109
+ };
110
+ const processedConfig = processPlayerConfig(mockNpoPlayer, playerConfig);
111
+ expect(processedConfig.logs).toEqual({
112
+ bitmovin: false,
113
+ level: LogLevel.WARN
114
+ });
115
+ });
116
+ });
@@ -1,4 +1,6 @@
1
1
  import { StreamObject, StreamOptions } from 'types/interfaces';
2
2
  import { SourceConfig } from 'bitmovin-player';
3
3
  import { NpoPlayerServices } from '../../../services/services';
4
- export declare function processSourceConfig(NpoPlayerServices: NpoPlayerServices, _sourceConfig: SourceConfig | undefined, _streamObject: StreamObject, drm: string | undefined, streamOptions: StreamOptions, version: string, npoTagPartyId: string | undefined, npoPlayerServices: NpoPlayerServices): Promise<SourceConfig>;
4
+ import { NPOTag } from '@npotag/tag';
5
+ export declare const getSubtitleLabels: (data: any) => string;
6
+ export declare function processSourceConfig(npoPlayerServices: NpoPlayerServices, source: string, _sourceConfig: SourceConfig | undefined, _streamObject: StreamObject, drm: string | undefined, streamOptions: StreamOptions, version: string, npoTagInstance: NPOTag | undefined): Promise<SourceConfig>;
@@ -1,6 +1,7 @@
1
1
  import { HttpResponseType } from 'bitmovin-player';
2
2
  import { npoCdnProviders } from '../../../services/cdnProviders/cdnProviders';
3
3
  import { replaceSpecialCharacters } from '../../utilities/utilities.text';
4
+ import subtitleLabelMap from '../../../lang/subtitleLabels/nl.json';
4
5
  async function setDrm(sourceConfig, drm, _streamObject, useWidevineServerCertificate) {
5
6
  const npoDrmGateway = 'https://npo-drm-gateway.samgcloud.nepworldwide.nl/authentication?custom_data=';
6
7
  switch (drm) {
@@ -66,11 +67,9 @@ async function getServerCertificate(url) {
66
67
  const response = await fetch(url);
67
68
  return response.arrayBuffer();
68
69
  }
69
- const getSubtitleLabels = function (data) {
70
- if (data.label === 'nl') {
71
- return 'Nederlands';
72
- }
73
- return data.label;
70
+ export const getSubtitleLabels = function (data) {
71
+ const langKey = data.lang.toLowerCase();
72
+ return subtitleLabelMap[langKey] || data.label || langKey;
74
73
  };
75
74
  const getQualityLabels = function (data) {
76
75
  return data.height + 'p';
@@ -102,7 +101,7 @@ function setStreamProfile(sourceConfig, _sourceConfig, _streamObject) {
102
101
  }
103
102
  return sourceConfig;
104
103
  }
105
- export async function processSourceConfig(NpoPlayerServices, _sourceConfig = {}, _streamObject, drm = undefined, streamOptions, version, npoTagPartyId, npoPlayerServices) {
104
+ export async function processSourceConfig(npoPlayerServices, source, _sourceConfig = {}, _streamObject, drm = undefined, streamOptions, version, npoTagInstance) {
106
105
  let sourceConfig = {};
107
106
  sourceConfig.title = _sourceConfig.title
108
107
  ? replacePlaceholders(_sourceConfig.title, _streamObject.metadata)
@@ -118,11 +117,11 @@ export async function processSourceConfig(NpoPlayerServices, _sourceConfig = {},
118
117
  };
119
118
  if (streamOptions.fragments || _streamObject.segment) {
120
119
  const segment = streamOptions.fragments
121
- ? NpoPlayerServices.convertFragmentToSegment(streamOptions.fragments)
120
+ ? npoPlayerServices.convertFragmentToSegment(streamOptions.fragments)
122
121
  : _streamObject.segment;
123
122
  if (segment) {
124
123
  ;
125
- sourceConfig.markers = NpoPlayerServices.segmentMarkersHandler(segment, sourceConfig.metadata.title);
124
+ sourceConfig.markers = npoPlayerServices.segmentMarkersHandler(segment, sourceConfig.metadata.title);
126
125
  }
127
126
  }
128
127
  sourceConfig.subtitleTracks =
@@ -140,7 +139,6 @@ export async function processSourceConfig(NpoPlayerServices, _sourceConfig = {},
140
139
  _sourceConfig.thumbnailTrack ?? _streamObject.assets.scrubbingThumbnail
141
140
  ? { url: _streamObject.assets.scrubbingThumbnail }
142
141
  : undefined;
143
- sourceConfig.options = _sourceConfig.options;
144
142
  sourceConfig = setStreamProfile(sourceConfig, _sourceConfig, _streamObject);
145
143
  sourceConfig = await setDrm(sourceConfig, drm, _streamObject, streamOptions.useWidevineServerCertificate ?? true);
146
144
  const streamSource = _streamObject.stream.streamURL;
@@ -162,7 +160,7 @@ export async function processSourceConfig(NpoPlayerServices, _sourceConfig = {},
162
160
  sourceConfig.analytics.title = _streamObject.metadata.title;
163
161
  sourceConfig.analytics.customData2 = _streamObject.user.type;
164
162
  sourceConfig.analytics.customData3 = npoPlayerServices.getAVType(_streamObject.stream.avType);
165
- sourceConfig.analytics.customData4 = npoTagPartyId;
163
+ sourceConfig.analytics.customData4 = npoTagInstance?.getParty();
166
164
  sourceConfig.analytics.customData5 = version;
167
165
  sourceConfig.analytics = {
168
166
  ...sourceConfig.analytics,
@@ -180,6 +178,18 @@ export async function processSourceConfig(NpoPlayerServices, _sourceConfig = {},
180
178
  }
181
179
  };
182
180
  }
181
+ sourceConfig.metadata = {
182
+ ...sourceConfig.metadata
183
+ };
184
+ sourceConfig.metadata.jwt = source;
185
+ sourceConfig.metadata.streamLinkAsJsonString = JSON.stringify(_streamObject);
186
+ sourceConfig.metadata.npoTagSession = String(npoTagInstance?.getSerializedSessionInfo());
187
+ sourceConfig.metadata.hasPreroll = _streamObject.metadata.hasPreroll || 'false';
188
+ sourceConfig.metadata.prerollUrl = _streamObject.assets.preroll || '';
189
+ sourceConfig.metadata.onScreenDebug = 'false';
190
+ sourceConfig.metadata.frameworkLevelDebug = 'false';
191
+ sourceConfig.metadata.description = sourceConfig.description || '';
192
+ sourceConfig.metadata.avType = npoPlayerServices.getAVType(_streamObject.stream.avType);
183
193
  return sourceConfig;
184
194
  }
185
195
  function decidePoster(streamObject, sourceConfig, streamOptions) {
@@ -0,0 +1,25 @@
1
+ import { getSubtitleLabels } from './processsourceconfig';
2
+ describe('getSubtitleLabels', () => {
3
+ it('should return the correct language label for known lang codes', () => {
4
+ expect(getSubtitleLabels({ lang: 'ar' })).toBe('Arabisch');
5
+ expect(getSubtitleLabels({ lang: 'bg' })).toBe('Bulgaars');
6
+ expect(getSubtitleLabels({ lang: 'de' })).toBe('Duits');
7
+ expect(getSubtitleLabels({ lang: 'en' })).toBe('Engels');
8
+ expect(getSubtitleLabels({ lang: 'es' })).toBe('Spaans');
9
+ expect(getSubtitleLabels({ lang: 'fr' })).toBe('Frans');
10
+ expect(getSubtitleLabels({ lang: 'it' })).toBe('Italiaans');
11
+ expect(getSubtitleLabels({ lang: 'nl' })).toBe('Nederlands');
12
+ expect(getSubtitleLabels({ lang: 'ma' })).toBe('Marokkaans');
13
+ expect(getSubtitleLabels({ lang: 'pl' })).toBe('Pools');
14
+ expect(getSubtitleLabels({ lang: 'ua' })).toBe('Oekraïens');
15
+ expect(getSubtitleLabels({ lang: 'tr' })).toBe('Turks');
16
+ });
17
+ it('should return the original label if the lang code is unknown', () => {
18
+ expect(getSubtitleLabels({ lang: 'jp', label: 'Japanese' })).toBe('Japanese');
19
+ expect(getSubtitleLabels({ lang: 'jp' })).toBe('jp');
20
+ });
21
+ it('should handle mixed case lang codes', () => {
22
+ expect(getSubtitleLabels({ lang: 'EN' })).toBe('Engels');
23
+ expect(getSubtitleLabels({ lang: 'Fr' })).toBe('Frans');
24
+ });
25
+ });
@@ -1,6 +1,81 @@
1
1
  export declare const localizationConfig: {
2
2
  language: string;
3
3
  vocabularies: {
4
- nl: Record<string, string>;
4
+ nl: {
5
+ "settings.video.quality": string;
6
+ "settings.audio.quality": string;
7
+ "settings.audio.track": string;
8
+ "settings.audio.mute": string;
9
+ "settings.audio.unmute": string;
10
+ "settings.audio.volume": string;
11
+ "settings.subtitles.window.color": string;
12
+ "settings.subtitles.window.opacity": string;
13
+ "settings.subtitles": string;
14
+ "settings.subtitles.font.color": string;
15
+ "settings.subtitles.font.opacity": string;
16
+ "settings.subtitles.background.color": string;
17
+ "settings.subtitles.background.opacity": string;
18
+ "colors.white": string;
19
+ "colors.black": string;
20
+ "colors.red": string;
21
+ "colors.green": string;
22
+ "colors.blue": string;
23
+ "colors.cyan": string;
24
+ "colors.yellow": string;
25
+ "colors.magenta": string;
26
+ percent: string;
27
+ "settings.subtitles.font.size": string;
28
+ "settings.subtitles.characterEdge": string;
29
+ "settings.subtitles.characterEdge.raised": string;
30
+ "settings.subtitles.characterEdge.depressed": string;
31
+ "settings.subtitles.characterEdge.uniform": string;
32
+ "settings.subtitles.characterEdge.dropshadowed": string;
33
+ "settings.subtitles.font.family": string;
34
+ "settings.subtitles.font.family.monospacedserif": string;
35
+ "settings.subtitles.font.family.proportionalserif": string;
36
+ "settings.subtitles.font.family.monospacedsansserif": string;
37
+ "settings.subtitles.font.family.proportionalsansserif": string;
38
+ "settings.subtitles.font.family.casual": string;
39
+ "settings.subtitles.font.family.cursive": string;
40
+ "settings.subtitles.font.family.smallcapital": string;
41
+ "settings.time.hours": string;
42
+ "settings.time.minutes": string;
43
+ "settings.time.seconds": string;
44
+ "ads.remainingTime": string;
45
+ settings: string;
46
+ fullscreen: string;
47
+ speed: string;
48
+ playPause: string;
49
+ play: string;
50
+ pause: string;
51
+ open: string;
52
+ close: string;
53
+ pictureInPicture: string;
54
+ appleAirplay: string;
55
+ googleCast: string;
56
+ vr: string;
57
+ off: string;
58
+ auto: string;
59
+ back: string;
60
+ reset: string;
61
+ replay: string;
62
+ normal: string;
63
+ default: string;
64
+ live: string;
65
+ "subtitle.example": string;
66
+ "subtitle.select": string;
67
+ playingOn: string;
68
+ connectingTo: string;
69
+ watermarkLink: string;
70
+ controlBar: string;
71
+ player: string;
72
+ videoPlayer: string;
73
+ audioPlayer: string;
74
+ seekBar: string;
75
+ "seekBar.value": string;
76
+ "seekBar.timeshift": string;
77
+ "seekBar.durationText": string;
78
+ "segment.unplayableTitle": string;
79
+ };
5
80
  };
6
81
  };
@@ -1,7 +1,7 @@
1
- import * as nlJson from '../../lang/nl.json';
1
+ import nlJson from '../../lang/nl.json';
2
2
  export const localizationConfig = {
3
3
  language: 'nl',
4
4
  vocabularies: {
5
- nl: nlJson.default
5
+ nl: { ...nlJson }
6
6
  }
7
7
  };
@@ -5,22 +5,24 @@ export function bindPlayerEvents(npoPlayer, player) {
5
5
  if (player == undefined)
6
6
  return;
7
7
  const isLiveStream = npoPlayer.streamObject.stream.isLiveStream;
8
- let currentTime = 0;
9
8
  let isNewSource = true;
9
+ let data = {};
10
10
  const timeDifference = (time) => {
11
11
  if (time === undefined || time === null)
12
12
  return;
13
- currentTime = isLiveStream ? +(time - Date.now() / 1000).toFixed(3) : +time.toFixed(3);
14
- return currentTime;
13
+ const currentUnixTime = Date.now() / 1000;
14
+ const timeOffset = (time - currentUnixTime).toFixed(3);
15
+ const threeDecimalTime = time.toFixed(3);
16
+ const currentTime = isLiveStream ? +timeOffset : +threeDecimalTime;
17
+ data = { stream_position: currentTime };
15
18
  };
16
19
  const logEventHandler = (eventName) => {
17
20
  return (e) => {
18
- const eventTime = e.time === undefined ? undefined : timeDifference(e.time);
19
- logEvent(npoPlayer, eventName, eventTime);
21
+ timeDifference(e.time);
22
+ logEvent(npoPlayer, eventName, data);
20
23
  };
21
24
  };
22
25
  const seekHandler = (e) => {
23
- let data = {};
24
26
  if (e.type === 'timeshift') {
25
27
  const timeDifferencePosition = -1 * (Date.now() / 1000 - e.position);
26
28
  const timeDifferenceTarget = -1 * (Date.now() / 1000 - e.target);
@@ -37,42 +39,48 @@ export function bindPlayerEvents(npoPlayer, player) {
37
39
  }
38
40
  logEvent(npoPlayer, 'seek', data);
39
41
  };
40
- const handleSourceLoaded = () => {
42
+ const handleSourceLoaded = (e) => {
43
+ timeDifference(e.time);
41
44
  isNewSource = true;
42
45
  if (isLiveStream) {
43
- logEvent(npoPlayer, 'stop');
46
+ logEvent(npoPlayer, 'stop', data);
44
47
  }
45
48
  };
46
49
  const handlePlay = (e) => {
50
+ timeDifference(e.time);
47
51
  if (!isNewSource && e.issuer !== 'ui-seek') {
48
- logEvent(npoPlayer, 'resume', timeDifference(e.time));
52
+ logEvent(npoPlayer, 'resume', data);
49
53
  }
50
54
  };
51
55
  const pausedHandler = (e) => {
56
+ timeDifference(e.time);
52
57
  if (e.issuer !== 'ui-seek') {
53
- logEvent(npoPlayer, 'pause', timeDifference(e.time));
58
+ logEvent(npoPlayer, 'pause', data);
54
59
  }
55
60
  };
56
61
  const handleTime = (e) => {
62
+ timeDifference(e.time);
57
63
  if (isNewSource && !npoPlayer.adBreakActive) {
58
- logEvent(npoPlayer, 'start', timeDifference(e.time));
64
+ logEvent(npoPlayer, 'start', data);
59
65
  isNewSource = false;
60
66
  }
61
67
  else {
62
- logEvent(npoPlayer, 'time', timeDifference(e.time));
68
+ logEvent(npoPlayer, 'time', data);
63
69
  }
64
70
  };
65
- const handleViewModeChange = () => {
71
+ const handleViewModeChange = (e) => {
66
72
  const newViewMode = player.getViewMode();
73
+ timeDifference(e.time);
67
74
  if (newViewMode === ViewMode.Fullscreen) {
68
- logEvent(npoPlayer, 'fullscreen', currentTime);
75
+ logEvent(npoPlayer, 'fullscreen', data);
69
76
  }
70
77
  else {
71
- logEvent(npoPlayer, 'windowed', currentTime);
78
+ logEvent(npoPlayer, 'windowed', data);
72
79
  }
73
80
  };
74
- const stopBeforeUnload = () => {
75
- logEvent(npoPlayer, 'stop');
81
+ const stopBeforeUnload = (e) => {
82
+ timeDifference(e.time);
83
+ logEvent(npoPlayer, 'stop', data);
76
84
  };
77
85
  for (const [key, value] of logEventHandlers) {
78
86
  player.off(key, value);
@@ -1,13 +1,8 @@
1
1
  export function logEvent(npoPlayer, event, data) {
2
2
  if (!npoPlayer.player || !npoPlayer.streamTracker || npoPlayer.adBreakActive || npoPlayer.player.isCasting())
3
3
  return;
4
- try {
5
- npoPlayer.player.getCurrentTime();
6
- }
7
- catch (error) {
8
- console.warn('Er gaat iets mis met de player API', error);
9
- }
10
- const streamOptions = { stream_position: npoPlayer.player.getCurrentTime() };
4
+ if (data === undefined || Object.keys(data).length === 0)
5
+ data = { stream_position: 0 };
11
6
  const eventHandlers = {
12
7
  start: npoPlayer.streamTracker.start,
13
8
  buffering: npoPlayer.streamTracker.buffering,
@@ -27,5 +22,5 @@ export function logEvent(npoPlayer, event, data) {
27
22
  };
28
23
  const streamTrackerHandler = eventHandlers[event];
29
24
  npoPlayer.logEmitter.emit('logEvent', event, data);
30
- streamTrackerHandler(streamOptions);
25
+ streamTrackerHandler(data);
31
26
  }
@@ -1,4 +1,4 @@
1
- import { type NPOTag } from '@npotag/tag';
1
+ import { type NPOTag, type PageTracker } from '@npotag/tag';
2
2
  import { type InitialisationProps } from '@npotag/tag/dist/types/src/npoTag';
3
3
  import type NpoPlayer from '../../../npoplayer';
4
- export declare function initPlayerTracker(npoplayer: NpoPlayer, _npotag?: InitialisationProps | undefined, _npotaginstance?: NPOTag): void;
4
+ export declare function initPlayerTracker(npoPlayer: NpoPlayer, _npoTag?: InitialisationProps, _npoTagInstance?: NPOTag, _npoTagPageTracker?: PageTracker): void;