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.
Files changed (36) hide show
  1. package/dist/stormcloud-vp.min.js +3 -1
  2. package/lib/index.cjs +536 -341
  3. package/lib/index.cjs.map +1 -1
  4. package/lib/index.d.cts +37 -2
  5. package/lib/index.d.ts +37 -2
  6. package/lib/index.js +477 -342
  7. package/lib/index.js.map +1 -1
  8. package/lib/player/StormcloudVideoPlayer.cjs +266 -206
  9. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  10. package/lib/player/StormcloudVideoPlayer.d.cts +5 -2
  11. package/lib/players/FilePlayer.cjs.map +1 -1
  12. package/lib/players/HlsPlayer.cjs +266 -206
  13. package/lib/players/HlsPlayer.cjs.map +1 -1
  14. package/lib/players/HlsPlayer.d.cts +1 -1
  15. package/lib/players/index.cjs +266 -206
  16. package/lib/players/index.cjs.map +1 -1
  17. package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
  18. package/lib/sdk/hlsAdPlayer.d.cts +1 -1
  19. package/lib/sdk/pal.cjs.map +1 -1
  20. package/lib/{types-DDwAfBLt.d.cts → types-BmF_60m2.d.cts} +2 -0
  21. package/lib/ui/StormcloudVideoPlayer.cjs +450 -341
  22. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  23. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  24. package/lib/utils/browserCompat.cjs.map +1 -1
  25. package/lib/utils/mqttClient.cjs +245 -0
  26. package/lib/utils/mqttClient.cjs.map +1 -0
  27. package/lib/utils/mqttClient.d.cts +13 -0
  28. package/lib/utils/mqttConfig.cjs +141 -0
  29. package/lib/utils/mqttConfig.cjs.map +1 -0
  30. package/lib/utils/mqttConfig.d.cts +20 -0
  31. package/lib/utils/polyfills.cjs.map +1 -1
  32. package/lib/utils/tracking.cjs +182 -170
  33. package/lib/utils/tracking.cjs.map +1 -1
  34. package/lib/utils/tracking.d.cts +1 -1
  35. package/package.json +3 -1
  36. package/src/certs/emqxsl-ca.crt +22 -0
@@ -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, hashArray, hashHex, error, hash, i1, char, fallbackHash, timestamp, random;
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 && crypto.subtle.digest)) return [
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
- hashArray = Array.from(new Uint8Array(hashBuffer));
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
- error = _state.sent();
439
- console.warn("[StormcloudVideoPlayer] crypto.subtle.digest not supported, using fallback hash");
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
- var PLAYER_TRACKING_BASE_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking";
464
- var TRACK_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/metrics/ingest");
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 postJson(url, licenseKey, body) {
571
+ function buildPlayerMetricEvent() {
509
572
  return _async_to_generator(function() {
510
- var response;
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 > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
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
- browserId = _state.sent();
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: browserId,
560
- browserId: browserId,
589
+ _object_spread({
590
+ player_id: playerId,
561
591
  device_type: clientInfo.deviceType,
562
- deviceType: clientInfo.deviceType,
563
- input_stream_type: context.inputStreamType,
564
- os: clientInfo.os,
565
- ad_loaded: flags.adLoaded,
566
- ad_detect: flags.adDetect,
567
- license_key: licenseKey,
568
- capture_at: captureAt,
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, clientInfo, browserId, trackingData, error;
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
- 4,
623
+ 3,
589
624
  ,
590
- 5
625
+ 4
591
626
  ]);
592
- clientInfo = getClientInfo();
593
627
  return [
594
628
  4,
595
- getBrowserID(clientInfo)
596
- ];
597
- case 2:
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 3:
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
- 5
643
+ 4
625
644
  ];
626
- case 4:
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
- 5
650
+ 4
632
651
  ];
633
- case 5:
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
- 4,
763
+ 3,
742
764
  ,
743
- 5
765
+ 4
744
766
  ]);
745
767
  return [
746
768
  4,
747
- buildPlayerMetricEvent(licenseKey, context, {
769
+ buildPlayerMetricEvent(context, {
748
770
  captureAt: adImpressionInfo.timestamp
749
771
  })
750
772
  ];
751
773
  case 2:
752
774
  metricEvent = _state.sent();
753
- return [
754
- 4,
755
- Promise.all([
756
- postJson(HEARTBEAT_URL, licenseKey, metricEvent),
757
- postJson(IMPRESSIONS_URL, licenseKey, {
758
- events: [
759
- {
760
- player_id: metricEvent.player_id,
761
- ad_played_count: 1,
762
- ad_url: adImpressionInfo.adUrl,
763
- license_key: licenseKey,
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
- 5
788
+ 4
775
789
  ];
776
- case 4:
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
- 5
795
+ 4
782
796
  ];
783
- case 5:
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
- 4,
820
+ 3,
804
821
  ,
805
- 5
822
+ 4
806
823
  ]);
807
824
  return [
808
825
  4,
809
- buildPlayerMetricEvent(licenseKey, context, flags)
826
+ buildPlayerMetricEvent(context, flags)
810
827
  ];
811
828
  case 2:
812
829
  heartbeatData = _state.sent();
813
- return [
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
- 5
833
+ 4
822
834
  ];
823
- case 4:
835
+ case 3:
824
836
  error = _state.sent();
825
837
  console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
826
838
  return [
827
839
  3,
828
- 5
840
+ 4
829
841
  ];
830
- case 5:
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"]}
@@ -1,4 +1,4 @@
1
- import { C as ClientInfo, a as AdDetectInfo, P as PlayerAnalyticsContext, b as AdImpressionInfo, c as AdLoadedInfo } from '../types-DDwAfBLt.cjs';
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>;