{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/player/AdBreakOrchestrator.cjs","../../src/player/AdBreakOrchestrator.ts","../../src/utils/mqttConfig.ts","../../src/utils/mqttClient.ts","../../src/utils/vastEnvironmentSignals.ts","../../src/utils/tracking.ts","../../src/player/PlayerControls.ts"],"names":["__create","Object","create","__defProp","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__getProtoOf","getPrototypeOf","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toESM","mod","isNodeMode","__esModule","value","__toCommonJS","AdBreakOrchestrator_exports","AdBreakOrchestrator","module","exports","mqttConfig","DEFAULT_MQTT_CONFIG","enabled","brokerAddress","brokerPort","wsPort","username","password","topicPrefix","qos","isMQTTEnabled","buildMQTTBrokerUrl","brokerUrl","buildPlayerTopic","licenseKey","channel","import_mqtt","require","LOG","KEEPALIVE_SECONDS","CONNECT_TIMEOUT_MS","RECONNECT_BASE_MS","RECONNECT_MAX_MS","MAX_RECONNECT_ATTEMPTS","COOLDOWN_MS","client","status","reconnectAttempts","reconnectTimer","stopped","clearReconnectTimer","clearTimeout","removeAllListeners","end","scheduleReconnect","console","warn","teardownClient","setTimeout","openConnection","delay","Math","min","jitter","floor","random","url","clientId","toString","slice","mqtt","connect","keepalive","clean","reconnectPeriod","connectTimeout","queueQoSZero","reschedulePings","timerVariant","err","on","info","message","initMQTTClient","ensureMQTTClient","publishMQTT","topic","payload","connected","publish","JSON","stringify","PLAYER_ID_STORAGE_KEY","DEVICE_ID_STORAGE_KEYS","inferDeviceIdType","navigator","ua","userAgent","test","resolveNativeDeviceId","bridgeSignals","readNativeBridgeSignals","platformDevice","readPlatformNativeDeviceId","deviceId","readStoredDeviceId","isMacroPlaceholder","isZeroedAdId","getOrCreateStoredUuid","storageKey","window","existing","readPersistentId","writePersistentId","id","createUuid","readStoredString","readCookie","localStorage","setItem","writeCookie","document","prefix","encodeURIComponent","parts","cookie","split","part","entry","trim","startsWith","stormcloudCtv","decodeURIComponent","length","PERSISTENT_ID_COOKIE_MAX_AGE","bytes","crypto","randomUUID","Uint8Array","getRandomValues","seed","Date","now","performanceNow","i","hex","padStart","performance","adinfo","webapis","tifa","getTIFA","lat","isLATEnabled","deviceIdType","limitAdTracking","webOSDev","lgudid","LGUDID","roku","Roku","rida","getPublisherUniqueId","ZEROED_AD_ID_PATTERNS","Set","t","has","toLowerCase","candidates","__STORMCLOUD_CTV_AD_INFO__","__STORMCLOUD_CTV__","AiryTV","Android","webOS","tizen","amazon","filter","Boolean","contentUrl","readBridgeValue","appId","source","appName","readBridgeBoolean","keys","result","normalized","normalizeBoolean","getItem","cachedBrowserId","buildAnalyticsContext","config","context","debugAdTiming","isLiveStream","inputStreamType","getClientInfo","screen","platform","vendor","maxTouchPoints","memory","deviceMemory","hardwareConcurrency","screenInfo","width","height","availWidth","availHeight","orientation","type","pixelDepth","deviceType","brand","os","model","isSmartTV","isAndroid","isWebView","isWebApp","includes","webosMatch","match","tizenMatch","tvMatch","androidModelMatch","outerHeight","outerWidth","matchMedia","matches","standalone","angle","substring","domain","location","hostname","origin","path","pathname","language","languages","join","doNotTrack","referrer","visibilityState","sha256Hex","input","encodedData","utf8","buffer","hashBuffer","hash","char","fallbackHash","timestamp","subtle","digest","TextEncoder","encode","unescape","charCodeAt","Array","map","b","abs","resolvePlayerIdentitySeed","nativeDeviceId","clientInfo","persistedId","getBrowserID","canPublish","buildPlayerMetricEvent","flags","playerId","captureAt","log","toISOString","player_id","device_type","ad_loaded","adLoaded","ad_detect","adDetect","capture_at","input_stream_type","publishTracking","body","sendAdLoadedTracking","adLoadedInfo","error","sendHeartbeat","sendAdImpressionTracking","adImpressionInfo","metricEvent","events","ad_played_count","ad_url","adUrl","heartbeatData","timing","attachImaEventListeners","analyticsContext","catch","getAdAudioVolume","adPlayer","getOriginalMutedState","getOriginalVolume","host","adConfig","cueManager","placeholder","inAdBreak","showAds","isShowingPlaceholder","adDetectSentForCurrentBreak","continuousFetchingActive","continuousFetchLoopRunning","adRequestQueue","adPodQueue","maxPlaceholderDurationMs","pendingAdBreak","debug","nextAdRequestUrl","baseVastUrl","remaining","getWallClockRemainingAdMs","getDurationBudgetRemainingMs","breakDurationMs","generatePodVastUrl","getAdPlayer","notePodAdStarted","index","total","adIndex","currentAdIndex","recordPlayedAdDuration","getRemainingAdMs","consecutiveFailures","queuedUrls","hasTimeToStartAnotherAd","tryNextAvailableAdWithRateLimit","handleAdPodComplete","errorPayload","vastErrorCode","errorMessage","errorCode","code","cause","innerError","causeMessage","String","handleAdFailure","clearAdFailsafeTimer","clearAdRequestWatchdog","activeAdRequestToken","resetGamNoFillCounter","expectedAdBreakDurationMs","getAdBreakEndWallClockMs","scheduleAdStopAtBreakBoundary","setAdVolume","hidePlaceholder","video","muted","recreateAdController","shouldShowPlaceholder","show","showPlaceholder","preservedMutedState","preservedVolumeState","destroy","continueLiveStreamDuringAds","shouldContinueLiveStreamDuringAds","newAdPlayer","createAdPlayer","setAdPlayer","initialize","updateOriginalMutedState","clearPendingAdBreak","prefetchTimerId","pendingScte35CueKey","podAssignedByPrefetch","handleAdStart","_marker","scheduled","tags","adBreakDurationMs","mode","currentMuted","currentVolume","podPlayed","findCurrentOrNextBreak","currentTime","consumedVmapBreakIds","add","getAdBreakKey","selectVastTagsForBreak","apiVastTagUrl","durationSeconds","durationMs","failedVastUrls","clear","temporaryFailureUrls","totalAdRequestsInBreak","beginNewAdPod","volume","adBreakPlayedDurationMs","totalAdsInBreak","startOptimizedPod","startContinuousFetching","podUrl","requestToken","podCount","vastUrls","adRequestTokenCounter","startAdRequestWatchdog","enforceGlobalRateLimit","lastAdRequestTime"],"mappings":"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","sourcesContent":["\"use strict\";\nvar __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n  for (var name in all)\n    __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n  if (from && typeof from === \"object\" || typeof from === \"function\") {\n    for (let key of __getOwnPropNames(from))\n      if (!__hasOwnProp.call(to, key) && key !== except)\n        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n  }\n  return to;\n};\nvar __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n  // If the importer is in node compatibility mode or this is not an ESM\n  // file that has been converted to a CommonJS file using a Babel-\n  // compatible transform (i.e. \"__esModule\" has not been set), then set\n  // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n  isNodeMode || !mod || !mod.__esModule ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target,\n  mod\n));\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/player/AdBreakOrchestrator.ts\nvar AdBreakOrchestrator_exports = {};\n__export(AdBreakOrchestrator_exports, {\n  AdBreakOrchestrator: () => AdBreakOrchestrator\n});\nmodule.exports = __toCommonJS(AdBreakOrchestrator_exports);\n\n// src/utils/mqttConfig.ts\nvar DEFAULT_MQTT_CONFIG = {\n  enabled: true,\n  brokerAddress: \"vecbae77.ala.us-east-1.emqxsl.com\",\n  brokerPort: 8883,\n  wsPort: 8084,\n  username: \"for-sonifi\",\n  password: \"sonifi-mqtt\",\n  topicPrefix: \"adstorm/players\",\n  qos: 1\n};\nvar mqttConfig = { ...DEFAULT_MQTT_CONFIG };\nfunction isMQTTEnabled() {\n  return mqttConfig.enabled;\n}\nfunction buildMQTTBrokerUrl() {\n  if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;\n  return `wss://${mqttConfig.brokerAddress}:${mqttConfig.wsPort}/mqtt`;\n}\nfunction buildPlayerTopic(licenseKey, channel) {\n  return `${mqttConfig.topicPrefix}/${licenseKey}/${channel}`;\n}\n\n// src/utils/mqttClient.ts\nvar import_mqtt = __toESM(require(\"mqtt\"), 1);\nvar LOG = \"[StormcloudVideoPlayer][MQTT]\";\nvar KEEPALIVE_SECONDS = 30;\nvar CONNECT_TIMEOUT_MS = 1e4;\nvar RECONNECT_BASE_MS = 5e3;\nvar RECONNECT_MAX_MS = 6e4;\nvar MAX_RECONNECT_ATTEMPTS = 8;\nvar COOLDOWN_MS = 10 * 6e4;\nvar client = null;\nvar status = \"disconnected\";\nvar reconnectAttempts = 0;\nvar reconnectTimer = null;\nvar stopped = false;\nfunction clearReconnectTimer() {\n  if (reconnectTimer) {\n    clearTimeout(reconnectTimer);\n    reconnectTimer = null;\n  }\n}\nfunction teardownClient() {\n  if (client) {\n    try {\n      client.removeAllListeners();\n      client.end(true);\n    } catch {\n    }\n    client = null;\n  }\n}\nfunction scheduleReconnect() {\n  if (stopped || !isMQTTEnabled() || reconnectTimer) return;\n  if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {\n    console.warn(\n      `${LOG} giving up after ${reconnectAttempts} attempts; cooling down for ${COOLDOWN_MS / 6e4}m`\n    );\n    teardownClient();\n    status = \"disconnected\";\n    reconnectTimer = setTimeout(() => {\n      reconnectTimer = null;\n      reconnectAttempts = 0;\n      openConnection();\n    }, COOLDOWN_MS);\n    return;\n  }\n  const delay = Math.min(\n    RECONNECT_MAX_MS,\n    RECONNECT_BASE_MS * 2 ** reconnectAttempts\n  );\n  const jitter = Math.floor(Math.random() * 1e3);\n  reconnectAttempts += 1;\n  reconnectTimer = setTimeout(() => {\n    reconnectTimer = null;\n    openConnection();\n  }, delay + jitter);\n}\nfunction openConnection() {\n  if (stopped || !isMQTTEnabled()) return;\n  teardownClient();\n  const url = buildMQTTBrokerUrl();\n  status = \"connecting\";\n  const clientId = `stormcloud-vp-${Math.random().toString(36).slice(2, 9)}`;\n  try {\n    client = import_mqtt.default.connect(url, {\n      clientId,\n      username: mqttConfig.username,\n      password: mqttConfig.password,\n      keepalive: KEEPALIVE_SECONDS,\n      clean: true,\n      // Disable MQTT.js internal reconnect; we manage reconnection ourselves.\n      reconnectPeriod: 0,\n      connectTimeout: CONNECT_TIMEOUT_MS,\n      queueQoSZero: false,\n      reschedulePings: true,\n      // Root-cause fix for the Tizen 4.0 \"Keepalive timeout\" loop: MQTT.js's\n      // default timerVariant \"auto\" runs the keepalive interval inside a\n      // Blob-URL Web Worker (via worker-timers). On Samsung Tizen 4.0 (old\n      // WebKit) served from a file:// origin that worker can't tick reliably,\n      // so the keepalive counter trips onKeepaliveTimeout almost immediately\n      // after connect. Force the native setInterval timer instead; the main\n      // thread is free because video is decoded by the native AVPlay pipeline.\n      timerVariant: \"native\"\n    });\n  } catch (err) {\n    status = \"error\";\n    console.warn(`${LOG} connect() threw:`, err);\n    scheduleReconnect();\n    return;\n  }\n  client.on(\"connect\", () => {\n    status = \"connected\";\n    reconnectAttempts = 0;\n    clearReconnectTimer();\n    console.info(`${LOG} connected to ${url}`);\n  });\n  client.on(\"offline\", () => {\n    if (status === \"connected\") status = \"disconnected\";\n    console.warn(`${LOG} offline`);\n    scheduleReconnect();\n  });\n  client.on(\"error\", (err) => {\n    status = \"error\";\n    console.warn(`${LOG} error:`, err.message);\n    scheduleReconnect();\n  });\n  client.on(\"close\", () => {\n    if (status === \"connected\") status = \"disconnected\";\n    scheduleReconnect();\n  });\n}\nfunction initMQTTClient() {\n  if (client || !isMQTTEnabled()) return;\n  stopped = false;\n  reconnectAttempts = 0;\n  openConnection();\n}\nfunction ensureMQTTClient() {\n  if (isMQTTEnabled() && !client && !reconnectTimer) {\n    initMQTTClient();\n  }\n}\nfunction publishMQTT(topic, payload) {\n  if (!isMQTTEnabled()) {\n    return false;\n  }\n  ensureMQTTClient();\n  if (!client || !client.connected) {\n    return false;\n  }\n  try {\n    client.publish(topic, JSON.stringify(payload), { qos: mqttConfig.qos });\n    return true;\n  } catch (err) {\n    console.warn(`${LOG} publish failed on ${topic}:`, err);\n    return false;\n  }\n}\n\n// src/utils/vastEnvironmentSignals.ts\nvar PLAYER_ID_STORAGE_KEY = \"stormcloud.player.id\";\nvar DEVICE_ID_STORAGE_KEYS = [\n  \"rdid\",\n  \"ifa\",\n  \"advertisingId\",\n  \"advertising_id\",\n  \"deviceAdvertisingId\",\n  \"aaid\",\n  \"adid\",\n  \"rida\",\n  \"tifa\",\n  \"lgudid\"\n];\nfunction inferDeviceIdType() {\n  if (typeof navigator === \"undefined\") {\n    return void 0;\n  }\n  const ua = navigator.userAgent;\n  if (/Roku/i.test(ua)) return \"rida\";\n  if (/Tizen|Samsung/i.test(ua)) return \"tifa\";\n  if (/AppleTV|Apple TV/i.test(ua)) return \"tvOS\";\n  if (/AFT|Fire TV|Amazon/i.test(ua)) return \"afai\";\n  if (/Web0S|webOS|LG Browser|LGSTB|LGE/i.test(ua)) return \"lgudid\";\n  if (/Android|Google TV/i.test(ua)) return \"aaid\";\n  if (/iPhone|iPad|iPod/i.test(ua)) return \"idfa\";\n  return void 0;\n}\nfunction resolveNativeDeviceId() {\n  const bridgeSignals = readNativeBridgeSignals();\n  const platformDevice = readPlatformNativeDeviceId();\n  const deviceId = bridgeSignals.deviceId || readStoredDeviceId() || platformDevice.deviceId;\n  if (!deviceId || isMacroPlaceholder(deviceId) || isZeroedAdId(deviceId)) {\n    return void 0;\n  }\n  return deviceId;\n}\nfunction getOrCreateStoredUuid(storageKey) {\n  if (typeof window === \"undefined\") {\n    return void 0;\n  }\n  const existing = readPersistentId(storageKey);\n  if (existing) {\n    writePersistentId(storageKey, existing);\n    return existing;\n  }\n  const id = createUuid();\n  writePersistentId(storageKey, id);\n  return id;\n}\nfunction readPersistentId(storageKey) {\n  return readStoredString(storageKey) || readCookie(storageKey);\n}\nfunction writePersistentId(storageKey, value) {\n  try {\n    window.localStorage?.setItem(storageKey, value);\n  } catch {\n  }\n  writeCookie(storageKey, value);\n}\nvar PERSISTENT_ID_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5;\nfunction readCookie(name) {\n  if (typeof document === \"undefined\") {\n    return void 0;\n  }\n  try {\n    const prefix = encodeURIComponent(name) + \"=\";\n    const parts = document.cookie ? document.cookie.split(\";\") : [];\n    for (const part of parts) {\n      const entry = part.trim();\n      if (entry.startsWith(prefix)) {\n        return decodeURIComponent(entry.slice(prefix.length)) || void 0;\n      }\n    }\n  } catch {\n  }\n  return void 0;\n}\nfunction writeCookie(name, value) {\n  if (typeof document === \"undefined\") {\n    return;\n  }\n  try {\n    document.cookie = encodeURIComponent(name) + \"=\" + encodeURIComponent(value) + \";path=/;max-age=\" + PERSISTENT_ID_COOKIE_MAX_AGE + \";SameSite=Lax\";\n  } catch {\n  }\n}\nfunction createUuid() {\n  if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n    return crypto.randomUUID();\n  }\n  const bytes = new Uint8Array(16);\n  if (typeof crypto !== \"undefined\" && typeof crypto.getRandomValues === \"function\") {\n    crypto.getRandomValues(bytes);\n  } else {\n    let seed = Date.now() ^ Math.floor(performanceNow() * 1e3);\n    for (let i = 0; i < 16; i++) {\n      seed = seed * 1103515245 + 12345 & 2147483647;\n      bytes[i] = (seed ^ Math.floor(Math.random() * 256)) & 255;\n    }\n  }\n  bytes[6] = (bytes[6] ?? 0) & 15 | 64;\n  bytes[8] = (bytes[8] ?? 0) & 63 | 128;\n  let hex = \"\";\n  for (let i = 0; i < 16; i++) {\n    hex += (bytes[i] ?? 0).toString(16).padStart(2, \"0\");\n  }\n  return hex.slice(0, 8) + \"-\" + hex.slice(8, 12) + \"-\" + hex.slice(12, 16) + \"-\" + hex.slice(16, 20) + \"-\" + hex.slice(20, 32);\n}\nfunction performanceNow() {\n  try {\n    if (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n      return performance.now();\n    }\n  } catch {\n  }\n  return 0;\n}\nfunction readPlatformNativeDeviceId() {\n  if (typeof window === \"undefined\") return {};\n  try {\n    const adinfo = window.webapis?.adinfo;\n    if (adinfo) {\n      const tifa = typeof adinfo.getTIFA === \"function\" ? adinfo.getTIFA() : adinfo.tifa;\n      if (typeof tifa === \"string\" && tifa && !isMacroPlaceholder(tifa)) {\n        const lat = typeof adinfo.isLATEnabled === \"function\" ? adinfo.isLATEnabled() : void 0;\n        return {\n          deviceId: tifa,\n          deviceIdType: \"tifa\",\n          ...typeof lat === \"boolean\" ? { limitAdTracking: lat } : {}\n        };\n      }\n    }\n  } catch {\n  }\n  try {\n    const webOSDev = window.webOSDev;\n    if (webOSDev) {\n      const lgudid = typeof webOSDev.LGUDID === \"function\" ? webOSDev.LGUDID() : webOSDev.lgudid ?? webOSDev.LGUDID;\n      if (typeof lgudid === \"string\" && lgudid) {\n        if (isZeroedAdId(lgudid)) {\n          return { deviceIdType: \"lgudid\", limitAdTracking: true };\n        }\n        if (!isMacroPlaceholder(lgudid)) {\n          return { deviceId: lgudid, deviceIdType: \"lgudid\", limitAdTracking: false };\n        }\n      }\n    }\n  } catch {\n  }\n  try {\n    const roku = window.Roku;\n    if (roku) {\n      const rida = typeof roku.getPublisherUniqueId === \"function\" ? roku.getPublisherUniqueId() : roku.rida;\n      if (typeof rida === \"string\" && rida && !isMacroPlaceholder(rida)) {\n        return { deviceId: rida, deviceIdType: \"rida\", limitAdTracking: false };\n      }\n    }\n  } catch {\n  }\n  return {};\n}\nvar ZEROED_AD_ID_PATTERNS = /* @__PURE__ */ new Set([\n  \"38400000-8cf0-11bd-b23e-10b96e40000d\",\n  \"00000000-0000-0000-0000-000000000000\"\n]);\nfunction isMacroPlaceholder(value) {\n  const t = value.trim();\n  return !t || /^(\\[[^\\]]*\\]|\\{[^}]*\\}|%%[^%]*%%)$/.test(t) || /^(unknown|null|undefined|none|n\\/a|\\$\\{[^}]+\\})$/i.test(t) || ZEROED_AD_ID_PATTERNS.has(t.toLowerCase());\n}\nfunction isZeroedAdId(value) {\n  return ZEROED_AD_ID_PATTERNS.has(value.trim().toLowerCase());\n}\nfunction readNativeBridgeSignals() {\n  if (typeof window === \"undefined\") {\n    return {};\n  }\n  const candidates = [\n    window.__STORMCLOUD_CTV_AD_INFO__,\n    window.__STORMCLOUD_CTV__,\n    window.stormcloudCtv,\n    window.AiryTV,\n    window.Android,\n    window.webOS,\n    window.tizen,\n    window.amazon\n  ].filter(Boolean);\n  for (const source of candidates) {\n    const contentUrl = readBridgeValue(source, [\"contentUrl\", \"url\", \"videoUrl\", \"canonicalUrl\"]) || void 0;\n    const appId = readBridgeValue(source, [\"appId\", \"msid\", \"applicationId\", \"packageName\"]) || void 0;\n    const appName = readBridgeValue(source, [\"appName\", \"an\", \"applicationName\"]) || void 0;\n    const deviceId = readBridgeValue(source, [\n      \"rdid\",\n      \"ifa\",\n      \"advertisingId\",\n      \"adId\",\n      \"deviceId\",\n      \"lgudid\",\n      \"LGUDID\",\n      \"tifa\",\n      \"rida\",\n      \"afai\",\n      \"aaid\"\n    ]) || void 0;\n    if (!contentUrl && !deviceId && !appId && !appName) continue;\n    const deviceIdType = readBridgeValue(source, [\n      \"idtype\",\n      \"deviceIdType\",\n      \"advertisingIdType\",\n      \"idType\"\n    ]) || inferDeviceIdType();\n    const limitAdTracking = readBridgeBoolean(source, [\n      \"is_lat\",\n      \"limitAdTracking\",\n      \"limitedAdTracking\",\n      \"lat\"\n    ]);\n    return {\n      ...contentUrl ? { contentUrl } : {},\n      ...appId ? { appId } : {},\n      ...appName ? { appName } : {},\n      ...deviceId ? { deviceId } : {},\n      ...deviceId && deviceIdType ? { deviceIdType } : {},\n      ...deviceId && limitAdTracking != null ? { limitAdTracking } : {}\n    };\n  }\n  return {};\n}\nfunction readBridgeValue(source, keys) {\n  for (const key of keys) {\n    const value = source?.[key];\n    if (typeof value === \"string\" && value) {\n      return value;\n    }\n    if (typeof value === \"function\") {\n      try {\n        const result = value.call(source);\n        if (typeof result === \"string\" && result) {\n          return result;\n        }\n      } catch {\n      }\n    }\n  }\n  return void 0;\n}\nfunction readBridgeBoolean(source, keys) {\n  for (const key of keys) {\n    const value = source?.[key];\n    const normalized = normalizeBoolean(value);\n    if (normalized != null) {\n      return normalized;\n    }\n    if (typeof value === \"function\") {\n      try {\n        const result = normalizeBoolean(value.call(source));\n        if (result != null) {\n          return result;\n        }\n      } catch {\n      }\n    }\n  }\n  return void 0;\n}\nfunction readStoredDeviceId() {\n  for (const key of DEVICE_ID_STORAGE_KEYS) {\n    const value = readStoredString(key);\n    if (value) {\n      return value;\n    }\n  }\n  return void 0;\n}\nfunction readStoredString(key) {\n  if (typeof window === \"undefined\") {\n    return void 0;\n  }\n  try {\n    const value = window.localStorage?.getItem(key);\n    return value || void 0;\n  } catch {\n    return void 0;\n  }\n}\nfunction normalizeBoolean(value) {\n  if (typeof value === \"boolean\") {\n    return value;\n  }\n  if (typeof value === \"number\") {\n    return value === 1;\n  }\n  if (typeof value === \"string\") {\n    const normalized = value.toLowerCase();\n    if (normalized === \"1\" || normalized === \"true\" || normalized === \"yes\") {\n      return true;\n    }\n    if (normalized === \"0\" || normalized === \"false\" || normalized === \"no\") {\n      return false;\n    }\n  }\n  return void 0;\n}\n\n// src/utils/tracking.ts\nvar cachedBrowserId = null;\nfunction buildAnalyticsContext(config) {\n  const context = {};\n  if (config.debugAdTiming) {\n    context.debugAdTiming = true;\n  }\n  if (config.isLiveStream !== void 0) {\n    context.inputStreamType = config.isLiveStream ? \"live\" : \"vod\";\n  }\n  return context;\n}\nfunction getClientInfo() {\n  const ua = navigator.userAgent;\n  const platform = navigator.platform;\n  const vendor = navigator.vendor || \"\";\n  const maxTouchPoints = navigator.maxTouchPoints || 0;\n  const memory = navigator.deviceMemory || null;\n  const hardwareConcurrency = navigator.hardwareConcurrency || 1;\n  const screenInfo = {\n    width: screen?.width,\n    height: screen?.height,\n    availWidth: screen?.availWidth,\n    availHeight: screen?.availHeight,\n    orientation: screen?.orientation?.type || \"\",\n    pixelDepth: screen?.pixelDepth\n  };\n  let deviceType = \"desktop\";\n  let brand = \"Unknown\";\n  let os = \"Unknown\";\n  let model = \"\";\n  let isSmartTV = false;\n  let isAndroid = false;\n  let isWebView = false;\n  let isWebApp = false;\n  if (ua.includes(\"Web0S\")) {\n    brand = \"LG\";\n    os = \"webOS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n    const webosMatch = ua.match(/Web0S\\/([^\\s]+)/);\n    model = webosMatch ? `webOS ${webosMatch[1]}` : \"webOS TV\";\n  } else if (ua.includes(\"Tizen\")) {\n    brand = \"Samsung\";\n    os = \"Tizen\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n    const tizenMatch = ua.match(/Tizen\\/([^\\s]+)/);\n    const tvMatch = ua.match(/(?:Smart-TV|SMART-TV|TV)/i) ? \"Smart TV\" : \"\";\n    model = tizenMatch ? `Tizen ${tizenMatch[1]} ${tvMatch}`.trim() : \"Tizen TV\";\n  } else if (ua.includes(\"Philips\")) {\n    brand = \"Philips\";\n    os = \"Saphi\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"Sharp\") || ua.includes(\"AQUOS\")) {\n    brand = \"Sharp\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"Android\") && (ua.includes(\"Sony\") || vendor.includes(\"Sony\"))) {\n    brand = \"Sony\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"Android\") && (ua.includes(\"NetCast\") || ua.includes(\"LG\"))) {\n    brand = \"LG\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\" Roku\") || ua.includes(\"Roku/\")) {\n    brand = \"Roku\";\n    os = \"Roku OS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"AppleTV\")) {\n    brand = \"Apple\";\n    os = \"tvOS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  }\n  if (ua.includes(\"Android\")) {\n    isAndroid = true;\n    os = \"Android\";\n    deviceType = /Mobile/.test(ua) ? \"mobile\" : \"tablet\";\n    if (ua.includes(\"Android\") && (maxTouchPoints === 0 || ua.includes(\"Google TV\") || ua.includes(\"XiaoMi\"))) {\n      deviceType = \"tv\";\n      isSmartTV = true;\n      brand = brand === \"Unknown\" ? \"Android TV\" : brand;\n    }\n    const androidModelMatch = ua.match(/\\(([^)]*Android[^)]*)\\)/);\n    if (androidModelMatch && androidModelMatch[1]) {\n      model = androidModelMatch[1];\n    }\n  }\n  if (/iPad|iPhone|iPod/.test(ua)) {\n    os = \"iOS\";\n    deviceType = \"mobile\";\n    brand = \"Apple\";\n    if (navigator.maxTouchPoints > 1 && /iPad/.test(ua)) {\n      deviceType = \"tablet\";\n    }\n  }\n  if (!isAndroid && !isSmartTV && !/Mobile/.test(ua)) {\n    if (ua.includes(\"Windows\")) {\n      os = \"Windows\";\n      deviceType = \"desktop\";\n    } else if (ua.includes(\"Mac\") && !/iPhone/.test(ua)) {\n      os = \"macOS\";\n      deviceType = \"desktop\";\n      if (maxTouchPoints > 1) deviceType = \"tablet\";\n    } else if (ua.includes(\"Linux\")) {\n      os = \"Linux\";\n      deviceType = \"desktop\";\n    }\n  }\n  if (brand === \"Unknown\") {\n    if (vendor.includes(\"Google\") || ua.includes(\"Chrome\")) brand = \"Google\";\n    if (vendor.includes(\"Apple\")) brand = \"Apple\";\n    if (vendor.includes(\"Samsung\") || ua.includes(\"SM-\")) brand = \"Samsung\";\n  }\n  isWebView = /wv|WebView|Linux; U;/.test(ua);\n  if (window?.outerHeight === 0 && window?.outerWidth === 0) {\n    isWebView = true;\n  }\n  isWebApp = window.matchMedia(\"(display-mode: standalone)\").matches || window.navigator.standalone === true || window.screen?.orientation?.angle !== void 0;\n  return {\n    brand,\n    os,\n    model: model || ua.substring(0, 50) + \"...\",\n    deviceType,\n    isSmartTV,\n    isAndroid,\n    isWebView,\n    isWebApp,\n    domain: window.location.hostname,\n    origin: window.location.origin,\n    path: window.location.pathname,\n    userAgent: ua,\n    vendor,\n    platform,\n    screen: screenInfo,\n    hardwareConcurrency,\n    deviceMemory: memory,\n    maxTouchPoints,\n    language: navigator.language,\n    languages: navigator.languages?.join(\",\") || \"\",\n    cookieEnabled: navigator.cookieEnabled,\n    doNotTrack: navigator.doNotTrack || \"\",\n    referrer: document.referrer,\n    visibilityState: document.visibilityState\n  };\n}\nasync function sha256Hex(input) {\n  if (typeof crypto !== \"undefined\" && crypto.subtle?.digest) {\n    try {\n      await crypto.subtle.digest(\"SHA-256\", new Uint8Array([1, 2, 3]));\n      let encodedData;\n      if (typeof TextEncoder !== \"undefined\") {\n        encodedData = new TextEncoder().encode(input);\n      } else {\n        const utf8 = unescape(encodeURIComponent(input));\n        const buffer = new Uint8Array(utf8.length);\n        for (let i = 0; i < utf8.length; i++) {\n          buffer[i] = utf8.charCodeAt(i);\n        }\n        encodedData = buffer;\n      }\n      const hashBuffer = await crypto.subtle.digest(\"SHA-256\", encodedData);\n      return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n    } catch {\n      console.warn(\n        \"[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash\"\n      );\n    }\n  }\n  let hash = 0;\n  for (let i = 0; i < input.length; i++) {\n    const char = input.charCodeAt(i);\n    hash = (hash << 5) - hash + char;\n    hash = hash & hash;\n  }\n  const fallbackHash = Math.abs(hash).toString(16).padStart(8, \"0\");\n  const timestamp = Date.now().toString(16).padStart(12, \"0\");\n  const random = Math.random().toString(16).substring(2, 14).padStart(12, \"0\");\n  return (fallbackHash + timestamp + random).padEnd(64, \"0\");\n}\nfunction resolvePlayerIdentitySeed(clientInfo) {\n  const nativeDeviceId = resolveNativeDeviceId();\n  if (nativeDeviceId) {\n    return `device:${nativeDeviceId}`;\n  }\n  const persistedId = getOrCreateStoredUuid(PLAYER_ID_STORAGE_KEY);\n  if (persistedId) {\n    return `persisted:${persistedId}`;\n  }\n  return `fingerprint:${JSON.stringify(clientInfo)}`;\n}\nasync function getBrowserID(clientInfo) {\n  if (cachedBrowserId) {\n    return cachedBrowserId;\n  }\n  cachedBrowserId = await sha256Hex(resolvePlayerIdentitySeed(clientInfo));\n  return cachedBrowserId;\n}\nfunction canPublish(licenseKey) {\n  return Boolean(isMQTTEnabled() && licenseKey);\n}\nasync function buildPlayerMetricEvent(context = {}, flags = {}) {\n  const clientInfo = getClientInfo();\n  const debugAdTiming = !!context.debugAdTiming;\n  if (debugAdTiming) {\n    console.log(\"[StormcloudVideoPlayer] clientInfo: \", JSON.stringify(clientInfo, null, 2));\n  }\n  const playerId = await getBrowserID(clientInfo);\n  if (debugAdTiming) {\n    console.log(\"[StormcloudVideoPlayer] playerId: \", playerId);\n  }\n  const captureAt = flags.captureAt ?? (/* @__PURE__ */ new Date()).toISOString();\n  return {\n    player_id: playerId,\n    device_type: clientInfo.deviceType,\n    os: clientInfo.os.toLowerCase(),\n    ad_loaded: flags.adLoaded ?? false,\n    ad_detect: flags.adDetect ?? false,\n    capture_at: captureAt,\n    ...context.inputStreamType ? { input_stream_type: context.inputStreamType } : {}\n  };\n}\nfunction publishTracking(licenseKey, channel, body) {\n  ensureMQTTClient();\n  publishMQTT(buildPlayerTopic(licenseKey, channel), body);\n}\nasync function sendAdLoadedTracking(licenseKey, adLoadedInfo, context = {}) {\n  try {\n    await sendHeartbeat(licenseKey, context, {\n      adLoaded: true,\n      captureAt: adLoadedInfo.timestamp\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending ad loaded tracking:\",\n      error\n    );\n  }\n}\nasync function sendAdImpressionTracking(licenseKey, adImpressionInfo, context = {}) {\n  if (!canPublish(licenseKey)) return;\n  try {\n    const metricEvent = await buildPlayerMetricEvent(context, {\n      captureAt: adImpressionInfo.timestamp\n    });\n    publishTracking(licenseKey, \"heartbeat\", metricEvent);\n    publishTracking(licenseKey, \"impressions\", {\n      events: [\n        {\n          player_id: metricEvent.player_id,\n          ad_played_count: 1,\n          ad_url: adImpressionInfo.adUrl,\n          capture_at: adImpressionInfo.timestamp\n        }\n      ]\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending ad impression tracking:\",\n      error\n    );\n  }\n}\nasync function sendHeartbeat(licenseKey, context = {}, flags = {}) {\n  if (!canPublish(licenseKey)) return;\n  try {\n    const heartbeatData = await buildPlayerMetricEvent(context, flags);\n    publishTracking(licenseKey, \"heartbeat\", heartbeatData);\n  } catch (error) {\n    console.error(\"[StormcloudVideoPlayer] Error sending heartbeat:\", error);\n  }\n}\n\n// src/player/PlayerControls.ts\nfunction getAdAudioVolume(adPlayer) {\n  return adPlayer.getOriginalMutedState() ? 0 : adPlayer.getOriginalVolume();\n}\n\n// src/player/AdBreakOrchestrator.ts\nvar AdBreakOrchestrator = class {\n  constructor(host, timing, adConfig, cueManager, placeholder) {\n    this.inAdBreak = false;\n    this.showAds = false;\n    this.isShowingPlaceholder = false;\n    this.adDetectSentForCurrentBreak = false;\n    this.continuousFetchingActive = false;\n    this.continuousFetchLoopRunning = false;\n    this.adRequestQueue = [];\n    this.adPodQueue = [];\n    this.maxPlaceholderDurationMs = 5e3;\n    this.pendingAdBreak = null;\n    this.host = host;\n    this.timing = timing;\n    this.adConfig = adConfig;\n    this.cueManager = cueManager;\n    this.placeholder = placeholder;\n  }\n  get debug() {\n    return !!this.host.config.debugAdTiming;\n  }\n  get analyticsContext() {\n    return buildAnalyticsContext(this.host.config);\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  IMA event listeners\n  // ───────────────────────────────────────────────────────────────\n  nextAdRequestUrl(baseVastUrl) {\n    const remaining = Math.min(\n      this.timing.getWallClockRemainingAdMs(),\n      this.timing.getDurationBudgetRemainingMs()\n    );\n    const breakDurationMs = remaining > 0 ? remaining : void 0;\n    return this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);\n  }\n  attachImaEventListeners() {\n    const adPlayer = this.host.getAdPlayer();\n    adPlayer.on(\"pod_ad_started\", (payload) => {\n      this.timing.notePodAdStarted();\n      if (this.debug) {\n        console.log(\n          `[POD] Playing pod ad ${payload?.index != null ? payload.index + 1 : \"?\"}/${payload?.total ?? \"?\"}`\n        );\n      }\n    });\n    adPlayer.on(\"all_ads_completed\", () => {\n      sendAdImpressionTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", adIndex: this.timing.currentAdIndex, timestamp: (/* @__PURE__ */ new Date()).toISOString() },\n        this.analyticsContext\n      ).catch(() => {\n      });\n      this.timing.recordPlayedAdDuration(this.host.getAdPlayer());\n      const remaining = this.timing.getRemainingAdMs();\n      this.timing.consecutiveFailures = 0;\n      if (this.debug) {\n        console.log(\n          `[StormcloudVideoPlayer] all_ads_completed event received - remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`,\n          this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n        );\n      }\n      if (this.inAdBreak) {\n        if (this.timing.hasTimeToStartAnotherAd()) {\n          this.tryNextAvailableAdWithRateLimit();\n        } else {\n          this.handleAdPodComplete();\n        }\n      }\n    });\n    adPlayer.on(\"ad_error\", (errorPayload) => {\n      let errorMessage = \"Ad playback failed\";\n      if (errorPayload) {\n        const errorCode = errorPayload.code || errorPayload.errorCode || \"unknown\";\n        const vastErrorCode = errorPayload.vastErrorCode;\n        const message = errorPayload.message || errorPayload.errorMessage || \"Unknown error\";\n        const cause = errorPayload.cause || errorPayload.innerError || errorPayload.error;\n        errorMessage = `Ad error: AdError ${errorCode}: ${message}`;\n        if (vastErrorCode && vastErrorCode !== \"N/A\" && vastErrorCode !== errorCode) {\n          errorMessage += ` (VAST Error Code: ${vastErrorCode})`;\n        }\n        if (cause) {\n          const causeMessage = typeof cause === \"string\" ? cause : cause.message || String(cause);\n          errorMessage += `. Caused by: ${causeMessage}`;\n        }\n      }\n      console.error(\"[AD-ERROR]\", errorMessage, errorPayload || \"\");\n      this.handleAdFailure();\n    });\n    adPlayer.on(\"content_pause\", () => {\n      this.timing.clearAdFailsafeTimer();\n      this.timing.clearAdRequestWatchdog();\n      this.timing.activeAdRequestToken = null;\n      this.showAds = true;\n      this.timing.resetGamNoFillCounter();\n      if (this.inAdBreak && this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n        this.timing.scheduleAdStopAtBreakBoundary();\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Starting ad break timer on content_pause (first ad starting)\");\n        }\n      }\n      this.host.getAdPlayer().setAdVolume(getAdAudioVolume(this.host.getAdPlayer()));\n      if (this.isShowingPlaceholder) {\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Hiding placeholder - new ads starting\");\n        }\n        this.host.getAdPlayer().hidePlaceholder();\n        this.isShowingPlaceholder = false;\n      }\n    });\n    adPlayer.on(\"content_resume\", () => {\n      sendAdImpressionTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", adIndex: this.timing.currentAdIndex, timestamp: (/* @__PURE__ */ new Date()).toISOString() },\n        this.analyticsContext\n      ).catch(() => {\n      });\n      if (!this.host.video.muted) {\n        this.host.video.muted = true;\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Muted video on content_resume (ad ended, gap between ads)\");\n        }\n      }\n      this.timing.clearAdFailsafeTimer();\n      this.timing.clearAdRequestWatchdog();\n      this.timing.activeAdRequestToken = null;\n      this.showAds = false;\n      if (!this.inAdBreak) {\n        return;\n      }\n      this.timing.consecutiveFailures = 0;\n      this.timing.recordPlayedAdDuration(this.host.getAdPlayer());\n      const remaining = this.timing.getRemainingAdMs();\n      if (this.debug) {\n        console.log(\n          `[CONTINUOUS-FETCH] content_resume event: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`,\n          this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n        );\n      }\n      if (this.timing.hasTimeToStartAnotherAd()) {\n        this.tryNextAvailableAdWithRateLimit();\n      } else {\n        this.handleAdPodComplete();\n      }\n    });\n  }\n  recreateAdController() {\n    if (this.debug) {\n      console.log(\"[StormcloudVideoPlayer] Recreating AdController for new ad\");\n    }\n    const shouldShowPlaceholder = this.inAdBreak && this.showAds;\n    const adPlayer = this.host.getAdPlayer();\n    if (shouldShowPlaceholder && adPlayer) {\n      if (this.debug) {\n        console.log(\"[StormcloudVideoPlayer] Showing placeholder before destroying old AdController\");\n      }\n      this.placeholder.show();\n      adPlayer.showPlaceholder();\n      this.isShowingPlaceholder = true;\n    }\n    const preservedMutedState = adPlayer.getOriginalMutedState();\n    const preservedVolumeState = adPlayer.getOriginalVolume();\n    if (adPlayer) {\n      try {\n        adPlayer.destroy();\n        this.host.video.muted = true;\n      } catch (error) {\n        if (this.debug) {\n          console.warn(\"[StormcloudVideoPlayer] Error destroying old AdController:\", error);\n        }\n      }\n    }\n    const continueLiveStreamDuringAds = this.host.shouldContinueLiveStreamDuringAds();\n    const newAdPlayer = this.host.createAdPlayer(continueLiveStreamDuringAds);\n    this.host.setAdPlayer(newAdPlayer);\n    newAdPlayer.initialize();\n    newAdPlayer.updateOriginalMutedState(preservedMutedState, preservedVolumeState);\n    this.attachImaEventListeners();\n    if (shouldShowPlaceholder) {\n      this.placeholder.show();\n      newAdPlayer.showPlaceholder();\n      this.isShowingPlaceholder = true;\n    }\n  }\n  clearPendingAdBreak() {\n    if (this.prefetchTimerId != null) {\n      clearTimeout(this.prefetchTimerId);\n      this.prefetchTimerId = void 0;\n    }\n    this.pendingAdBreak = null;\n    this.cueManager.pendingScte35CueKey = void 0;\n    this.adConfig.podAssignedByPrefetch = false;\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Ad break start\n  // ───────────────────────────────────────────────────────────────\n  async handleAdStart(_marker) {\n    const scheduled = this.adConfig.findCurrentOrNextBreak(\n      this.host.video.currentTime * 1e3\n    );\n    if (scheduled) {\n      this.adConfig.consumedVmapBreakIds.add(this.adConfig.getAdBreakKey(scheduled));\n    }\n    const tags = this.adConfig.selectVastTagsForBreak(scheduled);\n    let baseVastUrl;\n    if (tags && tags.length > 0 && tags[0]) {\n      baseVastUrl = tags[0];\n    } else if (this.adConfig.apiVastTagUrl) {\n      baseVastUrl = this.adConfig.apiVastTagUrl;\n    } else {\n      this.clearPendingAdBreak();\n      return;\n    }\n    const adBreakDurationMs = _marker.durationSeconds != null ? _marker.durationSeconds * 1e3 : scheduled?.durationMs;\n    if (this.debug) {\n      const mode = this.host.config.isLiveStream ? \"LIVE\" : \"VOD\";\n      console.log(\n        `[CONTINUOUS-FETCH] ${mode} MODE: Target duration=${adBreakDurationMs}ms`\n      );\n    }\n    this.timing.failedVastUrls.clear();\n    this.timing.temporaryFailureUrls.clear();\n    this.timing.consecutiveFailures = 0;\n    this.continuousFetchingActive = true;\n    this.isShowingPlaceholder = false;\n    this.timing.totalAdRequestsInBreak = 0;\n    if (this.adConfig.podAssignedByPrefetch) {\n      this.adConfig.podAssignedByPrefetch = false;\n    } else {\n      this.adConfig.beginNewAdPod();\n    }\n    const currentMuted = this.host.video.muted;\n    const currentVolume = this.host.video.volume;\n    this.host.getAdPlayer().updateOriginalMutedState(currentMuted, currentVolume);\n    this.inAdBreak = true;\n    this.timing.adBreakPlayedDurationMs = 0;\n    this.timing.currentAdIndex = 0;\n    this.timing.totalAdsInBreak = 0;\n    this.adPodQueue = [];\n    this.showAds = false;\n    if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {\n      this.timing.expectedAdBreakDurationMs = adBreakDurationMs;\n    }\n    const podPlayed = await this.startOptimizedPod(\n      baseVastUrl,\n      adBreakDurationMs,\n      currentMuted,\n      currentVolume\n    );\n    if (!podPlayed) {\n      if (this.debug) {\n        console.log(\"[POD] Initial pod request did not fill; attempting top-up pod request\");\n      }\n      this.continuousFetchingActive = true;\n      this.startContinuousFetching(baseVastUrl);\n      await this.tryNextAvailableAdWithRateLimit();\n    }\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Optimized pod (single multi-ad request)\n  // ───────────────────────────────────────────────────────────────\n  async startOptimizedPod(baseVastUrl, breakDurationMs, currentMuted, currentVolume) {\n    let podUrl;\n    if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {\n      podUrl = this.pendingAdBreak.vastUrls[0];\n      this.clearPendingAdBreak();\n    } else {\n      podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);\n    }\n    if (!podUrl) {\n      return false;\n    }\n    this.continuousFetchingActive = false;\n    this.recreateAdController();\n    const adPlayer = this.host.getAdPlayer();\n    const requestToken = ++this.timing.adRequestTokenCounter;\n    this.timing.activeAdRequestToken = requestToken;\n    this.timing.startAdRequestWatchdog(requestToken);\n    try {\n      await this.timing.enforceGlobalRateLimit();\n      this.timing.lastAdRequestTime = Date.now();\n      await adPlayer.requestAds(podUrl);\n      this.timing.clearAdRequestWatchdog();\n      if (this.timing.activeAdRequestToken !== requestToken) {\n        return true;\n      }\n      const podCount = adPlayer.getPodAdCount?.() ?? 1;\n      if (podCount < 1) {\n        return false;\n      }\n      if (this.debug) {\n        console.log(\n          podCount === 1 ? \"[POD] Response contained a single ad; playing it and enabling pod top-up\" : `[POD] Optimized pod returned ${podCount} ads; playing pod in one controller`\n        );\n      }\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: podUrl, timestamp: (/* @__PURE__ */ new Date()).toISOString() },\n        this.analyticsContext\n      ).catch(() => {\n      });\n      this.timing.startAdFailsafeTimer(requestToken);\n      await adPlayer.play();\n      this.timing.markAdStarted();\n      if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n        this.timing.scheduleAdStopAtBreakBoundary();\n      }\n      adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);\n      this.timing.consecutiveFailures = 0;\n      this.continuousFetchingActive = true;\n      this.startContinuousFetching(baseVastUrl);\n      return true;\n    } catch (error) {\n      this.timing.clearAdRequestWatchdog();\n      this.timing.clearAdFailsafeTimer();\n      if (this.timing.activeAdRequestToken === requestToken) {\n        this.timing.activeAdRequestToken = null;\n      }\n      if (this.debug) {\n        console.warn(\"[POD] Optimized pod request/play failed:\", error);\n      }\n      if (this.timing.isTemporaryAdError(error)) {\n        this.timing.temporaryFailureUrls.set(podUrl, Date.now());\n      }\n      this.continuousFetchingActive = true;\n      this.recreateAdController();\n      return false;\n    }\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Continuous fetching\n  // ───────────────────────────────────────────────────────────────\n  startContinuousFetching(baseVastUrl) {\n    if (!this.continuousFetchingActive) {\n      return;\n    }\n    if (this.continuousFetchLoopRunning) {\n      if (this.debug) {\n        console.log(\"[CONTINUOUS-FETCH] Fetch loop already running, skipping duplicate start\");\n      }\n      return;\n    }\n    if (this.debug) {\n      console.log(\"[CONTINUOUS-FETCH] Starting continuous ad fetching loop\");\n    }\n    this.continuousFetchLoopRunning = true;\n    this.continuousFetchLoop(baseVastUrl);\n  }\n  async continuousFetchLoop(baseVastUrl) {\n    const epoch = this.timing.adBreakEpoch;\n    while (this.continuousFetchingActive && this.inAdBreak && epoch === this.timing.adBreakEpoch) {\n      const remaining = Math.min(\n        this.timing.getWallClockRemainingAdMs(),\n        this.timing.getDurationBudgetRemainingMs()\n      );\n      if (remaining <= this.timing.getMinRemainingMsToStartAd()) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Not enough ad break time left (wall=${this.timing.getWallClockRemainingAdMs()}ms, budget=${this.timing.getDurationBudgetRemainingMs()}ms), stopping URL generation`);\n        }\n        break;\n      }\n      if (this.timing.isGamInCooldown()) {\n        if (this.debug) {\n          console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, stopping URL generation\");\n        }\n        break;\n      }\n      if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), stopping URL generation`);\n        }\n        break;\n      }\n      if (this.timing.totalAdRequestsInBreak >= this.timing.maxTotalAdRequestsPerBreak) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Maximum ad requests reached (${this.timing.maxTotalAdRequestsPerBreak}), stopping`);\n        }\n        break;\n      }\n      const maxQueueSize = 5;\n      if (this.adRequestQueue.length >= maxQueueSize) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] URL queue full (${this.adRequestQueue.length}), waiting...`);\n        }\n        await new Promise((resolve) => setTimeout(resolve, 1e3));\n        continue;\n      }\n      const newAdUrl = this.nextAdRequestUrl(baseVastUrl);\n      if (!newAdUrl || this.timing.failedVastUrls.has(newAdUrl) || this.timing.isUrlInCooldown(newAdUrl)) {\n        await new Promise((resolve) => setTimeout(resolve, 500));\n        continue;\n      }\n      if (this.debug) {\n        const queuedUrlsPreview = [...this.adRequestQueue, newAdUrl];\n        console.log(\n          `[CONTINUOUS-FETCH] Queued VAST URL (queue size: ${queuedUrlsPreview.length})`,\n          { queuedUrls: queuedUrlsPreview }\n        );\n      }\n      this.adRequestQueue.push(newAdUrl);\n      const generationDelay = this.timing.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, this.timing.consecutiveFailures), 5e3) : 500;\n      await new Promise((resolve) => setTimeout(resolve, generationDelay));\n    }\n    this.continuousFetchLoopRunning = false;\n    if (this.debug) {\n      console.log(\n        `[CONTINUOUS-FETCH] URL generation loop ended (queued: ${this.adRequestQueue.length})`,\n        this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n      );\n    }\n  }\n  stopContinuousFetching() {\n    this.continuousFetchingActive = false;\n    this.continuousFetchLoopRunning = false;\n    this.placeholder.hide();\n    if (this.debug) {\n      console.log(\"[CONTINUOUS-FETCH] Stopping continuous ad fetching\");\n    }\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Ad iteration\n  // ───────────────────────────────────────────────────────────────\n  async tryNextAvailableAdWithRateLimit() {\n    if (!this.inAdBreak) {\n      return;\n    }\n    const epoch = this.timing.adBreakEpoch;\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), ending ad break gracefully`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n    const backoffMultiplier = Math.pow(2, this.timing.consecutiveFailures);\n    const backoffDelay = Math.min(this.timing.backoffBaseMs * backoffMultiplier, this.timing.maxBackoffMs);\n    const effectiveMinInterval = this.timing.minAdRequestIntervalMs + (this.timing.consecutiveFailures > 0 ? backoffDelay : 0);\n    const timeSinceLastRequest = Date.now() - this.timing.lastAdRequestTime;\n    if (timeSinceLastRequest < effectiveMinInterval) {\n      const waitTime = effectiveMinInterval - timeSinceLastRequest;\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Rate limiting: waiting ${waitTime}ms before next request (backoff: ${this.timing.consecutiveFailures} failures)`);\n      }\n      await new Promise((resolve) => setTimeout(resolve, waitTime));\n    }\n    if (!this.inAdBreak || epoch !== this.timing.adBreakEpoch) {\n      return;\n    }\n    return this.tryNextAvailableAd(0);\n  }\n  async tryNextAvailableAd(retryCount = 0) {\n    if (!this.inAdBreak) {\n      return;\n    }\n    const remaining = this.timing.getRemainingAdMs();\n    if (!this.timing.hasTimeToStartAnotherAd()) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Not enough time remaining (${remaining}ms) to start another ad, ending ad break`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) {\n        console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, ending ad break gracefully\");\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), ending ad break`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n    this.timing.cleanupTemporaryFailures();\n    while (this.adRequestQueue.length > 0) {\n      const nextAdUrl = this.adRequestQueue.shift();\n      if (!nextAdUrl) continue;\n      if (this.timing.failedVastUrls.has(nextAdUrl)) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping permanently-failed URL, trying next...\");\n        continue;\n      }\n      if (this.timing.isUrlInCooldown(nextAdUrl)) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping URL in cooldown (recent no-fill), trying next...\");\n        continue;\n      }\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Requesting next ad via HLS ad player (${this.timing.currentAdIndex + 1}, ${this.adRequestQueue.length} remaining in queue)`);\n      }\n      try {\n        await this.playSingleAd(nextAdUrl);\n        this.timing.consecutiveFailures = 0;\n      } catch (error) {\n        const errorMessage = error.message;\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Ad request failed:\", errorMessage);\n        if (errorMessage.includes(\"ad_duration_exceeds_budget\")) {\n          await this.tryNextAvailableAdWithRateLimit();\n          return;\n        }\n        if (!errorMessage.includes(\"permanently failed\") && !errorMessage.includes(\"in cooldown\")) {\n          this.timing.consecutiveFailures++;\n        }\n        await this.tryNextAvailableAdWithRateLimit();\n      }\n      return;\n    }\n    const maxRetries = 3;\n    if (this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Queue empty, waiting for URLs... (${retryCount + 1}/${maxRetries})`);\n      }\n      await new Promise((resolve) => setTimeout(resolve, 500));\n      await this.tryNextAvailableAd(retryCount + 1);\n      return;\n    }\n    if (!this.isShowingPlaceholder && remaining > 1e3) {\n      this.showPlaceholderAndWaitForAds();\n    } else {\n      if (this.debug) console.log(\"[CONTINUOUS-FETCH] No more ads available, ending ad break\");\n      this.handleAdPodComplete();\n    }\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Single ad playback\n  // ───────────────────────────────────────────────────────────────\n  async playSingleAd(vastTagUrl) {\n    const adPlayer = this.host.getAdPlayer();\n    if (adPlayer.isAdPlaying()) return;\n    if (this.timing.failedVastUrls.has(vastTagUrl)) {\n      if (this.debug) console.warn(\"[AD-ERROR] Skipping permanently-failed VAST URL:\", vastTagUrl);\n      throw new Error(\"VAST URL permanently failed\");\n    }\n    if (this.timing.isUrlInCooldown(vastTagUrl)) {\n      if (this.debug) console.warn(\"[AD-ERROR] Skipping URL in cooldown (recent no-fill):\", vastTagUrl);\n      throw new Error(\"VAST URL in cooldown after no-fill\");\n    }\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.warn(\"[AD-ERROR] Too many consecutive failures, not requesting more ads\");\n      throw new Error(\"Too many consecutive failures\");\n    }\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) console.warn(\"[CIRCUIT-BREAKER] GAM in cooldown, skipping ad request\");\n      throw new Error(\"GAM in cooldown\");\n    }\n    this.recreateAdController();\n    const requestToken = ++this.timing.adRequestTokenCounter;\n    this.timing.activeAdRequestToken = requestToken;\n    this.timing.startAdRequestWatchdog(requestToken);\n    try {\n      await this.timing.enforceGlobalRateLimit();\n      this.timing.lastAdRequestTime = Date.now();\n      const ap = this.host.getAdPlayer();\n      await ap.requestAds(vastTagUrl);\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: vastTagUrl, timestamp: (/* @__PURE__ */ new Date()).toISOString() },\n        this.analyticsContext\n      ).catch(() => {\n      });\n      this.timing.clearAdRequestWatchdog();\n      if (this.timing.activeAdRequestToken !== requestToken) return;\n      if (!this.timing.ensureLoadedAdFitsBudget(ap)) {\n        this.timing.rejectLoadedAdForDuration(ap);\n        this.recreateAdController();\n        throw new Error(\"ad_duration_exceeds_budget\");\n      }\n      try {\n        this.timing.startAdFailsafeTimer(requestToken);\n        await ap.play();\n        this.timing.markAdStarted();\n        if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n          this.timing.scheduleAdStopAtBreakBoundary();\n        }\n        ap.setAdVolume(getAdAudioVolume(ap));\n        this.timing.consecutiveFailures = 0;\n        this.timing.temporaryFailureUrls.delete(vastTagUrl);\n      } catch (playError) {\n        if (this.debug) console.error(\"[AD-ERROR] Failed to play ad:\", playError);\n        if (this.timing.isTemporaryAdError(playError)) {\n          this.timing.temporaryFailureUrls.set(vastTagUrl, Date.now());\n          if (this.debug) console.log(\"[AD-ERROR] Temporary play error - URL can be retried after cooldown\");\n        } else {\n          this.timing.failedVastUrls.add(vastTagUrl);\n          if (this.debug) console.log(\"[AD-ERROR] Permanent play error - URL blacklisted\");\n        }\n        this.timing.clearAdFailsafeTimer();\n        if (this.timing.activeAdRequestToken === requestToken) {\n          this.timing.activeAdRequestToken = null;\n        }\n        throw playError;\n      }\n    } catch (error) {\n      const errorMessage = error?.message || \"\";\n      if (this.debug) console.error(\"[AD-ERROR] Ad request failed:\", errorMessage);\n      if (this.timing.isTemporaryAdError(error)) {\n        this.timing.temporaryFailureUrls.set(vastTagUrl, Date.now());\n        if (this.debug) console.log(\"[AD-ERROR] Temporary error (no-fill/timeout) - URL can be retried after cooldown\");\n      } else {\n        this.timing.failedVastUrls.add(vastTagUrl);\n        if (this.debug) console.log(\"[AD-ERROR] Permanent error - URL blacklisted:\", errorMessage);\n      }\n      this.timing.clearAdRequestWatchdog();\n      this.timing.clearAdFailsafeTimer();\n      if (this.timing.activeAdRequestToken === requestToken) {\n        this.timing.activeAdRequestToken = null;\n      }\n      throw error;\n    }\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Placeholder wait\n  // ───────────────────────────────────────────────────────────────\n  async showPlaceholderAndWaitForAds() {\n    const remaining = this.timing.getRemainingAdMs();\n    const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, skipping placeholder wait\");\n      this.handleAdPodComplete();\n      return;\n    }\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping placeholder - too many consecutive failures\");\n      this.handleAdPodComplete();\n      return;\n    }\n    if (waitTime < 1e3) {\n      this.handleAdPodComplete();\n      return;\n    }\n    if (this.debug) {\n      console.log(`[CONTINUOUS-FETCH] Showing placeholder for ${waitTime}ms while waiting for ad URLs`);\n    }\n    this.isShowingPlaceholder = true;\n    this.host.getAdPlayer().showPlaceholder();\n    const checkInterval = 300;\n    const maxChecks = Math.floor(waitTime / checkInterval);\n    for (let i = 0; i < maxChecks; i++) {\n      await new Promise((resolve) => setTimeout(resolve, checkInterval));\n      if (!this.inAdBreak) return;\n      if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Too many failures during placeholder wait\");\n        break;\n      }\n      if (!this.timing.hasTimeToStartAnotherAd()) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Not enough time remaining during placeholder wait, ending ad break\");\n        break;\n      }\n      while (this.adRequestQueue.length > 0) {\n        if (!this.timing.hasTimeToStartAnotherAd()) break;\n        const nextAdUrl = this.adRequestQueue.shift();\n        if (!nextAdUrl) continue;\n        if (this.timing.failedVastUrls.has(nextAdUrl)) continue;\n        if (this.timing.isUrlInCooldown(nextAdUrl)) continue;\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Ad URL available, requesting via HLS ad player\");\n        this.isShowingPlaceholder = false;\n        this.host.getAdPlayer().hidePlaceholder();\n        try {\n          await this.playSingleAd(nextAdUrl);\n          this.timing.consecutiveFailures = 0;\n        } catch (error) {\n          const errorMessage = error.message;\n          if (errorMessage.includes(\"ad_duration_exceeds_budget\")) {\n            await this.tryNextAvailableAdWithRateLimit();\n            return;\n          }\n          if (!errorMessage.includes(\"permanently failed\") && !errorMessage.includes(\"in cooldown\")) {\n            this.timing.consecutiveFailures++;\n          }\n          await this.tryNextAvailableAdWithRateLimit();\n        }\n        return;\n      }\n    }\n    if (this.debug) console.log(\"[CONTINUOUS-FETCH] Placeholder timeout, ending ad break\");\n    this.isShowingPlaceholder = false;\n    this.host.getAdPlayer().hidePlaceholder();\n    this.handleAdPodComplete();\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Ad break completion\n  // ───────────────────────────────────────────────────────────────\n  handleAdPodComplete() {\n    if (this.debug) {\n      console.log(\"[StormcloudVideoPlayer] Ad pod complete - cleaning up\");\n    }\n    this.timing.resetForBreakEnd();\n    this.stopContinuousFetching();\n    this.clearPendingAdBreak();\n    if (this.isShowingPlaceholder) {\n      this.host.getAdPlayer().hidePlaceholder();\n      this.isShowingPlaceholder = false;\n    }\n    this.adRequestQueue = [];\n    this.inAdBreak = false;\n    this.adDetectSentForCurrentBreak = false;\n    this.cueManager.activeScte35BreakKey = void 0;\n    this.cueManager.scheduledScte35BreakKey = void 0;\n    this.adPodQueue = [];\n    this.showAds = false;\n    this.host.getAdPlayer().stop().catch(() => {\n    });\n    const restoredMuted = this.host.getAdPlayer().getOriginalMutedState();\n    const restoredVolume = this.host.getAdPlayer().getOriginalVolume();\n    if (this.host.video.muted !== restoredMuted) {\n      this.host.video.muted = restoredMuted;\n    }\n    if (Math.abs(this.host.video.volume - restoredVolume) > 0.01) {\n      this.host.video.volume = restoredVolume;\n    }\n    this.host.video.style.visibility = \"visible\";\n    this.host.video.style.opacity = \"1\";\n    if (this.host.isLiveStream()) {\n      const liveSyncPos = this.host.getLiveSyncPosition();\n      if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {\n        if (this.debug) {\n          console.log(\n            `[StormcloudVideoPlayer] Live break end \\u2014 snapping to live edge: ${this.host.video.currentTime.toFixed(2)}s \\u2192 ${liveSyncPos.toFixed(2)}s`\n          );\n        }\n        this.host.video.currentTime = liveSyncPos;\n        this.host.restartHlsLoad(liveSyncPos);\n      }\n      this.resumeContentPlayback();\n      this.monitorLiveResumeStall(this.host.video.currentTime);\n      return;\n    }\n    this.resumeContentPlayback();\n  }\n  getBufferedAhead() {\n    const video = this.host.video;\n    const t = video.currentTime;\n    const ranges = video.buffered;\n    for (let i = 0; i < ranges.length; i++) {\n      if (t >= ranges.start(i) - 0.5 && t <= ranges.end(i)) {\n        return ranges.end(i) - t;\n      }\n    }\n    return 0;\n  }\n  monitorLiveResumeStall(resumeBaseline) {\n    const pollIntervalMs = 1e3;\n    const graceMs = 5e3;\n    const recoveryCooldownMs = 5e3;\n    const maxRecoveries = 2;\n    const maxMonitorMs = 3e4;\n    const startTime = Date.now();\n    let baseline = resumeBaseline;\n    let recoveries = 0;\n    let lastRecoveryAt = 0;\n    const poll = () => {\n      if (this.inAdBreak) {\n        return;\n      }\n      const video = this.host.video;\n      const currentTime = video.currentTime;\n      const advanced = currentTime > baseline + 0.5;\n      const playing = !video.paused && video.readyState >= 3;\n      if (advanced && playing) {\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Live stream resumed successfully after ad break\");\n        }\n        return;\n      }\n      if (video.paused) {\n        video.play()?.catch(() => {\n        });\n      }\n      const elapsed = Date.now() - startTime;\n      const bufferedAhead = this.getBufferedAhead();\n      const wedged = bufferedAhead < 0.5 && !advanced;\n      if (elapsed >= maxMonitorMs) {\n        if (this.debug && !advanced) {\n          console.warn(\n            `[StormcloudVideoPlayer] Live stream failed to resume after ${(maxMonitorMs / 1e3).toFixed(0)}s (t=${currentTime.toFixed(2)}s, buffered=${bufferedAhead.toFixed(2)}s)`\n          );\n        }\n        return;\n      }\n      const canRecover = wedged && elapsed >= graceMs && Date.now() - lastRecoveryAt >= recoveryCooldownMs && recoveries < maxRecoveries;\n      if (canRecover) {\n        recoveries++;\n        lastRecoveryAt = Date.now();\n        const isFinalAttempt = recoveries >= maxRecoveries;\n        if (!isFinalAttempt) {\n          const liveSyncPos = this.host.getLiveSyncPosition();\n          const reloadPos = liveSyncPos != null && liveSyncPos > currentTime ? liveSyncPos : currentTime;\n          if (this.debug) {\n            console.warn(\n              `[StormcloudVideoPlayer] Content wedged after ad break (t=${currentTime.toFixed(2)}s, buffered=${bufferedAhead.toFixed(2)}s) \\u2014 restarting HLS load at ${reloadPos.toFixed(2)}s (recovery ${recoveries}/${maxRecoveries})`\n            );\n          }\n          if (liveSyncPos != null && liveSyncPos > currentTime) {\n            video.currentTime = liveSyncPos;\n          }\n          this.host.restartHlsLoad(reloadPos);\n        } else {\n          if (this.debug) {\n            console.warn(\n              `[StormcloudVideoPlayer] Content still wedged after ad break (t=${currentTime.toFixed(2)}s, buffered=${bufferedAhead.toFixed(2)}s) \\u2014 hard-reloading live stream (recovery ${recoveries}/${maxRecoveries})`\n            );\n          }\n          this.host.reloadLiveStream();\n        }\n        baseline = video.currentTime;\n        if (video.paused) {\n          video.play()?.catch(() => {\n          });\n        }\n      }\n      window.setTimeout(poll, pollIntervalMs);\n    };\n    window.setTimeout(poll, pollIntervalMs);\n  }\n  resumeContentPlayback(attempt = 0) {\n    const maxAttempts = 3;\n    if (!this.host.video.paused) {\n      if (this.debug && attempt === 0) {\n        console.log(\"[StormcloudVideoPlayer] Content video already playing after ads\");\n      }\n      return;\n    }\n    if (this.debug) {\n      console.log(\n        `[StormcloudVideoPlayer] Resuming content playback after ads (attempt ${attempt + 1}/${maxAttempts})`\n      );\n    }\n    const playResult = this.host.video.play();\n    if (playResult && typeof playResult.catch === \"function\") {\n      playResult.catch((error) => {\n        if (attempt + 1 >= maxAttempts) {\n          if (this.debug) {\n            console.warn(\n              \"[StormcloudVideoPlayer] Failed to resume content playback after ads:\",\n              error\n            );\n          }\n          return;\n        }\n        const backoffMs = 250 * (attempt + 1);\n        window.setTimeout(() => {\n          this.resumeContentPlayback(attempt + 1);\n        }, backoffMs);\n      });\n    }\n  }\n  handleAdFailure() {\n    this.timing.consecutiveFailures++;\n    this.timing.recordGamNoFill();\n    const remaining = this.timing.getRemainingAdMs();\n    if (this.debug) {\n      console.log(\n        `[CONTINUOUS-FETCH] Ad failure: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}, consecutiveFailures=${this.timing.consecutiveFailures}`,\n        this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n      );\n    }\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.log(`[CONTINUOUS-FETCH] Max consecutive failures reached (${this.timing.consecutiveFailures}), ending ad break gracefully`);\n      this.handleAdPodComplete();\n      return;\n    }\n    if (this.timing.hasTimeToStartAnotherAd()) {\n      this.tryNextAvailableAdWithRateLimit();\n    } else {\n      if (this.debug) console.log(\"[AD-ERROR] Ad failed and not enough time remaining for another ad. Failed URLs:\", this.timing.failedVastUrls.size);\n      this.handleAdPodComplete();\n    }\n  }\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n  AdBreakOrchestrator\n});\n","import type {\n  StormcloudVideoPlayerConfig,\n  Scte35Marker,\n  AdController,\n} from \"../types\";\nimport {\n  buildAnalyticsContext,\n  sendAdLoadedTracking,\n  sendAdImpressionTracking,\n} from \"../utils/tracking\";\nimport type { AdTimingService } from \"./AdTimingService\";\nimport type { AdConfigManager } from \"./AdConfigManager\";\nimport type { Scte35CueManager } from \"./Scte35CueManager\";\nimport type { PlaceholderLayer } from \"./PlaceholderLayer\";\nimport { getAdAudioVolume } from \"./PlayerControls\";\n\nexport interface AdBreakOrchestratorHost {\n  readonly config: StormcloudVideoPlayerConfig;\n  readonly video: HTMLVideoElement;\n  getAdPlayer(): AdController;\n  setAdPlayer(player: AdController): void;\n  createAdPlayer(continueLiveStreamDuringAds: boolean): AdController;\n  shouldContinueLiveStreamDuringAds(): boolean;\n  isLiveStream(): boolean;\n  getLiveSyncPosition(): number | undefined;\n  restartHlsLoad(position: number): void;\n  reloadLiveStream(): void;\n  generatePodVastUrl(baseUrl: string, breakDurationMs?: number): string;\n}\n\nexport class AdBreakOrchestrator {\n  inAdBreak = false;\n  showAds: boolean = false;\n  isShowingPlaceholder: boolean = false;\n  adDetectSentForCurrentBreak: boolean = false;\n\n  private continuousFetchingActive: boolean = false;\n  private continuousFetchLoopRunning = false;\n  adRequestQueue: string[] = [];\n  adPodQueue: string[] = [];\n  private readonly maxPlaceholderDurationMs: number = 5000;\n\n  pendingAdBreak: {\n    marker: Scte35Marker;\n    detectedAtFragmentSn?: number;\n    vastUrls: string[];\n    isFetching: boolean;\n    fetchStartTime?: number;\n  } | null = null;\n  prefetchTimerId: number | undefined;\n\n  private readonly host: AdBreakOrchestratorHost;\n  private readonly timing: AdTimingService;\n  private readonly adConfig: AdConfigManager;\n  private readonly cueManager: Scte35CueManager;\n  private readonly placeholder: PlaceholderLayer;\n\n  constructor(\n    host: AdBreakOrchestratorHost,\n    timing: AdTimingService,\n    adConfig: AdConfigManager,\n    cueManager: Scte35CueManager,\n    placeholder: PlaceholderLayer\n  ) {\n    this.host = host;\n    this.timing = timing;\n    this.adConfig = adConfig;\n    this.cueManager = cueManager;\n    this.placeholder = placeholder;\n  }\n\n  private get debug(): boolean {\n    return !!this.host.config.debugAdTiming;\n  }\n\n  private get analyticsContext() {\n    return buildAnalyticsContext(this.host.config);\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  IMA event listeners\n  // ───────────────────────────────────────────────────────────────\n\n  private nextAdRequestUrl(baseVastUrl: string): string {\n    const remaining = Math.min(\n      this.timing.getWallClockRemainingAdMs(),\n      this.timing.getDurationBudgetRemainingMs()\n    );\n    const breakDurationMs = remaining > 0 ? remaining : undefined;\n    return this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);\n  }\n\n  attachImaEventListeners(): void {\n    const adPlayer = this.host.getAdPlayer();\n    adPlayer.on(\"pod_ad_started\", (payload?: any) => {\n      this.timing.notePodAdStarted();\n      if (this.debug) {\n        console.log(\n          `[POD] Playing pod ad ${payload?.index != null ? payload.index + 1 : \"?\"}/${payload?.total ?? \"?\"}`\n        );\n      }\n    });\n    adPlayer.on(\"all_ads_completed\", () => {\n      sendAdImpressionTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", adIndex: this.timing.currentAdIndex, timestamp: new Date().toISOString() },\n        this.analyticsContext\n      ).catch(() => {});\n\n      this.timing.recordPlayedAdDuration(this.host.getAdPlayer());\n      const remaining = this.timing.getRemainingAdMs();\n\n      this.timing.consecutiveFailures = 0;\n\n      if (this.debug) {\n        console.log(\n          `[StormcloudVideoPlayer] all_ads_completed event received - remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`,\n          this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n        );\n      }\n\n      if (this.inAdBreak) {\n        if (this.timing.hasTimeToStartAnotherAd()) {\n          this.tryNextAvailableAdWithRateLimit();\n        } else {\n          this.handleAdPodComplete();\n        }\n      }\n    });\n    adPlayer.on(\"ad_error\", (errorPayload?: any) => {\n      let errorMessage = \"Ad playback failed\";\n\n      if (errorPayload) {\n        const errorCode = errorPayload.code || errorPayload.errorCode || \"unknown\";\n        const vastErrorCode = errorPayload.vastErrorCode;\n        const message = errorPayload.message || errorPayload.errorMessage || \"Unknown error\";\n        const cause = errorPayload.cause || errorPayload.innerError || errorPayload.error;\n\n        errorMessage = `Ad error: AdError ${errorCode}: ${message}`;\n\n        if (vastErrorCode && vastErrorCode !== \"N/A\" && vastErrorCode !== errorCode) {\n          errorMessage += ` (VAST Error Code: ${vastErrorCode})`;\n        }\n\n        if (cause) {\n          const causeMessage = typeof cause === \"string\" ? cause : (cause.message || String(cause));\n          errorMessage += `. Caused by: ${causeMessage}`;\n        }\n      }\n\n      console.error(\"[AD-ERROR]\", errorMessage, errorPayload || \"\");\n      this.handleAdFailure();\n    });\n    adPlayer.on(\"content_pause\", () => {\n      this.timing.clearAdFailsafeTimer();\n      this.timing.clearAdRequestWatchdog();\n      this.timing.activeAdRequestToken = null;\n      this.showAds = true;\n      this.timing.resetGamNoFillCounter();\n\n      if (this.inAdBreak && this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n        this.timing.scheduleAdStopAtBreakBoundary();\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Starting ad break timer on content_pause (first ad starting)\");\n        }\n      }\n\n      this.host.getAdPlayer().setAdVolume(getAdAudioVolume(this.host.getAdPlayer()));\n\n      if (this.isShowingPlaceholder) {\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Hiding placeholder - new ads starting\");\n        }\n        this.host.getAdPlayer().hidePlaceholder();\n        this.isShowingPlaceholder = false;\n      }\n    });\n    adPlayer.on(\"content_resume\", () => {\n      sendAdImpressionTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", adIndex: this.timing.currentAdIndex, timestamp: new Date().toISOString() },\n        this.analyticsContext\n      ).catch(() => {});\n\n      if (!this.host.video.muted) {\n        this.host.video.muted = true;\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Muted video on content_resume (ad ended, gap between ads)\");\n        }\n      }\n\n      this.timing.clearAdFailsafeTimer();\n      this.timing.clearAdRequestWatchdog();\n      this.timing.activeAdRequestToken = null;\n      this.showAds = false;\n\n      if (!this.inAdBreak) {\n        return;\n      }\n\n      this.timing.consecutiveFailures = 0;\n\n      this.timing.recordPlayedAdDuration(this.host.getAdPlayer());\n      const remaining = this.timing.getRemainingAdMs();\n\n      if (this.debug) {\n        console.log(\n          `[CONTINUOUS-FETCH] content_resume event: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`,\n          this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n        );\n      }\n\n      if (this.timing.hasTimeToStartAnotherAd()) {\n        this.tryNextAvailableAdWithRateLimit();\n      } else {\n        this.handleAdPodComplete();\n      }\n    });\n  }\n\n  recreateAdController(): void {\n    if (this.debug) {\n      console.log(\"[StormcloudVideoPlayer] Recreating AdController for new ad\");\n    }\n\n    const shouldShowPlaceholder = this.inAdBreak && this.showAds;\n    const adPlayer = this.host.getAdPlayer();\n    if (shouldShowPlaceholder && adPlayer) {\n      if (this.debug) {\n        console.log(\"[StormcloudVideoPlayer] Showing placeholder before destroying old AdController\");\n      }\n      this.placeholder.show();\n      adPlayer.showPlaceholder();\n      this.isShowingPlaceholder = true;\n    }\n\n    const preservedMutedState = adPlayer.getOriginalMutedState();\n    const preservedVolumeState = adPlayer.getOriginalVolume();\n\n    if (adPlayer) {\n      try {\n        adPlayer.destroy();\n        this.host.video.muted = true;\n      } catch (error) {\n        if (this.debug) {\n          console.warn(\"[StormcloudVideoPlayer] Error destroying old AdController:\", error);\n        }\n      }\n    }\n\n    const continueLiveStreamDuringAds = this.host.shouldContinueLiveStreamDuringAds();\n    const newAdPlayer = this.host.createAdPlayer(continueLiveStreamDuringAds);\n    this.host.setAdPlayer(newAdPlayer);\n\n    newAdPlayer.initialize();\n    newAdPlayer.updateOriginalMutedState(preservedMutedState, preservedVolumeState);\n    this.attachImaEventListeners();\n\n    if (shouldShowPlaceholder) {\n      this.placeholder.show();\n      newAdPlayer.showPlaceholder();\n      this.isShowingPlaceholder = true;\n    }\n  }\n\n  clearPendingAdBreak(): void {\n    if (this.prefetchTimerId != null) {\n      clearTimeout(this.prefetchTimerId);\n      this.prefetchTimerId = undefined;\n    }\n    this.pendingAdBreak = null;\n    this.cueManager.pendingScte35CueKey = undefined;\n    this.adConfig.podAssignedByPrefetch = false;\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Ad break start\n  // ───────────────────────────────────────────────────────────────\n\n  async handleAdStart(_marker: Scte35Marker): Promise<void> {\n    const scheduled = this.adConfig.findCurrentOrNextBreak(\n      this.host.video.currentTime * 1000\n    );\n    if (scheduled) {\n      this.adConfig.consumedVmapBreakIds.add(this.adConfig.getAdBreakKey(scheduled));\n    }\n    const tags = this.adConfig.selectVastTagsForBreak(scheduled);\n\n    let baseVastUrl: string;\n\n    if (tags && tags.length > 0 && tags[0]) {\n      baseVastUrl = tags[0];\n    } else if (this.adConfig.apiVastTagUrl) {\n      baseVastUrl = this.adConfig.apiVastTagUrl;\n    } else {\n      this.clearPendingAdBreak();\n      return;\n    }\n\n    const adBreakDurationMs =\n      _marker.durationSeconds != null\n        ? _marker.durationSeconds * 1000\n        : scheduled?.durationMs;\n\n    if (this.debug) {\n      const mode = this.host.config.isLiveStream ? \"LIVE\" : \"VOD\";\n      console.log(\n        `[CONTINUOUS-FETCH] ${mode} MODE: Target duration=${adBreakDurationMs}ms`\n      );\n    }\n\n    this.timing.failedVastUrls.clear();\n    this.timing.temporaryFailureUrls.clear();\n    this.timing.consecutiveFailures = 0;\n    this.continuousFetchingActive = true;\n    this.isShowingPlaceholder = false;\n    this.timing.totalAdRequestsInBreak = 0;\n\n    if (this.adConfig.podAssignedByPrefetch) {\n      this.adConfig.podAssignedByPrefetch = false;\n    } else {\n      this.adConfig.beginNewAdPod();\n    }\n\n    const currentMuted = this.host.video.muted;\n    const currentVolume = this.host.video.volume;\n    this.host.getAdPlayer().updateOriginalMutedState(currentMuted, currentVolume);\n\n    this.inAdBreak = true;\n    this.timing.adBreakPlayedDurationMs = 0;\n    this.timing.currentAdIndex = 0;\n    this.timing.totalAdsInBreak = 0;\n    this.adPodQueue = [];\n    this.showAds = false;\n\n    if (\n      this.timing.expectedAdBreakDurationMs == null &&\n      adBreakDurationMs != null\n    ) {\n      this.timing.expectedAdBreakDurationMs = adBreakDurationMs;\n    }\n\n    const podPlayed = await this.startOptimizedPod(\n      baseVastUrl,\n      adBreakDurationMs,\n      currentMuted,\n      currentVolume\n    );\n\n    if (!podPlayed) {\n      if (this.debug) {\n        console.log(\"[POD] Initial pod request did not fill; attempting top-up pod request\");\n      }\n      this.continuousFetchingActive = true;\n      this.startContinuousFetching(baseVastUrl);\n      await this.tryNextAvailableAdWithRateLimit();\n    }\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Optimized pod (single multi-ad request)\n  // ───────────────────────────────────────────────────────────────\n\n  private async startOptimizedPod(\n    baseVastUrl: string,\n    breakDurationMs: number | undefined,\n    currentMuted: boolean,\n    currentVolume: number\n  ): Promise<boolean> {\n    let podUrl: string | undefined;\n    if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {\n      podUrl = this.pendingAdBreak.vastUrls[0];\n      this.clearPendingAdBreak();\n    } else {\n      podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);\n    }\n\n    if (!podUrl) {\n      return false;\n    }\n\n    this.continuousFetchingActive = false;\n\n    this.recreateAdController();\n\n    const adPlayer = this.host.getAdPlayer();\n    const requestToken = ++this.timing.adRequestTokenCounter;\n    this.timing.activeAdRequestToken = requestToken;\n    this.timing.startAdRequestWatchdog(requestToken);\n\n    try {\n      await this.timing.enforceGlobalRateLimit();\n      this.timing.lastAdRequestTime = Date.now();\n      await adPlayer.requestAds(podUrl);\n      this.timing.clearAdRequestWatchdog();\n\n      if (this.timing.activeAdRequestToken !== requestToken) {\n        return true;\n      }\n\n      const podCount = adPlayer.getPodAdCount?.() ?? 1;\n      if (podCount < 1) {\n        return false;\n      }\n\n      if (this.debug) {\n        console.log(\n          podCount === 1\n            ? \"[POD] Response contained a single ad; playing it and enabling pod top-up\"\n            : `[POD] Optimized pod returned ${podCount} ads; playing pod in one controller`\n        );\n      }\n\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: podUrl, timestamp: new Date().toISOString() },\n        this.analyticsContext\n      ).catch(() => {});\n\n      this.timing.startAdFailsafeTimer(requestToken);\n      await adPlayer.play();\n      this.timing.markAdStarted();\n\n      if (\n        this.timing.expectedAdBreakDurationMs != null &&\n        this.timing.getAdBreakEndWallClockMs() == null\n      ) {\n        this.timing.scheduleAdStopAtBreakBoundary();\n      }\n\n      adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);\n      this.timing.consecutiveFailures = 0;\n\n      this.continuousFetchingActive = true;\n      this.startContinuousFetching(baseVastUrl);\n\n      return true;\n    } catch (error) {\n      this.timing.clearAdRequestWatchdog();\n      this.timing.clearAdFailsafeTimer();\n      if (this.timing.activeAdRequestToken === requestToken) {\n        this.timing.activeAdRequestToken = null;\n      }\n      if (this.debug) {\n        console.warn(\"[POD] Optimized pod request/play failed:\", error);\n      }\n\n      if (this.timing.isTemporaryAdError(error as Error)) {\n        this.timing.temporaryFailureUrls.set(podUrl, Date.now());\n      }\n\n      this.continuousFetchingActive = true;\n      this.recreateAdController();\n      return false;\n    }\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Continuous fetching\n  // ───────────────────────────────────────────────────────────────\n\n  private startContinuousFetching(baseVastUrl: string): void {\n    if (!this.continuousFetchingActive) {\n      return;\n    }\n\n    if (this.continuousFetchLoopRunning) {\n      if (this.debug) {\n        console.log(\"[CONTINUOUS-FETCH] Fetch loop already running, skipping duplicate start\");\n      }\n      return;\n    }\n\n    if (this.debug) {\n      console.log(\"[CONTINUOUS-FETCH] Starting continuous ad fetching loop\");\n    }\n\n    this.continuousFetchLoopRunning = true;\n    this.continuousFetchLoop(baseVastUrl);\n  }\n\n  private async continuousFetchLoop(baseVastUrl: string): Promise<void> {\n    const epoch = this.timing.adBreakEpoch;\n    while (\n      this.continuousFetchingActive &&\n      this.inAdBreak &&\n      epoch === this.timing.adBreakEpoch\n    ) {\n      const remaining = Math.min(\n        this.timing.getWallClockRemainingAdMs(),\n        this.timing.getDurationBudgetRemainingMs()\n      );\n\n      if (remaining <= this.timing.getMinRemainingMsToStartAd()) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Not enough ad break time left (wall=${this.timing.getWallClockRemainingAdMs()}ms, budget=${this.timing.getDurationBudgetRemainingMs()}ms), stopping URL generation`);\n        }\n        break;\n      }\n\n      if (this.timing.isGamInCooldown()) {\n        if (this.debug) {\n          console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, stopping URL generation\");\n        }\n        break;\n      }\n\n      if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), stopping URL generation`);\n        }\n        break;\n      }\n\n      if (this.timing.totalAdRequestsInBreak >= this.timing.maxTotalAdRequestsPerBreak) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Maximum ad requests reached (${this.timing.maxTotalAdRequestsPerBreak}), stopping`);\n        }\n        break;\n      }\n\n      const maxQueueSize = 5;\n      if (this.adRequestQueue.length >= maxQueueSize) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] URL queue full (${this.adRequestQueue.length}), waiting...`);\n        }\n        await new Promise(resolve => setTimeout(resolve, 1000));\n        continue;\n      }\n\n      const newAdUrl = this.nextAdRequestUrl(baseVastUrl);\n\n      if (!newAdUrl || this.timing.failedVastUrls.has(newAdUrl) || this.timing.isUrlInCooldown(newAdUrl)) {\n        await new Promise(resolve => setTimeout(resolve, 500));\n        continue;\n      }\n\n      if (this.debug) {\n        const queuedUrlsPreview = [...this.adRequestQueue, newAdUrl];\n        console.log(\n          `[CONTINUOUS-FETCH] Queued VAST URL (queue size: ${queuedUrlsPreview.length})`,\n          { queuedUrls: queuedUrlsPreview }\n        );\n      }\n\n      this.adRequestQueue.push(newAdUrl);\n\n      const generationDelay = this.timing.consecutiveFailures > 0\n        ? Math.min(1000 * Math.pow(2, this.timing.consecutiveFailures), 5000)\n        : 500;\n      await new Promise(resolve => setTimeout(resolve, generationDelay));\n    }\n\n    this.continuousFetchLoopRunning = false;\n\n    if (this.debug) {\n      console.log(\n        `[CONTINUOUS-FETCH] URL generation loop ended (queued: ${this.adRequestQueue.length})`,\n        this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n      );\n    }\n  }\n\n  stopContinuousFetching(): void {\n    this.continuousFetchingActive = false;\n    this.continuousFetchLoopRunning = false;\n\n    this.placeholder.hide();\n\n    if (this.debug) {\n      console.log(\"[CONTINUOUS-FETCH] Stopping continuous ad fetching\");\n    }\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Ad iteration\n  // ───────────────────────────────────────────────────────────────\n\n  async tryNextAvailableAdWithRateLimit(): Promise<void> {\n    if (!this.inAdBreak) {\n      return;\n    }\n\n    const epoch = this.timing.adBreakEpoch;\n\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), ending ad break gracefully`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n\n    const backoffMultiplier = Math.pow(2, this.timing.consecutiveFailures);\n    const backoffDelay = Math.min(this.timing.backoffBaseMs * backoffMultiplier, this.timing.maxBackoffMs);\n    const effectiveMinInterval = this.timing.minAdRequestIntervalMs + (this.timing.consecutiveFailures > 0 ? backoffDelay : 0);\n\n    const timeSinceLastRequest = Date.now() - this.timing.lastAdRequestTime;\n    if (timeSinceLastRequest < effectiveMinInterval) {\n      const waitTime = effectiveMinInterval - timeSinceLastRequest;\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Rate limiting: waiting ${waitTime}ms before next request (backoff: ${this.timing.consecutiveFailures} failures)`);\n      }\n      await new Promise(resolve => setTimeout(resolve, waitTime));\n    }\n\n    if (!this.inAdBreak || epoch !== this.timing.adBreakEpoch) {\n      return;\n    }\n\n    return this.tryNextAvailableAd(0);\n  }\n\n  private async tryNextAvailableAd(retryCount: number = 0): Promise<void> {\n    if (!this.inAdBreak) {\n      return;\n    }\n\n    const remaining = this.timing.getRemainingAdMs();\n\n    if (!this.timing.hasTimeToStartAnotherAd()) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Not enough time remaining (${remaining}ms) to start another ad, ending ad break`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) {\n        console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, ending ad break gracefully\");\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), ending ad break`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n\n    this.timing.cleanupTemporaryFailures();\n\n    while (this.adRequestQueue.length > 0) {\n      const nextAdUrl = this.adRequestQueue.shift();\n\n      if (!nextAdUrl) continue;\n\n      if (this.timing.failedVastUrls.has(nextAdUrl)) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping permanently-failed URL, trying next...\");\n        continue;\n      }\n\n      if (this.timing.isUrlInCooldown(nextAdUrl)) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping URL in cooldown (recent no-fill), trying next...\");\n        continue;\n      }\n\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Requesting next ad via HLS ad player (${this.timing.currentAdIndex + 1}, ${this.adRequestQueue.length} remaining in queue)`);\n      }\n\n      try {\n        await this.playSingleAd(nextAdUrl);\n        this.timing.consecutiveFailures = 0;\n      } catch (error) {\n        const errorMessage = (error as Error).message;\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Ad request failed:\", errorMessage);\n\n        if (errorMessage.includes(\"ad_duration_exceeds_budget\")) {\n          await this.tryNextAvailableAdWithRateLimit();\n          return;\n        }\n\n        if (!errorMessage.includes(\"permanently failed\") && !errorMessage.includes(\"in cooldown\")) {\n          this.timing.consecutiveFailures++;\n        }\n\n        await this.tryNextAvailableAdWithRateLimit();\n      }\n      return;\n    }\n\n    const maxRetries = 3;\n    if (this.continuousFetchingActive && retryCount < maxRetries && remaining > 2000) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Queue empty, waiting for URLs... (${retryCount + 1}/${maxRetries})`);\n      }\n      await new Promise(resolve => setTimeout(resolve, 500));\n      await this.tryNextAvailableAd(retryCount + 1);\n      return;\n    }\n\n    if (!this.isShowingPlaceholder && remaining > 1000) {\n      this.showPlaceholderAndWaitForAds();\n    } else {\n      if (this.debug) console.log(\"[CONTINUOUS-FETCH] No more ads available, ending ad break\");\n      this.handleAdPodComplete();\n    }\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Single ad playback\n  // ───────────────────────────────────────────────────────────────\n\n  async playSingleAd(vastTagUrl: string): Promise<void> {\n    const adPlayer = this.host.getAdPlayer();\n    if (adPlayer.isAdPlaying()) return;\n\n    if (this.timing.failedVastUrls.has(vastTagUrl)) {\n      if (this.debug) console.warn(\"[AD-ERROR] Skipping permanently-failed VAST URL:\", vastTagUrl);\n      throw new Error(\"VAST URL permanently failed\");\n    }\n\n    if (this.timing.isUrlInCooldown(vastTagUrl)) {\n      if (this.debug) console.warn(\"[AD-ERROR] Skipping URL in cooldown (recent no-fill):\", vastTagUrl);\n      throw new Error(\"VAST URL in cooldown after no-fill\");\n    }\n\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.warn(\"[AD-ERROR] Too many consecutive failures, not requesting more ads\");\n      throw new Error(\"Too many consecutive failures\");\n    }\n\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) console.warn(\"[CIRCUIT-BREAKER] GAM in cooldown, skipping ad request\");\n      throw new Error(\"GAM in cooldown\");\n    }\n\n    this.recreateAdController();\n\n    const requestToken = ++this.timing.adRequestTokenCounter;\n    this.timing.activeAdRequestToken = requestToken;\n    this.timing.startAdRequestWatchdog(requestToken);\n\n    try {\n      await this.timing.enforceGlobalRateLimit();\n      this.timing.lastAdRequestTime = Date.now();\n      const ap = this.host.getAdPlayer();\n      await ap.requestAds(vastTagUrl);\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: vastTagUrl, timestamp: new Date().toISOString() },\n        this.analyticsContext\n      ).catch(() => {});\n\n      this.timing.clearAdRequestWatchdog();\n\n      if (this.timing.activeAdRequestToken !== requestToken) return;\n\n      if (!this.timing.ensureLoadedAdFitsBudget(ap)) {\n        this.timing.rejectLoadedAdForDuration(ap);\n        this.recreateAdController();\n        throw new Error(\"ad_duration_exceeds_budget\");\n      }\n\n      try {\n        this.timing.startAdFailsafeTimer(requestToken);\n        await ap.play();\n        this.timing.markAdStarted();\n\n        if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n          this.timing.scheduleAdStopAtBreakBoundary();\n        }\n\n        ap.setAdVolume(getAdAudioVolume(ap));\n        this.timing.consecutiveFailures = 0;\n        this.timing.temporaryFailureUrls.delete(vastTagUrl);\n      } catch (playError) {\n        if (this.debug) console.error(\"[AD-ERROR] Failed to play ad:\", playError);\n\n        if (this.timing.isTemporaryAdError(playError as Error)) {\n          this.timing.temporaryFailureUrls.set(vastTagUrl, Date.now());\n          if (this.debug) console.log(\"[AD-ERROR] Temporary play error - URL can be retried after cooldown\");\n        } else {\n          this.timing.failedVastUrls.add(vastTagUrl);\n          if (this.debug) console.log(\"[AD-ERROR] Permanent play error - URL blacklisted\");\n        }\n\n        this.timing.clearAdFailsafeTimer();\n        if (this.timing.activeAdRequestToken === requestToken) {\n          this.timing.activeAdRequestToken = null;\n        }\n        throw playError;\n      }\n    } catch (error) {\n      const errorMessage = (error as Error)?.message || \"\";\n      if (this.debug) console.error(\"[AD-ERROR] Ad request failed:\", errorMessage);\n\n      if (this.timing.isTemporaryAdError(error as Error)) {\n        this.timing.temporaryFailureUrls.set(vastTagUrl, Date.now());\n        if (this.debug) console.log(\"[AD-ERROR] Temporary error (no-fill/timeout) - URL can be retried after cooldown\");\n      } else {\n        this.timing.failedVastUrls.add(vastTagUrl);\n        if (this.debug) console.log(\"[AD-ERROR] Permanent error - URL blacklisted:\", errorMessage);\n      }\n\n      this.timing.clearAdRequestWatchdog();\n      this.timing.clearAdFailsafeTimer();\n      if (this.timing.activeAdRequestToken === requestToken) {\n        this.timing.activeAdRequestToken = null;\n      }\n      throw error;\n    }\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Placeholder wait\n  // ───────────────────────────────────────────────────────────────\n\n  private async showPlaceholderAndWaitForAds(): Promise<void> {\n    const remaining = this.timing.getRemainingAdMs();\n    const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);\n\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, skipping placeholder wait\");\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping placeholder - too many consecutive failures\");\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (waitTime < 1000) {\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (this.debug) {\n      console.log(`[CONTINUOUS-FETCH] Showing placeholder for ${waitTime}ms while waiting for ad URLs`);\n    }\n\n    this.isShowingPlaceholder = true;\n    this.host.getAdPlayer().showPlaceholder();\n\n    const checkInterval = 300;\n    const maxChecks = Math.floor(waitTime / checkInterval);\n\n    for (let i = 0; i < maxChecks; i++) {\n      await new Promise(resolve => setTimeout(resolve, checkInterval));\n\n      if (!this.inAdBreak) return;\n\n      if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Too many failures during placeholder wait\");\n        break;\n      }\n\n      if (!this.timing.hasTimeToStartAnotherAd()) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Not enough time remaining during placeholder wait, ending ad break\");\n        break;\n      }\n\n      while (this.adRequestQueue.length > 0) {\n        if (!this.timing.hasTimeToStartAnotherAd()) break;\n\n        const nextAdUrl = this.adRequestQueue.shift();\n        if (!nextAdUrl) continue;\n        if (this.timing.failedVastUrls.has(nextAdUrl)) continue;\n        if (this.timing.isUrlInCooldown(nextAdUrl)) continue;\n\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Ad URL available, requesting via HLS ad player\");\n\n        this.isShowingPlaceholder = false;\n        this.host.getAdPlayer().hidePlaceholder();\n\n        try {\n          await this.playSingleAd(nextAdUrl);\n          this.timing.consecutiveFailures = 0;\n        } catch (error) {\n          const errorMessage = (error as Error).message;\n          if (errorMessage.includes(\"ad_duration_exceeds_budget\")) {\n            await this.tryNextAvailableAdWithRateLimit();\n            return;\n          }\n          if (!errorMessage.includes(\"permanently failed\") && !errorMessage.includes(\"in cooldown\")) {\n            this.timing.consecutiveFailures++;\n          }\n          await this.tryNextAvailableAdWithRateLimit();\n        }\n        return;\n      }\n    }\n\n    if (this.debug) console.log(\"[CONTINUOUS-FETCH] Placeholder timeout, ending ad break\");\n\n    this.isShowingPlaceholder = false;\n    this.host.getAdPlayer().hidePlaceholder();\n    this.handleAdPodComplete();\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Ad break completion\n  // ───────────────────────────────────────────────────────────────\n\n  handleAdPodComplete(): void {\n    if (this.debug) {\n      console.log(\"[StormcloudVideoPlayer] Ad pod complete - cleaning up\");\n    }\n\n    this.timing.resetForBreakEnd();\n\n    this.stopContinuousFetching();\n    this.clearPendingAdBreak();\n\n    if (this.isShowingPlaceholder) {\n      this.host.getAdPlayer().hidePlaceholder();\n      this.isShowingPlaceholder = false;\n    }\n\n    this.adRequestQueue = [];\n\n    this.inAdBreak = false;\n    this.adDetectSentForCurrentBreak = false;\n    this.cueManager.activeScte35BreakKey = undefined;\n    this.cueManager.scheduledScte35BreakKey = undefined;\n    this.adPodQueue = [];\n    this.showAds = false;\n\n    this.host.getAdPlayer().stop().catch(() => {});\n\n    const restoredMuted = this.host.getAdPlayer().getOriginalMutedState();\n    const restoredVolume = this.host.getAdPlayer().getOriginalVolume();\n\n    if (this.host.video.muted !== restoredMuted) {\n      this.host.video.muted = restoredMuted;\n    }\n    if (Math.abs(this.host.video.volume - restoredVolume) > 0.01) {\n      this.host.video.volume = restoredVolume;\n    }\n\n    this.host.video.style.visibility = \"visible\";\n    this.host.video.style.opacity = \"1\";\n\n    if (this.host.isLiveStream()) {\n      const liveSyncPos = this.host.getLiveSyncPosition();\n      if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {\n        if (this.debug) {\n          console.log(\n            `[StormcloudVideoPlayer] Live break end — snapping to live edge: ${this.host.video.currentTime.toFixed(2)}s → ${liveSyncPos.toFixed(2)}s`\n          );\n        }\n        this.host.video.currentTime = liveSyncPos;\n        this.host.restartHlsLoad(liveSyncPos);\n      }\n      this.resumeContentPlayback();\n      this.monitorLiveResumeStall(this.host.video.currentTime);\n      return;\n    }\n\n    this.resumeContentPlayback();\n  }\n\n  private getBufferedAhead(): number {\n    const video = this.host.video;\n    const t = video.currentTime;\n    const ranges = video.buffered;\n    for (let i = 0; i < ranges.length; i++) {\n      if (t >= ranges.start(i) - 0.5 && t <= ranges.end(i)) {\n        return ranges.end(i) - t;\n      }\n    }\n    return 0;\n  }\n\n  private monitorLiveResumeStall(resumeBaseline: number): void {\n    const pollIntervalMs = 1000;\n    const graceMs = 5000;\n    const recoveryCooldownMs = 5000;\n    const maxRecoveries = 2;\n    const maxMonitorMs = 30000;\n\n    const startTime = Date.now();\n    let baseline = resumeBaseline;\n    let recoveries = 0;\n    let lastRecoveryAt = 0;\n\n    const poll = (): void => {\n      if (this.inAdBreak) {\n        return;\n      }\n\n      const video = this.host.video;\n      const currentTime = video.currentTime;\n      const advanced = currentTime > baseline + 0.5;\n      const playing = !video.paused && video.readyState >= 3;\n\n      if (advanced && playing) {\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Live stream resumed successfully after ad break\");\n        }\n        return;\n      }\n\n      if (video.paused) {\n        video.play()?.catch(() => {});\n      }\n\n      const elapsed = Date.now() - startTime;\n      const bufferedAhead = this.getBufferedAhead();\n      const wedged = bufferedAhead < 0.5 && !advanced;\n\n      if (elapsed >= maxMonitorMs) {\n        if (this.debug && !advanced) {\n          console.warn(\n            `[StormcloudVideoPlayer] Live stream failed to resume after ${(maxMonitorMs / 1000).toFixed(0)}s (t=${currentTime.toFixed(2)}s, buffered=${bufferedAhead.toFixed(2)}s)`\n          );\n        }\n        return;\n      }\n\n      const canRecover =\n        wedged &&\n        elapsed >= graceMs &&\n        Date.now() - lastRecoveryAt >= recoveryCooldownMs &&\n        recoveries < maxRecoveries;\n\n      if (canRecover) {\n        recoveries++;\n        lastRecoveryAt = Date.now();\n\n        const isFinalAttempt = recoveries >= maxRecoveries;\n\n        if (!isFinalAttempt) {\n          const liveSyncPos = this.host.getLiveSyncPosition();\n          const reloadPos =\n            liveSyncPos != null && liveSyncPos > currentTime ? liveSyncPos : currentTime;\n\n          if (this.debug) {\n            console.warn(\n              `[StormcloudVideoPlayer] Content wedged after ad break (t=${currentTime.toFixed(2)}s, buffered=${bufferedAhead.toFixed(2)}s) — restarting HLS load at ${reloadPos.toFixed(2)}s (recovery ${recoveries}/${maxRecoveries})`\n            );\n          }\n\n          if (liveSyncPos != null && liveSyncPos > currentTime) {\n            video.currentTime = liveSyncPos;\n          }\n          this.host.restartHlsLoad(reloadPos);\n        } else {\n          if (this.debug) {\n            console.warn(\n              `[StormcloudVideoPlayer] Content still wedged after ad break (t=${currentTime.toFixed(2)}s, buffered=${bufferedAhead.toFixed(2)}s) — hard-reloading live stream (recovery ${recoveries}/${maxRecoveries})`\n            );\n          }\n          this.host.reloadLiveStream();\n        }\n\n        baseline = video.currentTime;\n        if (video.paused) {\n          video.play()?.catch(() => {});\n        }\n      }\n\n      window.setTimeout(poll, pollIntervalMs);\n    };\n\n    window.setTimeout(poll, pollIntervalMs);\n  }\n\n  private resumeContentPlayback(attempt: number = 0): void {\n    const maxAttempts = 3;\n\n    if (!this.host.video.paused) {\n      if (this.debug && attempt === 0) {\n        console.log(\"[StormcloudVideoPlayer] Content video already playing after ads\");\n      }\n      return;\n    }\n\n    if (this.debug) {\n      console.log(\n        `[StormcloudVideoPlayer] Resuming content playback after ads (attempt ${attempt + 1}/${maxAttempts})`\n      );\n    }\n\n    const playResult = this.host.video.play();\n    if (playResult && typeof playResult.catch === \"function\") {\n      playResult.catch((error) => {\n        if (attempt + 1 >= maxAttempts) {\n          if (this.debug) {\n            console.warn(\n              \"[StormcloudVideoPlayer] Failed to resume content playback after ads:\",\n              error\n            );\n          }\n          return;\n        }\n        const backoffMs = 250 * (attempt + 1);\n        window.setTimeout(() => {\n          this.resumeContentPlayback(attempt + 1);\n        }, backoffMs);\n      });\n    }\n  }\n\n  handleAdFailure(): void {\n    this.timing.consecutiveFailures++;\n    this.timing.recordGamNoFill();\n    const remaining = this.timing.getRemainingAdMs();\n\n    if (this.debug) {\n      console.log(\n        `[CONTINUOUS-FETCH] Ad failure: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}, consecutiveFailures=${this.timing.consecutiveFailures}`,\n        this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n      );\n    }\n\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.log(`[CONTINUOUS-FETCH] Max consecutive failures reached (${this.timing.consecutiveFailures}), ending ad break gracefully`);\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (this.timing.hasTimeToStartAnotherAd()) {\n      this.tryNextAvailableAdWithRateLimit();\n    } else {\n      if (this.debug) console.log(\"[AD-ERROR] Ad failed and not enough time remaining for another ad. Failed URLs:\", this.timing.failedVastUrls.size);\n      this.handleAdPodComplete();\n    }\n  }\n}\n","export const MQTT_CA_CERT_FILE = \"src/certs/emqxsl-ca.crt\";\n\nexport type MQTTConfig = {\n  enabled: boolean;\n  brokerAddress: string;\n  brokerPort: number;\n  wsPort: number;\n  username: string;\n  password: string;\n  topicPrefix: string;\n  qos: 0 | 1 | 2;\n  brokerUrl?: string;\n};\n\nexport const DEFAULT_MQTT_CONFIG: MQTTConfig = {\n  enabled: true,\n  brokerAddress: \"vecbae77.ala.us-east-1.emqxsl.com\",\n  brokerPort: 8883,\n  wsPort: 8084,\n  username: \"for-sonifi\",\n  password: \"sonifi-mqtt\",\n  topicPrefix: \"adstorm/players\",\n  qos: 1,\n};\n\nexport const mqttConfig: MQTTConfig = { ...DEFAULT_MQTT_CONFIG };\n\nexport function applyMQTTConfig(overrides: Partial<MQTTConfig>): void {\n  Object.assign(mqttConfig, overrides);\n}\n\nexport function isMQTTEnabled(): boolean {\n  return mqttConfig.enabled;\n}\n\nexport function buildMQTTBrokerUrl(): string {\n  if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;\n  return `wss://${mqttConfig.brokerAddress}:${mqttConfig.wsPort}/mqtt`;\n}\n\nexport function buildPlayerTopic(\n  licenseKey: string,\n  channel: \"metrics\" | \"impressions\" | \"heartbeat\"\n): string {\n  return `${mqttConfig.topicPrefix}/${licenseKey}/${channel}`;\n}\n","import mqtt from \"mqtt\";\nimport type { MqttClient } from \"mqtt\";\nimport {\n  applyMQTTConfig,\n  buildMQTTBrokerUrl,\n  isMQTTEnabled,\n  mqttConfig,\n} from \"./mqttConfig\";\nimport type { MQTTConfig } from \"./mqttConfig\";\n\nconst LOG = \"[StormcloudVideoPlayer][MQTT]\";\n\nconst KEEPALIVE_SECONDS = 30;\nconst CONNECT_TIMEOUT_MS = 10_000;\n\nconst RECONNECT_BASE_MS = 5_000;\nconst RECONNECT_MAX_MS = 60_000;\nconst MAX_RECONNECT_ATTEMPTS = 8;\nconst COOLDOWN_MS = 10 * 60_000;\n\nexport type MQTTStatus = \"disconnected\" | \"connecting\" | \"connected\" | \"error\";\n\nlet client: MqttClient | null = null;\nlet status: MQTTStatus = \"disconnected\";\nlet reconnectAttempts = 0;\nlet reconnectTimer: ReturnType<typeof setTimeout> | null = null;\nlet stopped = false;\n\nexport function getMQTTStatus(): MQTTStatus {\n  return status;\n}\n\nexport function isMQTTConnected(): boolean {\n  return status === \"connected\" && client !== null && client.connected;\n}\n\nexport function isMQTTConfigured(): boolean {\n  return isMQTTEnabled();\n}\n\nexport function configureMQTT(overrides: Partial<MQTTConfig>): void {\n  applyMQTTConfig(overrides);\n  disconnectMQTT();\n}\n\nfunction clearReconnectTimer(): void {\n  if (reconnectTimer) {\n    clearTimeout(reconnectTimer);\n    reconnectTimer = null;\n  }\n}\n\nfunction teardownClient(): void {\n  if (client) {\n    try {\n      client.removeAllListeners();\n      client.end(true);\n    } catch {\n      // ignore teardown errors\n    }\n    client = null;\n  }\n}\n\nfunction scheduleReconnect(): void {\n  if (stopped || !isMQTTEnabled() || reconnectTimer) return;\n\n  if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {\n    console.warn(\n      `${LOG} giving up after ${reconnectAttempts} attempts; cooling down for ${\n        COOLDOWN_MS / 60_000\n      }m`\n    );\n    teardownClient();\n    status = \"disconnected\";\n    reconnectTimer = setTimeout(() => {\n      reconnectTimer = null;\n      reconnectAttempts = 0;\n      openConnection();\n    }, COOLDOWN_MS);\n    return;\n  }\n\n  const delay = Math.min(\n    RECONNECT_MAX_MS,\n    RECONNECT_BASE_MS * 2 ** reconnectAttempts\n  );\n  const jitter = Math.floor(Math.random() * 1_000);\n  reconnectAttempts += 1;\n\n  reconnectTimer = setTimeout(() => {\n    reconnectTimer = null;\n    openConnection();\n  }, delay + jitter);\n}\n\nfunction openConnection(): void {\n  if (stopped || !isMQTTEnabled()) return;\n\n  teardownClient();\n\n  const url = buildMQTTBrokerUrl();\n  status = \"connecting\";\n\n  const clientId = `stormcloud-vp-${Math.random().toString(36).slice(2, 9)}`;\n\n  try {\n    client = mqtt.connect(url, {\n      clientId,\n      username: mqttConfig.username,\n      password: mqttConfig.password,\n      keepalive: KEEPALIVE_SECONDS,\n      clean: true,\n      // Disable MQTT.js internal reconnect; we manage reconnection ourselves.\n      reconnectPeriod: 0,\n      connectTimeout: CONNECT_TIMEOUT_MS,\n      queueQoSZero: false,\n      reschedulePings: true,\n      // Root-cause fix for the Tizen 4.0 \"Keepalive timeout\" loop: MQTT.js's\n      // default timerVariant \"auto\" runs the keepalive interval inside a\n      // Blob-URL Web Worker (via worker-timers). On Samsung Tizen 4.0 (old\n      // WebKit) served from a file:// origin that worker can't tick reliably,\n      // so the keepalive counter trips onKeepaliveTimeout almost immediately\n      // after connect. Force the native setInterval timer instead; the main\n      // thread is free because video is decoded by the native AVPlay pipeline.\n      timerVariant: \"native\",\n    });\n  } catch (err) {\n    status = \"error\";\n    console.warn(`${LOG} connect() threw:`, err);\n    scheduleReconnect();\n    return;\n  }\n\n  client.on(\"connect\", () => {\n    status = \"connected\";\n    reconnectAttempts = 0;\n    clearReconnectTimer();\n    console.info(`${LOG} connected to ${url}`);\n  });\n\n  client.on(\"offline\", () => {\n    if (status === \"connected\") status = \"disconnected\";\n    console.warn(`${LOG} offline`);\n    scheduleReconnect();\n  });\n\n  client.on(\"error\", (err) => {\n    status = \"error\";\n    console.warn(`${LOG} error:`, err.message);\n    scheduleReconnect();\n  });\n\n  client.on(\"close\", () => {\n    if (status === \"connected\") status = \"disconnected\";\n    scheduleReconnect();\n  });\n}\n\nexport function initMQTTClient(): void {\n  if (client || !isMQTTEnabled()) return;\n  stopped = false;\n  reconnectAttempts = 0;\n  openConnection();\n}\n\nexport function ensureMQTTClient(): void {\n  if (isMQTTEnabled() && !client && !reconnectTimer) {\n    initMQTTClient();\n  }\n}\n\nexport function publishMQTT(\n  topic: string,\n  payload: Record<string, unknown>\n): boolean {\n  if (!isMQTTEnabled()) {\n    return false;\n  }\n\n  ensureMQTTClient();\n\n  if (!client || !client.connected) {\n    return false;\n  }\n\n  try {\n    client.publish(topic, JSON.stringify(payload), { qos: mqttConfig.qos });\n    return true;\n  } catch (err) {\n    console.warn(`${LOG} publish failed on ${topic}:`, err);\n    return false;\n  }\n}\n\nexport function disconnectMQTT(): void {\n  stopped = true;\n  clearReconnectTimer();\n  teardownClient();\n  reconnectAttempts = 0;\n  status = \"disconnected\";\n}\n","export interface VastEnvironmentSignals {\n  isCtv: boolean;\n  contentUrl?: string;\n  appId?: string;\n  appName?: string;\n  sessionId?: string;\n  deviceId?: string;\n  deviceIdType?: string;\n  limitAdTracking?: boolean;\n  deviceTypeHint?: number;\n}\n\nconst AIRY_ANDROID_APP_ID = \"com.freeairytv.android\";\nconst AIRY_WEBOS_APP_ID    = \"com.freeairytv.webos\";\nconst AIRY_TIZEN_APP_ID    = \"com.freeairytv.samsung\";\nconst AIRY_ROKU_APP_ID     = \"com.freeairytv.roku\";\nconst AIRY_APP_NAME = \"AiryTV Movies & TV\";\nconst AIRY_DEFAULT_CONTENT_URL = \"https://live.airy.tv\";\nconst CTV_SESSION_STORAGE_KEY = \"stormcloud.ctv.sid\";\nexport const PLAYER_ID_STORAGE_KEY = \"stormcloud.player.id\";\n\nconst LG_TEST_APP_ID      = \"com.hulu.plus\";\nconst LG_TEST_APP_NAME    = \"Hulu\";\nconst LG_TEST_CONTENT_URL = \"https://www.hulu.com/live-tv\";\n\nlet _lgTestDeviceId: string | undefined;\nfunction getLgTestDeviceId(): string {\n  if (!_lgTestDeviceId) _lgTestDeviceId = createUuid();\n  return _lgTestDeviceId;\n}\n\nfunction isLgWebOS(): boolean {\n  if (typeof navigator === \"undefined\") return false;\n  return /Web0S|webOS|LG Browser|LGSTB|LGE/i.test(navigator.userAgent);\n}\n\nfunction getDefaultAppId(): string {\n  if (typeof navigator === \"undefined\") return AIRY_ANDROID_APP_ID;\n  const ua = navigator.userAgent;\n  if (/Web0S|webOS|LG Browser|LGSTB|LGE/i.test(ua)) return AIRY_WEBOS_APP_ID;\n  if (/Tizen|Samsung/i.test(ua))                       return AIRY_TIZEN_APP_ID;\n  if (/Roku/i.test(ua))                                return AIRY_ROKU_APP_ID;\n  return AIRY_ANDROID_APP_ID;\n}\n\nconst DEVICE_ID_STORAGE_KEYS = [\n  \"rdid\",\n  \"ifa\",\n  \"advertisingId\",\n  \"advertising_id\",\n  \"deviceAdvertisingId\",\n  \"aaid\",\n  \"adid\",\n  \"rida\",\n  \"tifa\",\n  \"lgudid\",\n];\n\nconst ANDROID_ID_TYPES = new Set([\"aaid\", \"adid\"]);\n\nexport function resolveVastEnvironmentSignals(isCtv: boolean): VastEnvironmentSignals {\n  const bridgeSignals  = readNativeBridgeSignals();\n  const platformDevice = readPlatformNativeDeviceId();\n  const lg = isCtv && isLgWebOS();\n\n  const deviceId =\n    bridgeSignals.deviceId  ||\n    readStoredDeviceId()    ||\n    platformDevice.deviceId ||\n    (lg && platformDevice.limitAdTracking !== true ? getLgTestDeviceId() : undefined);\n  const deviceIdType =\n    bridgeSignals.deviceIdType  ||\n    readStoredString(\"deviceIdType\") ||\n    readStoredString(\"idtype\") ||\n    platformDevice.deviceIdType ||\n    inferDeviceIdType() ||\n    (lg ? \"lgudid\" : undefined);\n  const platformLat = bridgeSignals.deviceId\n    ? bridgeSignals.limitAdTracking\n    : platformDevice.limitAdTracking;\n  const limitAdTracking =\n    deviceId != null\n      ? platformLat ?? readStoredLimitAdTracking() ?? false\n      : undefined;\n\n  if (isCtv) {\n    return {\n      isCtv: true,\n      contentUrl:\n        bridgeSignals.contentUrl ||\n        readStoredString(\"contentUrl\") ||\n        (lg ? LG_TEST_CONTENT_URL : AIRY_DEFAULT_CONTENT_URL),\n      appId:\n        bridgeSignals.appId ||\n        readStoredString(\"appId\") ||\n        readStoredString(\"msid\") ||\n        (lg ? LG_TEST_APP_ID : getDefaultAppId()),\n      appName:\n        bridgeSignals.appName ||\n        readStoredString(\"appName\") ||\n        readStoredString(\"an\") ||\n        (lg ? LG_TEST_APP_NAME : AIRY_APP_NAME),\n      sessionId: getOrCreateCtvSessionId(),\n      ...(deviceId ? { deviceId } : {}),\n      ...(deviceIdType ? { deviceIdType } : {}),\n      ...(limitAdTracking != null ? { limitAdTracking } : {}),\n      deviceTypeHint: 5,\n    };\n  }\n\n  return {\n    isCtv: false,\n    ...(deviceId ? { deviceId } : {}),\n    ...(deviceIdType ? { deviceIdType } : {}),\n    ...(limitAdTracking != null ? { limitAdTracking } : {}),\n  };\n}\n\nexport function inferDeviceIdType(): string | undefined {\n  if (typeof navigator === \"undefined\") {\n    return undefined;\n  }\n\n  const ua = navigator.userAgent;\n  if (/Roku/i.test(ua)) return \"rida\";\n  if (/Tizen|Samsung/i.test(ua)) return \"tifa\";\n  if (/AppleTV|Apple TV/i.test(ua)) return \"tvOS\";\n  if (/AFT|Fire TV|Amazon/i.test(ua)) return \"afai\";\n  if (/Web0S|webOS|LG Browser|LGSTB|LGE/i.test(ua)) return \"lgudid\";\n  if (/Android|Google TV/i.test(ua)) return \"aaid\";\n  if (/iPhone|iPad|iPod/i.test(ua)) return \"idfa\";\n  return undefined;\n}\n\nexport function isEquivalentDeviceIdType(\n  tagType: string | undefined,\n  runtimeType: string | undefined\n): boolean {\n  if (!tagType || !runtimeType) return false;\n  const tag = tagType.toLowerCase();\n  const runtime = runtimeType.toLowerCase();\n  if (tag === runtime) return true;\n  return ANDROID_ID_TYPES.has(tag) && ANDROID_ID_TYPES.has(runtime);\n}\n\nexport function resolveNativeDeviceId(): string | undefined {\n  const bridgeSignals = readNativeBridgeSignals();\n  const platformDevice = readPlatformNativeDeviceId();\n  const deviceId =\n    bridgeSignals.deviceId ||\n    readStoredDeviceId() ||\n    platformDevice.deviceId;\n\n  if (!deviceId || isMacroPlaceholder(deviceId) || isZeroedAdId(deviceId)) {\n    return undefined;\n  }\n\n  return deviceId;\n}\n\nexport function getOrCreateStoredUuid(storageKey: string): string | undefined {\n  if (typeof window === \"undefined\") {\n    return undefined;\n  }\n\n  const existing = readPersistentId(storageKey);\n  if (existing) {\n    writePersistentId(storageKey, existing);\n    return existing;\n  }\n\n  const id = createUuid();\n  writePersistentId(storageKey, id);\n  return id;\n}\n\nfunction getOrCreateCtvSessionId(): string {\n  const existing = readPersistentId(CTV_SESSION_STORAGE_KEY);\n  if (existing) {\n    writePersistentId(CTV_SESSION_STORAGE_KEY, existing);\n    return existing;\n  }\n\n  const sessionId = createUuid();\n  writePersistentId(CTV_SESSION_STORAGE_KEY, sessionId);\n  return sessionId;\n}\n\nfunction readPersistentId(storageKey: string): string | undefined {\n  return readStoredString(storageKey) || readCookie(storageKey);\n}\n\nfunction writePersistentId(storageKey: string, value: string): void {\n  try {\n    window.localStorage?.setItem(storageKey, value);\n  } catch {}\n  writeCookie(storageKey, value);\n}\n\nconst PERSISTENT_ID_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5; // ~5 years\n\nfunction readCookie(name: string): string | undefined {\n  if (typeof document === \"undefined\") {\n    return undefined;\n  }\n\n  try {\n    const prefix = encodeURIComponent(name) + \"=\";\n    const parts = document.cookie ? document.cookie.split(\";\") : [];\n    for (const part of parts) {\n      const entry = part.trim();\n      if (entry.startsWith(prefix)) {\n        return decodeURIComponent(entry.slice(prefix.length)) || undefined;\n      }\n    }\n  } catch {}\n\n  return undefined;\n}\n\nfunction writeCookie(name: string, value: string): void {\n  if (typeof document === \"undefined\") {\n    return;\n  }\n\n  try {\n    document.cookie =\n      encodeURIComponent(name) +\n      \"=\" +\n      encodeURIComponent(value) +\n      \";path=/;max-age=\" +\n      PERSISTENT_ID_COOKIE_MAX_AGE +\n      \";SameSite=Lax\";\n  } catch {}\n}\n\nfunction createUuid(): string {\n  if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n    return crypto.randomUUID();\n  }\n\n  const bytes = new Uint8Array(16);\n  if (typeof crypto !== \"undefined\" && typeof crypto.getRandomValues === \"function\") {\n    crypto.getRandomValues(bytes);\n  } else {\n    let seed = Date.now() ^ Math.floor(performanceNow() * 1e3);\n    for (let i = 0; i < 16; i++) {\n      seed = (seed * 1103515245 + 12345) & 0x7fffffff;\n      bytes[i] = (seed ^ Math.floor(Math.random() * 256)) & 0xff;\n    }\n  }\n\n  bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;\n  bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;\n\n  let hex = \"\";\n  for (let i = 0; i < 16; i++) {\n    hex += (bytes[i] ?? 0).toString(16).padStart(2, \"0\");\n  }\n\n  return (\n    hex.slice(0, 8) +\n    \"-\" +\n    hex.slice(8, 12) +\n    \"-\" +\n    hex.slice(12, 16) +\n    \"-\" +\n    hex.slice(16, 20) +\n    \"-\" +\n    hex.slice(20, 32)\n  );\n}\n\nfunction performanceNow(): number {\n  try {\n    if (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n      return performance.now();\n    }\n  } catch {}\n  return 0;\n}\n\nfunction readPlatformNativeDeviceId(): {\n  deviceId?: string;\n  deviceIdType?: string;\n  limitAdTracking?: boolean;\n} {\n  if (typeof window === \"undefined\") return {};\n\n  try {\n    const adinfo = (window as any).webapis?.adinfo;\n    if (adinfo) {\n      const tifa: unknown =\n        typeof adinfo.getTIFA === \"function\" ? adinfo.getTIFA() : adinfo.tifa;\n      if (typeof tifa === \"string\" && tifa && !isMacroPlaceholder(tifa)) {\n        const lat: unknown =\n          typeof adinfo.isLATEnabled === \"function\"\n            ? adinfo.isLATEnabled()\n            : undefined;\n        return {\n          deviceId: tifa,\n          deviceIdType: \"tifa\",\n          ...(typeof lat === \"boolean\" ? { limitAdTracking: lat } : {}),\n        };\n      }\n    }\n  } catch {}\n\n  try {\n    const webOSDev = (window as any).webOSDev;\n    if (webOSDev) {\n      const lgudid: unknown =\n        typeof webOSDev.LGUDID === \"function\"\n          ? webOSDev.LGUDID()\n          : webOSDev.lgudid ?? webOSDev.LGUDID;\n      if (typeof lgudid === \"string\" && lgudid) {\n        if (isZeroedAdId(lgudid)) {\n          return { deviceIdType: \"lgudid\", limitAdTracking: true };\n        }\n        if (!isMacroPlaceholder(lgudid)) {\n          return { deviceId: lgudid, deviceIdType: \"lgudid\", limitAdTracking: false };\n        }\n      }\n    }\n  } catch {}\n\n  try {\n    const roku = (window as any).Roku;\n    if (roku) {\n      const rida: unknown =\n        typeof roku.getPublisherUniqueId === \"function\"\n          ? roku.getPublisherUniqueId()\n          : roku.rida;\n      if (typeof rida === \"string\" && rida && !isMacroPlaceholder(rida)) {\n        return { deviceId: rida, deviceIdType: \"rida\", limitAdTracking: false };\n      }\n    }\n  } catch {}\n\n  return {};\n}\n\nconst ZEROED_AD_ID_PATTERNS: ReadonlySet<string> = new Set([\n  \"38400000-8cf0-11bd-b23e-10b96e40000d\",\n  \"00000000-0000-0000-0000-000000000000\",\n]);\n\nfunction isMacroPlaceholder(value: string): boolean {\n  const t = value.trim();\n  return !t ||\n    /^(\\[[^\\]]*\\]|\\{[^}]*\\}|%%[^%]*%%)$/.test(t) ||\n    /^(unknown|null|undefined|none|n\\/a|\\$\\{[^}]+\\})$/i.test(t) ||\n    ZEROED_AD_ID_PATTERNS.has(t.toLowerCase());\n}\n\nfunction isZeroedAdId(value: string): boolean {\n  return ZEROED_AD_ID_PATTERNS.has(value.trim().toLowerCase());\n}\n\nfunction readNativeBridgeSignals(): {\n  contentUrl?: string;\n  appId?: string;\n  appName?: string;\n  deviceId?: string;\n  deviceIdType?: string;\n  limitAdTracking?: boolean;\n} {\n  if (typeof window === \"undefined\") {\n    return {};\n  }\n\n  const candidates = [\n    (window as any).__STORMCLOUD_CTV_AD_INFO__,\n    (window as any).__STORMCLOUD_CTV__,\n    (window as any).stormcloudCtv,\n    (window as any).AiryTV,\n    (window as any).Android,\n    (window as any).webOS,\n    (window as any).tizen,\n    (window as any).amazon,\n  ].filter(Boolean);\n\n  for (const source of candidates) {\n    const contentUrl =\n      readBridgeValue(source, [\"contentUrl\", \"url\", \"videoUrl\", \"canonicalUrl\"]) ||\n      undefined;\n    const appId =\n      readBridgeValue(source, [\"appId\", \"msid\", \"applicationId\", \"packageName\"]) ||\n      undefined;\n    const appName =\n      readBridgeValue(source, [\"appName\", \"an\", \"applicationName\"]) || undefined;\n    const deviceId =\n      readBridgeValue(source, [\n        \"rdid\",\n        \"ifa\",\n        \"advertisingId\",\n        \"adId\",\n        \"deviceId\",\n        \"lgudid\",\n        \"LGUDID\",\n        \"tifa\",\n        \"rida\",\n        \"afai\",\n        \"aaid\",\n      ]) || undefined;\n\n    if (!contentUrl && !deviceId && !appId && !appName) continue;\n\n    const deviceIdType =\n      readBridgeValue(source, [\n        \"idtype\",\n        \"deviceIdType\",\n        \"advertisingIdType\",\n        \"idType\",\n      ]) || inferDeviceIdType();\n    const limitAdTracking = readBridgeBoolean(source, [\n      \"is_lat\",\n      \"limitAdTracking\",\n      \"limitedAdTracking\",\n      \"lat\",\n    ]);\n\n    return {\n      ...(contentUrl ? { contentUrl } : {}),\n      ...(appId ? { appId } : {}),\n      ...(appName ? { appName } : {}),\n      ...(deviceId ? { deviceId } : {}),\n      ...(deviceId && deviceIdType ? { deviceIdType } : {}),\n      ...(deviceId && limitAdTracking != null ? { limitAdTracking } : {}),\n    };\n  }\n\n  return {};\n}\n\nfunction readBridgeValue(source: any, keys: string[]): string | undefined {\n  for (const key of keys) {\n    const value = source?.[key];\n    if (typeof value === \"string\" && value) {\n      return value;\n    }\n    if (typeof value === \"function\") {\n      try {\n        const result = value.call(source);\n        if (typeof result === \"string\" && result) {\n          return result;\n        }\n      } catch {}\n    }\n  }\n  return undefined;\n}\n\nfunction readBridgeBoolean(source: any, keys: string[]): boolean | undefined {\n  for (const key of keys) {\n    const value = source?.[key];\n    const normalized = normalizeBoolean(value);\n    if (normalized != null) {\n      return normalized;\n    }\n    if (typeof value === \"function\") {\n      try {\n        const result = normalizeBoolean(value.call(source));\n        if (result != null) {\n          return result;\n        }\n      } catch {}\n    }\n  }\n  return undefined;\n}\n\nfunction readStoredDeviceId(): string | undefined {\n  for (const key of DEVICE_ID_STORAGE_KEYS) {\n    const value = readStoredString(key);\n    if (value) {\n      return value;\n    }\n  }\n  return undefined;\n}\n\nfunction readStoredString(key: string): string | undefined {\n  if (typeof window === \"undefined\") {\n    return undefined;\n  }\n\n  try {\n    const value = window.localStorage?.getItem(key);\n    return value || undefined;\n  } catch {\n    return undefined;\n  }\n}\n\nfunction readStoredLimitAdTracking(): boolean | undefined {\n  return normalizeBoolean(\n    readStoredString(\"limitAdTracking\") ||\n      readStoredString(\"limitedAdTracking\") ||\n      readStoredString(\"is_lat\")\n  );\n}\n\nfunction normalizeBoolean(value: unknown): boolean | undefined {\n  if (typeof value === \"boolean\") {\n    return value;\n  }\n  if (typeof value === \"number\") {\n    return value === 1;\n  }\n  if (typeof value === \"string\") {\n    const normalized = value.toLowerCase();\n    if (normalized === \"1\" || normalized === \"true\" || normalized === \"yes\") {\n      return true;\n    }\n    if (normalized === \"0\" || normalized === \"false\" || normalized === \"no\") {\n      return false;\n    }\n  }\n  return undefined;\n}\n","import type {\n  ClientInfo,\n  AdDetectInfo,\n  AdLoadedInfo,\n  AdImpressionInfo,\n  PlayerAnalyticsContext,\n  StormcloudVideoPlayerConfig,\n} from \"../types\";\n\nimport { buildPlayerTopic, isMQTTEnabled } from \"./mqttConfig\";\nimport { ensureMQTTClient, publishMQTT } from \"./mqttClient\";\nimport {\n  getOrCreateStoredUuid,\n  PLAYER_ID_STORAGE_KEY,\n  resolveNativeDeviceId,\n} from \"./vastEnvironmentSignals\";\n\nlet cachedBrowserId: string | null = null;\n\nexport function buildAnalyticsContext(\n  config: Pick<StormcloudVideoPlayerConfig, \"debugAdTiming\" | \"isLiveStream\">\n): PlayerAnalyticsContext {\n  const context: PlayerAnalyticsContext = {};\n  if (config.debugAdTiming) {\n    context.debugAdTiming = true;\n  }\n  if (config.isLiveStream !== undefined) {\n    context.inputStreamType = config.isLiveStream ? \"live\" : \"vod\";\n  }\n  return context;\n}\n\nexport function getClientInfo(): ClientInfo {\n  const ua = navigator.userAgent;\n  const platform = navigator.platform;\n  const vendor = navigator.vendor || \"\";\n  const maxTouchPoints = navigator.maxTouchPoints || 0;\n  const memory = (navigator as any).deviceMemory || null;\n  const hardwareConcurrency = navigator.hardwareConcurrency || 1;\n\n  const screenInfo = {\n    width: screen?.width,\n    height: screen?.height,\n    availWidth: screen?.availWidth,\n    availHeight: screen?.availHeight,\n    orientation: (screen?.orientation as any)?.type || \"\",\n    pixelDepth: screen?.pixelDepth,\n  };\n\n  let deviceType: \"tv\" | \"mobile\" | \"tablet\" | \"desktop\" = \"desktop\";\n  let brand = \"Unknown\";\n  let os = \"Unknown\";\n  let model = \"\";\n  let isSmartTV = false;\n  let isAndroid = false;\n  let isWebView = false;\n  let isWebApp = false;\n\n  if (ua.includes(\"Web0S\")) {\n    brand = \"LG\";\n    os = \"webOS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n    const webosMatch = ua.match(/Web0S\\/([^\\s]+)/);\n    model = webosMatch ? `webOS ${webosMatch[1]}` : \"webOS TV\";\n  } else if (ua.includes(\"Tizen\")) {\n    brand = \"Samsung\";\n    os = \"Tizen\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n    const tizenMatch = ua.match(/Tizen\\/([^\\s]+)/);\n    const tvMatch = ua.match(/(?:Smart-TV|SMART-TV|TV)/i) ? \"Smart TV\" : \"\";\n    model = tizenMatch\n      ? `Tizen ${tizenMatch[1]} ${tvMatch}`.trim()\n      : \"Tizen TV\";\n  } else if (ua.includes(\"Philips\")) {\n    brand = \"Philips\";\n    os = \"Saphi\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"Sharp\") || ua.includes(\"AQUOS\")) {\n    brand = \"Sharp\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (\n    ua.includes(\"Android\") &&\n    (ua.includes(\"Sony\") || vendor.includes(\"Sony\"))\n  ) {\n    brand = \"Sony\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (\n    ua.includes(\"Android\") &&\n    (ua.includes(\"NetCast\") || ua.includes(\"LG\"))\n  ) {\n    brand = \"LG\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\" Roku\") || ua.includes(\"Roku/\")) {\n    brand = \"Roku\";\n    os = \"Roku OS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"AppleTV\")) {\n    brand = \"Apple\";\n    os = \"tvOS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  }\n\n  if (ua.includes(\"Android\")) {\n    isAndroid = true;\n    os = \"Android\";\n    deviceType = /Mobile/.test(ua) ? \"mobile\" : \"tablet\";\n\n    if (\n      ua.includes(\"Android\") &&\n      (maxTouchPoints === 0 ||\n        ua.includes(\"Google TV\") ||\n        ua.includes(\"XiaoMi\"))\n    ) {\n      deviceType = \"tv\";\n      isSmartTV = true;\n      brand = brand === \"Unknown\" ? \"Android TV\" : brand;\n    }\n\n    const androidModelMatch = ua.match(/\\(([^)]*Android[^)]*)\\)/);\n    if (androidModelMatch && androidModelMatch[1]) {\n      model = androidModelMatch[1];\n    }\n  }\n\n  if (/iPad|iPhone|iPod/.test(ua)) {\n    os = \"iOS\";\n    deviceType = \"mobile\";\n    brand = \"Apple\";\n    if (navigator.maxTouchPoints > 1 && /iPad/.test(ua)) {\n      deviceType = \"tablet\";\n    }\n  }\n\n  if (!isAndroid && !isSmartTV && !/Mobile/.test(ua)) {\n    if (ua.includes(\"Windows\")) {\n      os = \"Windows\";\n      deviceType = \"desktop\";\n    } else if (ua.includes(\"Mac\") && !/iPhone/.test(ua)) {\n      os = \"macOS\";\n      deviceType = \"desktop\";\n      if (maxTouchPoints > 1) deviceType = \"tablet\";\n    } else if (ua.includes(\"Linux\")) {\n      os = \"Linux\";\n      deviceType = \"desktop\";\n    }\n  }\n\n  if (brand === \"Unknown\") {\n    if (vendor.includes(\"Google\") || ua.includes(\"Chrome\")) brand = \"Google\";\n    if (vendor.includes(\"Apple\")) brand = \"Apple\";\n    if (vendor.includes(\"Samsung\") || ua.includes(\"SM-\")) brand = \"Samsung\";\n  }\n\n  isWebView = /wv|WebView|Linux; U;/.test(ua);\n\n  if (window?.outerHeight === 0 && window?.outerWidth === 0) {\n    isWebView = true;\n  }\n\n  isWebApp =\n    window.matchMedia(\"(display-mode: standalone)\").matches ||\n    (window.navigator as any).standalone === true ||\n    window.screen?.orientation?.angle !== undefined;\n\n  return {\n    brand,\n    os,\n    model: model || ua.substring(0, 50) + \"...\",\n    deviceType,\n    isSmartTV,\n    isAndroid,\n    isWebView,\n    isWebApp,\n    domain: window.location.hostname,\n    origin: window.location.origin,\n    path: window.location.pathname,\n    userAgent: ua,\n    vendor,\n    platform,\n    screen: screenInfo,\n    hardwareConcurrency,\n    deviceMemory: memory,\n    maxTouchPoints,\n    language: navigator.language,\n    languages: navigator.languages?.join(\",\") || \"\",\n    cookieEnabled: navigator.cookieEnabled,\n    doNotTrack: navigator.doNotTrack || \"\",\n    referrer: document.referrer,\n    visibilityState: document.visibilityState,\n  };\n}\n\nasync function sha256Hex(input: string): Promise<string> {\n  if (typeof crypto !== \"undefined\" && crypto.subtle?.digest) {\n    try {\n      await crypto.subtle.digest(\"SHA-256\", new Uint8Array([1, 2, 3]));\n\n      let encodedData: BufferSource;\n      if (typeof TextEncoder !== \"undefined\") {\n        encodedData = new TextEncoder().encode(input);\n      } else {\n        const utf8 = unescape(encodeURIComponent(input));\n        const buffer = new Uint8Array(utf8.length);\n        for (let i = 0; i < utf8.length; i++) {\n          buffer[i] = utf8.charCodeAt(i);\n        }\n        encodedData = buffer;\n      }\n\n      const hashBuffer = await crypto.subtle.digest(\"SHA-256\", encodedData);\n      return Array.from(new Uint8Array(hashBuffer))\n        .map((b) => b.toString(16).padStart(2, \"0\"))\n        .join(\"\");\n    } catch {\n      console.warn(\n        \"[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash\"\n      );\n    }\n  }\n\n  let hash = 0;\n  for (let i = 0; i < input.length; i++) {\n    const char = input.charCodeAt(i);\n    hash = (hash << 5) - hash + char;\n    hash = hash & hash;\n  }\n\n  const fallbackHash = Math.abs(hash).toString(16).padStart(8, \"0\");\n  const timestamp = Date.now().toString(16).padStart(12, \"0\");\n  const random = Math.random().toString(16).substring(2, 14).padStart(12, \"0\");\n\n  return (fallbackHash + timestamp + random).padEnd(64, \"0\");\n}\n\nfunction resolvePlayerIdentitySeed(clientInfo: ClientInfo): string {\n  const nativeDeviceId = resolveNativeDeviceId();\n  if (nativeDeviceId) {\n    return `device:${nativeDeviceId}`;\n  }\n\n  const persistedId = getOrCreateStoredUuid(PLAYER_ID_STORAGE_KEY);\n  if (persistedId) {\n    return `persisted:${persistedId}`;\n  }\n\n  return `fingerprint:${JSON.stringify(clientInfo)}`;\n}\n\nexport async function getBrowserID(clientInfo: ClientInfo): Promise<string> {\n  if (cachedBrowserId) {\n    return cachedBrowserId;\n  }\n\n  cachedBrowserId = await sha256Hex(resolvePlayerIdentitySeed(clientInfo));\n  return cachedBrowserId;\n}\n\ntype PlayerMetricFlags = {\n  adLoaded?: boolean;\n  adDetect?: boolean;\n  captureAt?: string;\n};\n\nfunction canPublish(licenseKey: string | undefined): licenseKey is string {\n  return Boolean(isMQTTEnabled() && licenseKey);\n}\n\nasync function buildPlayerMetricEvent(\n  context: PlayerAnalyticsContext = {},\n  flags: PlayerMetricFlags = {}\n): Promise<Record<string, unknown>> {\n  const clientInfo = getClientInfo();\n\n  const debugAdTiming = !!context.debugAdTiming;\n  if (debugAdTiming) {\n    console.log(\"[StormcloudVideoPlayer] clientInfo: \", JSON.stringify(clientInfo, null, 2));\n  }\n\n  const playerId = await getBrowserID(clientInfo);\n\n  if (debugAdTiming) {\n    console.log(\"[StormcloudVideoPlayer] playerId: \", playerId);\n  }\n\n  const captureAt = flags.captureAt ?? new Date().toISOString();\n\n  return {\n    player_id: playerId,\n    device_type: clientInfo.deviceType,\n    os: clientInfo.os.toLowerCase(),\n    ad_loaded: flags.adLoaded ?? false,\n    ad_detect: flags.adDetect ?? false,\n    capture_at: captureAt,\n    ...(context.inputStreamType\n      ? { input_stream_type: context.inputStreamType }\n      : {}),\n  };\n}\n\nfunction publishTracking(\n  licenseKey: string,\n  channel: \"metrics\" | \"impressions\" | \"heartbeat\",\n  body: Record<string, unknown>\n): void {\n  ensureMQTTClient();\n  publishMQTT(buildPlayerTopic(licenseKey, channel), body);\n}\n\nexport async function sendInitialTracking(\n  licenseKey?: string,\n  context: PlayerAnalyticsContext = {}\n): Promise<void> {\n  if (!canPublish(licenseKey)) return;\n\n  try {\n    const metricEvent = await buildPlayerMetricEvent(context, {\n      adLoaded: false,\n      adDetect: false,\n    });\n\n    publishTracking(licenseKey, \"metrics\", {\n      events: [metricEvent],\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending initial tracking data:\",\n      error\n    );\n  }\n}\n\nexport async function sendAdDetectTracking(\n  licenseKey: string | undefined,\n  adDetectInfo: AdDetectInfo,\n  context: PlayerAnalyticsContext = {}\n): Promise<void> {\n  try {\n    await sendHeartbeat(licenseKey, context, {\n      adDetect: true,\n      captureAt: adDetectInfo.timestamp,\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending ad detect tracking:\",\n      error\n    );\n  }\n}\n\nexport async function sendAdLoadedTracking(\n  licenseKey: string | undefined,\n  adLoadedInfo: AdLoadedInfo,\n  context: PlayerAnalyticsContext = {}\n): Promise<void> {\n  try {\n    await sendHeartbeat(licenseKey, context, {\n      adLoaded: true,\n      captureAt: adLoadedInfo.timestamp,\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending ad loaded tracking:\",\n      error\n    );\n  }\n}\n\nexport async function sendAdImpressionTracking(\n  licenseKey: string | undefined,\n  adImpressionInfo: AdImpressionInfo,\n  context: PlayerAnalyticsContext = {}\n): Promise<void> {\n  if (!canPublish(licenseKey)) return;\n\n  try {\n    const metricEvent = await buildPlayerMetricEvent(context, {\n      captureAt: adImpressionInfo.timestamp,\n    });\n\n    publishTracking(licenseKey, \"heartbeat\", metricEvent);\n    publishTracking(licenseKey, \"impressions\", {\n      events: [\n        {\n          player_id: metricEvent.player_id,\n          ad_played_count: 1,\n          ad_url: adImpressionInfo.adUrl,\n          capture_at: adImpressionInfo.timestamp,\n        },\n      ],\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending ad impression tracking:\",\n      error\n    );\n  }\n}\n\nexport async function sendHeartbeat(\n  licenseKey?: string,\n  context: PlayerAnalyticsContext = {},\n  flags: PlayerMetricFlags = {}\n): Promise<void> {\n  if (!canPublish(licenseKey)) return;\n\n  try {\n    const heartbeatData = await buildPlayerMetricEvent(context, flags);\n    publishTracking(licenseKey, \"heartbeat\", heartbeatData);\n  } catch (error) {\n    console.error(\"[StormcloudVideoPlayer] Error sending heartbeat:\", error);\n  }\n}\n","import type { AdController } from \"../types\";\n\nexport function getAdAudioVolume(adPlayer: AdController): number {\n  return adPlayer.getOriginalMutedState() ? 0 : adPlayer.getOriginalVolume();\n}\n\nexport function toggleMute(\n  video: HTMLVideoElement,\n  adPlayer: AdController,\n  debug: boolean\n): void {\n  if (adPlayer.isAdPlaying()) {\n    const newMutedState = !adPlayer.getOriginalMutedState();\n    const restoreVolume = adPlayer.getOriginalVolume() || 1;\n\n    adPlayer.updateOriginalMutedState(newMutedState, video.volume);\n    adPlayer.setAdVolume(newMutedState ? 0 : restoreVolume);\n\n    if (debug) {\n      console.log(\n        \"[StormcloudVideoPlayer] Mute toggle during ad - immediately applied:\",\n        newMutedState\n      );\n    }\n  } else {\n    video.muted = !video.muted;\n    adPlayer.updateOriginalMutedState(video.muted, video.volume);\n    if (debug) {\n      console.log(\"[StormcloudVideoPlayer] Muted:\", video.muted);\n    }\n  }\n}\n\nexport function toggleFullscreen(\n  video: HTMLVideoElement,\n  debug: boolean\n): Promise<void> {\n  return new Promise((resolve, reject) => {\n    if (!document.fullscreenElement) {\n      const container = video.parentElement;\n      if (!container) {\n        reject(new Error(\"No parent container found for fullscreen\"));\n        return;\n      }\n      container\n        .requestFullscreen()\n        .then(() => {\n          if (debug) {\n            console.log(\"[StormcloudVideoPlayer] Entered fullscreen\");\n          }\n          resolve();\n        })\n        .catch((err) => {\n          if (debug) {\n            console.error(\"[StormcloudVideoPlayer] Fullscreen error:\", err);\n          }\n          reject(err);\n        });\n    } else {\n      document\n        .exitFullscreen()\n        .then(() => {\n          if (debug) {\n            console.log(\"[StormcloudVideoPlayer] Exited fullscreen\");\n          }\n          resolve();\n        })\n        .catch((err) => {\n          if (debug) {\n            console.error(\n              \"[StormcloudVideoPlayer] Exit fullscreen error:\",\n              err\n            );\n          }\n          reject(err);\n        });\n    }\n  });\n}\n\nexport function isMuted(\n  video: HTMLVideoElement,\n  adPlayer: AdController\n): boolean {\n  if (adPlayer.isAdPlaying()) {\n    return adPlayer.getOriginalMutedState();\n  }\n  return video.muted;\n}\n\nexport function setMuted(\n  video: HTMLVideoElement,\n  adPlayer: AdController,\n  muted: boolean,\n  debug: boolean\n): void {\n  const adPlaying = adPlayer.isAdPlaying();\n\n  if (adPlaying && muted === video.muted) {\n    if (debug) {\n      console.log(\n        \"[StormcloudVideoPlayer] setMuted reflective update during ad ignored\",\n        { muted }\n      );\n    }\n    return;\n  }\n\n  video.muted = muted;\n\n  if (adPlaying) {\n    adPlayer.updateOriginalMutedState(muted, video.volume);\n    adPlayer.setAdVolume(muted ? 0 : 1);\n    if (debug) {\n      console.log(\"[StormcloudVideoPlayer] setMuted applied during ad\", {\n        muted,\n      });\n    }\n    return;\n  }\n\n  adPlayer.updateOriginalMutedState(muted, video.volume);\n  if (debug) {\n    console.log(\"[StormcloudVideoPlayer] setMuted called:\", muted);\n  }\n}\n\nexport function setVolume(\n  video: HTMLVideoElement,\n  adPlayer: AdController,\n  volume: number,\n  debug: boolean\n): void {\n  const clampedVolume = Math.max(0, Math.min(1, volume));\n  const adPlaying = adPlayer.isAdPlaying();\n\n  if (adPlaying) {\n    adPlayer.setAdVolume(clampedVolume);\n    adPlayer.updateOriginalMutedState(clampedVolume === 0, clampedVolume);\n    if (debug) {\n      console.log(\"[StormcloudVideoPlayer] setVolume applied during ad\", {\n        volume: clampedVolume,\n      });\n    }\n  } else {\n    video.volume = clampedVolume;\n    video.muted = clampedVolume === 0;\n    adPlayer.updateOriginalMutedState(clampedVolume === 0, clampedVolume);\n    if (debug) {\n      console.log(\"[StormcloudVideoPlayer] setVolume called:\", clampedVolume);\n    }\n  }\n}\n\nexport function getVolume(\n  video: HTMLVideoElement,\n  adPlayer: AdController\n): number {\n  const adPlaying = adPlayer.isAdPlaying();\n  if (adPlaying) {\n    return adPlayer.getAdVolume();\n  }\n  return video.volume;\n}\n\nexport function isFullscreen(): boolean {\n  return !!document.fullscreenElement;\n}\n\nexport function resizePlayer(\n  video: HTMLVideoElement,\n  adPlayer: AdController,\n  debug: boolean\n): void {\n  if (debug) {\n    console.log(\"[StormcloudVideoPlayer] Resizing player\");\n  }\n\n  if (adPlayer && adPlayer.isAdPlaying()) {\n    const width = video.clientWidth || 640;\n    const height = video.clientHeight || 480;\n\n    if (debug) {\n      console.log(\n        `[StormcloudVideoPlayer] Resizing ads manager to ${width}x${height}`\n      );\n    }\n\n    adPlayer.resize(width, height);\n  }\n}\n"]}