stormcloud-video-player 0.5.4 → 0.5.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.
@@ -51,6 +51,9 @@ declare class StormcloudVideoPlayer {
51
51
  private readonly minAdRequestIntervalMs;
52
52
  private readonly backoffBaseMs;
53
53
  private readonly maxBackoffMs;
54
+ private readonly continuousFetchWallClockBufferMs;
55
+ private readonly continuousFetchMaxIterations;
56
+ private continuousFetchQueueFullConsecutiveWaits;
54
57
  private placeholderContainer;
55
58
  private preloadPool;
56
59
  private readonly maxPreloadPoolSize;
@@ -58,9 +61,10 @@ declare class StormcloudVideoPlayer {
58
61
  private preloadPoolLoopRunning;
59
62
  constructor(config: StormcloudVideoPlayerConfig);
60
63
  private createAdPlayer;
64
+ private getAdPlayerTypeLabel;
61
65
  load(): Promise<void>;
62
66
  private attachImaEventListeners;
63
- private recreateImaController;
67
+ private recreateAdController;
64
68
  private ensurePlaceholderContainer;
65
69
  private showPlaceholderLayer;
66
70
  private hidePlaceholderLayer;
@@ -80,6 +84,7 @@ declare class StormcloudVideoPlayer {
80
84
  private sendHeartbeatIfNeeded;
81
85
  private fetchAdConfiguration;
82
86
  private isPrebidMode;
87
+ private isLgStbDevice;
83
88
  getCurrentAdIndex(): number;
84
89
  getTotalAdsInBreak(): number;
85
90
  private generateVastUrlsWithCorrelators;
@@ -99,6 +104,8 @@ declare class StormcloudVideoPlayer {
99
104
  private continuousFetchLoopRunning;
100
105
  private startContinuousFetching;
101
106
  private continuousFetchLoop;
107
+ private waitForQueueWithBackoff;
108
+ private logQueueState;
102
109
  private stopContinuousFetching;
103
110
  private tryNextAvailableAdWithRateLimit;
104
111
  private tryNextAvailableAd;
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/players/FilePlayer.cjs"],"names":[],"mappings":"AAAA","sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/players/FilePlayer.tsx\nvar FilePlayer_exports = {};\n__export(FilePlayer_exports, {\n default: () => FilePlayer\n});\nmodule.exports = __toCommonJS(FilePlayer_exports);\nvar import_react = require(\"react\");\n\n// src/patterns.ts\nvar HLS_EXTENSIONS = /\\.(m3u8)($|\\?)/i;\nvar HLS_PATHS = /\\/hls\\//i;\nvar DASH_EXTENSIONS = /\\.(mpd)($|\\?)/i;\nvar VIDEO_EXTENSIONS = /\\.(mp4|webm|ogg|avi|mov|wmv|flv|mkv)($|\\?)/i;\nvar AUDIO_EXTENSIONS = /\\.(mp3|wav|ogg|aac|wma|flac|m4a)($|\\?)/i;\nvar canPlay = {\n hls: (url) => {\n if (!url || typeof url !== \"string\") return false;\n return HLS_EXTENSIONS.test(url) || HLS_PATHS.test(url);\n },\n dash: (url) => {\n if (!url || typeof url !== \"string\") return false;\n return DASH_EXTENSIONS.test(url);\n },\n video: (url) => {\n if (!url || typeof url !== \"string\") return false;\n return VIDEO_EXTENSIONS.test(url);\n },\n audio: (url) => {\n if (!url || typeof url !== \"string\") return false;\n return AUDIO_EXTENSIONS.test(url);\n },\n file: (url) => {\n if (!url || typeof url !== \"string\") return false;\n return VIDEO_EXTENSIONS.test(url) || AUDIO_EXTENSIONS.test(url);\n }\n};\n\n// src/players/FilePlayer.tsx\nvar FilePlayer = class extends import_react.Component {\n constructor() {\n super(...arguments);\n this.mounted = false;\n this.ready = false;\n this.load = () => {\n if (!this.props.videoElement || !this.props.src) return;\n const video = this.props.videoElement;\n const handleLoadedMetadata = () => {\n if (this.mounted && !this.ready) {\n this.ready = true;\n this.props.onReady?.();\n }\n };\n const handlePlay = () => {\n if (this.mounted) {\n this.props.onPlay?.();\n }\n };\n const handlePause = () => {\n if (this.mounted) {\n this.props.onPause?.();\n }\n };\n const handleEnded = () => {\n if (this.mounted) {\n this.props.onEnded?.();\n }\n };\n const handleError = (error) => {\n if (this.mounted) {\n this.props.onError?.(error);\n }\n };\n const handleLoadedData = () => {\n if (this.mounted) {\n this.props.onLoaded?.();\n }\n };\n video.addEventListener(\"loadedmetadata\", handleLoadedMetadata);\n video.addEventListener(\"play\", handlePlay);\n video.addEventListener(\"pause\", handlePause);\n video.addEventListener(\"ended\", handleEnded);\n video.addEventListener(\"error\", handleError);\n video.addEventListener(\"loadeddata\", handleLoadedData);\n video.src = this.props.src;\n if (this.props.autoplay !== void 0) video.autoplay = this.props.autoplay;\n if (this.props.muted !== void 0) video.muted = this.props.muted;\n if (this.props.loop !== void 0) video.loop = this.props.loop;\n if (this.props.controls !== void 0) video.controls = this.props.controls;\n if (this.props.playsInline !== void 0)\n video.playsInline = this.props.playsInline;\n if (this.props.preload !== void 0)\n video.preload = this.props.preload;\n if (this.props.poster !== void 0) video.poster = this.props.poster;\n this.props.onMount?.(this);\n return () => {\n video.removeEventListener(\"loadedmetadata\", handleLoadedMetadata);\n video.removeEventListener(\"play\", handlePlay);\n video.removeEventListener(\"pause\", handlePause);\n video.removeEventListener(\"ended\", handleEnded);\n video.removeEventListener(\"error\", handleError);\n video.removeEventListener(\"loadeddata\", handleLoadedData);\n };\n };\n this.play = () => {\n if (this.props.videoElement) {\n const video = this.props.videoElement;\n const hasValidSource = video.src || video.currentSrc && video.currentSrc !== \"\" || video.readyState >= 1;\n if (hasValidSource) {\n video.play()?.catch((error) => {\n console.error(\"[FilePlayer] Failed to play:\", error);\n this.props.onError?.(error);\n });\n } else {\n console.warn(\"[FilePlayer] Cannot play: video has no valid source\");\n }\n }\n };\n this.pause = () => {\n if (this.props.videoElement) {\n this.props.videoElement.pause();\n }\n };\n this.stop = () => {\n this.pause();\n if (this.props.videoElement) {\n this.props.videoElement.currentTime = 0;\n }\n };\n this.seekTo = (seconds, keepPlaying) => {\n if (this.props.videoElement) {\n this.props.videoElement.currentTime = seconds;\n if (!keepPlaying) {\n this.pause();\n }\n }\n };\n this.setVolume = (volume) => {\n if (this.props.videoElement) {\n this.props.videoElement.volume = Math.max(0, Math.min(1, volume));\n }\n };\n this.mute = () => {\n if (this.props.videoElement) {\n this.props.videoElement.muted = true;\n }\n };\n this.unmute = () => {\n if (this.props.videoElement) {\n this.props.videoElement.muted = false;\n }\n };\n this.setPlaybackRate = (rate) => {\n if (this.props.videoElement && rate > 0) {\n this.props.videoElement.playbackRate = rate;\n }\n };\n this.setLoop = (loop) => {\n if (this.props.videoElement) {\n this.props.videoElement.loop = loop;\n }\n };\n this.getDuration = () => {\n if (this.props.videoElement && isFinite(this.props.videoElement.duration)) {\n return this.props.videoElement.duration;\n }\n return null;\n };\n this.getCurrentTime = () => {\n if (this.props.videoElement && isFinite(this.props.videoElement.currentTime)) {\n return this.props.videoElement.currentTime;\n }\n return null;\n };\n this.getSecondsLoaded = () => {\n if (this.props.videoElement && this.props.videoElement.buffered.length > 0) {\n return this.props.videoElement.buffered.end(\n this.props.videoElement.buffered.length - 1\n );\n }\n return null;\n };\n this.getInternalPlayer = (key = \"player\") => {\n if (key === \"video\") return this.props.videoElement;\n return null;\n };\n this.enablePIP = async () => {\n if (this.props.videoElement && \"requestPictureInPicture\" in this.props.videoElement) {\n try {\n await this.props.videoElement.requestPictureInPicture();\n } catch (error) {\n console.warn(\"Picture-in-Picture failed:\", error);\n }\n }\n };\n this.disablePIP = async () => {\n if (document.pictureInPictureElement) {\n try {\n await document.exitPictureInPicture();\n } catch (error) {\n console.warn(\"Exit Picture-in-Picture failed:\", error);\n }\n }\n };\n }\n componentDidMount() {\n this.mounted = true;\n this.load();\n }\n componentWillUnmount() {\n this.mounted = false;\n }\n componentDidUpdate(prevProps) {\n if (prevProps.src !== this.props.src) {\n this.load();\n }\n }\n render() {\n return null;\n }\n};\nFilePlayer.displayName = \"FilePlayer\";\nFilePlayer.canPlay = canPlay.file;\n"]}
1
+ {"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/players/FilePlayer.cjs"],"names":["__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","name","all","target","enumerable","__copyProps","to","from","except","desc","key","call"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YACIA,OAAAA,GAAYC,OAAOC,cAAc;QACjCC,mBAAmBF,OAAOG,wBAAwB;QAClDC,MAAAA,SAAAA,GAAAA,GAAoBJ,OAAOK,mBAAmB;;;;;;iCAC9CC,CAAAA,OAAAA,IAAeN,CAAAA,CAAAA,KAAOO,OAAAA,EAAS,CAACC,CAAAA,aAAc,gBAAA,OAAA,KAAA,CAAA,YAAA,GAA9CF;;;;;;;;;;;;4BAEG;;gCAAIG,IAAQC,GAAAA,GACfX,EAAAA,CAAAA,OAAUY,KAAAA,CAAAA,EAAQF,MAAM,eAAA;;;4BADrB,EAAIA;;;;;;4BAC4BA,KAAK;4BAAEG,QAAAA,EAAY,EAAA,CAAA,8BAAA;;;;;;;;;;;YAEtDC,cAAc,qBAACC,IAAIC,MAAMC,QAAQC;;QACnC,IAAIF,EAAAA,OAAQ,CAAA,EAAA,GAAA,EAAOA,qCAAP,SAAOA,KAAG,MAAM,YAAY,OAAOA,SAAS,YAAY;;oBAC7D;;;;0CAAA,uBAAA,IAAA,2BAAA;;;;;;;;;;;;;;;;;;;;;;;4BAAA,GAAIG,MAAJ;kCACH,EAAA,EAAI,CAACZ,CAAAA,CAAAA,WAAaa,IAAI,CAACL,IAAII,QAAQA,OAAAA,CAAQF,QACzCjB,UAAUe,IAAII,KAAK;;;;;;;;;;;;;;;;;;0CAA6F;;wBAFpH,CAAA,OAAK,YAAWd,kBAAkBW,0BAA7B,SAAA,6BAAA,QAAA,yBAAA;;;;;sCAAA;wBAAA,IAAA,GAAA;;;;;;iCAAA,KAAA,IAAA,CAAA,KAAA,CAAA,GAAA,EAAA,QAAA;kCAAA;;;;;mCAAA;;sCAAA;;;;;;;IAGP,OAAA,OAAA,GAAA,QAAA,IAAA","sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/players/FilePlayer.tsx\nvar FilePlayer_exports = {};\n__export(FilePlayer_exports, {\n default: () => FilePlayer\n});\nmodule.exports = __toCommonJS(FilePlayer_exports);\nvar import_react = require(\"react\");\n\n// src/patterns.ts\nvar HLS_EXTENSIONS = /\\.(m3u8)($|\\?)/i;\nvar HLS_PATHS = /\\/hls\\//i;\nvar DASH_EXTENSIONS = /\\.(mpd)($|\\?)/i;\nvar VIDEO_EXTENSIONS = /\\.(mp4|webm|ogg|avi|mov|wmv|flv|mkv)($|\\?)/i;\nvar AUDIO_EXTENSIONS = /\\.(mp3|wav|ogg|aac|wma|flac|m4a)($|\\?)/i;\nvar canPlay = {\n hls: (url) => {\n if (!url || typeof url !== \"string\") return false;\n return HLS_EXTENSIONS.test(url) || HLS_PATHS.test(url);\n },\n dash: (url) => {\n if (!url || typeof url !== \"string\") return false;\n return DASH_EXTENSIONS.test(url);\n },\n video: (url) => {\n if (!url || typeof url !== \"string\") return false;\n return VIDEO_EXTENSIONS.test(url);\n },\n audio: (url) => {\n if (!url || typeof url !== \"string\") return false;\n return AUDIO_EXTENSIONS.test(url);\n },\n file: (url) => {\n if (!url || typeof url !== \"string\") return false;\n return VIDEO_EXTENSIONS.test(url) || AUDIO_EXTENSIONS.test(url);\n }\n};\n\n// src/players/FilePlayer.tsx\nvar FilePlayer = class extends import_react.Component {\n constructor() {\n super(...arguments);\n this.mounted = false;\n this.ready = false;\n this.load = () => {\n if (!this.props.videoElement || !this.props.src) return;\n const video = this.props.videoElement;\n const handleLoadedMetadata = () => {\n if (this.mounted && !this.ready) {\n this.ready = true;\n this.props.onReady?.();\n }\n };\n const handlePlay = () => {\n if (this.mounted) {\n this.props.onPlay?.();\n }\n };\n const handlePause = () => {\n if (this.mounted) {\n this.props.onPause?.();\n }\n };\n const handleEnded = () => {\n if (this.mounted) {\n this.props.onEnded?.();\n }\n };\n const handleError = (error) => {\n if (this.mounted) {\n this.props.onError?.(error);\n }\n };\n const handleLoadedData = () => {\n if (this.mounted) {\n this.props.onLoaded?.();\n }\n };\n video.addEventListener(\"loadedmetadata\", handleLoadedMetadata);\n video.addEventListener(\"play\", handlePlay);\n video.addEventListener(\"pause\", handlePause);\n video.addEventListener(\"ended\", handleEnded);\n video.addEventListener(\"error\", handleError);\n video.addEventListener(\"loadeddata\", handleLoadedData);\n video.src = this.props.src;\n if (this.props.autoplay !== void 0) video.autoplay = this.props.autoplay;\n if (this.props.muted !== void 0) video.muted = this.props.muted;\n if (this.props.loop !== void 0) video.loop = this.props.loop;\n if (this.props.controls !== void 0) video.controls = this.props.controls;\n if (this.props.playsInline !== void 0)\n video.playsInline = this.props.playsInline;\n if (this.props.preload !== void 0)\n video.preload = this.props.preload;\n if (this.props.poster !== void 0) video.poster = this.props.poster;\n this.props.onMount?.(this);\n return () => {\n video.removeEventListener(\"loadedmetadata\", handleLoadedMetadata);\n video.removeEventListener(\"play\", handlePlay);\n video.removeEventListener(\"pause\", handlePause);\n video.removeEventListener(\"ended\", handleEnded);\n video.removeEventListener(\"error\", handleError);\n video.removeEventListener(\"loadeddata\", handleLoadedData);\n };\n };\n this.play = () => {\n if (this.props.videoElement) {\n const video = this.props.videoElement;\n const hasValidSource = video.src || video.currentSrc && video.currentSrc !== \"\" || video.readyState >= 1;\n if (hasValidSource) {\n video.play()?.catch((error) => {\n console.error(\"[FilePlayer] Failed to play:\", error);\n this.props.onError?.(error);\n });\n } else {\n console.warn(\"[FilePlayer] Cannot play: video has no valid source\");\n }\n }\n };\n this.pause = () => {\n if (this.props.videoElement) {\n this.props.videoElement.pause();\n }\n };\n this.stop = () => {\n this.pause();\n if (this.props.videoElement) {\n this.props.videoElement.currentTime = 0;\n }\n };\n this.seekTo = (seconds, keepPlaying) => {\n if (this.props.videoElement) {\n this.props.videoElement.currentTime = seconds;\n if (!keepPlaying) {\n this.pause();\n }\n }\n };\n this.setVolume = (volume) => {\n if (this.props.videoElement) {\n this.props.videoElement.volume = Math.max(0, Math.min(1, volume));\n }\n };\n this.mute = () => {\n if (this.props.videoElement) {\n this.props.videoElement.muted = true;\n }\n };\n this.unmute = () => {\n if (this.props.videoElement) {\n this.props.videoElement.muted = false;\n }\n };\n this.setPlaybackRate = (rate) => {\n if (this.props.videoElement && rate > 0) {\n this.props.videoElement.playbackRate = rate;\n }\n };\n this.setLoop = (loop) => {\n if (this.props.videoElement) {\n this.props.videoElement.loop = loop;\n }\n };\n this.getDuration = () => {\n if (this.props.videoElement && isFinite(this.props.videoElement.duration)) {\n return this.props.videoElement.duration;\n }\n return null;\n };\n this.getCurrentTime = () => {\n if (this.props.videoElement && isFinite(this.props.videoElement.currentTime)) {\n return this.props.videoElement.currentTime;\n }\n return null;\n };\n this.getSecondsLoaded = () => {\n if (this.props.videoElement && this.props.videoElement.buffered.length > 0) {\n return this.props.videoElement.buffered.end(\n this.props.videoElement.buffered.length - 1\n );\n }\n return null;\n };\n this.getInternalPlayer = (key = \"player\") => {\n if (key === \"video\") return this.props.videoElement;\n return null;\n };\n this.enablePIP = async () => {\n if (this.props.videoElement && \"requestPictureInPicture\" in this.props.videoElement) {\n try {\n await this.props.videoElement.requestPictureInPicture();\n } catch (error) {\n console.warn(\"Picture-in-Picture failed:\", error);\n }\n }\n };\n this.disablePIP = async () => {\n if (document.pictureInPictureElement) {\n try {\n await document.exitPictureInPicture();\n } catch (error) {\n console.warn(\"Exit Picture-in-Picture failed:\", error);\n }\n }\n };\n }\n componentDidMount() {\n this.mounted = true;\n this.load();\n }\n componentWillUnmount() {\n this.mounted = false;\n }\n componentDidUpdate(prevProps) {\n if (prevProps.src !== this.props.src) {\n this.load();\n }\n }\n render() {\n return null;\n }\n};\nFilePlayer.displayName = \"FilePlayer\";\nFilePlayer.canPlay = canPlay.file;\n"]}
@@ -2058,10 +2058,10 @@ function createHlsAdPlayer(contentVideo, options) {
2058
2058
  if (options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) {
2059
2059
  if (contentVideo.paused) {
2060
2060
  console.log("[HlsAdPlayer] Content video paused in live mode, resuming playback");
2061
- contentVideo.play().catch(function() {});
2062
2061
  } else {
2063
2062
  console.log("[HlsAdPlayer] Content video already playing in live mode");
2064
2063
  }
2064
+ contentVideo.play().catch(function() {});
2065
2065
  }
2066
2066
  emit("content_resume");
2067
2067
  }
@@ -2334,8 +2334,8 @@ function createHlsAdPlayer(contentVideo, options) {
2334
2334
  if (options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) {
2335
2335
  if (contentVideo.paused) {
2336
2336
  console.log("[HlsAdPlayer] Content video paused in live mode, resuming playback on stop");
2337
- contentVideo.play().catch(function() {});
2338
2337
  }
2338
+ contentVideo.play().catch(function() {});
2339
2339
  }
2340
2340
  if (adHls) {
2341
2341
  adHls.destroy();
@@ -2344,6 +2344,8 @@ function createHlsAdPlayer(contentVideo, options) {
2344
2344
  if (adVideoElement) {
2345
2345
  adVideoElement.pause();
2346
2346
  adVideoElement.src = "";
2347
+ adVideoElement.remove();
2348
+ adVideoElement = void 0;
2347
2349
  }
2348
2350
  currentAd = void 0;
2349
2351
  return [
@@ -2994,10 +2996,10 @@ function createPrebidController(contentVideo, options) {
2994
2996
  if (options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) {
2995
2997
  if (contentVideo.paused) {
2996
2998
  console.log("".concat(LOG, " Content video paused in live mode, resuming playback"));
2997
- contentVideo.play().catch(function() {});
2998
2999
  } else {
2999
3000
  console.log("".concat(LOG, " Content video already playing in live mode"));
3000
3001
  }
3002
+ contentVideo.play().catch(function() {});
3001
3003
  }
3002
3004
  emit("content_resume");
3003
3005
  }
@@ -3363,8 +3365,8 @@ function createPrebidController(contentVideo, options) {
3363
3365
  if (options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) {
3364
3366
  if (contentVideo.paused) {
3365
3367
  console.log("".concat(LOG, " Content video paused in live mode, resuming playback on stop"));
3366
- contentVideo.play().catch(function() {});
3367
3368
  }
3369
+ contentVideo.play().catch(function() {});
3368
3370
  }
3369
3371
  if (adHls) {
3370
3372
  adHls.destroy();
@@ -3373,6 +3375,8 @@ function createPrebidController(contentVideo, options) {
3373
3375
  if (adVideoElement) {
3374
3376
  adVideoElement.pause();
3375
3377
  adVideoElement.src = "";
3378
+ adVideoElement.remove();
3379
+ adVideoElement = void 0;
3376
3380
  }
3377
3381
  currentAd = void 0;
3378
3382
  return [
@@ -4141,6 +4145,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4141
4145
  this.minAdRequestIntervalMs = 2500;
4142
4146
  this.backoffBaseMs = 1e3;
4143
4147
  this.maxBackoffMs = 15e3;
4148
+ this.continuousFetchWallClockBufferMs = 3e4;
4149
+ this.continuousFetchMaxIterations = 500;
4150
+ this.continuousFetchQueueFullConsecutiveWaits = 0;
4144
4151
  this.preloadPool = [];
4145
4152
  this.maxPreloadPoolSize = 3;
4146
4153
  this.preloadPoolActive = false;
@@ -4200,6 +4207,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4200
4207
  }
4201
4208
  }
4202
4209
  },
4210
+ {
4211
+ key: "getAdPlayerTypeLabel",
4212
+ value: function getAdPlayerTypeLabel() {
4213
+ var t = this.config.adPlayerType;
4214
+ if (t === "prebid") return "Prebid";
4215
+ if (t === "hls") return "HLS";
4216
+ return "IMA";
4217
+ }
4218
+ },
4203
4219
  {
4204
4220
  key: "load",
4205
4221
  value: function load() {
@@ -4689,6 +4705,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4689
4705
  }
4690
4706
  });
4691
4707
  this.ima.on("content_resume", function() {
4708
+ console.log("[StormcloudVideoPlayer] content_resume received, inAdBreak=%s, remaining=%s", _this.inAdBreak, _this.getRemainingAdMs());
4692
4709
  if (!_this.video.muted) {
4693
4710
  _this.video.muted = true;
4694
4711
  _this.video.volume = 0;
@@ -4719,15 +4736,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4719
4736
  }
4720
4737
  },
4721
4738
  {
4722
- key: "recreateImaController",
4723
- value: function recreateImaController() {
4739
+ key: "recreateAdController",
4740
+ value: function recreateAdController() {
4741
+ var label = this.getAdPlayerTypeLabel();
4724
4742
  if (this.config.debugAdTiming) {
4725
- console.log("[StormcloudVideoPlayer] Recreating ImaController for new ad");
4743
+ console.log("[StormcloudVideoPlayer] Recreating ad controller (".concat(label, ") for new ad"));
4726
4744
  }
4727
4745
  var shouldShowPlaceholder = this.inAdBreak && this.showAds;
4728
4746
  if (shouldShowPlaceholder && this.ima) {
4729
4747
  if (this.config.debugAdTiming) {
4730
- console.log("[StormcloudVideoPlayer] Showing placeholder before destroying old ImaController");
4748
+ console.log("[StormcloudVideoPlayer] Showing placeholder before destroying old ".concat(label, " controller"));
4731
4749
  }
4732
4750
  this.showPlaceholderLayer();
4733
4751
  this.ima.showPlaceholder();
@@ -4740,7 +4758,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4740
4758
  this.video.volume = 0;
4741
4759
  } catch (error) {
4742
4760
  if (this.config.debugAdTiming) {
4743
- console.warn("[StormcloudVideoPlayer] Error destroying old ImaController:", error);
4761
+ console.warn("[StormcloudVideoPlayer] Error destroying old ".concat(label, " controller:"), error);
4744
4762
  }
4745
4763
  }
4746
4764
  }
@@ -4820,7 +4838,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4820
4838
  });
4821
4839
  }
4822
4840
  if (this.config.debugAdTiming) {
4823
- console.log("[StormcloudVideoPlayer] Showing placeholder layer (between content and IMA)");
4841
+ console.log("[StormcloudVideoPlayer] Showing placeholder layer (between content and ".concat(this.getAdPlayerTypeLabel(), ")"));
4824
4842
  }
4825
4843
  }
4826
4844
  },
@@ -5532,6 +5550,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5532
5550
  return this.config.adPlayerType === "prebid";
5533
5551
  }
5534
5552
  },
5553
+ {
5554
+ key: "isLgStbDevice",
5555
+ value: function isLgStbDevice() {
5556
+ if (typeof navigator === "undefined" || !navigator.userAgent) return false;
5557
+ var ua = navigator.userAgent;
5558
+ return /Web0S|webOS|LG Browser|LGSTB/i.test(ua);
5559
+ }
5560
+ },
5535
5561
  {
5536
5562
  key: "getCurrentAdIndex",
5537
5563
  value: function getCurrentAdIndex() {
@@ -6346,15 +6372,34 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6346
6372
  key: "continuousFetchLoop",
6347
6373
  value: function continuousFetchLoop(baseVastUrl) {
6348
6374
  return _async_to_generator(function() {
6349
- var _this, _loop, _ret;
6375
+ var loopIterations, _this, _loop, _ret;
6350
6376
  return _ts_generator(this, function(_state) {
6351
6377
  switch(_state.label){
6352
6378
  case 0:
6379
+ loopIterations = 0;
6380
+ _state.label = 1;
6381
+ case 1:
6382
+ _state.trys.push([
6383
+ 1,
6384
+ ,
6385
+ 5,
6386
+ 6
6387
+ ]);
6353
6388
  _loop = function() {
6354
- var remaining, maxQueueSize, newAdUrl, queuedUrlsPreview, generationDelay;
6389
+ var remaining, elapsedMs, maxLoopMs, maxQueueSize, newAdUrl, queuedUrlsPreview, generationDelay;
6355
6390
  return _ts_generator(this, function(_state) {
6356
6391
  switch(_state.label){
6357
6392
  case 0:
6393
+ loopIterations++;
6394
+ if (loopIterations > _this.continuousFetchMaxIterations) {
6395
+ if (_this.config.debugAdTiming) {
6396
+ console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Max iterations reached (".concat(_this.continuousFetchMaxIterations, "), stopping URL generation"));
6397
+ }
6398
+ return [
6399
+ 2,
6400
+ "break"
6401
+ ];
6402
+ }
6358
6403
  remaining = _this.getRemainingAdMs();
6359
6404
  if (remaining <= 0) {
6360
6405
  if (_this.config.debugAdTiming) {
@@ -6365,6 +6410,19 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6365
6410
  "break"
6366
6411
  ];
6367
6412
  }
6413
+ if (_this.currentAdBreakStartWallClockMs != null && _this.expectedAdBreakDurationMs != null) {
6414
+ elapsedMs = Date.now() - _this.currentAdBreakStartWallClockMs;
6415
+ maxLoopMs = _this.expectedAdBreakDurationMs + _this.continuousFetchWallClockBufferMs;
6416
+ if (elapsedMs >= maxLoopMs) {
6417
+ if (_this.config.debugAdTiming) {
6418
+ console.log("[CONTINUOUS-FETCH] ⏹️ Wall-clock limit reached (".concat(elapsedMs, "ms >= ").concat(maxLoopMs, "ms), stopping URL generation"));
6419
+ }
6420
+ return [
6421
+ 2,
6422
+ "break"
6423
+ ];
6424
+ }
6425
+ }
6368
6426
  if (_this.consecutiveFailures >= _this.maxConsecutiveFailures) {
6369
6427
  if (_this.config.debugAdTiming) {
6370
6428
  console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Too many consecutive failures (".concat(_this.consecutiveFailures, "), stopping URL generation"));
@@ -6386,46 +6444,63 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6386
6444
  maxQueueSize = 5;
6387
6445
  if (!(_this.adRequestQueue.length >= maxQueueSize)) return [
6388
6446
  3,
6389
- 2
6447
+ 4
6448
+ ];
6449
+ if (!(_this.isPrebidMode() && _this.isLgStbDevice())) return [
6450
+ 3,
6451
+ 1
6390
6452
  ];
6453
+ _this.adRequestQueue.shift();
6454
+ _this.adRequestQueue.push("");
6455
+ _this.totalAdsInBreak++;
6456
+ _this.continuousFetchQueueFullConsecutiveWaits = 0;
6391
6457
  if (_this.config.debugAdTiming) {
6392
- console.log("[CONTINUOUS-FETCH] ⏸️ URL queue full (".concat(_this.adRequestQueue.length, "), waiting..."));
6458
+ console.log("[CONTINUOUS-FETCH] Prebid slot rotated (queue full, dropped oldest); queue: ".concat(_this.adRequestQueue.length));
6393
6459
  }
6394
6460
  return [
6395
- 4,
6396
- new Promise(function(resolve) {
6397
- return setTimeout(resolve, 1e3);
6398
- })
6461
+ 3,
6462
+ 3
6399
6463
  ];
6400
6464
  case 1:
6465
+ return [
6466
+ 4,
6467
+ _this.waitForQueueWithBackoff()
6468
+ ];
6469
+ case 2:
6401
6470
  _state.sent();
6402
6471
  return [
6403
6472
  2,
6404
6473
  "continue"
6405
6474
  ];
6406
- case 2:
6475
+ case 3:
6476
+ return [
6477
+ 3,
6478
+ 8
6479
+ ];
6480
+ case 4:
6407
6481
  if (!_this.isPrebidMode()) return [
6408
6482
  3,
6409
- 3
6483
+ 5
6410
6484
  ];
6411
6485
  _this.adRequestQueue.push("");
6412
6486
  _this.totalAdsInBreak++;
6487
+ _this.continuousFetchQueueFullConsecutiveWaits = 0;
6413
6488
  if (_this.config.debugAdTiming) {
6414
6489
  console.log("[CONTINUOUS-FETCH] Prebid auction slot queued (queue: ".concat(_this.adRequestQueue.length, ")"));
6415
6490
  }
6416
6491
  return [
6417
6492
  3,
6418
- 6
6493
+ 8
6419
6494
  ];
6420
- case 3:
6495
+ case 5:
6421
6496
  if (!baseVastUrl) return [
6422
6497
  3,
6423
- 6
6498
+ 8
6424
6499
  ];
6425
6500
  newAdUrl = _this.generateVastUrlsWithCorrelators(baseVastUrl, 1)[0];
6426
6501
  if (!(!newAdUrl || _this.failedVastUrls.has(newAdUrl) || _this.isUrlInCooldown(newAdUrl))) return [
6427
6502
  3,
6428
- 5
6503
+ 7
6429
6504
  ];
6430
6505
  return [
6431
6506
  4,
@@ -6433,13 +6508,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6433
6508
  return setTimeout(resolve, 500);
6434
6509
  })
6435
6510
  ];
6436
- case 4:
6511
+ case 6:
6437
6512
  _state.sent();
6438
6513
  return [
6439
6514
  2,
6440
6515
  "continue"
6441
6516
  ];
6442
- case 5:
6517
+ case 7:
6443
6518
  if (_this.config.debugAdTiming) {
6444
6519
  queuedUrlsPreview = _to_consumable_array(_this.adRequestQueue).concat([
6445
6520
  newAdUrl
@@ -6450,8 +6525,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6450
6525
  }
6451
6526
  _this.adRequestQueue.push(newAdUrl);
6452
6527
  _this.totalAdsInBreak++;
6453
- _state.label = 6;
6454
- case 6:
6528
+ _this.continuousFetchQueueFullConsecutiveWaits = 0;
6529
+ _state.label = 8;
6530
+ case 8:
6455
6531
  generationDelay = _this.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, _this.consecutiveFailures), 5e3) : 500;
6456
6532
  return [
6457
6533
  4,
@@ -6459,7 +6535,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6459
6535
  return setTimeout(resolve, generationDelay);
6460
6536
  })
6461
6537
  ];
6462
- case 7:
6538
+ case 9:
6463
6539
  _state.sent();
6464
6540
  return [
6465
6541
  2
@@ -6467,33 +6543,41 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6467
6543
  }
6468
6544
  });
6469
6545
  };
6470
- _state.label = 1;
6471
- case 1:
6546
+ _state.label = 2;
6547
+ case 2:
6472
6548
  if (!(this.continuousFetchingActive && this.inAdBreak)) return [
6473
6549
  3,
6474
- 3
6550
+ 4
6475
6551
  ];
6476
6552
  _this = this;
6477
6553
  return [
6478
6554
  5,
6479
6555
  _ts_values(_loop())
6480
6556
  ];
6481
- case 2:
6557
+ case 3:
6482
6558
  _ret = _state.sent();
6483
6559
  if (_ret === "break") return [
6484
6560
  3,
6485
- 3
6561
+ 4
6486
6562
  ];
6487
6563
  return [
6488
6564
  3,
6489
- 1
6565
+ 2
6490
6566
  ];
6491
- case 3:
6567
+ case 4:
6568
+ return [
6569
+ 3,
6570
+ 6
6571
+ ];
6572
+ case 5:
6492
6573
  this.continuousFetchLoopRunning = false;
6574
+ this.continuousFetchQueueFullConsecutiveWaits = 0;
6575
+ return [
6576
+ 7
6577
+ ];
6578
+ case 6:
6493
6579
  if (this.config.debugAdTiming) {
6494
- console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 URL generation loop ended (queued: ".concat(this.adRequestQueue.length, ")"), this.adRequestQueue.length > 0 ? {
6495
- queuedUrls: _to_consumable_array(this.adRequestQueue)
6496
- } : {});
6580
+ this.logQueueState("URL generation loop ended");
6497
6581
  }
6498
6582
  return [
6499
6583
  2
@@ -6503,6 +6587,51 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6503
6587
  }).call(this);
6504
6588
  }
6505
6589
  },
6590
+ {
6591
+ key: "waitForQueueWithBackoff",
6592
+ value: function waitForQueueWithBackoff() {
6593
+ return _async_to_generator(function() {
6594
+ var delayMs, shouldLog;
6595
+ return _ts_generator(this, function(_state) {
6596
+ switch(_state.label){
6597
+ case 0:
6598
+ this.continuousFetchQueueFullConsecutiveWaits++;
6599
+ delayMs = Math.min(1e3 * Math.pow(2, this.continuousFetchQueueFullConsecutiveWaits - 1), 5e3);
6600
+ shouldLog = this.config.debugAdTiming && (this.continuousFetchQueueFullConsecutiveWaits <= 2 || this.continuousFetchQueueFullConsecutiveWaits % 4 === 0);
6601
+ if (shouldLog) {
6602
+ console.log("[CONTINUOUS-FETCH] ⏸️ URL queue full (".concat(this.adRequestQueue.length, "), waiting ").concat(delayMs, "ms (wait #").concat(this.continuousFetchQueueFullConsecutiveWaits, ")"));
6603
+ }
6604
+ return [
6605
+ 4,
6606
+ new Promise(function(resolve) {
6607
+ return setTimeout(resolve, delayMs);
6608
+ })
6609
+ ];
6610
+ case 1:
6611
+ _state.sent();
6612
+ return [
6613
+ 2
6614
+ ];
6615
+ }
6616
+ });
6617
+ }).call(this);
6618
+ }
6619
+ },
6620
+ {
6621
+ key: "logQueueState",
6622
+ value: function logQueueState(reason) {
6623
+ if (!this.config.debugAdTiming) return;
6624
+ console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 ".concat(reason), {
6625
+ queueLength: this.adRequestQueue.length,
6626
+ totalAdsInBreak: this.totalAdsInBreak,
6627
+ totalAdRequestsInBreak: this.totalAdRequestsInBreak,
6628
+ consecutiveFailures: this.consecutiveFailures,
6629
+ continuousFetchingActive: this.continuousFetchingActive,
6630
+ continuousFetchLoopRunning: this.continuousFetchLoopRunning,
6631
+ inAdBreak: this.inAdBreak
6632
+ });
6633
+ }
6634
+ },
6506
6635
  {
6507
6636
  key: "stopContinuousFetching",
6508
6637
  value: function stopContinuousFetching() {
@@ -6673,8 +6802,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6673
6802
  4
6674
6803
  ];
6675
6804
  }
6805
+ console.log("[StormcloudVideoPlayer] Requesting next ad (%s), queue remaining=%s", nextAdUrl === "" ? "Prebid" : "VAST", this.adRequestQueue.length);
6676
6806
  if (this.config.debugAdTiming) {
6677
- console.log("[CONTINUOUS-FETCH] \uD83C\uDFAC Requesting next ad via IMA SDK (".concat(this.currentAdIndex + 1, "/").concat(this.totalAdsInBreak, ", ").concat(this.adRequestQueue.length, " remaining in queue)"));
6807
+ console.log("[CONTINUOUS-FETCH] \uD83C\uDFAC Requesting next ad via ".concat(this.getAdPlayerTypeLabel(), " (").concat(this.currentAdIndex + 1, "/").concat(this.totalAdsInBreak, ", ").concat(this.adRequestQueue.length, " remaining in queue)"));
6678
6808
  }
6679
6809
  currentMuted1 = this.video.muted;
6680
6810
  currentVolume1 = this.video.volume;
@@ -6854,7 +6984,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6854
6984
  ];
6855
6985
  }
6856
6986
  if (this.config.debugAdTiming) {
6857
- console.log("[CONTINUOUS-FETCH] \u2705 Ad URL available, requesting via IMA SDK");
6987
+ console.log("[CONTINUOUS-FETCH] Ad URL available, requesting via ".concat(this.getAdPlayerTypeLabel()));
6858
6988
  }
6859
6989
  this.isShowingPlaceholder = false;
6860
6990
  this.ima.hidePlaceholder();
@@ -7196,7 +7326,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7196
7326
  }
7197
7327
  throw new Error("Too many consecutive failures");
7198
7328
  }
7199
- this.recreateImaController();
7329
+ this.recreateAdController();
7200
7330
  requestToken = ++this.adRequestTokenCounter;
7201
7331
  this.activeAdRequestToken = requestToken;
7202
7332
  this.startAdRequestWatchdog(requestToken);
@@ -7441,6 +7571,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7441
7571
  this.showAds = false;
7442
7572
  this.currentAdIndex = 0;
7443
7573
  this.totalAdsInBreak = 0;
7574
+ this.totalAdRequestsInBreak = 0;
7444
7575
  this.consecutiveFailures = 0;
7445
7576
  this.ima.stop().catch(function() {});
7446
7577
  var restoredMuted = this.ima.getOriginalMutedState();
@@ -7452,17 +7583,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7452
7583
  this.video.volume = restoredVolume;
7453
7584
  }
7454
7585
  if (this.shouldContinueLiveStreamDuringAds()) {
7455
- if (this.video.paused) {
7456
- var _this_video_play;
7457
- if (this.config.debugAdTiming) {
7586
+ var _this_video_play;
7587
+ if (this.config.debugAdTiming) {
7588
+ if (this.video.paused) {
7458
7589
  console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
7459
- }
7460
- (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
7461
- } else {
7462
- if (this.config.debugAdTiming) {
7590
+ } else {
7463
7591
  console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
7464
7592
  }
7465
7593
  }
7594
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
7466
7595
  } else if (this.video.paused) {
7467
7596
  var _this_video_play1;
7468
7597
  (_this_video_play1 = this.video.play()) === null || _this_video_play1 === void 0 ? void 0 : _this_video_play1.catch(function() {});