stormcloud-video-player 0.6.12 → 0.6.13
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/README.md +33 -294
- package/dist/stormcloud-vp.min.js +1 -1
- package/lib/index.cjs +144 -168
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +22 -3
- package/lib/index.d.ts +22 -3
- package/lib/index.js +113 -169
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +102 -166
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +1 -1
- package/lib/players/HlsPlayer.cjs +102 -166
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.d.cts +1 -1
- package/lib/players/index.cjs +102 -166
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/vastAdLayer.d.cts +1 -1
- package/lib/sdk/vastManager.d.cts +1 -1
- package/lib/{types-FjAlGhAL.d.cts → types-DSKC4ySr.d.cts} +0 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +104 -170
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
- package/lib/utils/mqttClient.cjs +79 -8
- package/lib/utils/mqttClient.cjs.map +1 -1
- package/lib/utils/mqttClient.d.cts +6 -2
- package/lib/utils/mqttConfig.cjs +136 -0
- package/lib/utils/mqttConfig.cjs.map +1 -0
- package/lib/utils/mqttConfig.d.cts +19 -0
- package/lib/utils/tracking.cjs +138 -159
- package/lib/utils/tracking.cjs.map +1 -1
- package/lib/utils/tracking.d.cts +2 -3
- package/package.json +2 -3
package/lib/utils/tracking.cjs
CHANGED
|
@@ -240,26 +240,95 @@ __export(tracking_exports, {
|
|
|
240
240
|
},
|
|
241
241
|
sendInitialTracking: function sendInitialTracking1() {
|
|
242
242
|
return sendInitialTracking;
|
|
243
|
-
},
|
|
244
|
-
setMQTTTopicPrefix: function setMQTTTopicPrefix1() {
|
|
245
|
-
return setMQTTTopicPrefix;
|
|
246
243
|
}
|
|
247
244
|
});
|
|
248
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
|
+
}
|
|
249
268
|
// src/utils/mqttClient.ts
|
|
250
269
|
var import_mqtt = __toESM(require("mqtt"), 1);
|
|
251
270
|
var LOG = "[StormcloudVideoPlayer][MQTT]";
|
|
252
271
|
var client = null;
|
|
253
|
-
|
|
254
|
-
|
|
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
|
+
}
|
|
255
320
|
}
|
|
256
321
|
function publishMQTT(topic, payload) {
|
|
322
|
+
if (!isMQTTEnabled()) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
ensureMQTTClient();
|
|
257
326
|
if (!client) {
|
|
258
327
|
return false;
|
|
259
328
|
}
|
|
260
329
|
try {
|
|
261
330
|
client.publish(topic, JSON.stringify(payload), {
|
|
262
|
-
qos:
|
|
331
|
+
qos: mqttConfig.qos
|
|
263
332
|
});
|
|
264
333
|
return true;
|
|
265
334
|
} catch (err) {
|
|
@@ -486,171 +555,91 @@ function getBrowserID(clientInfo) {
|
|
|
486
555
|
});
|
|
487
556
|
})();
|
|
488
557
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
mqttTopicPrefix = prefix || "adstorm";
|
|
558
|
+
function canPublish(licenseKey) {
|
|
559
|
+
return Boolean(isMQTTEnabled() && licenseKey);
|
|
492
560
|
}
|
|
493
|
-
|
|
494
|
-
var TRACK_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/metrics/ingest");
|
|
495
|
-
var HEARTBEAT_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/heartbeat");
|
|
496
|
-
var IMPRESSIONS_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/impressions/ingest");
|
|
497
|
-
function buildHeaders(licenseKey) {
|
|
498
|
-
var headers = {
|
|
499
|
-
"Content-Type": "application/json"
|
|
500
|
-
};
|
|
501
|
-
if (licenseKey) headers["Authorization"] = "Bearer ".concat(licenseKey);
|
|
502
|
-
return headers;
|
|
503
|
-
}
|
|
504
|
-
function postJson(url, licenseKey, body) {
|
|
561
|
+
function buildPlayerMetricEvent() {
|
|
505
562
|
return _async_to_generator(function() {
|
|
506
|
-
var
|
|
507
|
-
return _ts_generator(this, function(_state) {
|
|
508
|
-
switch(_state.label){
|
|
509
|
-
case 0:
|
|
510
|
-
return [
|
|
511
|
-
4,
|
|
512
|
-
fetch(url, {
|
|
513
|
-
method: "POST",
|
|
514
|
-
headers: buildHeaders(licenseKey),
|
|
515
|
-
body: JSON.stringify(body)
|
|
516
|
-
})
|
|
517
|
-
];
|
|
518
|
-
case 1:
|
|
519
|
-
response = _state.sent();
|
|
520
|
-
if (!response.ok) throw new Error("HTTP error! status: ".concat(response.status));
|
|
521
|
-
return [
|
|
522
|
-
4,
|
|
523
|
-
response.json()
|
|
524
|
-
];
|
|
525
|
-
case 2:
|
|
526
|
-
_state.sent();
|
|
527
|
-
return [
|
|
528
|
-
2
|
|
529
|
-
];
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
})();
|
|
533
|
-
}
|
|
534
|
-
function buildPlayerMetricEvent(_0) {
|
|
535
|
-
return _async_to_generator(function(licenseKey) {
|
|
536
|
-
var context, flags, _flags_captureAt, clientInfo, browserId, captureAt;
|
|
563
|
+
var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
|
|
537
564
|
var _arguments = arguments;
|
|
538
565
|
return _ts_generator(this, function(_state) {
|
|
539
566
|
switch(_state.label){
|
|
540
567
|
case 0:
|
|
541
|
-
context = _arguments.length >
|
|
568
|
+
context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
|
|
542
569
|
clientInfo = getClientInfo();
|
|
543
570
|
return [
|
|
544
571
|
4,
|
|
545
572
|
getBrowserID(clientInfo)
|
|
546
573
|
];
|
|
547
574
|
case 1:
|
|
548
|
-
|
|
575
|
+
playerId = _state.sent();
|
|
549
576
|
captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
|
|
550
577
|
return [
|
|
551
578
|
2,
|
|
552
|
-
{
|
|
553
|
-
player_id:
|
|
554
|
-
browserId: browserId,
|
|
579
|
+
_object_spread({
|
|
580
|
+
player_id: playerId,
|
|
555
581
|
device_type: clientInfo.deviceType,
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
timestamp: captureAt
|
|
564
|
-
}
|
|
582
|
+
os: clientInfo.os.toLowerCase(),
|
|
583
|
+
ad_loaded: (_flags_adLoaded = flags.adLoaded) !== null && _flags_adLoaded !== void 0 ? _flags_adLoaded : false,
|
|
584
|
+
ad_detect: (_flags_adDetect = flags.adDetect) !== null && _flags_adDetect !== void 0 ? _flags_adDetect : false,
|
|
585
|
+
capture_at: captureAt
|
|
586
|
+
}, context.inputStreamType ? {
|
|
587
|
+
input_stream_type: context.inputStreamType
|
|
588
|
+
} : {})
|
|
565
589
|
];
|
|
566
590
|
}
|
|
567
591
|
});
|
|
568
592
|
}).apply(this, arguments);
|
|
569
593
|
}
|
|
570
|
-
function
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
switch(_state.label){
|
|
574
|
-
case 0:
|
|
575
|
-
if (isMQTTConfigured()) {
|
|
576
|
-
publishMQTT(mqttTopic, body);
|
|
577
|
-
return [
|
|
578
|
-
2
|
|
579
|
-
];
|
|
580
|
-
}
|
|
581
|
-
return [
|
|
582
|
-
4,
|
|
583
|
-
postJson(httpUrl, licenseKey, body)
|
|
584
|
-
];
|
|
585
|
-
case 1:
|
|
586
|
-
_state.sent();
|
|
587
|
-
return [
|
|
588
|
-
2
|
|
589
|
-
];
|
|
590
|
-
}
|
|
591
|
-
});
|
|
592
|
-
})();
|
|
594
|
+
function publishTracking(licenseKey, channel, body) {
|
|
595
|
+
ensureMQTTClient();
|
|
596
|
+
publishMQTT(buildPlayerTopic(licenseKey, channel), body);
|
|
593
597
|
}
|
|
594
598
|
function sendInitialTracking(_0) {
|
|
595
599
|
return _async_to_generator(function(licenseKey) {
|
|
596
|
-
var context,
|
|
600
|
+
var context, metricEvent, error;
|
|
597
601
|
var _arguments = arguments;
|
|
598
602
|
return _ts_generator(this, function(_state) {
|
|
599
603
|
switch(_state.label){
|
|
600
604
|
case 0:
|
|
601
605
|
context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
|
|
606
|
+
if (!canPublish(licenseKey)) return [
|
|
607
|
+
2
|
|
608
|
+
];
|
|
602
609
|
_state.label = 1;
|
|
603
610
|
case 1:
|
|
604
611
|
_state.trys.push([
|
|
605
612
|
1,
|
|
606
|
-
|
|
613
|
+
3,
|
|
607
614
|
,
|
|
608
|
-
|
|
615
|
+
4
|
|
609
616
|
]);
|
|
610
|
-
clientInfo = getClientInfo();
|
|
611
617
|
return [
|
|
612
618
|
4,
|
|
613
|
-
|
|
619
|
+
buildPlayerMetricEvent(context, {
|
|
620
|
+
adLoaded: false,
|
|
621
|
+
adDetect: false
|
|
622
|
+
})
|
|
614
623
|
];
|
|
615
624
|
case 2:
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
trackingData = _object_spread({
|
|
619
|
-
browserId: browserId
|
|
620
|
-
}, clientInfo);
|
|
621
|
-
metricsBody = {
|
|
625
|
+
metricEvent = _state.sent();
|
|
626
|
+
publishTracking(licenseKey, "metrics", {
|
|
622
627
|
events: [
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
input_stream_type: context.inputStreamType,
|
|
627
|
-
os: clientInfo.os,
|
|
628
|
-
ad_loaded: false,
|
|
629
|
-
ad_detect: false,
|
|
630
|
-
license_key: licenseKey,
|
|
631
|
-
capture_at: captureAt
|
|
632
|
-
}
|
|
633
|
-
],
|
|
634
|
-
trackingData: trackingData
|
|
635
|
-
};
|
|
636
|
-
return [
|
|
637
|
-
4,
|
|
638
|
-
publishOrPost("".concat(mqttTopicPrefix, "/tracking/metrics"), TRACK_URL, licenseKey, metricsBody)
|
|
639
|
-
];
|
|
640
|
-
case 3:
|
|
641
|
-
_state.sent();
|
|
628
|
+
metricEvent
|
|
629
|
+
]
|
|
630
|
+
});
|
|
642
631
|
return [
|
|
643
632
|
3,
|
|
644
|
-
|
|
633
|
+
4
|
|
645
634
|
];
|
|
646
|
-
case
|
|
635
|
+
case 3:
|
|
647
636
|
error = _state.sent();
|
|
648
637
|
console.error("[StormcloudVideoPlayer] Error sending initial tracking data:", error);
|
|
649
638
|
return [
|
|
650
639
|
3,
|
|
651
|
-
|
|
640
|
+
4
|
|
652
641
|
];
|
|
653
|
-
case
|
|
642
|
+
case 4:
|
|
654
643
|
return [
|
|
655
644
|
2
|
|
656
645
|
];
|
|
@@ -748,61 +737,54 @@ function sendAdLoadedTracking(_0, _1) {
|
|
|
748
737
|
}
|
|
749
738
|
function sendAdImpressionTracking(_0, _1) {
|
|
750
739
|
return _async_to_generator(function(licenseKey, adImpressionInfo) {
|
|
751
|
-
var context, metricEvent,
|
|
740
|
+
var context, metricEvent, error;
|
|
752
741
|
var _arguments = arguments;
|
|
753
742
|
return _ts_generator(this, function(_state) {
|
|
754
743
|
switch(_state.label){
|
|
755
744
|
case 0:
|
|
756
745
|
context = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
|
|
746
|
+
if (!canPublish(licenseKey)) return [
|
|
747
|
+
2
|
|
748
|
+
];
|
|
757
749
|
_state.label = 1;
|
|
758
750
|
case 1:
|
|
759
751
|
_state.trys.push([
|
|
760
752
|
1,
|
|
761
|
-
|
|
753
|
+
3,
|
|
762
754
|
,
|
|
763
|
-
|
|
755
|
+
4
|
|
764
756
|
]);
|
|
765
757
|
return [
|
|
766
758
|
4,
|
|
767
|
-
buildPlayerMetricEvent(
|
|
759
|
+
buildPlayerMetricEvent(context, {
|
|
768
760
|
captureAt: adImpressionInfo.timestamp
|
|
769
761
|
})
|
|
770
762
|
];
|
|
771
763
|
case 2:
|
|
772
764
|
metricEvent = _state.sent();
|
|
773
|
-
|
|
774
|
-
|
|
765
|
+
publishTracking(licenseKey, "heartbeat", metricEvent);
|
|
766
|
+
publishTracking(licenseKey, "impressions", {
|
|
775
767
|
events: [
|
|
776
768
|
{
|
|
777
769
|
player_id: metricEvent.player_id,
|
|
778
770
|
ad_played_count: 1,
|
|
779
771
|
ad_url: adImpressionInfo.adUrl,
|
|
780
|
-
license_key: licenseKey,
|
|
781
772
|
capture_at: adImpressionInfo.timestamp
|
|
782
773
|
}
|
|
783
774
|
]
|
|
784
|
-
};
|
|
785
|
-
return [
|
|
786
|
-
4,
|
|
787
|
-
Promise.all([
|
|
788
|
-
publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatBody),
|
|
789
|
-
publishOrPost("".concat(mqttTopicPrefix, "/tracking/impressions"), IMPRESSIONS_URL, licenseKey, impressionsBody)
|
|
790
|
-
])
|
|
791
|
-
];
|
|
792
|
-
case 3:
|
|
793
|
-
_state.sent();
|
|
775
|
+
});
|
|
794
776
|
return [
|
|
795
777
|
3,
|
|
796
|
-
|
|
778
|
+
4
|
|
797
779
|
];
|
|
798
|
-
case
|
|
780
|
+
case 3:
|
|
799
781
|
error = _state.sent();
|
|
800
782
|
console.error("[StormcloudVideoPlayer] Error sending ad impression tracking:", error);
|
|
801
783
|
return [
|
|
802
784
|
3,
|
|
803
|
-
|
|
785
|
+
4
|
|
804
786
|
];
|
|
805
|
-
case
|
|
787
|
+
case 4:
|
|
806
788
|
return [
|
|
807
789
|
2
|
|
808
790
|
];
|
|
@@ -818,38 +800,36 @@ function sendHeartbeat(_0) {
|
|
|
818
800
|
switch(_state.label){
|
|
819
801
|
case 0:
|
|
820
802
|
context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
|
|
803
|
+
if (!canPublish(licenseKey)) return [
|
|
804
|
+
2
|
|
805
|
+
];
|
|
821
806
|
_state.label = 1;
|
|
822
807
|
case 1:
|
|
823
808
|
_state.trys.push([
|
|
824
809
|
1,
|
|
825
|
-
|
|
810
|
+
3,
|
|
826
811
|
,
|
|
827
|
-
|
|
812
|
+
4
|
|
828
813
|
]);
|
|
829
814
|
return [
|
|
830
815
|
4,
|
|
831
|
-
buildPlayerMetricEvent(
|
|
816
|
+
buildPlayerMetricEvent(context, flags)
|
|
832
817
|
];
|
|
833
818
|
case 2:
|
|
834
819
|
heartbeatData = _state.sent();
|
|
835
|
-
|
|
836
|
-
4,
|
|
837
|
-
publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatData)
|
|
838
|
-
];
|
|
839
|
-
case 3:
|
|
840
|
-
_state.sent();
|
|
820
|
+
publishTracking(licenseKey, "heartbeat", heartbeatData);
|
|
841
821
|
return [
|
|
842
822
|
3,
|
|
843
|
-
|
|
823
|
+
4
|
|
844
824
|
];
|
|
845
|
-
case
|
|
825
|
+
case 3:
|
|
846
826
|
error = _state.sent();
|
|
847
827
|
console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
|
|
848
828
|
return [
|
|
849
829
|
3,
|
|
850
|
-
|
|
830
|
+
4
|
|
851
831
|
];
|
|
852
|
-
case
|
|
832
|
+
case 4:
|
|
853
833
|
return [
|
|
854
834
|
2
|
|
855
835
|
];
|
|
@@ -865,7 +845,6 @@ function sendHeartbeat(_0) {
|
|
|
865
845
|
sendAdImpressionTracking: sendAdImpressionTracking,
|
|
866
846
|
sendAdLoadedTracking: sendAdLoadedTracking,
|
|
867
847
|
sendHeartbeat: sendHeartbeat,
|
|
868
|
-
sendInitialTracking: sendInitialTracking
|
|
869
|
-
setMQTTTopicPrefix: setMQTTTopicPrefix
|
|
848
|
+
sendInitialTracking: sendInitialTracking
|
|
870
849
|
});
|
|
871
850
|
//# sourceMappingURL=tracking.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/utils/tracking.cjs","../../src/utils/tracking.ts","../../src/utils/mqttClient.ts"],"names":["__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","value","mod","getClientInfo","sendAdDetectTracking","sendAdImpressionTracking","isNodeMode","__esModule","__toCommonJS","tracking_exports","getBrowserID","sendAdLoadedTracking","sendHeartbeat","sendInitialTracking","setMQTTTopicPrefix","module","exports","import_mqtt","__toESM","require","LOG","client","isMQTTConfigured","publishMQTT","topic","payload","publish","JSON","stringify","qos","err","console","warn","cachedBrowserId","screen","window","navigator","ua","userAgent","platform","vendor","maxTouchPoints","memory","deviceMemory","hardwareConcurrency","screenInfo","width","height","availWidth","orientation","pixelDepth","deviceType","brand","model","tizenMatch","tvMatch","trim","type","os","isSmartTV","isAndroid","isWebView","isWebApp","includes","m","match","test","androidModelMatch","outerHeight","outerWidth","substring","domain","location","hostname","origin","path","pathname"],"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 setMQTTTopicPrefix: () => setMQTTTopicPrefix\n});\nmodule.exports = __toCommonJS(tracking_exports);\n\n// src/utils/mqttClient.ts\nvar import_mqtt = __toESM(require(\"mqtt\"), 1);\nvar LOG = \"[StormcloudVideoPlayer][MQTT]\";\nvar client = null;\nfunction isMQTTConfigured() {\n return client !== null;\n}\nfunction publishMQTT(topic, payload) {\n if (!client) {\n return false;\n }\n try {\n client.publish(topic, JSON.stringify(payload), { qos: 1 });\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 m = ua.match(/Web0S\\/([^\\s]+)/);\n model = m ? `webOS ${m[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 (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?.[1]) model = androidModelMatch[1];\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)) deviceType = \"tablet\";\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) isWebView = true;\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) return cachedBrowserId;\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++) buffer[i] = utf8.charCodeAt(i);\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(\"[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash\");\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 mqttTopicPrefix = \"adstorm\";\nfunction setMQTTTopicPrefix(prefix) {\n mqttTopicPrefix = prefix || \"adstorm\";\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 = { \"Content-Type\": \"application/json\" };\n if (licenseKey) headers[\"Authorization\"] = `Bearer ${licenseKey}`;\n return headers;\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) throw new Error(`HTTP error! status: ${response.status}`);\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 publishOrPost(mqttTopic, httpUrl, licenseKey, body) {\n if (isMQTTConfigured()) {\n publishMQTT(mqttTopic, body);\n return;\n }\n await postJson(httpUrl, licenseKey, body);\n}\nasync function sendInitialTracking(licenseKey, context = {}) {\n try {\n const clientInfo = getClientInfo();\n const browserId = await getBrowserID(clientInfo);\n const captureAt = (/* @__PURE__ */ new Date()).toISOString();\n const trackingData = { browserId, ...clientInfo };\n const metricsBody = {\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: captureAt\n }\n ],\n trackingData\n };\n await publishOrPost(\n `${mqttTopicPrefix}/tracking/metrics`,\n TRACK_URL,\n licenseKey,\n metricsBody\n );\n } catch (error) {\n console.error(\"[StormcloudVideoPlayer] Error sending initial tracking data:\", error);\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(\"[StormcloudVideoPlayer] Error sending ad detect tracking:\", error);\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(\"[StormcloudVideoPlayer] Error sending ad loaded tracking:\", error);\n }\n}\nasync function sendAdImpressionTracking(licenseKey, adImpressionInfo, context = {}) {\n try {\n const metricEvent = await buildPlayerMetricEvent(licenseKey, context, {\n captureAt: adImpressionInfo.timestamp\n });\n const heartbeatBody = metricEvent;\n const impressionsBody = {\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 await Promise.all([\n publishOrPost(\n `${mqttTopicPrefix}/tracking/heartbeat`,\n HEARTBEAT_URL,\n licenseKey,\n heartbeatBody\n ),\n publishOrPost(\n `${mqttTopicPrefix}/tracking/impressions`,\n IMPRESSIONS_URL,\n licenseKey,\n impressionsBody\n )\n ]);\n } catch (error) {\n console.error(\"[StormcloudVideoPlayer] Error sending ad impression tracking:\", error);\n }\n}\nasync function sendHeartbeat(licenseKey, context = {}, flags = {}) {\n try {\n const heartbeatData = await buildPlayerMetricEvent(licenseKey, context, flags);\n await publishOrPost(\n `${mqttTopicPrefix}/tracking/heartbeat`,\n HEARTBEAT_URL,\n licenseKey,\n heartbeatData\n );\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 setMQTTTopicPrefix\n});\n","import type {\n ClientInfo,\n TrackingData,\n AdDetectInfo,\n AdLoadedInfo,\n AdImpressionInfo,\n PlayerAnalyticsContext,\n} from \"../types\";\nimport { isMQTTConfigured, 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\"; os = \"webOS\"; isSmartTV = true; deviceType = \"tv\";\n const m = ua.match(/Web0S\\/([^\\s]+)/);\n model = m ? `webOS ${m[1]}` : \"webOS TV\";\n } else if (ua.includes(\"Tizen\")) {\n brand = \"Samsung\"; os = \"Tizen\"; isSmartTV = true; 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\"; os = \"Saphi\"; isSmartTV = true; deviceType = \"tv\";\n } else if (ua.includes(\"Sharp\") || ua.includes(\"AQUOS\")) {\n brand = \"Sharp\"; os = \"Android TV\"; isSmartTV = true; deviceType = \"tv\";\n } else if (ua.includes(\"Android\") && (ua.includes(\"Sony\") || vendor.includes(\"Sony\"))) {\n brand = \"Sony\"; os = \"Android TV\"; isSmartTV = true; deviceType = \"tv\";\n } else if (ua.includes(\"Android\") && (ua.includes(\"NetCast\") || ua.includes(\"LG\"))) {\n brand = \"LG\"; os = \"Android TV\"; isSmartTV = true; deviceType = \"tv\";\n } else if (ua.includes(\" Roku\") || ua.includes(\"Roku/\")) {\n brand = \"Roku\"; os = \"Roku OS\"; isSmartTV = true; deviceType = \"tv\";\n } else if (ua.includes(\"AppleTV\")) {\n brand = \"Apple\"; os = \"tvOS\"; isSmartTV = true; deviceType = \"tv\";\n }\n\n if (ua.includes(\"Android\")) {\n isAndroid = true; os = \"Android\";\n deviceType = /Mobile/.test(ua) ? \"mobile\" : \"tablet\";\n if (maxTouchPoints === 0 || ua.includes(\"Google TV\") || ua.includes(\"XiaoMi\")) {\n deviceType = \"tv\"; isSmartTV = true;\n brand = brand === \"Unknown\" ? \"Android TV\" : brand;\n }\n const androidModelMatch = ua.match(/\\(([^)]*Android[^)]*)\\)/);\n if (androidModelMatch?.[1]) model = androidModelMatch[1];\n }\n\n if (/iPad|iPhone|iPod/.test(ua)) {\n os = \"iOS\"; deviceType = \"mobile\"; brand = \"Apple\";\n if (navigator.maxTouchPoints > 1 && /iPad/.test(ua)) deviceType = \"tablet\";\n }\n\n if (!isAndroid && !isSmartTV && !/Mobile/.test(ua)) {\n if (ua.includes(\"Windows\")) { os = \"Windows\"; deviceType = \"desktop\"; }\n else if (ua.includes(\"Mac\") && !/iPhone/.test(ua)) {\n os = \"macOS\"; deviceType = \"desktop\";\n if (maxTouchPoints > 1) deviceType = \"tablet\";\n } else if (ua.includes(\"Linux\")) { os = \"Linux\"; 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\n isWebView = /wv|WebView|Linux; U;/.test(ua);\n if (window?.outerHeight === 0 && window?.outerWidth === 0) isWebView = true;\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) return cachedBrowserId;\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 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++) buffer[i] = utf8.charCodeAt(i);\n encodedData = buffer;\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(\"[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}\n\nlet mqttTopicPrefix = \"adstorm\";\n\nexport function setMQTTTopicPrefix(prefix: string): void {\n mqttTopicPrefix = prefix || \"adstorm\";\n}\n\nconst PLAYER_TRACKING_BASE_URL =\n \"https://adstorm.co/api-adstorm-dev/adstorm/player-tracking\";\nconst TRACK_URL = `${PLAYER_TRACKING_BASE_URL}/metrics/ingest`;\nconst HEARTBEAT_URL = `${PLAYER_TRACKING_BASE_URL}/heartbeat`;\nconst IMPRESSIONS_URL = `${PLAYER_TRACKING_BASE_URL}/impressions/ingest`;\n\ntype PlayerMetricFlags = {\n adLoaded?: boolean;\n adDetect?: boolean;\n captureAt?: string;\n};\n\nfunction buildHeaders(licenseKey: string | undefined): Record<string, string> {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (licenseKey) headers[\"Authorization\"] = `Bearer ${licenseKey}`;\n return headers;\n}\n\nasync function postJson(\n url: string,\n licenseKey: string | undefined,\n body: Record<string, unknown>\n): Promise<void> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: buildHeaders(licenseKey),\n body: JSON.stringify(body),\n });\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);\n await response.json();\n}\n\nasync function buildPlayerMetricEvent(\n licenseKey: string | undefined,\n context: PlayerAnalyticsContext = {},\n flags: PlayerMetricFlags = {}\n): Promise<Record<string, unknown>> {\n const clientInfo = getClientInfo();\n const browserId = await getBrowserID(clientInfo);\n const captureAt = flags.captureAt ?? new Date().toISOString();\n\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}\n\nasync function publishOrPost(\n mqttTopic: string,\n httpUrl: string,\n licenseKey: string | undefined,\n body: Record<string, unknown>\n): Promise<void> {\n if (isMQTTConfigured()) {\n publishMQTT(mqttTopic, body);\n return;\n }\n await postJson(httpUrl, licenseKey, body);\n}\n\n\nexport async function sendInitialTracking(\n licenseKey?: string,\n context: PlayerAnalyticsContext = {}\n): Promise<void> {\n try {\n const clientInfo = getClientInfo();\n const browserId = await getBrowserID(clientInfo);\n const captureAt = new Date().toISOString();\n\n const trackingData: TrackingData = { browserId, ...clientInfo };\n\n const metricsBody: Record<string, unknown> = {\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: captureAt,\n },\n ],\n trackingData,\n };\n\n await publishOrPost(\n `${mqttTopicPrefix}/tracking/metrics`,\n TRACK_URL,\n licenseKey,\n metricsBody\n );\n } catch (error) {\n console.error(\"[StormcloudVideoPlayer] Error sending initial tracking data:\", error);\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(\"[StormcloudVideoPlayer] Error sending ad detect tracking:\", error);\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(\"[StormcloudVideoPlayer] Error sending ad loaded tracking:\", error);\n }\n}\n\nexport async function sendAdImpressionTracking(\n licenseKey: string | undefined,\n adImpressionInfo: AdImpressionInfo,\n context: PlayerAnalyticsContext = {}\n): Promise<void> {\n try {\n const metricEvent = await buildPlayerMetricEvent(licenseKey, context, {\n captureAt: adImpressionInfo.timestamp,\n });\n\n const heartbeatBody = metricEvent;\n const impressionsBody: Record<string, unknown> = {\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 await Promise.all([\n publishOrPost(\n `${mqttTopicPrefix}/tracking/heartbeat`,\n HEARTBEAT_URL,\n licenseKey,\n heartbeatBody\n ),\n publishOrPost(\n `${mqttTopicPrefix}/tracking/impressions`,\n IMPRESSIONS_URL,\n licenseKey,\n impressionsBody\n ),\n ]);\n } catch (error) {\n console.error(\"[StormcloudVideoPlayer] Error sending ad impression tracking:\", error);\n }\n}\n\nexport async function sendHeartbeat(\n licenseKey?: string,\n context: PlayerAnalyticsContext = {},\n flags: PlayerMetricFlags = {}\n): Promise<void> {\n try {\n const heartbeatData = await buildPlayerMetricEvent(licenseKey, context, flags);\n await publishOrPost(\n `${mqttTopicPrefix}/tracking/heartbeat`,\n HEARTBEAT_URL,\n licenseKey,\n heartbeatData\n );\n } catch (error) {\n console.error(\"[StormcloudVideoPlayer] Error sending heartbeat:\", error);\n }\n}\n","import mqtt from \"mqtt\";\nimport type { MqttClient } from \"mqtt\";\n\nconst LOG = \"[StormcloudVideoPlayer][MQTT]\";\n\nexport type MQTTStatus = \"disconnected\" | \"connecting\" | \"connected\" | \"error\";\n\nlet client: MqttClient | null = null;\nlet status: MQTTStatus = \"disconnected\";\nlet brokerUrl = \"\";\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 client !== null;\n}\n\nexport function initMQTTClient(\n url: string,\n _topicPrefix = \"adstorm\"\n): void {\n if (client) return;\n\n brokerUrl = url;\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 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 publishMQTT(\n topic: string,\n payload: Record<string, unknown>\n): boolean {\n if (!client) {\n return false;\n }\n try {\n client.publish(topic, JSON.stringify(payload), { qos: 1 });\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 brokerUrl = \"\";\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/utils/tracking.cjs","../../src/utils/tracking.ts","../../src/utils/mqttConfig.ts","../../src/utils/mqttClient.ts"],"names":["tracking_exports","licenseKey","mqttConfig","__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","getBrowserID","getClientInfo","sendAdDetectTracking","sendAdImpressionTracking","sendAdLoadedTracking","sendHeartbeat","sendInitialTracking","module","exports","DEFAULT_MQTT_CONFIG","enabled","brokerAddress","brokerPort","wsPort","username","password","topicPrefix","qos","isMQTTEnabled","buildMQTTBrokerUrl","brokerUrl","buildPlayerTopic","channel","import_mqtt","require","LOG","client","status","initMQTTClient","keepalive","mqtt","clean","connect","reconnectPeriod","url","err","console","on","warn","clientId","connectTimeout","queueQoSZero","info","message","ensureMQTTClient","publishMQTT","topic","payload","publish","JSON","stringify","cachedBrowserId","screen","window","navigator","ua","userAgent","screenInfo","vendor","maxTouchPoints","memory","deviceMemory","hardwareConcurrency","width","availWidth","availHeight","os","pixelDepth","deviceType","brand","model","isAndroid","isWebView","isWebApp","isSmartTV","m","match","includes","tizenMatch","tvMatch","trim"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wFCAA,SAAA,UAAAA,4BAAAA,gBC0CsCC,MAA1BC;;;;;;;;;;;;;;;;;;;;;;;;QFzCRC,QAAAA,GAAWC,OAAOC,MAAM;QACxBC,KAAAA,OAAYF,OAAOG,cAAc;QACjCC,YAAAA,OAAmBJ,OAAOK,wBAAwB;QAClDC,aAAAA,OAAoBN,OAAOO,mBAAmB;QAC9CC,IAAAA,IAAAA,GAAAA,EAAeR,GAAAA,CAAAA,GAAOS,cAAc;QACpCC,QAAAA,IAAAA,EAAeV,OAAOW,OAAPX,CAAOW,CAAAA,EAAAA,IAAAA,CAAS,CAACC,cAAc;IAClD,EAAIC,KAAAA,IAAAA,EAAW,CAAA,QAAA,CAAA,QAACC,EAAAA,MAAQC;QACtB,IAAK,IAAIC,QAAQD,IACfb,UAAUY,QAAQE,MAAM;YAAEC,CAAAA,IAAKF,GAAG,CAACC,KAAK;YAAEE,QAAAA,IAAY;QAAK,aAAA;QAC/D,IAAA,aAAA,GAAA,KAAA,CAAA;QACIC,IAAAA,QAAc,EAAA,GAAA,KAAA,CAAA,UAACC,IAAIC,MAAMC,QAAQC,GAAAA,aAAAA;QACnC,IAAIF,IAAAA,IAAQ,CAAA,OAAOA,CAAAA,SAAAA,OAAAA,UAAAA,CAAAA,EAAAA,EAAAA,KAAAA,OAAAA,SAAP,IAAA,KAAOA,KAAG,MAAM,YAAY,OAAOA,SAAS,YAAY;cAC7D,CAAA,GAAA,QAAA,CAAA,YAAA,SAAA,2BAAA;;;oBAAA,IAAIG,MAAJ;oBACH,CAAA,GAAI,CAACd,aAAae,IAAI,CAACL,IAAII,QAAQA,QAAQF,QACzCpB,UAAUkB,IAAII,KAAK;sBAAEP,IAAAA,CAAK,SAALA,GAAAA,GAAAA,QAAAA,CAAAA,UAAAA;mCAAWI,IAAI,CAACG,IAAI;;wBAAEN,YAAY,CAAEK,CAAAA,OAAOnB,iBAAiBiB,MAAMG,IAAG,KAAMD,KAAKL,UAAU;oBAAC,CAAA;;gBAFpH,QAAK,YAAWZ,kBAAkBe,0BAA7B,SAAA,6BAAA,QAAA,yBAAA;;gBAAA,IAAA;gBAAA,KAAA;;;yBAAA,6BAAA;wBAAA;;;wBAAA;8BAAA;;;;QAGP,QAAA;QACA,KAAA,EAAOD;QACT,YAAA;QACIM,UAAU,GAAA,cAACC,KAAKC,YAAYd;aAAYA,SAASa,OAAO,OAAO5B,SAASS,aAAamB,QAAQ,CAAC,GAAGR,YACnG,sEAAsE;MACtE,EAAA,GAAA,QAAA,CAAA,YAAA,uCAAiE;QACjE,YAAA,0DAAsE;QACtE,KAAA,gEAAqE;QACrES,aAAAA,CAAc,CAACD,OAAO,CAACA,GAAAA,CAAIE,MAAAA,IAAU,GAAG3B,IAAAA,MAAUY,QAAQ,WAAW;YAAEgB,OAAOH,YAAAA,KAAAA,GAAAA,QAAAA,CAAAA,gBAAAA,GAAAA,QAAAA,CAAAA,WAAAA;cAAKT,WAAAA,CAAY;YAAK,GAAKJ,QACzGa,CAAAA;;QAEEI,eAAe,sBAACJ;aAAQR,YAAYjB,OAAAA,GAAU,CAAC,GAAG,CAAA,CAAA,YAAc;kFAAE4B,OAAO,UAAA,CAAA,EAAA,EAAA,QAAA,iBAAA,CAAA,EAAA;MAAK,IAAIH;;QAEtF,KAAA,eAAwB;QC7BxB/B,aAAAA,MAAA,CAAA;QAAAiB,KAAAjB,GAAAA,eAAA;QAAAoC,IAAAA,UAAA,SAAAA,KAAAA,GAAAA,KAAAA,OAAAA,IAAAA,CAAAA,KAAAA,aAAAA;iBAAAA;;QAAAC,IAAAA,GAAAA,QAAA,CAAA,QAAAA,IAAAA;qBAAAA;;QAAAC,OAAAA,IAAAA,GAAAA,QAAA,CAAA,QAAAA,EAAAA,CAAAA,SAAAA,IAAAA,CAAAA,KAAAA;qBAAAA;;YAAAC,IAAAA,iBAAAA,GAAA,SAAAA,IAAAA;mBAAAA,GAAAA,QAAAA,CAAAA,UAAAA;;YAAAC,aAAAA,OAAA,SAAAA;mBAAAA;;MAAAC,EAAAA,UAAAA,GAAA,QAAA,CAAAA;mBAAAA,QAAAA,CAAAA,aAAAA,GAAAA,QAAAA,CAAAA,WAAAA,QAAAA;;QAAAC,IAAAA,OAAAA,QAAAA,CAAAA,CAAA,SAAAA,IAAAA,GAAAA,QAAAA,CAAAA,QAAAA,QAAAA;iBAAAA;;IAAA,IAAA,EAAA,UAAA,oBAAA,8BAAA,QAAA,WAAA,MAAA,KAAA,EAAA,WAAA,oBAAA,+BAAA,SAAA,UAAA,MAAA,GAAA,YAAA;IAAAC,KAAAC,MAAAA,CAAA,GAAAT,GAAAA,UAAAnC,CAAAA,8BAAAA,OAAAA,IAAAA,OAAAA,SAAAA,CAAAA,UAAAA,KAAAA,QAAAA,EAAAA,iBAAAA,OAAAA,MAAAA,cAAAA,sCAAAA,6BAAAA,eAAAA,WAAAA,cAAAA,iDAAAA,2BAAAA,KAAAA,MAAAA,KAAAA;ID0CA,OAAA,iBAA0B;eE9Bb6C,sBAAkC;YAC7CC,SAAS;QACTC,OAAAA,QAAe,CAAA,GAAA,SAAA,CAAA,GAAA,MAAA;oBACfC,YAAY;mBACZC,QAAQ;mBACRC,UAAU;QACVC,UAAU;QACVC,aAAa;QACbC,KAAK,GAAA,OAAA,QAAA,CAAA,QAAA;QACP,QAAA,OAAA,QAAA,CAAA,MAAA;QAEanD,MAAAA,OAAyB,QAAA,CAAA,QAAA,EAAK2C;QAMpC,KAASS,MAAAA;gBACd,OAAOpD,WAAW4C,OAAA;QACpB,UAAA;QAEO,KAASS,GAAAA;QACd,IAAIrD,WAAWsD,SAAA,EAAW,OAAOtD,WAAWsD,SAAA;QAC5C,OAAO,OAAA,EAAqCtD,OAA5BA,WAAW6C,aAAa,EAAA,KAAqB,OAAjB7C,WAAW+C,MAAM,EAAA;QAC/D,gBAAA;QAEO,KAASQ,KAAAA,UAAAA,EACdxD,MAAAA,IAAA,EACAyD,OAAA;QAEA,OAAO,GAA6BzD,CAAAA,EAAAA,uBAAAA,UAA1BC,OAAWkD,EAAAA,cAAenD,2CAAAA,qBAAfmD,IAAAA,CAAAA,EAAW,EAAA,KAAA,CAAkBM,OAAdzD,YAAU,KAAW,OAAPyD;QACpD,eAAA,UAAA,aAAA;QFsBA,YAAA,UAA0B,UAAA,IAAA;QGjE1BC,UAAAA,IAAiB7B,KAAAA,GAAA8B,KAAAA,GAAA,SAAA;QAUXC,MAAM,WAAA,SAAA,eAAA;IAIZ,EAAIC,SAA4B;AAChC,IAAIC,SAAqB;AAmBlB,SAASC,aAAAA,UAAAA;;YAIL,mCAKPF,OAASH,MAIPM,MAJOC,AAKPC,OALOD,CAAKE,AAMZC,GAGF,IATc,CAAQC,KAAK,EAU7B,CAASC,KAAK,aAQZC,IACF,IAEAV,MAKAA,GAAOW,EAAA,CAAG,mBAERD,QAAQE;;;;sBApCV,EAAA,EAAIZ,UAAU,CAACR,IAAAA;;wBAAAA,MAAiB;;oBAE1BgB,MAAMf,cAAAA,KAAAA,SAAAA,CAAAA;yBACZQ,CAAAA,OAAS,WAAA,iBAAA,iBAAA,OAAA,MAAA,cAAA,qCAAA,eAAA,MAAA,CAAA,GAATA;;;;;;;;;;;;oBAIA;;wBAAI,OAAA,MAAA,CAAA,MAAA,CAAA,WAAA,IAAA;4BAAA;4BAAA;4BAAA;;;;oBAAJ,EAAI;0BAEAY,KAAAA,KAAAA,WAAAA,aAAAA;4BACAzB,UAAUhD,IAAAA,OAAWgD,OAAAA,CAAA,KAAA,CAAA;0BACrBC,CAAAA,SAAUjD,WAAWiD,QAAA;wBACrBc,OAAAA,EAAW,OAAA,mBAAA;wBACXE,KAAO,IAAA,IAAA,WAAA,KAAA,MAAA;4BACPE,IAAAA,GAAAA,IAAAA,CAAiB,IAAA,MAAA,EAAA,IAAA,MAAA,CAAA,EAAA,GAAA,KAAA,UAAA,CAAA;4BACjBO,UAAAA,MAAgB;0BAChBC,cAAc;oBAChB;;wBAAA,OAAA,MAAA,CAAA,MAAA,CAAA,WAAA;;;oBAAA,aAAA;oBACF,UAAc,MAAA,IAAA,CAAA,IAAA,WAAA,aAAA,GAAA,CAAA,SAAA;+BAAA,EAAA,QAAA,CAAA,IAAA,QAAA,CAAA,GAAA;uBAAA,IAAA,CAAA;sBACZd,SAAS,OAAA;sBACTS;;wBAAAA,GAAQE,IAAA,CAAK,GAAM,OAAHb,KAAG,sBAAqBU;;;;oBAE1C,QAAA,IAAA,CAAA;;;;;;2BAIEC,GAAQM,IAAA,CAAK,GAAuBR,OAApBT,KAAG,kBAAoB,OAAHS;sBACtC,EAAA,KAAA,GAAA,KAAA,kBAAA,MAAA,EAAA,KAAA;wBAEAR,CAAOW,EAAA,CAAG,GAAA,UAAa,QAAA,UAAA,CAAA;4BACrBV,GAAAA,CAAAA,KAAS,GAAA,CAAA,IAAA,OAAA;4BACTS,GAAAA,KAAQM,EAAAA,EAAA,CAAK,GAAM,OAAHjB,KAAG;sBACrB;oBAEAC,eAAU,EAAW,GAAA,GAAA,CAAA,MAAA,QAAA,CAAA,IAAA,QAAA,CAAA,GAAA;oBACnBC,SAAS,GAAA,KAAA,GAAA,GAAA,QAAA,CAAA,IAAA,QAAA,CAAA,IAAA;6BACDW,GAAA,CAAK,CAAA,EAAM,IAAA,GAAHb,KAAG,GAAA,CAAA,IAAA,SAAA,CAAA,GAAA,IAAA,QAAA,CAAA,IAAA;sBACrB,gBAAA,CAAA,eAAA,YAAA,MAAA,EAAA,MAAA,CAAA,IAAA;sBAEAC;;wBAAAA,EAAOW,EAAA,CAAG,SAAS,SAACF;;;;YAClBR,SAAS;;QACTS,CAAAA,OAAQE,IAAA,CAAK,GAAM,MAAA,CAAHb,KAAG,YAAWU,IAAIQ,OAAO;MAC3C,KAAA,QAAA,mBAAA;IAEAjB,OAAOW,EAAA,CAAG,SAAS;QACjB,CAAIV,GAAAA,KAAW,aAAa;;YAAA,SAAA,OAG9B,kBAMA,iBACF,uCAPE;;;;;oBAH8B,UAAA,oEAAA,CAAA,GAAA,QAAA,oEAAA,CAAA;wBAC1BA,SAAS;oBACX;;wBAAA,aAAA;;;oBAAA,WAAA;oBACF,aAAA,mBAAA,MAAA,SAAA,cAAA,8BAAA,mBAAA,aAAA,GAAA,IAAA,OAAA,WAAA;oBACF;;wBAAA;4BAEO,KAASiB,MAAAA;4BACd,IAAI1B,SAAAA,UAAmB,CAACQ,QAAQ,EAAA;gCAC9BE,WAAAA,EAAAA,CAAAA,WAAAA;4BACF,SAAA,GAAA,kBAAA,MAAA,QAAA,cAAA,6BAAA,kBAAA;4BACF,SAAA,GAAA,kBAAA,MAAA,QAAA,cAAA,6BAAA,kBAAA;4BAEO,KAASiB,OAAAA,KACdC,KAAA,EACAC,OAAA;2BAEA,CAAI,CAAC7B,MAAAA,WAAiB,IAAA,GAAA;4BAAA,mBAAA,QAAA,eAAA;wBAAA,IAAA,CAAA;;;;QAEtB;;IAEA0B,KAAAA,gBAAAA,UAAAA,EAAAA,OAAAA,EAAAA,IAAAA;MAEA,IAAI,CAAClB,QAAQ;UACX,MAAA,CAAO,gBAAA,YAAA,UAAA;IACT;IAEA,IAAI,CAAA;wCAAA,UAAA;YAAA,SACmE,aH4BzE;;;;;oBG7BM,UAAA,oEAAA,CAAA;0BACFA,OAAOsB,GAAAA,IAAA,CAAQF,OAAOG,CAAAA,IAAKC,SAAA,CAAUH,UAAU;;;;;;;;;;;oBAAsB;;wBAAA,uBAAA,SAAA;8BACrE,OAAO,CAAA;4BACT,OAASZ,GAAAA,EAAK;4BACZC,QAAQE,IAAA,CAAK,GAA4BQ,OAAzBrB,KAAG,uBAA2B,OAALqB,OAAK,MAAKX;;;oBAHkB,cAAA;wBAIrE,OAAO,KAAA,YAAA,WAAA;wBACT,MAAA;4BAAA;;oBACF;;;;;;oBHsBA,aAAwB;oBC7HpBgB,QAAAA,KAAAA,CAAAA,IAAiC,4DAAA;;;;;;;;;;;YAW1BC,SACCA,UACIA,UACCA,UACCA,qBAAAA,UACFA,UAkEVC,SAA6BA,UAK/BA,4BAAAA,gBAsBWC;;IA1Gb,IAAMC,CAAKD,IAAAA,IAAUE,SAAA;wCAAA,UAAA,EAAA,YAAA;YAAA,SAOfC;;;;;oBAPe,UAAA,oEAAA,CAAA;;;;;;;;;;;wBAEfC,OAASJ,OAAAA,GAAUI,MAAA,GAAA,CAAU,QAAA;4BACnC,EAAMC,QAAAA,SAAiBL,UAAUK,cAAA,IAAkB;4BACnD,EAAMC,SAAUN,UAAkBO,GAAAA,SAAA,IAAgB;wBAClD,IAAMC,sBAAsBR,UAAUQ,mBAAA,IAAuB;;;oBAH7D,IAAMJ;;;;;;oBAKAD,UAAa;wBACjBM,IAAAA,CAAA,GAAOX,CAAAA,CAAAA,QAAAA,oBAAAA,8BAAAA,GAAAA,KAAQW,KAAA;;;;;;;;;;;YAEfC,UAAA,GAAYZ,WAAAA,oBAAAA,+BAAAA,SAAQY,UAAA;;QACpBC,CAAAA,IAAA,GAAab,WAAAA;wCAAAA,UAAAA,EAAAA,KAAAA,OAAAA;YAAAA,SAOXc,EAAK;;;;;oBAPMd,UAAAA,oEAAAA,CAAAA,WAAAA,SAAQa,WAAA;;;;;;;;;wBAErBE;;wBAAAA,QAAA,GAAYf,GAAAA,QAAAA,IAAAA,SAAAA,OAAAA,+BAAAA,SAAQe,UAAA;4BACtB,UAAA;4BAEA,EAAIC,SAAAA,IAAqD,SAAA,SAAA;wBACzD,IAAIC,QAAQ;;;;;;;;;oBACRH;oBACJ,IAAII,IAAAA,IAAQ,CAAA,CAAA,6DAAA;;;;;;;;;;;QAEZ,IAAIC,YAAY;;IAChB,IAAIC,CAAAA,KAAY;wCAAA,UAAA,EAAA,gBAAA;YAAA,SAIdH;;;;;oBAJc,UAAA,oEAAA,CAAA;sBAChB,EAAA,CAAA,CAAII,UAAAA,CAAW,YAAA;;;;;;;;;;;oBAGL;;wBAAA,uBAAA,SAAA;8BAAMP,KAAK,IAAA,iBAAA,SAAA;4BAASQ,YAAY;;;oBAAxCL,MAAQ,QAAA;wBAAsCD,YAAAA,CAAa,WAAA,aAAA;wBAC3D,IAAMO,IAAIpB,GAAGqB,CAAAA,IAAA,CAAM,OAAA,eAAA;0BACnBN,IAAAA,IAAQK,IAAI,SAAa,OAAJA,CAAA,CAAE,EAAE,IAAK;4BAChC,GAAA,IAAWpB,GAAGsB,QAAA,CAAS,UAAU;gCAC/BR,MAAQ,KAAA,YAAA,SAAA;gCAAWH,GAAK,cAAA;gCAASQ,QAAAA,EAAY,eAAA,KAAA;gCAAMN,WAAa,CAAA,iBAAA,SAAA;4BAChE,IAAMU,aAAavB,GAAGqB,KAAA,CAAM;;wBAE5BN,QAAQQ,aAAa,SAA0BC,OAAjBD,UAAA,CAAW,EAAE,EAAA,KAAW,OAAPC,SAAUC,IAAA,KAAS;;;;;;oBACpE,IAAWzB,GAAGsB,QAAA,CAAS,YAAY;wBACjCR,IAAAA,IAAQ,CAAA,CAAA,iEAAA;;;;;;;;;;;YAAyBK,YAAY;;QAAMN,CAAAA,MAAa;wCAAA,UAAA;YAAA,SAAA,OAE/CF,eAAqCE,UAAa;;;;;oBAFH,UAAA,oEAAA,CAAA,GAAA,QAAA,oEAAA,CAAA;sBAClE,EAAA,CAAA,IAAA,IAAWb,GAAGsB,QAAA,CAAS,IAAA,QAAYtB,GAAGsB,QAAA,CAAS,UAAU;;;;;;;;;;;oBACjC;;wBAAA,uBAAA,SAAA;;;oBAALX,GAAK,aAAA;wBAAcQ,YAAY,YAAA,aAAA;;;;;;oBAAMN;oBACxD,OAAA,CAAA,GAAWb,EAAAA,CAAGsB,QAAA,CAAS,cAAetB,CAAAA,GAAGsB,QAAA,CAAS,WAAWnB,KAAAA,EAAOmB,QAAA,CAAS,OAAM,GAAI;;;;;;;;;;;YACrEX,KAAK;;QAAcQ,YAAY,yCAAA;QAAMN,KAAAA,OAAAA,CAAa,EAAA;oBACpE,OAAA,IAAWb,GAAGsB,QAAA,CAAS,cAAetB,CAAAA,GAAGsB,QAAA,CAAS,cAActB,GAAGsB,QAAA,CAAS,KAAI,GAAI;yBAClFR,QAAQ;gCAAMH,KAAK;oCAAcQ,YAAY;gCAAMN,aAAa;qBAClE,OAAA,IAAWb,GAAGsB,QAAA,CAAS,YAAYtB,GAAGsB,QAAA,CAAS,UAAU;+BACvDR,QAAQ;SAAQH,KAAK","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 m = ua.match(/Web0S\\/([^\\s]+)/);\n model = m ? `webOS ${m[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 (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?.[1]) model = androidModelMatch[1];\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)) deviceType = \"tablet\";\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) isWebView = true;\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) return cachedBrowserId;\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++) buffer[i] = utf8.charCodeAt(i);\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(\"[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash\");\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(\"[StormcloudVideoPlayer] Error sending initial tracking data:\", error);\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(\"[StormcloudVideoPlayer] Error sending ad detect tracking:\", error);\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(\"[StormcloudVideoPlayer] Error sending ad loaded tracking:\", error);\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(\"[StormcloudVideoPlayer] Error sending ad impression tracking:\", error);\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","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\"; os = \"webOS\"; isSmartTV = true; deviceType = \"tv\";\n const m = ua.match(/Web0S\\/([^\\s]+)/);\n model = m ? `webOS ${m[1]}` : \"webOS TV\";\n } else if (ua.includes(\"Tizen\")) {\n brand = \"Samsung\"; os = \"Tizen\"; isSmartTV = true; 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\"; os = \"Saphi\"; isSmartTV = true; deviceType = \"tv\";\n } else if (ua.includes(\"Sharp\") || ua.includes(\"AQUOS\")) {\n brand = \"Sharp\"; os = \"Android TV\"; isSmartTV = true; deviceType = \"tv\";\n } else if (ua.includes(\"Android\") && (ua.includes(\"Sony\") || vendor.includes(\"Sony\"))) {\n brand = \"Sony\"; os = \"Android TV\"; isSmartTV = true; deviceType = \"tv\";\n } else if (ua.includes(\"Android\") && (ua.includes(\"NetCast\") || ua.includes(\"LG\"))) {\n brand = \"LG\"; os = \"Android TV\"; isSmartTV = true; deviceType = \"tv\";\n } else if (ua.includes(\" Roku\") || ua.includes(\"Roku/\")) {\n brand = \"Roku\"; os = \"Roku OS\"; isSmartTV = true; deviceType = \"tv\";\n } else if (ua.includes(\"AppleTV\")) {\n brand = \"Apple\"; os = \"tvOS\"; isSmartTV = true; deviceType = \"tv\";\n }\n\n if (ua.includes(\"Android\")) {\n isAndroid = true; os = \"Android\";\n deviceType = /Mobile/.test(ua) ? \"mobile\" : \"tablet\";\n if (maxTouchPoints === 0 || ua.includes(\"Google TV\") || ua.includes(\"XiaoMi\")) {\n deviceType = \"tv\"; isSmartTV = true;\n brand = brand === \"Unknown\" ? \"Android TV\" : brand;\n }\n const androidModelMatch = ua.match(/\\(([^)]*Android[^)]*)\\)/);\n if (androidModelMatch?.[1]) model = androidModelMatch[1];\n }\n\n if (/iPad|iPhone|iPod/.test(ua)) {\n os = \"iOS\"; deviceType = \"mobile\"; brand = \"Apple\";\n if (navigator.maxTouchPoints > 1 && /iPad/.test(ua)) deviceType = \"tablet\";\n }\n\n if (!isAndroid && !isSmartTV && !/Mobile/.test(ua)) {\n if (ua.includes(\"Windows\")) { os = \"Windows\"; deviceType = \"desktop\"; }\n else if (ua.includes(\"Mac\") && !/iPhone/.test(ua)) {\n os = \"macOS\"; deviceType = \"desktop\";\n if (maxTouchPoints > 1) deviceType = \"tablet\";\n } else if (ua.includes(\"Linux\")) { os = \"Linux\"; 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\n isWebView = /wv|WebView|Linux; U;/.test(ua);\n if (window?.outerHeight === 0 && window?.outerWidth === 0) isWebView = true;\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) return cachedBrowserId;\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 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++) buffer[i] = utf8.charCodeAt(i);\n encodedData = buffer;\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(\"[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}\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 ? { input_stream_type: context.inputStreamType } : {}),\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(\"[StormcloudVideoPlayer] Error sending initial tracking data:\", error);\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(\"[StormcloudVideoPlayer] Error sending ad detect tracking:\", error);\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(\"[StormcloudVideoPlayer] Error sending ad loaded tracking:\", error);\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(\"[StormcloudVideoPlayer] Error sending ad impression tracking:\", error);\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","export 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"]}
|