stormcloud-video-player 0.8.2 → 0.8.4
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/dist/stormcloud-vp.min.js +3 -1
- package/lib/index.cjs +536 -341
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +37 -2
- package/lib/index.d.ts +37 -2
- package/lib/index.js +477 -342
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +266 -206
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +5 -2
- package/lib/players/FilePlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.cjs +266 -206
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.d.cts +1 -1
- package/lib/players/index.cjs +266 -206
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
- package/lib/sdk/hlsAdPlayer.d.cts +1 -1
- package/lib/sdk/pal.cjs.map +1 -1
- package/lib/{types-DDwAfBLt.d.cts → types-BmF_60m2.d.cts} +2 -0
- package/lib/ui/StormcloudVideoPlayer.cjs +450 -341
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
- package/lib/utils/browserCompat.cjs.map +1 -1
- package/lib/utils/mqttClient.cjs +245 -0
- package/lib/utils/mqttClient.cjs.map +1 -0
- package/lib/utils/mqttClient.d.cts +13 -0
- package/lib/utils/mqttConfig.cjs +141 -0
- package/lib/utils/mqttConfig.cjs.map +1 -0
- package/lib/utils/mqttConfig.d.cts +20 -0
- package/lib/utils/polyfills.cjs.map +1 -1
- package/lib/utils/tracking.cjs +182 -170
- package/lib/utils/tracking.cjs.map +1 -1
- package/lib/utils/tracking.d.cts +1 -1
- package/package.json +3 -1
- package/src/certs/emqxsl-ca.crt +22 -0
package/lib/utils/tracking.cjs
CHANGED
|
@@ -159,9 +159,11 @@ function _ts_generator(thisArg, body) {
|
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
|
+
var __create = Object.create;
|
|
162
163
|
var __defProp = Object.defineProperty;
|
|
163
164
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
164
165
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
166
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
165
167
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
166
168
|
var __export = function __export(target, all) {
|
|
167
169
|
for(var name in all)__defProp(target, name, {
|
|
@@ -200,6 +202,16 @@ var __copyProps = function __copyProps(to, from, except, desc) {
|
|
|
200
202
|
}
|
|
201
203
|
return to;
|
|
202
204
|
};
|
|
205
|
+
var __toESM = function __toESM(mod, isNodeMode, target) {
|
|
206
|
+
return target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(// If the importer is in node compatibility mode or this is not an ESM
|
|
207
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
208
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
209
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
210
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
211
|
+
value: mod,
|
|
212
|
+
enumerable: true
|
|
213
|
+
}) : target, mod);
|
|
214
|
+
};
|
|
203
215
|
var __toCommonJS = function __toCommonJS(mod) {
|
|
204
216
|
return __copyProps(__defProp({}, "__esModule", {
|
|
205
217
|
value: true
|
|
@@ -231,6 +243,100 @@ __export(tracking_exports, {
|
|
|
231
243
|
}
|
|
232
244
|
});
|
|
233
245
|
module.exports = __toCommonJS(tracking_exports);
|
|
246
|
+
// src/utils/mqttConfig.ts
|
|
247
|
+
var DEFAULT_MQTT_CONFIG = {
|
|
248
|
+
enabled: true,
|
|
249
|
+
brokerAddress: "vecbae77.ala.us-east-1.emqxsl.com",
|
|
250
|
+
brokerPort: 8883,
|
|
251
|
+
wsPort: 8084,
|
|
252
|
+
username: "for-sonifi",
|
|
253
|
+
password: "sonifi-mqtt",
|
|
254
|
+
topicPrefix: "adstorm/players",
|
|
255
|
+
qos: 1
|
|
256
|
+
};
|
|
257
|
+
var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
|
|
258
|
+
function isMQTTEnabled() {
|
|
259
|
+
return mqttConfig.enabled;
|
|
260
|
+
}
|
|
261
|
+
function buildMQTTBrokerUrl() {
|
|
262
|
+
if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;
|
|
263
|
+
return "wss://".concat(mqttConfig.brokerAddress, ":").concat(mqttConfig.wsPort, "/mqtt");
|
|
264
|
+
}
|
|
265
|
+
function buildPlayerTopic(licenseKey, channel) {
|
|
266
|
+
return "".concat(mqttConfig.topicPrefix, "/").concat(licenseKey, "/").concat(channel);
|
|
267
|
+
}
|
|
268
|
+
// src/utils/mqttClient.ts
|
|
269
|
+
var import_mqtt = __toESM(require("mqtt"), 1);
|
|
270
|
+
var LOG = "[StormcloudVideoPlayer][MQTT]";
|
|
271
|
+
var client = null;
|
|
272
|
+
var status = "disconnected";
|
|
273
|
+
function initMQTTClient() {
|
|
274
|
+
if (client || !isMQTTEnabled()) return;
|
|
275
|
+
var url = buildMQTTBrokerUrl();
|
|
276
|
+
status = "connecting";
|
|
277
|
+
var clientId = "stormcloud-vp-".concat(Math.random().toString(36).slice(2, 9));
|
|
278
|
+
try {
|
|
279
|
+
client = import_mqtt.default.connect(url, {
|
|
280
|
+
clientId: clientId,
|
|
281
|
+
username: mqttConfig.username,
|
|
282
|
+
password: mqttConfig.password,
|
|
283
|
+
keepalive: 60,
|
|
284
|
+
clean: true,
|
|
285
|
+
reconnectPeriod: 5e3,
|
|
286
|
+
connectTimeout: 1e4,
|
|
287
|
+
queueQoSZero: false
|
|
288
|
+
});
|
|
289
|
+
} catch (err) {
|
|
290
|
+
status = "error";
|
|
291
|
+
console.warn("".concat(LOG, " connect() threw:"), err);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
client.on("connect", function() {
|
|
295
|
+
status = "connected";
|
|
296
|
+
console.info("".concat(LOG, " connected to ").concat(url));
|
|
297
|
+
});
|
|
298
|
+
client.on("reconnect", function() {
|
|
299
|
+
status = "connecting";
|
|
300
|
+
console.info("".concat(LOG, " reconnecting…"));
|
|
301
|
+
});
|
|
302
|
+
client.on("offline", function() {
|
|
303
|
+
status = "disconnected";
|
|
304
|
+
console.warn("".concat(LOG, " offline"));
|
|
305
|
+
});
|
|
306
|
+
client.on("error", function(err) {
|
|
307
|
+
status = "error";
|
|
308
|
+
console.warn("".concat(LOG, " error:"), err.message);
|
|
309
|
+
});
|
|
310
|
+
client.on("close", function() {
|
|
311
|
+
if (status === "connected") {
|
|
312
|
+
status = "disconnected";
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
function ensureMQTTClient() {
|
|
317
|
+
if (isMQTTEnabled() && !client) {
|
|
318
|
+
initMQTTClient();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function publishMQTT(topic, payload) {
|
|
322
|
+
if (!isMQTTEnabled()) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
ensureMQTTClient();
|
|
326
|
+
if (!client) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
client.publish(topic, JSON.stringify(payload), {
|
|
331
|
+
qos: mqttConfig.qos
|
|
332
|
+
});
|
|
333
|
+
return true;
|
|
334
|
+
} catch (err) {
|
|
335
|
+
console.warn("".concat(LOG, " publish failed on ").concat(topic, ":"), err);
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// src/utils/tracking.ts
|
|
234
340
|
var cachedBrowserId = null;
|
|
235
341
|
function getClientInfo() {
|
|
236
342
|
var _screen, _screen1, _screen2, _screen3, _screen_orientation, _screen4, _screen5, _window, _window1, _window_screen_orientation, _window_screen, _navigator_languages;
|
|
@@ -376,7 +482,7 @@ function getClientInfo() {
|
|
|
376
482
|
}
|
|
377
483
|
function getBrowserID(clientInfo) {
|
|
378
484
|
return _async_to_generator(function() {
|
|
379
|
-
var fingerprintString, encodedData, utf8, buffer, i, hashBuffer,
|
|
485
|
+
var _crypto_subtle, fingerprintString, encodedData, utf8, buffer, i, hashBuffer, hashHex, unused, hash, i1, char, fallbackHash, timestamp, random;
|
|
380
486
|
return _ts_generator(this, function(_state) {
|
|
381
487
|
switch(_state.label){
|
|
382
488
|
case 0:
|
|
@@ -387,7 +493,7 @@ function getBrowserID(clientInfo) {
|
|
|
387
493
|
];
|
|
388
494
|
}
|
|
389
495
|
fingerprintString = JSON.stringify(clientInfo);
|
|
390
|
-
if (!(typeof crypto !== "undefined" && crypto.subtle
|
|
496
|
+
if (!(typeof crypto !== "undefined" && ((_crypto_subtle = crypto.subtle) === null || _crypto_subtle === void 0 ? void 0 : _crypto_subtle.digest))) return [
|
|
391
497
|
3,
|
|
392
498
|
5
|
|
393
499
|
];
|
|
@@ -425,8 +531,7 @@ function getBrowserID(clientInfo) {
|
|
|
425
531
|
];
|
|
426
532
|
case 3:
|
|
427
533
|
hashBuffer = _state.sent();
|
|
428
|
-
|
|
429
|
-
hashHex = hashArray.map(function(b) {
|
|
534
|
+
hashHex = Array.from(new Uint8Array(hashBuffer)).map(function(b) {
|
|
430
535
|
return b.toString(16).padStart(2, "0");
|
|
431
536
|
}).join("");
|
|
432
537
|
cachedBrowserId = hashHex;
|
|
@@ -435,8 +540,8 @@ function getBrowserID(clientInfo) {
|
|
|
435
540
|
hashHex
|
|
436
541
|
];
|
|
437
542
|
case 4:
|
|
438
|
-
|
|
439
|
-
console.warn("[StormcloudVideoPlayer] crypto.subtle
|
|
543
|
+
unused = _state.sent();
|
|
544
|
+
console.warn("[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash");
|
|
440
545
|
return [
|
|
441
546
|
3,
|
|
442
547
|
5
|
|
@@ -460,177 +565,91 @@ function getBrowserID(clientInfo) {
|
|
|
460
565
|
});
|
|
461
566
|
})();
|
|
462
567
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
var HEARTBEAT_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/heartbeat");
|
|
466
|
-
var IMPRESSIONS_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/impressions/ingest");
|
|
467
|
-
function buildHeaders(licenseKey) {
|
|
468
|
-
var headers = {
|
|
469
|
-
"Content-Type": "application/json"
|
|
470
|
-
};
|
|
471
|
-
if (licenseKey) {
|
|
472
|
-
headers["Authorization"] = "Bearer ".concat(licenseKey);
|
|
473
|
-
}
|
|
474
|
-
return headers;
|
|
475
|
-
}
|
|
476
|
-
function sendTrackRequest(licenseKey, body) {
|
|
477
|
-
return _async_to_generator(function() {
|
|
478
|
-
var response;
|
|
479
|
-
return _ts_generator(this, function(_state) {
|
|
480
|
-
switch(_state.label){
|
|
481
|
-
case 0:
|
|
482
|
-
return [
|
|
483
|
-
4,
|
|
484
|
-
fetch(TRACK_URL, {
|
|
485
|
-
method: "POST",
|
|
486
|
-
headers: buildHeaders(licenseKey),
|
|
487
|
-
body: JSON.stringify(body)
|
|
488
|
-
})
|
|
489
|
-
];
|
|
490
|
-
case 1:
|
|
491
|
-
response = _state.sent();
|
|
492
|
-
if (!response.ok) {
|
|
493
|
-
throw new Error("HTTP error! status: ".concat(response.status));
|
|
494
|
-
}
|
|
495
|
-
return [
|
|
496
|
-
4,
|
|
497
|
-
response.json()
|
|
498
|
-
];
|
|
499
|
-
case 2:
|
|
500
|
-
_state.sent();
|
|
501
|
-
return [
|
|
502
|
-
2
|
|
503
|
-
];
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
})();
|
|
568
|
+
function canPublish(licenseKey) {
|
|
569
|
+
return Boolean(isMQTTEnabled() && licenseKey);
|
|
507
570
|
}
|
|
508
|
-
function
|
|
571
|
+
function buildPlayerMetricEvent() {
|
|
509
572
|
return _async_to_generator(function() {
|
|
510
|
-
var
|
|
511
|
-
return _ts_generator(this, function(_state) {
|
|
512
|
-
switch(_state.label){
|
|
513
|
-
case 0:
|
|
514
|
-
return [
|
|
515
|
-
4,
|
|
516
|
-
fetch(url, {
|
|
517
|
-
method: "POST",
|
|
518
|
-
headers: buildHeaders(licenseKey),
|
|
519
|
-
body: JSON.stringify(body)
|
|
520
|
-
})
|
|
521
|
-
];
|
|
522
|
-
case 1:
|
|
523
|
-
response = _state.sent();
|
|
524
|
-
if (!response.ok) {
|
|
525
|
-
throw new Error("HTTP error! status: ".concat(response.status));
|
|
526
|
-
}
|
|
527
|
-
return [
|
|
528
|
-
4,
|
|
529
|
-
response.json()
|
|
530
|
-
];
|
|
531
|
-
case 2:
|
|
532
|
-
_state.sent();
|
|
533
|
-
return [
|
|
534
|
-
2
|
|
535
|
-
];
|
|
536
|
-
}
|
|
537
|
-
});
|
|
538
|
-
})();
|
|
539
|
-
}
|
|
540
|
-
function buildPlayerMetricEvent(_0) {
|
|
541
|
-
return _async_to_generator(function(licenseKey) {
|
|
542
|
-
var context, flags, _flags_captureAt, clientInfo, browserId, captureAt;
|
|
573
|
+
var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
|
|
543
574
|
var _arguments = arguments;
|
|
544
575
|
return _ts_generator(this, function(_state) {
|
|
545
576
|
switch(_state.label){
|
|
546
577
|
case 0:
|
|
547
|
-
context = _arguments.length >
|
|
578
|
+
context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
|
|
548
579
|
clientInfo = getClientInfo();
|
|
549
580
|
return [
|
|
550
581
|
4,
|
|
551
582
|
getBrowserID(clientInfo)
|
|
552
583
|
];
|
|
553
584
|
case 1:
|
|
554
|
-
|
|
585
|
+
playerId = _state.sent();
|
|
555
586
|
captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
|
|
556
587
|
return [
|
|
557
588
|
2,
|
|
558
|
-
{
|
|
559
|
-
player_id:
|
|
560
|
-
browserId: browserId,
|
|
589
|
+
_object_spread({
|
|
590
|
+
player_id: playerId,
|
|
561
591
|
device_type: clientInfo.deviceType,
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
timestamp: captureAt
|
|
570
|
-
}
|
|
592
|
+
os: clientInfo.os.toLowerCase(),
|
|
593
|
+
ad_loaded: (_flags_adLoaded = flags.adLoaded) !== null && _flags_adLoaded !== void 0 ? _flags_adLoaded : false,
|
|
594
|
+
ad_detect: (_flags_adDetect = flags.adDetect) !== null && _flags_adDetect !== void 0 ? _flags_adDetect : false,
|
|
595
|
+
capture_at: captureAt
|
|
596
|
+
}, context.inputStreamType ? {
|
|
597
|
+
input_stream_type: context.inputStreamType
|
|
598
|
+
} : {})
|
|
571
599
|
];
|
|
572
600
|
}
|
|
573
601
|
});
|
|
574
602
|
}).apply(this, arguments);
|
|
575
603
|
}
|
|
604
|
+
function publishTracking(licenseKey, channel, body) {
|
|
605
|
+
ensureMQTTClient();
|
|
606
|
+
publishMQTT(buildPlayerTopic(licenseKey, channel), body);
|
|
607
|
+
}
|
|
576
608
|
function sendInitialTracking(_0) {
|
|
577
609
|
return _async_to_generator(function(licenseKey) {
|
|
578
|
-
var context,
|
|
610
|
+
var context, metricEvent, error;
|
|
579
611
|
var _arguments = arguments;
|
|
580
612
|
return _ts_generator(this, function(_state) {
|
|
581
613
|
switch(_state.label){
|
|
582
614
|
case 0:
|
|
583
615
|
context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
|
|
616
|
+
if (!canPublish(licenseKey)) return [
|
|
617
|
+
2
|
|
618
|
+
];
|
|
584
619
|
_state.label = 1;
|
|
585
620
|
case 1:
|
|
586
621
|
_state.trys.push([
|
|
587
622
|
1,
|
|
588
|
-
|
|
623
|
+
3,
|
|
589
624
|
,
|
|
590
|
-
|
|
625
|
+
4
|
|
591
626
|
]);
|
|
592
|
-
clientInfo = getClientInfo();
|
|
593
627
|
return [
|
|
594
628
|
4,
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
browserId = _state.sent();
|
|
599
|
-
trackingData = _object_spread({
|
|
600
|
-
browserId: browserId
|
|
601
|
-
}, clientInfo);
|
|
602
|
-
return [
|
|
603
|
-
4,
|
|
604
|
-
sendTrackRequest(licenseKey, {
|
|
605
|
-
events: [
|
|
606
|
-
{
|
|
607
|
-
player_id: browserId,
|
|
608
|
-
device_type: clientInfo.deviceType,
|
|
609
|
-
input_stream_type: context.inputStreamType,
|
|
610
|
-
os: clientInfo.os,
|
|
611
|
-
ad_loaded: false,
|
|
612
|
-
ad_detect: false,
|
|
613
|
-
license_key: licenseKey,
|
|
614
|
-
capture_at: /* @__PURE__ */ new Date().toISOString()
|
|
615
|
-
}
|
|
616
|
-
],
|
|
617
|
-
trackingData: trackingData
|
|
629
|
+
buildPlayerMetricEvent(context, {
|
|
630
|
+
adLoaded: false,
|
|
631
|
+
adDetect: false
|
|
618
632
|
})
|
|
619
633
|
];
|
|
620
|
-
case
|
|
621
|
-
_state.sent();
|
|
634
|
+
case 2:
|
|
635
|
+
metricEvent = _state.sent();
|
|
636
|
+
publishTracking(licenseKey, "metrics", {
|
|
637
|
+
events: [
|
|
638
|
+
metricEvent
|
|
639
|
+
]
|
|
640
|
+
});
|
|
622
641
|
return [
|
|
623
642
|
3,
|
|
624
|
-
|
|
643
|
+
4
|
|
625
644
|
];
|
|
626
|
-
case
|
|
645
|
+
case 3:
|
|
627
646
|
error = _state.sent();
|
|
628
647
|
console.error("[StormcloudVideoPlayer] Error sending initial tracking data:", error);
|
|
629
648
|
return [
|
|
630
649
|
3,
|
|
631
|
-
|
|
650
|
+
4
|
|
632
651
|
];
|
|
633
|
-
case
|
|
652
|
+
case 4:
|
|
634
653
|
return [
|
|
635
654
|
2
|
|
636
655
|
];
|
|
@@ -734,53 +753,48 @@ function sendAdImpressionTracking(_0, _1) {
|
|
|
734
753
|
switch(_state.label){
|
|
735
754
|
case 0:
|
|
736
755
|
context = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
|
|
756
|
+
if (!canPublish(licenseKey)) return [
|
|
757
|
+
2
|
|
758
|
+
];
|
|
737
759
|
_state.label = 1;
|
|
738
760
|
case 1:
|
|
739
761
|
_state.trys.push([
|
|
740
762
|
1,
|
|
741
|
-
|
|
763
|
+
3,
|
|
742
764
|
,
|
|
743
|
-
|
|
765
|
+
4
|
|
744
766
|
]);
|
|
745
767
|
return [
|
|
746
768
|
4,
|
|
747
|
-
buildPlayerMetricEvent(
|
|
769
|
+
buildPlayerMetricEvent(context, {
|
|
748
770
|
captureAt: adImpressionInfo.timestamp
|
|
749
771
|
})
|
|
750
772
|
];
|
|
751
773
|
case 2:
|
|
752
774
|
metricEvent = _state.sent();
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
capture_at: adImpressionInfo.timestamp
|
|
765
|
-
}
|
|
766
|
-
]
|
|
767
|
-
})
|
|
768
|
-
])
|
|
769
|
-
];
|
|
770
|
-
case 3:
|
|
771
|
-
_state.sent();
|
|
775
|
+
publishTracking(licenseKey, "heartbeat", metricEvent);
|
|
776
|
+
publishTracking(licenseKey, "impressions", {
|
|
777
|
+
events: [
|
|
778
|
+
{
|
|
779
|
+
player_id: metricEvent.player_id,
|
|
780
|
+
ad_played_count: 1,
|
|
781
|
+
ad_url: adImpressionInfo.adUrl,
|
|
782
|
+
capture_at: adImpressionInfo.timestamp
|
|
783
|
+
}
|
|
784
|
+
]
|
|
785
|
+
});
|
|
772
786
|
return [
|
|
773
787
|
3,
|
|
774
|
-
|
|
788
|
+
4
|
|
775
789
|
];
|
|
776
|
-
case
|
|
790
|
+
case 3:
|
|
777
791
|
error = _state.sent();
|
|
778
792
|
console.error("[StormcloudVideoPlayer] Error sending ad impression tracking:", error);
|
|
779
793
|
return [
|
|
780
794
|
3,
|
|
781
|
-
|
|
795
|
+
4
|
|
782
796
|
];
|
|
783
|
-
case
|
|
797
|
+
case 4:
|
|
784
798
|
return [
|
|
785
799
|
2
|
|
786
800
|
];
|
|
@@ -796,38 +810,36 @@ function sendHeartbeat(_0) {
|
|
|
796
810
|
switch(_state.label){
|
|
797
811
|
case 0:
|
|
798
812
|
context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
|
|
813
|
+
if (!canPublish(licenseKey)) return [
|
|
814
|
+
2
|
|
815
|
+
];
|
|
799
816
|
_state.label = 1;
|
|
800
817
|
case 1:
|
|
801
818
|
_state.trys.push([
|
|
802
819
|
1,
|
|
803
|
-
|
|
820
|
+
3,
|
|
804
821
|
,
|
|
805
|
-
|
|
822
|
+
4
|
|
806
823
|
]);
|
|
807
824
|
return [
|
|
808
825
|
4,
|
|
809
|
-
buildPlayerMetricEvent(
|
|
826
|
+
buildPlayerMetricEvent(context, flags)
|
|
810
827
|
];
|
|
811
828
|
case 2:
|
|
812
829
|
heartbeatData = _state.sent();
|
|
813
|
-
|
|
814
|
-
4,
|
|
815
|
-
postJson(HEARTBEAT_URL, licenseKey, heartbeatData)
|
|
816
|
-
];
|
|
817
|
-
case 3:
|
|
818
|
-
_state.sent();
|
|
830
|
+
publishTracking(licenseKey, "heartbeat", heartbeatData);
|
|
819
831
|
return [
|
|
820
832
|
3,
|
|
821
|
-
|
|
833
|
+
4
|
|
822
834
|
];
|
|
823
|
-
case
|
|
835
|
+
case 3:
|
|
824
836
|
error = _state.sent();
|
|
825
837
|
console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
|
|
826
838
|
return [
|
|
827
839
|
3,
|
|
828
|
-
|
|
840
|
+
4
|
|
829
841
|
];
|
|
830
|
-
case
|
|
842
|
+
case 4:
|
|
831
843
|
return [
|
|
832
844
|
2
|
|
833
845
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/utils/tracking.cjs"],"names":[],"mappings":"AAAA","sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/utils/tracking.ts\nvar tracking_exports = {};\n__export(tracking_exports, {\n getBrowserID: () => getBrowserID,\n getClientInfo: () => getClientInfo,\n sendAdDetectTracking: () => sendAdDetectTracking,\n sendAdImpressionTracking: () => sendAdImpressionTracking,\n sendAdLoadedTracking: () => sendAdLoadedTracking,\n sendHeartbeat: () => sendHeartbeat,\n sendInitialTracking: () => sendInitialTracking\n});\nmodule.exports = __toCommonJS(tracking_exports);\nvar cachedBrowserId = null;\nfunction getClientInfo() {\n const ua = navigator.userAgent;\n const platform = navigator.platform;\n const vendor = navigator.vendor || \"\";\n const maxTouchPoints = navigator.maxTouchPoints || 0;\n const memory = navigator.deviceMemory || null;\n const hardwareConcurrency = navigator.hardwareConcurrency || 1;\n const screenInfo = {\n width: screen?.width,\n height: screen?.height,\n availWidth: screen?.availWidth,\n availHeight: screen?.availHeight,\n orientation: screen?.orientation?.type || \"\",\n pixelDepth: screen?.pixelDepth\n };\n let deviceType = \"desktop\";\n let brand = \"Unknown\";\n let os = \"Unknown\";\n let model = \"\";\n let isSmartTV = false;\n let isAndroid = false;\n let isWebView = false;\n let isWebApp = false;\n if (ua.includes(\"Web0S\")) {\n brand = \"LG\";\n os = \"webOS\";\n isSmartTV = true;\n deviceType = \"tv\";\n const webosMatch = ua.match(/Web0S\\/([^\\s]+)/);\n model = webosMatch ? `webOS ${webosMatch[1]}` : \"webOS TV\";\n } else if (ua.includes(\"Tizen\")) {\n brand = \"Samsung\";\n os = \"Tizen\";\n isSmartTV = true;\n deviceType = \"tv\";\n const tizenMatch = ua.match(/Tizen\\/([^\\s]+)/);\n const tvMatch = ua.match(/(?:Smart-TV|SMART-TV|TV)/i) ? \"Smart TV\" : \"\";\n model = tizenMatch ? `Tizen ${tizenMatch[1]} ${tvMatch}`.trim() : \"Tizen TV\";\n } else if (ua.includes(\"Philips\")) {\n brand = \"Philips\";\n os = \"Saphi\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"Sharp\") || ua.includes(\"AQUOS\")) {\n brand = \"Sharp\";\n os = \"Android TV\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"Android\") && (ua.includes(\"Sony\") || vendor.includes(\"Sony\"))) {\n brand = \"Sony\";\n os = \"Android TV\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"Android\") && (ua.includes(\"NetCast\") || ua.includes(\"LG\"))) {\n brand = \"LG\";\n os = \"Android TV\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\" Roku\") || ua.includes(\"Roku/\")) {\n brand = \"Roku\";\n os = \"Roku OS\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"AppleTV\")) {\n brand = \"Apple\";\n os = \"tvOS\";\n isSmartTV = true;\n deviceType = \"tv\";\n }\n if (ua.includes(\"Android\")) {\n isAndroid = true;\n os = \"Android\";\n deviceType = /Mobile/.test(ua) ? \"mobile\" : \"tablet\";\n if (ua.includes(\"Android\") && (maxTouchPoints === 0 || ua.includes(\"Google TV\") || ua.includes(\"XiaoMi\"))) {\n deviceType = \"tv\";\n isSmartTV = true;\n brand = brand === \"Unknown\" ? \"Android TV\" : brand;\n }\n const androidModelMatch = ua.match(/\\(([^)]*Android[^)]*)\\)/);\n if (androidModelMatch && androidModelMatch[1]) {\n model = androidModelMatch[1];\n }\n }\n if (/iPad|iPhone|iPod/.test(ua)) {\n os = \"iOS\";\n deviceType = \"mobile\";\n brand = \"Apple\";\n if (navigator.maxTouchPoints > 1 && /iPad/.test(ua)) {\n deviceType = \"tablet\";\n }\n }\n if (!isAndroid && !isSmartTV && !/Mobile/.test(ua)) {\n if (ua.includes(\"Windows\")) {\n os = \"Windows\";\n deviceType = \"desktop\";\n } else if (ua.includes(\"Mac\") && !/iPhone/.test(ua)) {\n os = \"macOS\";\n deviceType = \"desktop\";\n if (maxTouchPoints > 1) deviceType = \"tablet\";\n } else if (ua.includes(\"Linux\")) {\n os = \"Linux\";\n deviceType = \"desktop\";\n }\n }\n if (brand === \"Unknown\") {\n if (vendor.includes(\"Google\") || ua.includes(\"Chrome\")) brand = \"Google\";\n if (vendor.includes(\"Apple\")) brand = \"Apple\";\n if (vendor.includes(\"Samsung\") || ua.includes(\"SM-\")) brand = \"Samsung\";\n }\n isWebView = /wv|WebView|Linux; U;/.test(ua);\n if (window?.outerHeight === 0 && window?.outerWidth === 0) {\n isWebView = true;\n }\n isWebApp = window.matchMedia(\"(display-mode: standalone)\").matches || window.navigator.standalone === true || window.screen?.orientation?.angle !== void 0;\n return {\n brand,\n os,\n model: model || ua.substring(0, 50) + \"...\",\n deviceType,\n isSmartTV,\n isAndroid,\n isWebView,\n isWebApp,\n domain: window.location.hostname,\n origin: window.location.origin,\n path: window.location.pathname,\n userAgent: ua,\n vendor,\n platform,\n screen: screenInfo,\n hardwareConcurrency,\n deviceMemory: memory,\n maxTouchPoints,\n language: navigator.language,\n languages: navigator.languages?.join(\",\") || \"\",\n cookieEnabled: navigator.cookieEnabled,\n doNotTrack: navigator.doNotTrack || \"\",\n referrer: document.referrer,\n visibilityState: document.visibilityState\n };\n}\nasync function getBrowserID(clientInfo) {\n if (cachedBrowserId) {\n return cachedBrowserId;\n }\n const fingerprintString = JSON.stringify(clientInfo);\n if (typeof crypto !== \"undefined\" && crypto.subtle && crypto.subtle.digest) {\n try {\n await crypto.subtle.digest(\"SHA-256\", new Uint8Array([1, 2, 3]));\n let encodedData;\n if (typeof TextEncoder !== \"undefined\") {\n encodedData = new TextEncoder().encode(fingerprintString);\n } else {\n const utf8 = unescape(encodeURIComponent(fingerprintString));\n const buffer = new Uint8Array(utf8.length);\n for (let i = 0; i < utf8.length; i++) {\n buffer[i] = utf8.charCodeAt(i);\n }\n encodedData = buffer;\n }\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", encodedData);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray.map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n cachedBrowserId = hashHex;\n return hashHex;\n } catch (error) {\n console.warn(\n \"[StormcloudVideoPlayer] crypto.subtle.digest not supported, using fallback hash\"\n );\n }\n }\n let hash = 0;\n for (let i = 0; i < fingerprintString.length; i++) {\n const char = fingerprintString.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n const fallbackHash = Math.abs(hash).toString(16).padStart(8, \"0\");\n const timestamp = Date.now().toString(16).padStart(12, \"0\");\n const random = Math.random().toString(16).substring(2, 14).padStart(12, \"0\");\n cachedBrowserId = (fallbackHash + timestamp + random).padEnd(64, \"0\");\n return cachedBrowserId;\n}\nvar PLAYER_TRACKING_BASE_URL = \"https://adstorm.co/api-adstorm-dev/adstorm/player-tracking\";\nvar TRACK_URL = `${PLAYER_TRACKING_BASE_URL}/metrics/ingest`;\nvar HEARTBEAT_URL = `${PLAYER_TRACKING_BASE_URL}/heartbeat`;\nvar IMPRESSIONS_URL = `${PLAYER_TRACKING_BASE_URL}/impressions/ingest`;\nfunction buildHeaders(licenseKey) {\n const headers = {\n \"Content-Type\": \"application/json\"\n };\n if (licenseKey) {\n headers[\"Authorization\"] = `Bearer ${licenseKey}`;\n }\n return headers;\n}\nasync function sendTrackRequest(licenseKey, body) {\n const response = await fetch(TRACK_URL, {\n method: \"POST\",\n headers: buildHeaders(licenseKey),\n body: JSON.stringify(body)\n });\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n await response.json();\n}\nasync function postJson(url, licenseKey, body) {\n const response = await fetch(url, {\n method: \"POST\",\n headers: buildHeaders(licenseKey),\n body: JSON.stringify(body)\n });\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n await response.json();\n}\nasync function buildPlayerMetricEvent(licenseKey, context = {}, flags = {}) {\n const clientInfo = getClientInfo();\n const browserId = await getBrowserID(clientInfo);\n const captureAt = flags.captureAt ?? (/* @__PURE__ */ new Date()).toISOString();\n return {\n player_id: browserId,\n browserId,\n device_type: clientInfo.deviceType,\n deviceType: clientInfo.deviceType,\n input_stream_type: context.inputStreamType,\n os: clientInfo.os,\n ad_loaded: flags.adLoaded,\n ad_detect: flags.adDetect,\n license_key: licenseKey,\n capture_at: captureAt,\n timestamp: captureAt\n };\n}\nasync function sendInitialTracking(licenseKey, context = {}) {\n try {\n const clientInfo = getClientInfo();\n const browserId = await getBrowserID(clientInfo);\n const trackingData = {\n browserId,\n ...clientInfo\n };\n await sendTrackRequest(licenseKey, {\n events: [\n {\n player_id: browserId,\n device_type: clientInfo.deviceType,\n input_stream_type: context.inputStreamType,\n os: clientInfo.os,\n ad_loaded: false,\n ad_detect: false,\n license_key: licenseKey,\n capture_at: (/* @__PURE__ */ new Date()).toISOString()\n }\n ],\n trackingData\n });\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending initial tracking data:\",\n error\n );\n }\n}\nasync function sendAdDetectTracking(licenseKey, adDetectInfo, context = {}) {\n try {\n await sendHeartbeat(licenseKey, context, {\n adDetect: true,\n captureAt: adDetectInfo.timestamp\n });\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending ad detect tracking:\",\n error\n );\n }\n}\nasync function sendAdLoadedTracking(licenseKey, adLoadedInfo, context = {}) {\n try {\n await sendHeartbeat(licenseKey, context, {\n adLoaded: true,\n captureAt: adLoadedInfo.timestamp\n });\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending ad loaded tracking:\",\n error\n );\n }\n}\nasync function sendAdImpressionTracking(licenseKey, adImpressionInfo, context = {}) {\n try {\n const metricEvent = await buildPlayerMetricEvent(licenseKey, context, {\n captureAt: adImpressionInfo.timestamp\n });\n await Promise.all([\n postJson(HEARTBEAT_URL, licenseKey, metricEvent),\n postJson(IMPRESSIONS_URL, licenseKey, {\n events: [\n {\n player_id: metricEvent.player_id,\n ad_played_count: 1,\n ad_url: adImpressionInfo.adUrl,\n license_key: licenseKey,\n capture_at: adImpressionInfo.timestamp\n }\n ]\n })\n ]);\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending ad impression tracking:\",\n error\n );\n }\n}\nasync function sendHeartbeat(licenseKey, context = {}, flags = {}) {\n try {\n const heartbeatData = await buildPlayerMetricEvent(\n licenseKey,\n context,\n flags\n );\n await postJson(HEARTBEAT_URL, licenseKey, heartbeatData);\n } catch (error) {\n console.error(\"[StormcloudVideoPlayer] Error sending heartbeat:\", error);\n }\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n getBrowserID,\n getClientInfo,\n sendAdDetectTracking,\n sendAdImpressionTracking,\n sendAdLoadedTracking,\n sendHeartbeat,\n sendInitialTracking\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/utils/tracking.cjs","../../src/utils/mqttConfig.ts","../../src/utils/mqttClient.ts","../../src/utils/tracking.ts"],"names":["enabled","__create","Object","create","__defProp","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__getProtoOf","getPrototypeOf","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toESM","mod","isNodeMode","__esModule","value","__toCommonJS","tracking_exports","getBrowserID","getClientInfo","sendAdDetectTracking","sendAdImpressionTracking","sendAdLoadedTracking","sendHeartbeat","sendInitialTracking","module","exports","DEFAULT_MQTT_CONFIG","brokerAddress","brokerPort","wsPort","username","password","topicPrefix","qos","mqttConfig","isMQTTEnabled","buildMQTTBrokerUrl","brokerUrl","buildPlayerTopic","licenseKey","channel","import_mqtt","require","LOG","client","status","initMQTTClient","url","clientId","Math","random","toString","slice","mqtt","connect","keepalive","queueQoSZero","console","on","ensureMQTTClient","clean","connectTimeout","err","warn","info","message","publishMQTT","topic","payload","publish","JSON","stringify","cachedBrowserId","platform","navigator","height","screen","window","vendor","maxTouchPoints","memory","deviceMemory","hardwareConcurrency","screenInfo","width","availWidth","availHeight","orientation","type","deviceType","brand","isWebApp","model","isSmartTV","isAndroid","isWebView","ua","includes","os","webosMatch","match","tizenMatch","tvMatch","trim","test","androidModelMatch"],"mappings":"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","sourcesContent":["\"use strict\";\nvar __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n // If the importer is in node compatibility mode or this is not an ESM\n // file that has been converted to a CommonJS file using a Babel-\n // compatible transform (i.e. \"__esModule\" has not been set), then set\n // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n isNodeMode || !mod || !mod.__esModule ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target,\n mod\n));\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/utils/tracking.ts\nvar tracking_exports = {};\n__export(tracking_exports, {\n getBrowserID: () => getBrowserID,\n getClientInfo: () => getClientInfo,\n sendAdDetectTracking: () => sendAdDetectTracking,\n sendAdImpressionTracking: () => sendAdImpressionTracking,\n sendAdLoadedTracking: () => sendAdLoadedTracking,\n sendHeartbeat: () => sendHeartbeat,\n sendInitialTracking: () => sendInitialTracking\n});\nmodule.exports = __toCommonJS(tracking_exports);\n\n// src/utils/mqttConfig.ts\nvar DEFAULT_MQTT_CONFIG = {\n enabled: true,\n brokerAddress: \"vecbae77.ala.us-east-1.emqxsl.com\",\n brokerPort: 8883,\n wsPort: 8084,\n username: \"for-sonifi\",\n password: \"sonifi-mqtt\",\n topicPrefix: \"adstorm/players\",\n qos: 1\n};\nvar mqttConfig = { ...DEFAULT_MQTT_CONFIG };\nfunction isMQTTEnabled() {\n return mqttConfig.enabled;\n}\nfunction buildMQTTBrokerUrl() {\n if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;\n return `wss://${mqttConfig.brokerAddress}:${mqttConfig.wsPort}/mqtt`;\n}\nfunction buildPlayerTopic(licenseKey, channel) {\n return `${mqttConfig.topicPrefix}/${licenseKey}/${channel}`;\n}\n\n// src/utils/mqttClient.ts\nvar import_mqtt = __toESM(require(\"mqtt\"), 1);\nvar LOG = \"[StormcloudVideoPlayer][MQTT]\";\nvar client = null;\nvar status = \"disconnected\";\nfunction initMQTTClient() {\n if (client || !isMQTTEnabled()) return;\n const url = buildMQTTBrokerUrl();\n status = \"connecting\";\n const clientId = `stormcloud-vp-${Math.random().toString(36).slice(2, 9)}`;\n try {\n client = import_mqtt.default.connect(url, {\n clientId,\n username: mqttConfig.username,\n password: mqttConfig.password,\n keepalive: 60,\n clean: true,\n reconnectPeriod: 5e3,\n connectTimeout: 1e4,\n queueQoSZero: false\n });\n } catch (err) {\n status = \"error\";\n console.warn(`${LOG} connect() threw:`, err);\n return;\n }\n client.on(\"connect\", () => {\n status = \"connected\";\n console.info(`${LOG} connected to ${url}`);\n });\n client.on(\"reconnect\", () => {\n status = \"connecting\";\n console.info(`${LOG} reconnecting\\u2026`);\n });\n client.on(\"offline\", () => {\n status = \"disconnected\";\n console.warn(`${LOG} offline`);\n });\n client.on(\"error\", (err) => {\n status = \"error\";\n console.warn(`${LOG} error:`, err.message);\n });\n client.on(\"close\", () => {\n if (status === \"connected\") {\n status = \"disconnected\";\n }\n });\n}\nfunction ensureMQTTClient() {\n if (isMQTTEnabled() && !client) {\n initMQTTClient();\n }\n}\nfunction publishMQTT(topic, payload) {\n if (!isMQTTEnabled()) {\n return false;\n }\n ensureMQTTClient();\n if (!client) {\n return false;\n }\n try {\n client.publish(topic, JSON.stringify(payload), { qos: mqttConfig.qos });\n return true;\n } catch (err) {\n console.warn(`${LOG} publish failed on ${topic}:`, err);\n return false;\n }\n}\n\n// src/utils/tracking.ts\nvar cachedBrowserId = null;\nfunction getClientInfo() {\n const ua = navigator.userAgent;\n const platform = navigator.platform;\n const vendor = navigator.vendor || \"\";\n const maxTouchPoints = navigator.maxTouchPoints || 0;\n const memory = navigator.deviceMemory || null;\n const hardwareConcurrency = navigator.hardwareConcurrency || 1;\n const screenInfo = {\n width: screen?.width,\n height: screen?.height,\n availWidth: screen?.availWidth,\n availHeight: screen?.availHeight,\n orientation: screen?.orientation?.type || \"\",\n pixelDepth: screen?.pixelDepth\n };\n let deviceType = \"desktop\";\n let brand = \"Unknown\";\n let os = \"Unknown\";\n let model = \"\";\n let isSmartTV = false;\n let isAndroid = false;\n let isWebView = false;\n let isWebApp = false;\n if (ua.includes(\"Web0S\")) {\n brand = \"LG\";\n os = \"webOS\";\n isSmartTV = true;\n deviceType = \"tv\";\n const webosMatch = ua.match(/Web0S\\/([^\\s]+)/);\n model = webosMatch ? `webOS ${webosMatch[1]}` : \"webOS TV\";\n } else if (ua.includes(\"Tizen\")) {\n brand = \"Samsung\";\n os = \"Tizen\";\n isSmartTV = true;\n deviceType = \"tv\";\n const tizenMatch = ua.match(/Tizen\\/([^\\s]+)/);\n const tvMatch = ua.match(/(?:Smart-TV|SMART-TV|TV)/i) ? \"Smart TV\" : \"\";\n model = tizenMatch ? `Tizen ${tizenMatch[1]} ${tvMatch}`.trim() : \"Tizen TV\";\n } else if (ua.includes(\"Philips\")) {\n brand = \"Philips\";\n os = \"Saphi\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"Sharp\") || ua.includes(\"AQUOS\")) {\n brand = \"Sharp\";\n os = \"Android TV\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"Android\") && (ua.includes(\"Sony\") || vendor.includes(\"Sony\"))) {\n brand = \"Sony\";\n os = \"Android TV\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"Android\") && (ua.includes(\"NetCast\") || ua.includes(\"LG\"))) {\n brand = \"LG\";\n os = \"Android TV\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\" Roku\") || ua.includes(\"Roku/\")) {\n brand = \"Roku\";\n os = \"Roku OS\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"AppleTV\")) {\n brand = \"Apple\";\n os = \"tvOS\";\n isSmartTV = true;\n deviceType = \"tv\";\n }\n if (ua.includes(\"Android\")) {\n isAndroid = true;\n os = \"Android\";\n deviceType = /Mobile/.test(ua) ? \"mobile\" : \"tablet\";\n if (ua.includes(\"Android\") && (maxTouchPoints === 0 || ua.includes(\"Google TV\") || ua.includes(\"XiaoMi\"))) {\n deviceType = \"tv\";\n isSmartTV = true;\n brand = brand === \"Unknown\" ? \"Android TV\" : brand;\n }\n const androidModelMatch = ua.match(/\\(([^)]*Android[^)]*)\\)/);\n if (androidModelMatch && androidModelMatch[1]) {\n model = androidModelMatch[1];\n }\n }\n if (/iPad|iPhone|iPod/.test(ua)) {\n os = \"iOS\";\n deviceType = \"mobile\";\n brand = \"Apple\";\n if (navigator.maxTouchPoints > 1 && /iPad/.test(ua)) {\n deviceType = \"tablet\";\n }\n }\n if (!isAndroid && !isSmartTV && !/Mobile/.test(ua)) {\n if (ua.includes(\"Windows\")) {\n os = \"Windows\";\n deviceType = \"desktop\";\n } else if (ua.includes(\"Mac\") && !/iPhone/.test(ua)) {\n os = \"macOS\";\n deviceType = \"desktop\";\n if (maxTouchPoints > 1) deviceType = \"tablet\";\n } else if (ua.includes(\"Linux\")) {\n os = \"Linux\";\n deviceType = \"desktop\";\n }\n }\n if (brand === \"Unknown\") {\n if (vendor.includes(\"Google\") || ua.includes(\"Chrome\")) brand = \"Google\";\n if (vendor.includes(\"Apple\")) brand = \"Apple\";\n if (vendor.includes(\"Samsung\") || ua.includes(\"SM-\")) brand = \"Samsung\";\n }\n isWebView = /wv|WebView|Linux; U;/.test(ua);\n if (window?.outerHeight === 0 && window?.outerWidth === 0) {\n isWebView = true;\n }\n isWebApp = window.matchMedia(\"(display-mode: standalone)\").matches || window.navigator.standalone === true || window.screen?.orientation?.angle !== void 0;\n return {\n brand,\n os,\n model: model || ua.substring(0, 50) + \"...\",\n deviceType,\n isSmartTV,\n isAndroid,\n isWebView,\n isWebApp,\n domain: window.location.hostname,\n origin: window.location.origin,\n path: window.location.pathname,\n userAgent: ua,\n vendor,\n platform,\n screen: screenInfo,\n hardwareConcurrency,\n deviceMemory: memory,\n maxTouchPoints,\n language: navigator.language,\n languages: navigator.languages?.join(\",\") || \"\",\n cookieEnabled: navigator.cookieEnabled,\n doNotTrack: navigator.doNotTrack || \"\",\n referrer: document.referrer,\n visibilityState: document.visibilityState\n };\n}\nasync function getBrowserID(clientInfo) {\n if (cachedBrowserId) {\n return cachedBrowserId;\n }\n const fingerprintString = JSON.stringify(clientInfo);\n if (typeof crypto !== \"undefined\" && crypto.subtle?.digest) {\n try {\n await crypto.subtle.digest(\"SHA-256\", new Uint8Array([1, 2, 3]));\n let encodedData;\n if (typeof TextEncoder !== \"undefined\") {\n encodedData = new TextEncoder().encode(fingerprintString);\n } else {\n const utf8 = unescape(encodeURIComponent(fingerprintString));\n const buffer = new Uint8Array(utf8.length);\n for (let i = 0; i < utf8.length; i++) {\n buffer[i] = utf8.charCodeAt(i);\n }\n encodedData = buffer;\n }\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", encodedData);\n const hashHex = Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n cachedBrowserId = hashHex;\n return hashHex;\n } catch {\n console.warn(\n \"[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash\"\n );\n }\n }\n let hash = 0;\n for (let i = 0; i < fingerprintString.length; i++) {\n const char = fingerprintString.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n const fallbackHash = Math.abs(hash).toString(16).padStart(8, \"0\");\n const timestamp = Date.now().toString(16).padStart(12, \"0\");\n const random = Math.random().toString(16).substring(2, 14).padStart(12, \"0\");\n cachedBrowserId = (fallbackHash + timestamp + random).padEnd(64, \"0\");\n return cachedBrowserId;\n}\nfunction canPublish(licenseKey) {\n return Boolean(isMQTTEnabled() && licenseKey);\n}\nasync function buildPlayerMetricEvent(context = {}, flags = {}) {\n const clientInfo = getClientInfo();\n const playerId = await getBrowserID(clientInfo);\n const captureAt = flags.captureAt ?? (/* @__PURE__ */ new Date()).toISOString();\n return {\n player_id: playerId,\n device_type: clientInfo.deviceType,\n os: clientInfo.os.toLowerCase(),\n ad_loaded: flags.adLoaded ?? false,\n ad_detect: flags.adDetect ?? false,\n capture_at: captureAt,\n ...context.inputStreamType ? { input_stream_type: context.inputStreamType } : {}\n };\n}\nfunction publishTracking(licenseKey, channel, body) {\n ensureMQTTClient();\n publishMQTT(buildPlayerTopic(licenseKey, channel), body);\n}\nasync function sendInitialTracking(licenseKey, context = {}) {\n if (!canPublish(licenseKey)) return;\n try {\n const metricEvent = await buildPlayerMetricEvent(context, {\n adLoaded: false,\n adDetect: false\n });\n publishTracking(licenseKey, \"metrics\", {\n events: [metricEvent]\n });\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending initial tracking data:\",\n error\n );\n }\n}\nasync function sendAdDetectTracking(licenseKey, adDetectInfo, context = {}) {\n try {\n await sendHeartbeat(licenseKey, context, {\n adDetect: true,\n captureAt: adDetectInfo.timestamp\n });\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending ad detect tracking:\",\n error\n );\n }\n}\nasync function sendAdLoadedTracking(licenseKey, adLoadedInfo, context = {}) {\n try {\n await sendHeartbeat(licenseKey, context, {\n adLoaded: true,\n captureAt: adLoadedInfo.timestamp\n });\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending ad loaded tracking:\",\n error\n );\n }\n}\nasync function sendAdImpressionTracking(licenseKey, adImpressionInfo, context = {}) {\n if (!canPublish(licenseKey)) return;\n try {\n const metricEvent = await buildPlayerMetricEvent(context, {\n captureAt: adImpressionInfo.timestamp\n });\n publishTracking(licenseKey, \"heartbeat\", metricEvent);\n publishTracking(licenseKey, \"impressions\", {\n events: [\n {\n player_id: metricEvent.player_id,\n ad_played_count: 1,\n ad_url: adImpressionInfo.adUrl,\n capture_at: adImpressionInfo.timestamp\n }\n ]\n });\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending ad impression tracking:\",\n error\n );\n }\n}\nasync function sendHeartbeat(licenseKey, context = {}, flags = {}) {\n if (!canPublish(licenseKey)) return;\n try {\n const heartbeatData = await buildPlayerMetricEvent(context, flags);\n publishTracking(licenseKey, \"heartbeat\", heartbeatData);\n } catch (error) {\n console.error(\"[StormcloudVideoPlayer] Error sending heartbeat:\", error);\n }\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n getBrowserID,\n getClientInfo,\n sendAdDetectTracking,\n sendAdImpressionTracking,\n sendAdLoadedTracking,\n sendHeartbeat,\n sendInitialTracking\n});\n","export const MQTT_CA_CERT_FILE = \"src/certs/emqxsl-ca.crt\";\n\nexport type MQTTConfig = {\n enabled: boolean;\n brokerAddress: string;\n brokerPort: number;\n wsPort: number;\n username: string;\n password: string;\n topicPrefix: string;\n qos: 0 | 1 | 2;\n brokerUrl?: string;\n};\n\nexport const DEFAULT_MQTT_CONFIG: MQTTConfig = {\n enabled: true,\n brokerAddress: \"vecbae77.ala.us-east-1.emqxsl.com\",\n brokerPort: 8883,\n wsPort: 8084,\n username: \"for-sonifi\",\n password: \"sonifi-mqtt\",\n topicPrefix: \"adstorm/players\",\n qos: 1,\n};\n\nexport const mqttConfig: MQTTConfig = { ...DEFAULT_MQTT_CONFIG };\n\nexport function applyMQTTConfig(overrides: Partial<MQTTConfig>): void {\n Object.assign(mqttConfig, overrides);\n}\n\nexport function isMQTTEnabled(): boolean {\n return mqttConfig.enabled;\n}\n\nexport function buildMQTTBrokerUrl(): string {\n if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;\n return `wss://${mqttConfig.brokerAddress}:${mqttConfig.wsPort}/mqtt`;\n}\n\nexport function buildPlayerTopic(\n licenseKey: string,\n channel: \"metrics\" | \"impressions\" | \"heartbeat\"\n): string {\n return `${mqttConfig.topicPrefix}/${licenseKey}/${channel}`;\n}\n","import mqtt from \"mqtt\";\nimport type { MqttClient } from \"mqtt\";\nimport {\n applyMQTTConfig,\n buildMQTTBrokerUrl,\n isMQTTEnabled,\n mqttConfig,\n} from \"./mqttConfig\";\nimport type { MQTTConfig } from \"./mqttConfig\";\n\nconst LOG = \"[StormcloudVideoPlayer][MQTT]\";\n\nexport type MQTTStatus = \"disconnected\" | \"connecting\" | \"connected\" | \"error\";\n\nlet client: MqttClient | null = null;\nlet status: MQTTStatus = \"disconnected\";\n\nexport function getMQTTStatus(): MQTTStatus {\n return status;\n}\n\nexport function isMQTTConnected(): boolean {\n return status === \"connected\" && client !== null && client.connected;\n}\n\nexport function isMQTTConfigured(): boolean {\n return isMQTTEnabled();\n}\n\nexport function configureMQTT(overrides: Partial<MQTTConfig>): void {\n applyMQTTConfig(overrides);\n disconnectMQTT();\n}\n\nexport function initMQTTClient(): void {\n if (client || !isMQTTEnabled()) return;\n\n const url = buildMQTTBrokerUrl();\n status = \"connecting\";\n\n const clientId = `stormcloud-vp-${Math.random().toString(36).slice(2, 9)}`;\n\n try {\n client = mqtt.connect(url, {\n clientId,\n username: mqttConfig.username,\n password: mqttConfig.password,\n keepalive: 60,\n clean: true,\n reconnectPeriod: 5000,\n connectTimeout: 10_000,\n queueQoSZero: false,\n });\n } catch (err) {\n status = \"error\";\n console.warn(`${LOG} connect() threw:`, err);\n return;\n }\n\n client.on(\"connect\", () => {\n status = \"connected\";\n console.info(`${LOG} connected to ${url}`);\n });\n\n client.on(\"reconnect\", () => {\n status = \"connecting\";\n console.info(`${LOG} reconnecting…`);\n });\n\n client.on(\"offline\", () => {\n status = \"disconnected\";\n console.warn(`${LOG} offline`);\n });\n\n client.on(\"error\", (err) => {\n status = \"error\";\n console.warn(`${LOG} error:`, err.message);\n });\n\n client.on(\"close\", () => {\n if (status === \"connected\") {\n status = \"disconnected\";\n }\n });\n}\n\nexport function ensureMQTTClient(): void {\n if (isMQTTEnabled() && !client) {\n initMQTTClient();\n }\n}\n\nexport function publishMQTT(\n topic: string,\n payload: Record<string, unknown>\n): boolean {\n if (!isMQTTEnabled()) {\n return false;\n }\n\n ensureMQTTClient();\n\n if (!client) {\n return false;\n }\n\n try {\n client.publish(topic, JSON.stringify(payload), { qos: mqttConfig.qos });\n return true;\n } catch (err) {\n console.warn(`${LOG} publish failed on ${topic}:`, err);\n return false;\n }\n}\n\nexport function disconnectMQTT(): void {\n if (client) {\n client.end(true);\n client = null;\n status = \"disconnected\";\n }\n}\n","import type {\n ClientInfo,\n AdDetectInfo,\n AdLoadedInfo,\n AdImpressionInfo,\n PlayerAnalyticsContext,\n} from \"../types\";\nimport { buildPlayerTopic, isMQTTEnabled } from \"./mqttConfig\";\nimport { ensureMQTTClient, publishMQTT } from \"./mqttClient\";\n\nlet cachedBrowserId: string | null = null;\n\nexport function getClientInfo(): ClientInfo {\n const ua = navigator.userAgent;\n const platform = navigator.platform;\n const vendor = navigator.vendor || \"\";\n const maxTouchPoints = navigator.maxTouchPoints || 0;\n const memory = (navigator as any).deviceMemory || null;\n const hardwareConcurrency = navigator.hardwareConcurrency || 1;\n\n const screenInfo = {\n width: screen?.width,\n height: screen?.height,\n availWidth: screen?.availWidth,\n availHeight: screen?.availHeight,\n orientation: (screen?.orientation as any)?.type || \"\",\n pixelDepth: screen?.pixelDepth,\n };\n\n let deviceType: \"tv\" | \"mobile\" | \"tablet\" | \"desktop\" = \"desktop\";\n let brand = \"Unknown\";\n let os = \"Unknown\";\n let model = \"\";\n let isSmartTV = false;\n let isAndroid = false;\n let isWebView = false;\n let isWebApp = false;\n\n if (ua.includes(\"Web0S\")) {\n brand = \"LG\";\n os = \"webOS\";\n isSmartTV = true;\n deviceType = \"tv\";\n const webosMatch = ua.match(/Web0S\\/([^\\s]+)/);\n model = webosMatch ? `webOS ${webosMatch[1]}` : \"webOS TV\";\n } else if (ua.includes(\"Tizen\")) {\n brand = \"Samsung\";\n os = \"Tizen\";\n isSmartTV = true;\n deviceType = \"tv\";\n const tizenMatch = ua.match(/Tizen\\/([^\\s]+)/);\n const tvMatch = ua.match(/(?:Smart-TV|SMART-TV|TV)/i) ? \"Smart TV\" : \"\";\n model = tizenMatch\n ? `Tizen ${tizenMatch[1]} ${tvMatch}`.trim()\n : \"Tizen TV\";\n } else if (ua.includes(\"Philips\")) {\n brand = \"Philips\";\n os = \"Saphi\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"Sharp\") || ua.includes(\"AQUOS\")) {\n brand = \"Sharp\";\n os = \"Android TV\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (\n ua.includes(\"Android\") &&\n (ua.includes(\"Sony\") || vendor.includes(\"Sony\"))\n ) {\n brand = \"Sony\";\n os = \"Android TV\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (\n ua.includes(\"Android\") &&\n (ua.includes(\"NetCast\") || ua.includes(\"LG\"))\n ) {\n brand = \"LG\";\n os = \"Android TV\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\" Roku\") || ua.includes(\"Roku/\")) {\n brand = \"Roku\";\n os = \"Roku OS\";\n isSmartTV = true;\n deviceType = \"tv\";\n } else if (ua.includes(\"AppleTV\")) {\n brand = \"Apple\";\n os = \"tvOS\";\n isSmartTV = true;\n deviceType = \"tv\";\n }\n\n if (ua.includes(\"Android\")) {\n isAndroid = true;\n os = \"Android\";\n deviceType = /Mobile/.test(ua) ? \"mobile\" : \"tablet\";\n\n if (\n ua.includes(\"Android\") &&\n (maxTouchPoints === 0 ||\n ua.includes(\"Google TV\") ||\n ua.includes(\"XiaoMi\"))\n ) {\n deviceType = \"tv\";\n isSmartTV = true;\n brand = brand === \"Unknown\" ? \"Android TV\" : brand;\n }\n\n const androidModelMatch = ua.match(/\\(([^)]*Android[^)]*)\\)/);\n if (androidModelMatch && androidModelMatch[1]) {\n model = androidModelMatch[1];\n }\n }\n\n if (/iPad|iPhone|iPod/.test(ua)) {\n os = \"iOS\";\n deviceType = \"mobile\";\n brand = \"Apple\";\n if (navigator.maxTouchPoints > 1 && /iPad/.test(ua)) {\n deviceType = \"tablet\";\n }\n }\n\n if (!isAndroid && !isSmartTV && !/Mobile/.test(ua)) {\n if (ua.includes(\"Windows\")) {\n os = \"Windows\";\n deviceType = \"desktop\";\n } else if (ua.includes(\"Mac\") && !/iPhone/.test(ua)) {\n os = \"macOS\";\n deviceType = \"desktop\";\n if (maxTouchPoints > 1) deviceType = \"tablet\";\n } else if (ua.includes(\"Linux\")) {\n os = \"Linux\";\n deviceType = \"desktop\";\n }\n }\n\n if (brand === \"Unknown\") {\n if (vendor.includes(\"Google\") || ua.includes(\"Chrome\")) brand = \"Google\";\n if (vendor.includes(\"Apple\")) brand = \"Apple\";\n if (vendor.includes(\"Samsung\") || ua.includes(\"SM-\")) brand = \"Samsung\";\n }\n\n isWebView = /wv|WebView|Linux; U;/.test(ua);\n\n if (window?.outerHeight === 0 && window?.outerWidth === 0) {\n isWebView = true;\n }\n\n isWebApp =\n window.matchMedia(\"(display-mode: standalone)\").matches ||\n (window.navigator as any).standalone === true ||\n window.screen?.orientation?.angle !== undefined;\n\n return {\n brand,\n os,\n model: model || ua.substring(0, 50) + \"...\",\n deviceType,\n isSmartTV,\n isAndroid,\n isWebView,\n isWebApp,\n domain: window.location.hostname,\n origin: window.location.origin,\n path: window.location.pathname,\n userAgent: ua,\n vendor,\n platform,\n screen: screenInfo,\n hardwareConcurrency,\n deviceMemory: memory,\n maxTouchPoints,\n language: navigator.language,\n languages: navigator.languages?.join(\",\") || \"\",\n cookieEnabled: navigator.cookieEnabled,\n doNotTrack: navigator.doNotTrack || \"\",\n referrer: document.referrer,\n visibilityState: document.visibilityState,\n };\n}\n\nexport async function getBrowserID(clientInfo: ClientInfo): Promise<string> {\n if (cachedBrowserId) {\n return cachedBrowserId;\n }\n\n const fingerprintString = JSON.stringify(clientInfo);\n\n if (typeof crypto !== \"undefined\" && crypto.subtle?.digest) {\n try {\n await crypto.subtle.digest(\"SHA-256\", new Uint8Array([1, 2, 3]));\n\n let encodedData: BufferSource;\n if (typeof TextEncoder !== \"undefined\") {\n encodedData = new TextEncoder().encode(fingerprintString);\n } else {\n const utf8 = unescape(encodeURIComponent(fingerprintString));\n const buffer = new Uint8Array(utf8.length);\n for (let i = 0; i < utf8.length; i++) {\n buffer[i] = utf8.charCodeAt(i);\n }\n encodedData = buffer;\n }\n\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", encodedData);\n const hashHex = Array.from(new Uint8Array(hashBuffer))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n cachedBrowserId = hashHex;\n return hashHex;\n } catch {\n console.warn(\n \"[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash\"\n );\n }\n }\n\n let hash = 0;\n for (let i = 0; i < fingerprintString.length; i++) {\n const char = fingerprintString.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n\n const fallbackHash = Math.abs(hash).toString(16).padStart(8, \"0\");\n const timestamp = Date.now().toString(16).padStart(12, \"0\");\n const random = Math.random().toString(16).substring(2, 14).padStart(12, \"0\");\n\n cachedBrowserId = (fallbackHash + timestamp + random).padEnd(64, \"0\");\n return cachedBrowserId;\n}\n\ntype PlayerMetricFlags = {\n adLoaded?: boolean;\n adDetect?: boolean;\n captureAt?: string;\n};\n\nfunction canPublish(licenseKey: string | undefined): licenseKey is string {\n return Boolean(isMQTTEnabled() && licenseKey);\n}\n\nasync function buildPlayerMetricEvent(\n context: PlayerAnalyticsContext = {},\n flags: PlayerMetricFlags = {}\n): Promise<Record<string, unknown>> {\n const clientInfo = getClientInfo();\n const playerId = await getBrowserID(clientInfo);\n const captureAt = flags.captureAt ?? new Date().toISOString();\n\n return {\n player_id: playerId,\n device_type: clientInfo.deviceType,\n os: clientInfo.os.toLowerCase(),\n ad_loaded: flags.adLoaded ?? false,\n ad_detect: flags.adDetect ?? false,\n capture_at: captureAt,\n ...(context.inputStreamType\n ? { input_stream_type: context.inputStreamType }\n : {}),\n };\n}\n\nfunction publishTracking(\n licenseKey: string,\n channel: \"metrics\" | \"impressions\" | \"heartbeat\",\n body: Record<string, unknown>\n): void {\n ensureMQTTClient();\n publishMQTT(buildPlayerTopic(licenseKey, channel), body);\n}\n\nexport async function sendInitialTracking(\n licenseKey?: string,\n context: PlayerAnalyticsContext = {}\n): Promise<void> {\n if (!canPublish(licenseKey)) return;\n\n try {\n const metricEvent = await buildPlayerMetricEvent(context, {\n adLoaded: false,\n adDetect: false,\n });\n\n publishTracking(licenseKey, \"metrics\", {\n events: [metricEvent],\n });\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending initial tracking data:\",\n error\n );\n }\n}\n\nexport async function sendAdDetectTracking(\n licenseKey: string | undefined,\n adDetectInfo: AdDetectInfo,\n context: PlayerAnalyticsContext = {}\n): Promise<void> {\n try {\n await sendHeartbeat(licenseKey, context, {\n adDetect: true,\n captureAt: adDetectInfo.timestamp,\n });\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending ad detect tracking:\",\n error\n );\n }\n}\n\nexport async function sendAdLoadedTracking(\n licenseKey: string | undefined,\n adLoadedInfo: AdLoadedInfo,\n context: PlayerAnalyticsContext = {}\n): Promise<void> {\n try {\n await sendHeartbeat(licenseKey, context, {\n adLoaded: true,\n captureAt: adLoadedInfo.timestamp,\n });\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending ad loaded tracking:\",\n error\n );\n }\n}\n\nexport async function sendAdImpressionTracking(\n licenseKey: string | undefined,\n adImpressionInfo: AdImpressionInfo,\n context: PlayerAnalyticsContext = {}\n): Promise<void> {\n if (!canPublish(licenseKey)) return;\n\n try {\n const metricEvent = await buildPlayerMetricEvent(context, {\n captureAt: adImpressionInfo.timestamp,\n });\n\n publishTracking(licenseKey, \"heartbeat\", metricEvent);\n publishTracking(licenseKey, \"impressions\", {\n events: [\n {\n player_id: metricEvent.player_id,\n ad_played_count: 1,\n ad_url: adImpressionInfo.adUrl,\n capture_at: adImpressionInfo.timestamp,\n },\n ],\n });\n } catch (error) {\n console.error(\n \"[StormcloudVideoPlayer] Error sending ad impression tracking:\",\n error\n );\n }\n}\n\nexport async function sendHeartbeat(\n licenseKey?: string,\n context: PlayerAnalyticsContext = {},\n flags: PlayerMetricFlags = {}\n): Promise<void> {\n if (!canPublish(licenseKey)) return;\n\n try {\n const heartbeatData = await buildPlayerMetricEvent(context, flags);\n publishTracking(licenseKey, \"heartbeat\", heartbeatData);\n } catch (error) {\n console.error(\"[StormcloudVideoPlayer] Error sending heartbeat:\", error);\n }\n}\n"]}
|
package/lib/utils/tracking.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as ClientInfo, a as AdDetectInfo, P as PlayerAnalyticsContext, b as AdImpressionInfo, c as AdLoadedInfo } from '../types-
|
|
1
|
+
import { C as ClientInfo, a as AdDetectInfo, P as PlayerAnalyticsContext, b as AdImpressionInfo, c as AdLoadedInfo } from '../types-BmF_60m2.cjs';
|
|
2
2
|
|
|
3
3
|
declare function getClientInfo(): ClientInfo;
|
|
4
4
|
declare function getBrowserID(clientInfo: ClientInfo): Promise<string>;
|