@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/CHANGELOG.md +12 -4
- package/README.md +10 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.LICENSE.txt +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.LICENSE.txt +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/umd/nrvideo.min.js +1 -1
- package/dist/umd/nrvideo.min.js.LICENSE.txt +1 -1
- package/dist/umd/nrvideo.min.js.map +1 -1
- package/package.json +5 -3
- package/src/constants.js +5 -1
- package/src/core.js +1 -1
- package/src/eventAggregator.js +0 -1
- package/src/recordEvent.js +2 -1
- package/src/videoConfiguration.js +45 -8
- package/src/videotracker.js +19 -10
- package/src/videotrackerstate.js +11 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/video-core",
|
|
3
|
-
"version": "4.1.0
|
|
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": "
|
|
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
package/src/eventAggregator.js
CHANGED
package/src/recordEvent.js
CHANGED
|
@@ -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}
|
|
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.
|
|
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}
|
|
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 };
|
package/src/videotracker.js
CHANGED
|
@@ -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
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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();
|
package/src/videotrackerstate.js
CHANGED
|
@@ -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
|
|
695
|
-
const currentWeightedBitrate = (bitrate *
|
|
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 /
|
|
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 */
|