@newrelic/video-videojs 4.1.1 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +121 -14
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.LICENSE.txt +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.LICENSE.txt +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/umd/newrelic-video-videojs.min.js +1 -1
- package/dist/umd/newrelic-video-videojs.min.js.LICENSE.txt +3 -1
- package/dist/umd/newrelic-video-videojs.min.js.map +1 -1
- package/package.json +9 -2
- package/src/ads/dai.js +6 -3
- package/src/ads/ima.js +3 -2
- package/src/ads/media-tailor.js +1281 -0
- package/src/ads/utils/mt-constants.js +83 -0
- package/src/ads/utils/mt.js +814 -0
- package/src/register-plugin.js +2 -2
- package/src/techs/contrib-hls.js +7 -8
- package/src/techs/hls-js.js +5 -5
- package/src/techs/shaka.js +7 -6
- package/src/tracker.js +172 -18
package/src/register-plugin.js
CHANGED
|
@@ -12,8 +12,8 @@ if (typeof videojs !== 'undefined') {
|
|
|
12
12
|
*/
|
|
13
13
|
registerPlugin('newrelic', function (options) {
|
|
14
14
|
if (!this.newrelictracker) {
|
|
15
|
-
this.newrelictracker = new nrvideo.VideojsTracker(this);
|
|
16
|
-
nrvideo.Core.addTracker(this.newrelictracker);
|
|
15
|
+
this.newrelictracker = new nrvideo.VideojsTracker(this, options);
|
|
16
|
+
nrvideo.Core.addTracker(this.newrelictracker, options);
|
|
17
17
|
}
|
|
18
18
|
return this.newrelictracker;
|
|
19
19
|
});
|
package/src/techs/contrib-hls.js
CHANGED
|
@@ -15,7 +15,7 @@ export default class ContribHlsTech {
|
|
|
15
15
|
getRenditionBitrate() {
|
|
16
16
|
try {
|
|
17
17
|
var media = this.tech.playlists.media();
|
|
18
|
-
if (media && media.attributes) return media.attributes.BANDWIDTH;
|
|
18
|
+
if (media && media.attributes) return Math.round(media.attributes.BANDWIDTH);
|
|
19
19
|
} catch (err) {}
|
|
20
20
|
return null;
|
|
21
21
|
}
|
|
@@ -50,7 +50,7 @@ export default class ContribHlsTech {
|
|
|
50
50
|
// Return highest available bitrate from all renditions
|
|
51
51
|
const playlists = this.tech.playlists.master.playlists;
|
|
52
52
|
if (playlists && playlists.length > 0) {
|
|
53
|
-
return Math.max(...playlists.map((p) => p.attributes.BANDWIDTH));
|
|
53
|
+
return Math.round(Math.max(...playlists.map((p) => p.attributes.BANDWIDTH)));
|
|
54
54
|
}
|
|
55
55
|
} catch (err) {}
|
|
56
56
|
return null;
|
|
@@ -61,14 +61,14 @@ export default class ContribHlsTech {
|
|
|
61
61
|
this.tech.stats?.bandwidth !== undefined &&
|
|
62
62
|
this.tech.stats.bandwidth > 0
|
|
63
63
|
) {
|
|
64
|
-
return this.tech.stats.bandwidth;
|
|
64
|
+
return Math.round(this.tech.stats.bandwidth);
|
|
65
65
|
}
|
|
66
66
|
return null;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
getNetworkDownloadBitrate() {
|
|
70
70
|
if (this.tech.throughput !== undefined && this.tech.throughput > 0) {
|
|
71
|
-
return this.tech.throughput;
|
|
71
|
+
return Math.round(this.tech.throughput);
|
|
72
72
|
}
|
|
73
73
|
return null;
|
|
74
74
|
}
|
|
@@ -79,11 +79,10 @@ export default class ContribHlsTech {
|
|
|
79
79
|
const media = this.tech.playlists.media();
|
|
80
80
|
if (media && media.attributes) {
|
|
81
81
|
// Use AVERAGE-BANDWIDTH if available, fallback to BANDWIDTH
|
|
82
|
-
|
|
83
|
-
media.attributes['AVERAGE-BANDWIDTH'] ||
|
|
82
|
+
const bitrate = media.attributes['AVERAGE-BANDWIDTH'] ||
|
|
84
83
|
media.attributes.BANDWIDTH ||
|
|
85
|
-
null
|
|
86
|
-
);
|
|
84
|
+
null;
|
|
85
|
+
return bitrate !== null ? Math.round(bitrate) : null;
|
|
87
86
|
}
|
|
88
87
|
} catch (err) {}
|
|
89
88
|
return null;
|
package/src/techs/hls-js.js
CHANGED
|
@@ -19,7 +19,7 @@ export default class HlsJs {
|
|
|
19
19
|
getRenditionBitrate(tech) {
|
|
20
20
|
try {
|
|
21
21
|
var level = this.tech.levels[this.tech.currentLevel];
|
|
22
|
-
if (level && level.bitrate) return level.bitrate;
|
|
22
|
+
if (level && level.bitrate) return Math.round(level.bitrate);
|
|
23
23
|
} catch (err) {}
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
@@ -50,7 +50,7 @@ export default class HlsJs {
|
|
|
50
50
|
// Get the current active level's bitrate from manifest
|
|
51
51
|
const level = this.tech.levels[this.tech.currentLevel];
|
|
52
52
|
if (level && level.bitrate) {
|
|
53
|
-
return level.bitrate;
|
|
53
|
+
return Math.round(level.bitrate);
|
|
54
54
|
}
|
|
55
55
|
} catch (err) {}
|
|
56
56
|
return null;
|
|
@@ -60,7 +60,7 @@ export default class HlsJs {
|
|
|
60
60
|
try {
|
|
61
61
|
// Return highest available bitrate from all renditions
|
|
62
62
|
if (this.tech.levels && this.tech.levels.length > 0) {
|
|
63
|
-
return Math.max(...this.tech.levels.map(l => l.bitrate));
|
|
63
|
+
return Math.round(Math.max(...this.tech.levels.map(l => l.bitrate)));
|
|
64
64
|
}
|
|
65
65
|
} catch (err) {}
|
|
66
66
|
return null;
|
|
@@ -70,14 +70,14 @@ export default class HlsJs {
|
|
|
70
70
|
try {
|
|
71
71
|
// VHS stats.bandwidth
|
|
72
72
|
if (this.tech.stats && this.tech.stats.bandwidth > 0)
|
|
73
|
-
return this.tech.stats.bandwidth;
|
|
73
|
+
return Math.round(this.tech.stats.bandwidth);
|
|
74
74
|
} catch (err) {}
|
|
75
75
|
return null;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
getNetworkDownloadBitrate() {
|
|
79
79
|
if (this.tech.throughput && this.tech.throughput > 0) {
|
|
80
|
-
return this.tech.throughput;
|
|
80
|
+
return Math.round(this.tech.throughput);
|
|
81
81
|
}
|
|
82
82
|
return null;
|
|
83
83
|
}
|
package/src/techs/shaka.js
CHANGED
|
@@ -13,7 +13,8 @@ export default class ShakaTech {
|
|
|
13
13
|
|
|
14
14
|
getRenditionBitrate(tech) {
|
|
15
15
|
try {
|
|
16
|
-
|
|
16
|
+
const bitrate = this.tech.getStats().streamBandwidth;
|
|
17
|
+
return bitrate ? Math.round(bitrate) : null;
|
|
17
18
|
} catch (err) {}
|
|
18
19
|
return null;
|
|
19
20
|
}
|
|
@@ -23,9 +24,9 @@ export default class ShakaTech {
|
|
|
23
24
|
// Return highest available bitrate from all variants
|
|
24
25
|
const tracks = this.tech.getVariantTracks();
|
|
25
26
|
if (tracks && tracks.length > 0) {
|
|
26
|
-
return Math.max(
|
|
27
|
+
return Math.round(Math.max(
|
|
27
28
|
...tracks.map((t) => t.videoBandwidth + (t.audioBandwidth || 0)),
|
|
28
|
-
);
|
|
29
|
+
));
|
|
29
30
|
}
|
|
30
31
|
} catch (err) {}
|
|
31
32
|
return null;
|
|
@@ -67,7 +68,7 @@ export default class ShakaTech {
|
|
|
67
68
|
// Get the current variant's bitrate from manifest (streamBandwidth)
|
|
68
69
|
var stats = this.tech.getStats();
|
|
69
70
|
if (stats && stats.streamBandwidth && stats.streamBandwidth > 0) {
|
|
70
|
-
return stats.streamBandwidth;
|
|
71
|
+
return Math.round(stats.streamBandwidth);
|
|
71
72
|
}
|
|
72
73
|
} catch (err) {}
|
|
73
74
|
return null;
|
|
@@ -78,7 +79,7 @@ export default class ShakaTech {
|
|
|
78
79
|
// Use estimatedBandwidth for measured bitrate
|
|
79
80
|
var stats = this.tech.getStats();
|
|
80
81
|
if (stats && stats.estimatedBandwidth > 0) {
|
|
81
|
-
return stats.estimatedBandwidth;
|
|
82
|
+
return Math.round(stats.estimatedBandwidth);
|
|
82
83
|
}
|
|
83
84
|
} catch (err) {}
|
|
84
85
|
return null;
|
|
@@ -89,7 +90,7 @@ export default class ShakaTech {
|
|
|
89
90
|
// Shaka: use estimatedBandwidth for download bitrate (no separate property)
|
|
90
91
|
var stats = this.tech.getStats();
|
|
91
92
|
if (stats && stats.estimatedBandwidth > 0) {
|
|
92
|
-
return stats.estimatedBandwidth;
|
|
93
|
+
return Math.round(stats.estimatedBandwidth);
|
|
93
94
|
}
|
|
94
95
|
} catch (err) {}
|
|
95
96
|
return null;
|
package/src/tracker.js
CHANGED
|
@@ -8,17 +8,86 @@ import ImaAdsTracker from './ads/ima';
|
|
|
8
8
|
import BrightcoveImaAdsTracker from './ads/brightcove-ima';
|
|
9
9
|
import FreewheelAdsTracker from './ads/freewheel';
|
|
10
10
|
import DaiAdsTracker from './ads/dai';
|
|
11
|
+
import MediaTailorAdsTracker from './ads/media-tailor';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Explicit ad tracking modes. Pass via config.ad.type.
|
|
15
|
+
*
|
|
16
|
+
* CSAI (Client-Side Ad Insertion)
|
|
17
|
+
* All CSAI frameworks share the adsready event path. The tracker auto-detects
|
|
18
|
+
* the right framework (Brightcove IMA → IMA → Freewheel → generic) — no sub-type needed.
|
|
19
|
+
*
|
|
20
|
+
* SSAI (Server-Side Ad Insertion) — sub-type is always required.
|
|
21
|
+
* Each SSAI platform has its own SDK and a different activation path.
|
|
22
|
+
* Cannot be auto-detected — declare which one you are using.
|
|
23
|
+
* SSAI.DAI — Google DAI (google.ima.dai.api)
|
|
24
|
+
* SSAI.MT — AWS MediaTailor
|
|
25
|
+
*
|
|
26
|
+
* If config.ad is not set, no ad tracking runs (safe default).
|
|
27
|
+
* To add a new SSAI platform: add a key to SSAI — validation derives from this object.
|
|
28
|
+
*/
|
|
29
|
+
export const AD_TRACKING = {
|
|
30
|
+
CSAI: 'csai',
|
|
31
|
+
SSAI: Object.freeze({
|
|
32
|
+
DAI: 'ssai:dai',
|
|
33
|
+
MT: 'ssai:mt',
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
11
36
|
|
|
12
37
|
export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
13
38
|
constructor(player, options) {
|
|
14
39
|
super(player, options);
|
|
15
|
-
|
|
40
|
+
this.options = options;
|
|
16
41
|
this.isContentEnd = false;
|
|
17
42
|
this.imaAdCuePoints = '';
|
|
18
43
|
this.daiInitialized = false;
|
|
44
|
+
this.adTracking = options?.config?.ad?.type || null;
|
|
45
|
+
|
|
46
|
+
// When options are provided but config.ad is not declared, no ad tracking
|
|
47
|
+
// will run. Warn so the caller knows to set it explicitly.
|
|
48
|
+
if (options && !this.adTracking) {
|
|
49
|
+
nrvideo.Log.warn(
|
|
50
|
+
'VideojsTracker: config.ad not set — no ad tracking will run. ' +
|
|
51
|
+
'Set config.ad.type to enable it (e.g. AD_TRACKING.CSAI, AD_TRACKING.SSAI.MT).'
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Merge config.ad options (segmentPrefix, trackingUrl) into the internal
|
|
56
|
+
// mediatailor object that MediaTailorAdsTracker reads.
|
|
57
|
+
if (this.adTracking === AD_TRACKING.SSAI.MT) {
|
|
58
|
+
const adConfig = options?.config?.ad || {};
|
|
59
|
+
this.options = Object.assign({}, this.options, {
|
|
60
|
+
mediatailor: {
|
|
61
|
+
segmentPrefix: adConfig.segmentPrefix,
|
|
62
|
+
trackingUrl: adConfig.trackingUrl,
|
|
63
|
+
...this.options?.mediatailor,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
19
68
|
nrvideo.Core.addTracker(this, options);
|
|
20
69
|
}
|
|
21
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Sets the ad tracking mode at runtime.
|
|
73
|
+
* Must be called before the first loadstart / adsready event to take effect.
|
|
74
|
+
* @param {'csai'|'ssai:dai'|'ssai:mt'} type
|
|
75
|
+
*/
|
|
76
|
+
setAdTracking(type) {
|
|
77
|
+
const valid = [
|
|
78
|
+
AD_TRACKING.CSAI,
|
|
79
|
+
...Object.values(AD_TRACKING.SSAI),
|
|
80
|
+
];
|
|
81
|
+
if (!valid.includes(type)) {
|
|
82
|
+
nrvideo.Log.warn(
|
|
83
|
+
`VideojsTracker.setAdTracking: unknown value "${type}". Valid values: ${valid.join(', ')}`
|
|
84
|
+
);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.adTracking = type;
|
|
88
|
+
nrvideo.Log.debug(`VideojsTracker: adTracking set to "${type}"`);
|
|
89
|
+
}
|
|
90
|
+
|
|
22
91
|
getTech() {
|
|
23
92
|
let tech = this.player.tech({ IWillNotUseThisInPlugins: true });
|
|
24
93
|
|
|
@@ -125,17 +194,18 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
125
194
|
if (tech?.vhs?.playlists?.media()) {
|
|
126
195
|
const activePlaylist = tech.vhs.playlists.media();
|
|
127
196
|
// Use AVERAGE-BANDWIDTH if available, fallback to BANDWIDTH
|
|
128
|
-
|
|
197
|
+
const bitrate =
|
|
129
198
|
activePlaylist.attributes['AVERAGE-BANDWIDTH'] ||
|
|
130
199
|
activePlaylist.attributes.BANDWIDTH ||
|
|
131
|
-
null
|
|
132
|
-
);
|
|
200
|
+
null;
|
|
201
|
+
return bitrate !== null ? Math.round(bitrate) : null;
|
|
133
202
|
}
|
|
134
203
|
|
|
135
204
|
// 2. Fallback to tech wrappers (Shaka/Hls.js) if they have a getBitrate method
|
|
136
205
|
const techWrapper = this.getTech();
|
|
137
206
|
if (techWrapper?.getBitrate) {
|
|
138
|
-
|
|
207
|
+
const bitrate = techWrapper.getBitrate();
|
|
208
|
+
return bitrate !== null ? Math.round(bitrate) : null;
|
|
139
209
|
}
|
|
140
210
|
} catch (err) {
|
|
141
211
|
/* ignore */
|
|
@@ -161,13 +231,14 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
161
231
|
const maxBitrate = Math.max(
|
|
162
232
|
...allRenditions.map((p) => p.attributes.BANDWIDTH || 0),
|
|
163
233
|
);
|
|
164
|
-
return maxBitrate > 0 ? maxBitrate : null;
|
|
234
|
+
return maxBitrate > 0 ? Math.round(maxBitrate) : null;
|
|
165
235
|
}
|
|
166
236
|
|
|
167
237
|
// Fallback to tech wrappers (Shaka/Hls.js)
|
|
168
238
|
const techWrapper = this.getTech();
|
|
169
239
|
if (techWrapper?.getManifestBitrate) {
|
|
170
|
-
|
|
240
|
+
const bitrate = techWrapper.getManifestBitrate();
|
|
241
|
+
return bitrate !== null ? Math.round(bitrate) : null;
|
|
171
242
|
}
|
|
172
243
|
} catch (e) {
|
|
173
244
|
/* ignore */
|
|
@@ -181,13 +252,14 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
181
252
|
|
|
182
253
|
// VHS stats.bandwidth
|
|
183
254
|
if (tech?.vhs?.stats?.bandwidth && tech.vhs.stats.bandwidth > 0) {
|
|
184
|
-
return tech.vhs.stats.bandwidth;
|
|
255
|
+
return Math.round(tech.vhs.stats.bandwidth);
|
|
185
256
|
}
|
|
186
257
|
|
|
187
258
|
// Fallback to tech wrappers (Shaka/Hls.js)
|
|
188
259
|
const techWrapper = this.getTech();
|
|
189
260
|
if (techWrapper?.getSegmentDownloadBitrate) {
|
|
190
|
-
|
|
261
|
+
const bitrate = techWrapper.getSegmentDownloadBitrate();
|
|
262
|
+
return bitrate !== null ? Math.round(bitrate) : null;
|
|
191
263
|
}
|
|
192
264
|
} catch (err) {
|
|
193
265
|
/* ignore */
|
|
@@ -199,13 +271,14 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
199
271
|
const tech = this.player.tech({ IWillNotUseThisInPlugins: true });
|
|
200
272
|
|
|
201
273
|
if (tech?.vhs?.throughput && tech.vhs.throughput > 0) {
|
|
202
|
-
return tech.vhs.throughput;
|
|
274
|
+
return Math.round(tech.vhs.throughput);
|
|
203
275
|
}
|
|
204
276
|
|
|
205
277
|
// Fallback to tech wrapper implementation
|
|
206
278
|
const techWrapper = this.getTech();
|
|
207
279
|
if (techWrapper?.getNetworkDownloadBitrate) {
|
|
208
|
-
|
|
280
|
+
const bitrate = techWrapper.getNetworkDownloadBitrate();
|
|
281
|
+
return bitrate !== null ? Math.round(bitrate) : null;
|
|
209
282
|
}
|
|
210
283
|
|
|
211
284
|
return null;
|
|
@@ -272,6 +345,7 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
272
345
|
this.onTimeupdate = this.onTimeupdate.bind(this);
|
|
273
346
|
this.OnAdsAllpodsCompleted = this.OnAdsAllpodsCompleted.bind(this);
|
|
274
347
|
this.onStreamManager = this.onStreamManager.bind(this);
|
|
348
|
+
this.onCanPlayThrough = this.onCanPlayThrough.bind(this);
|
|
275
349
|
|
|
276
350
|
this.player.on('loadstart', this.onDownload);
|
|
277
351
|
this.player.on('loadeddata', this.onDownload);
|
|
@@ -292,6 +366,7 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
292
366
|
this.player.on('timeupdate', this.onTimeupdate);
|
|
293
367
|
this.player.on('ads-allpods-completed', this.OnAdsAllpodsCompleted);
|
|
294
368
|
this.player.on('stream-manager', this.onStreamManager);
|
|
369
|
+
this.player.on('canplaythrough', this.onCanPlayThrough);
|
|
295
370
|
}
|
|
296
371
|
|
|
297
372
|
unregisterListeners() {
|
|
@@ -314,15 +389,31 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
314
389
|
this.player.off('timeupdate', this.onTimeupdate);
|
|
315
390
|
this.player.off('ads-allpods-completed', this.OnAdsAllpodsCompleted);
|
|
316
391
|
this.player.off('stream-manager', this.onStreamManager);
|
|
392
|
+
this.player.off('canplaythrough', this.onCanPlayThrough);
|
|
317
393
|
}
|
|
318
394
|
|
|
319
395
|
onDownload(e) {
|
|
320
396
|
this.sendDownload({ state: e.type });
|
|
397
|
+
|
|
398
|
+
if (
|
|
399
|
+
this.adTracking === AD_TRACKING.SSAI.MT &&
|
|
400
|
+
!this.adsTracker &&
|
|
401
|
+
e.type === 'loadstart'
|
|
402
|
+
) {
|
|
403
|
+
nrvideo.Log.debug('VideojsTracker: Creating MediaTailorAdsTracker');
|
|
404
|
+
this.setAdsTracker(new MediaTailorAdsTracker(this.player, this.options));
|
|
405
|
+
// MediaTailor SSAI starts with content, not ads (unlike client-side frameworks)
|
|
406
|
+
this.adsTracker.setIsAd(false);
|
|
407
|
+
}
|
|
321
408
|
}
|
|
322
409
|
|
|
323
410
|
// DAI methods
|
|
324
411
|
onStreamManager(event) {
|
|
325
|
-
if (
|
|
412
|
+
if (
|
|
413
|
+
this.adTracking === AD_TRACKING.SSAI.DAI &&
|
|
414
|
+
!this.adsTracker &&
|
|
415
|
+
event.StreamManager
|
|
416
|
+
) {
|
|
326
417
|
const daiTracker = new DaiAdsTracker(this.player);
|
|
327
418
|
daiTracker.setStreamManager(event.StreamManager);
|
|
328
419
|
this.setAdsTracker(daiTracker);
|
|
@@ -331,20 +422,29 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
331
422
|
// DAI methods end
|
|
332
423
|
|
|
333
424
|
onAdsready() {
|
|
425
|
+
// SSAI platforms never fire adsready — skip when an SSAI type is explicitly set.
|
|
426
|
+
if (Object.values(AD_TRACKING.SSAI).includes(this.adTracking)) return;
|
|
427
|
+
|
|
428
|
+
// config.ad.type is not set — warn and fall back to auto-detection.
|
|
429
|
+
if (!this.adTracking) {
|
|
430
|
+
nrvideo.Log.warn(
|
|
431
|
+
'VideojsTracker: adsready fired but config.ad.type is not set — ' +
|
|
432
|
+
'attempting CSAI auto-detection for backward compatibility.'
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
334
436
|
if (!this.adsTracker) {
|
|
335
437
|
if (BrightcoveImaAdsTracker.isUsing(this.player)) {
|
|
336
|
-
|
|
438
|
+
nrvideo.Log.debug('VideojsTracker: auto-detected BrightcoveImaAdsTracker');
|
|
337
439
|
this.setAdsTracker(new BrightcoveImaAdsTracker(this.player));
|
|
338
440
|
} else if (ImaAdsTracker.isUsing(this.player)) {
|
|
339
|
-
|
|
441
|
+
nrvideo.Log.debug('VideojsTracker: auto-detected ImaAdsTracker');
|
|
340
442
|
this.setAdsTracker(new ImaAdsTracker(this.player));
|
|
341
443
|
} else if (FreewheelAdsTracker.isUsing(this.player)) {
|
|
342
|
-
|
|
343
|
-
|
|
444
|
+
nrvideo.Log.debug('VideojsTracker: auto-detected FreewheelAdsTracker');
|
|
344
445
|
this.setAdsTracker(new FreewheelAdsTracker(this.player));
|
|
345
|
-
// } else if (OnceAdsTracker.isUsing(this)) { // Once
|
|
346
446
|
} else {
|
|
347
|
-
|
|
447
|
+
nrvideo.Log.debug('VideojsTracker: no specific CSAI framework detected, using generic VideojsAdsTracker');
|
|
348
448
|
this.setAdsTracker(new VideojsAdsTracker(this.player));
|
|
349
449
|
}
|
|
350
450
|
}
|
|
@@ -369,15 +469,50 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
369
469
|
this.FreewheelAdsCompleted = true;
|
|
370
470
|
}
|
|
371
471
|
|
|
472
|
+
/**
|
|
473
|
+
* Check if ads tracker is currently in ad mode
|
|
474
|
+
* @returns {boolean} True if ads are playing
|
|
475
|
+
*/
|
|
476
|
+
isAdsTrackerActive() {
|
|
477
|
+
// For CSAI: videojs-contrib-ads manages ad state exclusively
|
|
478
|
+
if (this.player.ads) {
|
|
479
|
+
return this.player.ads.state === 'ads-playback';
|
|
480
|
+
}
|
|
481
|
+
// For SSAI (no player.ads): check tracker's isAd flag (manually toggled)
|
|
482
|
+
return this.adsTracker && this.adsTracker.isAd && this.adsTracker.isAd();
|
|
483
|
+
}
|
|
484
|
+
|
|
372
485
|
onPlay() {
|
|
373
486
|
this.sendRequest();
|
|
374
487
|
}
|
|
375
488
|
|
|
376
489
|
onPause() {
|
|
490
|
+
// Don't send CONTENT_PAUSE if ads are playing (ads tracker handles it)
|
|
491
|
+
if (this.isAdsTrackerActive()) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
// Don't send CONTENT_PAUSE if video has ended (CONTENT_END will be sent instead)
|
|
495
|
+
if (this.player.ended()) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
377
498
|
this.sendPause();
|
|
378
499
|
}
|
|
379
500
|
|
|
501
|
+
onCanPlayThrough() {
|
|
502
|
+
// Don't send CONTENT_BUFFER_END if ads are playing (ads tracker handles it)
|
|
503
|
+
if (this.isAdsTrackerActive()) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
// Send BUFFER_END when buffer fills, even if paused
|
|
507
|
+
// This ensures accurate buffer duration metrics when user pauses during buffering
|
|
508
|
+
this.sendBufferEnd();
|
|
509
|
+
}
|
|
510
|
+
|
|
380
511
|
onPlaying() {
|
|
512
|
+
// Don't send CONTENT_RESUME if ads are playing (ads tracker handles it)
|
|
513
|
+
if (this.isAdsTrackerActive()) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
381
516
|
this.sendResume();
|
|
382
517
|
this.sendBufferEnd();
|
|
383
518
|
}
|
|
@@ -402,10 +537,18 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
402
537
|
}
|
|
403
538
|
|
|
404
539
|
onSeeking() {
|
|
540
|
+
// Don't send CONTENT_SEEK_START if ads are playing (ads tracker handles it)
|
|
541
|
+
if (this.isAdsTrackerActive()) {
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
405
544
|
this.sendSeekStart();
|
|
406
545
|
}
|
|
407
546
|
|
|
408
547
|
onSeeked() {
|
|
548
|
+
// Don't send CONTENT_SEEK_END if ads are playing (ads tracker handles it)
|
|
549
|
+
if (this.isAdsTrackerActive()) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
409
552
|
this.sendSeekEnd();
|
|
410
553
|
}
|
|
411
554
|
|
|
@@ -420,6 +563,10 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
420
563
|
}
|
|
421
564
|
|
|
422
565
|
onWaiting(e) {
|
|
566
|
+
// Don't send CONTENT_BUFFER_START if ads are playing (ads tracker handles it)
|
|
567
|
+
if (this.isAdsTrackerActive()) {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
423
570
|
this.sendBufferStart();
|
|
424
571
|
}
|
|
425
572
|
|
|
@@ -430,6 +577,11 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
430
577
|
}
|
|
431
578
|
}
|
|
432
579
|
|
|
580
|
+
// Attach AD_TRACKING and Log as static properties so UMD callers can use
|
|
581
|
+
// VideojsTracker.AD_TRACKING and VideojsTracker.Log without importing named exports.
|
|
582
|
+
VideojsTracker.AD_TRACKING = AD_TRACKING;
|
|
583
|
+
VideojsTracker.Log = nrvideo.Log;
|
|
584
|
+
|
|
433
585
|
// Static members
|
|
434
586
|
export {
|
|
435
587
|
HlsJsTech,
|
|
@@ -439,4 +591,6 @@ export {
|
|
|
439
591
|
ImaAdsTracker,
|
|
440
592
|
BrightcoveImaAdsTracker,
|
|
441
593
|
FreewheelAdsTracker,
|
|
594
|
+
DaiAdsTracker,
|
|
595
|
+
MediaTailorAdsTracker,
|
|
442
596
|
};
|