@newrelic/video-core 4.1.8-beta → 5.0.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 (46) hide show
  1. package/CHANGELOG.md +65 -15
  2. package/README.md +628 -45
  3. package/dist/cjs/browser/index.js +3 -0
  4. package/dist/cjs/browser/index.js.LICENSE.txt +6 -0
  5. package/dist/cjs/browser/index.js.map +1 -0
  6. package/dist/cjs/index.js +1 -1
  7. package/dist/cjs/index.js.LICENSE.txt +1 -1
  8. package/dist/cjs/index.js.map +1 -1
  9. package/dist/cjs/vega/index.js +3 -0
  10. package/dist/cjs/vega/index.js.LICENSE.txt +6 -0
  11. package/dist/cjs/vega/index.js.map +1 -0
  12. package/dist/esm/browser/index.js +3 -0
  13. package/dist/esm/browser/index.js.LICENSE.txt +6 -0
  14. package/dist/esm/browser/index.js.map +1 -0
  15. package/dist/esm/index.js +1 -1
  16. package/dist/esm/index.js.LICENSE.txt +1 -1
  17. package/dist/esm/index.js.map +1 -1
  18. package/dist/esm/vega/index.js +3 -0
  19. package/dist/esm/vega/index.js.LICENSE.txt +6 -0
  20. package/dist/esm/vega/index.js.map +1 -0
  21. package/dist/umd/nrvideo.min.js +1 -1
  22. package/dist/umd/nrvideo.min.js.LICENSE.txt +1 -1
  23. package/dist/umd/nrvideo.min.js.map +1 -1
  24. package/package.json +31 -6
  25. package/src/{agent.js → browser/agent.js} +43 -8
  26. package/src/{harvestScheduler.js → browser/harvestScheduler.js} +87 -86
  27. package/src/browser/index.js +58 -0
  28. package/src/connectedDevice/connectedDeviceAgent.js +116 -0
  29. package/src/connectedDevice/connectedDeviceConstants.js +121 -0
  30. package/src/connectedDevice/connectedDeviceHarvester.js +522 -0
  31. package/src/connectedDevice/index.js +49 -0
  32. package/src/constants.js +8 -0
  33. package/src/core.js +4 -18
  34. package/src/eventAggregator.js +48 -0
  35. package/src/index.js +9 -10
  36. package/src/obfuscate.js +33 -0
  37. package/src/optimizedHttpClient.js +6 -2
  38. package/src/recordEvent.js +56 -49
  39. package/src/tracker.js +14 -4
  40. package/src/utils/eventBuilder.js +126 -0
  41. package/src/utils/harvestTimer.js +109 -0
  42. package/src/{utils.js → utils/index.js} +2 -2
  43. package/src/utils/qoeFilters.js +149 -0
  44. package/src/videoConfiguration.js +90 -7
  45. package/src/videotracker.js +100 -32
  46. package/src/videotrackerstate.js +156 -31
package/README.md CHANGED
@@ -1,89 +1,672 @@
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
+
7
+ > **Note:** The `PageAction` event type is officially deprecated and is no longer maintained. It has been archived in the [`archived-master`](https://github.com/newrelic/video-core-js/tree/archived-master) branch for historical reference.
8
+
9
+ The **New Relic Video Core** library (`@newrelic/video-core`) is the foundational framework for 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. The library supports two harvester pipelines:
10
+
11
+ - **Browser pipeline** — ships events to `bam.nr-data.net` via the standard Browser collector (license key auth). Used by web-based players.
12
+ - **Connected-device pipeline** — ships events to `mobile-collector.newrelic.com` via the mobile collector. Used by Vega apps and other connected-device platforms.
13
+
14
+
15
+ Events are categorized into four distinct types:
16
+
17
+ | Event Type | Description |
18
+ |---|---|
19
+ | `VideoAction` | Content playback events (play, pause, seek, buffer, etc.) |
20
+ | `VideoAdAction` | Ad-related events (ad start, end, quartile, break, etc.) |
21
+ | `VideoErrorAction` | Error events (content errors, ad errors, crashes) |
22
+ | `VideoCustomAction` | Custom events defined by the integrator |
23
+
24
+ ---
25
+
26
+ ## Table of Contents
27
+
28
+ - [Installation](#installation)
29
+ - [Quick Start](#quick-start)
30
+ - [Configuration](#configuration)
31
+ - [Info Object (Required)](#info-object-required)
32
+ - [Config Object (Optional)](#config-object-optional)
33
+ - [Exposed API](#exposed-api)
34
+ - [Core](#core)
35
+ - [VideoTracker](#videotracker)
36
+ - [Tracker](#tracker)
37
+ - [Emitter](#emitter)
38
+ - [VideoTrackerState](#videotrackerstate)
39
+ - [Chrono](#chrono)
40
+ - [Log](#log)
41
+ - [Constants](#constants)
42
+ - [Building a Custom Tracker](#building-a-custom-tracker)
43
+ - [Tracker Methods Reference](#tracker-methods-reference)
44
+ - [Getter Methods (Override These)](#getter-methods-override-these)
45
+ - [Quality of Experience (QoE)](#quality-of-experience-qoe)
46
+ - [Obfuscation Rules](#obfuscation-rules)
47
+ - [Build & Development](#build--development)
48
+ - [Distribution Formats](#distribution-formats)
49
+ - [Testing](#testing)
50
+ - [Data Model](#data-model)
51
+ - [License](#license)
52
+
53
+ ---
54
+
55
+ ## Installation
56
+
57
+ ```bash
58
+ npm install @newrelic/video-core
59
+ ```
7
60
 
8
- ## Registering Trackers
61
+ Or include directly via UMD:
9
62
 
10
- Any browser-based video tracker can extend the `VideoTracker` class and use its core functionality.
63
+ ```html
64
+ <script src="dist/umd/nrvideo.min.js"></script>
65
+ ```
11
66
 
12
- To initialize a tracker, create an instance of your specific tracker class:
67
+ ## Quick Start
13
68
 
14
69
  ```javascript
70
+ import nrvideo from '@newrelic/video-core';
71
+
72
+ // 1. Define a custom tracker by extending VideoTracker
73
+ class MyPlayerTracker extends nrvideo.VideoTracker {
74
+ getTrackerName() { return 'my-player'; }
75
+ getSrc() { return this.player.currentSrc; }
76
+ getDuration() { return this.player.duration; }
77
+ getPlayhead() { return this.player.currentTime; }
78
+ getRenditionWidth() { return this.player.videoWidth; }
79
+ getRenditionHeight() { return this.player.videoHeight; }
80
+
81
+ registerListeners() {
82
+ this.player.addEventListener('play', () => this.sendRequest());
83
+ this.player.addEventListener('playing', () => this.sendStart());
84
+ this.player.addEventListener('pause', () => this.sendPause());
85
+ this.player.addEventListener('ended', () => this.sendEnd());
86
+ this.player.addEventListener('waiting', () => this.sendBufferStart());
87
+ this.player.addEventListener('seeking', () => this.sendSeekStart());
88
+ this.player.addEventListener('seeked', () => this.sendSeekEnd());
89
+ this.player.addEventListener('error', () => this.sendError({
90
+ errorName: this.player.error?.message,
91
+ errorCode: this.player.error?.code
92
+ }));
93
+ }
94
+
95
+ unregisterListeners() {
96
+ // Remove all listeners added in registerListeners
97
+ }
98
+ }
99
+
100
+ // 2. Configure and register the tracker
15
101
  const options = {
16
102
  info: {
17
- licenseKey: "xxxxxxxxxxx",
18
- beacon: "xxxxxxxxxx",
19
- applicationId: "xxxxxxx",
103
+ licenseKey: 'YOUR_LICENSE_KEY',
104
+ beacon: 'bam.nr-data.net',
105
+ applicationID: 'YOUR_APPLICATION_ID',
20
106
  },
21
- config: {
22
- qoeAggregate: true, // Optional: Enable/disable QoE (Quality of Experience) event aggregation (default: true)
107
+ };
108
+
109
+ const player = document.getElementById('myPlayer');
110
+ const tracker = new MyPlayerTracker(player, options);
111
+
112
+ // 3. Add tracker to Core to begin reporting
113
+ nrvideo.Core.addTracker(tracker, options);
114
+ ```
115
+
116
+ ## Configuration
117
+
118
+ The `options` object passed to `Core.addTracker()` consists of two top-level keys: `info` (credentials and pipeline config) and `config` (optional behaviour settings).
119
+
120
+ ---
121
+
122
+ ### Browser / Web Setup
123
+
124
+ **Obtain your credentials:**
125
+
126
+ 1. Log in to [one.newrelic.com](https://one.newrelic.com)
127
+ 2. Navigate to the video agent onboarding flow
128
+ 3. Copy your credentials: `licenseKey`, `beacon`, and `applicationId`
129
+
130
+ **With Application ID and Beacon:**
131
+
132
+ ```javascript
133
+ info: {
134
+ licenseKey: 'YOUR_LICENSE_KEY', // Required
135
+ applicationID: 'YOUR_APPLICATION_ID', // Required
136
+ beacon: 'bam.nr-data.net', // Required when applicationID is provided
137
+ }
138
+ ```
139
+
140
+ **With App Name and Region:**
141
+
142
+ ```javascript
143
+ info: {
144
+ licenseKey: 'YOUR_LICENSE_KEY', // Required
145
+ appName: 'My Video App', // Required when no applicationID
146
+ region: 'US', // Required when no applicationID — 'US' | 'EU' | 'staging'
147
+ }
148
+ ```
149
+
150
+ **Valid Browser Beacon Endpoints:**
151
+
152
+ | Region | Beacon |
153
+ |---|---|
154
+ | US | `bam.nr-data.net`, `bam-cell.nr-data.net` |
155
+ | EU | `bam.eu01.nr-data.net` |
156
+ | Staging | `staging-bam-cell.nr-data.net` |
157
+ | GOV | `gov-bam.nr-data.net` |
158
+
159
+ ---
160
+
161
+ ### Vega / Fire TV Setup
162
+
163
+ For Amazon Vega (Fire TV / Kepler runtime), import from the `/vega` subpath. The `info` object uses `applicationToken` and `endpoint` specific to the Vega pipeline, plus an optional `deviceInfo` block carrying runtime device identity.
164
+
165
+ **Obtain your application token:**
166
+
167
+ 1. Log in to [one.newrelic.com](https://one.newrelic.com)
168
+ 2. Navigate to the video agent onboarding flow
169
+ 3. Copy your `applicationToken` (begins with `AA...` and ends with `-NRMA`) and your `accountId`
170
+
171
+ ```javascript
172
+ info: {
173
+ accountId: 'YOUR_ACCOUNT_ID', // Required
174
+ applicationToken: 'YOUR_NRMA_TOKEN', // Required — begins "AA…-NRMA"
175
+ endpoint: 'US', // Required — 'US' | 'EU' | 'staging' | 'GOV'
176
+ deviceInfo: { // Optional — all sub-fields optional
177
+ uuid: getDeviceId(),
178
+ osVersion: getSystemVersion(),
179
+ deviceModel: getModel(),
180
+ deviceManufacturer: getBrand(),
181
+ osBuild: getBuildIdSync(),
182
+ appBuild: getBuildNumber(),
183
+ architecture: 'aarch64',
23
184
  },
185
+ }
186
+ ```
187
+
188
+ Install the device-info library to source real device attributes at runtime:
189
+
190
+ ```bash
191
+ npm install @amazon-devices/react-native-device-info --save
192
+ ```
193
+
194
+ ```javascript
195
+ import {
196
+ getDeviceId, getSystemVersion, getModel, getBrand,
197
+ getBuildIdSync, getBuildNumber,
198
+ } from '@amazon-devices/react-native-device-info';
199
+
200
+ const deviceInfo = {
201
+ uuid: getDeviceId(),
202
+ osVersion: getSystemVersion(),
203
+ deviceModel: getModel(),
204
+ deviceManufacturer: getBrand(),
205
+ osBuild: getBuildIdSync(), // OS image build
206
+ appBuild: getBuildNumber(), // app build number
207
+ architecture: 'aarch64',
24
208
  };
209
+ ```
210
+
211
+ #### `deviceInfo` field reference
212
+
213
+ All sub-fields are optional — missing values fall back to the SDK defaults. Extra fields are ignored.
214
+
215
+ | Field | Recommended source | Falls back to |
216
+ |---|---|---|
217
+ | `uuid` | `getDeviceId()` — stable model-code identifier | `"00000000-0000-0000-0000-000000000000"` |
218
+ | `osVersion` | `getSystemVersion()` | `"1.0"` |
219
+ | `deviceModel` | `getModel()` | `"VegaDevice"` |
220
+ | `deviceManufacturer` | `getBrand()` | `"Amazon"` |
221
+ | `osBuild` | `getBuildIdSync()` — **OS image build** | `"1"` |
222
+ | `appBuild` | `getBuildNumber()` — **app build number** | `"1"` |
223
+ | `architecture` | `'aarch64'` | `"aarch64"` |
224
+
225
+ > `osBuild` and `appBuild` are semantically distinct: `osBuild` is the OS image identifier set by Amazon; `appBuild` is your app's build number. Use `getBuildIdSync()` for the former and `getBuildNumber()` for the latter.
226
+
227
+ **Valid Vega Endpoints:**
25
228
 
26
- // User can get the `info` object by completing the onboarding process on New Relic.
27
- const tracker = new VideoSpecificTracker(player, options);
229
+ | Value | Routes to |
230
+ |---|---|
231
+ | `'US'` | `mobile-collector.newrelic.com` |
232
+ | `'EU'` | `mobile-collector.eu01.nr-data.net` |
233
+ | `'staging'` | `staging-mobile-collector.newrelic.com` |
234
+ | `'GOV'` | `gov-mobile-collector.newrelic.com` |
235
+
236
+ ---
237
+
238
+ ### Config Object (Optional)
239
+
240
+ | Option | Type | Default | Description |
241
+ |---|---|---|---|
242
+ | `qoeIntervalFactor` | `number` | `2` | 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. |
243
+ | `obfuscate` | `array` | `[]` | Regex-based rules to mask sensitive data before transmission. See [Obfuscation Rules](#obfuscation-rules). |
244
+
245
+ ---
246
+
247
+ ## Exposed API
248
+
249
+ All exports are available under the `nrvideo` namespace (UMD) or as named imports:
250
+
251
+ ```javascript
252
+ import nrvideo from '@newrelic/video-core';
253
+
254
+ // Available: nrvideo.Core, nrvideo.VideoTracker, nrvideo.Tracker,
255
+ // nrvideo.Emitter, nrvideo.VideoTrackerState, nrvideo.Chrono,
256
+ // nrvideo.Log, nrvideo.Constants, nrvideo.version,
257
+ // nrvideo.NrVideoEventAggregator, nrvideo.RetryQueueHandler,
258
+ // nrvideo.OptimizedHttpClient, nrvideo.HarvestScheduler,
259
+ // nrvideo.recordEvent
260
+ ```
261
+
262
+ ### Core
263
+
264
+ Static class managing tracker registration and event dispatch.
265
+
266
+ | Method | Description |
267
+ |---|---|
268
+ | `Core.addTracker(tracker, options)` | Registers a tracker and initializes video analytics config. Starts event reporting. |
269
+ | `Core.removeTracker(tracker)` | Disposes and removes a tracker. Stops its event reporting. |
270
+ | `Core.getTrackers()` | Returns the array of currently registered trackers. |
271
+ | `Core.send(eventType, actionName, data)` | Sends an event to the collector. Called internally by event handlers. |
272
+ | `Core.sendError(att)` | Sends a `VideoErrorAction` with `actionName: "ERROR"`. For external/app-level errors. |
273
+
274
+ ### VideoTracker
275
+
276
+ Base class for video player trackers. Extends `Tracker`.
277
+
278
+ | Method | Description |
279
+ |---|---|
280
+ | `constructor(player, options)` | Initializes the tracker. Lifecycle: constructor → `setOptions` → `setPlayer` → `registerListeners`. |
281
+ | `setPlayer(player, tag)` | Sets the player and optional DOM element. Calls `registerListeners()`. |
282
+ | `setOptions(options)` | Configures tracker options (heartbeat, customData, adsTracker, isAd, parentTracker). |
283
+ | `setAdsTracker(tracker)` | Sets a child ad tracker. Ad events are funneled through the parent. |
284
+ | `setUserId(userId)` | Sets a user identifier included as `enduser.id` in all events. |
285
+ | `setHarvestInterval(interval)` | Updates the harvest cycle interval in milliseconds. |
286
+ | `dispose()` | Stops heartbeat, disposes ad tracker, unregisters listeners, clears references. |
287
+ | `registerListeners()` | **Override this.** Attach player event listeners and map to `send*()` methods. |
288
+ | `unregisterListeners()` | **Override this.** Detach player event listeners. |
289
+ | `getAttributes(att)` | **Do NOT override.** Collects all video/ad attributes. Use getter methods instead. |
290
+
291
+ **State-changing methods** (call these from `registerListeners`):
292
+
293
+ | Method | Event Emitted (Content / Ad) | Description |
294
+ |---|---|---|
295
+ | `sendPlayerReady(att)` | `PLAYER_READY` | Player is initialized and ready. |
296
+ | `sendRequest(att)` | `CONTENT_REQUEST` / `AD_REQUEST` | Playback has been requested. |
297
+ | `sendStart(att)` | `CONTENT_START` / `AD_START` | First frame rendered. Starts heartbeat. |
298
+ | `sendEnd(att)` | `CONTENT_END` / `AD_END` | Playback ended. Stops heartbeat. |
299
+ | `sendPause(att)` | `CONTENT_PAUSE` / `AD_PAUSE` | Playback paused. |
300
+ | `sendResume(att)` | `CONTENT_RESUME` / `AD_RESUME` | Playback resumed. |
301
+ | `sendBufferStart(att)` | `CONTENT_BUFFER_START` / `AD_BUFFER_START` | Buffering started. |
302
+ | `sendBufferEnd(att)` | `CONTENT_BUFFER_END` / `AD_BUFFER_END` | Buffering ended. |
303
+ | `sendSeekStart(att)` | `CONTENT_SEEK_START` / `AD_SEEK_START` | Seek started. |
304
+ | `sendSeekEnd(att)` | `CONTENT_SEEK_END` / `AD_SEEK_END` | Seek ended. |
305
+ | `sendError(att)` | `CONTENT_ERROR` / `AD_ERROR` | Error occurred during playback. |
306
+ | `sendRenditionChanged(att)` | `CONTENT_RENDITION_CHANGE` / `AD_RENDITION_CHANGE` | Stream quality changed. |
307
+ | `sendDownload(att)` | `DOWNLOAD` | Download event. Requires `att.state`. |
308
+ | `sendHeartbeat(att)` | `CONTENT_HEARTBEAT` / `AD_HEARTBEAT` | Sent automatically every 30s (2s for ads). |
309
+ | `sendAdBreakStart(att)` | `AD_BREAK_START` | Ad break started (ads only). |
310
+ | `sendAdBreakEnd(att)` | `AD_BREAK_END` | Ad break ended (ads only). |
311
+ | `sendAdQuartile(att)` | `AD_QUARTILE` | Ad quartile reached. Requires `att.quartile`. |
312
+ | `sendAdClick(att)` | `AD_CLICK` | Ad clicked. Requires `att.url`. |
313
+ | `sendCustom(actionName, timeSinceAttName, att)` | Custom `VideoCustomAction` | Sends a custom event with a `timeSince` attribute. |
314
+
315
+ ### Tracker
316
+
317
+ Base class providing heartbeat, custom data, and attribute management. Extends `Emitter`.
318
+
319
+ | Method | Description |
320
+ |---|---|
321
+ | `setOptions(options)` | Set heartbeat interval, customData, and parentTracker. |
322
+ | `getHeartbeat()` | Returns heartbeat interval in ms. Default: 30000 (content), 2000 (ads). |
323
+ | `startHeartbeat()` | Starts the heartbeat interval. Called automatically on `sendStart`. |
324
+ | `stopHeartbeat()` | Stops the heartbeat interval. Called automatically on `sendEnd`. |
325
+ | `getAttributes(att)` | Returns base attributes (trackerName, coreVersion, isBackgroundEvent, etc.). |
326
+ | `sendVideoAction(event, att)` | Emits a `VideoAction` event. |
327
+ | `sendVideoAdAction(event, att)` | Emits a `VideoAdAction` event. |
328
+ | `sendVideoErrorAction(event, att)` | Emits a `VideoErrorAction` event. |
329
+ | `sendVideoCustomAction(event, att)` | Emits a `VideoCustomAction` event. |
330
+
331
+ ### Emitter
332
+
333
+ Event system base class.
334
+
335
+ | Method | Description |
336
+ |---|---|
337
+ | `on(event, callback)` | Subscribe to an event. Use `'*'` to listen to all events. |
338
+ | `off(event, callback)` | Unsubscribe from an event. |
339
+ | `emit(eventType, event, data)` | Emit an event to all subscribers. |
340
+
341
+ ### VideoTrackerState
342
+
343
+ Internal state machine managing view lifecycle, QoE KPIs, and timing.
344
+
345
+ | Property | Description |
346
+ |---|---|
347
+ | `numberOfErrors` | Error count for current view. |
348
+ | `numberOfAds` | Total ads shown. |
349
+ | `numberOfVideos` | Total videos played. |
350
+ | `totalPlaytime` | Content viewing time in ms (excludes pausing, buffering, ads). |
351
+ | `totalAdPlaytime` | Ad viewing time in ms. |
352
+ | `startupTime` | Time from `CONTENT_REQUEST` to `CONTENT_START` in ms. |
353
+ | `peakBitrate` | Maximum bitrate observed during playback. |
354
+
355
+ ### Chrono
356
+
357
+ Utility class for measuring time lapses.
358
+
359
+ | Method | Description |
360
+ |---|---|
361
+ | `start()` | Start the timer. |
362
+ | `stop()` | Stop the timer and return delta. |
363
+ | `getDeltaTime()` | Get elapsed time since `start()` in ms. |
364
+ | `getDuration()` | Get accumulated duration across multiple start/stop cycles. |
365
+ | `reset()` | Reset all values. |
366
+ | `clone()` | Create a copy of the chrono. |
367
+
368
+ ### Log
369
+
370
+ Static logging utility with configurable levels.
371
+
372
+ | Method | Description |
373
+ |---|---|
374
+ | `Log.error(...msg)` | Log an error. |
375
+ | `Log.warn(...msg)` | Log a warning. |
376
+ | `Log.notice(...msg)` | Log a notice. |
377
+ | `Log.debug(...msg)` | Log a debug message. |
378
+ | `Log.debugCommonVideoEvents(player, extraEvents, report)` | Attach debug listeners for common HTML5 video events. |
379
+
380
+ **Log Levels** (set via `Log.level`):
381
+
382
+ ```javascript
383
+ nrvideo.Log.level = nrvideo.Log.Levels.DEBUG; // ALL, DEBUG, NOTICE, WARNING, ERROR, SILENT
384
+ ```
385
+
386
+ ### Constants
387
+
388
+ **General constants** (used by both pipelines):
389
+
390
+ | Constant | Value | Description |
391
+ |---|---|---|
392
+ | `Constants.AdPositions` | `{ PRE, MID, POST }` | Ad position enum. |
393
+ | `Constants.COLLECTOR` | Object | Browser beacon endpoint URLs by region. |
394
+ | `Constants.VALID_EVENT_TYPES` | Array | `['VideoAction', 'VideoAdAction', 'VideoErrorAction', 'VideoCustomAction']` |
395
+ | `Constants.MAX_PAYLOAD_SIZE` | `1048576` | Maximum payload size (1 MB). |
396
+ | `Constants.MAX_BEACON_SIZE` | `61440` | Maximum beacon size (60 KB). |
397
+ | `Constants.MAX_EVENTS_PER_BATCH` | `1000` | Maximum events per batch. |
398
+ | `Constants.INTERVAL` | `10000` | Default harvest interval (10s). |
399
+ | `Constants.QOE_KPI_KEYS` | Array | KPI field names tracked in QoE aggregates. |
400
+ | `Constants.QOE_AGGREGATE_KEYS` | Array | Metadata field names included in QoE events. |
401
+
402
+ **Connected-device constants** (named exports from `constants.js`, used by the `/vega` subpath):
403
+
404
+ | Export | Description |
405
+ |---|---|
406
+ | `MOBILE_ENDPOINT` | Production mobile collector URL (`mobile-collector.newrelic.com`). |
407
+ | `STAGING_MOBILE_ENDPOINT` | Staging mobile collector URL. |
408
+ | `NR_ENDPOINT` | Endpoint region enum: `{ US, EU, STAGING }`. |
409
+ | `DEFAULT_HARVEST_TIME` | Default harvest cadence: `60_000` ms. |
410
+ | `DEFAULT_BUFFER_SIZE` | Default buffer cap: `100` events (informational). |
411
+ | `CD_DATA_TOKENS_PAYLOAD` | `/v5/connect` request body template (positional 2-tuple). |
412
+ | `CD_DEVICE_INFO` | `/v3/data` device-identity tuple (default Vega values). |
413
+ | `CD_METADATA` | `/v3/data` session-metadata object (default Vega values). |
414
+
415
+ ---
416
+
417
+ ## Building a Custom Tracker
418
+
419
+ Extend `VideoTracker` and override getter methods and listener registration:
420
+
421
+ ```javascript
422
+ class MyPlayerTracker extends nrvideo.VideoTracker {
423
+ // Required: identify your tracker
424
+ getTrackerName() { return 'my-custom-player'; }
425
+ getTrackerVersion() { return '1.0.0'; }
426
+
427
+ // Override getters to return player metadata
428
+ getVideoId() { return this.player.getContentId(); }
429
+ getTitle() { return this.player.getTitle(); }
430
+ getSrc() { return this.player.getSrc(); }
431
+ getDuration() { return this.player.getDuration() * 1000; } // in ms
432
+ getPlayhead() { return this.player.getCurrentTime() * 1000; }
433
+ getBitrate() { return this.player.getCurrentBitrate(); }
434
+ getRenditionName() { return this.player.getQualityLabel(); }
435
+ getRenditionHeight() { return this.player.getVideoHeight(); }
436
+ getRenditionWidth() { return this.player.getVideoWidth(); }
437
+ isLive() { return this.player.isLive(); }
438
+ isMuted() { return this.player.isMuted(); }
439
+ isFullscreen() { return this.player.isFullscreen(); }
440
+ getPlayrate() { return this.player.getPlaybackRate(); }
441
+ getPlayerName() { return 'My Player'; }
442
+ getPlayerVersion() { return this.player.version; }
443
+
444
+ // Map player events to tracker methods
445
+ registerListeners() {
446
+ this.player.on('play', () => this.sendRequest());
447
+ this.player.on('playing', () => this.sendStart());
448
+ this.player.on('pause', () => this.sendPause());
449
+ this.player.on('ended', () => this.sendEnd());
450
+ this.player.on('waiting', () => this.sendBufferStart());
451
+ this.player.on('canplay', () => this.sendBufferEnd());
452
+ this.player.on('seeking', () => this.sendSeekStart());
453
+ this.player.on('seeked', () => this.sendSeekEnd());
454
+ this.player.on('error', (e) => this.sendError({
455
+ errorName: e.message,
456
+ errorCode: e.code
457
+ }));
458
+ }
459
+
460
+ unregisterListeners() {
461
+ this.player.off('play');
462
+ this.player.off('playing');
463
+ // ... remove all listeners
464
+ }
465
+ }
466
+ ```
467
+
468
+ ---
469
+
470
+ ## Getter Methods (Override These)
471
+
472
+ These methods return `null` by default. Override them in your tracker to provide player-specific data:
473
+
474
+ | Getter | Attribute Set | Description |
475
+ |---|---|---|
476
+ | `getVideoId()` | `contentId` / `adId` | Content or ad identifier. |
477
+ | `getTitle()` | `contentTitle` / `adTitle` | Content or ad title. |
478
+ | `getSrc()` | `contentSrc` / `adSrc` | Media source URL. |
479
+ | `getDuration()` | `contentDuration` / `adDuration` | Duration in ms. |
480
+ | `getPlayhead()` | `contentPlayhead` / `adPlayhead` | Current playback position in ms. |
481
+ | `getBitrate()` | `contentBitrate` / `adBitrate` | Current bitrate in bits/s. |
482
+ | `getManifestBitrate()` | `contentManifestBitrate` | Manifest/playlist declared bitrate in bps. |
483
+ | `getSegmentDownloadBitrate()` | `contentSegmentDownloadBitrate` | Measured bitrate from segment download in bps. |
484
+ | `getNetworkDownloadBitrate()` | `contentNetworkDownloadBitrate` | Network download throughput in bps. |
485
+ | `getRenditionName()` | `contentRenditionName` / `adRenditionName` | Quality label (e.g., "1080p"). |
486
+ | `getRenditionHeight()` | `contentRenditionHeight` / `adRenditionHeight` | Rendition height in px. |
487
+ | `getRenditionWidth()` | `contentRenditionWidth` / `adRenditionWidth` | Rendition width in px. |
488
+ | `isLive()` | `contentIsLive` | `true` if live stream. |
489
+ | `isMuted()` | `contentIsMuted` / `adIsMuted` | `true` if muted. |
490
+ | `isFullscreen()` | `contentIsFullscreen` | `true` if fullscreen. |
491
+ | `getPlayrate()` | `contentPlayrate` | Playback speed (1.0 = normal). |
492
+ | `getLanguage()` | `contentLanguage` / `adLanguage` | Language in locale notation (e.g., `en_US`). |
493
+ | `getCdn()` | `contentCdn` / `adCdn` | CDN serving the content. |
494
+ | `getFps()` | `contentFps` / `adFps` | Current frames per second. |
495
+ | `isAutoplayed()` | `contentIsAutoplayed` | `true` if autoplayed. |
496
+ | `getPreload()` | `contentPreload` | Preload attribute value. |
497
+ | `getPlayerName()` | `playerName` | Player name. |
498
+ | `getPlayerVersion()` | `playerVersion` | Player version. |
499
+ | `getAdQuartile()` | `adQuartile` | Ad quartile (0–4). |
500
+ | `getAdPosition()` | `adPosition` | Ad position (`pre`, `mid`, `post`). |
501
+ | `getAdPartner()` | `adPartner` | Ad partner (e.g., `ima`, `freewheel`). |
502
+ | `getAdCreativeId()` | `adCreativeId` | Ad creative identifier. |
503
+
504
+ ---
505
+
506
+ ## Quality of Experience (QoE)
507
+
508
+ QoE tracking is **enabled by default**. The library tracks and reports QoE KPI metrics as `QOE_AGGREGATE` events. To disable, set `qoeAggregate: false` in the config object. The harvest interval multiplier can be configured via `qoeIntervalFactor` (default: `2`).
509
+
510
+ | KPI | Description |
511
+ |---|---|
512
+ | `startupTime` | Time from `CONTENT_REQUEST` to `CONTENT_START` in ms. |
513
+ | `peakBitrate` | Maximum bitrate observed during playback. |
514
+ | `averageBitrate` | Playtime-weighted average bitrate in bps. |
515
+ | `totalPlaytime` | Total content viewing time in ms (excludes pausing, buffering, ads). |
516
+ | `totalRebufferingTime` | Total ms spent rebuffering (excludes initial buffering). |
517
+ | `rebufferingRatio` | `(totalRebufferingTime / totalPlaytime) × 100`. |
518
+ | `hadStartupError` | `true` if error occurred before `CONTENT_START`. |
519
+ | `hadPlaybackError` | `true` if error occurred at any time during playback. |
520
+
521
+ QoE KPIs are refreshed before each harvest drain and always included in the final harvest at `CONTENT_END`.
522
+
523
+ ---
524
+
525
+ ## Obfuscation Rules
526
+
527
+ Mask sensitive data in event payloads before transmission using regex-based rules:
528
+
529
+ ```javascript
530
+ config: {
531
+ obfuscate: [
532
+ { regex: /\/user\/[^\/?"]+/, replacement: '/user/[REDACTED]' },
533
+ { regex: /token=[^&"]+/, replacement: 'token=[MASKED]' },
534
+ ]
535
+ }
28
536
  ```
29
537
 
30
- ### Configuration Options
538
+ | Field | Type | Description |
539
+ |---|---|---|
540
+ | `regex` | `string` \| `RegExp` | Pattern to match in the serialized JSON payload. |
541
+ | `replacement` | `string` | Replacement string for each match. |
31
542
 
32
- - **qoeAggregate** (boolean, optional, default: `true`): Controls whether Quality of Experience (QoE) events are aggregated and sent to New Relic. Set to `false` if you want to disable QoE event collection.
543
+ 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`.
33
544
 
545
+ ---
34
546
 
35
- ## APIs
547
+ ## Build & Development
36
548
 
37
- Some of the APIs exposed and commonly used are:
549
+ ```bash
550
+ # Install dependencies
551
+ npm install
38
552
 
39
- - `tracker.setUserId("userId")` &mdash; Set the user ID.
40
- - `tracker.setHarvestInterval(30000)` &mdash; Set the harvest interval time (in milliseconds).
41
- - `tracker.setOptions({ customData: { key: value } })` &mdash; Set custom options or data.
42
- - `tracker.sendCustom("CustomEvent", { data: "custom-test" })` &mdash; Send a custom event.
553
+ # Production build
554
+ npm run build
43
555
 
44
- Any event emitted by the tracker will be sent to New Relic and processed according to its type.
556
+ # Development build (unminified)
557
+ npm run build:dev
45
558
 
46
- Once the tracker is added, any event it emits will be sent to New Relic and processed by the following functions:
559
+ # Watch mode (production)
560
+ npm run watch
561
+
562
+ # Watch mode (development)
563
+ npm run watch:dev
564
+
565
+ # Clean build artifacts
566
+ npm run clean
567
+ ```
568
+
569
+ ## Distribution Formats
570
+
571
+ The build produces multiple output formats including subpath exports for tree-shaking:
572
+
573
+ | Format | Path | Usage |
574
+ |---|---|---|
575
+ | **UMD** | `dist/umd/nrvideo.min.js` | `<script>` tag → `window.nrvideo` |
576
+ | **CommonJS** | `dist/cjs/index.js` | `require('@newrelic/video-core')` — both pipelines |
577
+ | **ES Module** | `dist/esm/index.js` | `import nrvideo from '@newrelic/video-core'` — both pipelines |
578
+ | **CJS Browser** | `dist/cjs/browser/index.js` | `require('@newrelic/video-core/browser')` — Browser pipeline only |
579
+ | **CJS Connected-Device** | `dist/cjs/vega/index.js` | `require('@newrelic/video-core/vega')` — connected-device pipeline only |
580
+ | **ESM Browser** | `dist/esm/browser/index.js` | `import ... from '@newrelic/video-core/browser'` |
581
+ | **ESM Connected-Device** | `dist/esm/vega/index.js` | `import ... from '@newrelic/video-core/vega'` |
582
+
583
+ ### Subpath imports
584
+
585
+ Import the specific subpath to ship only the harvester chain you need. The unused pipeline is tree-shaken from the bundle.
47
586
 
48
587
  ```javascript
49
- tracker.sendVideoAction("VideoEvent", { data: 1 });
50
- tracker.sendVideoAdAction("AdEvent", { data: "test-1" });
51
- tracker.sendVideoErrorAction("ErrorEvent", { data: "error-test" });
52
- tracker.sendVideoCustomAction("CustomEvent", { data: "custom-test" });
588
+ // Browser pipeline only — tree-shakes out the connected-device chain
589
+ import nrvideo from '@newrelic/video-core/browser';
590
+
591
+ // Connected-device pipeline only — tree-shakes out Browser harvester and harvestScheduler
592
+ import nrvideo from '@newrelic/video-core/vega';
593
+
594
+ // Both pipelines — use with the default entry
595
+ import nrvideo from '@newrelic/video-core';
53
596
  ```
54
597
 
598
+ ## Testing
599
+
600
+ ```bash
601
+ # Run tests with coverage
602
+ npm test
603
+ ```
604
+
605
+ Tests are written using [Jest](https://jestjs.io/) and located in the `test/` directory.
606
+
607
+ ---
608
+
55
609
  ## Data Model
56
610
 
57
- To understand which actions and attributes are captured and emitted by the tracker under different event types, see [DataModel.md](DATAMODEL.md).
611
+ For a comprehensive reference of all event types, attributes, and action names, see [DATAMODEL.md](DATAMODEL.md).
612
+
613
+ ---
614
+
615
+ ## License
616
+
617
+ This project is licensed under the [Apache 2.0 License](LICENSE).
618
+
619
+ ---
620
+
621
+ ## Support
622
+
623
+ Should you need assistance with New Relic products, you are in good hands with several support channels.
624
+
625
+ If the issue has been confirmed as a bug or is a feature request, please file a [GitHub issue](../../issues).
626
+
627
+ **Support Channels:**
628
+
629
+ - [New Relic Documentation](https://docs.newrelic.com): Comprehensive guidance for using our platform
630
+ - [New Relic Community](https://discuss.newrelic.com): The best place to engage in troubleshooting questions
631
+ - [New Relic University](https://learn.newrelic.com): A range of online training for New Relic users of every level
632
+ - [New Relic Technical Support](https://support.newrelic.com): 24/7/365 ticketed support. Read more about our Technical Support Offerings
633
+
634
+ **Additional Resources:**
635
+
636
+ - [DATAMODEL.md](DATAMODEL.md) — Complete event and attribute reference
637
+ - [DEVELOPING.md](DEVELOPING.md) — Building and testing instructions
638
+ - [CONTRIBUTING.md](CONTRIBUTING.md) — Contribution guidelines
639
+
640
+ ---
58
641
 
59
- ## Documentation
642
+ ## Contributing
60
643
 
61
- 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.
644
+ 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.
62
645
 
63
- # Support
646
+ 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.
64
647
 
65
- 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.
648
+ For more details on how best to contribute, see [CONTRIBUTING.md](CONTRIBUTING.md).
66
649
 
67
- 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.
650
+ ---
68
651
 
69
- ## Community
652
+ ## A Note About Vulnerabilities
70
653
 
71
- 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:
654
+ 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.
72
655
 
73
- https://discuss.newrelic.com/t/video-core-js-tracker/100303
656
+ 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/).
74
657
 
75
- ## Issues / enhancement requests
658
+ ---
76
659
 
77
- 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.
660
+ ## Acknowledgments
78
661
 
79
- # Contributing
662
+ If you would like to contribute to this project, review [these guidelines](CONTRIBUTING.md).
80
663
 
81
- 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).
664
+ To all contributors, we thank you! Without your contribution, this project would not be what it is today.
82
665
 
83
- 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.
666
+ ---
84
667
 
85
- # License
668
+ ## License
86
669
 
87
- This project is distributed under the [Apache 2.0](https://apache.org/licenses/LICENSE-2.0.txt) License.
670
+ The Video Core JS library is licensed under the [Apache 2.0 License](LICENSE).
88
671
 
89
- 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).
672
+ 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).