@npo/player 1.24.6 → 1.25.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 (87) hide show
  1. package/README.md +1 -1
  2. package/lib/js/api/getstreamobject.test.js +2 -2
  3. package/lib/js/playeractions/handlers/processplayerconfig.js +14 -0
  4. package/lib/js/playeractions/handlers/processplayerconfig.test.d.ts +1 -0
  5. package/lib/js/playeractions/handlers/processplayerconfig.test.js +116 -0
  6. package/lib/js/playeractions/handlers/processsourceconfig.d.ts +2 -1
  7. package/lib/js/playeractions/handlers/processsourceconfig.js +16 -5
  8. package/lib/js/settings/localization.d.ts +76 -1
  9. package/lib/js/settings/localization.js +2 -2
  10. package/lib/js/tracking/handlers/eventbinding.js +25 -17
  11. package/lib/js/tracking/handlers/eventlogging.js +3 -8
  12. package/lib/js/tracking/handlers/playertrackerinit.d.ts +2 -2
  13. package/lib/js/tracking/handlers/playertrackerinit.js +8 -6
  14. package/lib/js/tracking/handlers/playertrackerinit.test.d.ts +1 -0
  15. package/lib/js/tracking/handlers/playertrackerinit.test.js +74 -0
  16. package/lib/js/utilities/utilities.element.d.ts +6 -0
  17. package/lib/js/utilities/utilities.element.js +10 -0
  18. package/lib/js/utilities/utilities.element.test.js +18 -5
  19. package/lib/npoplayer.d.ts +13 -3
  20. package/lib/npoplayer.js +43 -28
  21. package/lib/npoplayer.test.js +12 -4
  22. package/lib/package.json +2 -2
  23. package/lib/services/cdnProviders/cdnProviders.js +4 -2
  24. package/lib/services/nicamHandlers/nicamhandler.d.ts +6 -0
  25. package/lib/{ui/handlers → services/nicamHandlers}/nicamhandler.js +14 -6
  26. package/lib/services/nicamHandlers/nicamhandler.test.js +69 -0
  27. package/lib/services/npoPlayerAPI/npoPlayerAPI.d.ts +1 -0
  28. package/lib/services/npoPlayerAPI/npoPlayerAPI.js +6 -3
  29. package/lib/services/segmentHandlers/setSegmentMarkers.js +1 -1
  30. package/lib/services/services.d.ts +3 -0
  31. package/lib/services/services.js +7 -0
  32. package/lib/services/streamoptionsHandlers/steamOptionsHandler.d.ts +6 -0
  33. package/lib/services/streamoptionsHandlers/steamOptionsHandler.js +78 -0
  34. package/lib/services/streamoptionsHandlers/streamOptionsHandler.test.js +187 -0
  35. package/lib/src/js/playeractions/handlers/processplayerconfig.test.d.ts +1 -0
  36. package/lib/src/js/playeractions/handlers/processsourceconfig.d.ts +2 -1
  37. package/lib/src/js/settings/localization.d.ts +76 -1
  38. package/lib/src/js/tracking/handlers/playertrackerinit.d.ts +2 -2
  39. package/lib/src/js/tracking/handlers/playertrackerinit.test.d.ts +1 -0
  40. package/lib/src/js/utilities/utilities.element.d.ts +6 -0
  41. package/lib/src/npoplayer.d.ts +13 -3
  42. package/lib/src/services/nicamHandlers/nicamhandler.d.ts +6 -0
  43. package/lib/src/services/nicamHandlers/nicamhandler.test.d.ts +1 -0
  44. package/lib/src/services/npoPlayerAPI/npoPlayerAPI.d.ts +1 -0
  45. package/lib/src/services/services.d.ts +3 -0
  46. package/lib/src/services/streamoptionsHandlers/steamOptionsHandler.d.ts +6 -0
  47. package/lib/src/services/streamoptionsHandlers/streamOptionsHandler.test.d.ts +1 -0
  48. package/lib/src/types/interfaces.d.ts +2 -1
  49. package/lib/src/ui/components/buttons.d.ts +6 -3
  50. package/lib/src/ui/components/controlbar.d.ts +2 -2
  51. package/lib/src/ui/components/nativemobile/buttons.d.ts +8 -0
  52. package/lib/src/ui/components/playnext.d.ts +1 -0
  53. package/lib/src/ui/components/topbar.d.ts +2 -2
  54. package/lib/src/ui/components/verticalvideo/controlbar.d.ts +2 -2
  55. package/lib/src/ui/handlers/timecontrolhandlers.d.ts +3 -3
  56. package/lib/src/ui/uicontainer.d.ts +2 -3
  57. package/lib/tests/mocks/mockNpoplayer.js +3 -0
  58. package/lib/tests/mocks/playerContextMock.js +1 -0
  59. package/lib/types/interfaces.d.ts +2 -1
  60. package/lib/ui/components/buttons.d.ts +6 -3
  61. package/lib/ui/components/buttons.js +17 -17
  62. package/lib/ui/components/controlbar.d.ts +2 -2
  63. package/lib/ui/components/controlbar.js +15 -10
  64. package/lib/ui/components/nativemobile/buttons.d.ts +8 -0
  65. package/lib/ui/components/nativemobile/buttons.js +41 -1
  66. package/lib/ui/components/nativemobile/controlbar.js +1 -2
  67. package/lib/ui/components/playnext.d.ts +1 -0
  68. package/lib/ui/components/topbar.d.ts +2 -2
  69. package/lib/ui/components/topbar.js +14 -9
  70. package/lib/ui/components/verticalvideo/controlbar.d.ts +2 -2
  71. package/lib/ui/components/verticalvideo/controlbar.js +4 -4
  72. package/lib/ui/handlers/timecontrolhandlers.d.ts +3 -3
  73. package/lib/ui/nativemobileuifactory.js +20 -24
  74. package/lib/ui/nativemobileuifactory.test.js +0 -5
  75. package/lib/ui/uicontainer.d.ts +2 -3
  76. package/lib/ui/uicontainer.js +7 -5
  77. package/package.json +2 -2
  78. package/src/style/components/_icons.scss +5 -0
  79. package/src/style/npoplayer.css +2 -1
  80. package/lib/src/ui/handlers/nicamhandler.d.ts +0 -6
  81. package/lib/src/ui/handlers/streamhandler.d.ts +0 -2
  82. package/lib/ui/handlers/nicamhandler.d.ts +0 -6
  83. package/lib/ui/handlers/nicamhandler.test.js +0 -36
  84. package/lib/ui/handlers/streamhandler.d.ts +0 -2
  85. package/lib/ui/handlers/streamhandler.js +0 -60
  86. /package/lib/{src/ui/handlers → services/nicamHandlers}/nicamhandler.test.d.ts +0 -0
  87. /package/lib/{ui/handlers/nicamhandler.test.d.ts → services/streamoptionsHandlers/streamOptionsHandler.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.24.0
14
+ Current version: v1.25.0
15
15
 
16
16
  The changelog is available at https://docs.npoplayer.nl/implementation/web/changelog/
17
17
 
@@ -2,8 +2,8 @@ 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/steamOptionsHandler', () => ({
6
+ handleStreamOptions: jest.fn()
7
7
  }));
8
8
  describe('getStreamObject', () => {
9
9
  let npoplayer;
@@ -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,5 @@
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 function processSourceConfig(npoPlayerServices: NpoPlayerServices, source: string, _sourceConfig: SourceConfig | undefined, _streamObject: StreamObject, drm: string | undefined, streamOptions: StreamOptions, version: string, npoTagInstance: NPOTag | undefined): Promise<SourceConfig>;
@@ -102,7 +102,7 @@ function setStreamProfile(sourceConfig, _sourceConfig, _streamObject) {
102
102
  }
103
103
  return sourceConfig;
104
104
  }
105
- export async function processSourceConfig(NpoPlayerServices, _sourceConfig = {}, _streamObject, drm = undefined, streamOptions, version, npoTagPartyId, npoPlayerServices) {
105
+ export async function processSourceConfig(npoPlayerServices, source, _sourceConfig = {}, _streamObject, drm = undefined, streamOptions, version, npoTagInstance) {
106
106
  let sourceConfig = {};
107
107
  sourceConfig.title = _sourceConfig.title
108
108
  ? replacePlaceholders(_sourceConfig.title, _streamObject.metadata)
@@ -118,11 +118,11 @@ export async function processSourceConfig(NpoPlayerServices, _sourceConfig = {},
118
118
  };
119
119
  if (streamOptions.fragments || _streamObject.segment) {
120
120
  const segment = streamOptions.fragments
121
- ? NpoPlayerServices.convertFragmentToSegment(streamOptions.fragments)
121
+ ? npoPlayerServices.convertFragmentToSegment(streamOptions.fragments)
122
122
  : _streamObject.segment;
123
123
  if (segment) {
124
124
  ;
125
- sourceConfig.markers = NpoPlayerServices.segmentMarkersHandler(segment, sourceConfig.metadata.title);
125
+ sourceConfig.markers = npoPlayerServices.segmentMarkersHandler(segment, sourceConfig.metadata.title);
126
126
  }
127
127
  }
128
128
  sourceConfig.subtitleTracks =
@@ -140,7 +140,6 @@ export async function processSourceConfig(NpoPlayerServices, _sourceConfig = {},
140
140
  _sourceConfig.thumbnailTrack ?? _streamObject.assets.scrubbingThumbnail
141
141
  ? { url: _streamObject.assets.scrubbingThumbnail }
142
142
  : undefined;
143
- sourceConfig.options = _sourceConfig.options;
144
143
  sourceConfig = setStreamProfile(sourceConfig, _sourceConfig, _streamObject);
145
144
  sourceConfig = await setDrm(sourceConfig, drm, _streamObject, streamOptions.useWidevineServerCertificate ?? true);
146
145
  const streamSource = _streamObject.stream.streamURL;
@@ -162,7 +161,7 @@ export async function processSourceConfig(NpoPlayerServices, _sourceConfig = {},
162
161
  sourceConfig.analytics.title = _streamObject.metadata.title;
163
162
  sourceConfig.analytics.customData2 = _streamObject.user.type;
164
163
  sourceConfig.analytics.customData3 = npoPlayerServices.getAVType(_streamObject.stream.avType);
165
- sourceConfig.analytics.customData4 = npoTagPartyId;
164
+ sourceConfig.analytics.customData4 = npoTagInstance?.getParty();
166
165
  sourceConfig.analytics.customData5 = version;
167
166
  sourceConfig.analytics = {
168
167
  ...sourceConfig.analytics,
@@ -180,6 +179,18 @@ export async function processSourceConfig(NpoPlayerServices, _sourceConfig = {},
180
179
  }
181
180
  };
182
181
  }
182
+ sourceConfig.metadata = {
183
+ ...sourceConfig.metadata
184
+ };
185
+ sourceConfig.metadata.jwt = source;
186
+ sourceConfig.metadata.streamLinkAsJsonString = JSON.stringify(_streamObject);
187
+ sourceConfig.metadata.npoTagSession = String(npoTagInstance?.getSerializedSessionInfo());
188
+ sourceConfig.metadata.hasPreroll = _streamObject.metadata.hasPreroll || 'false';
189
+ sourceConfig.metadata.prerollUrl = _streamObject.assets.preroll || '';
190
+ sourceConfig.metadata.onScreenDebug = 'false';
191
+ sourceConfig.metadata.frameworkLevelDebug = 'false';
192
+ sourceConfig.metadata.description = sourceConfig.description || '';
193
+ sourceConfig.metadata.avType = npoPlayerServices.getAVType(_streamObject.stream.avType);
183
194
  return sourceConfig;
184
195
  }
185
196
  function decidePoster(streamObject, sourceConfig, streamOptions) {
@@ -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;
@@ -1,15 +1,17 @@
1
1
  import { newATInternetPlugin, newGovoltePlugin, newPageTracker, newTag } from '@npotag/tag';
2
2
  const npoTagPlugins = [newGovoltePlugin({ maxRetryCount: 5, delayBetweenRetriesInMs: 3000 }), newATInternetPlugin()];
3
- export function initPlayerTracker(npoplayer, _npotag, _npotaginstance) {
4
- if (npoplayer.npoTag == undefined) {
5
- npoplayer.npoTag = {
3
+ export function initPlayerTracker(npoPlayer, _npoTag, _npoTagInstance, _npoTagPageTracker) {
4
+ if (!npoPlayer.npoTag) {
5
+ npoPlayer.npoTag = {
6
6
  npoTagInstance: undefined,
7
7
  pageTracker: undefined,
8
8
  heartbeatInterval: undefined
9
9
  };
10
10
  }
11
- npoplayer.npoTag.npoTagInstance = _npotag == undefined ? _npotaginstance : newTag(_npotag, npoTagPlugins);
12
- if (npoplayer.npoTag.npoTagInstance != undefined) {
13
- npoplayer.npoTag.pageTracker = newPageTracker(npoplayer.npoTag.npoTagInstance);
11
+ if (!npoPlayer.npoTag?.npoTagInstance) {
12
+ npoPlayer.npoTag.npoTagInstance = _npoTagInstance ?? (_npoTag ? newTag(_npoTag, npoTagPlugins) : undefined);
13
+ }
14
+ if (!npoPlayer.npoTag?.pageTracker && npoPlayer.npoTag?.npoTagInstance) {
15
+ npoPlayer.npoTag.pageTracker = _npoTagPageTracker ?? newPageTracker(npoPlayer.npoTag.npoTagInstance);
14
16
  }
15
17
  }
@@ -0,0 +1,74 @@
1
+ import { newPageTracker, newTag } from '@npotag/tag';
2
+ import { initPlayerTracker } from './playertrackerinit';
3
+ jest.mock('@npotag/tag', () => ({
4
+ newATInternetPlugin: jest.fn(),
5
+ newGovoltePlugin: jest.fn(),
6
+ newPageTracker: jest.fn(),
7
+ newTag: jest.fn()
8
+ }));
9
+ describe('initPlayerTracker', () => {
10
+ let mockNpoPlayer;
11
+ beforeEach(() => {
12
+ mockNpoPlayer = {
13
+ npoTag: undefined
14
+ };
15
+ jest.clearAllMocks();
16
+ });
17
+ it('should initialize npoTag if it is undefined', () => {
18
+ initPlayerTracker(mockNpoPlayer);
19
+ expect(mockNpoPlayer.npoTag).toBeDefined();
20
+ expect(mockNpoPlayer.npoTag?.npoTagInstance).toBeUndefined();
21
+ expect(mockNpoPlayer.npoTag?.pageTracker).toBeUndefined();
22
+ });
23
+ it('should initialize npoTagInstance if _npoTagInstance is provided', () => {
24
+ const mockNpoTagInstance = {};
25
+ initPlayerTracker(mockNpoPlayer, undefined, mockNpoTagInstance);
26
+ expect(mockNpoPlayer.npoTag?.npoTagInstance).toBe(mockNpoTagInstance);
27
+ expect(mockNpoPlayer.npoTag?.pageTracker).toBeUndefined();
28
+ });
29
+ it('should create a new npoTagInstance if _npoTag is provided and _npoTagInstance is undefined', () => {
30
+ const mockNpoTag = {};
31
+ const mockNewTagInstance = {};
32
+ newTag.mockReturnValue(mockNewTagInstance);
33
+ initPlayerTracker(mockNpoPlayer, mockNpoTag);
34
+ expect(newTag).toHaveBeenCalledWith(mockNpoTag, expect.any(Array));
35
+ expect(mockNpoPlayer.npoTag?.npoTagInstance).toBe(mockNewTagInstance);
36
+ expect(mockNpoPlayer.npoTag?.pageTracker).toBeUndefined();
37
+ });
38
+ it('should initialize pageTracker if _npoTagPageTracker is provided', () => {
39
+ const mockPageTracker = {};
40
+ const mockNpoTagInstance = {};
41
+ mockNpoPlayer.npoTag = {
42
+ npoTagInstance: mockNpoTagInstance,
43
+ pageTracker: undefined,
44
+ heartbeatInterval: undefined
45
+ };
46
+ initPlayerTracker(mockNpoPlayer, undefined, mockNpoTagInstance, mockPageTracker);
47
+ expect(mockNpoPlayer.npoTag?.pageTracker).toBe(mockPageTracker);
48
+ });
49
+ it('should create a new pageTracker if _npoTagPageTracker is undefined', () => {
50
+ const mockNpoTagInstance = {};
51
+ const mockNewPageTracker = {};
52
+ newPageTracker.mockReturnValue(mockNewPageTracker);
53
+ mockNpoPlayer.npoTag = {
54
+ npoTagInstance: mockNpoTagInstance,
55
+ pageTracker: undefined,
56
+ heartbeatInterval: undefined
57
+ };
58
+ initPlayerTracker(mockNpoPlayer);
59
+ expect(newPageTracker).toHaveBeenCalledWith(mockNpoTagInstance);
60
+ expect(mockNpoPlayer.npoTag?.pageTracker).toBe(mockNewPageTracker);
61
+ });
62
+ it('should not overwrite existing pageTracker', () => {
63
+ const mockPageTracker = {};
64
+ const mockNpoTagInstance = {};
65
+ mockNpoPlayer.npoTag = {
66
+ npoTagInstance: mockNpoTagInstance,
67
+ pageTracker: mockPageTracker,
68
+ heartbeatInterval: undefined
69
+ };
70
+ initPlayerTracker(mockNpoPlayer);
71
+ expect(mockNpoPlayer.npoTag?.pageTracker).toBe(mockPageTracker);
72
+ expect(newPageTracker).not.toHaveBeenCalled();
73
+ });
74
+ });
@@ -4,7 +4,13 @@ interface ElementByQuery {
4
4
  }
5
5
  interface RemoveClassFromElementByQuery extends ElementByQuery {
6
6
  className: string;
7
+ condition: boolean;
8
+ }
9
+ export interface UpdateClassFromElementByQuery extends ElementByQuery {
10
+ className: string;
11
+ condition: boolean;
7
12
  }
8
13
  export declare function getElementByQuery({ container, query }: ElementByQuery): Element | null;
9
14
  export declare function removeClassFromElementByQuery(params: RemoveClassFromElementByQuery): void;
15
+ export declare function updateClassFromElementByQuery(params: UpdateClassFromElementByQuery): void;
10
16
  export {};
@@ -6,3 +6,13 @@ export function removeClassFromElementByQuery(params) {
6
6
  const element = getElementByQuery({ container, query });
7
7
  element?.classList.remove(className);
8
8
  }
9
+ export function updateClassFromElementByQuery(params) {
10
+ const { className, container, query, condition } = params;
11
+ const element = getElementByQuery({ container, query });
12
+ if (condition && element) {
13
+ element.classList.add(className);
14
+ }
15
+ else if (element) {
16
+ element.classList.remove(className);
17
+ }
18
+ }
@@ -1,5 +1,6 @@
1
- import { getElementByQuery, removeClassFromElementByQuery } from './utilities.element';
1
+ import { getElementByQuery, updateClassFromElementByQuery } from './utilities.element';
2
2
  const CLASS = 'element-class';
3
+ const NEW_CLASS = 'new-class';
3
4
  const ID = 'element-id';
4
5
  const PARENT_ELEMENT = document.createElement('div');
5
6
  const CHILD_ELEMENT_WITH_ID = document.createElement('div');
@@ -13,13 +14,25 @@ describe('getElementByQuery', () => {
13
14
  expect(getElementByQuery({ container: PARENT_ELEMENT, query: `#${ID}` })).toEqual(CHILD_ELEMENT_WITH_ID);
14
15
  });
15
16
  });
16
- describe('removeClassFromElementByQuery', () => {
17
- it('should remove the specified class from from the queried element', () => {
18
- removeClassFromElementByQuery({
17
+ describe('updateClassFromElementByQuery', () => {
18
+ it('should remove the specified class from the queried element', () => {
19
+ updateClassFromElementByQuery({
19
20
  container: PARENT_ELEMENT,
20
21
  query: `.${CLASS}`,
21
- className: CLASS
22
+ className: CLASS,
23
+ condition: false
22
24
  });
23
25
  expect(PARENT_ELEMENT.querySelector(`.${CLASS}`)).toBeNull();
24
26
  });
27
+ it('should add the specified class to the queried element', () => {
28
+ const ELEMENT_WITHOUT_CLASS = document.createElement('div');
29
+ PARENT_ELEMENT.appendChild(ELEMENT_WITHOUT_CLASS);
30
+ updateClassFromElementByQuery({
31
+ container: PARENT_ELEMENT,
32
+ query: 'div:not([class])',
33
+ className: NEW_CLASS,
34
+ condition: true
35
+ });
36
+ expect(PARENT_ELEMENT.querySelector(`.${NEW_CLASS}`)).not.toBeNull();
37
+ });
25
38
  });
@@ -1,5 +1,5 @@
1
1
  import { NpoPlayerServices } from './services/services';
2
- import { type StreamTracker, type NPOTag } from '@npotag/tag';
2
+ import { type StreamTracker, type NPOTag, type PageTracker } from '@npotag/tag';
3
3
  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';
@@ -7,12 +7,15 @@ import { LogEmitter } from './types/classes';
7
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';
8
8
  export { type PlayerConfig, type InitialisationProps, type NPOTagObject, type StreamOptions, NpoPlayerUIVariants };
9
9
  export default class NpoPlayer {
10
- playerConfig: PlayerConfig;
10
+ playerConfig: PlayerConfig | undefined;
11
11
  sourceConfig: SourceConfig;
12
12
  streamObject: StreamObject;
13
13
  player: PlayerAPI | undefined;
14
14
  uiManager: UIManager | undefined;
15
15
  npoTag: NPOTagObject | undefined;
16
+ npoTagInitialisation: InitialisationProps | undefined;
17
+ npoTagInstance: NPOTag | undefined;
18
+ npoTagPageTracker: PageTracker | undefined;
16
19
  streamTracker: StreamTracker | undefined;
17
20
  logEmitter: LogEmitter;
18
21
  uiComponents: UIComponents;
@@ -31,7 +34,14 @@ export default class NpoPlayer {
31
34
  eventListeners: EventListeners | undefined;
32
35
  mockNpoPlayer: any;
33
36
  userPreferences: LocalStorageData;
34
- constructor(_container: HTMLElement, _playerConfig: PlayerConfig, _npotag?: InitialisationProps | undefined, _npotaginstance?: NPOTag | undefined, _variant?: NpoPlayerUIVariants);
37
+ constructor(container: HTMLElement | {
38
+ container: HTMLElement;
39
+ playerConfig: PlayerConfig;
40
+ npoTag?: InitialisationProps | undefined;
41
+ npoTagInstance?: NPOTag | undefined;
42
+ variant?: NpoPlayerUIVariants;
43
+ npoTagPageTracker?: PageTracker | undefined;
44
+ }, playerConfig?: PlayerConfig | undefined, npoTag?: InitialisationProps | undefined, npoTagInstance?: NPOTag | undefined, variant?: NpoPlayerUIVariants, npoTagPageTracker?: PageTracker | undefined);
35
45
  initPlayer(_container: HTMLElement, playerConfig: PlayerConfig): void;
36
46
  loadStream(source: string, options?: StreamOptions): Promise<void>;
37
47
  doError(input: any, status?: number): void;