@newrelic/video-core 4.1.8 → 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 (41) hide show
  1. package/README.md +140 -27
  2. package/dist/cjs/browser/index.js +3 -0
  3. package/dist/cjs/browser/index.js.LICENSE.txt +6 -0
  4. package/dist/cjs/browser/index.js.map +1 -0
  5. package/dist/cjs/index.js +1 -1
  6. package/dist/cjs/index.js.LICENSE.txt +1 -1
  7. package/dist/cjs/index.js.map +1 -1
  8. package/dist/cjs/vega/index.js +3 -0
  9. package/dist/cjs/vega/index.js.LICENSE.txt +6 -0
  10. package/dist/cjs/vega/index.js.map +1 -0
  11. package/dist/esm/browser/index.js +3 -0
  12. package/dist/esm/browser/index.js.LICENSE.txt +6 -0
  13. package/dist/esm/browser/index.js.map +1 -0
  14. package/dist/esm/index.js +1 -1
  15. package/dist/esm/index.js.LICENSE.txt +1 -1
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/vega/index.js +3 -0
  18. package/dist/esm/vega/index.js.LICENSE.txt +6 -0
  19. package/dist/esm/vega/index.js.map +1 -0
  20. package/dist/umd/nrvideo.min.js +1 -1
  21. package/dist/umd/nrvideo.min.js.LICENSE.txt +1 -1
  22. package/dist/umd/nrvideo.min.js.map +1 -1
  23. package/package.json +25 -7
  24. package/src/{agent.js → browser/agent.js} +14 -35
  25. package/src/{harvestScheduler.js → browser/harvestScheduler.js} +55 -137
  26. package/src/browser/index.js +58 -0
  27. package/src/connectedDevice/connectedDeviceAgent.js +116 -0
  28. package/src/connectedDevice/connectedDeviceConstants.js +121 -0
  29. package/src/connectedDevice/connectedDeviceHarvester.js +522 -0
  30. package/src/connectedDevice/index.js +49 -0
  31. package/src/constants.js +2 -0
  32. package/src/core.js +4 -18
  33. package/src/index.js +9 -10
  34. package/src/recordEvent.js +56 -49
  35. package/src/tracker.js +14 -4
  36. package/src/utils/eventBuilder.js +126 -0
  37. package/src/utils/harvestTimer.js +109 -0
  38. package/src/{utils.js → utils/index.js} +2 -2
  39. package/src/utils/qoeFilters.js +149 -0
  40. package/src/videoConfiguration.js +56 -10
  41. package/src/videotracker.js +26 -9
package/README.md CHANGED
@@ -3,9 +3,14 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@newrelic/video-core.svg)](https://www.npmjs.com/package/@newrelic/video-core)
4
4
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
5
5
 
6
+
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.
7
8
 
8
- 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.
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
+
9
14
 
10
15
  Events are categorized into four distinct types:
11
16
 
@@ -95,13 +100,10 @@ class MyPlayerTracker extends nrvideo.VideoTracker {
95
100
  // 2. Configure and register the tracker
96
101
  const options = {
97
102
  info: {
98
- licenseKey: 'YOUR_LICENSE_KEY',
99
- beacon: 'bam.nr-data.net',
103
+ licenseKey: 'YOUR_LICENSE_KEY',
104
+ beacon: 'bam.nr-data.net',
100
105
  applicationID: 'YOUR_APPLICATION_ID',
101
106
  },
102
- config: {
103
- qoeAggregate: true,
104
- },
105
107
  };
106
108
 
107
109
  const player = document.getElementById('myPlayer');
@@ -113,41 +115,39 @@ nrvideo.Core.addTracker(tracker, options);
113
115
 
114
116
  ## Configuration
115
117
 
116
- The `options` object passed to `Core.addTracker()` consists of two parts:
118
+ The `options` object passed to `Core.addTracker()` consists of two top-level keys: `info` (credentials and pipeline config) and `config` (optional behaviour settings).
117
119
 
118
- ### Getting Your Configuration
120
+ ---
121
+
122
+ ### Browser / Web Setup
119
123
 
120
- Before initializing the tracker, obtain your New Relic configuration:
124
+ **Obtain your credentials:**
121
125
 
122
126
  1. Log in to [one.newrelic.com](https://one.newrelic.com)
123
127
  2. Navigate to the video agent onboarding flow
124
128
  3. Copy your credentials: `licenseKey`, `beacon`, and `applicationId`
125
129
 
126
- ### Info Object (Required)
127
-
128
- Obtained through the New Relic onboarding process. Two configuration modes are supported:
129
-
130
- **Mode 1 — With Application ID and Beacon:**
130
+ **With Application ID and Beacon:**
131
131
 
132
132
  ```javascript
133
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
134
+ licenseKey: 'YOUR_LICENSE_KEY', // Required
135
+ applicationID: 'YOUR_APPLICATION_ID', // Required
136
+ beacon: 'bam.nr-data.net', // Required when applicationID is provided
137
137
  }
138
138
  ```
139
139
 
140
- **Mode 2 — With App Name and Region:**
140
+ **With App Name and Region:**
141
141
 
142
142
  ```javascript
143
143
  info: {
144
144
  licenseKey: 'YOUR_LICENSE_KEY', // Required
145
- appName: 'My Video App', // Required when no applicationID
146
- region: 'US', // Required when no applicationID
145
+ appName: 'My Video App', // Required when no applicationID
146
+ region: 'US', // Required when no applicationID — 'US' | 'EU' | 'staging'
147
147
  }
148
148
  ```
149
149
 
150
- **Valid Beacon Endpoints:**
150
+ **Valid Browser Beacon Endpoints:**
151
151
 
152
152
  | Region | Beacon |
153
153
  |---|---|
@@ -156,11 +156,89 @@ info: {
156
156
  | Staging | `staging-bam-cell.nr-data.net` |
157
157
  | GOV | `gov-bam.nr-data.net` |
158
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',
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',
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:**
228
+
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
+
159
238
  ### Config Object (Optional)
160
239
 
161
240
  | Option | Type | Default | Description |
162
241
  |---|---|---|---|
163
- | `qoeAggregate` | `boolean` | `true` | Enable Quality of Experience event aggregation. QoE is enabled out of the box; set to `false` to disable. |
164
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. |
165
243
  | `obfuscate` | `array` | `[]` | Regex-based rules to mask sensitive data before transmission. See [Obfuscation Rules](#obfuscation-rules). |
166
244
 
@@ -192,7 +270,6 @@ Static class managing tracker registration and event dispatch.
192
270
  | `Core.getTrackers()` | Returns the array of currently registered trackers. |
193
271
  | `Core.send(eventType, actionName, data)` | Sends an event to the collector. Called internally by event handlers. |
194
272
  | `Core.sendError(att)` | Sends a `VideoErrorAction` with `actionName: "ERROR"`. For external/app-level errors. |
195
- | `Core.forceHarvest()` | Forces an immediate harvest of all pending events. Returns a `Promise`. |
196
273
 
197
274
  ### VideoTracker
198
275
 
@@ -308,15 +385,32 @@ nrvideo.Log.level = nrvideo.Log.Levels.DEBUG; // ALL, DEBUG, NOTICE, WARNING, ER
308
385
 
309
386
  ### Constants
310
387
 
388
+ **General constants** (used by both pipelines):
389
+
311
390
  | Constant | Value | Description |
312
391
  |---|---|---|
313
392
  | `Constants.AdPositions` | `{ PRE, MID, POST }` | Ad position enum. |
314
- | `Constants.COLLECTOR` | Object | Beacon endpoint URLs by region. |
393
+ | `Constants.COLLECTOR` | Object | Browser beacon endpoint URLs by region. |
315
394
  | `Constants.VALID_EVENT_TYPES` | Array | `['VideoAction', 'VideoAdAction', 'VideoErrorAction', 'VideoCustomAction']` |
316
395
  | `Constants.MAX_PAYLOAD_SIZE` | `1048576` | Maximum payload size (1 MB). |
317
396
  | `Constants.MAX_BEACON_SIZE` | `61440` | Maximum beacon size (60 KB). |
318
397
  | `Constants.MAX_EVENTS_PER_BATCH` | `1000` | Maximum events per batch. |
319
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). |
320
414
 
321
415
  ---
322
416
 
@@ -474,13 +568,32 @@ npm run clean
474
568
 
475
569
  ## Distribution Formats
476
570
 
477
- The build produces three output formats:
571
+ The build produces multiple output formats including subpath exports for tree-shaking:
478
572
 
479
573
  | Format | Path | Usage |
480
574
  |---|---|---|
481
575
  | **UMD** | `dist/umd/nrvideo.min.js` | `<script>` tag → `window.nrvideo` |
482
- | **CommonJS** | `dist/cjs/index.js` | `require('@newrelic/video-core')` |
483
- | **ES Module** | `dist/esm/index.js` | `import nrvideo from '@newrelic/video-core'` |
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.
586
+
587
+ ```javascript
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';
596
+ ```
484
597
 
485
598
  ## Testing
486
599
 
@@ -0,0 +1,3 @@
1
+ /*! For license information please see index.js.LICENSE.txt */
2
+ (()=>{"use strict";const t={d:(e,i)=>{if(Array.isArray(i))for(var s=0;s<i.length;){var r=i[s++],a=i[s++];t.o(e,r)?0===a&&s++:0===a?Object.defineProperty(e,r,{enumerable:!0,value:i[s++]}):Object.defineProperty(e,r,{enumerable:!0,get:a})}else for(var r in i)t.o(i,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:i[r]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}};let e={};t.r(e),t.d(e,{Chrono:()=>u,Constants:()=>h,Core:()=>w,Emitter:()=>l,HarvestScheduler:()=>q,Log:()=>n,NrVideoEventAggregator:()=>P,OptimizedHttpClient:()=>F,RetryQueueHandler:()=>H,Tracker:()=>m,VideoTracker:()=>C,VideoTrackerState:()=>D,default:()=>M,getRegisteredHarvester:()=>p,recordEvent:()=>v,version:()=>d.rE,videoAnalyticsHarvester:()=>G});class i{static error(...t){s(t,i.Levels.ERROR,"darkred")}static warn(...t){s(t,i.Levels.WARNING,"darkorange")}static notice(...t){s([].slice.call(arguments),i.Levels.NOTICE,"darkcyan")}static debug(...t){s(t,i.Levels.DEBUG,"indigo")}static debugCommonVideoEvents(t,e,s){try{if(i.level<=i.Levels.DEBUG){s=s||function(t){i.debug("Event: "+t.type)};var r=["canplay","buffering","waiting","ended","play","playing","pause","resume","error","abort","seek","seeking","seeked","stalled","dispose","loadeddata","loadstart","loadedmetadata"];e&&(null===e[0]?(e.shift(),r=e):r=r.concat(e));for(var a=0;a<r.length;a++)"function"==typeof t?t.call(window,r[a],s):t.on?t.on(r[a],s):t.addEventListener?t.addEventListener(r[a],s):t.addEventHandler?t.addEventHandler(r[a],s):i.warn("debugCommonVideoEvents: No common listener function found for ",t)}}catch(t){i.warn(t)}}}function s(t,e,s){e=e||i.Levels.NOTICE,s=s||"darkcyan";var n,o,h,d,l,c,u=i.prefix;(i.includeTime&&(u+=(n=new Date,o=("0"+n.getDate()).slice(-2),h=("0"+n.getMinutes()).slice(-2),d=("0"+n.getSeconds()).slice(-2),l=("00"+n.getMilliseconds()).slice(-3),"["+o+":"+h+":"+d+"."+l+"] ")),u+=function(t){return a[t]}(e)+":",i.level<=e&&e!==i.Levels.SILENT)&&(!i.colorful||"undefined"!=typeof document&&document.documentMode?r(t,u):(c=e===i.Levels.ERROR&&console.error?console.error:e===i.Levels.WARNING&&console.warn?console.warn:e===i.Levels.DEBUG&&console.debug&&null==window.cast?console.debug:console.log,u="%c"+u,t.splice(0,0,u,"color: "+s),c.apply(console,t)))}function r(t,e){if(t instanceof Array)for(var i in t)r(t[i],e);else"string"==typeof t?console.log(e+" "+t):(console.log(e+"↵"),console.log(t))}i.Levels={SILENT:5,ERROR:4,WARNING:3,NOTICE:2,DEBUG:1,ALL:0},i.level=i.Levels.ERROR,i.colorful=!0,i.includeTime=!0,i.prefix="[nrvideo]";const a={4:"e",3:"w",2:"n",1:"d"};!function(){if("undefined"!=typeof window&&window.location&&window.location.search){var t=/\?.*&*nrvideo-debug=(.+)/i.exec(window.location.search);null!==t&&("true"===t[1]?i.level=i.Levels.ALL:i.level=t[1]),null!==/\?.*&*nrvideo-colors=false/i.exec(window.location.search)&&(i.colorful=!1)}}();const n=i;class o{}o.AdPositions={PRE:"pre",MID:"mid",POST:"post"},o.COLLECTOR={US:["bam.nr-data.net","bam-cell.nr-data.net"],EU:"bam.eu01.nr-data.net",JP:"bam.jp.nr-data.net",Staging:"staging-bam-cell.nr-data.net",GOV:"gov-bam.nr-data.net"},o.VALID_EVENT_TYPES=["VideoAction","VideoAdAction","VideoErrorAction","VideoCustomAction"],o.MAX_PAYLOAD_SIZE=1048576,o.MAX_BEACON_SIZE=61440,o.MAX_EVENTS_PER_BATCH=1e3,o.INTERVAL=1e4,o.DEFAULT_QOE_INTERVAL_FACTOR=2,o.QOE_KPI_KEYS=["startupTime","peakBitrate","averageBitrate","totalPlaytime","totalRebufferingTime","rebufferingRatio","hadStartupError","hadPlaybackError","numberOfErrors"],o.QOE_AGGREGATE_KEYS=["coreVersion","instrumentation.name","instrumentation.provider","instrumentation.version","isBackgroundEvent","playerName","playerVersion","src","viewId","viewSession","contentIsAutoplayed","contentIsMuted","contentRenditionHeight","contentRenditionWidth","contentSrc","numberOfVideos","pageUrl","trackerName","trackerVersion","contentDuration","contentPlayrate","contentPlayhead","contentPreload","elapsedTime","contentTitle","contentId","contentIsLive","deviceType","deviceGroup","deviceManufacturer","deviceModel","deviceName","deviceSize","deviceUuid","contentRenditionName","contentIsFullscreen","contentCdn","contentFps","asnOrganization","asnLongitude","asnLatitude","asn","timeSinceRequested","timeSinceStarted"];const h=o,d={rE:"5.0.0"};const l=class{on(t,e){if(this._listeners=this._listeners||{},"function"==typeof e)return this._listeners[t]=this._listeners[t]||[],this._listeners[t].push(e),this}off(t,e){if(this._listeners=this._listeners||{},this._listeners[t]){var i=this._listeners[t].indexOf(e);-1!==i&&this._listeners[t].splice(i,1)}return this}emit(t,e,i){return this._listeners=this._listeners||{},i=i||{},Array.isArray(this._listeners[e])&&this._listeners[e].forEach(s=>{s.call(this,{eventType:t,type:e,data:i,target:this})}),Array.isArray(this._listeners["*"])&&this._listeners["*"].forEach(s=>{s.call(this,{eventType:t,type:e,data:i,target:this})}),this}};class c{constructor(){this.reset()}reset(){this.startTime=0,this.stopTime=0,this.accumulator=0,this.offset=0}getDeltaTime(){return this.startTime?this.offset+((new Date).getTime()-this.startTime):null}start(){this.startTime=(new Date).getTime(),this.stopTime=0}stop(){return this.stopTime=(new Date).getTime(),this.startTime<this.stopTime&&(this.accumulator+=this.stopTime-this.startTime),this.getDeltaTime()}getDuration(){return this.stopTime?this.accumulator+this.offset:this.accumulator+(this.getDeltaTime()??0)}clone(){var t=new c;return t.startTime=this.startTime,t.stopTime=this.stopTime,t.offset=this.offset,t.accumulator=this.accumulator,t}}const u=c;class g extends l{constructor(t){super(),this.customData={},this.heartbeat=null,this.parentTracker=null,this._trackerReadyChrono=new u,this._trackerReadyChrono.start(),this._actionTable=h.ACTION_TABLE,this._actionAdTable=h.ACTION_AD_TABLE,t=t||{},this.setOptions(t)}setOptions(t){t&&(t.parentTracker&&(this.parentTracker=t.parentTracker),t.customData&&(this.customData=t.customData),t.heartbeat&&(this.heartbeat=t.heartbeat))}dispose(){this.unregisterListeners()}registerListeners(){}unregisterListeners(){}getHeartbeat(){return this.state._isAd?2e3:this.heartbeat?this.heartbeat:this.parentTracker&&this.parentTracker.heartbeat?this.parentTracker.heartbeat:3e4}startHeartbeat(){this._heartbeatInterval=setInterval(this.sendHeartbeat.bind(this),Math.max(this.getHeartbeat(),2e3))}stopHeartbeat(){this._heartbeatInterval&&clearInterval(this._heartbeatInterval)}sendHeartbeat(t){this.sendVideoAction(g.Events.HEARTBEAT,t)}getAttributes(t,e){(t=t||{}).trackerName=this.getTrackerName(),t.trackerVersion=this.getTrackerVersion(),t.coreVersion=d.rE,t.timeSinceTrackerReady=this._trackerReadyChrono.getDeltaTime();for(let e in this.customData)t[e]=this.customData[e];return null!=document.hidden&&(t.isBackgroundEvent=document.hidden),t}getTrackerVersion(){return d.rE}getTrackerName(){return"base-tracker"}sendVideoAction(t,e){this.emit("VideoAction",t,this.getAttributes(e))}sendVideoAdAction(t,e){this.emit("VideoAdAction",t,this.getAttributes(e))}sendVideoErrorAction(t,e){let i=this.isAd()?"adError":"videoError";this.emit("VideoErrorAction",t,this.getAttributes(e,i))}sendVideoCustomAction(t,e){this.emit("VideoCustomAction",t,this.getAttributes(e,"customAction"))}setHarvestInterval(t){const e=this.getHarvester?.();if(e)try{e.setHarvestInterval(t)}catch(t){return void n.error("Failed to set harvest interval:",t.message)}else n.error("Tracker has no harvester; setHarvestInterval ignored")}getHarvester(){return null}}g.Events={HEARTBEAT:"HEARTBEAT",QOE_AGGREGATE:"QOE_AGGREGATE"};const m=g;function f(t){try{return JSON.stringify(t,(()=>{const t=new WeakSet;return(e,i)=>{if("object"==typeof i&&null!==i){if(t.has(i))return;t.add(i)}return i}})())??""}catch(t){return n.error("Error stringifying value:",t.message),""}}function E(t){if("string"==typeof t&&t.length)return t.length;if("object"==typeof t){if("undefined"!=typeof ArrayBuffer&&t instanceof ArrayBuffer&&t.byteLength)return t.byteLength;if("undefined"!=typeof Blob&&t instanceof Blob&&t.size)return t.size;if(!("undefined"!=typeof FormData&&t instanceof FormData))try{return f(t).length}catch(t){return}}}function A(t,e,i,{addTimeSinceLoad:s=!1}={}){const{appName:r,applicationID:a}=i,{qoe:n,...o}=e,d=n?{...n}:{},l={...a?{}:{appName:r},timestamp:Date.now(),...s?{timeSinceLoad:"undefined"!=typeof window&&window.performance?window.performance.now()/1e3:null}:{}},c={...o,eventType:t,...l};let u=null;if("VideoAction"===t){const t=(g=h.QOE_AGGREGATE_KEYS,f=e,g&&Array.isArray(g)&&0!==g.length?f&&"object"==typeof f?g.reduce((t,e)=>(e in f&&(t[e]=f[e]),t),{}):{}:f);u={eventType:"VideoAction",actionName:m.Events.QOE_AGGREGATE,qoeAggregateVersion:"1.0.0",...d,...t,...l}}var g,f;return{eventObject:c,qoeEventObject:u}}const T=Object.create(null);function p(t){return T[t]}function v(t,e={}){try{if(!h.VALID_EVENT_TYPES.includes(t))return n.warn("Invalid event type provided to recordEvent",{eventType:t}),!1;const i="Vega"===e.src,s=i?"Vega":"Browser",r="undefined"!=typeof window?window:void 0,a=i?globalThis.__NRVIDEO_CD__?.info:r?.NRVIDEO?.info;if(!a)return;const o=T[s];if(!o)return n.warn("No harvester registered for routing key",{routingKey:s}),!1;return function(t,e,i,s,r,{addTimeSinceLoad:a=!1}={}){if(!s)return!1;const{eventObject:n,qoeEventObject:o}=A(t,e,i,{addTimeSinceLoad:a}),h=s.addEvent(n);return o&&r?h&&s.addEvent(o):h}(t,e,a,o,i?globalThis.__NRVIDEO_CD__?.config?.qoeAggregate:r?.NRVIDEO?.config?.qoeAggregate,{addTimeSinceLoad:!i})}catch(t){return n.error("Failed to record event:",t.message),!1}}const{COLLECTOR:S}=h;class y{setConfiguration(t,e,i){return!!("Vega"===i?this.validateVegaFields(t):this.validateRequiredFields(t))&&(!!this.validateConfigFields(e)&&(this.initializeGlobalConfig(t,e,i),n.notice("Video analytics configuration initialized successfully"),!0))}validateVegaFields(t){return t&&"object"==typeof t?t.applicationToken?!!["US","EU","staging","GOV"].includes(t.endpoint)||(n.error("Invalid endpoint (must be US, EU, staging, or GOV)"),!1):(n.error("applicationToken is required"),!1):(n.error("Configuration must be an object"),!1)}validateRequiredFields(t){if(!t||"object"!=typeof t)return n.error("Configuration must be an object"),!1;const{licenseKey:e,appName:i,region:s,applicationID:r,beacon:a}=t;if(!e)return n.error("licenseKey is required"),!1;if(r){if(!a)return n.error("beacon is required when applicationID is provided"),!1;if(!Object.values(S).flatMap(t=>t).includes(a))return n.error(`Invalid beacon: ${a}`),!1}else{if(!i||!s)return n.error("appName and region are required when applicationID is not provided"),!1;if(!S[s])return n.error(`Invalid region: ${s}. Valid regions are: ${Object.keys(S).join(", ")}`),!1}return!0}validateConfigFields(t){if(null==t)return!0;if("object"!=typeof t||Array.isArray(t))return n.error("config must be an object"),!1;const{qoeAggregate:e,obfuscate:i}=t;return void 0!==e&&"boolean"!=typeof e?(n.error("qoeAggregate must be a boolean"),!1):!(void 0!==i&&!Array.isArray(i))||(n.error("obfuscate must be an array"),!1)}filterObfuscateRules(t){return t?t.filter(t=>{const e=void 0!==t.regex&&("string"==typeof t.regex||t.regex instanceof RegExp),i=void 0!==t.replacement&&"string"==typeof t.replacement;return!(!e||!i)||(n.warn("obfuscate rule missing required 'regex' (string|RegExp) and/or 'replacement' (string), skipping:",t),!1)}):[]}sanitizeQoeIntervalFactor(t){return null==t?h.DEFAULT_QOE_INTERVAL_FACTOR:"number"==typeof t&&Number.isInteger(t)&&t>=1?t:(n.warn(`Invalid qoeIntervalFactor "${t}" — must be a positive integer. Defaulting to ${h.DEFAULT_QOE_INTERVAL_FACTOR}.`),h.DEFAULT_QOE_INTERVAL_FACTOR)}initializeGlobalConfig(t,e,i){if("Vega"===i)return void(globalThis.__NRVIDEO_CD__={info:{accountId:t.accountId,applicationToken:t.applicationToken,endpoint:t.endpoint,...t.appName?{appName:t.appName}:{},...t.applicationID?{applicationID:t.applicationID}:{},...t.deviceInfo?{deviceInfo:t.deviceInfo}:{}},config:{qoeAggregate:e?.qoeAggregate??!0,qoeIntervalFactor:this.sanitizeQoeIntervalFactor(e?.qoeIntervalFactor),obfuscate:this.filterObfuscateRules(e?.obfuscate)}});let{licenseKey:s,appName:r,region:a,beacon:n,applicationID:o}=t;n="US"===a?h.COLLECTOR.US[0]:n||S[a],window.NRVIDEO={info:{...a?{region:a}:{},beacon:n,licenseKey:s,applicationID:o,...o?{}:{appName:r}},config:{qoeAggregate:e?.qoeAggregate??!0,qoeIntervalFactor:this.sanitizeQoeIntervalFactor(e?.qoeIntervalFactor),obfuscate:this.filterObfuscateRules(e?.obfuscate)}}}}const _=new y;class R{static addTracker(t,e){var i,s,r;e?.info&&(i=e.info,s=e?.config,r=e?.src,_.setConfiguration(i,s,r)),t.on&&t.emit?(B.push(t),t.on("*",b),"function"==typeof t.trackerInit&&t.trackerInit()):n.error("Tried to load a non-tracker.",t)}static removeTracker(t){t.off("*",b),t.dispose();let e=B.indexOf(t);-1!==e&&B.splice(e,1)}static getTrackers(){return B}static send(t,e,i){return v(t,{actionName:e,...i})}static sendError(t){return v("VideoErrorAction",{actionName:"ERROR",...t})}}let B=[];function b(t){try{let e=function(t){let e={};for(let i in t)null!==t[i]&&void 0!==t[i]&&(e[i]=t[i]);return e}(t.data);n.level<=n.Levels.DEBUG?n.notice("Sent",t.type,e):n.notice("Sent",t.type),R.send(t.eventType,t.type,e)}catch(t){n.error("Error in event handler:",t.message)}}const w=R;const D=class{constructor(){this.reset(),this._createdAt=Date.now(),this._hb=!0,this._acc=0,this._bufferAcc=0}reset(){this._viewSession=null,this._viewCount=0,this._isAd=!1,this.numberOfErrors=0,this.numberOfAds=0,this.numberOfVideos=0,this.totalPlaytime=0,this.weightedAverageBitrate=0,this.totalAdPlaytime=0,this.isAdBreak=!1,this.initialBufferingHappened=!1,this.startupTime=null,this.peakBitrate=0,this.partialAverageBitrate=0,this._totalBitrateDuration=0,this.weightedBitrate=0,this._lastBitrate=null,this._lastBitrateChangeTimestamp=null,this._downloadBitrates=[],this._lastDownloadBitrate=null,this.totalSwitchUps=0,this.totalSwitchDowns=0,this.totalPauseTime=0,this._playedRenditions=new Set,this.hadStartupError=!1,this.hadPlaybackError=!1,this.totalRebufferingTime=0,this.resetFlags(),this.resetChronos()}resetFlags(){this.isPlayerReady=!1,this.isRequested=!1,this.isStarted=!1,this.isPaused=!1,this.isSeeking=!1,this.isBuffering=!1,this.isPlaying=!1}resetChronos(){this.timeSinceRequested=new u,this.timeSinceStarted=new u,this.timeSincePaused=new u,this.timeSinceSeekBegin=new u,this.timeSinceBufferBegin=new u,this.timeSinceAdBreakStart=new u,this.timeSinceLastDownload=new u,this.timeSinceLastHeartbeat=new u,this.timeSinceLastRenditionChange=new u,this.timeSinceLastAdQuartile=new u,this.timeSinceLastAd=new u,this.timeSinceLastError=new u,this.timeSinceLastAdError=new u,this.timeSinceResumed=new u,this.timeSinceSeekEnd=new u,this.playtimeSinceLastEvent=new u,this.customTimeSinceAttributes={},this.elapsedTime=new u,this.bufferElapsedTime=new u,this._totalAdPlaytime=new u}isAd(){return this._isAd}setIsAd(t){this._isAd=t}setTimeSinceAttribute(t){this.customTimeSinceAttributes[t]=new u,this.customTimeSinceAttributes[t].start()}removeTimeSinceAttribute(t){delete this.customTimeSinceAttributes[t]}getViewSession(){if(!this._viewSession){let t=(new Date).getTime(),e=Math.random().toString(36).substring(2)+Math.random().toString(36).substring(2);this._viewSession=t+"-"+e}return this._viewSession}getViewId(){return this.getViewSession()+"-"+this._viewCount}getStateAttributes(t){t=t||{},this.isAd()?(this.isRequested&&(t.timeSinceAdRequested=this.timeSinceRequested.getDeltaTime(),t.timeSinceLastAdHeartbeat=this.timeSinceLastHeartbeat.getDeltaTime()),this.isStarted&&(t.timeSinceAdStarted=this.timeSinceStarted.getDeltaTime()),this.isPaused&&(t.timeSinceAdPaused=this.timeSincePaused.getDeltaTime()),this.isBuffering&&(t.timeSinceAdBufferBegin=this.timeSinceBufferBegin.getDeltaTime()),this.isSeeking&&(t.timeSinceAdSeekBegin=this.timeSinceSeekBegin.getDeltaTime()),this.isAdBreak&&(t.timeSinceAdBreakBegin=this.timeSinceAdBreakStart.getDeltaTime()),this.numberOfErrors>0&&this.timeSinceLastAdError.startTime>0&&(t.timeSinceLastAdError=this.timeSinceLastAdError.getDeltaTime()),t.numberOfAds=this.numberOfAds):(this.isRequested&&(t.timeSinceRequested=this.timeSinceRequested.getDeltaTime(),t.timeSinceLastHeartbeat=this.timeSinceLastHeartbeat.getDeltaTime()),this.isStarted&&(t.timeSinceStarted=this.timeSinceStarted.getDeltaTime()),this.isPaused&&(t.timeSincePaused=this.timeSincePaused.getDeltaTime()),this.isBuffering&&(t.timeSinceBufferBegin=this.timeSinceBufferBegin.getDeltaTime()),this.isSeeking&&(t.timeSinceSeekBegin=this.timeSinceSeekBegin.getDeltaTime()),t.timeSinceLastAd=this.timeSinceLastAd.getDeltaTime(),this.numberOfErrors>0&&this.timeSinceLastError.startTime>0&&(t.timeSinceLastError=this.timeSinceLastError.getDeltaTime()),t.numberOfVideos=this.numberOfVideos),t.numberOfErrors=this.numberOfErrors,this.isAd()||(this.playtimeSinceLastEvent.startTime>0?t.playtimeSinceLastEvent=this.playtimeSinceLastEvent.getDeltaTime():t.playtimeSinceLastEvent=0,this.isPlaying?this.playtimeSinceLastEvent.start():this.playtimeSinceLastEvent.reset(),this.totalPlaytime+=t.playtimeSinceLastEvent,t.totalPlaytime=this.totalPlaytime);for(const[e,i]of Object.entries(this.customTimeSinceAttributes))t[e]=i.getDeltaTime();return t}getQoeAttributes(t){t=t||{};const e={};try{if(null!==this.startupTime&&(e.startupTime=this.startupTime),this.peakBitrate>0&&(e.peakBitrate=this.peakBitrate),e.hadStartupError=this.hadStartupError,e.hadPlaybackError=this.hadPlaybackError,e.totalRebufferingTime=this.totalRebufferingTime,e.rebufferingRatio=this.totalPlaytime>0?this.totalRebufferingTime/this.totalPlaytime*100:0,e.totalPlaytime=this.totalPlaytime,e.averageBitrate=this.weightedBitrate,e.numberOfErrors=this.numberOfErrors,this._downloadBitrates.length>0){const t=this._downloadBitrates.reduce((t,e)=>t+e,0);e.avgDownloadRate=Math.round(t/this._downloadBitrates.length),e.minDownloadRate=Math.min(...this._downloadBitrates),e.maxDownloadRate=Math.max(...this._downloadBitrates)}e.totalSwitchUps=this.totalSwitchUps,e.totalSwitchDowns=this.totalSwitchDowns,e.totalPauseTime=this.timeSincePaused.getDuration(),e.totalViewSessionTime=this.totalPlaytime,e.totalRenditions=this._playedRenditions.size,e.qoeAggregateVersion="1.1.0"}catch(t){n.error("Failed to add attributes for QOE KPIs",t.message)}return t.qoe=e,t}calculateBufferType(t){let e="";return e=t?"initial":this.isSeeking?"seek":this.isPaused?"pause":"connection",n.debug("Buffer Type = "+e),e}goViewCountUp(){this._viewCount++}goPlayerReady(){return!this.isPlayerReady&&(this.isPlayerReady=!0,!0)}goRequest(){return!this.isRequested&&(this.isRequested=!0,this.timeSinceLastAd.reset(),this.timeSinceRequested.start(),!0)}goStart(){return!(!this.isRequested||this.isStarted)&&(this.isAd()?this.numberOfAds++:this.numberOfVideos++,this.isStarted=!0,this.isPlaying=!0,this.timeSinceStarted.start(),this.playtimeSinceLastEvent.start(),!0)}goEnd(){return!!this.isRequested&&(this.numberOfErrors=0,this.resetFlags(),this.timeSinceRequested.stop(),this.timeSinceStarted.stop(),this.playtimeSinceLastEvent.stop(),this.isPlaying=!1,!0)}goPause(){return!(!this.isStarted||this.isPaused)&&(this.isPaused=!0,this.isPlaying=!1,this.timeSincePaused.start(),this.playtimeSinceLastEvent.stop(),this.timeSinceResumed.reset(),this.isBuffering&&(this._bufferAcc+=this.bufferElapsedTime.getDeltaTime()),this.elapsedTime.start(),!0)}goResume(){return!(!this.isStarted||!this.isPaused)&&(this.isPaused=!1,this.isPlaying=!0,this.timeSincePaused.stop(),this.timeSinceResumed.start(),this._hb?(this._acc=this.elapsedTime.getDeltaTime(),this._hb=!1):(this.isBuffering&&this.bufferElapsedTime.start(),this._acc+=this.elapsedTime.getDeltaTime()),!0)}goBufferStart(){return!(!this.isRequested||this.isBuffering)&&(this.isBuffering=!0,this.isPlaying=!1,this.timeSinceBufferBegin.start(),this.bufferElapsedTime.start(),!0)}goBufferEnd(){return!(!this.isRequested||!this.isBuffering)&&(this.isBuffering=!1,this.isPlaying=!0,this.timeSinceBufferBegin.stop(),this._hb?(this._bufferAcc=this.bufferElapsedTime.getDeltaTime(),this._hb=!1):this._bufferAcc+=this.bufferElapsedTime.getDeltaTime(),!this.isAd()&&this.initialBufferingHappened&&(this.totalRebufferingTime+=this.timeSinceBufferBegin.stopTime-this.timeSinceBufferBegin.startTime),!0)}goSeekStart(){return!(!this.isStarted||this.isSeeking)&&(this.isSeeking=!0,this.isPlaying=!1,this.timeSinceSeekBegin.start(),this.timeSinceSeekEnd.reset(),!0)}goSeekEnd(){return!(!this.isStarted||!this.isSeeking)&&(this.isSeeking=!1,this.isPlaying=!0,this.timeSinceSeekBegin.stop(),this.timeSinceSeekEnd.start(),!0)}goAdBreakStart(){return!this.isAdBreak&&(this.isAdBreak=!0,this.timeSinceAdBreakStart.start(),!0)}goAdBreakEnd(){return!!this.isAdBreak&&(this.isRequested=!1,this.isAdBreak=!1,this.totalAdPlaytime=this.timeSinceAdBreakStart.getDeltaTime(),this.timeSinceAdBreakStart.stop(),!0)}goDownload(){this.timeSinceLastDownload.start()}goHeartbeat(){this.timeSinceLastHeartbeat.start()}goRenditionChange(){this.timeSinceLastRenditionChange.start()}goAdQuartile(){this.timeSinceLastAdQuartile.start()}goError(){this.isError=!0,this.numberOfErrors++,this.isAd()?this.timeSinceLastAdError.start():(this.timeSinceLastError.start(),this.isStarted?this.hadPlaybackError=!0:this.hadStartupError=!0)}goLastAd(){this.timeSinceLastAd.start()}trackContentBitrateState(t){if(t&&"number"==typeof t){this.peakBitrate=Math.max(this.peakBitrate,t);const e=Date.now();if(!this.isPlaying){if(null!==this._lastBitrate&&null!==this._lastBitrateChangeTimestamp){const t=e-this._lastBitrateChangeTimestamp;t>0&&(this.partialAverageBitrate+=this._lastBitrate*t,this._totalBitrateDuration+=t),this._lastBitrateChangeTimestamp=null}return void(this._lastBitrate=t)}if(null===this._lastBitrateChangeTimestamp&&null!==this._lastBitrate&&(this._lastBitrateChangeTimestamp=e),null!==this._lastBitrate&&this._lastBitrate!==t&&null!==this._lastBitrateChangeTimestamp){const t=e-this._lastBitrateChangeTimestamp;t>0&&(this.partialAverageBitrate+=this._lastBitrate*t,this._totalBitrateDuration+=t),this._lastBitrateChangeTimestamp=e}null===this._lastBitrateChangeTimestamp&&(this._lastBitrateChangeTimestamp=e),this._lastBitrate=t;let i=this.partialAverageBitrate,s=this._totalBitrateDuration;const r=e-this._lastBitrateChangeTimestamp;r>0&&(i+=t*r,s+=r),this.weightedBitrate=s>0?Math.round(i/s):t}}trackDownloadRate(t){!t||"number"!=typeof t||t<=0||t!==this._lastDownloadBitrate&&(this._downloadBitrates.push(t),this._lastDownloadBitrate=t)}addPlayedRendition(t,e){t&&e&&this._playedRenditions.add(`${t}x${e}`)}resetViewIdTrackedState(){this.peakBitrate=0,this.startupTime=null,this.partialAverageBitrate=0,this._totalBitrateDuration=0,this.weightedBitrate=0,this._lastBitrate=null,this._lastBitrateChangeTimestamp=null,this._downloadBitrates=[],this._lastDownloadBitrate=null,this.totalSwitchUps=0,this.totalSwitchDowns=0,this.timeSincePaused.reset(),this._playedRenditions=new Set}clearTotalAdsTime(){n.debug("clear total ads time",this.totalAdTime),this._totalAdPlaytime.reset()}totalAdTime(){return this._totalAdPlaytime.getDuration()}startAdsTime(){return n.debug("startAdsTime"),this._totalAdPlaytime.start()}stopAdsTime(){return n.debug("stopAdsTime"),this._totalAdPlaytime.stop()}setStartupTime(t){null===this.startupTime&&(this.startupTime=Math.max(this.timeSinceRequested.getDeltaTime()-t,0))}};class k extends m{constructor(t,e){super(),this.state=new D,this.adsTracker=null,this._lastBufferType=null,this._userId=null,this._src=null,e=e||{},this.setOptions(e),t&&this.setPlayer(t,e.tag),n.notice("Tracker "+this.getTrackerName()+" v"+this.getTrackerVersion()+" is ready.")}setUserId(t){this._userId=t}setOptions(t){t&&(t.adsTracker&&this.setAdsTracker(t.adsTracker),"boolean"==typeof t.isAd&&this.setIsAd(t.isAd),void 0!==t.src&&(null!==this._src&&this._src!==t.src?n.warn(`setOptions: src is locked to '${this._src}'. Ignoring override '${t.src}'.`):(this._src=t.src,this.adsTracker&&null==this.adsTracker._src&&(this.adsTracker._src=this._src))),m.prototype.setOptions.apply(this,arguments))}setPlayer(t,e){(this.player||this.tag)&&this.dispose(),"undefined"!=typeof document&&document.getElementById&&("string"==typeof t&&(t=document.getElementById(t)),"string"==typeof e&&(e=document.getElementById(e))),e=e||t,this.player=t,this.tag=e,this.registerListeners()}isAd(){return this.state.isAd()}setIsAd(t){this.state.setIsAd(t)}setAdsTracker(t){this.disposeAdsTracker(),t&&(this.adsTracker=t,this.adsTracker.setIsAd(!0),this.adsTracker.parentTracker=this,null!=this._src&&null==t._src&&(t._src=this._src),this.adsTracker.on("*",N.bind(this)))}disposeAdsTracker(){this.adsTracker&&(this.adsTracker.off("*",N),this.adsTracker.dispose())}dispose(){this.stopHeartbeat(),this.disposeAdsTracker(),this.unregisterListeners(),this.player=null,this.tag=null}registerListeners(){}unregisterListeners(){}getViewId(){return this.parentTracker?this.parentTracker.getViewId():this.state.getViewId()}getViewSession(){return this.parentTracker?this.parentTracker.getViewSession():this.state.getViewSession()}getVideoId(){return null}getTitle(){return null}isLive(){return null}getBitrate(){return null}getManifestBitrate(){return null}getSegmentDownloadBitrate(){return null}getNetworkDownloadBitrate(){return null}getWebkitBitrate(){if(this.tag&&this.tag.webkitVideoDecodedByteCount){let t;if(this._lastWebkitBitrate){t=this.tag.webkitVideoDecodedByteCount;let e=t-this._lastWebkitBitrate,i=this.getHeartbeat()/1e3;t=Math.round(e/i*8)}return this._lastWebkitBitrate=this.tag.webkitVideoDecodedByteCount,t||null}}getRenditionName(){return null}getRenditionBitrate(){return null}getRenditionShift(t){let e,i=this.getManifestBitrate();return this.isAd()?(e=this._lastAdRendition,t&&(this._lastAdRendition=i)):(e=this._lastRendition,t&&(this._lastRendition=i)),i&&e?i>e?"up":i<e?"down":null:null}getRenditionHeight(){return this.tag?this.tag.videoHeight:null}getRenditionWidth(){return this.tag?this.tag.videoWidth:null}getDuration(){return this.tag?this.tag.duration:null}getPlayhead(){return this.tag?this.tag.currentTime:null}getLanguage(){return null}getSrc(){return this.tag?this.tag.currentSrc:null}getPlayrate(){return this.tag?this.tag.playbackRate:null}isMuted(){return this.tag?this.tag.muted:null}isFullscreen(){return null}getCdn(){return null}getPlayerName(){return this.getTrackerName()}getPlayerVersion(){return d.rE}getFps(){return null}isAutoplayed(){return this.tag?this.tag.autoplay:null}getPreload(){return this.tag?this.tag.preload:null}getAdQuartile(){return null}getAdPosition(){return this.parentTracker?this.parentTracker.state.isStarted?"mid":"pre":null}getAdPartner(){return null}getAdCreativeId(){return null}getInstrumentationProvider(){return null}getInstrumentationName(){return null}getInstrumentationVersion(){return null}getAttributes(t,e){if(void 0===(t=m.prototype.getAttributes.apply(this,arguments)).isAd&&(t.isAd=this.isAd()),t.viewSession=this.getViewSession(),t.viewId=this.getViewId(),t.playerName=this.getPlayerName(),t.playerVersion=this.getPlayerVersion(),t["instrumentation.provider"]=this.getInstrumentationProvider(),t["instrumentation.name"]=this.getInstrumentationName(),t["instrumentation.version"]=this.getInstrumentationVersion(),t["enduser.id"]=this._userId,t.src=this._src||"Browser","customAction"===e)return t;try{t.pageUrl=window.location.href}catch(t){}this.isAd()?(t.adId=this.getVideoId(),t.adTitle=this.getTitle(),t.adSrc=this.getSrc(),t.adCdn=this.getCdn(),this.state.isStarted&&(t.adBitrate=this.getBitrate()||0),t.adRenditionName=this.getRenditionName(),t.adRenditionHeight=this.getRenditionHeight(),t.adRenditionWidth=this.getRenditionWidth(),t.adDuration=this.getDuration(),t.adPlayhead=this.getPlayhead(),t.adLanguage=this.getLanguage(),t.adIsMuted=this.isMuted(),t.adFps=this.getFps(),t.adPosition=this.getAdPosition(),t.adCreativeId=this.getAdCreativeId(),t.adPartner=this.getAdPartner()):(t.contentId=this.getVideoId(),t.contentTitle=this.getTitle(),t.contentSrc=this.getSrc(),t.contentCdn=this.getCdn(),t.contentPlayhead=this.getPlayhead(),t.contentIsLive=this.isLive(),this.state.isStarted&&(t.contentBitrate=this.getBitrate()||0,t.contentManifestBitrate=this.getManifestBitrate()||0,t.contentSegmentDownloadBitrate=this.getSegmentDownloadBitrate()||0,t.contentNetworkDownloadBitrate=this.getNetworkDownloadBitrate()||0),t.contentRenditionName=this.getRenditionName(),t.contentRenditionHeight=this.getRenditionHeight(),t.contentRenditionWidth=this.getRenditionWidth(),t.contentDuration=this.getDuration(),t.contentLanguage=this.getLanguage(),t.contentPlayrate=this.getPlayrate(),t.contentIsFullscreen=this.isFullscreen(),t.contentIsMuted=this.isMuted(),t.contentIsAutoplayed=this.isAutoplayed(),t.contentPreload=this.getPreload(),t.contentFps=this.getFps(),null!=this.adsTracker&&this.adsTracker.state.totalAdPlaytime>0&&(t.totalAdPlaytime=this.adsTracker.state.totalAdPlaytime)),this.state.getStateAttributes(t),this.state.isStarted&&!this.isAd()&&(this.state.trackContentBitrateState(t.contentBitrate),this.state.trackDownloadRate(t.contentNetworkDownloadBitrate),this.state.addPlayedRendition(t.contentRenditionHeight,t.contentRenditionWidth));for(let e in this.customData)t[e]=this.customData[e];if(!this.isAd()){this.state.getQoeAttributes(t);const e=t.qoe;for(let t in this.customData)e[t]=this.customData[t]}return t}sendCustom(t,e,i){i=i||{},this.sendVideoCustomAction(t,i),this.state.setTimeSinceAttribute(e)}sendPlayerReady(t){this.state.goPlayerReady()&&(t=t||{},this.sendVideoAction(k.Events.PLAYER_READY,t))}sendRequest(t){if(this.state.goRequest()){let e;this.isAd()?(e=k.Events.AD_REQUEST,this.sendVideoAdAction(e,t)):(e=k.Events.CONTENT_REQUEST,this.sendVideoAction(e,t))}}sendStart(t){if(this.state.goStart()){let e;if(this.isAd())e=k.Events.AD_START,this.parentTracker&&(this.parentTracker.state.isPlaying=!1),this.sendVideoAdAction(e,t),this.state.startAdsTime();else{e=k.Events.CONTENT_START;let i=0;this.adsTracker&&((this.adsTracker.state.isPlaying||this.adsTracker.state.isBuffering)&&(this.adsTracker.state.stopAdsTime(),this.adsTracker.state.isPlaying=!1,this.adsTracker.state.isBuffering=!1),i=this.adsTracker.state.totalAdTime()??0),this.state.setStartupTime(i),this.sendVideoAction(e,t);const s=this.getHarvester?.();s?.setBeforeDrainCallback(()=>{if(this.state){const t=this.state.getQoeAttributes({}).qoe;s.refreshQoeKpis(t,this.getViewId())}})}this.startHeartbeat(),this.state.goHeartbeat()}}sendEnd(t){if(this.state.goEnd()){let e;if(t=t||{},this.isAd()?(e=k.Events.AD_END,t.timeSinceAdRequested=this.state.timeSinceRequested.getDeltaTime(),t.timeSinceAdStarted=this.state.timeSinceStarted.getDeltaTime(),this.parentTracker&&(this.parentTracker.state.isPlaying=!0,this.parentTracker.state.timeSincePaused.reset()),this.state.stopAdsTime()):(e=k.Events.CONTENT_END,t.timeSinceRequested=this.state.timeSinceRequested.getDeltaTime(),t.timeSinceStarted=this.state.timeSinceStarted.getDeltaTime()),this.stopHeartbeat(),this.isAd()?this.sendVideoAdAction(e,t):this.sendVideoAction(e,t),this.parentTracker&&this.isAd()&&this.parentTracker.state.goLastAd(),this.state.goViewCountUp(),this.state.totalPlaytime=0,!this.isAd()){const t=this.getHarvester?.();t?.forceNextQoeCycle?.(),t?.setBeforeDrainCallback?.(null),this.adsTracker&&this.adsTracker.state.clearTotalAdsTime(),this.state.resetViewIdTrackedState()}}}sendPause(t){if(this.state.goPause()){let e=this.isAd()?k.Events.AD_PAUSE:k.Events.CONTENT_PAUSE;this.isAd()?this.sendVideoAdAction(e,t):this.sendVideoAction(e,t)}}sendResume(t){if(this.state.goResume()){let e;t=t||{},this.isAd()?(e=k.Events.AD_RESUME,t.timeSinceAdPaused=this.state.timeSincePaused.getDeltaTime()):(e=k.Events.CONTENT_RESUME,t.timeSincePaused=this.state.timeSincePaused.getDeltaTime()),this.isAd()?this.sendVideoAdAction(e,t):this.sendVideoAction(e,t)}}sendBufferStart(t){if(this.state.goBufferStart()){let e;t=t||{},e=this.isAd()?k.Events.AD_BUFFER_START:k.Events.CONTENT_BUFFER_START,t=this.buildBufferAttributes(t),this._lastBufferType=t.bufferType,this.isAd()?this.sendVideoAdAction(e,t):this.sendVideoAction(e,t)}}sendBufferEnd(t){if(this.state.goBufferEnd()){let e;t=t||{},this.isAd()?(e=k.Events.AD_BUFFER_END,t.timeSinceAdBufferBegin=this.state.timeSinceBufferBegin.getDeltaTime()):(e=k.Events.CONTENT_BUFFER_END,t.timeSinceBufferBegin=this.state.timeSinceBufferBegin.getDeltaTime()),t=this.buildBufferAttributes(t),null!=this._lastBufferType&&(t.bufferType=this._lastBufferType),this.isAd()?this.sendVideoAdAction(e,t):this.sendVideoAction(e,t),this.state.initialBufferingHappened=!0}}buildBufferAttributes(t){return null==t.timeSinceStarted||t.timeSinceStarted<100?t.isInitialBuffering=!this.state.initialBufferingHappened:t.isInitialBuffering=!1,t.bufferType=this.state.calculateBufferType(t.isInitialBuffering),t.timeSinceResumed=this.state.timeSinceResumed.getDeltaTime(),t.timeSinceSeekEnd=this.state.timeSinceSeekEnd.getDeltaTime(),t}sendSeekStart(t){if(this.state.goSeekStart()){let e;e=this.isAd()?k.Events.AD_SEEK_START:k.Events.CONTENT_SEEK_START,this.isAd()?this.sendVideoAdAction(e,t):this.sendVideoAction(e,t)}}sendSeekEnd(t){if(this.state.goSeekEnd()){let e;t=t||{},this.isAd()?(e=k.Events.AD_SEEK_END,t.timeSinceAdSeekBegin=this.state.timeSinceSeekBegin.getDeltaTime()):(e=k.Events.CONTENT_SEEK_END,t.timeSinceSeekBegin=this.state.timeSinceSeekBegin.getDeltaTime()),this.isAd()?this.sendVideoAdAction(e,t):this.sendVideoAction(e,t)}}sendDownload(t){(t=t||{}).state||n.warn("Called sendDownload without { state: xxxxx }."),this.sendVideoAction(k.Events.DOWNLOAD,t),this.state.goDownload()}sendError(t){(t=t||{}).isAd=this.isAd(),this.state.goError();let e=this.isAd()?k.Events.AD_ERROR:k.Events.CONTENT_ERROR;this.sendVideoErrorAction(e,t)}sendRenditionChanged(t){(t=t||{}).timeSinceLastRenditionChange=this.state.timeSinceLastRenditionChange.getDeltaTime();const{oldBitrate:e,newBitrate:i}=t;let s;this.isAd()||void 0===e||void 0===i||(i>e?(t.shift="up",this.state.totalSwitchUps+=1):i<e?(t.shift="down",this.state.totalSwitchDowns+=1):t.shift=null),delete t.oldBitrate,delete t.newBitrate,s=this.isAd()?k.Events.AD_RENDITION_CHANGE:k.Events.CONTENT_RENDITION_CHANGE,this.isAd()?this.sendVideoAdAction(s,t):this.sendVideoAction(s,t),this.state.goRenditionChange()}sendHeartbeat(t){if(this.state.isRequested){let e,i=this.getHeartbeat();this.state._hb=!0,i=this.adjustElapsedTimeForPause(i),this.isAd()?(e=k.Events.AD_HEARTBEAT,"bitmovin-ads"===this.getPlayerName()?this.sendVideoAdAction(e,t):this.sendVideoAdAction(e,{elapsedTime:i,...t})):(e=k.Events.CONTENT_HEARTBEAT,this.sendVideoAction(e,{elapsedTime:i,...t})),this.state.goHeartbeat()}}adjustElapsedTimeForPause(t){return this.state._acc&&(t-=this.state._acc,this.state._acc=0),this.state.isPaused&&((t-=this.state.elapsedTime.getDeltaTime())<10&&(t=0),this.state.elapsedTime.start()),this.state._bufferAcc?(t-=this.state._bufferAcc,this.state._bufferAcc=0):this.state.isBuffering&&((t-=this.state.bufferElapsedTime.getDeltaTime())<5&&(t=0),this.state.bufferElapsedTime.start()),Math.max(0,t)}sendAdBreakStart(t){this.isAd()&&this.state.goAdBreakStart()&&(this.state.totalAdPlaytime=0,this.parentTracker&&(this.parentTracker.state.isPlaying=!1),this.sendVideoAdAction(k.Events.AD_BREAK_START,t))}sendAdBreakEnd(t){this.isAd()&&this.state.goAdBreakEnd()&&((t=t||{}).timeSinceAdBreakBegin=this.state.timeSinceAdBreakStart.getDeltaTime(),this.sendVideoAdAction(k.Events.AD_BREAK_END,t),this.parentTracker&&(this.parentTracker.state.isPlaying=!0),this.stopHeartbeat(),this.parentTracker&&this.isAd()&&this.parentTracker.state.goLastAd())}sendAdQuartile(t){this.isAd()&&((t=t||{}).quartile||n.warn("Called sendAdQuartile without { quartile: xxxxx }."),t.timeSinceLastAdQuartile=this.state.timeSinceLastAdQuartile.getDeltaTime(),this.sendVideoAdAction(k.Events.AD_QUARTILE,t),this.state.goAdQuartile())}sendAdClick(t){this.isAd()&&((t=t||{}).url||n.warn("Called sendAdClick without { url: xxxxx }."),this.sendVideoAdAction(k.Events.AD_CLICK,t))}}function N(t){t.type!==k.Events.AD_ERROR?this.sendVideoAdAction(t.type,t.data):this.sendVideoErrorAction(t.type,t.data)}k.Events={PLAYER_READY:"PLAYER_READY",DOWNLOAD:"DOWNLOAD",ERROR:"ERROR",CONTENT_REQUEST:"CONTENT_REQUEST",CONTENT_START:"CONTENT_START",CONTENT_END:"CONTENT_END",CONTENT_PAUSE:"CONTENT_PAUSE",CONTENT_RESUME:"CONTENT_RESUME",CONTENT_SEEK_START:"CONTENT_SEEK_START",CONTENT_SEEK_END:"CONTENT_SEEK_END",CONTENT_BUFFER_START:"CONTENT_BUFFER_START",CONTENT_BUFFER_END:"CONTENT_BUFFER_END",CONTENT_HEARTBEAT:"CONTENT_HEARTBEAT",CONTENT_RENDITION_CHANGE:"CONTENT_RENDITION_CHANGE",CONTENT_ERROR:"CONTENT_ERROR",AD_REQUEST:"AD_REQUEST",AD_START:"AD_START",AD_END:"AD_END",AD_PAUSE:"AD_PAUSE",AD_RESUME:"AD_RESUME",AD_SEEK_START:"AD_SEEK_START",AD_SEEK_END:"AD_SEEK_END",AD_BUFFER_START:"AD_BUFFER_START",AD_BUFFER_END:"AD_BUFFER_END",AD_HEARTBEAT:"AD_HEARTBEAT",AD_RENDITION_CHANGE:"AD_RENDITION_CHANGE",AD_ERROR:"AD_ERROR",AD_BREAK_START:"AD_BREAK_START",AD_BREAK_END:"AD_BREAK_END",AD_QUARTILE:"AD_QUARTILE",AD_CLICK:"AD_CLICK"};const C=k,{MAX_PAYLOAD_SIZE:O,MAX_EVENTS_PER_BATCH:I}=h;class P{constructor(){this.buffer=[],this.maxPayloadSize=O,this.maxEventsPerBatch=I,this.currentPayloadSize=0,this.totalEvents=0,this.smartHarvestPayloadThreshold=Math.floor(.6*this.maxPayloadSize),this.overflowPayloadThreshold=Math.floor(.9*this.maxPayloadSize),this.smartHarvestEventThreshold=Math.floor(.6*this.maxEventsPerBatch),this.overflowEventThreshold=Math.floor(.9*this.maxEventsPerBatch),this.onSmartHarvestTrigger=null}addOrReplaceByActionName(t,e){const i=this.buffer.findIndex(e=>e.actionName===t);try{return-1===i?this.add(e):this.add(e,i),!0}catch(t){return n.error("Failed to set or replace the event to buffer:",t.message),!1}}addOrReplaceByActionNameAndViewId(t,e,i){const s=this.buffer.findIndex(i=>i.actionName===t&&i.viewId===e);try{return-1===s?this.add(i):this.add(i,s),!0}catch(t){return n.error("Failed to set or replace the event to buffer:",t.message),!1}}findByActionNameAndViewId(t,e){return this.buffer.find(i=>i.actionName===t&&i.viewId===e)||null}findByActionName(t){return this.buffer.find(e=>e.actionName===t)||null}add(t,e){try{const i=E(t),s=this.currentPayloadSize+i>=this.maxPayloadSize,r=this.totalEvents+1>=this.maxEventsPerBatch;if((s||r)&&this.makeRoom(i),null!=e&&e>-1){const s=E(this.buffer[e]);this.buffer[e]=t,this.currentPayloadSize+=i-s}else this.buffer.push(t),this.totalEvents++,this.currentPayloadSize+=i;return this.checkSmartHarvestTrigger(),!0}catch(t){return n.error("Failed to add event to buffer:",t.message),!1}}checkSmartHarvestTrigger(){const t=this.currentPayloadSize/this.maxPayloadSize,e=this.totalEvents/this.maxEventsPerBatch,i=this.currentPayloadSize>=this.overflowPayloadThreshold,s=this.totalEvents>=this.overflowEventThreshold;if(i||s){const s=i?`payload ${this.currentPayloadSize}/${this.maxPayloadSize} bytes (${Math.round(100*t)}%)`:`events ${this.totalEvents}/${this.maxEventsPerBatch} (${Math.round(100*e)}%)`;n.warn(`OVERFLOW PREVENTION: ${s} - Emergency harvest triggered`),this.onSmartHarvestTrigger&&"function"==typeof this.onSmartHarvestTrigger&&this.onSmartHarvestTrigger("overflow",90)}else{const t=this.currentPayloadSize>=this.smartHarvestPayloadThreshold,e=this.totalEvents>=this.smartHarvestEventThreshold;(t||e)&&this.onSmartHarvestTrigger&&"function"==typeof this.onSmartHarvestTrigger&&this.onSmartHarvestTrigger("smart",60)}}setSmartHarvestCallback(t){this.onSmartHarvestTrigger=t}drain(){try{const t=this.buffer.splice(0);return this.totalEvents=0,this.currentPayloadSize=0,t}catch(t){return n.error("Failed to drain events from buffer:",t.message),[]}}isEmpty(){return 0===this.totalEvents}size(){return this.totalEvents}clear(){this.buffer=[],this.totalEvents=0}makeRoom(t){if(t>this.maxPayloadSize)n.error("Event dropped: Event size exceeds maximum payload size.");else for(;(this.totalEvents>=this.maxEventsPerBatch||this.currentPayloadSize+t>=this.maxPayloadSize)&&this.buffer.length>0;){const t=E(this.buffer.shift());this.totalEvents--,this.currentPayloadSize-=t,n.warn("Event buffer full, oldest event removed.")}}}const{MAX_PAYLOAD_SIZE:V,MAX_EVENTS_PER_BATCH:L}=h;class H{constructor(){this.retryQueue=[],this.maxQueueSize=L,this.maxQueueSizeBytes=V}addFailedEvents(t){try{const e=Array.isArray(t)?t:[t];n.notice(`Adding ${e.length} failed events to retry queue`,{queueSizeBefore:this.retryQueue.length});for(const t of e){this.retryQueue.length>=this.maxQueueSize&&this.evictOldestEvent();const e=E(t);for(;E(this.retryQueue)+e>this.maxQueueSizeBytes;)this.evictOldestEvent();this.retryQueue.push({...t})}}catch(t){n.error("Failed to add events to retry queue:",t.message)}}discardEvent(t,e){n.warn("Discarded event",{reason:e,eventType:t.eventType})}evictOldestEvent(){if(this.retryQueue.length>0){const t=this.retryQueue.shift();this.discardEvent(t,"Queue full - evicted oldest")}}getRetryEventsToFit(t,e){const i=[];let s=0,r=0;for(let a=this.retryQueue.length-1;a>=0;a--){const n=this.retryQueue[a];if(r>=e)break;const o=E(n);if(s+o>t)break;i.unshift(n),s+=o,r++,this.retryQueue.splice(a,1)}return i}getQueueSize(){return this.retryQueue.length}clear(){this.retryQueue=[]}}class F{async send(t,e){const{url:i,payload:s,options:r={}}=t;try{if(!i||!s)throw new Error("URL and payload are required");const t={url:i,payload:s,options:r,callback:e};await this.executeRequest(t)}catch(t){n.error("Failed to send request:",t.message),e({retry:!1,status:0,error:t.message})}}async executeRequest(t){const{url:e,payload:i,options:s,callback:r}=t,a=Date.now();try{const r=(o=JSON.stringify(i.body),h=window.NRVIDEO?.config?.obfuscate,h&&0!==h.length?h.reduce((t,e)=>{let i;try{if(e.regex instanceof RegExp){const t=e.regex.flags.includes("g")?e.regex.flags:e.regex.flags+"g";i=new RegExp(e.regex.source,t)}else i=new RegExp(e.regex,"g")}catch(i){return n.warn("applyObfuscationRules: invalid regex, skipping rule:",e.regex,i.message),t}return t.replace(i,e.replacement)},o):o);if(s.isFinalHarvest&&navigator.sendBeacon){const i=await this.sendWithBeacon(e,r),s={success:i,status:i?204:0};return void this.handleRequestComplete(t,s,a)}const d=await this.fetchWithTimeout(e,{method:"POST",body:r,headers:{"Content-Type":"application/json"},keepalive:s.isFinalHarvest},1e4),l={success:d.ok,status:d.status,statusText:d.statusText};this.handleRequestComplete(t,l,a)}catch(e){const i={success:!1,status:0,error:e.message};this.handleRequestComplete(t,i,a)}var o,h}handleRequestComplete(t,e){const{callback:i}=t;i({retry:!e.success&&(0===e.status||function(t){switch(t){case 408:case 429:case 500:return!0;case 401:case 403:case 404:return!1}return t>=502&&t<=504||t>=512&&t<=530}(e.status)),status:e.status,error:e.error})}async sendWithBeacon(t,e){try{return navigator.sendBeacon(t,e)}catch(t){return n.warn("sendBeacon failed, falling back to fetch:",t.message),!1}}async fetchWithTimeout(t,e,i){const s=new AbortController,r=setTimeout(()=>s.abort(),i);try{const i=await fetch(t,{...e,signal:s.signal});return clearTimeout(r),i}catch(t){if(clearTimeout(r),"AbortError"===t.name)throw new Error(`Request timeout after ${i}ms`);throw t}}}function Q(t,e){const i=t[e.viewId];if(!i)return!1;for(const t of h.QOE_KPI_KEYS)if(e[t]!==i[t])return!1;return!0}function x(t,e){const i={};for(const t of h.QOE_KPI_KEYS)i[t]=e[t];t[e.viewId]=i}class q{constructor(t){this.eventBuffer=t,this.retryQueueHandler=new H,this.httpClient=new F,this.fallBackUrl="",this.retryCount=0,this.eventBuffer instanceof P&&this.eventBuffer.setSmartHarvestCallback((t,e)=>this.triggerSmartHarvest(t,e)),this.harvestCycle=h.INTERVAL,this.isHarvesting=!1,this.qoeCycleCount=1,this.forceNextQoeCycle=!1,this.beforeDrainCallback=null,this._lastSentQoeKpis={},this.timer=function({interval:t,onTick:e,errorLabel:i}){let s=!1,r=null,a=t;function o(){s&&(r=setTimeout(async()=>{r=null;try{await e()}catch(t){n.error(`${i}: scheduled tick failed:`,t.message)}finally{s&&o()}},a))}return{start(){s||(s=!0,o())},stop(){s=!1,r&&(clearTimeout(r),r=null)},cancelAndReschedule(){r&&(clearTimeout(r),r=null),s&&o()},updateInterval(t){"number"!=typeof t||t<=0||(a=t,s&&r&&(clearTimeout(r),r=null,o()))},isRunning:()=>s}}({interval:this.harvestCycle,onTick:()=>this.onHarvestInterval(),errorLabel:"HarvestScheduler"}),this.setupPageLifecycleHandlers()}get isStarted(){return this.timer.isRunning()}startScheduler(){this.timer.isRunning()?n.warn("Harvest scheduler is already started"):(n.notice("Starting harvest scheduler",{harvestCycle:this.harvestCycle,eventBufferSize:this.eventBuffer?this.eventBuffer.size():0}),this.timer.start())}stopScheduler(){this.timer.isRunning()&&(this.timer.stop(),n.notice("Harvest scheduler stopped"))}async triggerSmartHarvest(t,e){if(n.notice(`${t} harvest triggered at ${e}% threshold`,{type:t,threshold:e}),this.eventBuffer&&!this.eventBuffer.isEmpty())try{await this.triggerHarvest({})}catch(e){n.error(`${t} harvest failed:`,e.message)}finally{this.timer.cancelAndReschedule()}}async onHarvestInterval(){const t=this.eventBuffer&&!this.eventBuffer.isEmpty(),e=this.retryQueueHandler&&this.retryQueueHandler.getQueueSize()>0;(t||e)&&await this.triggerHarvest({})}async triggerHarvest(t={}){if(this.isHarvesting)return{success:!1,reason:"harvest_in_progress"};this.isHarvesting=!0;try{let e=this.drainEvents(t);if(t.isFinalHarvest){const t=h.MAX_BEACON_SIZE;E(e)>t&&(e=this.trimEventsToFit(e,t))}const i=await this.sendChunk(e,t,!0);return{success:i.success,totalChunks:1,results:[i]}}catch(t){return n.error("Harvest cycle failed:",t.message),this.handleHarvestFailure(t),{success:!1,error:t.message}}finally{this.isHarvesting=!1}}trimEventsToFit(t,e){if(0===t.length)return t;const i=[];let s=0;for(let r=t.length-1;r>=0;r--){const a=t[r],n=E({ins:[a,...i]});n>e||(i.unshift(a),s=n)}const r=t.length-i.length;if(r>0){const a=t.slice(0,r);n.warn(`Discarded ${r} events to fit beacon size limit`,{originalCount:t.length,trimmedCount:i.length,finalSize:s,maxSize:e}),this.retryQueueHandler&&this.retryQueueHandler.addFailedEvents(a)}return i}drainEvents(t={}){const e=window.NRVIDEO?.config?.qoeIntervalFactor??1,i=!!t.isFinalHarvest||this.forceNextQoeCycle,s=(this.qoeCycleCount-1)%e==0||i;if(this.forceNextQoeCycle&&(this.forceNextQoeCycle=!1),this.beforeDrainCallback&&"function"==typeof this.beforeDrainCallback)try{this.beforeDrainCallback()}catch(t){n.error("Before drain callback failed:",t.message)}let r=function(t,e,i){if(e)return t;const s=[];for(const e of t)e.actionName===m.Events.QOE_AGGREGATE?i.add(e):s.push(e);return s}(this.eventBuffer.drain(),s,this.eventBuffer);this.qoeCycleCount++,function(t,e,i){for(let s=t.length-1;s>=0;s--){const r=t[s];r.actionName===m.Events.QOE_AGGREGATE&&(!i&&Q(e,r)?t.splice(s,1):x(e,r))}}(r,this._lastSentQoeKpis,i);let a=[...r],o=E(r);if(this.retryQueueHandler&&this.retryQueueHandler.getQueueSize()>0){const t=h.MAX_PAYLOAD_SIZE-o,e=h.MAX_EVENTS_PER_BATCH-a.length;if(t>0&&e>0){const i=this.retryQueueHandler.getRetryEventsToFit(t,e);i.length>0&&(a=[...i,...a])}}return a}async sendChunk(t,e,i){const s={url:function(t){try{if(!window.NRVIDEO||!window.NRVIDEO.info)throw new Error("NRVIDEO info is not available.");let{beacon:e,licenseKey:i,applicationID:s}=window.NRVIDEO.info;if(!e||!i)throw new Error("Options object provided by New Relic is not correctly initialized");return s?`https://${t||e}/ins/1/${i}?a=${s}&v=${d.rE}&ref=${window.location.href}&ca=VA`:`https://${t||e}/ins/1/${i}?&v=${d.rE}&ref=${window.location.href}&ca=VA`}catch(t){return console.error(t.message),null}}(this.fallBackUrl),payload:{body:{ins:t}},options:{...e,isLastChunk:i}};return new Promise(e=>{this.httpClient.send(s,i=>{i.retry?this.handleRequestFailure(t):(this.retryCount=0,this.fallBackUrl=""),e({success:!i.retry,status:i.status,error:i.error,chunk:t,eventCount:t.length})})})}handleRequestFailure(t){this.retryQueueHandler.addFailedEvents(t),"US"===window.NRVIDEO?.info?.region&&(this.retryCount++,this.retryCount>5?(this.retryCount=0,this.fallBackUrl=""):this.retryCount>=2&&(this.fallBackUrl=h.COLLECTOR.US[1]))}handleHarvestFailure(t){n.warn("Harvest failure handled",{error:t.message})}updateHarvestInterval(t){"number"!=typeof t||isNaN(t)?n.warn("Invalid newInterval provided to updateHarvestInterval"):t<1e3||t>3e5?n.warn("newInterval out of bounds (1000-300000), ignoring"):this.harvestCycle!==t&&(this.harvestCycle=t,n.notice("Updated harvestCycle:",this.harvestCycle),this.timer.updateInterval(t))}setupPageLifecycleHandlers(){let t=!1;const e=()=>{t||(t=!0,this.triggerHarvest({isFinalHarvest:!0,force:!0}))};document.addEventListener("visibilitychange",()=>{document.hidden&&e()}),window.addEventListener("pagehide",()=>{e()}),window.addEventListener("beforeunload",()=>{e()})}}const U=new class{constructor(){this.isInitialized=!1,this.harvestScheduler=null,this.eventBuffer=null}initialize(){if(this.isInitialized)n.warn("Video analytics agent already initialized");else try{this.eventBuffer=new P,this.harvestScheduler=new q(this.eventBuffer),this.harvestScheduler.startScheduler(),this.isInitialized=!0,n.notice("Video analytics agent initialized successfully")}catch(t){n.error("Failed to initialize video analytics agent:",t.message)}}addEvent(t){this.isInitialized||(n.warn("Video analytics agent not initialized, initializing now"),this.initialize());try{return function(t,e){return!!e&&(e.actionName===m.Events.QOE_AGGREGATE?e.viewId?t.addOrReplaceByActionNameAndViewId(m.Events.QOE_AGGREGATE,e.viewId,e):t.addOrReplaceByActionName(m.Events.QOE_AGGREGATE,e):t.add(e))}(this.eventBuffer,t)}catch(t){return n.error("Failed to add event to harvesting system:",t.message),!1}}setHarvestInterval(t){this.isInitialized||this.initialize(),this.harvestScheduler.updateHarvestInterval(t)}forceNextQoeCycle(){this.harvestScheduler&&(this.harvestScheduler.forceNextQoeCycle=!0)}setBeforeDrainCallback(t){this.harvestScheduler&&(this.harvestScheduler.beforeDrainCallback=t)}refreshQoeKpis(t,e){!function(t,e,i){if(!t||!e)return;const s=i?t.findByActionNameAndViewId(m.Events.QOE_AGGREGATE,i):t.findByActionName(m.Events.QOE_AGGREGATE);if(!s)return;const r={...s};for(const t of h.QOE_KPI_KEYS)t in e&&(r[t]=e[t]);i?t.addOrReplaceByActionNameAndViewId(m.Events.QOE_AGGREGATE,i,r):t.addOrReplaceByActionName(m.Events.QOE_AGGREGATE,r)}(this.eventBuffer,t,e)}};var z;z=U,T["Browser"]=z;const G=U,M={Constants:h,Chrono:u,Log:n,Emitter:l,Tracker:m,VideoTracker:C,VideoTrackerState:D,Core:w,version:d.rE,NrVideoEventAggregator:P,RetryQueueHandler:H,OptimizedHttpClient:F,HarvestScheduler:q,recordEvent:v};module.exports=e})();
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * @license Apache-2.0
3
+ * @newrelic/video-core 5.0.0
4
+ * Copyright New Relic <http://newrelic.com/>
5
+ * @author Jordi Aguilar
6
+ */