@npo/player 1.25.0 → 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 (127) 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 +12 -12
  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 +4 -4
  9. package/lib/js/playeractions/handlers/processsourceconfig.d.ts +1 -0
  10. package/lib/js/playeractions/handlers/processsourceconfig.js +4 -5
  11. package/lib/js/playeractions/handlers/processsourceconfig.test.js +25 -0
  12. package/lib/js/tracking/handlers/playertrackerstart.js +1 -1
  13. package/lib/lang/nl.json +1 -1
  14. package/lib/lang/subtitleLabels/nl.json +14 -0
  15. package/lib/npoplayer.d.ts +2 -3
  16. package/lib/npoplayer.js +24 -32
  17. package/lib/npoplayer.test.js +1 -1
  18. package/lib/package.json +2 -2
  19. package/lib/services/a11y/setup.js +2 -5
  20. package/lib/services/a11y/setup.test.js +2 -19
  21. package/lib/services/advertHandlers/discardAdBreak.js +4 -4
  22. package/lib/services/advertHandlers/discardAdBreak.test.js +25 -13
  23. package/lib/services/advertHandlers/handlePreRolls.js +19 -19
  24. package/lib/services/advertHandlers/handlePrerolls.test.js +4 -4
  25. package/lib/services/drmHandlers/decideprofile.js +1 -1
  26. package/lib/services/drmHandlers/decideprofile.test.js +1 -1
  27. package/lib/services/drmHandlers/verifydrm.js +6 -6
  28. package/lib/services/drmHandlers/verifydrm.test.js +5 -5
  29. package/lib/services/eventListenerHandlers/removeEventListeners.js +2 -2
  30. package/lib/services/eventListenerHandlers/removeEventListeners.test.js +9 -9
  31. package/lib/services/keyboardHandlers/resolvekeypress.js +5 -5
  32. package/lib/services/keyboardHandlers/resolvekeypress.test.js +1 -1
  33. package/lib/services/liveStreamHandlers/handleLiveStreamControls.js +2 -2
  34. package/lib/services/liveStreamHandlers/handleLiveStreamControls.test.js +2 -2
  35. package/lib/services/localStorageHandlers/localStorageHandlers.js +2 -2
  36. package/lib/services/localStorageHandlers/localStorageHandlers.test.js +2 -5
  37. package/lib/services/nicamHandlers/nicamhandler.d.ts +1 -2
  38. package/lib/services/nicamHandlers/nicamhandler.js +17 -16
  39. package/lib/services/nicamHandlers/nicamhandler.test.js +72 -9
  40. package/lib/services/npoPlayerAPI/npoPlayerAPI.d.ts +2 -2
  41. package/lib/services/npoPlayerAPI/npoPlayerAPI.js +16 -23
  42. package/lib/services/preferences/handlePreferences.d.ts +2 -0
  43. package/lib/services/preferences/handlePreferences.js +42 -0
  44. package/lib/services/preferences/handlePreferences.test.js +102 -0
  45. package/lib/services/segmentHandlers/addSegmentEventListeners.js +2 -2
  46. package/lib/services/segmentHandlers/addSegmentEventListeners.test.js +1 -1
  47. package/lib/services/services.d.ts +6 -2
  48. package/lib/services/services.js +21 -2
  49. package/lib/services/streamoptionsHandlers/{steamOptionsHandler.js → streamOptionsHandler.js} +11 -11
  50. package/lib/services/streamoptionsHandlers/streamOptionsHandler.test.js +20 -20
  51. package/lib/services/uiHandlers/uiVisiblityHandler.d.ts +3 -0
  52. package/lib/services/uiHandlers/uiVisiblityHandler.js +26 -0
  53. package/lib/services/uiHandlers/uiVisiblityHandler.test.d.ts +1 -0
  54. package/lib/services/uiHandlers/uiVisiblityHandler.test.js +62 -0
  55. package/lib/src/js/api/getstreamobject.d.ts +1 -1
  56. package/lib/src/js/playeractions/handlers/processplayerconfig.d.ts +1 -1
  57. package/lib/src/js/playeractions/handlers/processsourceconfig.d.ts +1 -0
  58. package/lib/src/js/playeractions/handlers/processsourceconfig.test.d.ts +1 -0
  59. package/lib/src/npoplayer.d.ts +2 -3
  60. package/lib/src/services/nicamHandlers/nicamhandler.d.ts +1 -2
  61. package/lib/src/services/npoPlayerAPI/npoPlayerAPI.d.ts +2 -2
  62. package/lib/src/services/preferences/handlePreferences.d.ts +2 -0
  63. package/lib/src/services/preferences/handlePreferences.test.d.ts +1 -0
  64. package/lib/src/services/services.d.ts +6 -2
  65. package/lib/src/services/uiHandlers/uiVisiblityHandler.d.ts +3 -0
  66. package/lib/src/services/uiHandlers/uiVisiblityHandler.test.d.ts +1 -0
  67. package/lib/src/types/classes.d.ts +6 -0
  68. package/lib/src/types/interfaces.d.ts +24 -3
  69. package/lib/src/ui/components/adbutton.d.ts +1 -1
  70. package/lib/src/ui/components/adlabel.d.ts +1 -1
  71. package/lib/src/ui/components/buttons.d.ts +6 -16
  72. package/lib/src/ui/components/controlbar.d.ts +2 -2
  73. package/lib/src/ui/components/ctabar.d.ts +1 -1
  74. package/lib/src/ui/components/playnext.d.ts +1 -4
  75. package/lib/src/ui/components/seekbar.d.ts +1 -1
  76. package/lib/src/ui/components/titlebar.d.ts +1 -1
  77. package/lib/src/ui/handlers/playnextscreen.test.d.ts +1 -0
  78. package/lib/src/ui/uicontainer.test.d.ts +1 -0
  79. package/lib/tests/mocks/mockLogEmitter.d.ts +2 -0
  80. package/lib/tests/mocks/mockLogEmitter.js +20 -0
  81. package/lib/tests/mocks/mockNpoplayer.js +1 -2
  82. package/lib/tests/mocks/playerContextMock.d.ts +5 -3
  83. package/lib/tests/mocks/playerContextMock.js +7 -5
  84. package/lib/types/classes.d.ts +6 -0
  85. package/lib/types/classes.js +12 -0
  86. package/lib/types/interfaces.d.ts +24 -3
  87. package/lib/types/interfaces.js +1 -0
  88. package/lib/ui/components/adbutton.d.ts +1 -1
  89. package/lib/ui/components/adbutton.js +3 -3
  90. package/lib/ui/components/adlabel.d.ts +1 -1
  91. package/lib/ui/components/adlabel.js +3 -3
  92. package/lib/ui/components/buttons.d.ts +6 -16
  93. package/lib/ui/components/buttons.js +27 -10
  94. package/lib/ui/components/controlbar.d.ts +2 -2
  95. package/lib/ui/components/controlbar.js +20 -37
  96. package/lib/ui/components/ctabar.d.ts +1 -1
  97. package/lib/ui/components/ctabar.js +4 -4
  98. package/lib/ui/components/nativemobile/topbar.js +2 -2
  99. package/lib/ui/components/playnext.d.ts +1 -4
  100. package/lib/ui/components/playnext.js +3 -3
  101. package/lib/ui/components/seekbar.d.ts +1 -1
  102. package/lib/ui/components/seekbar.js +5 -3
  103. package/lib/ui/components/settingspanel.js +9 -9
  104. package/lib/ui/components/titlebar.d.ts +1 -1
  105. package/lib/ui/components/titlebar.js +2 -2
  106. package/lib/ui/components/topbar.js +6 -20
  107. package/lib/ui/components/verticalvideo/controlbar.js +1 -1
  108. package/lib/ui/handlers/playnextscreen.test.d.ts +1 -0
  109. package/lib/ui/nativemobileuifactory.js +1 -1
  110. package/lib/ui/nativemobileuifactory.test.js +2 -1
  111. package/lib/ui/uicontainer.js +11 -30
  112. package/lib/ui/uicontainer.test.d.ts +1 -0
  113. package/lib/ui/uicontainer.test.js +80 -0
  114. package/package.json +2 -2
  115. package/src/style/components/_advert.scss +0 -9
  116. package/src/style/components/_nicam.scss +5 -4
  117. package/src/style/components/_settingspanel.scss +48 -17
  118. package/src/style/components/vertical-video/_settingspanel.scss +1 -1
  119. package/src/style/npoplayer.css +25 -20
  120. package/src/style/variants/_player-base.scss +4 -0
  121. package/src/style/variants/_player-large.scss +5 -1
  122. package/src/style/variants/_player-small.scss +11 -8
  123. /package/lib/{src/ui/handlers/playnextstreen.test.d.ts → js/playeractions/handlers/processsourceconfig.test.d.ts} +0 -0
  124. /package/lib/{ui/handlers/playnextstreen.test.d.ts → services/preferences/handlePreferences.test.d.ts} +0 -0
  125. /package/lib/services/streamoptionsHandlers/{steamOptionsHandler.d.ts → streamOptionsHandler.d.ts} +0 -0
  126. /package/lib/src/services/streamoptionsHandlers/{steamOptionsHandler.d.ts → streamOptionsHandler.d.ts} +0 -0
  127. /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.25.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('../../services/streamoptionsHandlers/steamOptionsHandler', () => ({
5
+ jest.mock('../../services/streamoptionsHandlers/streamOptionsHandler', () => ({
6
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;
@@ -2,4 +2,5 @@ import { StreamObject, StreamOptions } from 'types/interfaces';
2
2
  import { SourceConfig } from 'bitmovin-player';
3
3
  import { NpoPlayerServices } from '../../../services/services';
4
4
  import { NPOTag } from '@npotag/tag';
5
+ export declare const getSubtitleLabels: (data: any) => string;
5
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';
@@ -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
+ });
@@ -12,7 +12,7 @@ export function startPlayerTracker(playerInstance, duration, version, source) {
12
12
  stream_length: duration,
13
13
  stream_id: validatePrid(source),
14
14
  player_id: 'npoplayer-web',
15
- av_type: playerInstance.npoplayerServices.getAVType(playerInstance.streamObject.stream.avType),
15
+ av_type: playerInstance.npoPlayerServices.getAVType(playerInstance.streamObject.stream.avType),
16
16
  player_version: version,
17
17
  sko_player_version: '1.0.0'
18
18
  };
package/lib/lang/nl.json CHANGED
@@ -52,7 +52,7 @@
52
52
  "googleCast": "Google Cast",
53
53
  "vr": "VR",
54
54
  "off": "Uit",
55
- "auto": "automatisch",
55
+ "auto": "Automatisch",
56
56
  "back": "Terug",
57
57
  "reset": "Herstellen",
58
58
  "replay": "Replay",
@@ -0,0 +1,14 @@
1
+ {
2
+ "ar": "Arabisch",
3
+ "bg": "Bulgaars",
4
+ "de": "Duits",
5
+ "en": "Engels",
6
+ "es": "Spaans",
7
+ "fr": "Frans",
8
+ "it": "Italiaans",
9
+ "nl": "Nederlands",
10
+ "ma": "Marokkaans",
11
+ "pl": "Pools",
12
+ "ua": "Oekraïens",
13
+ "tr": "Turks"
14
+ }
@@ -4,7 +4,7 @@ import { type InitialisationProps } from '@npotag/tag/dist/types/src/npoTag';
4
4
  import { type PlayerAPI, type PlayerConfig, type SourceConfig } from 'bitmovin-player';
5
5
  import { UIManager } from 'bitmovin-player-ui';
6
6
  import { LogEmitter } from './types/classes';
7
- import { type DRMProfile, type ApiPayload, type NPOTagObject, type StreamObject, type StreamOptions, type UIComponents, type TimeLineMarker, type LocalStorageData, type PlayerContext, type EventListeners, NpoPlayerUIVariants } from './types/interfaces';
7
+ import { type DRMProfile, type ApiPayload, type NPOTagObject, type StreamObject, type StreamOptions, type UIComponents, type TimeLineMarker, type PlayerContext, type EventListeners, NpoPlayerUIVariants } from './types/interfaces';
8
8
  export { type PlayerConfig, type InitialisationProps, type NPOTagObject, type StreamOptions, NpoPlayerUIVariants };
9
9
  export default class NpoPlayer {
10
10
  playerConfig: PlayerConfig | undefined;
@@ -30,10 +30,9 @@ export default class NpoPlayer {
30
30
  isShowingPlayNextScreen: boolean;
31
31
  canceledPlayNextScreen: boolean;
32
32
  playerContext: PlayerContext | undefined;
33
- npoplayerServices: NpoPlayerServices;
33
+ npoPlayerServices: NpoPlayerServices;
34
34
  eventListeners: EventListeners | undefined;
35
35
  mockNpoPlayer: any;
36
- userPreferences: LocalStorageData;
37
36
  constructor(container: HTMLElement | {
38
37
  container: HTMLElement;
39
38
  playerConfig: PlayerConfig;
package/lib/npoplayer.js CHANGED
@@ -12,7 +12,7 @@ import { logVersion } from './js/utilities/utilities.version';
12
12
  import { getStreamDurationInSeconds } from './js/utilities/utilities.stream';
13
13
  import { LogEmitter } from './types/classes';
14
14
  import pkg from '../package.json';
15
- import { NpoPlayerUIVariants, LocalStorageValues } from './types/interfaces';
15
+ import { NpoPlayerUIVariants } from './types/interfaces';
16
16
  import { nativeMobileUiFactory } from './ui/nativemobileuifactory';
17
17
  import { setupMediaSessionActionHandlers } from './js/playeractions/handlers/mediasessionactions';
18
18
  import { removeReplayClass } from './js/playeractions/handlers/removereplayclass';
@@ -45,9 +45,8 @@ export default class NpoPlayer {
45
45
  this.isShowingPlayNextScreen = false;
46
46
  this.canceledPlayNextScreen = false;
47
47
  this.playerContext = undefined;
48
- this.npoplayerServices = new NpoPlayerServices();
48
+ this.npoPlayerServices = new NpoPlayerServices();
49
49
  this.eventListeners = undefined;
50
- this.userPreferences = {};
51
50
  if (typeof container === 'object' && !(container instanceof HTMLElement)) {
52
51
  const params = container;
53
52
  this.container = params.container;
@@ -80,35 +79,25 @@ export default class NpoPlayer {
80
79
  this.player = new Player(_container, processedPlayerConfig);
81
80
  Player.addModule(getModuleExport(AdsModuleBM));
82
81
  const npoPlayerAPI = new NpoPlayerAPI(this.player);
83
- this.playerContext = { player: npoPlayerAPI, npoplayer: this };
84
- this.userPreferences = this.npoplayerServices.getStoredUserPrefs();
85
- this.npoplayerServices.handleUserPrefs(this.playerContext);
86
- this.npoplayerServices.setAccessibilityAttributes(this.playerContext);
87
- window.addEventListener('beforeunload', () => {
88
- if (this.player) {
89
- this.npoplayerServices.setStoredUserPrefs(LocalStorageValues.SUBTITLES_ENABLED, npoPlayerAPI.areSubtitlesEnabled().toString());
90
- this.npoplayerServices.setStoredUserPrefs(LocalStorageValues.VOLUME, npoPlayerAPI.getVolume());
91
- this.npoplayerServices.setStoredUserPrefs(LocalStorageValues.IS_MUTED, npoPlayerAPI.isMuted().toString());
92
- }
93
- });
82
+ this.playerContext = { player: npoPlayerAPI, npoPlayer: this };
83
+ this.npoPlayerServices.handleUserPrefs(this.playerContext);
84
+ this.npoPlayerServices.setAccessibilityAttributes(this.playerContext);
85
+ this.npoPlayerServices.handlePreferences(this.playerContext);
94
86
  this.container.addEventListener('keydown', (e) => {
95
87
  if (!this.playerContext)
96
88
  return;
97
- this.npoplayerServices.keyboardHandler(this.playerContext, e);
89
+ this.npoPlayerServices.keyboardHandler(this.playerContext, e);
98
90
  }, true);
99
91
  }
100
92
  async loadStream(source, options = {}) {
101
93
  if (!this.playerContext)
102
94
  return;
103
95
  this.streamOptions = options;
104
- if (this.player == undefined) {
96
+ if (!this.player) {
105
97
  console.error('Er is nog geen player geladen.');
106
98
  return;
107
99
  }
108
- if (this.adBreakActive) {
109
- const currentAd = this.player.ads.getActiveAdBreak().id;
110
- this.player.ads.discardAdBreak(currentAd);
111
- }
100
+ this.npoPlayerServices.discardAdBreak(this.playerContext);
112
101
  let _streamObject;
113
102
  const sourceIsUrl = isUrl(source);
114
103
  const sourceIsMedia = await isMediaUrl(source);
@@ -131,12 +120,13 @@ export default class NpoPlayer {
131
120
  customData5: this.version
132
121
  }
133
122
  };
123
+ await this.playerContext.player.createUIManager(this.playerContext, this.variant);
134
124
  await this.playerContext.player?.load(this.sourceConfig);
135
125
  getDurationAndStartPlayerTracker();
136
126
  }
137
127
  else if (sourceIsJWTToken) {
138
128
  this.jwt = source;
139
- const profile = await this.npoplayerServices.decideProfile(this.playerContext, options?.preferredDRM ?? '');
129
+ const profile = await this.npoPlayerServices.decideProfile(this.playerContext, options?.preferredDRM ?? '');
140
130
  const defaultEndpoint = 'https://prod.npoplayer.nl/';
141
131
  const endpoint = options?.endpoint ?? defaultEndpoint;
142
132
  const payload = {
@@ -153,12 +143,10 @@ export default class NpoPlayer {
153
143
  }
154
144
  }
155
145
  };
146
+ await this.playerContext.player.createUIManager(this.playerContext, this.variant);
156
147
  try {
157
148
  _streamObject = await getStreamObject(this, payload);
158
149
  this.streamObject = _streamObject;
159
- if (_streamObject) {
160
- await this.playerContext.player.createUIManager(this.playerContext, this.variant);
161
- }
162
150
  }
163
151
  catch (error) {
164
152
  this.doError('Het is niet gelukt de stream op te halen. \n' + error);
@@ -168,8 +156,8 @@ export default class NpoPlayer {
168
156
  if (this.streamObject?.stream == undefined)
169
157
  return;
170
158
  const drmType = this.streamObject.stream.drmType ?? undefined;
171
- 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);
172
- await this.npoplayerServices.verifyDRM(this.playerContext, payload);
159
+ 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
+ await this.npoPlayerServices.verifyDRM(this.playerContext, payload);
173
161
  setupMediaSessionActionHandlers(this.player, this.sourceConfig, _streamObject);
174
162
  logEvent(this, 'load');
175
163
  let streamDuration = _streamObject.metadata.duration;
@@ -192,7 +180,7 @@ export default class NpoPlayer {
192
180
  this.streamObject.assets.preroll) {
193
181
  this.player.on(PlayerEvent.AdBreakFinished, initAndStartTracker);
194
182
  this.player.on(PlayerEvent.AdError, initAndStartTracker);
195
- await this.npoplayerServices.schedulePreRolls(this.playerContext);
183
+ await this.npoPlayerServices.schedulePreRolls(this.playerContext);
196
184
  }
197
185
  else {
198
186
  initAndStartTracker();
@@ -201,11 +189,12 @@ export default class NpoPlayer {
201
189
  else {
202
190
  this.doError(`Het is niet gelukt de stream op te halen: \n Input is geen valide token of media object.`, 500);
203
191
  }
204
- this.npoplayerServices.setupNicamKijkwijzerIcons(this.playerContext);
205
- this.player.on(PlayerEvent.Play, () => {
206
- this.npoplayerServices.showNicamAfterUiDelay(this.playerContext, this.uiManager);
192
+ this.npoPlayerServices.handleStreamOptions(this.playerContext);
193
+ this.npoPlayerServices.setupNicamKijkwijzerIcons(this.playerContext);
194
+ this.player.on(PlayerEvent.Ready, () => {
195
+ this.npoPlayerServices.showNicamAfterUiDelay(this.playerContext);
207
196
  });
208
- this.npoplayerServices.handleVerticalVideoControls(this.playerContext, this.variant);
197
+ this.npoPlayerServices.handleVerticalVideoControls(this.playerContext, this.variant);
209
198
  this.player.on(PlayerEvent.Seek, () => {
210
199
  removeReplayClass(this.player);
211
200
  });
@@ -226,7 +215,7 @@ export default class NpoPlayer {
226
215
  this.player.on(PlayerEvent.Ready, setLiveOffsetListener);
227
216
  }
228
217
  doError(input, status) {
229
- if (this.player == undefined)
218
+ if (!this.player)
230
219
  return;
231
220
  if (status) {
232
221
  this.logEmitter.emit('logError', status);
@@ -328,6 +317,9 @@ export default class NpoPlayer {
328
317
  clearInterval(this.npoTag?.heartbeatInterval);
329
318
  }
330
319
  logEvent(this, 'stop');
320
+ if (this.playerContext) {
321
+ this.npoPlayerServices.removeUivisiblityHandlers(this.playerContext);
322
+ }
331
323
  this.uiManager?.release();
332
324
  this.uiManager = undefined;
333
325
  await this.player?.destroy();
@@ -3,7 +3,7 @@ const div = document.createElement('div');
3
3
  const testPlayerConfig = {
4
4
  key: 'dummy-key'
5
5
  };
6
- jest.mock('./services/streamoptionsHandlers/steamOptionsHandler', () => ({
6
+ jest.mock('./services/streamoptionsHandlers/streamOptionsHandler', () => ({
7
7
  handleStreamOptions: jest.fn()
8
8
  }));
9
9
  let player;
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npo/player",
3
- "version": "1.25.0",
3
+ "version": "1.26.0",
4
4
  "description": "NPO Player",
5
5
  "author": "Publieke Omroep <player@npo.nl>",
6
6
  "contributors": [
@@ -89,7 +89,7 @@
89
89
  "webpack-dev-server": "^4.11.1"
90
90
  },
91
91
  "dependencies": {
92
- "@npotag/tag": "3.2.1",
92
+ "@npotag/tag": "3.2.4",
93
93
  "bitmovin-player": "^8.166.0",
94
94
  "bitmovin-player-ui": "3.67.0"
95
95
  },
@@ -1,16 +1,13 @@
1
1
  import { NpoPlayerEvent } from '../../types/events';
2
2
  import { addAccessibilityAttributes } from '../../ui/handlers/accessibilityhandler';
3
3
  export function setupAccessibilityAttributes(playerContext) {
4
- const { player, npoplayer } = playerContext;
4
+ const { player, npoPlayer } = playerContext;
5
5
  const events = [NpoPlayerEvent.SourceLoaded, NpoPlayerEvent.AdBreakFinished, NpoPlayerEvent.AdError];
6
6
  const triggerAddAccessibilityAttributes = () => {
7
7
  for (const event of events) {
8
8
  player?.off(event, triggerAddAccessibilityAttributes);
9
9
  }
10
- if (npoplayer.streamOptions.enableSubtitles === true || npoplayer.userPreferences.subtitles_enabled === 'true') {
11
- player?.enableSubtitles();
12
- }
13
- addAccessibilityAttributes(npoplayer.container, npoplayer.streamObject.metadata);
10
+ addAccessibilityAttributes(npoPlayer.container, npoPlayer.streamObject.metadata);
14
11
  };
15
12
  for (const event of events) {
16
13
  player?.on(event, triggerAddAccessibilityAttributes);
@@ -31,14 +31,11 @@ describe('setupAccessibilityAttributes', () => {
31
31
  };
32
32
  playerContext = {
33
33
  player: mockPlayerAPI,
34
- npoplayer: {
34
+ npoPlayer: {
35
35
  ...mockNpoPlayer,
36
36
  streamOptions: {
37
37
  enableSubtitles: false
38
38
  },
39
- userPreferences: {
40
- subtitles_enabled: 'false'
41
- },
42
39
  container: document.createElement('div'),
43
40
  streamObject: {},
44
41
  playerConfig: {},
@@ -69,20 +66,6 @@ describe('setupAccessibilityAttributes', () => {
69
66
  afterEach(() => {
70
67
  jest.clearAllMocks();
71
68
  });
72
- it('should enable subtitles if playerContext.npoplayer.userPreferences.subtitles_enabled is "true"', () => {
73
- playerContext.npoplayer.userPreferences.subtitles_enabled = 'true';
74
- setupAccessibilityAttributes(playerContext);
75
- const triggerFunction = mockAddEventListener.mock.calls[0][1];
76
- triggerFunction();
77
- expect(mockEnableSubtitles).toHaveBeenCalled();
78
- });
79
- it('should enable subtitles if streamOptions.enableSubtitles is true', () => {
80
- playerContext.npoplayer.streamOptions.enableSubtitles = true;
81
- setupAccessibilityAttributes(playerContext);
82
- const triggerFunction = mockAddEventListener.mock.calls[0][1];
83
- triggerFunction();
84
- expect(mockEnableSubtitles).toHaveBeenCalled();
85
- });
86
69
  it('should set up event listeners for specified events', () => {
87
70
  setupAccessibilityAttributes(playerContext);
88
71
  expect(mockAddEventListener).toHaveBeenCalledTimes(3);
@@ -103,6 +86,6 @@ describe('setupAccessibilityAttributes', () => {
103
86
  setupAccessibilityAttributes(playerContext);
104
87
  const triggerFunction = mockAddEventListener.mock.calls[0][1];
105
88
  triggerFunction();
106
- expect(addAccessibilityAttributes).toHaveBeenCalledWith(playerContext.npoplayer.container, playerContext.npoplayer.streamObject.metadata);
89
+ expect(addAccessibilityAttributes).toHaveBeenCalledWith(playerContext.npoPlayer.container, playerContext.npoPlayer.streamObject.metadata);
107
90
  });
108
91
  });
@@ -1,11 +1,11 @@
1
1
  export function discardAdBreak(playerContext) {
2
- const { npoplayer, player } = playerContext;
3
- const { adBreakActive } = npoplayer;
2
+ const { npoPlayer, player } = playerContext;
3
+ const { adBreakActive } = npoPlayer;
4
4
  if (!player || adBreakActive === false)
5
5
  return;
6
6
  const activeAdBreak = player.getActiveAdBreak();
7
7
  if (activeAdBreak?.id) {
8
- player.discardAdBreak(activeAdBreak.id);
8
+ player.playerAPI.ads.discardAdBreak(activeAdBreak.id);
9
9
  }
10
- npoplayer.adBreakActive = false;
10
+ npoPlayer.adBreakActive = false;
11
11
  }
@@ -2,59 +2,71 @@ import createPlayerContextMock, { createMockNpoPlayer, createMockNpoPlayerAPI }
2
2
  import { discardAdBreak } from './discardAdBreak';
3
3
  describe('discardAdBreak', () => {
4
4
  let mockNpoPlayerAPI;
5
- let npoplayerMock;
5
+ let npoPlayerMock;
6
6
  const activeAdBreakMock = { id: 'ad-break-id' };
7
7
  afterEach(() => {
8
8
  jest.resetAllMocks();
9
9
  });
10
+ it('should not discard ad break if player is undefined', () => {
11
+ npoPlayerMock = createMockNpoPlayer({
12
+ adBreakActive: true
13
+ });
14
+ const playerContextMock = createPlayerContextMock({
15
+ player: undefined,
16
+ npoPlayer: npoPlayerMock
17
+ });
18
+ discardAdBreak(playerContextMock);
19
+ expect(npoPlayerMock.adBreakActive).toBe(true);
20
+ });
10
21
  it('should not discard ad break if adBreakActive is false', () => {
11
22
  mockNpoPlayerAPI = createMockNpoPlayerAPI({
12
- getActiveAdBreak: jest.fn().mockReturnValue(undefined),
23
+ getActiveAdBreak: jest.fn().mockReturnValue(activeAdBreakMock),
13
24
  discardAdBreak: jest.fn()
14
25
  });
15
- npoplayerMock = createMockNpoPlayer({
26
+ npoPlayerMock = createMockNpoPlayer({
16
27
  adBreakActive: false
17
28
  });
18
29
  const playerContextMock = createPlayerContextMock({
19
30
  player: mockNpoPlayerAPI,
20
- npoplayer: npoplayerMock
31
+ npoPlayer: npoPlayerMock
21
32
  });
22
33
  discardAdBreak(playerContextMock);
23
34
  expect(mockNpoPlayerAPI.getActiveAdBreak).not.toHaveBeenCalled();
24
- expect(mockNpoPlayerAPI.discardAdBreak).not.toHaveBeenCalled();
35
+ expect(mockNpoPlayerAPI.playerAPI.ads.discardAdBreak).not.toHaveBeenCalled();
36
+ expect(npoPlayerMock.adBreakActive).toBe(false);
25
37
  });
26
38
  it('should discard active ad break if adBreakActive is true and ad break exists', () => {
27
39
  mockNpoPlayerAPI = createMockNpoPlayerAPI({
28
40
  getActiveAdBreak: jest.fn().mockReturnValue(activeAdBreakMock),
29
41
  discardAdBreak: jest.fn()
30
42
  });
31
- npoplayerMock = createMockNpoPlayer({
43
+ npoPlayerMock = createMockNpoPlayer({
32
44
  adBreakActive: true
33
45
  });
34
46
  const playerContextMock = createPlayerContextMock({
35
47
  player: mockNpoPlayerAPI,
36
- npoplayer: npoplayerMock
48
+ npoPlayer: npoPlayerMock
37
49
  });
38
50
  discardAdBreak(playerContextMock);
39
51
  expect(mockNpoPlayerAPI.getActiveAdBreak).toHaveBeenCalled();
40
- expect(mockNpoPlayerAPI.discardAdBreak).toHaveBeenCalledWith(activeAdBreakMock.id);
41
- expect(npoplayerMock.adBreakActive).toBe(false);
52
+ expect(mockNpoPlayerAPI.playerAPI.ads.discardAdBreak).toHaveBeenCalledWith(activeAdBreakMock.id);
53
+ expect(npoPlayerMock.adBreakActive).toBe(false);
42
54
  });
43
55
  it('should not discard ad break if active ad break does not exist', () => {
44
56
  mockNpoPlayerAPI = createMockNpoPlayerAPI({
45
57
  getActiveAdBreak: jest.fn().mockReturnValue(undefined),
46
58
  discardAdBreak: jest.fn()
47
59
  });
48
- npoplayerMock = createMockNpoPlayer({
60
+ npoPlayerMock = createMockNpoPlayer({
49
61
  adBreakActive: true
50
62
  });
51
63
  const playerContextMock = createPlayerContextMock({
52
64
  player: mockNpoPlayerAPI,
53
- npoplayer: npoplayerMock
65
+ npoPlayer: npoPlayerMock
54
66
  });
55
67
  discardAdBreak(playerContextMock);
56
68
  expect(mockNpoPlayerAPI.getActiveAdBreak).toHaveBeenCalled();
57
- expect(mockNpoPlayerAPI.discardAdBreak).not.toHaveBeenCalled();
58
- expect(npoplayerMock.adBreakActive).toBe(false);
69
+ expect(mockNpoPlayerAPI.playerAPI.ads.discardAdBreak).not.toHaveBeenCalled();
70
+ expect(npoPlayerMock.adBreakActive).toBe(false);
59
71
  });
60
72
  });