@newrelic/video-core 4.1.0-beta → 4.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/video-core",
3
- "version": "4.1.0-beta",
3
+ "version": "4.1.0",
4
4
  "description": "New Relic video tracking core library",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -10,7 +10,7 @@
10
10
  "watch": "webpack --mode production --progress --color --watch",
11
11
  "watch:dev": "webpack --progress --color --watch",
12
12
  "clean": "rm -rf dist coverage doc",
13
- "test": "nyc --reporter=html --reporter=text mocha --require @babel/register",
13
+ "test": "jest --coverage",
14
14
  "doc": "jsdoc -c jsdoc.json -d documentation",
15
15
  "deploy": "node scripts/deploy.js",
16
16
  "third-party-updates": "oss third-party manifest --includeOptDeps && oss third-party notices --includeOptDeps && git add THIRD_PARTY_NOTICES.md third_party_manifest.json"
@@ -33,9 +33,11 @@
33
33
  "@babel/preset-env": "^7.24.5",
34
34
  "@babel/register": "^7.24.6",
35
35
  "aws-sdk": "^2.920.0",
36
+ "babel-jest": "^30.2.0",
36
37
  "babel-loader": "^9.1.3",
37
- "chai": "^4.3.4",
38
38
  "diff": "^5.0.0",
39
+ "jest": "^30.2.0",
40
+ "jest-environment-jsdom": "^30.2.0",
39
41
  "jsdom": "^25.0.1",
40
42
  "mocha": "^10.4.0",
41
43
  "nyc": "^15.1.0",
package/src/constants.js CHANGED
@@ -45,7 +45,11 @@ Constants.INTERVAL = 10000; //10 seconds
45
45
  Constants.QOE_AGGREGATE_KEYS = [
46
46
  "coreVersion", "instrumentation.name",
47
47
  "instrumentation.provider", "instrumentation.version", "isBackgroundEvent", "playerName", "playerVersion",
48
- "src", "viewId", "viewSession", "contentIsAutoplayed"
48
+ "src", "viewId", "viewSession", "contentIsAutoplayed", "contentIsMuted", "contentRenditionHeight", "contentRenditionWidth",
49
+ "contentSrc", "numberOfVideos", "pageUrl", "trackerName", "trackerVersion", "contentDuration", "contentPlayrate", "contentPlayhead",
50
+ "contentPreload", "elapsedTime", "contentTitle", "contentId", "contentIsLive", "deviceType", "deviceGroup", "deviceManufacturer",
51
+ "deviceModel", "deviceName", "deviceSize", "deviceUuid", "contentRenditionName", "contentIsFullscreen", "contentCdn",
52
+ "contentFps", "asnOrganization", "asnLongitude", "asnLatitude", "asn", "timeSinceRequested", "timeSinceStarted"
49
53
  ]
50
54
 
51
55
  export default Constants;
package/src/core.js CHANGED
@@ -16,7 +16,7 @@ class Core {
16
16
  static addTracker(tracker, options) {
17
17
  // Set video analytics configuration
18
18
  if (options?.info) {
19
- setVideoConfig(options.info);
19
+ setVideoConfig(options.info, options?.config);
20
20
  }
21
21
 
22
22
  if (tracker.on && tracker.emit) {
@@ -53,7 +53,6 @@ export class NrVideoEventAggregator {
53
53
  Log.error("Failed to set or replace the event to buffer:", error.message);
54
54
  return false;
55
55
  }
56
- return false;
57
56
  }
58
57
 
59
58
  /**
@@ -47,6 +47,7 @@ export function recordEvent(eventType, attributes = {}) {
47
47
  qoeEventObject = {
48
48
  eventType: "VideoAction",
49
49
  actionName: Tracker.Events.QOE_AGGREGATE,
50
+ qoeAggregateVersion: '1.0.0',
50
51
  ...qoeAttrs,
51
52
  ...metadataAttributes,
52
53
  ...otherAttrs,
@@ -56,7 +57,7 @@ export function recordEvent(eventType, attributes = {}) {
56
57
  // Send to video analytics harvester
57
58
  const success = videoAnalyticsHarvester.addEvent(eventObject);
58
59
 
59
- if(qoeEventObject) {
60
+ if(qoeEventObject && window?.NRVIDEO?.config?.qoeAggregate) {
60
61
  const successQoe = videoAnalyticsHarvester.addEvent(qoeEventObject);
61
62
  return success && successQoe;
62
63
  }
@@ -10,12 +10,19 @@ const { COLLECTOR } = Constants;
10
10
  class VideoConfiguration {
11
11
  /**
12
12
  * Validates and sets the video analytics configuration.
13
- * @param {object} userConfig - User provided configuration
13
+ * @param {object} userInfo - User provided configuration
14
+ * @param {object} [config] - Optional configuration object
14
15
  * @returns {boolean} True if configuration is valid and set
15
16
  */
16
17
 
17
- setConfiguration(userInfo) {
18
- this.initializeGlobalConfig(userInfo);
18
+ setConfiguration(userInfo, config) {
19
+ if (!this.validateRequiredFields(userInfo)) {
20
+ return false;
21
+ }
22
+ if (!this.validateConfigFields(config)) {
23
+ return false;
24
+ }
25
+ this.initializeGlobalConfig(userInfo, config);
19
26
  Log.notice("Video analytics configuration initialized successfully");
20
27
  return true;
21
28
  }
@@ -70,11 +77,37 @@ class VideoConfiguration {
70
77
  return true;
71
78
  }
72
79
 
80
+ /**
81
+ * Validates optional config fields.
82
+ * @param {object} config - Config to validate
83
+ * @returns {boolean} True if valid
84
+ */
85
+ validateConfigFields(config) {
86
+ if (config === null || config === undefined) {
87
+ return true;
88
+ }
89
+
90
+ if (typeof config !== "object" || Array.isArray(config)) {
91
+ Log.error("config must be an object");
92
+ return false;
93
+ }
94
+
95
+ const { qoeAggregate } = config;
96
+
97
+ if (qoeAggregate !== undefined && typeof qoeAggregate !== "boolean") {
98
+ Log.error("qoeAggregate must be a boolean");
99
+ return false;
100
+ }
101
+
102
+ return true;
103
+ }
104
+
73
105
  /**
74
106
  * Initializes the global NRVIDEO configuration object.
107
+ * @param {object} userInfo - User provided configuration
108
+ * @param {object} [config] - Optional configuration object
75
109
  */
76
- initializeGlobalConfig(userInfo) {
77
- if (!this.validateRequiredFields(userInfo)) return;
110
+ initializeGlobalConfig(userInfo, config) {
78
111
 
79
112
  let { licenseKey, appName, region, beacon, applicationID } = userInfo;
80
113
 
@@ -93,6 +126,9 @@ class VideoConfiguration {
93
126
  applicationID,
94
127
  ...(applicationID ? {} : { appName }), // Only include appName when no applicationID
95
128
  },
129
+ config: {
130
+ qoeAggregate: config?.qoeAggregate ?? false,
131
+ }
96
132
  };
97
133
  }
98
134
  }
@@ -102,11 +138,12 @@ const videoConfiguration = new VideoConfiguration();
102
138
 
103
139
  /**
104
140
  * Sets the video analytics configuration.
105
- * @param {object} config - Configuration object
141
+ * @param {object} info - Info configuration object
142
+ * @param {object} [config] - Optional configuration object
106
143
  * @returns {boolean} True if configuration was set successfully
107
144
  */
108
- export function setVideoConfig(info) {
109
- return videoConfiguration.setConfiguration(info);
145
+ export function setVideoConfig(info, config) {
146
+ return videoConfiguration.setConfiguration(info, config);
110
147
  }
111
148
 
112
149
  export { videoConfiguration };
@@ -465,12 +465,17 @@ class VideoTracker extends Tracker {
465
465
  att.adTitle = this.getTitle();
466
466
  att.adSrc = this.getSrc();
467
467
  att.adCdn = this.getCdn();
468
- att.adBitrate =
469
- this.getBitrate() ||
470
- this.getWebkitBitrate() ||
471
- this.getRenditionBitrate();
468
+
469
+ // Only add bitrate attributes after ad has started
470
+ if (this.state.isStarted) {
471
+ att.adBitrate =
472
+ this.getBitrate() ||
473
+ this.getWebkitBitrate() ||
474
+ this.getRenditionBitrate();
475
+ att.adRenditionBitrate = this.getRenditionBitrate();
476
+ }
477
+
472
478
  att.adRenditionName = this.getRenditionName();
473
- att.adRenditionBitrate = this.getRenditionBitrate();
474
479
  att.adRenditionHeight = this.getRenditionHeight();
475
480
  att.adRenditionWidth = this.getRenditionWidth();
476
481
  att.adDuration = this.getDuration();
@@ -492,13 +497,17 @@ class VideoTracker extends Tracker {
492
497
  att.contentPlayhead = this.getPlayhead();
493
498
 
494
499
  att.contentIsLive = this.isLive();
495
- att.contentBitrate =
496
- this.getBitrate() ||
497
- this.getWebkitBitrate() ||
498
- this.getRenditionBitrate();
500
+
501
+ // Only add bitrate attributes after content has started
502
+ if (this.state.isStarted) {
503
+ att.contentBitrate =
504
+ this.getBitrate() ||
505
+ this.getWebkitBitrate() ||
506
+ this.getRenditionBitrate();
507
+ att.contentRenditionBitrate = this.getRenditionBitrate();
508
+ }
499
509
 
500
510
  att.contentRenditionName = this.getRenditionName();
501
- att.contentRenditionBitrate = this.getRenditionBitrate();
502
511
  att.contentRenditionHeight = this.getRenditionHeight();
503
512
  att.contentRenditionWidth = this.getRenditionWidth();
504
513
  att.contentDuration = this.getDuration();
@@ -93,6 +93,11 @@ class VideoTrackerState {
93
93
  */
94
94
  this._lastBitrate = null;
95
95
 
96
+ /**
97
+ * Tracks the last updated timestamp for bitrate
98
+ * */
99
+ this._lastBitrateChangeTimestamp = null;
100
+
96
101
  /**
97
102
  * total bitrate partial value for average weighted average bitrate
98
103
  */
@@ -358,6 +363,7 @@ class VideoTrackerState {
358
363
  : 0;
359
364
  kpi["totalPlaytime"] = this.totalPlaytime;
360
365
  kpi["averageBitrate"] = this.weightedBitrate;
366
+ kpi["numberOfErrors"] = this.numberOfErrors;
361
367
  } catch (error) {
362
368
  Log.error("Failed to add attributes for QOE KPIs", error.message);
363
369
  }
@@ -691,11 +697,12 @@ class VideoTrackerState {
691
697
  this.peakBitrate = Math.max(this.peakBitrate, bitrate);
692
698
 
693
699
  if(this._lastBitrate === null || this._lastBitrate !== bitrate) {
694
- const totalPlaytime = this.timeSinceLastRenditionChange.getDeltaTime() || this.totalPlaytime;
695
- const currentWeightedBitrate = (bitrate * totalPlaytime);
700
+ const deltaPlaytime = this._lastBitrateChangeTimestamp === null ? this.totalPlaytime : Date.now() - this._lastBitrateChangeTimestamp;
701
+ const currentWeightedBitrate = (bitrate * deltaPlaytime);
696
702
  this.partialAverageBitrate += currentWeightedBitrate;
697
- this.weightedBitrate = currentWeightedBitrate / totalPlaytime;
703
+ this.weightedBitrate = currentWeightedBitrate / deltaPlaytime;
698
704
  this._lastBitrate = bitrate;
705
+ this._lastBitrateChangeTimestamp = Date.now();
699
706
  }
700
707
  }
701
708
  }
@@ -708,6 +715,7 @@ class VideoTrackerState {
708
715
  this.partialAverageBitrate = 0;
709
716
  this.startupTime = null;
710
717
  this._lastBitrate = null;
718
+ this._lastBitrateChangeTimestamp = null;
711
719
  }
712
720
 
713
721
  /** Methods to manage total ads time chrono */