stormcloud-video-player 0.7.3 → 0.7.5

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/lib/index.js CHANGED
@@ -369,185 +369,8 @@ function _ts_values(o) {
369
369
  // src/ui/StormcloudVideoPlayer.tsx
370
370
  import React2, { useEffect as useEffect2, useRef as useRef2, useMemo, useCallback as useCallback2 } from "react";
371
371
  // src/player/StormcloudVideoPlayer.ts
372
- import Hls2 from "hls.js";
372
+ import Hls from "hls.js";
373
373
  // src/sdk/vastParser.ts
374
- function isHlsType(type) {
375
- return type === "application/x-mpegURL" || type.includes("m3u8");
376
- }
377
- function isMp4Type(type) {
378
- return type === "video/mp4" || type.includes("mp4");
379
- }
380
- function parseVastXml(xmlString) {
381
- var filter = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all", logPrefix = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : "[VastParser]";
382
- try {
383
- var _xmlDoc_querySelector, _xmlDoc_querySelector1, _xmlDoc_querySelector_textContent, _xmlDoc_querySelector2;
384
- var parser = new DOMParser();
385
- var xmlDoc = parser.parseFromString(xmlString, "text/xml");
386
- var parserError = xmlDoc.querySelector("parsererror");
387
- if (parserError) {
388
- console.error("".concat(logPrefix, " XML parsing error (malformed VAST XML):"), parserError.textContent);
389
- return null;
390
- }
391
- var adElement = xmlDoc.querySelector("Ad");
392
- if (!adElement) {
393
- console.warn("".concat(logPrefix, " No Ad element found in VAST XML"));
394
- return null;
395
- }
396
- var adId = adElement.getAttribute("id") || "unknown";
397
- var title = ((_xmlDoc_querySelector = xmlDoc.querySelector("AdTitle")) === null || _xmlDoc_querySelector === void 0 ? void 0 : _xmlDoc_querySelector.textContent) || "Ad";
398
- var isNoAdAvailable = adId === "empty" || title.toLowerCase().includes("no ad available") || title.toLowerCase() === "no ad available";
399
- var durationText = ((_xmlDoc_querySelector1 = xmlDoc.querySelector("Duration")) === null || _xmlDoc_querySelector1 === void 0 ? void 0 : _xmlDoc_querySelector1.textContent) || "00:00:30";
400
- var durationParts = durationText.split(":");
401
- var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + Math.round(parseFloat(durationParts[2] || "0"));
402
- var mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
403
- var mediaFiles = [];
404
- console.log("".concat(logPrefix, " Found ").concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
405
- mediaFileElements.forEach(function(mf, index) {
406
- var _mf_textContent;
407
- var type = mf.getAttribute("type") || "";
408
- var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
409
- var width = mf.getAttribute("width") || "";
410
- var height = mf.getAttribute("height") || "";
411
- console.log("".concat(logPrefix, " MediaFile ").concat(index, ': type="').concat(type, '", url="').concat(url.substring(0, 80), '...", width="').concat(width, '", height="').concat(height, '"'));
412
- if (!url) {
413
- console.warn("".concat(logPrefix, " MediaFile ").concat(index, " has empty URL"));
414
- return;
415
- }
416
- var isHls = isHlsType(type);
417
- var isMp4 = isMp4Type(type);
418
- var accepted = false;
419
- if (filter === "hls-only") {
420
- accepted = isHls;
421
- } else if (filter === "mp4-first") {
422
- accepted = isMp4 || isHls;
423
- } else {
424
- accepted = true;
425
- }
426
- if (!accepted) {
427
- console.log("".concat(logPrefix, " MediaFile ").concat(index, ' ignored (type="').concat(type, '" not accepted by filter "').concat(filter, '")'));
428
- return;
429
- }
430
- var bitrateAttr = mf.getAttribute("bitrate");
431
- var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
432
- mediaFiles.push({
433
- url: url,
434
- type: type,
435
- width: parseInt(width || "1920", 10),
436
- height: parseInt(height || "1080", 10),
437
- bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
438
- });
439
- console.log("".concat(logPrefix, ' Added MediaFile: type="').concat(type, '" url="').concat(url.substring(0, 80), '..."'));
440
- });
441
- if (filter === "mp4-first" && mediaFiles.length > 1) {
442
- mediaFiles.sort(function(a, b) {
443
- var aIsMp4 = isMp4Type(a.type) ? 0 : 1;
444
- var bIsMp4 = isMp4Type(b.type) ? 0 : 1;
445
- return aIsMp4 - bIsMp4;
446
- });
447
- }
448
- if (mediaFiles.length === 0) {
449
- if (isNoAdAvailable) {
450
- console.warn("".concat(logPrefix, " No ads available (VAST response indicates no ads)"));
451
- } else {
452
- console.warn("".concat(logPrefix, " No compatible media files found in VAST XML"));
453
- }
454
- return null;
455
- }
456
- var trackingUrls = {
457
- impression: [],
458
- start: [],
459
- firstQuartile: [],
460
- midpoint: [],
461
- thirdQuartile: [],
462
- complete: [],
463
- mute: [],
464
- unmute: [],
465
- pause: [],
466
- resume: [],
467
- fullscreen: [],
468
- exitFullscreen: [],
469
- skip: [],
470
- error: []
471
- };
472
- xmlDoc.querySelectorAll("Impression").forEach(function(el) {
473
- var _el_textContent;
474
- var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
475
- if (url) trackingUrls.impression.push(url);
476
- });
477
- xmlDoc.querySelectorAll("Tracking").forEach(function(el) {
478
- var _el_textContent;
479
- var event = el.getAttribute("event");
480
- var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
481
- if (event && url) {
482
- var eventKey = event;
483
- if (trackingUrls[eventKey]) {
484
- trackingUrls[eventKey].push(url);
485
- }
486
- }
487
- });
488
- var clickThrough = (_xmlDoc_querySelector2 = xmlDoc.querySelector("ClickThrough")) === null || _xmlDoc_querySelector2 === void 0 ? void 0 : (_xmlDoc_querySelector_textContent = _xmlDoc_querySelector2.textContent) === null || _xmlDoc_querySelector_textContent === void 0 ? void 0 : _xmlDoc_querySelector_textContent.trim();
489
- return {
490
- id: adId,
491
- title: title,
492
- duration: duration,
493
- mediaFiles: mediaFiles,
494
- trackingUrls: trackingUrls,
495
- clickThrough: clickThrough
496
- };
497
- } catch (error) {
498
- console.error("".concat(logPrefix, " Error parsing VAST XML:"), error);
499
- return null;
500
- }
501
- }
502
- function fetchAndParseVastAd(vastTagUrl) {
503
- var filter = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all", logPrefix = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : "[VastParser]";
504
- return _async_to_generator(function() {
505
- var response, vastXml;
506
- return _ts_generator(this, function(_state) {
507
- switch(_state.label){
508
- case 0:
509
- return [
510
- 4,
511
- fetch(vastTagUrl, {
512
- mode: "cors",
513
- credentials: "include",
514
- headers: {
515
- Accept: "application/xml, text/xml, */*"
516
- },
517
- referrerPolicy: "no-referrer-when-downgrade"
518
- })
519
- ];
520
- case 1:
521
- response = _state.sent();
522
- if (!response.ok) {
523
- throw new Error("Failed to fetch VAST: ".concat(response.statusText));
524
- }
525
- return [
526
- 4,
527
- response.text()
528
- ];
529
- case 2:
530
- vastXml = _state.sent();
531
- console.log("".concat(logPrefix, " VAST XML received"));
532
- console.log("".concat(logPrefix, " VAST XML content (first 2000 chars):"), vastXml.substring(0, 2e3));
533
- return [
534
- 2,
535
- parseVastXml(vastXml, filter, logPrefix)
536
- ];
537
- }
538
- });
539
- })();
540
- }
541
- function createEmptyTrackingState() {
542
- return {
543
- impression: false,
544
- start: false,
545
- firstQuartile: false,
546
- midpoint: false,
547
- thirdQuartile: false,
548
- complete: false
549
- };
550
- }
551
374
  function firePixelWithRetry(url) {
552
375
  var retries = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 2, delayMs = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : 500, logPrefix = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : "[VastParser]";
553
376
  return _async_to_generator(function() {
@@ -670,16 +493,164 @@ function fireTrackingPixels(urls, sessionId) {
670
493
  }
671
494
  });
672
495
  }
673
- // src/sdk/vastManager.ts
674
- var VAST_TAG_URL = "https://pubads.g.doubleclick.net/gampad/ads?iu=/21821455290/Airy-Android&description_url=http%3A%2F%2Fairy.tv&tfcd=0&npa=0&sz=1x1%7C300x250%7C400x300%7C640x480&gdfp_req=1&unviewed_position_start=1&correlator=[placeholder]&vpos=preroll&output=vast&env=vp&vpmute=0&vpa=click";
675
- var DEFAULT_TIMEOUT_MS = 5e3;
676
- var MAX_RETRIES = 3;
677
- var RETRY_BACKOFF_MS = 1500;
678
- function createVastManager() {
679
- var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
680
- var _options_debug;
681
- var initialized = false;
682
- var debug = (_options_debug = options.debug) !== null && _options_debug !== void 0 ? _options_debug : false;
496
+ // src/sdk/adstormPlayer.ts
497
+ var SUPPORTED_VIDEO_EXTENSIONS = [
498
+ ".mp4",
499
+ ".webm",
500
+ ".ogg",
501
+ ".m3u8",
502
+ ".ts"
503
+ ];
504
+ var UNSUPPORTED_VIDEO_EXTENSIONS = [
505
+ ".flv",
506
+ ".f4v",
507
+ ".swf",
508
+ ".wmv",
509
+ ".avi",
510
+ ".mov",
511
+ ".mkv"
512
+ ];
513
+ var REQUEST_TIMEOUT_MS = 5e3;
514
+ var REQUEST_MAX_RETRIES = 3;
515
+ var REQUEST_RETRY_BACKOFF_MS = 1500;
516
+ var AD_LAYER_Z_INDEX = "30";
517
+ var COUNTDOWN_Z_INDEX = "31";
518
+ var STALL_TIMEOUT_MS = 8e3;
519
+ function getFileExtension(url) {
520
+ try {
521
+ var pathname = new URL(url, "http://dummy").pathname;
522
+ var lastDot = pathname.lastIndexOf(".");
523
+ if (lastDot === -1) return "";
524
+ return pathname.slice(lastDot).toLowerCase();
525
+ } catch (unused) {
526
+ var lastDot1 = url.lastIndexOf(".");
527
+ if (lastDot1 === -1) return "";
528
+ var ext = url.slice(lastDot1).split(/[?#]/)[0];
529
+ return (ext || "").toLowerCase();
530
+ }
531
+ }
532
+ function isUnsupportedFormat(url) {
533
+ var ext = getFileExtension(url);
534
+ return UNSUPPORTED_VIDEO_EXTENSIONS.indexOf(ext) !== -1;
535
+ }
536
+ function replaceFlvExtension(url) {
537
+ var ext = getFileExtension(url);
538
+ if (ext === ".flv") {
539
+ return url.replace(/\.flv(\?|$)/i, ".mp4$1");
540
+ }
541
+ return url;
542
+ }
543
+ function isSupportedFormat(url, mimeType) {
544
+ if (isUnsupportedFormat(url)) {
545
+ return false;
546
+ }
547
+ var ext = getFileExtension(url);
548
+ if (SUPPORTED_VIDEO_EXTENSIONS.indexOf(ext) !== -1) {
549
+ return true;
550
+ }
551
+ if (ext === "" || ext === ".") {
552
+ return mimeType.includes("video/mp4") || mimeType.includes("video/webm") || mimeType.includes("m3u8") || mimeType.includes("application/x-mpegurl");
553
+ }
554
+ return false;
555
+ }
556
+ function createAdStormPlayer(contentVideo, options) {
557
+ var licenseKey = options.licenseKey, _options_debug = options.debug, debug = _options_debug === void 0 ? false : _options_debug;
558
+ var adPlaying = false;
559
+ var originalMutedState = false;
560
+ var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
561
+ var listeners = /* @__PURE__ */ new Map();
562
+ var adVideoElement;
563
+ var adContainerEl;
564
+ var adCountdownEl;
565
+ var currentAd;
566
+ var destroyed = false;
567
+ var tornDown = false;
568
+ var continueLiveStreamDuringAds = false;
569
+ var sessionId;
570
+ var adStallTimerId;
571
+ var adCountdownTimerId;
572
+ var adHideTimerId;
573
+ var lastCountdownSecond = -1;
574
+ var adListenersBound = false;
575
+ var parentPositionOverridden = false;
576
+ var adHandlers = {
577
+ timeupdate: function timeupdate() {
578
+ if (!currentAd || !adVideoElement || destroyed || tornDown) return;
579
+ var progress = adVideoElement.currentTime / currentAd.duration;
580
+ if (progress >= 0.25 && !trackingFired.firstQuartile) {
581
+ trackingFired.firstQuartile = true;
582
+ fireTrackingPixels2(currentAd.trackingUrls.firstQuartile);
583
+ }
584
+ if (progress >= 0.5 && !trackingFired.midpoint) {
585
+ trackingFired.midpoint = true;
586
+ fireTrackingPixels2(currentAd.trackingUrls.midpoint);
587
+ }
588
+ if (progress >= 0.75 && !trackingFired.thirdQuartile) {
589
+ trackingFired.thirdQuartile = true;
590
+ fireTrackingPixels2(currentAd.trackingUrls.thirdQuartile);
591
+ }
592
+ updateAdCountdown();
593
+ },
594
+ playing: function playing() {
595
+ clearAdStallTimer();
596
+ if (!currentAd || trackingFired.start || destroyed || tornDown) return;
597
+ trackingFired.start = true;
598
+ fireTrackingPixels2(currentAd.trackingUrls.start);
599
+ startAdCountdown();
600
+ log("Ad started playing");
601
+ },
602
+ ended: function ended() {
603
+ if (!currentAd || trackingFired.complete || destroyed || tornDown) return;
604
+ trackingFired.complete = true;
605
+ fireTrackingPixels2(currentAd.trackingUrls.complete);
606
+ log("Ad completed");
607
+ handleAdComplete();
608
+ },
609
+ error: function error(e) {
610
+ if (destroyed || tornDown) return;
611
+ console.error("[AdStormPlayer] Ad video error:", e);
612
+ if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);
613
+ handleAdError();
614
+ },
615
+ waiting: function waiting() {
616
+ clearAdStallTimer();
617
+ adStallTimerId = setTimeout(function() {
618
+ adStallTimerId = void 0;
619
+ if (!adPlaying || destroyed || tornDown) return;
620
+ console.warn("[AdStormPlayer] Ad playback stalled too long");
621
+ handleAdError();
622
+ }, STALL_TIMEOUT_MS);
623
+ },
624
+ volumechange: function volumechange() {
625
+ if (!currentAd || !adVideoElement || destroyed || tornDown) return;
626
+ if (adVideoElement.muted || adVideoElement.volume <= 0) {
627
+ fireTrackingPixels2(currentAd.trackingUrls.mute);
628
+ } else {
629
+ fireTrackingPixels2(currentAd.trackingUrls.unmute);
630
+ }
631
+ },
632
+ pause: function pause() {
633
+ if (!currentAd || !adVideoElement || destroyed || tornDown) return;
634
+ if (!adVideoElement.ended) {
635
+ fireTrackingPixels2(currentAd.trackingUrls.pause);
636
+ }
637
+ },
638
+ play: function play() {
639
+ if (!currentAd || !adVideoElement || destroyed || tornDown) return;
640
+ if (adVideoElement.currentTime > 0) {
641
+ fireTrackingPixels2(currentAd.trackingUrls.resume);
642
+ }
643
+ }
644
+ };
645
+ var trackingFired = {
646
+ impression: false,
647
+ start: false,
648
+ firstQuartile: false,
649
+ midpoint: false,
650
+ thirdQuartile: false,
651
+ complete: false
652
+ };
653
+ var preloadSlots = /* @__PURE__ */ new Map();
683
654
  function log() {
684
655
  for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
685
656
  args[_key] = arguments[_key];
@@ -687,58 +658,340 @@ function createVastManager() {
687
658
  if (debug) {
688
659
  var _console;
689
660
  (_console = console).log.apply(_console, [
690
- "[VastManager]"
661
+ "[AdStormPlayer]"
691
662
  ].concat(_to_consumable_array(args)));
692
663
  }
693
664
  }
694
- function warn() {
695
- for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
696
- args[_key] = arguments[_key];
665
+ function emit(event, payload) {
666
+ var set = listeners.get(event);
667
+ if (!set) return;
668
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
669
+ try {
670
+ for(var _iterator = Array.from(set)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
671
+ var fn = _step.value;
672
+ try {
673
+ fn(payload);
674
+ } catch (error) {
675
+ console.warn("[AdStormPlayer] Error in event listener for ".concat(event, ":"), error);
676
+ }
677
+ }
678
+ } catch (err) {
679
+ _didIteratorError = true;
680
+ _iteratorError = err;
681
+ } finally{
682
+ try {
683
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
684
+ _iterator.return();
685
+ }
686
+ } finally{
687
+ if (_didIteratorError) {
688
+ throw _iteratorError;
689
+ }
690
+ }
697
691
  }
698
- var _console;
699
- (_console = console).warn.apply(_console, [
700
- "[VastManager]"
701
- ].concat(_to_consumable_array(args)));
702
692
  }
703
- function initialize() {
704
- return _async_to_generator(function() {
705
- return _ts_generator(this, function(_state) {
706
- if (initialized) return [
707
- 2
708
- ];
709
- initialized = true;
710
- log("Initialized, VAST tag URL:", VAST_TAG_URL.split("?")[0]);
711
- return [
712
- 2
713
- ];
693
+ function fireTrackingPixels2(urls) {
694
+ fireTrackingPixels(urls, sessionId, "[AdStormPlayer]");
695
+ }
696
+ function clearAdStallTimer() {
697
+ if (adStallTimerId) {
698
+ clearTimeout(adStallTimerId);
699
+ adStallTimerId = void 0;
700
+ }
701
+ }
702
+ function clearAdCountdownTimer() {
703
+ if (adCountdownTimerId) {
704
+ clearInterval(adCountdownTimerId);
705
+ adCountdownTimerId = void 0;
706
+ }
707
+ lastCountdownSecond = -1;
708
+ }
709
+ function updateAdCountdown() {
710
+ if (!adCountdownEl || !adVideoElement || !currentAd || !adPlaying) return;
711
+ var remainingSec = Math.max(0, Math.ceil((currentAd.duration || 0) - adVideoElement.currentTime));
712
+ if (remainingSec === lastCountdownSecond) return;
713
+ lastCountdownSecond = remainingSec;
714
+ adCountdownEl.textContent = "Ad ".concat(remainingSec, "s");
715
+ emit("ad_countdown", {
716
+ remainingSec: remainingSec,
717
+ durationSec: currentAd.duration,
718
+ currentTimeSec: adVideoElement.currentTime
719
+ });
720
+ }
721
+ function startAdCountdown() {
722
+ clearAdCountdownTimer();
723
+ updateAdCountdown();
724
+ adCountdownTimerId = setInterval(updateAdCountdown, 250);
725
+ }
726
+ function generateSessionId() {
727
+ return "adstorm-".concat(Date.now(), "-").concat(Math.random().toString(36).slice(2, 10));
728
+ }
729
+ function bindAdEventListeners() {
730
+ if (!adVideoElement || adListenersBound) return;
731
+ adVideoElement.addEventListener("timeupdate", adHandlers.timeupdate);
732
+ adVideoElement.addEventListener("playing", adHandlers.playing);
733
+ adVideoElement.addEventListener("ended", adHandlers.ended);
734
+ adVideoElement.addEventListener("error", adHandlers.error);
735
+ adVideoElement.addEventListener("waiting", adHandlers.waiting);
736
+ adVideoElement.addEventListener("volumechange", adHandlers.volumechange);
737
+ adVideoElement.addEventListener("pause", adHandlers.pause);
738
+ adVideoElement.addEventListener("play", adHandlers.play);
739
+ adListenersBound = true;
740
+ }
741
+ function unbindAdEventListeners() {
742
+ if (!adVideoElement || !adListenersBound) return;
743
+ adVideoElement.removeEventListener("timeupdate", adHandlers.timeupdate);
744
+ adVideoElement.removeEventListener("playing", adHandlers.playing);
745
+ adVideoElement.removeEventListener("ended", adHandlers.ended);
746
+ adVideoElement.removeEventListener("error", adHandlers.error);
747
+ adVideoElement.removeEventListener("waiting", adHandlers.waiting);
748
+ adVideoElement.removeEventListener("volumechange", adHandlers.volumechange);
749
+ adVideoElement.removeEventListener("pause", adHandlers.pause);
750
+ adVideoElement.removeEventListener("play", adHandlers.play);
751
+ adListenersBound = false;
752
+ }
753
+ function teardownCurrentPlayback() {
754
+ unbindAdEventListeners();
755
+ clearAdStallTimer();
756
+ clearAdCountdownTimer();
757
+ if (!adVideoElement) return;
758
+ adVideoElement.pause();
759
+ adVideoElement.removeAttribute("src");
760
+ adVideoElement.load();
761
+ }
762
+ function buildVastUrl(durationSeconds, metadata) {
763
+ var baseUrl = "https://adstorm.co/api-adstorm-dev/adstorm/vast/".concat(licenseKey, "/pod");
764
+ var defaultMetadata = {
765
+ video: {
766
+ codec: "h264",
767
+ width: contentVideo.videoWidth || 1280,
768
+ height: contentVideo.videoHeight || 720,
769
+ fps: 29.97,
770
+ bitrate: 5e3,
771
+ profile: "high",
772
+ pix_fmt: "yuv420p",
773
+ has_b_frames: 0
774
+ },
775
+ audio: {
776
+ codec: "aac",
777
+ sample_rate: 48e3,
778
+ bitrate: 128
779
+ }
780
+ };
781
+ var finalMetadata = metadata || defaultMetadata;
782
+ var metadataStr = encodeURIComponent(JSON.stringify(finalMetadata));
783
+ return "".concat(baseUrl, "?duration=").concat(Math.ceil(durationSeconds), "&metadata=").concat(metadataStr);
784
+ }
785
+ function parseVastXml(xmlString) {
786
+ var ads = [];
787
+ try {
788
+ var parser = new DOMParser();
789
+ var xmlDoc = parser.parseFromString(xmlString, "text/xml");
790
+ var parserError = xmlDoc.querySelector("parsererror");
791
+ if (parserError) {
792
+ console.error("[AdStormPlayer] XML parsing error:", parserError.textContent);
793
+ return [];
794
+ }
795
+ var adElements = xmlDoc.querySelectorAll("Ad");
796
+ adElements.forEach(function(adElement) {
797
+ var _adElement_querySelector, _adElement_querySelector1, _adElement_querySelector_textContent, _adElement_querySelector2;
798
+ var adId = adElement.getAttribute("id") || "unknown";
799
+ var title = ((_adElement_querySelector = adElement.querySelector("AdTitle")) === null || _adElement_querySelector === void 0 ? void 0 : _adElement_querySelector.textContent) || "Ad";
800
+ var durationText = ((_adElement_querySelector1 = adElement.querySelector("Duration")) === null || _adElement_querySelector1 === void 0 ? void 0 : _adElement_querySelector1.textContent) || "00:00:30";
801
+ var durationParts = durationText.split(":");
802
+ var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseFloat(durationParts[2] || "0");
803
+ var mediaFileElements = adElement.querySelectorAll("MediaFile");
804
+ var mediaFiles = [];
805
+ mediaFileElements.forEach(function(mf) {
806
+ var _mf_textContent;
807
+ var type = mf.getAttribute("type") || "";
808
+ var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
809
+ var width = parseInt(mf.getAttribute("width") || "1920", 10);
810
+ var height = parseInt(mf.getAttribute("height") || "1080", 10);
811
+ var bitrate = mf.getAttribute("bitrate") ? parseInt(mf.getAttribute("bitrate"), 10) : void 0;
812
+ if (!url) {
813
+ log("Skipping empty MediaFile URL");
814
+ return;
815
+ }
816
+ var originalUrl = url;
817
+ url = replaceFlvExtension(url);
818
+ if (url !== originalUrl) {
819
+ log("Converted FLV to MP4: ".concat(originalUrl, " -> ").concat(url));
820
+ }
821
+ if (isUnsupportedFormat(url)) {
822
+ var ext = getFileExtension(url);
823
+ log("Skipping unsupported format: ".concat(url, " (extension: ").concat(ext, ", declared type: ").concat(type, ")"));
824
+ return;
825
+ }
826
+ if (isSupportedFormat(url, type)) {
827
+ mediaFiles.push({
828
+ url: url,
829
+ type: type,
830
+ width: width,
831
+ height: height,
832
+ bitrate: bitrate
833
+ });
834
+ log("Found media file: ".concat(url, " (").concat(type, ", ").concat(width, "x").concat(height, ")"));
835
+ } else {
836
+ log("Skipping incompatible media file: ".concat(url, " (type: ").concat(type, ")"));
837
+ }
838
+ });
839
+ if (mediaFiles.length === 0) {
840
+ log("No valid media files found in ad:", adId);
841
+ return;
842
+ }
843
+ var trackingUrls = {
844
+ impression: [],
845
+ start: [],
846
+ firstQuartile: [],
847
+ midpoint: [],
848
+ thirdQuartile: [],
849
+ complete: [],
850
+ mute: [],
851
+ unmute: [],
852
+ pause: [],
853
+ resume: [],
854
+ error: []
855
+ };
856
+ adElement.querySelectorAll("Impression").forEach(function(el) {
857
+ var _el_textContent;
858
+ var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
859
+ if (url) trackingUrls.impression.push(url);
860
+ });
861
+ adElement.querySelectorAll("Tracking").forEach(function(el) {
862
+ var _el_textContent;
863
+ var event = el.getAttribute("event");
864
+ var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
865
+ if (event && url) {
866
+ var eventKey = event;
867
+ if (trackingUrls[eventKey]) {
868
+ trackingUrls[eventKey].push(url);
869
+ }
870
+ }
871
+ });
872
+ var clickThrough = (_adElement_querySelector2 = adElement.querySelector("ClickThrough")) === null || _adElement_querySelector2 === void 0 ? void 0 : (_adElement_querySelector_textContent = _adElement_querySelector2.textContent) === null || _adElement_querySelector_textContent === void 0 ? void 0 : _adElement_querySelector_textContent.trim();
873
+ ads.push({
874
+ id: adId,
875
+ title: title,
876
+ duration: duration,
877
+ mediaFiles: mediaFiles,
878
+ trackingUrls: trackingUrls,
879
+ clickThrough: clickThrough
880
+ });
881
+ log("Parsed ad: ".concat(title, ", duration: ").concat(duration, "s, media files: ").concat(mediaFiles.length));
714
882
  });
715
- })();
883
+ } catch (error) {
884
+ console.error("[AdStormPlayer] Error parsing VAST XML:", error);
885
+ }
886
+ return ads;
887
+ }
888
+ function selectBestMediaFile(mediaFiles) {
889
+ if (mediaFiles.length === 0) return null;
890
+ if (mediaFiles.length === 1) return mediaFiles[0];
891
+ var mp4Files = mediaFiles.filter(function(mf) {
892
+ return mf.type.includes("video/mp4");
893
+ });
894
+ var candidates = mp4Files.length > 0 ? mp4Files : mediaFiles;
895
+ var targetWidth = contentVideo.videoWidth || 1280;
896
+ var targetHeight = contentVideo.videoHeight || 720;
897
+ candidates.sort(function(a, b) {
898
+ var diffA = Math.abs(a.width - targetWidth) + Math.abs(a.height - targetHeight);
899
+ var diffB = Math.abs(b.width - targetWidth) + Math.abs(b.height - targetHeight);
900
+ return diffA - diffB;
901
+ });
902
+ return candidates[0] || null;
903
+ }
904
+ function createAdVideoElement() {
905
+ var video = document.createElement("video");
906
+ video.style.position = "absolute";
907
+ video.style.left = "0";
908
+ video.style.top = "0";
909
+ video.style.width = "100%";
910
+ video.style.height = "100%";
911
+ video.style.objectFit = "contain";
912
+ video.style.backgroundColor = "#000";
913
+ video.style.zIndex = "1";
914
+ video.playsInline = true;
915
+ video.preload = "auto";
916
+ video.muted = originalMutedState;
917
+ video.volume = originalMutedState ? 0 : originalVolume;
918
+ return video;
919
+ }
920
+ function setAdPlayingFlag(isPlaying) {
921
+ if (isPlaying) {
922
+ contentVideo.dataset.stormcloudAdPlaying = "true";
923
+ } else {
924
+ delete contentVideo.dataset.stormcloudAdPlaying;
925
+ }
926
+ }
927
+ function setupAdEventListeners() {
928
+ bindAdEventListeners();
929
+ }
930
+ function handleAdComplete() {
931
+ if (destroyed || tornDown) return;
932
+ log("Handling ad completion");
933
+ adPlaying = false;
934
+ setAdPlayingFlag(false);
935
+ clearAdStallTimer();
936
+ clearAdCountdownTimer();
937
+ if (adContainerEl) {
938
+ adContainerEl.style.opacity = "0";
939
+ adHideTimerId = setTimeout(function() {
940
+ if (adContainerEl) {
941
+ adContainerEl.style.display = "none";
942
+ adContainerEl.style.pointerEvents = "none";
943
+ }
944
+ }, 300);
945
+ }
946
+ contentVideo.style.visibility = "visible";
947
+ contentVideo.style.opacity = "1";
948
+ contentVideo.muted = originalMutedState;
949
+ contentVideo.volume = originalVolume;
950
+ currentAd = void 0;
951
+ emit("content_resume");
952
+ emit("all_ads_completed");
953
+ }
954
+ function handleAdError() {
955
+ if (destroyed || tornDown) return;
956
+ log("Handling ad error");
957
+ if (!adPlaying) return;
958
+ adPlaying = false;
959
+ setAdPlayingFlag(false);
960
+ clearAdStallTimer();
961
+ clearAdCountdownTimer();
962
+ contentVideo.muted = originalMutedState;
963
+ contentVideo.volume = originalVolume;
964
+ contentVideo.style.visibility = "visible";
965
+ contentVideo.style.opacity = "1";
966
+ if (adContainerEl) {
967
+ adContainerEl.style.display = "none";
968
+ adContainerEl.style.pointerEvents = "none";
969
+ }
970
+ currentAd = void 0;
971
+ emit("ad_error");
972
+ emit("content_resume");
716
973
  }
717
- function requestBids(_context) {
974
+ function fetchVastOnce(durationSeconds) {
718
975
  return _async_to_generator(function() {
719
- var correlator, url, controller, timeoutId, _ref, _ref1, _vastAd_mediaFiles_, _vastAd_mediaFiles_1, fetchOptions, response, vastXml, vastAd, bid, error;
976
+ var vastUrl, controller, timeoutId, requestInit, response, xmlText;
720
977
  return _ts_generator(this, function(_state) {
721
978
  switch(_state.label){
722
979
  case 0:
723
- if (!initialized) {
724
- throw new Error("VastManager not initialized. Call initialize() first.");
725
- }
726
- correlator = Math.floor(Math.random() * 1e12).toString();
727
- url = VAST_TAG_URL.replace("[placeholder]", correlator);
728
- log("Fetching VAST tag, correlator:", correlator);
980
+ vastUrl = buildVastUrl(durationSeconds);
981
+ log("Fetching VAST from:", vastUrl);
729
982
  controller = typeof AbortController !== "undefined" ? new AbortController() : null;
730
983
  timeoutId = setTimeout(function() {
731
984
  return controller === null || controller === void 0 ? void 0 : controller.abort();
732
- }, DEFAULT_TIMEOUT_MS);
985
+ }, REQUEST_TIMEOUT_MS);
733
986
  _state.label = 1;
734
987
  case 1:
735
988
  _state.trys.push([
736
989
  1,
737
- 4,
738
990
  ,
991
+ 4,
739
992
  5
740
993
  ]);
741
- fetchOptions = {
994
+ requestInit = {
742
995
  method: "GET",
743
996
  mode: "cors",
744
997
  credentials: "omit",
@@ -747,62 +1000,34 @@ function createVastManager() {
747
1000
  },
748
1001
  referrerPolicy: "no-referrer-when-downgrade"
749
1002
  };
750
- if (controller) fetchOptions.signal = controller.signal;
1003
+ if (controller) {
1004
+ requestInit.signal = controller.signal;
1005
+ }
751
1006
  return [
752
1007
  4,
753
- fetch(url, fetchOptions)
1008
+ fetch(vastUrl, requestInit)
754
1009
  ];
755
1010
  case 2:
756
1011
  response = _state.sent();
757
- clearTimeout(timeoutId);
758
1012
  if (!response.ok) {
759
- throw new Error("VAST request returned HTTP ".concat(response.status));
1013
+ throw new Error("Failed to fetch VAST: ".concat(response.status, " ").concat(response.statusText));
760
1014
  }
761
1015
  return [
762
1016
  4,
763
1017
  response.text()
764
1018
  ];
765
1019
  case 3:
766
- vastXml = _state.sent();
767
- log("VAST XML received, length:", vastXml.length);
768
- vastAd = parseVastXml(vastXml, "mp4-first", "[VastManager]");
769
- if (!vastAd) {
770
- log("VAST parsed but no usable ad found");
771
- return [
772
- 2,
773
- []
774
- ];
775
- }
776
- log("Ad parsed: id=".concat(vastAd.id, ", duration=").concat(vastAd.duration, "s, mediaFiles=").concat(vastAd.mediaFiles.length));
777
- bid = {
778
- bidder: "vast-direct",
779
- cpm: 0,
780
- vastXml: vastXml,
781
- width: (_ref = (_vastAd_mediaFiles_ = vastAd.mediaFiles[0]) === null || _vastAd_mediaFiles_ === void 0 ? void 0 : _vastAd_mediaFiles_.width) !== null && _ref !== void 0 ? _ref : 0,
782
- height: (_ref1 = (_vastAd_mediaFiles_1 = vastAd.mediaFiles[0]) === null || _vastAd_mediaFiles_1 === void 0 ? void 0 : _vastAd_mediaFiles_1.height) !== null && _ref1 !== void 0 ? _ref1 : 0,
783
- adId: vastAd.id,
784
- impId: correlator,
785
- creativeId: vastAd.id,
786
- currency: "USD",
787
- durationSec: vastAd.duration
788
- };
1020
+ xmlText = _state.sent();
1021
+ log("VAST response received, length:", xmlText.length);
789
1022
  return [
790
1023
  2,
791
- [
792
- bid
793
- ]
1024
+ parseVastXml(xmlText)
794
1025
  ];
795
1026
  case 4:
796
- error = _state.sent();
797
1027
  clearTimeout(timeoutId);
798
- if ((error === null || error === void 0 ? void 0 : error.name) === "AbortError") {
799
- warn("VAST request timed out after ".concat(DEFAULT_TIMEOUT_MS, "ms"));
800
- return [
801
- 2,
802
- []
803
- ];
804
- }
805
- throw error;
1028
+ return [
1029
+ 7
1030
+ ];
806
1031
  case 5:
807
1032
  return [
808
1033
  2
@@ -811,14 +1036,14 @@ function createVastManager() {
811
1036
  });
812
1037
  })();
813
1038
  }
814
- function requestBidsUntilResponse(context) {
1039
+ function fetchVast(durationSeconds) {
815
1040
  return _async_to_generator(function() {
816
1041
  var _loop, lastError, attempt, _ret;
817
1042
  return _ts_generator(this, function(_state) {
818
1043
  switch(_state.label){
819
1044
  case 0:
820
1045
  _loop = function(attempt) {
821
- var bids, err, delay;
1046
+ var ads, error, delay;
822
1047
  return _ts_generator(this, function(_state) {
823
1048
  switch(_state.label){
824
1049
  case 0:
@@ -830,39 +1055,39 @@ function createVastManager() {
830
1055
  ]);
831
1056
  return [
832
1057
  4,
833
- requestBids(context)
1058
+ fetchVastOnce(durationSeconds)
834
1059
  ];
835
1060
  case 1:
836
- bids = _state.sent();
837
- if (bids.length > 0) {
838
- log("requestBidsUntilResponse: got ".concat(bids.length, " ad(s) on attempt ").concat(attempt));
839
- return [
840
- 2,
841
- {
842
- v: bids
843
- }
844
- ];
845
- }
846
- log("requestBidsUntilResponse: no ads on attempt ".concat(attempt, "/").concat(MAX_RETRIES));
1061
+ ads = _state.sent();
1062
+ if (ads.length > 0) return [
1063
+ 2,
1064
+ {
1065
+ v: ads
1066
+ }
1067
+ ];
1068
+ log("No ad returned from VAST on attempt ".concat(attempt, "/").concat(REQUEST_MAX_RETRIES));
847
1069
  return [
848
1070
  3,
849
1071
  3
850
1072
  ];
851
1073
  case 2:
852
- err = _state.sent();
853
- lastError = err;
854
- warn("requestBidsUntilResponse: attempt ".concat(attempt, "/").concat(MAX_RETRIES, " failed:"), err);
1074
+ error = _state.sent();
1075
+ lastError = error;
1076
+ if ((error === null || error === void 0 ? void 0 : error.name) === "AbortError") {
1077
+ console.warn("[AdStormPlayer] VAST request timed out (".concat(REQUEST_TIMEOUT_MS, "ms), attempt ").concat(attempt, "/").concat(REQUEST_MAX_RETRIES));
1078
+ } else {
1079
+ console.warn("[AdStormPlayer] VAST request failed on attempt ".concat(attempt, "/").concat(REQUEST_MAX_RETRIES, ":"), error);
1080
+ }
855
1081
  return [
856
1082
  3,
857
1083
  3
858
1084
  ];
859
1085
  case 3:
860
- if (!(attempt < MAX_RETRIES)) return [
1086
+ if (!(attempt < REQUEST_MAX_RETRIES)) return [
861
1087
  3,
862
1088
  5
863
1089
  ];
864
- delay = RETRY_BACKOFF_MS * attempt;
865
- log("requestBidsUntilResponse: waiting ".concat(delay, "ms before retry"));
1090
+ delay = REQUEST_RETRY_BACKOFF_MS * attempt;
866
1091
  return [
867
1092
  4,
868
1093
  new Promise(function(resolve) {
@@ -879,13 +1104,10 @@ function createVastManager() {
879
1104
  }
880
1105
  });
881
1106
  };
882
- if (!initialized) {
883
- throw new Error("VastManager not initialized. Call initialize() first.");
884
- }
885
1107
  attempt = 1;
886
1108
  _state.label = 1;
887
1109
  case 1:
888
- if (!(attempt <= MAX_RETRIES)) return [
1110
+ if (!(attempt <= REQUEST_MAX_RETRIES)) return [
889
1111
  3,
890
1112
  4
891
1113
  ];
@@ -907,7 +1129,9 @@ function createVastManager() {
907
1129
  1
908
1130
  ];
909
1131
  case 4:
910
- if (_instanceof(lastError, Error)) throw lastError;
1132
+ if (_instanceof(lastError, Error)) {
1133
+ throw lastError;
1134
+ }
911
1135
  return [
912
1136
  2,
913
1137
  []
@@ -916,946 +1140,468 @@ function createVastManager() {
916
1140
  });
917
1141
  })();
918
1142
  }
919
- function destroy() {
920
- initialized = false;
921
- log("Destroyed");
922
- }
923
- return {
924
- initialize: initialize,
925
- requestBids: requestBids,
926
- requestBidsUntilResponse: requestBidsUntilResponse,
927
- destroy: destroy,
928
- get isInitialized () {
929
- return initialized;
930
- }
931
- };
932
- }
933
- // src/sdk/vastAdLayer.ts
934
- import Hls from "hls.js";
935
- var LOG = "[VastAdLayer]";
936
- function resolveBidToVastAd(winner, logPrefix) {
937
- if (winner.vastXml) {
938
- var ad = parseVastXml(winner.vastXml, "mp4-first", logPrefix);
939
- return Promise.resolve(ad);
940
- }
941
- if (winner.vastUrl) {
942
- return fetchAndParseVastAd(winner.vastUrl, "mp4-first", logPrefix);
943
- }
944
- return Promise.resolve(null);
945
- }
946
- function createVastAdLayer(contentVideo, options) {
947
- var _ref, _ref1, _ref2, _ref3, _ref4;
948
- var adPlaying = false;
949
- var originalMutedState = false;
950
- var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
951
- var listeners = /* @__PURE__ */ new Map();
952
- var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
953
- var continueLiveStreamDuringAds = (_ref = options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) !== null && _ref !== void 0 ? _ref : false;
954
- var smartTVMode = (_ref1 = options === null || options === void 0 ? void 0 : options.smartTVMode) !== null && _ref1 !== void 0 ? _ref1 : false;
955
- var singleElementMode = (_ref2 = options === null || options === void 0 ? void 0 : options.singleElementMode) !== null && _ref2 !== void 0 ? _ref2 : false;
956
- var forceMP4Ads = (_ref3 = options === null || options === void 0 ? void 0 : options.forceMP4Ads) !== null && _ref3 !== void 0 ? _ref3 : smartTVMode || singleElementMode;
957
- var debug = (_ref4 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref4 !== void 0 ? _ref4 : false;
958
- var adVideoElement;
959
- var adHls;
960
- var adContainerEl;
961
- var currentAd;
962
- var sessionId;
963
- var destroyed = false;
964
- var tornDown = false;
965
- var trackingFired = createEmptyTrackingState();
966
- var adStallTimerId;
967
- var currentAdEventHandlers;
968
- var preloadSlots = /* @__PURE__ */ new Map();
969
- function emit(event, payload) {
970
- var set = listeners.get(event);
971
- if (!set) return;
972
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
973
- try {
974
- for(var _iterator = Array.from(set)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
975
- var fn = _step.value;
976
- try {
977
- fn(payload);
978
- } catch (error) {
979
- console.warn("".concat(LOG, " Error in event listener for ").concat(event, ":"), error);
980
- }
981
- }
982
- } catch (err) {
983
- _didIteratorError = true;
984
- _iteratorError = err;
985
- } finally{
986
- try {
987
- if (!_iteratorNormalCompletion && _iterator.return != null) {
988
- _iterator.return();
989
- }
990
- } finally{
991
- if (_didIteratorError) {
992
- throw _iteratorError;
993
- }
994
- }
995
- }
996
- }
997
- function generateSessionId() {
998
- return "session-".concat(Date.now(), "-").concat(Math.random().toString(36).substr(2, 9));
999
- }
1000
- function fireTrackingPixels2(urls) {
1001
- fireTrackingPixels(urls, sessionId, LOG);
1002
- }
1003
- function getMainStreamQuality() {
1004
- if (!(mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.levels)) return null;
1005
- var currentLevel = mainHlsInstance.currentLevel;
1006
- if (currentLevel === -1 || !mainHlsInstance.levels[currentLevel]) {
1007
- var autoLevel = mainHlsInstance.loadLevel;
1008
- if (autoLevel !== -1 && mainHlsInstance.levels[autoLevel]) {
1009
- var level2 = mainHlsInstance.levels[autoLevel];
1010
- return {
1011
- width: level2.width || 1920,
1012
- height: level2.height || 1080,
1013
- bitrate: level2.bitrate || 5e6
1014
- };
1015
- }
1016
- return null;
1143
+ function getDurationSecondsFromContext(requestContext) {
1144
+ var _ctx_remainingBreakSec;
1145
+ if (!requestContext || (typeof requestContext === "undefined" ? "undefined" : _type_of(requestContext)) !== "object") {
1146
+ return 30;
1017
1147
  }
1018
- var level = mainHlsInstance.levels[currentLevel];
1019
- return {
1020
- width: level.width || 1920,
1021
- height: level.height || 1080,
1022
- bitrate: level.bitrate || 5e6
1023
- };
1024
- }
1025
- function selectBestMediaFile(mediaFiles) {
1026
- var _ref;
1027
- var _scoredFiles_;
1028
- if (mediaFiles.length === 0) throw new Error("No media files available");
1029
- var candidates = mediaFiles;
1030
- if (forceMP4Ads) {
1031
- var mp4Only = candidates.filter(function(f) {
1032
- return !isHlsMediaFile(f);
1033
- });
1034
- if (mp4Only.length > 0) {
1035
- candidates = mp4Only;
1036
- if (debug) console.log("".concat(LOG, " forceMP4Ads: filtered to ").concat(mp4Only.length, " MP4-only file(s)"));
1037
- } else if (debug) {
1038
- console.warn("".concat(LOG, " forceMP4Ads: no MP4 files available, falling back to all media files"));
1039
- }
1040
- } else {
1041
- var mp4Only1 = candidates.filter(function(f) {
1042
- return !isHlsMediaFile(f);
1043
- });
1044
- if (mp4Only1.length > 0) {
1045
- candidates = mp4Only1;
1046
- if (debug) console.log("".concat(LOG, " Preferring ").concat(mp4Only1.length, " MP4 file(s) over HLS (mp4-first)"));
1047
- }
1048
- }
1049
- var firstFile = candidates[0];
1050
- if (candidates.length === 1) return firstFile;
1051
- var mainQuality = getMainStreamQuality();
1052
- if (!mainQuality) {
1053
- if (debug) console.log("".concat(LOG, " No main stream quality info, using first media file"));
1054
- return firstFile;
1148
+ var ctx = requestContext;
1149
+ var value = (_ctx_remainingBreakSec = ctx.remainingBreakSec) !== null && _ctx_remainingBreakSec !== void 0 ? _ctx_remainingBreakSec : ctx.breakDurationSec;
1150
+ if (typeof value !== "number" || Number.isNaN(value)) {
1151
+ return 30;
1055
1152
  }
1056
- var scoredFiles = candidates.map(function(file) {
1057
- var widthDiff = Math.abs(file.width - mainQuality.width);
1058
- var heightDiff = Math.abs(file.height - mainQuality.height);
1059
- var resolutionDiff = widthDiff + heightDiff;
1060
- var fileBitrate = (file.bitrate || 5e3) * 1e3;
1061
- var bitrateDiff = Math.abs(fileBitrate - mainQuality.bitrate);
1062
- var score = resolutionDiff * 2 + bitrateDiff / 1e3;
1063
- return {
1064
- file: file,
1065
- score: score
1066
- };
1067
- });
1068
- scoredFiles.sort(function(a, b) {
1069
- return a.score - b.score;
1070
- });
1071
- return (_ref = (_scoredFiles_ = scoredFiles[0]) === null || _scoredFiles_ === void 0 ? void 0 : _scoredFiles_.file) !== null && _ref !== void 0 ? _ref : firstFile;
1072
- }
1073
- function isHlsMediaFile(file) {
1074
- return file.type === "application/x-mpegURL" || file.type.includes("m3u8");
1075
- }
1076
- function createAdVideoElement() {
1077
- var video = document.createElement("video");
1078
- video.style.position = "absolute";
1079
- video.style.left = "0";
1080
- video.style.top = "0";
1081
- video.style.width = "100%";
1082
- video.style.height = "100%";
1083
- video.style.objectFit = "contain";
1084
- video.style.backgroundColor = "#000";
1085
- video.playsInline = true;
1086
- video.muted = false;
1087
- video.volume = 1;
1088
- return video;
1153
+ return Math.max(1, Math.ceil(value));
1089
1154
  }
1090
- function clearAdStallTimer() {
1091
- if (adStallTimerId != null) {
1092
- clearTimeout(adStallTimerId);
1093
- adStallTimerId = void 0;
1094
- }
1095
- }
1096
- function removeAdEventListeners() {
1097
- clearAdStallTimer();
1098
- if (!currentAdEventHandlers || !adVideoElement) return;
1099
- var el = adVideoElement;
1100
- el.removeEventListener("timeupdate", currentAdEventHandlers.timeupdate);
1101
- el.removeEventListener("playing", currentAdEventHandlers.playing);
1102
- el.removeEventListener("ended", currentAdEventHandlers.ended);
1103
- el.removeEventListener("error", currentAdEventHandlers.error);
1104
- el.removeEventListener("volumechange", currentAdEventHandlers.volumechange);
1105
- el.removeEventListener("pause", currentAdEventHandlers.pause);
1106
- el.removeEventListener("play", currentAdEventHandlers.play);
1107
- el.removeEventListener("waiting", currentAdEventHandlers.waiting);
1108
- currentAdEventHandlers = void 0;
1109
- }
1110
- function setupAdEventListeners() {
1111
- if (!adVideoElement) return;
1112
- removeAdEventListeners();
1113
- var handlers = {
1114
- timeupdate: function timeupdate() {
1115
- var ad = currentAd;
1116
- if (!ad || !adVideoElement) return;
1117
- var progress = adVideoElement.currentTime / ad.duration;
1118
- if (progress >= 0.25 && !trackingFired.firstQuartile) {
1119
- trackingFired.firstQuartile = true;
1120
- fireTrackingPixels2(ad.trackingUrls.firstQuartile);
1121
- }
1122
- if (progress >= 0.5 && !trackingFired.midpoint) {
1123
- trackingFired.midpoint = true;
1124
- fireTrackingPixels2(ad.trackingUrls.midpoint);
1125
- }
1126
- if (progress >= 0.75 && !trackingFired.thirdQuartile) {
1127
- trackingFired.thirdQuartile = true;
1128
- fireTrackingPixels2(ad.trackingUrls.thirdQuartile);
1129
- }
1130
- },
1131
- playing: function playing() {
1132
- clearAdStallTimer();
1133
- var ad = currentAd;
1134
- if (!ad || trackingFired.start) return;
1135
- trackingFired.start = true;
1136
- fireTrackingPixels2(ad.trackingUrls.start);
1137
- if (debug) console.log("".concat(LOG, " Ad started playing"));
1138
- },
1139
- ended: function ended() {
1140
- if (tornDown || !currentAd || trackingFired.complete) return;
1141
- trackingFired.complete = true;
1142
- fireTrackingPixels2(currentAd.trackingUrls.complete);
1143
- if (debug) console.log("".concat(LOG, " Ad completed"));
1144
- handleAdComplete();
1145
- },
1146
- error: function error(e) {
1147
- if (tornDown) return;
1148
- console.error("".concat(LOG, " Ad video error:"), e);
1149
- if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);
1150
- handleAdError();
1151
- },
1152
- volumechange: function volumechange() {
1153
- if (!currentAd || !adVideoElement) return;
1154
- if (adVideoElement.muted) {
1155
- fireTrackingPixels2(currentAd.trackingUrls.mute);
1156
- } else {
1157
- fireTrackingPixels2(currentAd.trackingUrls.unmute);
1158
- }
1159
- },
1160
- pause: function pause() {
1161
- if (currentAd && adVideoElement && !adVideoElement.ended) {
1162
- fireTrackingPixels2(currentAd.trackingUrls.pause);
1163
- }
1164
- },
1165
- play: function play() {
1166
- if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1167
- fireTrackingPixels2(currentAd.trackingUrls.resume);
1168
- }
1169
- },
1170
- waiting: function waiting() {
1171
- clearAdStallTimer();
1172
- adStallTimerId = setTimeout(function() {
1173
- adStallTimerId = void 0;
1174
- if (adPlaying) {
1175
- if (debug) console.warn("".concat(LOG, " Ad video stalled for too long, treating as error"));
1176
- handleAdError();
1177
- }
1178
- }, 8e3);
1179
- }
1180
- };
1181
- adVideoElement.addEventListener("timeupdate", handlers.timeupdate);
1182
- adVideoElement.addEventListener("playing", handlers.playing);
1183
- adVideoElement.addEventListener("ended", handlers.ended);
1184
- adVideoElement.addEventListener("error", handlers.error);
1185
- adVideoElement.addEventListener("volumechange", handlers.volumechange);
1186
- adVideoElement.addEventListener("pause", handlers.pause);
1187
- adVideoElement.addEventListener("play", handlers.play);
1188
- adVideoElement.addEventListener("waiting", handlers.waiting);
1189
- currentAdEventHandlers = handlers;
1190
- }
1191
- function setAdPlayingFlag(isPlaying) {
1192
- if (isPlaying) {
1193
- contentVideo.dataset.stormcloudAdPlaying = "true";
1194
- } else {
1195
- delete contentVideo.dataset.stormcloudAdPlaying;
1196
- }
1197
- }
1198
- function handleAdComplete() {
1199
- if (tornDown) return;
1200
- clearAdStallTimer();
1201
- if (debug) console.log("".concat(LOG, " Handling ad completion"));
1202
- adPlaying = false;
1203
- setAdPlayingFlag(false);
1204
- if (adContainerEl) {
1205
- adContainerEl.style.display = "none";
1206
- adContainerEl.style.pointerEvents = "none";
1207
- }
1208
- emit("ad_impression");
1209
- emit("content_resume");
1210
- }
1211
- function handleAdError() {
1212
- if (tornDown) return;
1213
- if (!adPlaying) return;
1214
- clearAdStallTimer();
1215
- if (debug) console.log("".concat(LOG, " Handling ad error"));
1216
- adPlaying = false;
1217
- setAdPlayingFlag(false);
1218
- if (adContainerEl) {
1219
- adContainerEl.style.display = "none";
1220
- adContainerEl.style.pointerEvents = "none";
1221
- }
1222
- emit("ad_error");
1223
- }
1224
- function teardownCurrentPlayback() {
1225
- removeAdEventListeners();
1226
- if (adHls) {
1227
- adHls.destroy();
1228
- adHls = void 0;
1229
- }
1230
- if (adVideoElement) {
1231
- if (singleElementMode && adVideoElement === contentVideo) {
1232
- contentVideo.pause();
1233
- } else {
1234
- adVideoElement.pause();
1235
- adVideoElement.removeAttribute("src");
1236
- adVideoElement.load();
1237
- }
1238
- }
1239
- }
1240
- function startNativePlayback(mediaFile) {
1241
- if (!adVideoElement) return;
1242
- if (debug) console.log("".concat(LOG, " Starting native MP4 playback: ").concat(mediaFile.url));
1243
- adVideoElement.src = mediaFile.url;
1244
- adVideoElement.load();
1245
- adVideoElement.play().catch(function(error) {
1246
- console.error("".concat(LOG, " Error starting native ad playback:"), error);
1247
- handleAdError();
1248
- });
1249
- }
1250
- function startHlsPlayback(mediaFile) {
1251
- if (!adVideoElement) return;
1252
- if (debug) console.log("".concat(LOG, " Starting HLS playback: ").concat(mediaFile.url));
1253
- if (Hls.isSupported()) {
1254
- if (adHls) {
1255
- adHls.destroy();
1256
- adHls = void 0;
1257
- }
1258
- adHls = new Hls({
1259
- enableWorker: true,
1260
- lowLatencyMode: false
1261
- });
1262
- adHls.loadSource(mediaFile.url);
1263
- adHls.attachMedia(adVideoElement);
1264
- adHls.on(Hls.Events.MANIFEST_PARSED, function() {
1265
- if (!adPlaying) return;
1266
- adVideoElement.play().catch(function(error) {
1267
- console.error("".concat(LOG, " Error starting HLS ad playback:"), error);
1268
- handleAdError();
1269
- });
1270
- });
1271
- var nonFatalNetworkErrors = 0;
1272
- adHls.on(Hls.Events.ERROR, function(_event, data) {
1273
- if (data.fatal) {
1274
- handleAdError();
1275
- } else if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {
1276
- nonFatalNetworkErrors++;
1277
- if (nonFatalNetworkErrors >= 3) {
1278
- if (debug) console.warn("".concat(LOG, " Too many non-fatal HLS network errors (").concat(nonFatalNetworkErrors, "), treating as fatal"));
1279
- handleAdError();
1280
- }
1281
- }
1282
- });
1283
- } else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1284
- adVideoElement.src = mediaFile.url;
1285
- adVideoElement.play().catch(function(error) {
1286
- console.error("".concat(LOG, " Error starting native HLS ad playback:"), error);
1287
- handleAdError();
1288
- });
1289
- } else {
1290
- console.error("".concat(LOG, " HLS not supported on this platform"));
1291
- handleAdError();
1292
- }
1293
- }
1294
- function startPlayback(mediaFile) {
1295
- if (!adVideoElement) return;
1296
- if (singleElementMode && isHlsMediaFile(mediaFile)) {
1297
- var mp4Fallback = currentAd === null || currentAd === void 0 ? void 0 : currentAd.mediaFiles.find(function(f) {
1298
- return !isHlsMediaFile(f);
1299
- });
1300
- if (mp4Fallback) {
1301
- if (debug) console.log("".concat(LOG, " singleElementMode: HLS ad blocked, using MP4 fallback"));
1302
- startNativePlayback(mp4Fallback);
1303
- return;
1304
- }
1305
- }
1306
- if (isHlsMediaFile(mediaFile)) {
1307
- startHlsPlayback(mediaFile);
1308
- } else {
1309
- startNativePlayback(mediaFile);
1310
- }
1311
- }
1312
- function playAd(bids) {
1155
+ function requestAdFromApi(requestContext) {
1313
1156
  return _async_to_generator(function() {
1314
- var winner, ad, contentVolume, adVolume2, mediaFile2, _contentVideo_parentElement, container, adVolume, mediaFile;
1157
+ var durationSeconds, ads;
1315
1158
  return _ts_generator(this, function(_state) {
1316
1159
  switch(_state.label){
1317
1160
  case 0:
1318
- if (destroyed) {
1319
- return [
1320
- 2,
1321
- Promise.reject(new Error("Layer has been destroyed"))
1322
- ];
1323
- }
1324
- if (bids.length === 0) {
1325
- return [
1326
- 2,
1327
- Promise.reject(new Error("No bids provided"))
1328
- ];
1329
- }
1330
- winner = bids[0];
1331
- if (debug) {
1332
- console.log("".concat(LOG, " Winning bid: ").concat(winner.bidder, " $").concat(winner.cpm.toFixed(2), " ").concat(winner.currency));
1333
- }
1161
+ durationSeconds = getDurationSecondsFromContext(requestContext);
1334
1162
  return [
1335
1163
  4,
1336
- resolveBidToVastAd(winner, LOG)
1164
+ fetchVast(durationSeconds)
1337
1165
  ];
1338
1166
  case 1:
1339
- ad = _state.sent();
1340
- if (!ad) {
1341
- if (debug) console.warn("".concat(LOG, " Winning bid has no VAST URL or XML"));
1342
- emit("ad_error");
1343
- return [
1344
- 2,
1345
- Promise.reject(new Error("No VAST from bid"))
1346
- ];
1347
- }
1348
- if (debug) {
1349
- console.log("".concat(LOG, " Ad parsed: ").concat(ad.title, ", duration: ").concat(ad.duration, "s, mediaFiles: ").concat(ad.mediaFiles.length));
1350
- }
1351
- sessionId = generateSessionId();
1352
- currentAd = ad;
1353
- trackingFired = _object_spread({}, createEmptyTrackingState());
1354
- fireTrackingPixels2(ad.trackingUrls.impression);
1355
- trackingFired.impression = true;
1356
- contentVolume = contentVideo.volume;
1357
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1358
- if (!singleElementMode) return [
1359
- 3,
1360
- 3
1361
- ];
1362
- mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1363
- teardownCurrentPlayback();
1364
- adVideoElement = contentVideo;
1365
- adHls = void 0;
1366
- adPlaying = true;
1367
- setAdPlayingFlag(true);
1368
- contentVideo.removeAttribute("src");
1369
- contentVideo.load();
1370
- if (!continueLiveStreamDuringAds) {
1371
- contentVideo.pause();
1372
- }
1373
- contentVideo.muted = true;
1374
- contentVideo.volume = 0;
1375
- return [
1376
- 4,
1377
- new Promise(function(resolve) {
1378
- return setTimeout(resolve, 200);
1379
- })
1380
- ];
1381
- case 2:
1382
- _state.sent();
1383
- if (destroyed || tornDown) return [
1384
- 2
1385
- ];
1386
- contentVideo.style.visibility = "visible";
1387
- contentVideo.style.opacity = "1";
1388
- emit("content_pause");
1389
- setupAdEventListeners();
1390
- adVolume2 = originalMutedState ? 1 : originalVolume;
1391
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume2));
1392
- adVideoElement.muted = false;
1393
- mediaFile2 = selectBestMediaFile(ad.mediaFiles);
1394
- if (debug) console.log("".concat(LOG, " Loading ad from: ").concat(mediaFile2.url));
1395
- startPlayback(mediaFile2);
1396
- return [
1397
- 2
1398
- ];
1399
- case 3:
1400
- if (!adContainerEl) {
1401
- ;
1402
- container = document.createElement("div");
1403
- container.style.position = "absolute";
1404
- container.style.left = "0";
1405
- container.style.top = "0";
1406
- container.style.right = "0";
1407
- container.style.bottom = "0";
1408
- container.style.display = "none";
1409
- container.style.alignItems = "center";
1410
- container.style.justifyContent = "center";
1411
- container.style.pointerEvents = "none";
1412
- container.style.zIndex = "10";
1413
- container.style.backgroundColor = "#000";
1414
- (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1415
- adContainerEl = container;
1416
- }
1417
- if (!adVideoElement) {
1418
- adVideoElement = createAdVideoElement();
1419
- adContainerEl.appendChild(adVideoElement);
1420
- setupAdEventListeners();
1421
- } else {
1422
- teardownCurrentPlayback();
1423
- }
1424
- if (!continueLiveStreamDuringAds) {
1425
- contentVideo.pause();
1426
- }
1427
- contentVideo.muted = true;
1428
- contentVideo.volume = 0;
1429
- adPlaying = true;
1430
- setAdPlayingFlag(true);
1431
- adVolume = originalMutedState ? 1 : originalVolume;
1432
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1433
- adVideoElement.muted = false;
1434
- if (adContainerEl) {
1435
- adContainerEl.style.display = "flex";
1436
- adContainerEl.style.pointerEvents = "auto";
1437
- }
1438
- emit("content_pause");
1439
- mediaFile = selectBestMediaFile(ad.mediaFiles);
1440
- if (debug) console.log("".concat(LOG, " Loading ad from: ").concat(mediaFile.url));
1441
- startPlayback(mediaFile);
1167
+ ads = _state.sent();
1442
1168
  return [
1443
- 2
1169
+ 2,
1170
+ ads[0] || null
1444
1171
  ];
1445
1172
  }
1446
1173
  });
1447
1174
  })();
1448
1175
  }
1449
- function ensureAdContainer() {
1450
- if (!adContainerEl) {
1451
- var _contentVideo_parentElement;
1452
- var container = document.createElement("div");
1453
- container.style.position = "absolute";
1454
- container.style.left = "0";
1455
- container.style.top = "0";
1456
- container.style.right = "0";
1457
- container.style.bottom = "0";
1458
- container.style.display = "none";
1459
- container.style.alignItems = "center";
1460
- container.style.justifyContent = "center";
1461
- container.style.pointerEvents = "none";
1462
- container.style.zIndex = "10";
1463
- container.style.backgroundColor = "#000";
1464
- (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1465
- adContainerEl = container;
1466
- }
1467
- return adContainerEl;
1176
+ function assignCurrentAd(ad) {
1177
+ currentAd = ad;
1178
+ sessionId = generateSessionId();
1179
+ trackingFired = {
1180
+ impression: false,
1181
+ start: false,
1182
+ firstQuartile: false,
1183
+ midpoint: false,
1184
+ thirdQuartile: false,
1185
+ complete: false
1186
+ };
1187
+ fireTrackingPixels2(currentAd.trackingUrls.impression);
1188
+ trackingFired.impression = true;
1189
+ emit("ad_impression");
1468
1190
  }
1469
- function preloadAd(bids, token) {
1470
- return _async_to_generator(function() {
1471
- var winner, ad, mediaFile, slot, videoEl, container, hls, slot1, slot2;
1472
- return _ts_generator(this, function(_state) {
1473
- switch(_state.label){
1474
- case 0:
1475
- if (destroyed) return [
1476
- 2
1477
- ];
1478
- winner = bids[0];
1479
- if (!winner) return [
1480
- 2
1481
- ];
1482
- if (debug) console.log("".concat(LOG, " [preload] Resolving VAST for token=").concat(token));
1483
- return [
1484
- 4,
1485
- resolveBidToVastAd(winner, LOG)
1486
- ];
1487
- case 1:
1488
- ad = _state.sent();
1489
- if (!ad || destroyed) return [
1490
- 2
1491
- ];
1492
- mediaFile = selectBestMediaFile(ad.mediaFiles);
1493
- if (!mediaFile) return [
1494
- 2
1495
- ];
1496
- if (smartTVMode || singleElementMode) {
1497
- slot = {
1498
- bids: bids,
1499
- ad: ad,
1500
- mediaFile: mediaFile,
1501
- videoEl: null,
1502
- ready: true
1503
- };
1504
- preloadSlots.set(token, slot);
1505
- if (debug) console.log("".concat(LOG, " [preload] Metadata-only preload (smartTV/singleElement), token=").concat(token, ", url=").concat(mediaFile.url));
1191
+ return {
1192
+ initialize: function initialize() {
1193
+ log("Initializing");
1194
+ if (!adContainerEl) {
1195
+ var _contentVideo_parentElement;
1196
+ var parent = contentVideo.parentElement;
1197
+ if (parent) {
1198
+ var computed = window.getComputedStyle(parent).position;
1199
+ if (computed === "static") {
1200
+ parent.style.position = "relative";
1201
+ parentPositionOverridden = true;
1202
+ }
1203
+ }
1204
+ var container = document.createElement("div");
1205
+ container.style.position = "absolute";
1206
+ container.style.left = "0";
1207
+ container.style.top = "0";
1208
+ container.style.right = "0";
1209
+ container.style.bottom = "0";
1210
+ container.style.display = "none";
1211
+ container.style.alignItems = "center";
1212
+ container.style.justifyContent = "center";
1213
+ container.style.pointerEvents = "none";
1214
+ container.style.zIndex = AD_LAYER_Z_INDEX;
1215
+ container.style.backgroundColor = "#000";
1216
+ container.style.transition = "opacity 0.3s ease-in-out";
1217
+ container.style.opacity = "0";
1218
+ container.style.isolation = "isolate";
1219
+ var countdown = document.createElement("div");
1220
+ countdown.style.position = "absolute";
1221
+ countdown.style.left = "12px";
1222
+ countdown.style.top = "12px";
1223
+ countdown.style.padding = "4px 8px";
1224
+ countdown.style.borderRadius = "4px";
1225
+ countdown.style.background = "rgba(0,0,0,0.75)";
1226
+ countdown.style.color = "#fff";
1227
+ countdown.style.fontFamily = "sans-serif";
1228
+ countdown.style.fontSize = "12px";
1229
+ countdown.style.lineHeight = "1.2";
1230
+ countdown.style.pointerEvents = "none";
1231
+ countdown.style.zIndex = COUNTDOWN_Z_INDEX;
1232
+ countdown.textContent = "Ad";
1233
+ container.appendChild(countdown);
1234
+ (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1235
+ adContainerEl = container;
1236
+ adCountdownEl = countdown;
1237
+ }
1238
+ },
1239
+ requestAds: function requestAds(duration) {
1240
+ return _async_to_generator(function() {
1241
+ var durationSeconds, parsed, ads, error;
1242
+ return _ts_generator(this, function(_state) {
1243
+ switch(_state.label){
1244
+ case 0:
1245
+ log("Requesting ads for duration:", duration);
1246
+ if (adPlaying) {
1247
+ return [
1248
+ 2,
1249
+ Promise.reject(new Error("Ad already playing"))
1250
+ ];
1251
+ }
1252
+ if (destroyed) {
1253
+ return [
1254
+ 2,
1255
+ Promise.reject(new Error("Player has been destroyed"))
1256
+ ];
1257
+ }
1258
+ _state.label = 1;
1259
+ case 1:
1260
+ _state.trys.push([
1261
+ 1,
1262
+ 3,
1263
+ ,
1264
+ 4
1265
+ ]);
1266
+ tornDown = false;
1267
+ durationSeconds = 30;
1268
+ parsed = parseInt(duration || "", 10);
1269
+ if (!isNaN(parsed) && parsed > 0) {
1270
+ durationSeconds = parsed;
1271
+ }
1506
1272
  return [
1507
- 2
1273
+ 4,
1274
+ fetchVast(durationSeconds)
1508
1275
  ];
1509
- }
1510
- videoEl = createAdVideoElement();
1511
- videoEl.style.visibility = "hidden";
1512
- videoEl.style.pointerEvents = "none";
1513
- videoEl.preload = "auto";
1514
- container = ensureAdContainer();
1515
- container.appendChild(videoEl);
1516
- if (isHlsMediaFile(mediaFile) && Hls.isSupported()) {
1517
- hls = new Hls({
1518
- enableWorker: true,
1519
- lowLatencyMode: false
1520
- });
1521
- hls.loadSource(mediaFile.url);
1522
- hls.attachMedia(videoEl);
1523
- slot1 = {
1524
- bids: bids,
1525
- ad: ad,
1526
- mediaFile: mediaFile,
1527
- videoEl: videoEl,
1528
- hlsInstance: hls,
1529
- ready: false
1530
- };
1531
- preloadSlots.set(token, slot1);
1532
- hls.on(Hls.Events.MANIFEST_PARSED, function() {
1533
- var s = preloadSlots.get(token);
1534
- if (s) s.ready = true;
1535
- if (debug) console.log("".concat(LOG, " [preload] HLS manifest parsed, token=").concat(token));
1536
- });
1537
- hls.on(Hls.Events.ERROR, function(_evt, data) {
1538
- if (!preloadSlots.has(token)) return;
1539
- if (data.fatal) {
1540
- if (debug) console.warn("".concat(LOG, " [preload] HLS error for token=").concat(token));
1541
- preloadSlots.delete(token);
1542
- hls.destroy();
1543
- videoEl.remove();
1544
- }
1545
- });
1546
- } else {
1547
- videoEl.src = mediaFile.url;
1548
- videoEl.load();
1549
- slot2 = {
1550
- bids: bids,
1551
- ad: ad,
1552
- mediaFile: mediaFile,
1553
- videoEl: videoEl,
1554
- ready: false
1555
- };
1556
- preloadSlots.set(token, slot2);
1557
- videoEl.addEventListener("canplay", function() {
1558
- var s = preloadSlots.get(token);
1559
- if (s) s.ready = true;
1560
- if (debug) console.log("".concat(LOG, " [preload] canplay fired, token=").concat(token));
1561
- }, {
1562
- once: true
1563
- });
1564
- }
1565
- if (debug) console.log("".concat(LOG, " [preload] Started buffering token=").concat(token, ", url=").concat(mediaFile.url));
1566
- return [
1567
- 2
1568
- ];
1569
- }
1570
- });
1571
- })();
1572
- }
1573
- function playPreloaded(token) {
1574
- return _async_to_generator(function() {
1575
- var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, nonFatalNetworkErrors, adVolume, container;
1576
- return _ts_generator(this, function(_state) {
1577
- switch(_state.label){
1578
- case 0:
1579
- if (destroyed) return [
1580
- 2,
1581
- Promise.reject(new Error("Layer has been destroyed"))
1582
- ];
1583
- slot = preloadSlots.get(token);
1584
- if (!slot) {
1585
- if (debug) console.warn("".concat(LOG, " [preload] No slot found for token=").concat(token, ", nothing to play"));
1276
+ case 2:
1277
+ ads = _state.sent();
1278
+ if (ads.length === 0) {
1279
+ log("No ads available from VAST response");
1280
+ emit("ad_error");
1281
+ return [
1282
+ 2,
1283
+ Promise.resolve()
1284
+ ];
1285
+ }
1286
+ assignCurrentAd(ads[0]);
1287
+ log("Ad loaded: ".concat(currentAd.title, ", duration: ").concat(currentAd.duration, "s"));
1288
+ return [
1289
+ 2,
1290
+ Promise.resolve()
1291
+ ];
1292
+ case 3:
1293
+ error = _state.sent();
1294
+ console.error("[AdStormPlayer] Error requesting ads:", error);
1295
+ emit("ad_error");
1296
+ return [
1297
+ 2,
1298
+ Promise.reject(error)
1299
+ ];
1300
+ case 4:
1586
1301
  return [
1587
1302
  2
1588
1303
  ];
1589
- }
1590
- preloadSlots.delete(token);
1591
- if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1592
- contentVolume = contentVideo.volume;
1593
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1594
- sessionId = generateSessionId();
1595
- currentAd = slot.ad;
1596
- trackingFired = _object_spread({}, createEmptyTrackingState());
1597
- fireTrackingPixels2(slot.ad.trackingUrls.impression);
1598
- trackingFired.impression = true;
1599
- if (!singleElementMode) return [
1600
- 3,
1601
- 2
1602
- ];
1603
- mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1604
- teardownCurrentPlayback();
1605
- adVideoElement = contentVideo;
1606
- adHls = void 0;
1607
- adPlaying = true;
1608
- setAdPlayingFlag(true);
1609
- contentVideo.removeAttribute("src");
1610
- contentVideo.load();
1611
- contentVideo.muted = true;
1612
- contentVideo.volume = 0;
1613
- return [
1614
- 4,
1615
- new Promise(function(resolve) {
1616
- return setTimeout(resolve, 200);
1617
- })
1618
- ];
1619
- case 1:
1620
- _state.sent();
1621
- if (destroyed || tornDown) return [
1622
- 2
1623
- ];
1624
- contentVideo.style.visibility = "visible";
1625
- contentVideo.style.opacity = "1";
1626
- emit("content_pause");
1627
- setupAdEventListeners();
1628
- adVolume2 = originalMutedState ? 1 : originalVolume;
1629
- contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1630
- contentVideo.muted = false;
1631
- if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1632
- startPlayback(slot.mediaFile);
1633
- return [
1634
- 2
1635
- ];
1636
- case 2:
1637
- if (smartTVMode && !slot.videoEl) {
1638
- teardownCurrentPlayback();
1639
- if (adVideoElement) {
1640
- adVideoElement.remove();
1641
- adVideoElement = void 0;
1304
+ }
1305
+ });
1306
+ })();
1307
+ },
1308
+ play: function play() {
1309
+ return _async_to_generator(function() {
1310
+ var mediaFile, error;
1311
+ return _ts_generator(this, function(_state) {
1312
+ switch(_state.label){
1313
+ case 0:
1314
+ if (!currentAd) {
1315
+ return [
1316
+ 2,
1317
+ Promise.reject(new Error("No ad loaded"))
1318
+ ];
1319
+ }
1320
+ if (destroyed) {
1321
+ return [
1322
+ 2,
1323
+ Promise.reject(new Error("Player has been destroyed"))
1324
+ ];
1325
+ }
1326
+ log("Starting ad playback");
1327
+ _state.label = 1;
1328
+ case 1:
1329
+ _state.trys.push([
1330
+ 1,
1331
+ 3,
1332
+ ,
1333
+ 4
1334
+ ]);
1335
+ tornDown = false;
1336
+ if (adHideTimerId) {
1337
+ clearTimeout(adHideTimerId);
1338
+ adHideTimerId = void 0;
1339
+ }
1340
+ if (!adVideoElement) {
1341
+ adVideoElement = createAdVideoElement();
1342
+ adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.appendChild(adVideoElement);
1343
+ } else {
1344
+ teardownCurrentPlayback();
1642
1345
  }
1643
- videoEl = createAdVideoElement();
1644
- videoEl.style.visibility = "visible";
1645
- videoEl.style.pointerEvents = "none";
1646
- container2 = ensureAdContainer();
1647
- container2.appendChild(videoEl);
1648
- adVideoElement = videoEl;
1649
1346
  setupAdEventListeners();
1347
+ trackingFired = {
1348
+ impression: trackingFired.impression,
1349
+ start: false,
1350
+ firstQuartile: false,
1351
+ midpoint: false,
1352
+ thirdQuartile: false,
1353
+ complete: false
1354
+ };
1355
+ contentVideo.style.transition = "opacity 0.3s ease-in-out";
1356
+ contentVideo.style.opacity = "0";
1357
+ setTimeout(function() {
1358
+ contentVideo.style.visibility = "hidden";
1359
+ }, 300);
1360
+ contentVideo.muted = true;
1361
+ contentVideo.volume = 0;
1650
1362
  if (!continueLiveStreamDuringAds) {
1651
1363
  contentVideo.pause();
1652
1364
  }
1653
- contentVideo.muted = true;
1654
- contentVideo.volume = 0;
1655
1365
  adPlaying = true;
1656
1366
  setAdPlayingFlag(true);
1657
- adVolume21 = originalMutedState ? 1 : originalVolume;
1658
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1659
- adVideoElement.muted = false;
1660
- container2.style.display = "flex";
1661
- container2.style.pointerEvents = "auto";
1367
+ if (adVideoElement) {
1368
+ adVideoElement.volume = originalMutedState ? 0 : originalVolume;
1369
+ adVideoElement.muted = originalMutedState;
1370
+ }
1371
+ if (adContainerEl) {
1372
+ adContainerEl.style.display = "flex";
1373
+ adContainerEl.style.pointerEvents = "auto";
1374
+ adContainerEl.offsetHeight;
1375
+ adContainerEl.style.opacity = "1";
1376
+ }
1662
1377
  emit("content_pause");
1663
- if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1664
- startPlayback(slot.mediaFile);
1378
+ mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1379
+ if (!mediaFile) {
1380
+ throw new Error("No media file available");
1381
+ }
1382
+ log("Playing media file:", mediaFile.url);
1383
+ adVideoElement.src = mediaFile.url;
1384
+ adVideoElement.load();
1665
1385
  return [
1666
- 2
1667
- ];
1668
- }
1669
- teardownCurrentPlayback();
1670
- if (adVideoElement && adVideoElement !== slot.videoEl) {
1671
- adVideoElement.remove();
1672
- }
1673
- slot.videoEl.style.visibility = "visible";
1674
- slot.videoEl.style.pointerEvents = "none";
1675
- adVideoElement = slot.videoEl;
1676
- adHls = slot.hlsInstance;
1677
- if (adHls) {
1678
- nonFatalNetworkErrors = 0;
1679
- adHls.on(Hls.Events.ERROR, function(_event, data) {
1680
- if (!adPlaying) return;
1681
- if (data.fatal) {
1682
- handleAdError();
1683
- } else if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {
1684
- nonFatalNetworkErrors++;
1685
- if (nonFatalNetworkErrors >= 3) {
1686
- if (debug) console.warn("".concat(LOG, " [preload] Too many non-fatal HLS network errors during playback, treating as fatal"));
1687
- handleAdError();
1688
- }
1689
- }
1690
- });
1691
- }
1692
- setupAdEventListeners();
1693
- if (!continueLiveStreamDuringAds) {
1694
- contentVideo.pause();
1695
- }
1696
- contentVideo.muted = true;
1697
- contentVideo.volume = 0;
1698
- adPlaying = true;
1699
- setAdPlayingFlag(true);
1700
- adVolume = originalMutedState ? 1 : originalVolume;
1701
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1702
- adVideoElement.muted = false;
1703
- container = ensureAdContainer();
1704
- container.style.display = "flex";
1705
- container.style.pointerEvents = "auto";
1706
- emit("content_pause");
1707
- adVideoElement.play().catch(function(error) {
1708
- console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1709
- handleAdError();
1710
- });
1711
- return [
1712
- 2
1713
- ];
1714
- }
1715
- });
1716
- })();
1717
- }
1718
- function cancelPreload(token) {
1719
- var slot = preloadSlots.get(token);
1720
- if (!slot) return;
1721
- preloadSlots.delete(token);
1722
- if (slot.hlsInstance) {
1723
- slot.hlsInstance.destroy();
1724
- }
1725
- if (slot.videoEl) {
1726
- slot.videoEl.pause();
1727
- slot.videoEl.removeAttribute("src");
1728
- slot.videoEl.load();
1729
- slot.videoEl.remove();
1730
- }
1731
- if (debug) console.log("".concat(LOG, " [preload] Cancelled and cleaned up token=").concat(token));
1732
- }
1733
- return {
1734
- initialize: function initialize() {
1735
- if (debug) console.log("".concat(LOG, " Initializing"));
1736
- },
1737
- updateOptions: function updateOptions(opts) {
1738
- if (opts.continueLiveStreamDuringAds !== void 0) {
1739
- continueLiveStreamDuringAds = opts.continueLiveStreamDuringAds;
1740
- }
1741
- if (opts.mainHlsInstance !== void 0) {
1742
- var _opts_mainHlsInstance;
1743
- mainHlsInstance = (_opts_mainHlsInstance = opts.mainHlsInstance) !== null && _opts_mainHlsInstance !== void 0 ? _opts_mainHlsInstance : void 0;
1744
- }
1745
- },
1746
- playAd: playAd,
1747
- preloadAd: preloadAd,
1748
- playPreloaded: playPreloaded,
1749
- hasPreloaded: function hasPreloaded(token) {
1750
- return preloadSlots.has(token);
1751
- },
1752
- cancelPreload: cancelPreload,
1753
- pause: function pause() {
1754
- if (!adPlaying || !adVideoElement) return;
1755
- try {
1756
- if (!adVideoElement.paused) adVideoElement.pause();
1757
- } catch (error) {
1758
- if (debug) console.warn("".concat(LOG, " Error pausing ad:"), error);
1759
- }
1760
- },
1761
- resume: function resume() {
1762
- if (!adPlaying || !adVideoElement) return;
1763
- try {
1764
- if (adVideoElement.paused) adVideoElement.play().catch(function() {});
1765
- } catch (error) {
1766
- if (debug) console.warn("".concat(LOG, " Error resuming ad:"), error);
1767
- }
1386
+ 4,
1387
+ adVideoElement.play()
1388
+ ];
1389
+ case 2:
1390
+ _state.sent();
1391
+ return [
1392
+ 2,
1393
+ Promise.resolve()
1394
+ ];
1395
+ case 3:
1396
+ error = _state.sent();
1397
+ console.error("[AdStormPlayer] Error playing ad:", error);
1398
+ handleAdError();
1399
+ return [
1400
+ 2,
1401
+ Promise.reject(error)
1402
+ ];
1403
+ case 4:
1404
+ return [
1405
+ 2
1406
+ ];
1407
+ }
1408
+ });
1409
+ })();
1768
1410
  },
1769
1411
  stop: function stop() {
1770
1412
  return _async_to_generator(function() {
1771
1413
  return _ts_generator(this, function(_state) {
1414
+ log("Stopping ad");
1772
1415
  tornDown = true;
1773
- if (debug) console.log("".concat(LOG, " Stopping ad"));
1774
1416
  adPlaying = false;
1775
1417
  setAdPlayingFlag(false);
1776
- contentVideo.muted = originalMutedState;
1777
- contentVideo.volume = originalMutedState ? 0 : originalVolume;
1418
+ clearAdStallTimer();
1419
+ clearAdCountdownTimer();
1420
+ if (adContainerEl) {
1421
+ adContainerEl.style.opacity = "0";
1422
+ adHideTimerId = setTimeout(function() {
1423
+ if (adContainerEl) {
1424
+ adContainerEl.style.display = "none";
1425
+ adContainerEl.style.pointerEvents = "none";
1426
+ }
1427
+ }, 300);
1428
+ }
1429
+ teardownCurrentPlayback();
1778
1430
  contentVideo.style.visibility = "visible";
1779
1431
  contentVideo.style.opacity = "1";
1780
- if (singleElementMode) {
1781
- teardownCurrentPlayback();
1782
- contentVideo.removeAttribute("src");
1783
- contentVideo.load();
1784
- adVideoElement = void 0;
1785
- } else {
1786
- if (adContainerEl) {
1787
- adContainerEl.style.display = "none";
1788
- adContainerEl.style.pointerEvents = "none";
1789
- }
1790
- if (continueLiveStreamDuringAds) {
1791
- contentVideo.play().catch(function() {});
1792
- }
1793
- teardownCurrentPlayback();
1794
- if (adVideoElement) {
1795
- adVideoElement.pause();
1796
- adVideoElement.removeAttribute("src");
1797
- adVideoElement.load();
1798
- }
1799
- }
1432
+ contentVideo.muted = originalMutedState;
1433
+ contentVideo.volume = originalVolume;
1800
1434
  currentAd = void 0;
1801
1435
  tornDown = false;
1802
1436
  return [
1803
- 2
1437
+ 2,
1438
+ Promise.resolve()
1804
1439
  ];
1805
1440
  });
1806
1441
  })();
1807
1442
  },
1443
+ pause: function pause() {
1444
+ if (!adPlaying || !adVideoElement) return;
1445
+ try {
1446
+ if (!adVideoElement.paused) adVideoElement.pause();
1447
+ } catch (error) {
1448
+ console.warn("[AdStormPlayer] Error pausing ad:", error);
1449
+ }
1450
+ },
1451
+ resume: function resume() {
1452
+ if (!adPlaying || !adVideoElement) return;
1453
+ try {
1454
+ if (adVideoElement.paused) adVideoElement.play().catch(function() {});
1455
+ } catch (error) {
1456
+ console.warn("[AdStormPlayer] Error resuming ad:", error);
1457
+ }
1458
+ },
1808
1459
  destroy: function destroy() {
1809
- tornDown = true;
1810
- if (debug) console.log("".concat(LOG, " Destroying"));
1460
+ log("Destroying");
1811
1461
  destroyed = true;
1462
+ tornDown = true;
1812
1463
  adPlaying = false;
1813
1464
  setAdPlayingFlag(false);
1465
+ clearAdStallTimer();
1466
+ clearAdCountdownTimer();
1467
+ if (adHideTimerId) {
1468
+ clearTimeout(adHideTimerId);
1469
+ adHideTimerId = void 0;
1470
+ }
1814
1471
  contentVideo.muted = originalMutedState;
1815
1472
  contentVideo.volume = originalVolume;
1816
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1817
- try {
1818
- for(var _iterator = Array.from(preloadSlots.entries())[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1819
- var _step_value = _sliced_to_array(_step.value, 1), token = _step_value[0];
1820
- cancelPreload(token);
1821
- }
1822
- } catch (err) {
1823
- _didIteratorError = true;
1824
- _iteratorError = err;
1825
- } finally{
1826
- try {
1827
- if (!_iteratorNormalCompletion && _iterator.return != null) {
1828
- _iterator.return();
1829
- }
1830
- } finally{
1831
- if (_didIteratorError) {
1832
- throw _iteratorError;
1833
- }
1834
- }
1835
- }
1473
+ contentVideo.style.visibility = "visible";
1474
+ contentVideo.style.opacity = "1";
1836
1475
  teardownCurrentPlayback();
1837
- if (adVideoElement) {
1838
- if (singleElementMode && adVideoElement === contentVideo) {
1839
- contentVideo.removeAttribute("src");
1840
- contentVideo.load();
1841
- } else {
1842
- adVideoElement.pause();
1843
- adVideoElement.removeAttribute("src");
1844
- adVideoElement.remove();
1845
- }
1846
- adVideoElement = void 0;
1847
- }
1476
+ adVideoElement === null || adVideoElement === void 0 ? void 0 : adVideoElement.remove();
1477
+ adVideoElement = void 0;
1848
1478
  if (adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.parentElement) {
1849
1479
  adContainerEl.parentElement.removeChild(adContainerEl);
1850
1480
  }
1851
1481
  adContainerEl = void 0;
1482
+ adCountdownEl = void 0;
1852
1483
  currentAd = void 0;
1484
+ sessionId = void 0;
1485
+ preloadSlots.clear();
1853
1486
  listeners.clear();
1487
+ if (parentPositionOverridden && contentVideo.parentElement) {
1488
+ contentVideo.parentElement.style.position = "";
1489
+ parentPositionOverridden = false;
1490
+ }
1491
+ },
1492
+ updateOptions: function updateOptions(opts) {
1493
+ if (opts.continueLiveStreamDuringAds !== void 0) {
1494
+ continueLiveStreamDuringAds = opts.continueLiveStreamDuringAds;
1495
+ }
1496
+ },
1497
+ playAd: function playAd(requestContext) {
1498
+ return _async_to_generator(function() {
1499
+ var ad;
1500
+ return _ts_generator(this, function(_state) {
1501
+ switch(_state.label){
1502
+ case 0:
1503
+ if (destroyed) return [
1504
+ 2,
1505
+ Promise.reject(new Error("Player has been destroyed"))
1506
+ ];
1507
+ if (!!currentAd) return [
1508
+ 3,
1509
+ 2
1510
+ ];
1511
+ return [
1512
+ 4,
1513
+ requestAdFromApi(requestContext)
1514
+ ];
1515
+ case 1:
1516
+ ad = _state.sent();
1517
+ if (!ad) {
1518
+ emit("ad_error", {
1519
+ message: "No valid ad from AdStorm API"
1520
+ });
1521
+ return [
1522
+ 2,
1523
+ Promise.reject(new Error("No valid ad from AdStorm API"))
1524
+ ];
1525
+ }
1526
+ assignCurrentAd(ad);
1527
+ _state.label = 2;
1528
+ case 2:
1529
+ return [
1530
+ 2,
1531
+ this.play()
1532
+ ];
1533
+ }
1534
+ });
1535
+ }).call(this);
1536
+ },
1537
+ preloadAd: function preloadAd(arg1, arg2) {
1538
+ return _async_to_generator(function() {
1539
+ var token, requestContext, ad;
1540
+ return _ts_generator(this, function(_state) {
1541
+ switch(_state.label){
1542
+ case 0:
1543
+ if (destroyed) return [
1544
+ 2
1545
+ ];
1546
+ token = typeof arg1 === "string" ? arg1 : typeof arg2 === "string" ? arg2 : void 0;
1547
+ if (!token) return [
1548
+ 2
1549
+ ];
1550
+ requestContext = typeof arg1 === "string" ? arg2 : arg1;
1551
+ return [
1552
+ 4,
1553
+ requestAdFromApi(requestContext)
1554
+ ];
1555
+ case 1:
1556
+ ad = _state.sent();
1557
+ if (!ad) return [
1558
+ 2
1559
+ ];
1560
+ preloadSlots.set(token, {
1561
+ ad: ad
1562
+ });
1563
+ return [
1564
+ 2
1565
+ ];
1566
+ }
1567
+ });
1568
+ })();
1569
+ },
1570
+ playPreloaded: function playPreloaded(token) {
1571
+ return _async_to_generator(function() {
1572
+ var slot;
1573
+ return _ts_generator(this, function(_state) {
1574
+ if (destroyed) return [
1575
+ 2,
1576
+ Promise.reject(new Error("Player has been destroyed"))
1577
+ ];
1578
+ slot = preloadSlots.get(token);
1579
+ if (!slot) {
1580
+ return [
1581
+ 2,
1582
+ Promise.reject(new Error("No preloaded ad for token ".concat(token)))
1583
+ ];
1584
+ }
1585
+ preloadSlots.delete(token);
1586
+ assignCurrentAd(slot.ad);
1587
+ return [
1588
+ 2,
1589
+ this.play()
1590
+ ];
1591
+ });
1592
+ }).call(this);
1593
+ },
1594
+ hasPreloaded: function hasPreloaded(token) {
1595
+ return preloadSlots.has(token);
1596
+ },
1597
+ cancelPreload: function cancelPreload(token) {
1598
+ preloadSlots.delete(token);
1854
1599
  },
1855
1600
  isAdPlaying: function isAdPlaying() {
1856
1601
  return adPlaying;
1857
1602
  },
1858
1603
  resize: function resize(width, height) {
1604
+ log("Resizing to ".concat(width, "x").concat(height));
1859
1605
  if (adContainerEl) {
1860
1606
  adContainerEl.style.width = "".concat(width, "px");
1861
1607
  adContainerEl.style.height = "".concat(height, "px");
@@ -1875,6 +1621,7 @@ function createVastAdLayer(contentVideo, options) {
1875
1621
  },
1876
1622
  updateOriginalMutedState: function updateOriginalMutedState(muted, volume) {
1877
1623
  var nextVolume = typeof volume === "number" && !Number.isNaN(volume) ? Math.max(0, Math.min(1, volume)) : originalVolume;
1624
+ log("updateOriginalMutedState: muted=".concat(muted, ", volume=").concat(nextVolume));
1878
1625
  originalMutedState = muted;
1879
1626
  originalVolume = nextVolume;
1880
1627
  },
@@ -1887,6 +1634,7 @@ function createVastAdLayer(contentVideo, options) {
1887
1634
  setAdVolume: function setAdVolume(volume) {
1888
1635
  if (adVideoElement && adPlaying) {
1889
1636
  adVideoElement.volume = Math.max(0, Math.min(1, volume));
1637
+ adVideoElement.muted = volume === 0;
1890
1638
  }
1891
1639
  },
1892
1640
  getAdVolume: function getAdVolume() {
@@ -1896,11 +1644,16 @@ function createVastAdLayer(contentVideo, options) {
1896
1644
  return 1;
1897
1645
  },
1898
1646
  showPlaceholder: function showPlaceholder() {
1899
- if (singleElementMode) return;
1900
- contentVideo.style.opacity = "0";
1901
- contentVideo.style.visibility = "hidden";
1902
1647
  if (!adContainerEl) {
1903
1648
  var _contentVideo_parentElement;
1649
+ var parent = contentVideo.parentElement;
1650
+ if (parent) {
1651
+ var computed = window.getComputedStyle(parent).position;
1652
+ if (computed === "static") {
1653
+ parent.style.position = "relative";
1654
+ parentPositionOverridden = true;
1655
+ }
1656
+ }
1904
1657
  var container = document.createElement("div");
1905
1658
  container.style.position = "absolute";
1906
1659
  container.style.left = "0";
@@ -1911,24 +1664,43 @@ function createVastAdLayer(contentVideo, options) {
1911
1664
  container.style.alignItems = "center";
1912
1665
  container.style.justifyContent = "center";
1913
1666
  container.style.pointerEvents = "none";
1914
- container.style.zIndex = "10";
1667
+ container.style.zIndex = AD_LAYER_Z_INDEX;
1915
1668
  container.style.backgroundColor = "#000";
1669
+ container.style.isolation = "isolate";
1670
+ var countdown = document.createElement("div");
1671
+ countdown.style.position = "absolute";
1672
+ countdown.style.left = "12px";
1673
+ countdown.style.top = "12px";
1674
+ countdown.style.padding = "4px 8px";
1675
+ countdown.style.borderRadius = "4px";
1676
+ countdown.style.background = "rgba(0,0,0,0.75)";
1677
+ countdown.style.color = "#fff";
1678
+ countdown.style.fontFamily = "sans-serif";
1679
+ countdown.style.fontSize = "12px";
1680
+ countdown.style.lineHeight = "1.2";
1681
+ countdown.style.pointerEvents = "none";
1682
+ countdown.style.zIndex = COUNTDOWN_Z_INDEX;
1683
+ countdown.textContent = "Ad";
1684
+ container.appendChild(countdown);
1916
1685
  (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1917
1686
  adContainerEl = container;
1687
+ adCountdownEl = countdown;
1918
1688
  }
1919
1689
  if (adContainerEl) {
1920
1690
  adContainerEl.style.display = "flex";
1691
+ adContainerEl.style.opacity = "1";
1921
1692
  adContainerEl.style.pointerEvents = "auto";
1922
1693
  }
1923
1694
  },
1924
1695
  hidePlaceholder: function hidePlaceholder() {
1925
1696
  if (adContainerEl) {
1926
- adContainerEl.style.display = "none";
1927
- adContainerEl.style.pointerEvents = "none";
1928
- }
1929
- if (!adPlaying) {
1930
- contentVideo.style.visibility = "visible";
1931
- contentVideo.style.opacity = "1";
1697
+ adContainerEl.style.opacity = "0";
1698
+ setTimeout(function() {
1699
+ if (adContainerEl) {
1700
+ adContainerEl.style.display = "none";
1701
+ adContainerEl.style.pointerEvents = "none";
1702
+ }
1703
+ }, 300);
1932
1704
  }
1933
1705
  }
1934
1706
  };
@@ -3020,6 +2792,7 @@ function supportsFeature(feature) {
3020
2792
  }
3021
2793
  }
3022
2794
  // src/player/StormcloudVideoPlayer.ts
2795
+ var DEBUG_HISTORY_LIMIT = 120;
3023
2796
  var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3024
2797
  "use strict";
3025
2798
  function StormcloudVideoPlayer(config) {
@@ -3059,31 +2832,34 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3059
2832
  this.backoffBaseMs = 1e3;
3060
2833
  this.maxBackoffMs = 15e3;
3061
2834
  this.MIN_AD_REMAINING_MS = 15e3;
2835
+ this.adRequestTimeoutMs = 5e3;
2836
+ this.adRequestMaxRetries = 3;
2837
+ this.adRequestRetryBackoffMs = 1500;
3062
2838
  this.preloadedTokens = [];
2839
+ this.debugLogEntries = [];
2840
+ this.scteMarkerHistory = [];
3063
2841
  initializePolyfills();
3064
2842
  var browserOverrides = getBrowserConfigOverrides();
3065
2843
  this.config = _object_spread({}, browserOverrides, config);
3066
2844
  this.video = config.videoElement;
3067
2845
  this.adTransitionGapMs = (_this_config_adTransitionGapMs = this.config.adTransitionGapMs) !== null && _this_config_adTransitionGapMs !== void 0 ? _this_config_adTransitionGapMs : 100;
3068
2846
  logBrowserInfo(config.debugAdTiming);
3069
- this.vastManager = createVastManager(config.debugAdTiming !== void 0 ? {
3070
- debug: !!config.debugAdTiming
3071
- } : {});
3072
2847
  var browserForAdLayer = detectBrowser();
3073
2848
  var isSinglePipeline = browserForAdLayer.isSmartTV || !!this.config.singlePipelineMode;
3074
- this.adLayer = createVastAdLayer(this.video, {
3075
- continueLiveStreamDuringAds: false,
3076
- smartTVMode: isSinglePipeline,
3077
- singleElementMode: isSinglePipeline,
3078
- forceMP4Ads: isSinglePipeline,
2849
+ this.adLayer = createAdStormPlayer(this.video, {
2850
+ licenseKey: this.config.licenseKey || "",
3079
2851
  debug: !!config.debugAdTiming
3080
2852
  });
2853
+ this.adLayer.updateOptions({
2854
+ continueLiveStreamDuringAds: !isSinglePipeline && this.shouldContinueLiveStreamDuringAds()
2855
+ });
3081
2856
  }
3082
2857
  _create_class(StormcloudVideoPlayer, [
3083
2858
  {
3084
2859
  key: "adRequest",
3085
2860
  value: function adRequest(context) {
3086
2861
  return _async_to_generator(function() {
2862
+ var _ref, _ref1, durationSeconds;
3087
2863
  return _ts_generator(this, function(_state) {
3088
2864
  switch(_state.label){
3089
2865
  case 0:
@@ -3091,15 +2867,28 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3091
2867
  2,
3092
2868
  []
3093
2869
  ];
2870
+ durationSeconds = Math.max(1, Math.ceil((_ref = (_ref1 = context === null || context === void 0 ? void 0 : context.remainingBreakSec) !== null && _ref1 !== void 0 ? _ref1 : context === null || context === void 0 ? void 0 : context.breakDurationSec) !== null && _ref !== void 0 ? _ref : 30));
3094
2871
  return [
3095
2872
  4,
3096
- this.vastManager.initialize()
2873
+ this.adLayer.requestAds(String(durationSeconds))
3097
2874
  ];
3098
2875
  case 1:
3099
2876
  _state.sent();
3100
2877
  return [
3101
2878
  2,
3102
- this.vastManager.requestBidsUntilResponse(context)
2879
+ [
2880
+ {
2881
+ bidder: "adstorm-direct",
2882
+ cpm: 0,
2883
+ width: 0,
2884
+ height: 0,
2885
+ adId: "adstorm",
2886
+ impId: "",
2887
+ creativeId: "adstorm",
2888
+ currency: "USD",
2889
+ durationSec: durationSeconds
2890
+ }
2891
+ ]
3103
2892
  ];
3104
2893
  }
3105
2894
  });
@@ -3156,7 +2945,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3156
2945
  2
3157
2946
  ];
3158
2947
  case 3:
3159
- this.hls = new Hls2(_object_spread_props(_object_spread({
2948
+ this.hls = new Hls(_object_spread_props(_object_spread({
3160
2949
  enableWorker: true,
3161
2950
  backBufferLength: 30,
3162
2951
  liveDurationInfinity: true,
@@ -3174,11 +2963,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3174
2963
  nudgeMaxRetry: 3,
3175
2964
  startPosition: -1
3176
2965
  }));
3177
- this.hls.on(Hls2.Events.MEDIA_ATTACHED, function() {
2966
+ this.hls.on(Hls.Events.MEDIA_ATTACHED, function() {
3178
2967
  var _this_hls;
3179
2968
  (_this_hls = _this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.loadSource(_this.config.src);
3180
2969
  });
3181
- this.hls.on(Hls2.Events.MANIFEST_PARSED, function(_, data) {
2970
+ this.hls.on(Hls.Events.MANIFEST_PARSED, function(_, data) {
3182
2971
  return _async_to_generator(function() {
3183
2972
  var _this_config_minSegmentsBeforePlay, _ref, _this_hls_levels, _this_hls, adBehavior, _this_hls1, minSegments, _this_video_play;
3184
2973
  return _ts_generator(this, function(_state) {
@@ -3240,7 +3029,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3240
3029
  });
3241
3030
  }).call(_this);
3242
3031
  });
3243
- this.hls.on(Hls2.Events.LEVEL_LOADED, function(_evt, data) {
3032
+ this.hls.on(Hls.Events.LEVEL_LOADED, function(_evt, data) {
3244
3033
  if (_this.inAdBreak || _this.pendingAdBreak) {
3245
3034
  return;
3246
3035
  }
@@ -3313,7 +3102,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3313
3102
  }
3314
3103
  }
3315
3104
  });
3316
- this.hls.on(Hls2.Events.FRAG_BUFFERED, function(_evt, data) {
3105
+ this.hls.on(Hls.Events.FRAG_BUFFERED, function(_evt, data) {
3317
3106
  return _async_to_generator(function() {
3318
3107
  var _this, _this_config_minSegmentsBeforePlay, minSegments, _this_video_play;
3319
3108
  return _ts_generator(this, function(_state) {
@@ -3373,7 +3162,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3373
3162
  });
3374
3163
  }).call(_this);
3375
3164
  });
3376
- this.hls.on(Hls2.Events.FRAG_PARSING_METADATA, function(_evt, data) {
3165
+ this.hls.on(Hls.Events.FRAG_PARSING_METADATA, function(_evt, data) {
3377
3166
  var id3Tags = ((data === null || data === void 0 ? void 0 : data.samples) || []).map(function(s) {
3378
3167
  return {
3379
3168
  key: "ID3",
@@ -3385,7 +3174,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3385
3174
  return _this.onId3Tag(tag);
3386
3175
  });
3387
3176
  });
3388
- this.hls.on(Hls2.Events.FRAG_CHANGED, function(_evt, data) {
3177
+ this.hls.on(Hls.Events.FRAG_CHANGED, function(_evt, data) {
3389
3178
  var frag = data === null || data === void 0 ? void 0 : data.frag;
3390
3179
  var tagList = frag === null || frag === void 0 ? void 0 : frag.tagList;
3391
3180
  if (!Array.isArray(tagList)) return;
@@ -3494,14 +3283,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3494
3283
  }
3495
3284
  }
3496
3285
  });
3497
- this.hls.on(Hls2.Events.ERROR, function(_evt, data) {
3286
+ this.hls.on(Hls.Events.ERROR, function(_evt, data) {
3498
3287
  if (data === null || data === void 0 ? void 0 : data.fatal) {
3499
3288
  switch(data.type){
3500
- case Hls2.ErrorTypes.NETWORK_ERROR:
3289
+ case Hls.ErrorTypes.NETWORK_ERROR:
3501
3290
  var _this_hls;
3502
3291
  (_this_hls = _this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.startLoad();
3503
3292
  break;
3504
- case Hls2.ErrorTypes.MEDIA_ERROR:
3293
+ case Hls.ErrorTypes.MEDIA_ERROR:
3505
3294
  var _this_hls1;
3506
3295
  (_this_hls1 = _this.hls) === null || _this_hls1 === void 0 ? void 0 : _this_hls1.recoverMediaError();
3507
3296
  break;
@@ -3555,6 +3344,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3555
3344
  errorMessage += ". Caused by: ".concat(causeMessage);
3556
3345
  }
3557
3346
  }
3347
+ _this.pushDebugLog("error", "ad", errorMessage, _object_spread({}, errorPayload ? {
3348
+ payload: errorPayload
3349
+ } : {}));
3558
3350
  console.error("[AD-ERROR]", errorMessage, errorPayload || "");
3559
3351
  _this.adLayer.stop().catch(function() {});
3560
3352
  _this.handleAdFailure();
@@ -3593,7 +3385,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3593
3385
  }
3594
3386
  });
3595
3387
  this.adLayer.on("content_resume", function() {
3388
+ var _ref, _ref1;
3389
+ var _this_savedMutedStateBeforeScte, _this_savedMutedStateBeforeScte1;
3596
3390
  var remaining = _this.getRemainingAdMs();
3391
+ var breakMuted = (_ref = (_this_savedMutedStateBeforeScte = _this.savedMutedStateBeforeScte) === null || _this_savedMutedStateBeforeScte === void 0 ? void 0 : _this_savedMutedStateBeforeScte.muted) !== null && _ref !== void 0 ? _ref : _this.adLayer.getOriginalMutedState();
3392
+ var breakVolume = (_ref1 = (_this_savedMutedStateBeforeScte1 = _this.savedMutedStateBeforeScte) === null || _this_savedMutedStateBeforeScte1 === void 0 ? void 0 : _this_savedMutedStateBeforeScte1.volume) !== null && _ref1 !== void 0 ? _ref1 : _this.adLayer.getOriginalVolume();
3597
3393
  if (_this.config.debugAdTiming) {
3598
3394
  console.log("[StormcloudVideoPlayer] content_resume received, inAdBreak=%s, remaining=%s, preloadedTokens=%d, pendingNext=%s", _this.inAdBreak, remaining, _this.preloadedTokens.length, !!_this.pendingNextAdBids);
3599
3395
  }
@@ -3608,7 +3404,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3608
3404
  return;
3609
3405
  }
3610
3406
  _this.consecutiveFailures = 0;
3611
- if (!_this.video.muted) {
3407
+ if (!_this.config.disableFiller && !_this.video.muted) {
3612
3408
  _this.video.muted = true;
3613
3409
  _this.video.volume = 0;
3614
3410
  }
@@ -3675,6 +3471,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3675
3471
  if (!_this.config.disableFiller) {
3676
3472
  _this.showPlaceholderLayer();
3677
3473
  _this.adLayer.showPlaceholder();
3474
+ } else {
3475
+ _this.adLayer.hidePlaceholder();
3476
+ if (_this.video.muted !== breakMuted) {
3477
+ _this.video.muted = breakMuted;
3478
+ }
3479
+ if (Math.abs(_this.video.volume - breakVolume) > 0.01) {
3480
+ _this.video.volume = breakVolume;
3481
+ }
3482
+ if (_this.video.paused) {
3483
+ var _this_video_play;
3484
+ (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
3485
+ }
3678
3486
  }
3679
3487
  _this.stopContinuousFetching();
3680
3488
  return;
@@ -3980,6 +3788,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3980
3788
  value: function onScte35Marker(marker) {
3981
3789
  var _this = this;
3982
3790
  if (this.config.disableAds) return;
3791
+ this.pushScteMarker(marker);
3792
+ this.pushDebugLog("info", "scte35", "SCTE-35 marker detected", {
3793
+ type: marker.type,
3794
+ ptsSeconds: marker.ptsSeconds,
3795
+ durationSeconds: marker.durationSeconds,
3796
+ currentTime: this.video.currentTime
3797
+ });
3983
3798
  if (this.config.debugAdTiming) {
3984
3799
  console.log("[StormcloudVideoPlayer] SCTE-35 marker detected:", {
3985
3800
  type: marker.type,
@@ -4402,6 +4217,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4402
4217
  return this.totalAdsInBreak;
4403
4218
  }
4404
4219
  },
4220
+ {
4221
+ key: "getRemainingAdSeconds",
4222
+ value: function getRemainingAdSeconds() {
4223
+ var remainingMs = this.getRemainingAdMs();
4224
+ if (!Number.isFinite(remainingMs) || remainingMs <= 0 || remainingMs === Number.MAX_SAFE_INTEGER) {
4225
+ return 0;
4226
+ }
4227
+ return Math.ceil(remainingMs / 1e3);
4228
+ }
4229
+ },
4405
4230
  {
4406
4231
  key: "isAdPlaying",
4407
4232
  value: function isAdPlaying() {
@@ -5631,7 +5456,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5631
5456
  });
5632
5457
  };
5633
5458
  var onManifestParsedRestore = function onManifestParsedRestore1() {
5634
- hlsRef.off(Hls2.Events.MANIFEST_PARSED, onManifestParsedRestore);
5459
+ hlsRef.off(Hls.Events.MANIFEST_PARSED, onManifestParsedRestore);
5635
5460
  if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5636
5461
  if (videoRef.muted !== savedMuted) videoRef.muted = savedMuted;
5637
5462
  if (Math.abs(videoRef.volume - savedVolume) > 0.01) videoRef.volume = savedVolume;
@@ -5644,7 +5469,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5644
5469
  }
5645
5470
  }
5646
5471
  };
5647
- hlsRef.on(Hls2.Events.MANIFEST_PARSED, onManifestParsedRestore);
5472
+ hlsRef.on(Hls.Events.MANIFEST_PARSED, onManifestParsedRestore);
5648
5473
  var pipelineDelayMs = 300;
5649
5474
  if (debugEnabled) {
5650
5475
  console.log("[StormcloudVideoPlayer] Smart TV: waiting ".concat(pipelineDelayMs, "ms for hardware pipeline release before re-attach"));
@@ -5838,6 +5663,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5838
5663
  if (!this.config.debugAdTiming) {
5839
5664
  return;
5840
5665
  }
5666
+ this.pushDebugLog("info", "ad-state", event, extra);
5841
5667
  console.log("[StormcloudVideoPlayer][AdState]", _object_spread({
5842
5668
  event: event,
5843
5669
  timestamp: /* @__PURE__ */ new Date().toISOString(),
@@ -5857,6 +5683,59 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5857
5683
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
5858
5684
  }
5859
5685
  },
5686
+ {
5687
+ key: "pushScteMarker",
5688
+ value: function pushScteMarker(marker) {
5689
+ if (!this.config.debugAdTiming) return;
5690
+ this.scteMarkerHistory.push({
5691
+ timestampMs: Date.now(),
5692
+ marker: marker
5693
+ });
5694
+ if (this.scteMarkerHistory.length > DEBUG_HISTORY_LIMIT) {
5695
+ this.scteMarkerHistory = this.scteMarkerHistory.slice(-DEBUG_HISTORY_LIMIT);
5696
+ }
5697
+ }
5698
+ },
5699
+ {
5700
+ key: "pushDebugLog",
5701
+ value: function pushDebugLog(level, category, message, details) {
5702
+ if (!this.config.debugAdTiming) return;
5703
+ this.debugLogEntries.push(_object_spread({
5704
+ timestampMs: Date.now(),
5705
+ level: level,
5706
+ category: category,
5707
+ message: message
5708
+ }, details ? {
5709
+ details: details
5710
+ } : {}));
5711
+ if (this.debugLogEntries.length > DEBUG_HISTORY_LIMIT) {
5712
+ this.debugLogEntries = this.debugLogEntries.slice(-DEBUG_HISTORY_LIMIT);
5713
+ }
5714
+ }
5715
+ },
5716
+ {
5717
+ key: "getRecentScteMarkers",
5718
+ value: function getRecentScteMarkers() {
5719
+ return this.scteMarkerHistory.map(function(entry) {
5720
+ return _object_spread({
5721
+ timestampMs: entry.timestampMs,
5722
+ type: entry.marker.type
5723
+ }, entry.marker.ptsSeconds !== void 0 ? {
5724
+ ptsSeconds: entry.marker.ptsSeconds
5725
+ } : {}, entry.marker.durationSeconds !== void 0 ? {
5726
+ durationSeconds: entry.marker.durationSeconds
5727
+ } : {}, entry.marker.raw !== void 0 ? {
5728
+ raw: entry.marker.raw
5729
+ } : {});
5730
+ });
5731
+ }
5732
+ },
5733
+ {
5734
+ key: "getDebugLogs",
5735
+ value: function getDebugLogs() {
5736
+ return this.debugLogEntries.slice();
5737
+ }
5738
+ },
5860
5739
  {
5861
5740
  key: "toggleMute",
5862
5741
  value: function toggleMute() {
@@ -6102,13 +5981,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6102
5981
  (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.destroy();
6103
5982
  (_this_adLayer = this.adLayer) === null || _this_adLayer === void 0 ? void 0 : _this_adLayer.destroy();
6104
5983
  this.consecutiveFailures = 0;
5984
+ this.debugLogEntries = [];
5985
+ this.scteMarkerHistory = [];
6105
5986
  }
6106
5987
  }
6107
5988
  ]);
6108
5989
  return StormcloudVideoPlayer;
6109
5990
  }();
6110
5991
  // src/ui/StormcloudVideoPlayer.tsx
6111
- import { FaPlay, FaPause, FaVolumeUp, FaVolumeMute, FaVolumeDown, FaExpand, FaCompress } from "react-icons/fa";
5992
+ import { FaPlay, FaPause, FaVolumeUp, FaVolumeMute, FaVolumeDown, FaExpand, FaCompress, FaCog, FaTimes } from "react-icons/fa";
6112
5993
  // src/ui/OverlayRenderer.tsx
6113
5994
  import React, { useEffect, useRef, useState, useCallback } from "react";
6114
5995
  // src/utils/overlays.ts
@@ -7215,10 +7096,12 @@ var CRITICAL_PROPS = [
7215
7096
  "allowNativeHls",
7216
7097
  "licenseKey",
7217
7098
  "lowLatencyMode",
7218
- "driftToleranceMs"
7099
+ "driftToleranceMs",
7100
+ "debugAdTiming"
7219
7101
  ];
7220
7102
  var CONTROLS_HIDE_DELAY = 3e3;
7221
7103
  var DEFAULT_PLAYER_ASPECT_RATIO = 16 / 9;
7104
+ var DEBUG_PANEL_MARKER_LIMIT = 12;
7222
7105
  var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7223
7106
  var src = props.src, autoplay = props.autoplay, muted = props.muted, lowLatencyMode = props.lowLatencyMode, allowNativeHls = props.allowNativeHls, driftToleranceMs = props.driftToleranceMs, immediateManifestAds = props.immediateManifestAds, debugAdTiming = props.debugAdTiming, showCustomControls = props.showCustomControls, hideLoadingIndicator = props.hideLoadingIndicator, onVolumeToggle = props.onVolumeToggle, onFullscreenToggle = props.onFullscreenToggle, onControlClick = props.onControlClick, onReady = props.onReady, wrapperClassName = props.wrapperClassName, wrapperStyle = props.wrapperStyle, className = props.className, style = props.style, controls = props.controls, playsInline = props.playsInline, preload = props.preload, poster = props.poster, children = props.children, licenseKey = props.licenseKey, minSegmentsBeforePlay = props.minSegmentsBeforePlay, disableAds = props.disableAds, disableFiller = props.disableFiller, swirlProjectId = props.swirlProjectId, restVideoAttrs = _object_without_properties(props, [
7224
7107
  "src",
@@ -7258,7 +7141,8 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7258
7141
  var _React2_useState = _sliced_to_array(React2.useState({
7259
7142
  showAds: false,
7260
7143
  currentIndex: 0,
7261
- totalAds: 0
7144
+ totalAds: 0,
7145
+ remainingSeconds: 0
7262
7146
  }), 2), adStatus = _React2_useState[0], setAdStatus = _React2_useState[1];
7263
7147
  var _React2_useState1 = _sliced_to_array(React2.useState(true), 2), shouldShowNativeControls = _React2_useState1[0], setShouldShowNativeControls = _React2_useState1[1];
7264
7148
  var _React2_useState2 = _sliced_to_array(React2.useState(false), 2), isMuted = _React2_useState2[0], setIsMuted = _React2_useState2[1];
@@ -7280,6 +7164,8 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7280
7164
  var _React2_useState18 = _sliced_to_array(React2.useState(typeof window !== "undefined" ? window.innerWidth : 1920), 2), viewportWidth = _React2_useState18[0], setViewportWidth = _React2_useState18[1];
7281
7165
  var _React2_useState19 = _sliced_to_array(React2.useState(typeof window !== "undefined" ? window.innerHeight > window.innerWidth : false), 2), isPortrait = _React2_useState19[0], setIsPortrait = _React2_useState19[1];
7282
7166
  var _React2_useState20 = _sliced_to_array(React2.useState(DEFAULT_PLAYER_ASPECT_RATIO), 2), playerAspectRatio = _React2_useState20[0], setPlayerAspectRatio = _React2_useState20[1];
7167
+ var _React2_useState21 = _sliced_to_array(React2.useState(false), 2), showDebugPanel = _React2_useState21[0], setShowDebugPanel = _React2_useState21[1];
7168
+ var _React2_useState22 = _sliced_to_array(React2.useState([]), 2), debugMarkers = _React2_useState22[0], setDebugMarkers = _React2_useState22[1];
7283
7169
  var getResponsiveScale = function getResponsiveScale() {
7284
7170
  if (viewportWidth < 480) return 0.7;
7285
7171
  if (viewportWidth < 768) return 0.8;
@@ -7287,6 +7173,28 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7287
7173
  return 1;
7288
7174
  };
7289
7175
  var responsiveScale = getResponsiveScale();
7176
+ var formatDebugClock = function formatDebugClock(timestampMs) {
7177
+ try {
7178
+ var localTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
7179
+ return new Date(timestampMs).toLocaleTimeString("en-GB", {
7180
+ hour12: false,
7181
+ hour: "2-digit",
7182
+ minute: "2-digit",
7183
+ second: "2-digit",
7184
+ timeZone: localTimeZone
7185
+ });
7186
+ } catch (unused) {
7187
+ return "--:--:--";
7188
+ }
7189
+ };
7190
+ var formatDebugRaw = function formatDebugRaw(raw) {
7191
+ if (!raw || (typeof raw === "undefined" ? "undefined" : _type_of(raw)) !== "object") return "";
7192
+ var obj = raw;
7193
+ if (typeof obj.tag === "string") return obj.tag;
7194
+ if (typeof obj.id3 === "string") return "ID3";
7195
+ if (typeof obj.splice_command_type === "number") return "binary splice";
7196
+ return "marker";
7197
+ };
7290
7198
  var resetControlsTimer = useCallback2(function() {
7291
7199
  if (controlsTimerRef.current) {
7292
7200
  clearTimeout(controlsTimerRef.current);
@@ -7510,15 +7418,17 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7510
7418
  var showAds = showAdsFromMethod || showAdsFromAttribute;
7511
7419
  var currentIndex = playerRef.current.getCurrentAdIndex();
7512
7420
  var totalAds = playerRef.current.getTotalAdsInBreak();
7421
+ var remainingSeconds = playerRef.current.getRemainingAdSeconds();
7513
7422
  setAdStatus(function(prev) {
7514
- if (prev.showAds !== showAds || prev.currentIndex !== currentIndex || prev.totalAds !== totalAds) {
7423
+ if (prev.showAds !== showAds || prev.currentIndex !== currentIndex || prev.totalAds !== totalAds || prev.remainingSeconds !== remainingSeconds) {
7515
7424
  if (showAds && !prev.showAds) {
7516
7425
  setShowCenterPlay(false);
7517
7426
  }
7518
7427
  return {
7519
7428
  showAds: showAds,
7520
7429
  currentIndex: currentIndex,
7521
- totalAds: totalAds
7430
+ totalAds: totalAds,
7431
+ remainingSeconds: remainingSeconds
7522
7432
  };
7523
7433
  }
7524
7434
  return prev;
@@ -7667,6 +7577,26 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7667
7577
  }
7668
7578
  };
7669
7579
  }, []);
7580
+ useEffect2(function() {
7581
+ if (!debugAdTiming) {
7582
+ setShowDebugPanel(false);
7583
+ setDebugMarkers([]);
7584
+ return;
7585
+ }
7586
+ var updateDebugData = function updateDebugData() {
7587
+ var player = playerRef.current;
7588
+ if (!player) return;
7589
+ setDebugMarkers(player.getRecentScteMarkers().slice(-DEBUG_PANEL_MARKER_LIMIT).reverse());
7590
+ };
7591
+ updateDebugData();
7592
+ var interval = window.setInterval(updateDebugData, 500);
7593
+ return function() {
7594
+ return clearInterval(interval);
7595
+ };
7596
+ }, [
7597
+ debugAdTiming,
7598
+ criticalPropsKey
7599
+ ]);
7670
7600
  var handleWrapperMouseMove = useCallback2(function() {
7671
7601
  resetControlsTimer();
7672
7602
  }, [
@@ -7685,7 +7615,7 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7685
7615
  return /* @__PURE__ */ jsxs2(Fragment2, {
7686
7616
  children: [
7687
7617
  /* @__PURE__ */ jsx2("style", {
7688
- children: "\n @keyframes sc-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n @keyframes sc-loading-glow {\n 0%, 100% { opacity: 0.85; transform: scale(1); }\n 50% { opacity: 1; transform: scale(1.05); }\n }\n @keyframes sc-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.6; }\n }\n @keyframes sc-fade-in {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .sc-wrapper:fullscreen,\n .sc-wrapper:has(*:fullscreen) {\n border-radius: 0 !important;\n box-shadow: none !important;\n width: 100vw !important;\n height: 100vh !important;\n max-width: 100vw !important;\n max-height: 100vh !important;\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n z-index: 999999 !important;\n background: #000 !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n }\n .sc-ctrl-btn {\n background: none;\n border: none;\n color: #fff;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n padding: 8px;\n transition: background 0.15s ease, opacity 0.15s ease;\n opacity: 0.9;\n }\n .sc-ctrl-btn:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.1);\n }\n .sc-ctrl-btn:active {\n opacity: 0.7;\n }\n .sc-controls-bar {\n transition: opacity 0.35s ease, transform 0.35s ease;\n }\n .sc-progress-track:hover .sc-progress-thumb {\n transform: translate(-50%, -50%) scale(1) !important;\n }\n .sc-loading-hidden .sc-loading-indicator {\n display: none !important;\n }\n "
7618
+ children: "\n @keyframes sc-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n @keyframes sc-loading-glow {\n 0%, 100% { opacity: 0.85; transform: scale(1); }\n 50% { opacity: 1; transform: scale(1.05); }\n }\n @keyframes sc-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.6; }\n }\n @keyframes sc-fade-in {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .sc-wrapper:fullscreen,\n .sc-wrapper:has(*:fullscreen) {\n border-radius: 0 !important;\n box-shadow: none !important;\n width: 100vw !important;\n height: 100vh !important;\n max-width: 100vw !important;\n max-height: 100vh !important;\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n z-index: 999999 !important;\n background: #000 !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n }\n .sc-ctrl-btn {\n background: none;\n border: none;\n color: #fff;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n padding: 8px;\n transition: background 0.15s ease, opacity 0.15s ease;\n opacity: 0.9;\n }\n .sc-ctrl-btn:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.1);\n }\n .sc-ctrl-btn:active {\n opacity: 0.7;\n }\n .sc-controls-bar {\n transition: opacity 0.35s ease, transform 0.35s ease;\n }\n .sc-progress-track:hover .sc-progress-thumb {\n transform: translate(-50%, -50%) scale(1) !important;\n }\n .sc-loading-hidden .sc-loading-indicator {\n display: none !important;\n }\n .sc-debug-scroll::-webkit-scrollbar {\n width: 8px;\n }\n .sc-debug-scroll::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.22);\n border-radius: 4px;\n }\n .sc-debug-scroll {\n overflow-x: hidden !important;\n }\n "
7689
7619
  }),
7690
7620
  /* @__PURE__ */ jsxs2("div", {
7691
7621
  ref: wrapperRef,
@@ -7908,6 +7838,197 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7908
7838
  " of ",
7909
7839
  Math.max(adStatus.totalAds, adStatus.currentIndex)
7910
7840
  ]
7841
+ }),
7842
+ adStatus.remainingSeconds > 0 && /* @__PURE__ */ jsxs2("div", {
7843
+ style: {
7844
+ background: "rgba(0, 0, 0, 0.5)",
7845
+ backdropFilter: "blur(12px)",
7846
+ color: "rgba(255, 255, 255, 0.9)",
7847
+ padding: "".concat(4 * responsiveScale, "px ").concat(10 * responsiveScale, "px"),
7848
+ borderRadius: "6px",
7849
+ fontSize: "".concat(11 * responsiveScale, "px"),
7850
+ fontWeight: "600",
7851
+ border: "1px solid rgba(255, 255, 255, 0.08)"
7852
+ },
7853
+ children: [
7854
+ adStatus.remainingSeconds,
7855
+ "s"
7856
+ ]
7857
+ })
7858
+ ]
7859
+ }),
7860
+ debugAdTiming && !showLicenseWarning && !showDebugPanel && /* @__PURE__ */ jsx2("button", {
7861
+ className: "sc-ctrl-btn",
7862
+ onClick: function onClick() {
7863
+ setShowDebugPanel(function(prev) {
7864
+ return !prev;
7865
+ });
7866
+ resetControlsTimer();
7867
+ },
7868
+ style: {
7869
+ position: "absolute",
7870
+ top: "".concat(12 * responsiveScale, "px"),
7871
+ right: "".concat(12 * responsiveScale, "px"),
7872
+ zIndex: 61,
7873
+ width: "".concat(34 * responsiveScale, "px"),
7874
+ height: "".concat(34 * responsiveScale, "px"),
7875
+ borderRadius: "50%",
7876
+ background: showDebugPanel ? "rgba(255, 255, 255, 0.2)" : "rgba(0, 0, 0, 0.55)",
7877
+ border: "1px solid rgba(255, 255, 255, 0.2)",
7878
+ backdropFilter: "blur(12px)",
7879
+ WebkitBackdropFilter: "blur(12px)"
7880
+ },
7881
+ title: showDebugPanel ? "Hide debug panel" : "Show debug panel",
7882
+ children: /* @__PURE__ */ jsx2(FaCog, {
7883
+ size: Math.max(12, 15 * responsiveScale)
7884
+ })
7885
+ }),
7886
+ debugAdTiming && showDebugPanel && !showLicenseWarning && /* @__PURE__ */ jsxs2("div", {
7887
+ style: {
7888
+ position: "absolute",
7889
+ top: "".concat(12 * responsiveScale, "px"),
7890
+ right: "".concat(12 * responsiveScale, "px"),
7891
+ width: "".concat(Math.min(440, Math.max(320, viewportWidth * 0.42)), "px"),
7892
+ maxWidth: "92vw",
7893
+ height: isPortrait ? "52vh" : "420px",
7894
+ maxHeight: "58vh",
7895
+ zIndex: 60,
7896
+ background: "rgba(10, 10, 10, 0.74)",
7897
+ border: "1px solid rgba(255, 255, 255, 0.14)",
7898
+ borderRadius: "12px",
7899
+ boxShadow: "0 16px 48px rgba(0, 0, 0, 0.45)",
7900
+ backdropFilter: "blur(16px)",
7901
+ WebkitBackdropFilter: "blur(16px)",
7902
+ color: "rgba(255,255,255,0.94)",
7903
+ overflow: "hidden"
7904
+ },
7905
+ children: [
7906
+ /* @__PURE__ */ jsxs2("div", {
7907
+ style: {
7908
+ display: "flex",
7909
+ alignItems: "center",
7910
+ justifyContent: "space-between",
7911
+ padding: "10px 12px",
7912
+ borderBottom: "1px solid rgba(255,255,255,0.1)"
7913
+ },
7914
+ children: [
7915
+ /* @__PURE__ */ jsx2("div", {
7916
+ style: {
7917
+ fontSize: "13px",
7918
+ fontWeight: 700,
7919
+ letterSpacing: "0.02em"
7920
+ },
7921
+ children: "Debug Ad Timing"
7922
+ }),
7923
+ /* @__PURE__ */ jsx2("button", {
7924
+ className: "sc-ctrl-btn",
7925
+ onClick: function onClick() {
7926
+ return setShowDebugPanel(false);
7927
+ },
7928
+ style: {
7929
+ padding: "4px",
7930
+ borderRadius: "6px",
7931
+ minWidth: "26px",
7932
+ minHeight: "26px"
7933
+ },
7934
+ title: "Close panel",
7935
+ children: /* @__PURE__ */ jsx2(FaTimes, {
7936
+ size: 12
7937
+ })
7938
+ })
7939
+ ]
7940
+ }),
7941
+ /* @__PURE__ */ jsx2("div", {
7942
+ className: "sc-debug-scroll",
7943
+ style: {
7944
+ padding: "10px 12px 12px",
7945
+ overflowY: "auto",
7946
+ overflowX: "hidden",
7947
+ height: "calc(100% - 46px)",
7948
+ display: "grid",
7949
+ gap: "12px"
7950
+ },
7951
+ children: /* @__PURE__ */ jsxs2("div", {
7952
+ children: [
7953
+ /* @__PURE__ */ jsx2("div", {
7954
+ style: {
7955
+ fontSize: "11px",
7956
+ fontWeight: 700,
7957
+ textTransform: "uppercase",
7958
+ letterSpacing: "0.08em",
7959
+ color: "rgba(255,255,255,0.68)",
7960
+ marginBottom: "8px"
7961
+ },
7962
+ children: "SCTE-35 markers"
7963
+ }),
7964
+ /* @__PURE__ */ jsx2("div", {
7965
+ style: {
7966
+ display: "grid",
7967
+ gap: "6px"
7968
+ },
7969
+ children: debugMarkers.length === 0 ? /* @__PURE__ */ jsx2("div", {
7970
+ style: {
7971
+ fontSize: "12px",
7972
+ color: "rgba(255,255,255,0.55)"
7973
+ },
7974
+ children: "No markers detected yet."
7975
+ }) : debugMarkers.map(function(entry, idx) {
7976
+ return /* @__PURE__ */ jsxs2("div", {
7977
+ style: {
7978
+ display: "grid",
7979
+ gridTemplateColumns: "56px 52px 1fr",
7980
+ gap: "8px",
7981
+ alignItems: "center",
7982
+ fontFamily: "'SF Mono', 'Cascadia Code', monospace",
7983
+ fontSize: "11px",
7984
+ background: "rgba(255,255,255,0.05)",
7985
+ border: "1px solid rgba(255,255,255,0.08)",
7986
+ borderRadius: "8px",
7987
+ padding: "6px 8px"
7988
+ },
7989
+ children: [
7990
+ /* @__PURE__ */ jsx2("span", {
7991
+ style: {
7992
+ color: "rgba(255,255,255,0.68)"
7993
+ },
7994
+ children: formatDebugClock(entry.timestampMs)
7995
+ }),
7996
+ /* @__PURE__ */ jsx2("span", {
7997
+ style: {
7998
+ textTransform: "uppercase",
7999
+ fontWeight: 700,
8000
+ color: entry.type === "start" ? "#34d399" : entry.type === "end" ? "#f87171" : "#fbbf24"
8001
+ },
8002
+ children: entry.type
8003
+ }),
8004
+ /* @__PURE__ */ jsx2("span", {
8005
+ style: {
8006
+ color: "rgba(255,255,255,0.88)"
8007
+ },
8008
+ children: /* @__PURE__ */ jsxs2("span", {
8009
+ style: {
8010
+ display: "inline-block",
8011
+ maxWidth: "100%",
8012
+ verticalAlign: "bottom",
8013
+ overflow: "hidden",
8014
+ textOverflow: "ellipsis",
8015
+ whiteSpace: "nowrap"
8016
+ },
8017
+ children: [
8018
+ entry.durationSeconds != null ? "dur:".concat(entry.durationSeconds.toFixed(2), "s") : "dur:-",
8019
+ " ",
8020
+ entry.ptsSeconds != null ? "pts:".concat(entry.ptsSeconds.toFixed(2)) : "pts:-",
8021
+ " ",
8022
+ formatDebugRaw(entry.raw)
8023
+ ]
8024
+ })
8025
+ })
8026
+ ]
8027
+ }, "".concat(entry.timestampMs, "-").concat(idx));
8028
+ })
8029
+ })
8030
+ ]
8031
+ })
7911
8032
  })
7912
8033
  ]
7913
8034
  }),
@@ -9707,5 +9828,5 @@ var createStormcloudPlayer = function createStormcloudPlayer(playerList, fallbac
9707
9828
  };
9708
9829
  var StormcloudPlayer = createStormcloudPlayer(players_default, players_default[players_default.length - 1]);
9709
9830
  var StormcloudPlayer_default = StormcloudPlayer;
9710
- export { IS_BROWSER, IS_GLOBAL, IS_IOS, IS_SAFARI, SUPPORTS_DASH, SUPPORTS_HLS, StormcloudPlayer_default as StormcloudPlayer, StormcloudVideoPlayer, StormcloudVideoPlayerComponent, canPlay, createStormcloudPlayer, createVastAdLayer, createVastManager, StormcloudVideoPlayerComponent as default, detectBrowser, fetchProjectOverlays, getBrowserConfigOverrides, getBrowserID, getClientInfo, initializePolyfills, isMediaStream, isOverlayActive, lazy, logBrowserInfo, merge, omit, parseQuery, players_default as players, randomString, resolveImageUrl, sendHeartbeat, sendInitialTracking, supportsFeature, supportsModernJS, supportsWebKitPresentationMode, timeStringToSeconds };
9831
+ export { IS_BROWSER, IS_GLOBAL, IS_IOS, IS_SAFARI, SUPPORTS_DASH, SUPPORTS_HLS, StormcloudPlayer_default as StormcloudPlayer, StormcloudVideoPlayer, StormcloudVideoPlayerComponent, canPlay, createAdStormPlayer, createStormcloudPlayer, StormcloudVideoPlayerComponent as default, detectBrowser, fetchProjectOverlays, getBrowserConfigOverrides, getBrowserID, getClientInfo, initializePolyfills, isMediaStream, isOverlayActive, lazy, logBrowserInfo, merge, omit, parseQuery, players_default as players, randomString, resolveImageUrl, sendHeartbeat, sendInitialTracking, supportsFeature, supportsModernJS, supportsWebKitPresentationMode, timeStringToSeconds };
9711
9832
  //# sourceMappingURL=index.js.map