@newrelic/video-core 4.1.5 → 4.1.6

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 CHANGED
@@ -1,6 +1,13 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [4.1.6] - 2026/04/16
5
+
6
+ ### Chore
7
+
8
+ - **README.md overhaul**
9
+ Rewrote `README.md` with comprehensive documentation including table of contents, installation instructions, quick start guide, configuration reference, exposed API details, custom tracker guide, getter methods reference, QoE section, obfuscation rules, build & development instructions, and distribution formats.
10
+
4
11
  ## [4.1.5] - 2026/04/08
5
12
 
6
13
  ### Changed
package/README.md CHANGED
@@ -1,159 +1,557 @@
1
- [![Community Project header](https://github.com/newrelic/open-source-office/raw/master/examples/categories/images/Community_Project.png)](https://github.com/newrelic/open-source-office/blob/master/examples/categories/index.md#community-project)
2
-
3
1
  # New Relic Video Core - JavaScript
4
2
 
5
- The New Relic video tracking core library is the base for all video trackers in the browser platform. It contains the classes and core mechanisms used by the player specific trackers.
6
- It segregates the events into different event types based on action, such as video-related events going to `VideoAction`, ad-related events to `VideoAdAction`, errors to `VideoErrorAction`, and custom actions to `VideoCustomAction`.
3
+ [![npm version](https://img.shields.io/npm/v/@newrelic/video-core.svg)](https://www.npmjs.com/package/@newrelic/video-core)
4
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
5
+
6
+ The **New Relic Video Core** library (`@newrelic/video-core`) is the foundational framework for all browser-based video trackers in the New Relic ecosystem. It provides the core classes, state management, event harvesting, and data transmission pipeline that player-specific trackers extend.
7
+
8
+ Events are categorized into four distinct types:
9
+
10
+ | Event Type | Description |
11
+ |---|---|
12
+ | `VideoAction` | Content playback events (play, pause, seek, buffer, etc.) |
13
+ | `VideoAdAction` | Ad-related events (ad start, end, quartile, break, etc.) |
14
+ | `VideoErrorAction` | Error events (content errors, ad errors, crashes) |
15
+ | `VideoCustomAction` | Custom events defined by the integrator |
16
+
17
+ ---
18
+
19
+ ## Table of Contents
20
+
21
+ - [Installation](#installation)
22
+ - [Quick Start](#quick-start)
23
+ - [Configuration](#configuration)
24
+ - [Info Object (Required)](#info-object-required)
25
+ - [Config Object (Optional)](#config-object-optional)
26
+ - [Exposed API](#exposed-api)
27
+ - [Core](#core)
28
+ - [VideoTracker](#videotracker)
29
+ - [Tracker](#tracker)
30
+ - [Emitter](#emitter)
31
+ - [VideoTrackerState](#videotrackerstate)
32
+ - [Chrono](#chrono)
33
+ - [Log](#log)
34
+ - [Constants](#constants)
35
+ - [Building a Custom Tracker](#building-a-custom-tracker)
36
+ - [Tracker Methods Reference](#tracker-methods-reference)
37
+ - [Getter Methods (Override These)](#getter-methods-override-these)
38
+ - [Quality of Experience (QoE)](#quality-of-experience-qoe)
39
+ - [Obfuscation Rules](#obfuscation-rules)
40
+ - [Build & Development](#build--development)
41
+ - [Distribution Formats](#distribution-formats)
42
+ - [Testing](#testing)
43
+ - [Data Model](#data-model)
44
+ - [License](#license)
45
+
46
+ ---
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ npm install @newrelic/video-core
52
+ ```
7
53
 
8
- ## Registering Trackers
54
+ Or include directly via UMD:
9
55
 
10
- Any browser-based video tracker can extend the `VideoTracker` class and use its core functionality.
56
+ ```html
57
+ <script src="dist/umd/nrvideo.min.js"></script>
58
+ ```
11
59
 
12
- To initialize a tracker, create an instance of your specific tracker class:
60
+ ## Quick Start
13
61
 
14
62
  ```javascript
63
+ import nrvideo from '@newrelic/video-core';
64
+
65
+ // 1. Define a custom tracker by extending VideoTracker
66
+ class MyPlayerTracker extends nrvideo.VideoTracker {
67
+ getTrackerName() { return 'my-player'; }
68
+ getSrc() { return this.player.currentSrc; }
69
+ getDuration() { return this.player.duration; }
70
+ getPlayhead() { return this.player.currentTime; }
71
+ getRenditionWidth() { return this.player.videoWidth; }
72
+ getRenditionHeight() { return this.player.videoHeight; }
73
+
74
+ registerListeners() {
75
+ this.player.addEventListener('play', () => this.sendRequest());
76
+ this.player.addEventListener('playing', () => this.sendStart());
77
+ this.player.addEventListener('pause', () => this.sendPause());
78
+ this.player.addEventListener('ended', () => this.sendEnd());
79
+ this.player.addEventListener('waiting', () => this.sendBufferStart());
80
+ this.player.addEventListener('seeking', () => this.sendSeekStart());
81
+ this.player.addEventListener('seeked', () => this.sendSeekEnd());
82
+ this.player.addEventListener('error', () => this.sendError({
83
+ errorName: this.player.error?.message,
84
+ errorCode: this.player.error?.code
85
+ }));
86
+ }
87
+
88
+ unregisterListeners() {
89
+ // Remove all listeners added in registerListeners
90
+ }
91
+ }
92
+
93
+ // 2. Configure and register the tracker
15
94
  const options = {
16
95
  info: {
17
- licenseKey: "xxxxxxxxxxx",
18
- beacon: "xxxxxxxxxx",
19
- applicationId: "xxxxxxx",
96
+ licenseKey: 'YOUR_LICENSE_KEY',
97
+ beacon: 'bam.nr-data.net',
98
+ applicationID: 'YOUR_APPLICATION_ID',
20
99
  },
21
100
  config: {
22
- qoeAggregate: false, // Optional: Enable/disable QoE (Quality of Experience) event aggregation (default: false)
23
- qoeIntervalFactor: 2, // Optional: Include QoE aggregate events once every N harvest cycles; must be a whole number (e.g. 2, not 2.1) (default: 1)
101
+ qoeAggregate: true,
24
102
  },
25
103
  };
26
104
 
27
- // User can get the `info` object by completing the onboarding process on New Relic.
28
- const tracker = new VideoSpecificTracker(player, options);
105
+ const player = document.getElementById('myPlayer');
106
+ const tracker = new MyPlayerTracker(player, options);
107
+
108
+ // 3. Add tracker to Core to begin reporting
109
+ nrvideo.Core.addTracker(tracker, options);
29
110
  ```
30
111
 
31
- ### Configuration Options
112
+ ## Configuration
32
113
 
33
- - **qoeAggregate** (boolean, optional, default: `false`): Controls whether Quality of Experience (QoE) events are aggregated and sent to New Relic. Set to `true` if you want to enable QoE event collection.
114
+ The `options` object passed to `Core.addTracker()` consists of two parts:
34
115
 
35
- - **qoeIntervalFactor** (number, optional, default: `1`): Controls how frequently QoE aggregate events are included in harvest cycles. A value of `1` includes them on every cycle; a value of `N` includes them once every N cycles. Must be a positive integer — invalid values default to `1`. QoE events are always included on the first and final harvest cycles regardless of this setting.
116
+ ### Getting Your Configuration
36
117
 
37
- - **obfuscate** (array, optional): A list of regex-based obfuscation rules applied to event payloads before they are sent to the New Relic collector. See [Obfuscation Rules](#obfuscation-rules) below.
118
+ Before initializing the tracker, obtain your New Relic configuration:
38
119
 
120
+ 1. Log in to [one.newrelic.com](https://one.newrelic.com)
121
+ 2. Navigate to the video agent onboarding flow
122
+ 3. Copy your credentials: `licenseKey`, `beacon`, and `applicationId`
39
123
 
40
- ## Obfuscation Rules
124
+ ### Info Object (Required)
125
+
126
+ Obtained through the New Relic onboarding process. Two configuration modes are supported:
127
+
128
+ **Mode 1 — With Application ID and Beacon:**
41
129
 
42
- Video trackers can capture rich telemetry (content URLs, titles, ad metadata, etc.) that may inadvertently contain PII or sensitive tokens. The `obfuscate` config option lets you define regex-based rules that mask sensitive data in the serialized event payload **before** it is sent to the New Relic collector.
130
+ ```javascript
131
+ info: {
132
+ licenseKey: 'YOUR_LICENSE_KEY', // Required
133
+ applicationID: 'YOUR_APPLICATION_ID', // Required
134
+ beacon: 'bam.nr-data.net', // Required when applicationID is provided
135
+ }
136
+ ```
43
137
 
44
- This matches the approach used by the New Relic Browser Agent.
138
+ **Mode 2 With App Name and Region:**
45
139
 
46
- ### Configuration
140
+ ```javascript
141
+ info: {
142
+ licenseKey: 'YOUR_LICENSE_KEY', // Required
143
+ appName: 'My Video App', // Required when no applicationID
144
+ region: 'US', // Required when no applicationID
145
+ }
146
+ ```
47
147
 
48
- Pass an `obfuscate` array in the `config` parameter of `setVideoConfig` (or the tracker options). Each rule must have:
148
+ **Valid Beacon Endpoints:**
49
149
 
50
- | Field | Type | Description |
51
- |-------|------|-------------|
52
- | `regex` | `string` \| `RegExp` | Pattern to match in the serialized payload |
53
- | `replacement` | `string` | String to replace each match with |
150
+ | Region | Beacon |
151
+ |---|---|
152
+ | US | `bam.nr-data.net`, `bam-cell.nr-data.net` |
153
+ | EU | `bam.eu01.nr-data.net` |
154
+ | Staging | `staging-bam-cell.nr-data.net` |
155
+ | GOV | `gov-bam.nr-data.net` |
156
+
157
+ ### Config Object (Optional)
158
+
159
+ | Option | Type | Default | Description |
160
+ |---|---|---|---|
161
+ | `qoeAggregate` | `boolean` | `false` | Enable Quality of Experience event aggregation. |
162
+ | `qoeIntervalFactor` | `number` | `1` | Include QoE aggregate events once every N harvest cycles. Must be a positive integer. QoE events are always sent on the first and final harvest cycles. |
163
+ | `obfuscate` | `array` | `[]` | Regex-based rules to mask sensitive data before transmission. See [Obfuscation Rules](#obfuscation-rules). |
164
+
165
+ ---
166
+
167
+ ## Exposed API
168
+
169
+ All exports are available under the `nrvideo` namespace (UMD) or as named imports:
54
170
 
55
171
  ```javascript
56
- const options = {
57
- info: {
58
- licenseKey: "xxxxxxxxxxx",
59
- beacon: "xxxxxxxxxx",
60
- applicationId: "xxxxxxx",
61
- },
62
- config: {
63
- obfuscate: [
64
- { regex: /\/user\/\d+/, replacement: "/user/[REDACTED]" },
65
- ],
66
- },
67
- };
172
+ import nrvideo from '@newrelic/video-core';
173
+
174
+ // Available: nrvideo.Core, nrvideo.VideoTracker, nrvideo.Tracker,
175
+ // nrvideo.Emitter, nrvideo.VideoTrackerState, nrvideo.Chrono,
176
+ // nrvideo.Log, nrvideo.Constants, nrvideo.version,
177
+ // nrvideo.NrVideoEventAggregator, nrvideo.RetryQueueHandler,
178
+ // nrvideo.OptimizedHttpClient, nrvideo.HarvestScheduler,
179
+ // nrvideo.recordEvent
180
+ ```
68
181
 
69
- const tracker = new VideoSpecificTracker(player, options);
182
+ ### Core
183
+
184
+ Static class managing tracker registration and event dispatch.
185
+
186
+ | Method | Description |
187
+ |---|---|
188
+ | `Core.addTracker(tracker, options)` | Registers a tracker and initializes video analytics config. Starts event reporting. |
189
+ | `Core.removeTracker(tracker)` | Disposes and removes a tracker. Stops its event reporting. |
190
+ | `Core.getTrackers()` | Returns the array of currently registered trackers. |
191
+ | `Core.send(eventType, actionName, data)` | Sends an event to the collector. Called internally by event handlers. |
192
+ | `Core.sendError(att)` | Sends a `VideoErrorAction` with `actionName: "ERROR"`. For external/app-level errors. |
193
+ | `Core.forceHarvest()` | Forces an immediate harvest of all pending events. Returns a `Promise`. |
194
+
195
+ ### VideoTracker
196
+
197
+ Base class for video player trackers. Extends `Tracker`.
198
+
199
+ | Method | Description |
200
+ |---|---|
201
+ | `constructor(player, options)` | Initializes the tracker. Lifecycle: constructor → `setOptions` → `setPlayer` → `registerListeners`. |
202
+ | `setPlayer(player, tag)` | Sets the player and optional DOM element. Calls `registerListeners()`. |
203
+ | `setOptions(options)` | Configures tracker options (heartbeat, customData, adsTracker, isAd, parentTracker). |
204
+ | `setAdsTracker(tracker)` | Sets a child ad tracker. Ad events are funneled through the parent. |
205
+ | `setUserId(userId)` | Sets a user identifier included as `enduser.id` in all events. |
206
+ | `setHarvestInterval(interval)` | Updates the harvest cycle interval in milliseconds. |
207
+ | `dispose()` | Stops heartbeat, disposes ad tracker, unregisters listeners, clears references. |
208
+ | `registerListeners()` | **Override this.** Attach player event listeners and map to `send*()` methods. |
209
+ | `unregisterListeners()` | **Override this.** Detach player event listeners. |
210
+ | `getAttributes(att)` | **Do NOT override.** Collects all video/ad attributes. Use getter methods instead. |
211
+
212
+ **State-changing methods** (call these from `registerListeners`):
213
+
214
+ | Method | Event Emitted (Content / Ad) | Description |
215
+ |---|---|---|
216
+ | `sendPlayerReady(att)` | `PLAYER_READY` | Player is initialized and ready. |
217
+ | `sendRequest(att)` | `CONTENT_REQUEST` / `AD_REQUEST` | Playback has been requested. |
218
+ | `sendStart(att)` | `CONTENT_START` / `AD_START` | First frame rendered. Starts heartbeat. |
219
+ | `sendEnd(att)` | `CONTENT_END` / `AD_END` | Playback ended. Stops heartbeat. |
220
+ | `sendPause(att)` | `CONTENT_PAUSE` / `AD_PAUSE` | Playback paused. |
221
+ | `sendResume(att)` | `CONTENT_RESUME` / `AD_RESUME` | Playback resumed. |
222
+ | `sendBufferStart(att)` | `CONTENT_BUFFER_START` / `AD_BUFFER_START` | Buffering started. |
223
+ | `sendBufferEnd(att)` | `CONTENT_BUFFER_END` / `AD_BUFFER_END` | Buffering ended. |
224
+ | `sendSeekStart(att)` | `CONTENT_SEEK_START` / `AD_SEEK_START` | Seek started. |
225
+ | `sendSeekEnd(att)` | `CONTENT_SEEK_END` / `AD_SEEK_END` | Seek ended. |
226
+ | `sendError(att)` | `CONTENT_ERROR` / `AD_ERROR` | Error occurred during playback. |
227
+ | `sendRenditionChanged(att)` | `CONTENT_RENDITION_CHANGE` / `AD_RENDITION_CHANGE` | Stream quality changed. |
228
+ | `sendDownload(att)` | `DOWNLOAD` | Download event. Requires `att.state`. |
229
+ | `sendHeartbeat(att)` | `CONTENT_HEARTBEAT` / `AD_HEARTBEAT` | Sent automatically every 30s (2s for ads). |
230
+ | `sendAdBreakStart(att)` | `AD_BREAK_START` | Ad break started (ads only). |
231
+ | `sendAdBreakEnd(att)` | `AD_BREAK_END` | Ad break ended (ads only). |
232
+ | `sendAdQuartile(att)` | `AD_QUARTILE` | Ad quartile reached. Requires `att.quartile`. |
233
+ | `sendAdClick(att)` | `AD_CLICK` | Ad clicked. Requires `att.url`. |
234
+ | `sendCustom(actionName, timeSinceAttName, att)` | Custom `VideoCustomAction` | Sends a custom event with a `timeSince` attribute. |
235
+
236
+ ### Tracker
237
+
238
+ Base class providing heartbeat, custom data, and attribute management. Extends `Emitter`.
239
+
240
+ | Method | Description |
241
+ |---|---|
242
+ | `setOptions(options)` | Set heartbeat interval, customData, and parentTracker. |
243
+ | `getHeartbeat()` | Returns heartbeat interval in ms. Default: 30000 (content), 2000 (ads). |
244
+ | `startHeartbeat()` | Starts the heartbeat interval. Called automatically on `sendStart`. |
245
+ | `stopHeartbeat()` | Stops the heartbeat interval. Called automatically on `sendEnd`. |
246
+ | `getAttributes(att)` | Returns base attributes (trackerName, coreVersion, isBackgroundEvent, etc.). |
247
+ | `sendVideoAction(event, att)` | Emits a `VideoAction` event. |
248
+ | `sendVideoAdAction(event, att)` | Emits a `VideoAdAction` event. |
249
+ | `sendVideoErrorAction(event, att)` | Emits a `VideoErrorAction` event. |
250
+ | `sendVideoCustomAction(event, att)` | Emits a `VideoCustomAction` event. |
251
+
252
+ ### Emitter
253
+
254
+ Event system base class.
255
+
256
+ | Method | Description |
257
+ |---|---|
258
+ | `on(event, callback)` | Subscribe to an event. Use `'*'` to listen to all events. |
259
+ | `off(event, callback)` | Unsubscribe from an event. |
260
+ | `emit(eventType, event, data)` | Emit an event to all subscribers. |
261
+
262
+ ### VideoTrackerState
263
+
264
+ Internal state machine managing view lifecycle, QoE KPIs, and timing.
265
+
266
+ | Property | Description |
267
+ |---|---|
268
+ | `numberOfErrors` | Error count for current view. |
269
+ | `numberOfAds` | Total ads shown. |
270
+ | `numberOfVideos` | Total videos played. |
271
+ | `totalPlaytime` | Content viewing time in ms (excludes pausing, buffering, ads). |
272
+ | `totalAdPlaytime` | Ad viewing time in ms. |
273
+ | `startupTime` | Time from `CONTENT_REQUEST` to `CONTENT_START` in ms. |
274
+ | `peakBitrate` | Maximum bitrate observed during playback. |
275
+
276
+ ### Chrono
277
+
278
+ Utility class for measuring time lapses.
279
+
280
+ | Method | Description |
281
+ |---|---|
282
+ | `start()` | Start the timer. |
283
+ | `stop()` | Stop the timer and return delta. |
284
+ | `getDeltaTime()` | Get elapsed time since `start()` in ms. |
285
+ | `getDuration()` | Get accumulated duration across multiple start/stop cycles. |
286
+ | `reset()` | Reset all values. |
287
+ | `clone()` | Create a copy of the chrono. |
288
+
289
+ ### Log
290
+
291
+ Static logging utility with configurable levels.
292
+
293
+ | Method | Description |
294
+ |---|---|
295
+ | `Log.error(...msg)` | Log an error. |
296
+ | `Log.warn(...msg)` | Log a warning. |
297
+ | `Log.notice(...msg)` | Log a notice. |
298
+ | `Log.debug(...msg)` | Log a debug message. |
299
+ | `Log.debugCommonVideoEvents(player, extraEvents, report)` | Attach debug listeners for common HTML5 video events. |
300
+
301
+ **Log Levels** (set via `Log.level`):
302
+
303
+ ```javascript
304
+ nrvideo.Log.level = nrvideo.Log.Levels.DEBUG; // ALL, DEBUG, NOTICE, WARNING, ERROR, SILENT
70
305
  ```
71
306
 
72
- ### Examples
307
+ ### Constants
308
+
309
+ | Constant | Value | Description |
310
+ |---|---|---|
311
+ | `Constants.AdPositions` | `{ PRE, MID, POST }` | Ad position enum. |
312
+ | `Constants.COLLECTOR` | Object | Beacon endpoint URLs by region. |
313
+ | `Constants.VALID_EVENT_TYPES` | Array | `['VideoAction', 'VideoAdAction', 'VideoErrorAction', 'VideoCustomAction']` |
314
+ | `Constants.MAX_PAYLOAD_SIZE` | `1048576` | Maximum payload size (1 MB). |
315
+ | `Constants.MAX_BEACON_SIZE` | `61440` | Maximum beacon size (60 KB). |
316
+ | `Constants.MAX_EVENTS_PER_BATCH` | `1000` | Maximum events per batch. |
317
+ | `Constants.INTERVAL` | `10000` | Default harvest interval (10s). |
73
318
 
74
- **Mask user IDs in content URLs:**
319
+ ---
320
+
321
+ ## Building a Custom Tracker
322
+
323
+ Extend `VideoTracker` and override getter methods and listener registration:
75
324
 
76
325
  ```javascript
77
- obfuscate: [
78
- { regex: /\/user\/[^\/?"]+/, replacement: "/user/[REDACTED]" }
79
- ]
80
- // "https://cdn.example.com/user/john.doe/video.mp4"
81
- // → "https://cdn.example.com/user/[REDACTED]/video.mp4"
326
+ class MyPlayerTracker extends nrvideo.VideoTracker {
327
+ // Required: identify your tracker
328
+ getTrackerName() { return 'my-custom-player'; }
329
+ getTrackerVersion() { return '1.0.0'; }
330
+
331
+ // Override getters to return player metadata
332
+ getVideoId() { return this.player.getContentId(); }
333
+ getTitle() { return this.player.getTitle(); }
334
+ getSrc() { return this.player.getSrc(); }
335
+ getDuration() { return this.player.getDuration() * 1000; } // in ms
336
+ getPlayhead() { return this.player.getCurrentTime() * 1000; }
337
+ getBitrate() { return this.player.getCurrentBitrate(); }
338
+ getRenditionName() { return this.player.getQualityLabel(); }
339
+ getRenditionHeight() { return this.player.getVideoHeight(); }
340
+ getRenditionWidth() { return this.player.getVideoWidth(); }
341
+ isLive() { return this.player.isLive(); }
342
+ isMuted() { return this.player.isMuted(); }
343
+ isFullscreen() { return this.player.isFullscreen(); }
344
+ getPlayrate() { return this.player.getPlaybackRate(); }
345
+ getPlayerName() { return 'My Player'; }
346
+ getPlayerVersion() { return this.player.version; }
347
+
348
+ // Map player events to tracker methods
349
+ registerListeners() {
350
+ this.player.on('play', () => this.sendRequest());
351
+ this.player.on('playing', () => this.sendStart());
352
+ this.player.on('pause', () => this.sendPause());
353
+ this.player.on('ended', () => this.sendEnd());
354
+ this.player.on('waiting', () => this.sendBufferStart());
355
+ this.player.on('canplay', () => this.sendBufferEnd());
356
+ this.player.on('seeking', () => this.sendSeekStart());
357
+ this.player.on('seeked', () => this.sendSeekEnd());
358
+ this.player.on('error', (e) => this.sendError({
359
+ errorName: e.message,
360
+ errorCode: e.code
361
+ }));
362
+ }
363
+
364
+ unregisterListeners() {
365
+ this.player.off('play');
366
+ this.player.off('playing');
367
+ // ... remove all listeners
368
+ }
369
+ }
82
370
  ```
83
371
 
84
- **Mask auth tokens in query strings:**
372
+ ---
373
+
374
+ ## Getter Methods (Override These)
375
+
376
+ These methods return `null` by default. Override them in your tracker to provide player-specific data:
377
+
378
+ | Getter | Attribute Set | Description |
379
+ |---|---|---|
380
+ | `getVideoId()` | `contentId` / `adId` | Content or ad identifier. |
381
+ | `getTitle()` | `contentTitle` / `adTitle` | Content or ad title. |
382
+ | `getSrc()` | `contentSrc` / `adSrc` | Media source URL. |
383
+ | `getDuration()` | `contentDuration` / `adDuration` | Duration in ms. |
384
+ | `getPlayhead()` | `contentPlayhead` / `adPlayhead` | Current playback position in ms. |
385
+ | `getBitrate()` | `contentBitrate` / `adBitrate` | Current bitrate in bits/s. |
386
+ | `getManifestBitrate()` | `contentManifestBitrate` | Manifest/playlist declared bitrate in bps. |
387
+ | `getSegmentDownloadBitrate()` | `contentSegmentDownloadBitrate` | Measured bitrate from segment download in bps. |
388
+ | `getNetworkDownloadBitrate()` | `contentNetworkDownloadBitrate` | Network download throughput in bps. |
389
+ | `getRenditionName()` | `contentRenditionName` / `adRenditionName` | Quality label (e.g., "1080p"). |
390
+ | `getRenditionHeight()` | `contentRenditionHeight` / `adRenditionHeight` | Rendition height in px. |
391
+ | `getRenditionWidth()` | `contentRenditionWidth` / `adRenditionWidth` | Rendition width in px. |
392
+ | `isLive()` | `contentIsLive` | `true` if live stream. |
393
+ | `isMuted()` | `contentIsMuted` / `adIsMuted` | `true` if muted. |
394
+ | `isFullscreen()` | `contentIsFullscreen` | `true` if fullscreen. |
395
+ | `getPlayrate()` | `contentPlayrate` | Playback speed (1.0 = normal). |
396
+ | `getLanguage()` | `contentLanguage` / `adLanguage` | Language in locale notation (e.g., `en_US`). |
397
+ | `getCdn()` | `contentCdn` / `adCdn` | CDN serving the content. |
398
+ | `getFps()` | `contentFps` / `adFps` | Current frames per second. |
399
+ | `isAutoplayed()` | `contentIsAutoplayed` | `true` if autoplayed. |
400
+ | `getPreload()` | `contentPreload` | Preload attribute value. |
401
+ | `getPlayerName()` | `playerName` | Player name. |
402
+ | `getPlayerVersion()` | `playerVersion` | Player version. |
403
+ | `getAdQuartile()` | `adQuartile` | Ad quartile (0–4). |
404
+ | `getAdPosition()` | `adPosition` | Ad position (`pre`, `mid`, `post`). |
405
+ | `getAdPartner()` | `adPartner` | Ad partner (e.g., `ima`, `freewheel`). |
406
+ | `getAdCreativeId()` | `adCreativeId` | Ad creative identifier. |
407
+
408
+ ---
409
+
410
+ ## Quality of Experience (QoE)
411
+
412
+ When `qoeAggregate` is enabled, the library tracks and reports QoE KPI metrics as `QOE_AGGREGATE` events:
413
+
414
+ | KPI | Description |
415
+ |---|---|
416
+ | `startupTime` | Time from `CONTENT_REQUEST` to `CONTENT_START` in ms. |
417
+ | `peakBitrate` | Maximum bitrate observed during playback. |
418
+ | `averageBitrate` | Playtime-weighted average bitrate in bps. |
419
+ | `totalPlaytime` | Total content viewing time in ms (excludes pausing, buffering, ads). |
420
+ | `totalRebufferingTime` | Total ms spent rebuffering (excludes initial buffering). |
421
+ | `rebufferingRatio` | `(totalRebufferingTime / totalPlaytime) × 100`. |
422
+ | `hadStartupError` | `true` if error occurred before `CONTENT_START`. |
423
+ | `hadPlaybackError` | `true` if error occurred at any time during playback. |
424
+
425
+ QoE KPIs are refreshed before each harvest drain and always included in the final harvest at `CONTENT_END`.
426
+
427
+ ---
428
+
429
+ ## Obfuscation Rules
430
+
431
+ Mask sensitive data in event payloads before transmission using regex-based rules:
85
432
 
86
433
  ```javascript
87
- obfuscate: [
88
- { regex: /token=[^&"]+/, replacement: "token=[MASKED]" }
89
- ]
90
- // "https://cdn.example.com/stream.m3u8?token=eyJhbGci..."
91
- // → "https://cdn.example.com/stream.m3u8?token=[MASKED]"
434
+ config: {
435
+ obfuscate: [
436
+ { regex: /\/user\/[^\/?"]+/, replacement: '/user/[REDACTED]' },
437
+ { regex: /token=[^&"]+/, replacement: 'token=[MASKED]' },
438
+ ]
439
+ }
92
440
  ```
93
441
 
94
- ### Rule ordering
442
+ | Field | Type | Description |
443
+ |---|---|---|
444
+ | `regex` | `string` \| `RegExp` | Pattern to match in the serialized JSON payload. |
445
+ | `replacement` | `string` | Replacement string for each match. |
95
446
 
96
- Rules are applied in array order. The output of one rule is the input for the next, so ordering matters when rules overlap.
447
+ Rules are applied sequentially the output of one rule feeds into the next. Invalid regex patterns are skipped with a warning logged via `Log.warn`.
97
448
 
98
- ### Edge cases
449
+ ---
99
450
 
100
- - **Invalid regex**: The rule is skipped and a warning is logged via `Log.warn`. Other rules continue to apply normally.
101
- - **Empty replacement**: Setting `replacement: ""` deletes the matched content entirely.
102
- - **RegExp flags**: The global flag (`g`) is always applied so all occurrences in the payload are replaced. Other flags (`i`, `m`, etc.) on `RegExp` objects are preserved.
451
+ ## Build & Development
103
452
 
453
+ ```bash
454
+ # Install dependencies
455
+ npm install
104
456
 
105
- ## APIs
457
+ # Production build
458
+ npm run build
106
459
 
107
- Some of the APIs exposed and commonly used are:
460
+ # Development build (unminified)
461
+ npm run build:dev
108
462
 
109
- - `tracker.setUserId("userId")` &mdash; Set the user ID.
110
- - `tracker.setHarvestInterval(30000)` &mdash; Set the harvest interval time (in milliseconds).
111
- - `tracker.setOptions({ customData: { key: value } })` &mdash; Set custom options or data.
112
- - `tracker.sendCustom("CustomEvent", { data: "custom-test" })` &mdash; Send a custom event.
463
+ # Watch mode (production)
464
+ npm run watch
113
465
 
114
- Any event emitted by the tracker will be sent to New Relic and processed according to its type.
466
+ # Watch mode (development)
467
+ npm run watch:dev
115
468
 
116
- Once the tracker is added, any event it emits will be sent to New Relic and processed by the following functions:
469
+ # Clean build artifacts
470
+ npm run clean
471
+ ```
117
472
 
118
- ```javascript
119
- tracker.sendVideoAction("VideoEvent", { data: 1 });
120
- tracker.sendVideoAdAction("AdEvent", { data: "test-1" });
121
- tracker.sendVideoErrorAction("ErrorEvent", { data: "error-test" });
122
- tracker.sendVideoCustomAction("CustomEvent", { data: "custom-test" });
473
+ ## Distribution Formats
474
+
475
+ The build produces three output formats:
476
+
477
+ | Format | Path | Usage |
478
+ |---|---|---|
479
+ | **UMD** | `dist/umd/nrvideo.min.js` | `<script>` tag → `window.nrvideo` |
480
+ | **CommonJS** | `dist/cjs/index.js` | `require('@newrelic/video-core')` |
481
+ | **ES Module** | `dist/esm/index.js` | `import nrvideo from '@newrelic/video-core'` |
482
+
483
+ ## Testing
484
+
485
+ ```bash
486
+ # Run tests with coverage
487
+ npm test
123
488
  ```
124
489
 
490
+ Tests are written using [Jest](https://jestjs.io/) and located in the `test/` directory.
491
+
492
+ ---
493
+
125
494
  ## Data Model
126
495
 
127
- To understand which actions and attributes are captured and emitted by the tracker under different event types, see [DataModel.md](DATAMODEL.md).
496
+ For a comprehensive reference of all event types, attributes, and action names, see [DATAMODEL.md](DATAMODEL.md).
497
+
498
+ ---
499
+
500
+ ## License
501
+
502
+ This project is licensed under the [Apache 2.0 License](LICENSE).
503
+
504
+ ---
505
+
506
+ ## Support
507
+
508
+ Should you need assistance with New Relic products, you are in good hands with several support channels.
509
+
510
+ If the issue has been confirmed as a bug or is a feature request, please file a [GitHub issue](../../issues).
511
+
512
+ **Support Channels:**
513
+
514
+ - [New Relic Documentation](https://docs.newrelic.com): Comprehensive guidance for using our platform
515
+ - [New Relic Community](https://discuss.newrelic.com): The best place to engage in troubleshooting questions
516
+ - [New Relic University](https://learn.newrelic.com): A range of online training for New Relic users of every level
517
+ - [New Relic Technical Support](https://support.newrelic.com): 24/7/365 ticketed support. Read more about our Technical Support Offerings
518
+
519
+ **Additional Resources:**
520
+
521
+ - [DATAMODEL.md](DATAMODEL.md) — Complete event and attribute reference
522
+ - [DEVELOPING.md](DEVELOPING.md) — Building and testing instructions
523
+ - [CONTRIBUTING.md](CONTRIBUTING.md) — Contribution guidelines
524
+
525
+ ---
128
526
 
129
- ## Documentation
527
+ ## Contributing
130
528
 
131
- All classes are documented using autodocs. The documents, generated with [jsdoc](https://github.com/jsdoc/jsdoc), can be found in the `documentation` directory of the current repo.
529
+ We encourage your contributions to improve the Video Core JS library! Keep in mind that when you submit your pull request, you'll need to sign the CLA via the click-through using CLA-Assistant. You only have to sign the CLA one time per project.
132
530
 
133
- # Support
531
+ If you have any questions, or to execute our corporate CLA (which is required if your contribution is on behalf of a company), drop us an email at opensource@newrelic.com.
134
532
 
135
- New Relic has open-sourced this project. This project is provided AS-IS WITHOUT WARRANTY OR DEDICATED SUPPORT. Issues and contributions should be reported to the project here on GitHub.
533
+ For more details on how best to contribute, see [CONTRIBUTING.md](CONTRIBUTING.md).
136
534
 
137
- We encourage you to bring your experiences and questions to the [Explorers Hub](https://discuss.newrelic.com) where our community members collaborate on solutions and new ideas.
535
+ ---
138
536
 
139
- ## Community
537
+ ## A Note About Vulnerabilities
140
538
 
141
- New Relic hosts and moderates an online forum where customers can interact with New Relic employees as well as other customers to get help and share best practices. Like all official New Relic open source projects, there's a related Community topic in the New Relic Explorers Hub. You can find this project's topic/threads here:
539
+ As noted in our [security policy](../../security/policy), New Relic is committed to the privacy and security of our customers and their data. We believe that providing coordinated disclosure by security researchers and engaging with the security community are important means to achieve our security goals.
142
540
 
143
- https://discuss.newrelic.com/t/video-core-js-tracker/100303
541
+ If you believe you have found a security vulnerability in this project or any of New Relic's products or websites, we welcome and greatly appreciate you reporting it to New Relic through our [bug bounty program](https://docs.newrelic.com/docs/security/security-privacy/information-security/report-security-vulnerabilities/).
144
542
 
145
- ## Issues / enhancement requests
543
+ ---
146
544
 
147
- Issues and enhancement requests can be submitted in the [Issues tab of this repository](../../issues). Please search for and review the existing open issues before submitting a new issue.
545
+ ## Acknowledgments
148
546
 
149
- # Contributing
547
+ If you would like to contribute to this project, review [these guidelines](CONTRIBUTING.md).
150
548
 
151
- Contributions are encouraged! If you submit an enhancement request, we'll invite you to contribute the change yourself. Please review our [Contributors Guide](CONTRIBUTING.md).
549
+ To all contributors, we thank you! Without your contribution, this project would not be what it is today.
152
550
 
153
- Keep in mind that when you submit your pull request, you'll need to sign the CLA via the click-through using CLA-Assistant. If you'd like to execute our corporate CLA, or if you have any questions, please drop us an email at opensource+videoagent@newrelic.com.
551
+ ---
154
552
 
155
- # License
553
+ ## License
156
554
 
157
- This project is distributed under the [Apache 2.0](https://apache.org/licenses/LICENSE-2.0.txt) License.
555
+ The Video Core JS library is licensed under the [Apache 2.0 License](LICENSE).
158
556
 
159
- The video-core also uses source code from third-party libraries. Full details on which libraries are used and the terms under which they are licensed can be found in the [third-party notices document](THIRD_PARTY_NOTICES.md).
557
+ The Video Core JS library also uses source code from third-party libraries. Full details on which libraries are used and the terms under which they are licensed can be found in the [third-party notices document](THIRD_PARTY_NOTICES.md).