@npo/player 1.27.5 → 1.27.7

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.
@@ -8,16 +8,23 @@ export function handleStartOffset(playerContext, offset) {
8
8
  return;
9
9
  }
10
10
  const seek = () => {
11
- player.off(NpoPlayerEvent.Ready, seek);
12
- player.off(NpoPlayerEvent.SourceLoaded, seek);
13
- player.off(NpoPlayerEvent.AdBreakFinished, seek);
14
- player.off(NpoPlayerEvent.AdError, seek);
15
11
  player.seek(offset);
16
12
  };
17
- player.on(NpoPlayerEvent.Ready, seek);
18
- player.on(NpoPlayerEvent.SourceLoaded, seek);
19
- player.on(NpoPlayerEvent.AdBreakFinished, seek);
20
- player.on(NpoPlayerEvent.AdError, seek);
13
+ const handleStartOffsetCallbackOnReady = () => seek();
14
+ const handleStartOffsetCallbackOnSourceLoaded = () => seek();
15
+ const handleStartOffsetCallbackOnAdBreakFinished = () => seek();
16
+ const handleStartOffsetCallbackOnAdError = () => seek();
17
+ playerContext.player.on(NpoPlayerEvent.Ready, handleStartOffsetCallbackOnReady);
18
+ playerContext.player.on(NpoPlayerEvent.SourceLoaded, handleStartOffsetCallbackOnSourceLoaded);
19
+ playerContext.player.on(NpoPlayerEvent.AdBreakFinished, handleStartOffsetCallbackOnAdBreakFinished);
20
+ playerContext.player.on(NpoPlayerEvent.AdError, handleStartOffsetCallbackOnAdError);
21
+ playerContext.npoPlayer.eventListeners = {
22
+ ...playerContext.npoPlayer.eventListeners,
23
+ handleStartOffsetCallbackOnReady,
24
+ handleStartOffsetCallbackOnSourceLoaded,
25
+ handleStartOffsetCallbackOnAdBreakFinished,
26
+ handleStartOffsetCallbackOnAdError
27
+ };
21
28
  }
22
29
  export function shiftToProgramStart(playerContext, timestamp) {
23
30
  const { player } = playerContext;
package/lib/npoplayer.js CHANGED
@@ -114,12 +114,12 @@ export default class NpoPlayer {
114
114
  customData5: this.version
115
115
  }
116
116
  };
117
+ this.npoPlayerServices.handleStreamOptions(this.playerContext);
117
118
  void this.playerContext.player.createUIManager(this.playerContext, this.variant);
118
119
  await this.playerContext.player?.load(this.sourceConfig);
119
120
  this.npoPlayerServices.startPlayerTracker({
120
121
  playerContext: this.playerContext,
121
- source: source,
122
- duration: undefined
122
+ source: source
123
123
  });
124
124
  }
125
125
  else if (sourceIsJWTToken) {
@@ -158,6 +158,7 @@ export default class NpoPlayer {
158
158
  return;
159
159
  }
160
160
  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);
161
+ this.npoPlayerServices.handleStreamOptions(this.playerContext);
161
162
  await this.npoPlayerServices.verifyDRM(this.playerContext, payload);
162
163
  const streamDuration = _streamObject.metadata.duration;
163
164
  this.npoPlayerServices.startPlayerTracker({
@@ -169,7 +170,6 @@ export default class NpoPlayer {
169
170
  else {
170
171
  await this.npoPlayerServices.handleError(this.playerContext, 500);
171
172
  }
172
- this.npoPlayerServices.handleStreamOptions(this.playerContext);
173
173
  this.npoPlayerServices.setupNicamKijkwijzerIcons(this.playerContext);
174
174
  setupMediaSessionActionHandlers(this.player, this.sourceConfig, this.streamObject);
175
175
  this.hidePlayNextScreen();
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npo/player",
3
- "version": "1.27.5",
3
+ "version": "1.27.7",
4
4
  "description": "NPO Player",
5
5
  "author": "Publieke Omroep <player@npo.nl>",
6
6
  "contributors": [
@@ -2,13 +2,17 @@ import { NpoPlayerEvent } from '../../types/events';
2
2
  export const removeEventListeners = (playerContext) => {
3
3
  const { eventListeners } = playerContext.npoPlayer;
4
4
  if (eventListeners) {
5
- const { segmentHandleTimeChangedCallback, segmentSeekFunctionCallback, liveStreamHandleTimeChangedCallback, adBreakFinishedCallback, adErrorCallback } = eventListeners;
5
+ const { segmentHandleTimeChangedCallback, segmentSeekFunctionCallback, liveStreamHandleTimeChangedCallback, adBreakFinishedCallback, adErrorCallback, handleStartOffsetCallbackOnReady, handleStartOffsetCallbackOnSourceLoaded, handleStartOffsetCallbackOnAdBreakFinished, handleStartOffsetCallbackOnAdError } = eventListeners;
6
6
  const eventMapping = [
7
7
  { event: NpoPlayerEvent.TimeChanged, callback: liveStreamHandleTimeChangedCallback },
8
8
  { event: NpoPlayerEvent.TimeChanged, callback: segmentHandleTimeChangedCallback },
9
9
  { event: NpoPlayerEvent.Seek, callback: segmentSeekFunctionCallback },
10
10
  { event: NpoPlayerEvent.AdBreakFinished, callback: adBreakFinishedCallback },
11
- { event: NpoPlayerEvent.AdError, callback: adErrorCallback }
11
+ { event: NpoPlayerEvent.AdError, callback: adErrorCallback },
12
+ { event: NpoPlayerEvent.Ready, callback: handleStartOffsetCallbackOnReady },
13
+ { event: NpoPlayerEvent.SourceLoaded, callback: handleStartOffsetCallbackOnSourceLoaded },
14
+ { event: NpoPlayerEvent.AdBreakFinished, callback: handleStartOffsetCallbackOnAdBreakFinished },
15
+ { event: NpoPlayerEvent.AdError, callback: handleStartOffsetCallbackOnAdError }
12
16
  ];
13
17
  for (const { event, callback } of eventMapping) {
14
18
  if (callback) {
@@ -7,36 +7,30 @@ describe('removeEventListeners', () => {
7
7
  jest.clearAllMocks();
8
8
  mockPlayerContext = createPlayerContextMock();
9
9
  });
10
- it('should remove TimeChanged event listener if exists', () => {
11
- mockPlayerContext.npoPlayer.eventListeners = {
10
+ it('should remove all event listeners if they exist', () => {
11
+ const eventListeners = {
12
12
  segmentHandleTimeChangedCallback: jest.fn(),
13
13
  liveStreamHandleTimeChangedCallback: jest.fn(),
14
- segmentSeekFunctionCallback: jest.fn()
15
- };
16
- removeEventListeners(mockPlayerContext);
17
- if (mockPlayerContext.npoPlayer.eventListeners) {
18
- expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.TimeChanged, mockPlayerContext.npoPlayer.eventListeners.segmentHandleTimeChangedCallback);
19
- expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.TimeChanged, mockPlayerContext.npoPlayer.eventListeners.liveStreamHandleTimeChangedCallback);
20
- }
21
- });
22
- it('should remove Seek event listener if exists', () => {
23
- mockPlayerContext.npoPlayer.eventListeners = {
24
- segmentHandleTimeChangedCallback: jest.fn(),
25
- liveStreamHandleTimeChangedCallback: jest.fn(),
26
- segmentSeekFunctionCallback: jest.fn()
27
- };
28
- removeEventListeners(mockPlayerContext);
29
- if (mockPlayerContext.npoPlayer.eventListeners) {
30
- expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.Seek, mockPlayerContext.npoPlayer.eventListeners.segmentSeekFunctionCallback);
31
- }
32
- });
33
- it('should reset EventListeners in playerContext', () => {
34
- mockPlayerContext.npoPlayer.eventListeners = {
35
- segmentHandleTimeChangedCallback: jest.fn(),
36
- liveStreamHandleTimeChangedCallback: jest.fn(),
37
- segmentSeekFunctionCallback: jest.fn()
14
+ segmentSeekFunctionCallback: jest.fn(),
15
+ adBreakFinishedCallback: jest.fn(),
16
+ adErrorCallback: jest.fn(),
17
+ handleStartOffsetCallbackOnReady: jest.fn(),
18
+ handleStartOffsetCallbackOnSourceLoaded: jest.fn(),
19
+ handleStartOffsetCallbackOnAdBreakFinished: jest.fn(),
20
+ handleStartOffsetCallbackOnAdError: jest.fn()
38
21
  };
22
+ mockPlayerContext.npoPlayer.eventListeners = { ...eventListeners };
39
23
  removeEventListeners(mockPlayerContext);
24
+ expect(mockPlayerContext.player.off).toHaveBeenCalledTimes(9);
25
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.TimeChanged, eventListeners.liveStreamHandleTimeChangedCallback);
26
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.TimeChanged, eventListeners.segmentHandleTimeChangedCallback);
27
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.Seek, eventListeners.segmentSeekFunctionCallback);
28
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.AdBreakFinished, eventListeners.adBreakFinishedCallback);
29
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.AdError, eventListeners.adErrorCallback);
30
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.Ready, eventListeners.handleStartOffsetCallbackOnReady);
31
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.SourceLoaded, eventListeners.handleStartOffsetCallbackOnSourceLoaded);
32
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.AdBreakFinished, eventListeners.handleStartOffsetCallbackOnAdBreakFinished);
33
+ expect(mockPlayerContext.player.off).toHaveBeenCalledWith(NpoPlayerEvent.AdError, eventListeners.handleStartOffsetCallbackOnAdError);
40
34
  expect(mockPlayerContext.npoPlayer.eventListeners).toBeUndefined();
41
35
  });
42
36
  });
@@ -172,6 +172,7 @@ export class NpoPlayerAPI {
172
172
  playerContext.npoPlayer.uiManager = uiManager;
173
173
  playerContext.npoPlayer.variant = variant;
174
174
  npoPlayerServices.addUivisiblityHandlers(playerContext);
175
+ npoPlayerServices.setupNicamKijkwijzerIcons(playerContext);
175
176
  npoPlayerServices.showNicamAfterUiDelay(playerContext);
176
177
  }
177
178
  }
@@ -70,7 +70,8 @@ export function setupAutoplay(playerContext) {
70
70
  };
71
71
  const doAutoPlay = async () => {
72
72
  player.off(NpoPlayerEvent.Ready, handleAutoPlay);
73
+ player.off(NpoPlayerEvent.SourceLoaded, handleAutoPlay);
73
74
  await player.play();
74
75
  };
75
- player.on(NpoPlayerEvent.Ready, handleAutoPlay);
76
+ player.on(NpoPlayerEvent.SourceLoaded, handleAutoPlay);
76
77
  }
@@ -178,7 +178,7 @@ describe('handleStreamOptions', () => {
178
178
  player: mockNpoPlayerAPI
179
179
  });
180
180
  setupAutoplay(playerContextMock);
181
- expect(playerContextMock.player.on).toHaveBeenCalledWith(NpoPlayerEvent.Ready, expect.any(Function));
181
+ expect(playerContextMock.player.on).toHaveBeenCalledWith(NpoPlayerEvent.SourceLoaded, expect.any(Function));
182
182
  const handleAutoPlay = playerContextMock.player.on.mock.calls[0][1];
183
183
  await handleAutoPlay();
184
184
  expect(playerContextMock.player.play).toHaveBeenCalled();
@@ -17,7 +17,7 @@ export const startPlayerTracker = function ({ playerContext, source, duration })
17
17
  }) || 'unknown';
18
18
  npoPlayer.streamTracker = initStreamTracker({
19
19
  playerContext: playerContext,
20
- duration: getStreamDurationInSeconds({ duration: playerDuration }),
20
+ duration: getStreamDurationInSeconds({ duration: playerDuration, durationIsInMs: !!duration }),
21
21
  source: analyticsPrid
22
22
  });
23
23
  bindPlayerEvents(playerContext);
@@ -22,31 +22,32 @@ jest.mock('./eventLogging', () => ({
22
22
  }));
23
23
  describe('startPlayerTracker', () => {
24
24
  let playerContextMock;
25
+ let playerMock;
25
26
  beforeEach(() => {
27
+ playerMock = { getDuration: jest.fn().mockReturnValue(200) };
26
28
  playerContextMock = createPlayerContextMock({
27
29
  npoPlayer: {
28
- npoTag: undefined,
29
- npoTagInitialisation: undefined,
30
- npoTagInstance: undefined,
31
- npoTagPageTracker: undefined
30
+ player: playerMock,
31
+ sourceConfig: {},
32
+ streamObject: {}
32
33
  }
33
34
  });
34
35
  jest.clearAllMocks();
35
36
  });
36
- it('should initialize the stream tracker with correct parameters', () => {
37
+ it('should initialize the stream tracker with the provided duration', () => {
37
38
  const source = 'some-source';
38
39
  const duration = 150;
39
40
  getAnalyticsPrid.mockReturnValue('some-prid');
40
41
  getStreamDurationInSeconds.mockReturnValue(duration);
41
- startPlayerTracker({
42
- playerContext: playerContextMock,
43
- source,
44
- duration
45
- });
42
+ startPlayerTracker({ playerContext: playerContextMock, source, duration });
46
43
  expect(getAnalyticsPrid).toHaveBeenCalledWith({
47
44
  source,
48
- sourceConfig: undefined,
49
- streamObject: undefined
45
+ sourceConfig: {},
46
+ streamObject: {}
47
+ });
48
+ expect(getStreamDurationInSeconds).toHaveBeenCalledWith({
49
+ duration,
50
+ durationIsInMs: true
50
51
  });
51
52
  expect(initStreamTracker).toHaveBeenCalledWith({
52
53
  playerContext: playerContextMock,
@@ -56,4 +57,20 @@ describe('startPlayerTracker', () => {
56
57
  expect(bindPlayerEvents).toHaveBeenCalledWith(playerContextMock);
57
58
  expect(logEvent).toHaveBeenCalledWith({ playerContext: playerContextMock, event: 'load' });
58
59
  });
60
+ it('should fallback to player.getDuration() if duration is not provided', () => {
61
+ const source = 'some-source';
62
+ const fallbackDuration = 200;
63
+ getAnalyticsPrid.mockReturnValue('some-prid');
64
+ getStreamDurationInSeconds.mockReturnValue(fallbackDuration);
65
+ startPlayerTracker({ playerContext: playerContextMock, source });
66
+ expect(getStreamDurationInSeconds).toHaveBeenCalledWith({
67
+ duration: fallbackDuration,
68
+ durationIsInMs: false
69
+ });
70
+ expect(initStreamTracker).toHaveBeenCalledWith({
71
+ playerContext: playerContextMock,
72
+ duration: fallbackDuration,
73
+ source: 'some-prid'
74
+ });
75
+ });
59
76
  });
@@ -21,6 +21,10 @@ export interface EventListeners {
21
21
  segmentSeekFunctionCallback?: NpoPlayerEventCallback;
22
22
  adBreakFinishedCallback?: NpoPlayerEventCallback;
23
23
  adErrorCallback?: NpoPlayerEventCallback;
24
+ handleStartOffsetCallbackOnReady?: NpoPlayerEventCallback;
25
+ handleStartOffsetCallbackOnSourceLoaded?: NpoPlayerEventCallback;
26
+ handleStartOffsetCallbackOnAdBreakFinished?: NpoPlayerEventCallback;
27
+ handleStartOffsetCallbackOnAdError?: NpoPlayerEventCallback;
24
28
  }
25
29
  export interface StreamObject {
26
30
  stream: StreamObject_Stream;
@@ -230,8 +234,8 @@ export type LocalStorageData = Partial<Record<LocalStorageValues, string | numbe
230
234
  export type NPOGoogleCastRemoteControlConfig = GoogleCastRemoteControlConfig;
231
235
  export interface PlayerTrackerParams {
232
236
  playerContext: PlayerContext;
233
- duration: number | undefined;
234
- source: string | undefined;
237
+ duration?: number;
238
+ source?: string;
235
239
  }
236
240
  export interface LogEventParams {
237
241
  playerContext: PlayerContext;
@@ -21,6 +21,10 @@ export interface EventListeners {
21
21
  segmentSeekFunctionCallback?: NpoPlayerEventCallback;
22
22
  adBreakFinishedCallback?: NpoPlayerEventCallback;
23
23
  adErrorCallback?: NpoPlayerEventCallback;
24
+ handleStartOffsetCallbackOnReady?: NpoPlayerEventCallback;
25
+ handleStartOffsetCallbackOnSourceLoaded?: NpoPlayerEventCallback;
26
+ handleStartOffsetCallbackOnAdBreakFinished?: NpoPlayerEventCallback;
27
+ handleStartOffsetCallbackOnAdError?: NpoPlayerEventCallback;
24
28
  }
25
29
  export interface StreamObject {
26
30
  stream: StreamObject_Stream;
@@ -230,8 +234,8 @@ export type LocalStorageData = Partial<Record<LocalStorageValues, string | numbe
230
234
  export type NPOGoogleCastRemoteControlConfig = GoogleCastRemoteControlConfig;
231
235
  export interface PlayerTrackerParams {
232
236
  playerContext: PlayerContext;
233
- duration: number | undefined;
234
- source: string | undefined;
237
+ duration?: number;
238
+ source?: string;
235
239
  }
236
240
  export interface LogEventParams {
237
241
  playerContext: PlayerContext;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npo/player",
3
- "version": "1.27.5",
3
+ "version": "1.27.7",
4
4
  "description": "NPO Player",
5
5
  "author": "Publieke Omroep <player@npo.nl>",
6
6
  "contributors": [