stormcloud-video-player 0.7.13 → 0.7.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/stormcloud-vp.min.js +1 -1
- package/lib/index.cjs +371 -49
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +12 -0
- package/lib/index.d.ts +12 -0
- package/lib/index.js +372 -50
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +38 -10
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +4 -0
- package/lib/players/FilePlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.cjs +38 -10
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +38 -10
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/adstormPlayer.cjs.map +1 -1
- package/lib/sdk/vastParser.cjs.map +1 -1
- package/lib/ui/OverlayRenderer.cjs +408 -37
- package/lib/ui/OverlayRenderer.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +371 -49
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/utils/browserCompat.cjs.map +1 -1
- package/lib/utils/overlays.d.cts +8 -0
- package/lib/utils/polyfills.cjs.map +1 -1
- package/lib/utils/tracking.cjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/sdk/adstormPlayer.cjs","../../src/sdk/adstormPlayer.ts","../../src/sdk/vastParser.ts"],"names":["__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toCommonJS","mod","value","adstormPlayer_exports","createAdStormPlayer","module","exports","firePixelWithRetry","url","retries","delayMs","logPrefix","attempt","fetch","method","mode","cache","keepalive","Promise","r","setTimeout","Math","pow","console","warn","fireTrackingPixels","urls","sessionId","length","forEach","trackingUrl","includes","catch","img","Image","onerror","src","log","error","SUPPORTED_VIDEO_EXTENSIONS","UNSUPPORTED_VIDEO_EXTENSIONS","REQUEST_TIMEOUT_MS","REQUEST_RETRY_BACKOFF_MS","REQUEST_MAX_RETRIES","AD_LAYER_Z_INDEX","COUNTDOWN_Z_INDEX","STALL_TIMEOUT_MS","getFileExtension","pathname","URL","lastDot","lastIndexOf","slice","toLowerCase","ext","split","isUnsupportedFormat","indexOf","replaceFlvExtension","replace","isSupportedFormat","mimeType","contentVideo","options","licenseKey","debug","adPlaying","originalMutedState","originalVolume","max","min","volume","listeners","Map","adVideoElement","adContainerEl","adCountdownEl","currentAd","destroyed","tornDown","continueLiveStreamDuringAds","adStallTimerId","adCountdownTimerId","adHideTimerId","lastCountdownSecond","adListenersBound","parentPositionOverridden","adHandlers","timeupdate","progress","currentTime","duration","trackingFired","firstQuartile","trackingUrls","midpoint","thirdQuartile","updateAdCountdown","playing","clearAdStallTimer","start","startAdCountdown","ended","complete","handleAdComplete","e","handleAdError","waiting","volumechange","muted","mute","unmute","pause","play","resume","impression","preloadSlots","args","emit","event","payload","set","Array","fn","clearTimeout","clearAdCountdownTimer","clearInterval","remainingSec","ceil","textContent","durationSec","currentTimeSec","setInterval","generateSessionId","Date","now","random","toString","bindAdEventListeners","addEventListener","unbindAdEventListeners","removeEventListener","teardownCurrentPlayback","ads","parserError","removeAttribute","load","buildVastUrl","durationSeconds","baseUrl","parseVastXml","xmlString","parser","DOMParser","xmlDoc","parseFromString","querySelector","adElements","querySelectorAll","adElement","adId","getAttribute","title","durationText","durationParts","parseInt","parseFloat","mediaFileElements","mf","eventKey","type","trim","width","height","bitrate","originalUrl","mediaFiles","push","el","clickThrough","id","selectBestMediaFile","mp4Files","filter","candidates","targetWidth","videoWidth","targetHeight","videoHeight","sort","a","b","diffA","abs","diffB","createAdVideoElement","video","document","createElement","style","position","left","top","objectFit","backgroundColor","zIndex","playsInline","preload","setAdPlayingFlag","isPlaying","dataset","stormcloudAdPlaying","setupAdEventListeners","opacity","display","pointerEvents","visibility","fetchVastOnce","vastUrl","controller","timeoutId","requestInit","response","xmlText","AbortController","abort","credentials","headers","Accept","referrerPolicy","signal","ok","Error","status","statusText","text","fetchVast","lastError","delay","resolve"],"mappings":"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","sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/sdk/adstormPlayer.ts\nvar adstormPlayer_exports = {};\n__export(adstormPlayer_exports, {\n createAdStormPlayer: () => createAdStormPlayer\n});\nmodule.exports = __toCommonJS(adstormPlayer_exports);\n\n// src/sdk/vastParser.ts\nasync function firePixelWithRetry(url, retries = 2, delayMs = 500, logPrefix = \"[VastParser]\") {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n await fetch(url, {\n method: \"GET\",\n mode: \"no-cors\",\n cache: \"no-cache\",\n keepalive: true\n });\n return;\n } catch {\n if (attempt < retries) {\n await new Promise((r) => setTimeout(r, delayMs * Math.pow(2, attempt)));\n } else {\n console.warn(`${logPrefix} Tracking pixel failed after ${retries + 1} attempts: ${url}`);\n }\n }\n }\n}\nfunction fireTrackingPixels(urls, sessionId, logPrefix = \"[VastParser]\") {\n if (!urls || urls.length === 0) return;\n urls.forEach((url) => {\n try {\n let trackingUrl = url;\n if (sessionId) {\n trackingUrl = `${trackingUrl}${trackingUrl.includes(\"?\") ? \"&\" : \"?\"}session_id=${sessionId}`;\n }\n if (typeof fetch !== \"undefined\") {\n firePixelWithRetry(trackingUrl, 2, 500, logPrefix).catch(() => {\n });\n } else {\n const img = new Image(1, 1);\n img.onerror = () => {\n };\n img.src = trackingUrl;\n }\n console.log(`${logPrefix} Fired tracking pixel: ${trackingUrl}`);\n } catch (error) {\n console.warn(`${logPrefix} Error firing tracking pixel:`, error);\n }\n });\n}\n\n// src/sdk/adstormPlayer.ts\nvar SUPPORTED_VIDEO_EXTENSIONS = [\".mp4\", \".webm\", \".ogg\", \".m3u8\", \".ts\"];\nvar UNSUPPORTED_VIDEO_EXTENSIONS = [\".flv\", \".f4v\", \".swf\", \".wmv\", \".avi\", \".mov\", \".mkv\"];\nvar REQUEST_TIMEOUT_MS = 5e3;\nvar REQUEST_MAX_RETRIES = 3;\nvar REQUEST_RETRY_BACKOFF_MS = 1500;\nvar AD_LAYER_Z_INDEX = \"30\";\nvar COUNTDOWN_Z_INDEX = \"31\";\nvar STALL_TIMEOUT_MS = 8e3;\nfunction getFileExtension(url) {\n try {\n const pathname = new URL(url, \"http://dummy\").pathname;\n const lastDot = pathname.lastIndexOf(\".\");\n if (lastDot === -1) return \"\";\n return pathname.slice(lastDot).toLowerCase();\n } catch {\n const lastDot = url.lastIndexOf(\".\");\n if (lastDot === -1) return \"\";\n const ext = url.slice(lastDot).split(/[?#]/)[0];\n return (ext || \"\").toLowerCase();\n }\n}\nfunction isUnsupportedFormat(url) {\n const ext = getFileExtension(url);\n return UNSUPPORTED_VIDEO_EXTENSIONS.indexOf(ext) !== -1;\n}\nfunction replaceFlvExtension(url) {\n const ext = getFileExtension(url);\n if (ext === \".flv\") {\n return url.replace(/\\.flv(\\?|$)/i, \".mp4$1\");\n }\n return url;\n}\nfunction isSupportedFormat(url, mimeType) {\n if (isUnsupportedFormat(url)) {\n return false;\n }\n const ext = getFileExtension(url);\n if (SUPPORTED_VIDEO_EXTENSIONS.indexOf(ext) !== -1) {\n return true;\n }\n if (ext === \"\" || ext === \".\") {\n return mimeType.includes(\"video/mp4\") || mimeType.includes(\"video/webm\") || mimeType.includes(\"m3u8\") || mimeType.includes(\"application/x-mpegurl\");\n }\n return false;\n}\nfunction createAdStormPlayer(contentVideo, options) {\n const { licenseKey, debug = false } = options;\n let adPlaying = false;\n let originalMutedState = false;\n let originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));\n const listeners = /* @__PURE__ */ new Map();\n let adVideoElement;\n let adContainerEl;\n let adCountdownEl;\n let currentAd;\n let destroyed = false;\n let tornDown = false;\n let continueLiveStreamDuringAds = false;\n let sessionId;\n let adStallTimerId;\n let adCountdownTimerId;\n let adHideTimerId;\n let lastCountdownSecond = -1;\n let adListenersBound = false;\n let parentPositionOverridden = false;\n const adHandlers = {\n timeupdate: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n const progress = adVideoElement.currentTime / currentAd.duration;\n if (progress >= 0.25 && !trackingFired.firstQuartile) {\n trackingFired.firstQuartile = true;\n fireTrackingPixels2(currentAd.trackingUrls.firstQuartile);\n }\n if (progress >= 0.5 && !trackingFired.midpoint) {\n trackingFired.midpoint = true;\n fireTrackingPixels2(currentAd.trackingUrls.midpoint);\n }\n if (progress >= 0.75 && !trackingFired.thirdQuartile) {\n trackingFired.thirdQuartile = true;\n fireTrackingPixels2(currentAd.trackingUrls.thirdQuartile);\n }\n updateAdCountdown();\n },\n playing: () => {\n clearAdStallTimer();\n if (!currentAd || trackingFired.start || destroyed || tornDown) return;\n trackingFired.start = true;\n fireTrackingPixels2(currentAd.trackingUrls.start);\n startAdCountdown();\n log(\"Ad started playing\");\n },\n ended: () => {\n if (!currentAd || trackingFired.complete || destroyed || tornDown) return;\n trackingFired.complete = true;\n fireTrackingPixels2(currentAd.trackingUrls.complete);\n log(\"Ad completed\");\n handleAdComplete();\n },\n error: (e) => {\n if (destroyed || tornDown) return;\n console.error(\"[AdStormPlayer] Ad video error:\", e);\n if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);\n handleAdError();\n },\n waiting: () => {\n clearAdStallTimer();\n adStallTimerId = setTimeout(() => {\n adStallTimerId = void 0;\n if (!adPlaying || destroyed || tornDown) return;\n console.warn(\"[AdStormPlayer] Ad playback stalled too long\");\n handleAdError();\n }, STALL_TIMEOUT_MS);\n },\n volumechange: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n if (adVideoElement.muted || adVideoElement.volume <= 0) {\n fireTrackingPixels2(currentAd.trackingUrls.mute);\n } else {\n fireTrackingPixels2(currentAd.trackingUrls.unmute);\n }\n },\n pause: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n if (!adVideoElement.ended) {\n fireTrackingPixels2(currentAd.trackingUrls.pause);\n }\n },\n play: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n if (adVideoElement.currentTime > 0) {\n fireTrackingPixels2(currentAd.trackingUrls.resume);\n }\n }\n };\n let trackingFired = {\n impression: false,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false\n };\n const preloadSlots = /* @__PURE__ */ new Map();\n function log(...args) {\n if (debug) {\n console.log(\"[AdStormPlayer]\", ...args);\n }\n }\n function emit(event, payload) {\n const set = listeners.get(event);\n if (!set) return;\n for (const fn of Array.from(set)) {\n try {\n fn(payload);\n } catch (error) {\n console.warn(`[AdStormPlayer] Error in event listener for ${event}:`, error);\n }\n }\n }\n function fireTrackingPixels2(urls) {\n fireTrackingPixels(urls, sessionId, \"[AdStormPlayer]\");\n }\n function clearAdStallTimer() {\n if (adStallTimerId) {\n clearTimeout(adStallTimerId);\n adStallTimerId = void 0;\n }\n }\n function clearAdCountdownTimer() {\n if (adCountdownTimerId) {\n clearInterval(adCountdownTimerId);\n adCountdownTimerId = void 0;\n }\n lastCountdownSecond = -1;\n }\n function updateAdCountdown() {\n if (!adCountdownEl || !adVideoElement || !currentAd || !adPlaying) return;\n const remainingSec = Math.max(\n 0,\n Math.ceil((currentAd.duration || 0) - adVideoElement.currentTime)\n );\n if (remainingSec === lastCountdownSecond) return;\n lastCountdownSecond = remainingSec;\n adCountdownEl.textContent = `Ad ${remainingSec}s`;\n emit(\"ad_countdown\", {\n remainingSec,\n durationSec: currentAd.duration,\n currentTimeSec: adVideoElement.currentTime\n });\n }\n function startAdCountdown() {\n clearAdCountdownTimer();\n updateAdCountdown();\n adCountdownTimerId = setInterval(updateAdCountdown, 250);\n }\n function generateSessionId() {\n return `adstorm-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n }\n function bindAdEventListeners() {\n if (!adVideoElement || adListenersBound) return;\n adVideoElement.addEventListener(\"timeupdate\", adHandlers.timeupdate);\n adVideoElement.addEventListener(\"playing\", adHandlers.playing);\n adVideoElement.addEventListener(\"ended\", adHandlers.ended);\n adVideoElement.addEventListener(\"error\", adHandlers.error);\n adVideoElement.addEventListener(\"waiting\", adHandlers.waiting);\n adVideoElement.addEventListener(\"volumechange\", adHandlers.volumechange);\n adVideoElement.addEventListener(\"pause\", adHandlers.pause);\n adVideoElement.addEventListener(\"play\", adHandlers.play);\n adListenersBound = true;\n }\n function unbindAdEventListeners() {\n if (!adVideoElement || !adListenersBound) return;\n adVideoElement.removeEventListener(\"timeupdate\", adHandlers.timeupdate);\n adVideoElement.removeEventListener(\"playing\", adHandlers.playing);\n adVideoElement.removeEventListener(\"ended\", adHandlers.ended);\n adVideoElement.removeEventListener(\"error\", adHandlers.error);\n adVideoElement.removeEventListener(\"waiting\", adHandlers.waiting);\n adVideoElement.removeEventListener(\"volumechange\", adHandlers.volumechange);\n adVideoElement.removeEventListener(\"pause\", adHandlers.pause);\n adVideoElement.removeEventListener(\"play\", adHandlers.play);\n adListenersBound = false;\n }\n function teardownCurrentPlayback() {\n unbindAdEventListeners();\n clearAdStallTimer();\n clearAdCountdownTimer();\n if (!adVideoElement) return;\n adVideoElement.pause();\n adVideoElement.removeAttribute(\"src\");\n adVideoElement.load();\n }\n function buildVastUrl(durationSeconds) {\n const baseUrl = `https://adstorm.co/api-adstorm-dev/adstorm/nab/vast/pod`;\n return `${baseUrl}?duration=${Math.ceil(durationSeconds)}`;\n }\n function parseVastXml(xmlString) {\n const ads = [];\n try {\n const parser = new DOMParser();\n const xmlDoc = parser.parseFromString(xmlString, \"text/xml\");\n const parserError = xmlDoc.querySelector(\"parsererror\");\n if (parserError) {\n console.error(\"[AdStormPlayer] XML parsing error:\", parserError.textContent);\n return [];\n }\n const adElements = xmlDoc.querySelectorAll(\"Ad\");\n adElements.forEach((adElement) => {\n const adId = adElement.getAttribute(\"id\") || \"unknown\";\n const title = adElement.querySelector(\"AdTitle\")?.textContent || \"Ad\";\n const durationText = adElement.querySelector(\"Duration\")?.textContent || \"00:00:30\";\n const durationParts = durationText.split(\":\");\n const duration = parseInt(durationParts[0] || \"0\", 10) * 3600 + parseInt(durationParts[1] || \"0\", 10) * 60 + parseFloat(durationParts[2] || \"0\");\n const mediaFileElements = adElement.querySelectorAll(\"MediaFile\");\n const mediaFiles = [];\n mediaFileElements.forEach((mf) => {\n const type = mf.getAttribute(\"type\") || \"\";\n let url = mf.textContent?.trim() || \"\";\n const width = parseInt(mf.getAttribute(\"width\") || \"1920\", 10);\n const height = parseInt(mf.getAttribute(\"height\") || \"1080\", 10);\n const bitrate = mf.getAttribute(\"bitrate\") ? parseInt(mf.getAttribute(\"bitrate\"), 10) : void 0;\n if (!url) {\n log(`Skipping empty MediaFile URL`);\n return;\n }\n const originalUrl = url;\n url = replaceFlvExtension(url);\n if (url !== originalUrl) {\n log(`Converted FLV to MP4: ${originalUrl} -> ${url}`);\n }\n if (isUnsupportedFormat(url)) {\n const ext = getFileExtension(url);\n log(`Skipping unsupported format: ${url} (extension: ${ext}, declared type: ${type})`);\n return;\n }\n if (isSupportedFormat(url, type)) {\n mediaFiles.push({ url, type, width, height, bitrate });\n log(`Found media file: ${url} (${type}, ${width}x${height})`);\n } else {\n log(`Skipping incompatible media file: ${url} (type: ${type})`);\n }\n });\n if (mediaFiles.length === 0) {\n log(\"No valid media files found in ad:\", adId);\n return;\n }\n const trackingUrls = {\n impression: [],\n start: [],\n firstQuartile: [],\n midpoint: [],\n thirdQuartile: [],\n complete: [],\n mute: [],\n unmute: [],\n pause: [],\n resume: [],\n error: []\n };\n adElement.querySelectorAll(\"Impression\").forEach((el) => {\n const url = el.textContent?.trim();\n if (url) trackingUrls.impression.push(url);\n });\n adElement.querySelectorAll(\"Tracking\").forEach((el) => {\n const event = el.getAttribute(\"event\");\n const url = el.textContent?.trim();\n if (event && url) {\n const eventKey = event;\n if (trackingUrls[eventKey]) {\n trackingUrls[eventKey].push(url);\n }\n }\n });\n const clickThrough = adElement.querySelector(\"ClickThrough\")?.textContent?.trim();\n ads.push({\n id: adId,\n title,\n duration,\n mediaFiles,\n trackingUrls,\n clickThrough\n });\n log(`Parsed ad: ${title}, duration: ${duration}s, media files: ${mediaFiles.length}`);\n });\n } catch (error) {\n console.error(\"[AdStormPlayer] Error parsing VAST XML:\", error);\n }\n return ads;\n }\n function selectBestMediaFile(mediaFiles) {\n if (mediaFiles.length === 0) return null;\n if (mediaFiles.length === 1) return mediaFiles[0];\n const mp4Files = mediaFiles.filter((mf) => mf.type.includes(\"video/mp4\"));\n const candidates = mp4Files.length > 0 ? mp4Files : mediaFiles;\n const targetWidth = contentVideo.videoWidth || 1280;\n const targetHeight = contentVideo.videoHeight || 720;\n candidates.sort((a, b) => {\n const diffA = Math.abs(a.width - targetWidth) + Math.abs(a.height - targetHeight);\n const diffB = Math.abs(b.width - targetWidth) + Math.abs(b.height - targetHeight);\n return diffA - diffB;\n });\n return candidates[0] || null;\n }\n function createAdVideoElement() {\n const video = document.createElement(\"video\");\n video.style.position = \"absolute\";\n video.style.left = \"0\";\n video.style.top = \"0\";\n video.style.width = \"100%\";\n video.style.height = \"100%\";\n video.style.objectFit = \"contain\";\n video.style.backgroundColor = \"#000\";\n video.style.zIndex = \"1\";\n video.playsInline = true;\n video.preload = \"auto\";\n video.muted = originalMutedState;\n video.volume = originalMutedState ? 0 : originalVolume;\n return video;\n }\n function setAdPlayingFlag(isPlaying) {\n if (isPlaying) {\n contentVideo.dataset.stormcloudAdPlaying = \"true\";\n } else {\n delete contentVideo.dataset.stormcloudAdPlaying;\n }\n }\n function setupAdEventListeners() {\n bindAdEventListeners();\n }\n function handleAdComplete() {\n if (destroyed || tornDown) return;\n log(\"Handling ad completion\");\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n if (adContainerEl) {\n adContainerEl.style.opacity = \"0\";\n adHideTimerId = setTimeout(() => {\n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n }, 300);\n }\n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n currentAd = void 0;\n emit(\"content_resume\");\n emit(\"all_ads_completed\");\n }\n function handleAdError() {\n if (destroyed || tornDown) return;\n log(\"Handling ad error\");\n if (!adPlaying) return;\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n currentAd = void 0;\n emit(\"ad_error\");\n emit(\"content_resume\");\n }\n async function fetchVastOnce(durationSeconds) {\n const vastUrl = buildVastUrl(durationSeconds);\n log(\"Fetching VAST from:\", vastUrl);\n const controller = typeof AbortController !== \"undefined\" ? new AbortController() : null;\n const timeoutId = setTimeout(() => controller?.abort(), REQUEST_TIMEOUT_MS);\n try {\n const requestInit = {\n method: \"GET\",\n mode: \"cors\",\n credentials: \"omit\",\n headers: {\n Accept: \"application/xml, text/xml, */*\"\n },\n referrerPolicy: \"no-referrer-when-downgrade\"\n };\n if (controller) {\n requestInit.signal = controller.signal;\n }\n const response = await fetch(vastUrl, requestInit);\n if (!response.ok) {\n throw new Error(`Failed to fetch VAST: ${response.status} ${response.statusText}`);\n }\n const xmlText = await response.text();\n log(\"VAST response received, length:\", xmlText.length);\n return parseVastXml(xmlText);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n async function fetchVast(durationSeconds) {\n let lastError;\n for (let attempt = 1; attempt <= REQUEST_MAX_RETRIES; attempt++) {\n try {\n const ads = await fetchVastOnce(durationSeconds);\n if (ads.length > 0) return ads;\n log(`No ad returned from VAST on attempt ${attempt}/${REQUEST_MAX_RETRIES}`);\n } catch (error) {\n lastError = error;\n if (error?.name === \"AbortError\") {\n console.warn(\n `[AdStormPlayer] VAST request timed out (${REQUEST_TIMEOUT_MS}ms), attempt ${attempt}/${REQUEST_MAX_RETRIES}`\n );\n } else {\n console.warn(`[AdStormPlayer] VAST request failed on attempt ${attempt}/${REQUEST_MAX_RETRIES}:`, error);\n }\n }\n if (attempt < REQUEST_MAX_RETRIES) {\n const delay = REQUEST_RETRY_BACKOFF_MS * attempt;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n if (lastError instanceof Error) {\n throw lastError;\n }\n return [];\n }\n function getDurationSecondsFromContext(requestContext) {\n if (!requestContext || typeof requestContext !== \"object\") {\n return 30;\n }\n const ctx = requestContext;\n const value = ctx.remainingBreakSec ?? ctx.breakDurationSec;\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n return 30;\n }\n return Math.max(1, Math.ceil(value));\n }\n async function requestAdFromApi(requestContext) {\n const durationSeconds = getDurationSecondsFromContext(requestContext);\n const ads = await fetchVast(durationSeconds);\n return ads[0] || null;\n }\n function assignCurrentAd(ad) {\n currentAd = ad;\n sessionId = generateSessionId();\n trackingFired = {\n impression: false,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false\n };\n fireTrackingPixels2(currentAd.trackingUrls.impression);\n trackingFired.impression = true;\n emit(\"ad_impression\");\n }\n return {\n initialize() {\n log(\"Initializing\");\n if (!adContainerEl) {\n const parent = contentVideo.parentElement;\n if (parent) {\n const computed = window.getComputedStyle(parent).position;\n if (computed === \"static\") {\n parent.style.position = \"relative\";\n parentPositionOverridden = true;\n }\n }\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.left = \"0\";\n container.style.top = \"0\";\n container.style.right = \"0\";\n container.style.bottom = \"0\";\n container.style.display = \"none\";\n container.style.alignItems = \"center\";\n container.style.justifyContent = \"center\";\n container.style.pointerEvents = \"none\";\n container.style.zIndex = AD_LAYER_Z_INDEX;\n container.style.backgroundColor = \"#000\";\n container.style.transition = \"opacity 0.3s ease-in-out\";\n container.style.opacity = \"0\";\n container.style.isolation = \"isolate\";\n const countdown = document.createElement(\"div\");\n countdown.style.position = \"absolute\";\n countdown.style.left = \"12px\";\n countdown.style.top = \"12px\";\n countdown.style.padding = \"4px 8px\";\n countdown.style.borderRadius = \"4px\";\n countdown.style.background = \"rgba(0,0,0,0.75)\";\n countdown.style.color = \"#fff\";\n countdown.style.fontFamily = \"sans-serif\";\n countdown.style.fontSize = \"12px\";\n countdown.style.lineHeight = \"1.2\";\n countdown.style.pointerEvents = \"none\";\n countdown.style.zIndex = COUNTDOWN_Z_INDEX;\n countdown.textContent = \"Ad\";\n container.appendChild(countdown);\n contentVideo.parentElement?.appendChild(container);\n adContainerEl = container;\n adCountdownEl = countdown;\n }\n },\n async requestAds(duration) {\n log(\"Requesting ads for duration:\", duration);\n if (adPlaying) {\n return Promise.reject(new Error(\"Ad already playing\"));\n }\n if (destroyed) {\n return Promise.reject(new Error(\"Player has been destroyed\"));\n }\n try {\n tornDown = false;\n let durationSeconds = 30;\n const parsed = parseInt(duration || \"\", 10);\n if (!isNaN(parsed) && parsed > 0) {\n durationSeconds = parsed;\n }\n const ads = await fetchVast(durationSeconds);\n if (ads.length === 0) {\n log(\"No ads available from VAST response\");\n emit(\"ad_error\");\n return Promise.resolve();\n }\n assignCurrentAd(ads[0]);\n log(`Ad loaded: ${currentAd.title}, duration: ${currentAd.duration}s`);\n return Promise.resolve();\n } catch (error) {\n console.error(\"[AdStormPlayer] Error requesting ads:\", error);\n emit(\"ad_error\");\n return Promise.reject(error);\n }\n },\n async play() {\n if (!currentAd) {\n return Promise.reject(new Error(\"No ad loaded\"));\n }\n if (destroyed) {\n return Promise.reject(new Error(\"Player has been destroyed\"));\n }\n log(\"Starting ad playback\");\n try {\n tornDown = false;\n if (adHideTimerId) {\n clearTimeout(adHideTimerId);\n adHideTimerId = void 0;\n }\n if (!adVideoElement) {\n adVideoElement = createAdVideoElement();\n adContainerEl?.appendChild(adVideoElement);\n } else {\n teardownCurrentPlayback();\n }\n setupAdEventListeners();\n trackingFired = {\n impression: trackingFired.impression,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false\n };\n contentVideo.style.transition = \"opacity 0.3s ease-in-out\";\n contentVideo.style.opacity = \"0\";\n setTimeout(() => {\n contentVideo.style.visibility = \"hidden\";\n }, 300);\n contentVideo.muted = true;\n contentVideo.volume = 0;\n if (!continueLiveStreamDuringAds) {\n contentVideo.pause();\n }\n adPlaying = true;\n setAdPlayingFlag(true);\n if (adVideoElement) {\n adVideoElement.volume = originalMutedState ? 0 : originalVolume;\n adVideoElement.muted = originalMutedState;\n }\n if (adContainerEl) {\n adContainerEl.style.display = \"flex\";\n adContainerEl.style.pointerEvents = \"auto\";\n adContainerEl.offsetHeight;\n adContainerEl.style.opacity = \"1\";\n }\n emit(\"content_pause\");\n const mediaFile = selectBestMediaFile(currentAd.mediaFiles);\n if (!mediaFile) {\n throw new Error(\"No media file available\");\n }\n log(\"Playing media file:\", mediaFile.url);\n adVideoElement.src = mediaFile.url;\n adVideoElement.load();\n await adVideoElement.play();\n return Promise.resolve();\n } catch (error) {\n console.error(\"[AdStormPlayer] Error playing ad:\", error);\n handleAdError();\n return Promise.reject(error);\n }\n },\n async stop() {\n log(\"Stopping ad\");\n tornDown = true;\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n if (adContainerEl) {\n adContainerEl.style.opacity = \"0\";\n adHideTimerId = setTimeout(() => {\n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n }, 300);\n }\n teardownCurrentPlayback();\n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n currentAd = void 0;\n return Promise.resolve();\n },\n pause() {\n if (!adPlaying || !adVideoElement) return;\n try {\n if (!adVideoElement.paused) adVideoElement.pause();\n } catch (error) {\n console.warn(\"[AdStormPlayer] Error pausing ad:\", error);\n }\n },\n resume() {\n if (!adPlaying || !adVideoElement) return;\n try {\n if (adVideoElement.paused) adVideoElement.play().catch(() => {\n });\n } catch (error) {\n console.warn(\"[AdStormPlayer] Error resuming ad:\", error);\n }\n },\n destroy() {\n log(\"Destroying\");\n destroyed = true;\n tornDown = true;\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n if (adHideTimerId) {\n clearTimeout(adHideTimerId);\n adHideTimerId = void 0;\n }\n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n teardownCurrentPlayback();\n adVideoElement?.remove();\n adVideoElement = void 0;\n if (adContainerEl?.parentElement) {\n adContainerEl.parentElement.removeChild(adContainerEl);\n }\n adContainerEl = void 0;\n adCountdownEl = void 0;\n currentAd = void 0;\n sessionId = void 0;\n preloadSlots.clear();\n listeners.clear();\n if (parentPositionOverridden && contentVideo.parentElement) {\n contentVideo.parentElement.style.position = \"\";\n parentPositionOverridden = false;\n }\n },\n updateOptions(opts) {\n if (opts.continueLiveStreamDuringAds !== void 0) {\n continueLiveStreamDuringAds = opts.continueLiveStreamDuringAds;\n }\n },\n async playAd(requestContext) {\n if (destroyed) return Promise.reject(new Error(\"Player has been destroyed\"));\n if (!currentAd) {\n const ad = await requestAdFromApi(requestContext);\n if (!ad) {\n emit(\"ad_error\", { message: \"No valid ad from AdStorm API\" });\n return Promise.reject(new Error(\"No valid ad from AdStorm API\"));\n }\n assignCurrentAd(ad);\n }\n return this.play();\n },\n async preloadAd(arg1, arg2) {\n if (destroyed) return;\n const token = typeof arg1 === \"string\" ? arg1 : typeof arg2 === \"string\" ? arg2 : void 0;\n if (!token) return;\n if (currentAd) {\n preloadSlots.set(token, { ad: currentAd });\n currentAd = void 0;\n return;\n }\n const requestContext = typeof arg1 === \"string\" ? arg2 : arg1;\n const ad = await requestAdFromApi(requestContext);\n if (!ad) return;\n preloadSlots.set(token, { ad });\n },\n async playPreloaded(token) {\n if (destroyed) return Promise.reject(new Error(\"Player has been destroyed\"));\n const slot = preloadSlots.get(token);\n if (!slot) {\n return Promise.reject(new Error(`No preloaded ad for token ${token}`));\n }\n preloadSlots.delete(token);\n assignCurrentAd(slot.ad);\n return this.play();\n },\n hasPreloaded(token) {\n return preloadSlots.has(token);\n },\n cancelPreload(token) {\n preloadSlots.delete(token);\n },\n isAdPlaying() {\n return adPlaying;\n },\n resize(width, height) {\n log(`Resizing to ${width}x${height}`);\n if (adContainerEl) {\n adContainerEl.style.width = `${width}px`;\n adContainerEl.style.height = `${height}px`;\n }\n if (adVideoElement) {\n adVideoElement.style.width = `${width}px`;\n adVideoElement.style.height = `${height}px`;\n }\n },\n on(event, listener) {\n if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());\n listeners.get(event).add(listener);\n },\n off(event, listener) {\n listeners.get(event)?.delete(listener);\n },\n updateOriginalMutedState(muted, volume) {\n const nextVolume = typeof volume === \"number\" && !Number.isNaN(volume) ? Math.max(0, Math.min(1, volume)) : originalVolume;\n log(`updateOriginalMutedState: muted=${muted}, volume=${nextVolume}`);\n originalMutedState = muted;\n originalVolume = nextVolume;\n },\n getOriginalMutedState() {\n return originalMutedState;\n },\n getOriginalVolume() {\n return originalVolume;\n },\n setAdVolume(volume) {\n if (adVideoElement && adPlaying) {\n adVideoElement.volume = Math.max(0, Math.min(1, volume));\n adVideoElement.muted = volume === 0;\n }\n },\n getAdVolume() {\n if (adVideoElement && adPlaying) {\n return adVideoElement.volume;\n }\n return 1;\n },\n showPlaceholder() {\n if (!adContainerEl) {\n const parent = contentVideo.parentElement;\n if (parent) {\n const computed = window.getComputedStyle(parent).position;\n if (computed === \"static\") {\n parent.style.position = \"relative\";\n parentPositionOverridden = true;\n }\n }\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.left = \"0\";\n container.style.top = \"0\";\n container.style.right = \"0\";\n container.style.bottom = \"0\";\n container.style.display = \"none\";\n container.style.alignItems = \"center\";\n container.style.justifyContent = \"center\";\n container.style.pointerEvents = \"none\";\n container.style.zIndex = AD_LAYER_Z_INDEX;\n container.style.backgroundColor = \"#000\";\n container.style.isolation = \"isolate\";\n const countdown = document.createElement(\"div\");\n countdown.style.position = \"absolute\";\n countdown.style.left = \"12px\";\n countdown.style.top = \"12px\";\n countdown.style.padding = \"4px 8px\";\n countdown.style.borderRadius = \"4px\";\n countdown.style.background = \"rgba(0,0,0,0.75)\";\n countdown.style.color = \"#fff\";\n countdown.style.fontFamily = \"sans-serif\";\n countdown.style.fontSize = \"12px\";\n countdown.style.lineHeight = \"1.2\";\n countdown.style.pointerEvents = \"none\";\n countdown.style.zIndex = COUNTDOWN_Z_INDEX;\n countdown.textContent = \"Ad\";\n container.appendChild(countdown);\n contentVideo.parentElement?.appendChild(container);\n adContainerEl = container;\n adCountdownEl = countdown;\n }\n if (adContainerEl) {\n adContainerEl.style.display = \"flex\";\n adContainerEl.style.opacity = \"1\";\n adContainerEl.style.pointerEvents = \"auto\";\n }\n },\n hidePlaceholder() {\n if (adContainerEl) {\n adContainerEl.style.opacity = \"0\";\n setTimeout(() => {\n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n }, 300);\n }\n }\n };\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n createAdStormPlayer\n});\n","import type { AdController, AdBreakContext } from \"../types\";\nimport { fireTrackingPixels as fireTrackingPixelsShared } from \"./vastParser\";\n\ninterface VastMediaFile {\n url: string;\n type: string;\n width: number;\n height: number;\n bitrate?: number | undefined;\n}\n\nconst SUPPORTED_VIDEO_EXTENSIONS = ['.mp4', '.webm', '.ogg', '.m3u8', '.ts'];\nconst UNSUPPORTED_VIDEO_EXTENSIONS = ['.flv', '.f4v', '.swf', '.wmv', '.avi', '.mov', '.mkv'];\nconst REQUEST_TIMEOUT_MS = 5000;\nconst REQUEST_MAX_RETRIES = 3;\nconst REQUEST_RETRY_BACKOFF_MS = 1500;\nconst AD_LAYER_Z_INDEX = \"30\";\nconst COUNTDOWN_Z_INDEX = \"31\";\nconst STALL_TIMEOUT_MS = 8000;\n\nfunction getFileExtension(url: string): string {\n try {\n const pathname = new URL(url, 'http://dummy').pathname;\n const lastDot = pathname.lastIndexOf('.');\n if (lastDot === -1) return '';\n return pathname.slice(lastDot).toLowerCase();\n } catch {\n const lastDot = url.lastIndexOf('.');\n if (lastDot === -1) return '';\n const ext = url.slice(lastDot).split(/[?#]/)[0];\n return (ext || '').toLowerCase();\n }\n}\n\nfunction isUnsupportedFormat(url: string): boolean {\n const ext = getFileExtension(url);\n return UNSUPPORTED_VIDEO_EXTENSIONS.indexOf(ext) !== -1;\n}\n\nfunction replaceFlvExtension(url: string): string {\n const ext = getFileExtension(url);\n if (ext === '.flv') {\n return url.replace(/\\.flv(\\?|$)/i, '.mp4$1');\n }\n return url;\n}\n\nfunction isSupportedFormat(url: string, mimeType: string): boolean {\n if (isUnsupportedFormat(url)) {\n return false;\n }\n \n const ext = getFileExtension(url);\n \n if (SUPPORTED_VIDEO_EXTENSIONS.indexOf(ext) !== -1) {\n return true;\n }\n \n if (ext === '' || ext === '.') {\n return mimeType.includes('video/mp4') || \n mimeType.includes('video/webm') || \n mimeType.includes('m3u8') ||\n mimeType.includes('application/x-mpegurl');\n }\n \n return false;\n}\n\ninterface VastTrackingUrls {\n impression: string[];\n start: string[];\n firstQuartile: string[];\n midpoint: string[];\n thirdQuartile: string[];\n complete: string[];\n mute: string[];\n unmute: string[];\n pause: string[];\n resume: string[];\n error: string[];\n}\n\ninterface VastAd {\n id: string;\n title: string;\n duration: number;\n mediaFiles: VastMediaFile[];\n trackingUrls: VastTrackingUrls;\n clickThrough?: string | undefined;\n}\n\nexport interface AdStormPlayerOptions {\n licenseKey: string;\n debug?: boolean;\n}\n\nexport interface AdStormLayerOptionsUpdate {\n continueLiveStreamDuringAds?: boolean;\n mainHlsInstance?: unknown;\n}\n\nexport interface AdStormAdLayer extends AdController {\n requestAds: (duration?: string) => Promise<void>;\n updateOptions: (opts: AdStormLayerOptionsUpdate) => void;\n playAd: (requestContext?: unknown) => Promise<void>;\n preloadAd: (arg1: unknown, arg2?: unknown) => Promise<void>;\n playPreloaded: (token: string) => Promise<void>;\n hasPreloaded: (token: string) => boolean;\n cancelPreload: (token: string) => void;\n}\n\nexport function createAdStormPlayer(\n contentVideo: HTMLVideoElement,\n options: AdStormPlayerOptions\n): AdStormAdLayer {\n const { licenseKey, debug = false } = options;\n \n let adPlaying = false;\n let originalMutedState = false;\n let originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));\n const listeners = new Map<string, Set<(payload?: any) => void>>();\n \n let adVideoElement: HTMLVideoElement | undefined;\n let adContainerEl: HTMLDivElement | undefined;\n let adCountdownEl: HTMLDivElement | undefined;\n let currentAd: VastAd | undefined;\n let destroyed = false;\n let tornDown = false;\n let continueLiveStreamDuringAds = false;\n let sessionId: string | undefined;\n let adStallTimerId: ReturnType<typeof setTimeout> | undefined;\n let adCountdownTimerId: ReturnType<typeof setInterval> | undefined;\n let adHideTimerId: ReturnType<typeof setTimeout> | undefined;\n let lastCountdownSecond = -1;\n let adListenersBound = false;\n let parentPositionOverridden = false;\n\n const adHandlers = {\n timeupdate: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n const progress = adVideoElement.currentTime / currentAd.duration;\n\n if (progress >= 0.25 && !trackingFired.firstQuartile) {\n trackingFired.firstQuartile = true;\n fireTrackingPixels(currentAd.trackingUrls.firstQuartile);\n }\n if (progress >= 0.5 && !trackingFired.midpoint) {\n trackingFired.midpoint = true;\n fireTrackingPixels(currentAd.trackingUrls.midpoint);\n }\n if (progress >= 0.75 && !trackingFired.thirdQuartile) {\n trackingFired.thirdQuartile = true;\n fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);\n }\n updateAdCountdown();\n },\n playing: () => {\n clearAdStallTimer();\n if (!currentAd || trackingFired.start || destroyed || tornDown) return;\n trackingFired.start = true;\n fireTrackingPixels(currentAd.trackingUrls.start);\n startAdCountdown();\n log(\"Ad started playing\");\n },\n ended: () => {\n if (!currentAd || trackingFired.complete || destroyed || tornDown) return;\n trackingFired.complete = true;\n fireTrackingPixels(currentAd.trackingUrls.complete);\n log(\"Ad completed\");\n handleAdComplete();\n },\n error: (e: Event) => {\n if (destroyed || tornDown) return;\n console.error(\"[AdStormPlayer] Ad video error:\", e);\n if (currentAd) fireTrackingPixels(currentAd.trackingUrls.error);\n handleAdError();\n },\n waiting: () => {\n clearAdStallTimer();\n adStallTimerId = setTimeout(() => {\n adStallTimerId = undefined;\n if (!adPlaying || destroyed || tornDown) return;\n console.warn(\"[AdStormPlayer] Ad playback stalled too long\");\n handleAdError();\n }, STALL_TIMEOUT_MS);\n },\n volumechange: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n if (adVideoElement.muted || adVideoElement.volume <= 0) {\n fireTrackingPixels(currentAd.trackingUrls.mute);\n } else {\n fireTrackingPixels(currentAd.trackingUrls.unmute);\n }\n },\n pause: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n if (!adVideoElement.ended) {\n fireTrackingPixels(currentAd.trackingUrls.pause);\n }\n },\n play: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n if (adVideoElement.currentTime > 0) {\n fireTrackingPixels(currentAd.trackingUrls.resume);\n }\n },\n };\n \n let trackingFired = {\n impression: false,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false,\n };\n const preloadSlots = new Map<string, { ad: VastAd }>();\n\n function log(...args: any[]): void {\n if (debug) {\n console.log(\"[AdStormPlayer]\", ...args);\n }\n }\n\n function emit(event: string, payload?: any): void {\n const set = listeners.get(event);\n if (!set) return;\n for (const fn of Array.from(set)) {\n try {\n fn(payload);\n } catch (error) {\n console.warn(`[AdStormPlayer] Error in event listener for ${event}:`, error);\n }\n }\n }\n\n function fireTrackingPixels(urls: string[]): void {\n fireTrackingPixelsShared(urls, sessionId, \"[AdStormPlayer]\");\n }\n\n function clearAdStallTimer(): void {\n if (adStallTimerId) {\n clearTimeout(adStallTimerId);\n adStallTimerId = undefined;\n }\n }\n\n function clearAdCountdownTimer(): void {\n if (adCountdownTimerId) {\n clearInterval(adCountdownTimerId);\n adCountdownTimerId = undefined;\n }\n lastCountdownSecond = -1;\n }\n\n function updateAdCountdown(): void {\n if (!adCountdownEl || !adVideoElement || !currentAd || !adPlaying) return;\n const remainingSec = Math.max(\n 0,\n Math.ceil((currentAd.duration || 0) - adVideoElement.currentTime)\n );\n if (remainingSec === lastCountdownSecond) return;\n lastCountdownSecond = remainingSec;\n adCountdownEl.textContent = `Ad ${remainingSec}s`;\n emit(\"ad_countdown\", {\n remainingSec,\n durationSec: currentAd.duration,\n currentTimeSec: adVideoElement.currentTime,\n });\n }\n\n function startAdCountdown(): void {\n clearAdCountdownTimer();\n updateAdCountdown();\n adCountdownTimerId = setInterval(updateAdCountdown, 250);\n }\n\n function generateSessionId(): string {\n return `adstorm-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n }\n\n function bindAdEventListeners(): void {\n if (!adVideoElement || adListenersBound) return;\n adVideoElement.addEventListener(\"timeupdate\", adHandlers.timeupdate);\n adVideoElement.addEventListener(\"playing\", adHandlers.playing);\n adVideoElement.addEventListener(\"ended\", adHandlers.ended);\n adVideoElement.addEventListener(\"error\", adHandlers.error);\n adVideoElement.addEventListener(\"waiting\", adHandlers.waiting);\n adVideoElement.addEventListener(\"volumechange\", adHandlers.volumechange);\n adVideoElement.addEventListener(\"pause\", adHandlers.pause);\n adVideoElement.addEventListener(\"play\", adHandlers.play);\n adListenersBound = true;\n }\n\n function unbindAdEventListeners(): void {\n if (!adVideoElement || !adListenersBound) return;\n adVideoElement.removeEventListener(\"timeupdate\", adHandlers.timeupdate);\n adVideoElement.removeEventListener(\"playing\", adHandlers.playing);\n adVideoElement.removeEventListener(\"ended\", adHandlers.ended);\n adVideoElement.removeEventListener(\"error\", adHandlers.error);\n adVideoElement.removeEventListener(\"waiting\", adHandlers.waiting);\n adVideoElement.removeEventListener(\"volumechange\", adHandlers.volumechange);\n adVideoElement.removeEventListener(\"pause\", adHandlers.pause);\n adVideoElement.removeEventListener(\"play\", adHandlers.play);\n adListenersBound = false;\n }\n\n function teardownCurrentPlayback(): void {\n unbindAdEventListeners();\n clearAdStallTimer();\n clearAdCountdownTimer();\n if (!adVideoElement) return;\n adVideoElement.pause();\n adVideoElement.removeAttribute(\"src\");\n adVideoElement.load();\n }\n\n function buildVastUrl(durationSeconds: number): string {\n const baseUrl = `https://adstorm.co/api-adstorm-dev/adstorm/nab/vast/pod`;\n\n return `${baseUrl}?duration=${Math.ceil(durationSeconds)}`;\n }\n\n function parseVastXml(xmlString: string): VastAd[] {\n const ads: VastAd[] = [];\n \n try {\n const parser = new DOMParser();\n const xmlDoc = parser.parseFromString(xmlString, \"text/xml\");\n \n const parserError = xmlDoc.querySelector(\"parsererror\");\n if (parserError) {\n console.error(\"[AdStormPlayer] XML parsing error:\", parserError.textContent);\n return [];\n }\n \n const adElements = xmlDoc.querySelectorAll(\"Ad\");\n \n adElements.forEach((adElement) => {\n const adId = adElement.getAttribute(\"id\") || \"unknown\";\n const title = adElement.querySelector(\"AdTitle\")?.textContent || \"Ad\";\n \n const durationText = adElement.querySelector(\"Duration\")?.textContent || \"00:00:30\";\n const durationParts = durationText.split(\":\");\n const duration =\n parseInt(durationParts[0] || \"0\", 10) * 3600 +\n parseInt(durationParts[1] || \"0\", 10) * 60 +\n parseFloat(durationParts[2] || \"0\");\n \n const mediaFileElements = adElement.querySelectorAll(\"MediaFile\");\n const mediaFiles: VastMediaFile[] = [];\n \n mediaFileElements.forEach((mf) => {\n const type = mf.getAttribute(\"type\") || \"\";\n let url = mf.textContent?.trim() || \"\";\n const width = parseInt(mf.getAttribute(\"width\") || \"1920\", 10);\n const height = parseInt(mf.getAttribute(\"height\") || \"1080\", 10);\n const bitrate = mf.getAttribute(\"bitrate\") \n ? parseInt(mf.getAttribute(\"bitrate\")!, 10) \n : undefined;\n \n if (!url) {\n log(`Skipping empty MediaFile URL`);\n return;\n }\n \n const originalUrl = url;\n url = replaceFlvExtension(url);\n if (url !== originalUrl) {\n log(`Converted FLV to MP4: ${originalUrl} -> ${url}`);\n }\n \n if (isUnsupportedFormat(url)) {\n const ext = getFileExtension(url);\n log(`Skipping unsupported format: ${url} (extension: ${ext}, declared type: ${type})`);\n return;\n }\n \n if (isSupportedFormat(url, type)) {\n mediaFiles.push({ url, type, width, height, bitrate });\n log(`Found media file: ${url} (${type}, ${width}x${height})`);\n } else {\n log(`Skipping incompatible media file: ${url} (type: ${type})`);\n }\n });\n \n if (mediaFiles.length === 0) {\n log(\"No valid media files found in ad:\", adId);\n return;\n }\n \n const trackingUrls: VastTrackingUrls = {\n impression: [],\n start: [],\n firstQuartile: [],\n midpoint: [],\n thirdQuartile: [],\n complete: [],\n mute: [],\n unmute: [],\n pause: [],\n resume: [],\n error: [],\n };\n \n adElement.querySelectorAll(\"Impression\").forEach((el) => {\n const url = el.textContent?.trim();\n if (url) trackingUrls.impression.push(url);\n });\n \n adElement.querySelectorAll(\"Tracking\").forEach((el) => {\n const event = el.getAttribute(\"event\");\n const url = el.textContent?.trim();\n if (event && url) {\n const eventKey = event as keyof VastTrackingUrls;\n if (trackingUrls[eventKey]) {\n trackingUrls[eventKey].push(url);\n }\n }\n });\n \n const clickThrough = adElement.querySelector(\"ClickThrough\")?.textContent?.trim();\n \n ads.push({\n id: adId,\n title,\n duration,\n mediaFiles,\n trackingUrls,\n clickThrough,\n });\n \n log(`Parsed ad: ${title}, duration: ${duration}s, media files: ${mediaFiles.length}`);\n });\n \n } catch (error) {\n console.error(\"[AdStormPlayer] Error parsing VAST XML:\", error);\n }\n \n return ads;\n }\n\n function selectBestMediaFile(mediaFiles: VastMediaFile[]): VastMediaFile | null {\n if (mediaFiles.length === 0) return null;\n if (mediaFiles.length === 1) return mediaFiles[0]!;\n \n const mp4Files = mediaFiles.filter(mf => mf.type.includes(\"video/mp4\"));\n const candidates = mp4Files.length > 0 ? mp4Files : mediaFiles;\n \n const targetWidth = contentVideo.videoWidth || 1280;\n const targetHeight = contentVideo.videoHeight || 720;\n \n candidates.sort((a, b) => {\n const diffA = Math.abs(a.width - targetWidth) + Math.abs(a.height - targetHeight);\n const diffB = Math.abs(b.width - targetWidth) + Math.abs(b.height - targetHeight);\n return diffA - diffB;\n });\n \n return candidates[0] || null;\n }\n\n function createAdVideoElement(): HTMLVideoElement {\n const video = document.createElement(\"video\");\n video.style.position = \"absolute\";\n video.style.left = \"0\";\n video.style.top = \"0\";\n video.style.width = \"100%\";\n video.style.height = \"100%\";\n video.style.objectFit = \"contain\";\n video.style.backgroundColor = \"#000\";\n video.style.zIndex = \"1\";\n video.playsInline = true;\n video.preload = \"auto\";\n video.muted = originalMutedState;\n video.volume = originalMutedState ? 0 : originalVolume;\n \n return video;\n }\n\n function setAdPlayingFlag(isPlaying: boolean): void {\n if (isPlaying) {\n contentVideo.dataset.stormcloudAdPlaying = \"true\";\n } else {\n delete contentVideo.dataset.stormcloudAdPlaying;\n }\n }\n\n function setupAdEventListeners(): void {\n bindAdEventListeners();\n }\n\n function handleAdComplete(): void {\n if (destroyed || tornDown) return;\n log(\"Handling ad completion\");\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n \n if (adContainerEl) {\n adContainerEl.style.opacity = \"0\";\n adHideTimerId = setTimeout(() => {\n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n }, 300);\n }\n \n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n currentAd = undefined;\n \n emit(\"content_resume\");\n emit(\"all_ads_completed\");\n }\n\n function handleAdError(): void {\n if (destroyed || tornDown) return;\n log(\"Handling ad error\");\n if (!adPlaying) return;\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n \n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n \n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n \n currentAd = undefined;\n emit(\"ad_error\");\n emit(\"content_resume\");\n }\n\n async function fetchVastOnce(durationSeconds: number): Promise<VastAd[]> {\n const vastUrl = buildVastUrl(durationSeconds);\n log(\"Fetching VAST from:\", vastUrl);\n\n const controller =\n typeof AbortController !== \"undefined\" ? new AbortController() : null;\n const timeoutId = setTimeout(() => controller?.abort(), REQUEST_TIMEOUT_MS);\n\n try {\n const requestInit: RequestInit = {\n method: \"GET\",\n mode: \"cors\",\n credentials: \"omit\",\n headers: {\n Accept: \"application/xml, text/xml, */*\",\n },\n referrerPolicy: \"no-referrer-when-downgrade\",\n };\n if (controller) {\n requestInit.signal = controller.signal;\n }\n\n const response = await fetch(vastUrl, requestInit);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch VAST: ${response.status} ${response.statusText}`);\n }\n\n const xmlText = await response.text();\n log(\"VAST response received, length:\", xmlText.length);\n return parseVastXml(xmlText);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async function fetchVast(durationSeconds: number): Promise<VastAd[]> {\n let lastError: unknown;\n for (let attempt = 1; attempt <= REQUEST_MAX_RETRIES; attempt++) {\n try {\n const ads = await fetchVastOnce(durationSeconds);\n if (ads.length > 0) return ads;\n log(`No ad returned from VAST on attempt ${attempt}/${REQUEST_MAX_RETRIES}`);\n } catch (error: any) {\n lastError = error;\n if (error?.name === \"AbortError\") {\n console.warn(\n `[AdStormPlayer] VAST request timed out (${REQUEST_TIMEOUT_MS}ms), attempt ${attempt}/${REQUEST_MAX_RETRIES}`\n );\n } else {\n console.warn(`[AdStormPlayer] VAST request failed on attempt ${attempt}/${REQUEST_MAX_RETRIES}:`, error);\n }\n }\n\n if (attempt < REQUEST_MAX_RETRIES) {\n const delay = REQUEST_RETRY_BACKOFF_MS * attempt;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n if (lastError instanceof Error) {\n throw lastError;\n }\n return [];\n }\n\n function getDurationSecondsFromContext(requestContext?: unknown): number {\n if (!requestContext || typeof requestContext !== \"object\") {\n return 30;\n }\n const ctx = requestContext as AdBreakContext;\n const value = ctx.remainingBreakSec ?? ctx.breakDurationSec;\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n return 30;\n }\n return Math.max(1, Math.ceil(value));\n }\n\n async function requestAdFromApi(requestContext?: unknown): Promise<VastAd | null> {\n const durationSeconds = getDurationSecondsFromContext(requestContext);\n const ads = await fetchVast(durationSeconds);\n return ads[0] || null;\n }\n\n function assignCurrentAd(ad: VastAd): void {\n currentAd = ad;\n sessionId = generateSessionId();\n trackingFired = {\n impression: false,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false,\n };\n fireTrackingPixels(currentAd.trackingUrls.impression);\n trackingFired.impression = true;\n emit(\"ad_impression\");\n }\n\n return {\n initialize() {\n log(\"Initializing\");\n \n if (!adContainerEl) {\n const parent = contentVideo.parentElement;\n if (parent) {\n const computed = window.getComputedStyle(parent).position;\n if (computed === \"static\") {\n parent.style.position = \"relative\";\n parentPositionOverridden = true;\n }\n }\n\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.left = \"0\";\n container.style.top = \"0\";\n container.style.right = \"0\";\n container.style.bottom = \"0\";\n container.style.display = \"none\";\n container.style.alignItems = \"center\";\n container.style.justifyContent = \"center\";\n container.style.pointerEvents = \"none\";\n container.style.zIndex = AD_LAYER_Z_INDEX;\n container.style.backgroundColor = \"#000\";\n container.style.transition = \"opacity 0.3s ease-in-out\";\n container.style.opacity = \"0\";\n container.style.isolation = \"isolate\";\n \n const countdown = document.createElement(\"div\");\n countdown.style.position = \"absolute\";\n countdown.style.left = \"12px\";\n countdown.style.top = \"12px\";\n countdown.style.padding = \"4px 8px\";\n countdown.style.borderRadius = \"4px\";\n countdown.style.background = \"rgba(0,0,0,0.75)\";\n countdown.style.color = \"#fff\";\n countdown.style.fontFamily = \"sans-serif\";\n countdown.style.fontSize = \"12px\";\n countdown.style.lineHeight = \"1.2\";\n countdown.style.pointerEvents = \"none\";\n countdown.style.zIndex = COUNTDOWN_Z_INDEX;\n countdown.textContent = \"Ad\";\n container.appendChild(countdown);\n\n contentVideo.parentElement?.appendChild(container);\n adContainerEl = container;\n adCountdownEl = countdown;\n }\n },\n\n async requestAds(duration?: string) {\n log(\"Requesting ads for duration:\", duration);\n \n if (adPlaying) {\n return Promise.reject(new Error(\"Ad already playing\"));\n }\n if (destroyed) {\n return Promise.reject(new Error(\"Player has been destroyed\"));\n }\n \n try {\n tornDown = false;\n let durationSeconds = 30;\n const parsed = parseInt(duration || \"\", 10);\n if (!isNaN(parsed) && parsed > 0) {\n durationSeconds = parsed;\n }\n \n const ads = await fetchVast(durationSeconds);\n \n if (ads.length === 0) {\n log(\"No ads available from VAST response\");\n emit(\"ad_error\");\n return Promise.resolve();\n }\n \n assignCurrentAd(ads[0]!);\n log(`Ad loaded: ${currentAd!.title}, duration: ${currentAd!.duration}s`);\n \n return Promise.resolve();\n } catch (error) {\n console.error(\"[AdStormPlayer] Error requesting ads:\", error);\n emit(\"ad_error\");\n return Promise.reject(error);\n }\n },\n\n async play() {\n if (!currentAd) {\n return Promise.reject(new Error(\"No ad loaded\"));\n }\n if (destroyed) {\n return Promise.reject(new Error(\"Player has been destroyed\"));\n }\n \n log(\"Starting ad playback\");\n \n try {\n tornDown = false;\n if (adHideTimerId) {\n clearTimeout(adHideTimerId);\n adHideTimerId = undefined;\n }\n\n if (!adVideoElement) {\n adVideoElement = createAdVideoElement();\n adContainerEl?.appendChild(adVideoElement);\n } else {\n teardownCurrentPlayback();\n }\n setupAdEventListeners();\n \n trackingFired = {\n impression: trackingFired.impression,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false,\n };\n \n contentVideo.style.transition = \"opacity 0.3s ease-in-out\";\n contentVideo.style.opacity = \"0\";\n setTimeout(() => {\n contentVideo.style.visibility = \"hidden\";\n }, 300);\n contentVideo.muted = true;\n contentVideo.volume = 0;\n \n if (!continueLiveStreamDuringAds) {\n contentVideo.pause();\n }\n \n adPlaying = true;\n setAdPlayingFlag(true);\n \n if (adVideoElement) {\n adVideoElement.volume = originalMutedState ? 0 : originalVolume;\n adVideoElement.muted = originalMutedState;\n }\n \n if (adContainerEl) {\n adContainerEl.style.display = \"flex\";\n adContainerEl.style.pointerEvents = \"auto\";\n adContainerEl.offsetHeight;\n adContainerEl.style.opacity = \"1\";\n }\n \n emit(\"content_pause\");\n \n const mediaFile = selectBestMediaFile(currentAd.mediaFiles);\n if (!mediaFile) {\n throw new Error(\"No media file available\");\n }\n \n log(\"Playing media file:\", mediaFile.url);\n adVideoElement!.src = mediaFile.url;\n adVideoElement!.load();\n \n await adVideoElement!.play();\n \n return Promise.resolve();\n } catch (error) {\n console.error(\"[AdStormPlayer] Error playing ad:\", error);\n handleAdError();\n return Promise.reject(error);\n }\n },\n\n async stop() {\n log(\"Stopping ad\");\n tornDown = true;\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n \n if (adContainerEl) {\n adContainerEl.style.opacity = \"0\";\n adHideTimerId = setTimeout(() => {\n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n }, 300);\n }\n \n teardownCurrentPlayback();\n \n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n \n currentAd = undefined;\n return Promise.resolve();\n },\n\n pause() {\n if (!adPlaying || !adVideoElement) return;\n try {\n if (!adVideoElement.paused) adVideoElement.pause();\n } catch (error) {\n console.warn(\"[AdStormPlayer] Error pausing ad:\", error);\n }\n },\n\n resume() {\n if (!adPlaying || !adVideoElement) return;\n try {\n if (adVideoElement.paused) adVideoElement.play().catch(() => {});\n } catch (error) {\n console.warn(\"[AdStormPlayer] Error resuming ad:\", error);\n }\n },\n\n destroy() {\n log(\"Destroying\");\n destroyed = true;\n tornDown = true;\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n if (adHideTimerId) {\n clearTimeout(adHideTimerId);\n adHideTimerId = undefined;\n }\n \n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n \n teardownCurrentPlayback();\n adVideoElement?.remove();\n adVideoElement = undefined;\n \n if (adContainerEl?.parentElement) {\n adContainerEl.parentElement.removeChild(adContainerEl);\n }\n \n adContainerEl = undefined;\n adCountdownEl = undefined;\n currentAd = undefined;\n sessionId = undefined;\n preloadSlots.clear();\n listeners.clear();\n if (parentPositionOverridden && contentVideo.parentElement) {\n contentVideo.parentElement.style.position = \"\";\n parentPositionOverridden = false;\n }\n },\n\n updateOptions(opts: AdStormLayerOptionsUpdate) {\n if (opts.continueLiveStreamDuringAds !== undefined) {\n continueLiveStreamDuringAds = opts.continueLiveStreamDuringAds;\n }\n },\n\n async playAd(requestContext?: unknown) {\n if (destroyed) return Promise.reject(new Error(\"Player has been destroyed\"));\n if (!currentAd) {\n const ad = await requestAdFromApi(requestContext);\n if (!ad) {\n emit(\"ad_error\", { message: \"No valid ad from AdStorm API\" });\n return Promise.reject(new Error(\"No valid ad from AdStorm API\"));\n }\n assignCurrentAd(ad);\n }\n return this.play();\n },\n\n async preloadAd(arg1: unknown, arg2?: unknown) {\n if (destroyed) return;\n const token =\n typeof arg1 === \"string\"\n ? arg1\n : typeof arg2 === \"string\"\n ? arg2\n : undefined;\n if (!token) return;\n\n if (currentAd) {\n preloadSlots.set(token, { ad: currentAd });\n currentAd = undefined;\n return;\n }\n\n const requestContext = typeof arg1 === \"string\" ? arg2 : arg1;\n const ad = await requestAdFromApi(requestContext);\n if (!ad) return;\n preloadSlots.set(token, { ad });\n },\n\n async playPreloaded(token: string) {\n if (destroyed) return Promise.reject(new Error(\"Player has been destroyed\"));\n const slot = preloadSlots.get(token);\n if (!slot) {\n return Promise.reject(new Error(`No preloaded ad for token ${token}`));\n }\n preloadSlots.delete(token);\n assignCurrentAd(slot.ad);\n return this.play();\n },\n\n hasPreloaded(token: string): boolean {\n return preloadSlots.has(token);\n },\n\n cancelPreload(token: string) {\n preloadSlots.delete(token);\n },\n\n isAdPlaying() {\n return adPlaying;\n },\n\n resize(width: number, height: number) {\n log(`Resizing to ${width}x${height}`);\n \n if (adContainerEl) {\n adContainerEl.style.width = `${width}px`;\n adContainerEl.style.height = `${height}px`;\n }\n \n if (adVideoElement) {\n adVideoElement.style.width = `${width}px`;\n adVideoElement.style.height = `${height}px`;\n }\n },\n\n on(event: string, listener: (payload?: any) => void) {\n if (!listeners.has(event)) listeners.set(event, new Set());\n listeners.get(event)!.add(listener);\n },\n\n off(event: string, listener: (payload?: any) => void) {\n listeners.get(event)?.delete(listener);\n },\n\n updateOriginalMutedState(muted: boolean, volume?: number) {\n const nextVolume =\n typeof volume === \"number\" && !Number.isNaN(volume)\n ? Math.max(0, Math.min(1, volume))\n : originalVolume;\n log(`updateOriginalMutedState: muted=${muted}, volume=${nextVolume}`);\n originalMutedState = muted;\n originalVolume = nextVolume;\n },\n\n getOriginalMutedState() {\n return originalMutedState;\n },\n\n getOriginalVolume() {\n return originalVolume;\n },\n\n setAdVolume(volume: number) {\n if (adVideoElement && adPlaying) {\n adVideoElement.volume = Math.max(0, Math.min(1, volume));\n adVideoElement.muted = volume === 0;\n }\n },\n\n getAdVolume(): number {\n if (adVideoElement && adPlaying) {\n return adVideoElement.volume;\n }\n return 1;\n },\n\n showPlaceholder() {\n if (!adContainerEl) {\n const parent = contentVideo.parentElement;\n if (parent) {\n const computed = window.getComputedStyle(parent).position;\n if (computed === \"static\") {\n parent.style.position = \"relative\";\n parentPositionOverridden = true;\n }\n }\n\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.left = \"0\";\n container.style.top = \"0\";\n container.style.right = \"0\";\n container.style.bottom = \"0\";\n container.style.display = \"none\";\n container.style.alignItems = \"center\";\n container.style.justifyContent = \"center\";\n container.style.pointerEvents = \"none\";\n container.style.zIndex = AD_LAYER_Z_INDEX;\n container.style.backgroundColor = \"#000\";\n container.style.isolation = \"isolate\";\n\n const countdown = document.createElement(\"div\");\n countdown.style.position = \"absolute\";\n countdown.style.left = \"12px\";\n countdown.style.top = \"12px\";\n countdown.style.padding = \"4px 8px\";\n countdown.style.borderRadius = \"4px\";\n countdown.style.background = \"rgba(0,0,0,0.75)\";\n countdown.style.color = \"#fff\";\n countdown.style.fontFamily = \"sans-serif\";\n countdown.style.fontSize = \"12px\";\n countdown.style.lineHeight = \"1.2\";\n countdown.style.pointerEvents = \"none\";\n countdown.style.zIndex = COUNTDOWN_Z_INDEX;\n countdown.textContent = \"Ad\";\n container.appendChild(countdown);\n \n contentVideo.parentElement?.appendChild(container);\n adContainerEl = container;\n adCountdownEl = countdown;\n }\n \n if (adContainerEl) {\n adContainerEl.style.display = \"flex\";\n adContainerEl.style.opacity = \"1\";\n adContainerEl.style.pointerEvents = \"auto\";\n }\n },\n\n hidePlaceholder() {\n if (adContainerEl) {\n adContainerEl.style.opacity = \"0\";\n setTimeout(() => {\n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n }, 300);\n }\n },\n };\n}\n\n","export interface VastMediaFile {\n url: string;\n type: string;\n width: number;\n height: number;\n bitrate?: number | undefined;\n}\n\nexport interface VastTrackingUrls {\n impression: string[];\n start: string[];\n firstQuartile: string[];\n midpoint: string[];\n thirdQuartile: string[];\n complete: string[];\n mute: string[];\n unmute: string[];\n pause: string[];\n resume: string[];\n fullscreen: string[];\n exitFullscreen: string[];\n skip: string[];\n error: string[];\n}\n\nexport interface VastAd {\n id: string;\n title: string;\n duration: number;\n mediaFiles: VastMediaFile[];\n trackingUrls: VastTrackingUrls;\n clickThrough?: string | undefined;\n}\n\nexport type MediaFileFilter = \"hls-only\" | \"mp4-first\" | \"all\";\n\nfunction isHlsType(type: string): boolean {\n return type === \"application/x-mpegURL\" || type.includes(\"m3u8\");\n}\n\nfunction isMp4Type(type: string): boolean {\n return type === \"video/mp4\" || type.includes(\"mp4\");\n}\n\nexport function parseVastXml(\n xmlString: string,\n filter: MediaFileFilter = \"all\",\n logPrefix = \"[VastParser]\"\n): VastAd | null {\n try {\n const parser = new DOMParser();\n const xmlDoc = parser.parseFromString(xmlString, \"text/xml\");\n\n const parserError = xmlDoc.querySelector(\"parsererror\");\n if (parserError) {\n console.error(\n `${logPrefix} XML parsing error (malformed VAST XML):`,\n parserError.textContent\n );\n return null;\n }\n\n const adElement = xmlDoc.querySelector(\"Ad\");\n if (!adElement) {\n console.warn(`${logPrefix} No Ad element found in VAST XML`);\n return null;\n }\n\n const adId = adElement.getAttribute(\"id\") || \"unknown\";\n const title = xmlDoc.querySelector(\"AdTitle\")?.textContent || \"Ad\";\n\n const isNoAdAvailable =\n adId === \"empty\" ||\n title.toLowerCase().includes(\"no ad available\") ||\n title.toLowerCase() === \"no ad available\";\n\n const durationText =\n xmlDoc.querySelector(\"Duration\")?.textContent || \"00:00:30\";\n const durationParts = durationText.split(\":\");\n const duration =\n parseInt(durationParts[0] || \"0\", 10) * 3600 +\n parseInt(durationParts[1] || \"0\", 10) * 60 +\n Math.round(parseFloat(durationParts[2] || \"0\"));\n\n const mediaFileElements = xmlDoc.querySelectorAll(\"MediaFile\");\n const mediaFiles: VastMediaFile[] = [];\n\n console.log(\n `${logPrefix} Found ${mediaFileElements.length} MediaFile element(s) in VAST XML`\n );\n\n mediaFileElements.forEach((mf, index) => {\n const type = mf.getAttribute(\"type\") || \"\";\n const url = mf.textContent?.trim() || \"\";\n const width = mf.getAttribute(\"width\") || \"\";\n const height = mf.getAttribute(\"height\") || \"\";\n\n console.log(\n `${logPrefix} MediaFile ${index}: type=\"${type}\", url=\"${url.substring(0, 80)}...\", width=\"${width}\", height=\"${height}\"`\n );\n\n if (!url) {\n console.warn(`${logPrefix} MediaFile ${index} has empty URL`);\n return;\n }\n\n const isHls = isHlsType(type);\n const isMp4 = isMp4Type(type);\n\n let accepted = false;\n if (filter === \"hls-only\") {\n accepted = isHls;\n } else if (filter === \"mp4-first\") {\n accepted = isMp4 || isHls;\n } else {\n accepted = true;\n }\n\n if (!accepted) {\n console.log(\n `${logPrefix} MediaFile ${index} ignored (type=\"${type}\" not accepted by filter \"${filter}\")`\n );\n return;\n }\n\n const bitrateAttr = mf.getAttribute(\"bitrate\");\n const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : undefined;\n\n mediaFiles.push({\n url,\n type,\n width: parseInt(width || \"1920\", 10),\n height: parseInt(height || \"1080\", 10),\n bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : undefined,\n });\n\n console.log(`${logPrefix} Added MediaFile: type=\"${type}\" url=\"${url.substring(0, 80)}...\"`);\n });\n\n if (filter === \"mp4-first\" && mediaFiles.length > 1) {\n mediaFiles.sort((a, b) => {\n const aIsMp4 = isMp4Type(a.type) ? 0 : 1;\n const bIsMp4 = isMp4Type(b.type) ? 0 : 1;\n return aIsMp4 - bIsMp4;\n });\n }\n\n if (mediaFiles.length === 0) {\n if (isNoAdAvailable) {\n console.warn(\n `${logPrefix} No ads available (VAST response indicates no ads)`\n );\n } else {\n console.warn(`${logPrefix} No compatible media files found in VAST XML`);\n }\n return null;\n }\n\n const trackingUrls: VastTrackingUrls = {\n impression: [],\n start: [],\n firstQuartile: [],\n midpoint: [],\n thirdQuartile: [],\n complete: [],\n mute: [],\n unmute: [],\n pause: [],\n resume: [],\n fullscreen: [],\n exitFullscreen: [],\n skip: [],\n error: [],\n };\n\n xmlDoc.querySelectorAll(\"Impression\").forEach((el) => {\n const url = el.textContent?.trim();\n if (url) trackingUrls.impression.push(url);\n });\n\n xmlDoc.querySelectorAll(\"Tracking\").forEach((el) => {\n const event = el.getAttribute(\"event\");\n const url = el.textContent?.trim();\n if (event && url) {\n const eventKey = event as keyof VastTrackingUrls;\n if (trackingUrls[eventKey]) {\n trackingUrls[eventKey].push(url);\n }\n }\n });\n\n const clickThrough = xmlDoc\n .querySelector(\"ClickThrough\")\n ?.textContent?.trim();\n\n return {\n id: adId,\n title,\n duration,\n mediaFiles,\n trackingUrls,\n clickThrough,\n };\n } catch (error) {\n console.error(`${logPrefix} Error parsing VAST XML:`, error);\n return null;\n }\n}\n\nexport async function fetchAndParseVastAd(\n vastTagUrl: string,\n filter: MediaFileFilter = \"all\",\n logPrefix = \"[VastParser]\"\n): Promise<VastAd | null> {\n const response = await fetch(vastTagUrl, {\n mode: \"cors\",\n credentials: \"include\",\n headers: {\n Accept: \"application/xml, text/xml, */*\",\n },\n referrerPolicy: \"no-referrer-when-downgrade\",\n });\n if (!response.ok) {\n throw new Error(`Failed to fetch VAST: ${response.statusText}`);\n }\n\n const vastXml = await response.text();\n console.log(`${logPrefix} VAST XML received`);\n console.log(\n `${logPrefix} VAST XML content (first 2000 chars):`,\n vastXml.substring(0, 2000)\n );\n\n return parseVastXml(vastXml, filter, logPrefix);\n}\n\nexport function createEmptyTrackingState() {\n return {\n impression: false,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false,\n };\n}\n\nasync function firePixelWithRetry(\n url: string,\n retries = 2,\n delayMs = 500,\n logPrefix = \"[VastParser]\"\n): Promise<void> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n await fetch(url, {\n method: \"GET\",\n mode: \"no-cors\",\n cache: \"no-cache\",\n keepalive: true,\n });\n return;\n } catch {\n if (attempt < retries) {\n await new Promise((r) => setTimeout(r, delayMs * Math.pow(2, attempt)));\n } else {\n console.warn(`${logPrefix} Tracking pixel failed after ${retries + 1} attempts: ${url}`);\n }\n }\n }\n}\n\nexport function fireTrackingPixels(\n urls: string[],\n sessionId?: string,\n logPrefix = \"[VastParser]\"\n): void {\n if (!urls || urls.length === 0) return;\n\n urls.forEach((url) => {\n try {\n let trackingUrl = url;\n\n if (sessionId) {\n trackingUrl = `${trackingUrl}${\n trackingUrl.includes(\"?\") ? \"&\" : \"?\"\n }session_id=${sessionId}`;\n }\n\n if (typeof fetch !== \"undefined\") {\n firePixelWithRetry(trackingUrl, 2, 500, logPrefix).catch(() => {});\n } else {\n const img = new Image(1, 1);\n img.onerror = () => {};\n img.src = trackingUrl;\n }\n\n console.log(`${logPrefix} Fired tracking pixel: ${trackingUrl}`);\n } catch (error) {\n console.warn(`${logPrefix} Error firing tracking pixel:`, error);\n }\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/sdk/adstormPlayer.cjs"],"names":[],"mappings":"AAAA","sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/sdk/adstormPlayer.ts\nvar adstormPlayer_exports = {};\n__export(adstormPlayer_exports, {\n createAdStormPlayer: () => createAdStormPlayer\n});\nmodule.exports = __toCommonJS(adstormPlayer_exports);\n\n// src/sdk/vastParser.ts\nasync function firePixelWithRetry(url, retries = 2, delayMs = 500, logPrefix = \"[VastParser]\") {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n await fetch(url, {\n method: \"GET\",\n mode: \"no-cors\",\n cache: \"no-cache\",\n keepalive: true\n });\n return;\n } catch {\n if (attempt < retries) {\n await new Promise((r) => setTimeout(r, delayMs * Math.pow(2, attempt)));\n } else {\n console.warn(`${logPrefix} Tracking pixel failed after ${retries + 1} attempts: ${url}`);\n }\n }\n }\n}\nfunction fireTrackingPixels(urls, sessionId, logPrefix = \"[VastParser]\") {\n if (!urls || urls.length === 0) return;\n urls.forEach((url) => {\n try {\n let trackingUrl = url;\n if (sessionId) {\n trackingUrl = `${trackingUrl}${trackingUrl.includes(\"?\") ? \"&\" : \"?\"}session_id=${sessionId}`;\n }\n if (typeof fetch !== \"undefined\") {\n firePixelWithRetry(trackingUrl, 2, 500, logPrefix).catch(() => {\n });\n } else {\n const img = new Image(1, 1);\n img.onerror = () => {\n };\n img.src = trackingUrl;\n }\n console.log(`${logPrefix} Fired tracking pixel: ${trackingUrl}`);\n } catch (error) {\n console.warn(`${logPrefix} Error firing tracking pixel:`, error);\n }\n });\n}\n\n// src/sdk/adstormPlayer.ts\nvar SUPPORTED_VIDEO_EXTENSIONS = [\".mp4\", \".webm\", \".ogg\", \".m3u8\", \".ts\"];\nvar UNSUPPORTED_VIDEO_EXTENSIONS = [\".flv\", \".f4v\", \".swf\", \".wmv\", \".avi\", \".mov\", \".mkv\"];\nvar REQUEST_TIMEOUT_MS = 5e3;\nvar REQUEST_MAX_RETRIES = 3;\nvar REQUEST_RETRY_BACKOFF_MS = 1500;\nvar AD_LAYER_Z_INDEX = \"30\";\nvar COUNTDOWN_Z_INDEX = \"31\";\nvar STALL_TIMEOUT_MS = 8e3;\nfunction getFileExtension(url) {\n try {\n const pathname = new URL(url, \"http://dummy\").pathname;\n const lastDot = pathname.lastIndexOf(\".\");\n if (lastDot === -1) return \"\";\n return pathname.slice(lastDot).toLowerCase();\n } catch {\n const lastDot = url.lastIndexOf(\".\");\n if (lastDot === -1) return \"\";\n const ext = url.slice(lastDot).split(/[?#]/)[0];\n return (ext || \"\").toLowerCase();\n }\n}\nfunction isUnsupportedFormat(url) {\n const ext = getFileExtension(url);\n return UNSUPPORTED_VIDEO_EXTENSIONS.indexOf(ext) !== -1;\n}\nfunction replaceFlvExtension(url) {\n const ext = getFileExtension(url);\n if (ext === \".flv\") {\n return url.replace(/\\.flv(\\?|$)/i, \".mp4$1\");\n }\n return url;\n}\nfunction isSupportedFormat(url, mimeType) {\n if (isUnsupportedFormat(url)) {\n return false;\n }\n const ext = getFileExtension(url);\n if (SUPPORTED_VIDEO_EXTENSIONS.indexOf(ext) !== -1) {\n return true;\n }\n if (ext === \"\" || ext === \".\") {\n return mimeType.includes(\"video/mp4\") || mimeType.includes(\"video/webm\") || mimeType.includes(\"m3u8\") || mimeType.includes(\"application/x-mpegurl\");\n }\n return false;\n}\nfunction createAdStormPlayer(contentVideo, options) {\n const { licenseKey, debug = false } = options;\n let adPlaying = false;\n let originalMutedState = false;\n let originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));\n const listeners = /* @__PURE__ */ new Map();\n let adVideoElement;\n let adContainerEl;\n let adCountdownEl;\n let currentAd;\n let destroyed = false;\n let tornDown = false;\n let continueLiveStreamDuringAds = false;\n let sessionId;\n let adStallTimerId;\n let adCountdownTimerId;\n let adHideTimerId;\n let lastCountdownSecond = -1;\n let adListenersBound = false;\n let parentPositionOverridden = false;\n const adHandlers = {\n timeupdate: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n const progress = adVideoElement.currentTime / currentAd.duration;\n if (progress >= 0.25 && !trackingFired.firstQuartile) {\n trackingFired.firstQuartile = true;\n fireTrackingPixels2(currentAd.trackingUrls.firstQuartile);\n }\n if (progress >= 0.5 && !trackingFired.midpoint) {\n trackingFired.midpoint = true;\n fireTrackingPixels2(currentAd.trackingUrls.midpoint);\n }\n if (progress >= 0.75 && !trackingFired.thirdQuartile) {\n trackingFired.thirdQuartile = true;\n fireTrackingPixels2(currentAd.trackingUrls.thirdQuartile);\n }\n updateAdCountdown();\n },\n playing: () => {\n clearAdStallTimer();\n if (!currentAd || trackingFired.start || destroyed || tornDown) return;\n trackingFired.start = true;\n fireTrackingPixels2(currentAd.trackingUrls.start);\n startAdCountdown();\n log(\"Ad started playing\");\n },\n ended: () => {\n if (!currentAd || trackingFired.complete || destroyed || tornDown) return;\n trackingFired.complete = true;\n fireTrackingPixels2(currentAd.trackingUrls.complete);\n log(\"Ad completed\");\n handleAdComplete();\n },\n error: (e) => {\n if (destroyed || tornDown) return;\n console.error(\"[AdStormPlayer] Ad video error:\", e);\n if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);\n handleAdError();\n },\n waiting: () => {\n clearAdStallTimer();\n adStallTimerId = setTimeout(() => {\n adStallTimerId = void 0;\n if (!adPlaying || destroyed || tornDown) return;\n console.warn(\"[AdStormPlayer] Ad playback stalled too long\");\n handleAdError();\n }, STALL_TIMEOUT_MS);\n },\n volumechange: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n if (adVideoElement.muted || adVideoElement.volume <= 0) {\n fireTrackingPixels2(currentAd.trackingUrls.mute);\n } else {\n fireTrackingPixels2(currentAd.trackingUrls.unmute);\n }\n },\n pause: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n if (!adVideoElement.ended) {\n fireTrackingPixels2(currentAd.trackingUrls.pause);\n }\n },\n play: () => {\n if (!currentAd || !adVideoElement || destroyed || tornDown) return;\n if (adVideoElement.currentTime > 0) {\n fireTrackingPixels2(currentAd.trackingUrls.resume);\n }\n }\n };\n let trackingFired = {\n impression: false,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false\n };\n const preloadSlots = /* @__PURE__ */ new Map();\n function log(...args) {\n if (debug) {\n console.log(\"[AdStormPlayer]\", ...args);\n }\n }\n function emit(event, payload) {\n const set = listeners.get(event);\n if (!set) return;\n for (const fn of Array.from(set)) {\n try {\n fn(payload);\n } catch (error) {\n console.warn(`[AdStormPlayer] Error in event listener for ${event}:`, error);\n }\n }\n }\n function fireTrackingPixels2(urls) {\n fireTrackingPixels(urls, sessionId, \"[AdStormPlayer]\");\n }\n function clearAdStallTimer() {\n if (adStallTimerId) {\n clearTimeout(adStallTimerId);\n adStallTimerId = void 0;\n }\n }\n function clearAdCountdownTimer() {\n if (adCountdownTimerId) {\n clearInterval(adCountdownTimerId);\n adCountdownTimerId = void 0;\n }\n lastCountdownSecond = -1;\n }\n function updateAdCountdown() {\n if (!adCountdownEl || !adVideoElement || !currentAd || !adPlaying) return;\n const remainingSec = Math.max(\n 0,\n Math.ceil((currentAd.duration || 0) - adVideoElement.currentTime)\n );\n if (remainingSec === lastCountdownSecond) return;\n lastCountdownSecond = remainingSec;\n adCountdownEl.textContent = `Ad ${remainingSec}s`;\n emit(\"ad_countdown\", {\n remainingSec,\n durationSec: currentAd.duration,\n currentTimeSec: adVideoElement.currentTime\n });\n }\n function startAdCountdown() {\n clearAdCountdownTimer();\n updateAdCountdown();\n adCountdownTimerId = setInterval(updateAdCountdown, 250);\n }\n function generateSessionId() {\n return `adstorm-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n }\n function bindAdEventListeners() {\n if (!adVideoElement || adListenersBound) return;\n adVideoElement.addEventListener(\"timeupdate\", adHandlers.timeupdate);\n adVideoElement.addEventListener(\"playing\", adHandlers.playing);\n adVideoElement.addEventListener(\"ended\", adHandlers.ended);\n adVideoElement.addEventListener(\"error\", adHandlers.error);\n adVideoElement.addEventListener(\"waiting\", adHandlers.waiting);\n adVideoElement.addEventListener(\"volumechange\", adHandlers.volumechange);\n adVideoElement.addEventListener(\"pause\", adHandlers.pause);\n adVideoElement.addEventListener(\"play\", adHandlers.play);\n adListenersBound = true;\n }\n function unbindAdEventListeners() {\n if (!adVideoElement || !adListenersBound) return;\n adVideoElement.removeEventListener(\"timeupdate\", adHandlers.timeupdate);\n adVideoElement.removeEventListener(\"playing\", adHandlers.playing);\n adVideoElement.removeEventListener(\"ended\", adHandlers.ended);\n adVideoElement.removeEventListener(\"error\", adHandlers.error);\n adVideoElement.removeEventListener(\"waiting\", adHandlers.waiting);\n adVideoElement.removeEventListener(\"volumechange\", adHandlers.volumechange);\n adVideoElement.removeEventListener(\"pause\", adHandlers.pause);\n adVideoElement.removeEventListener(\"play\", adHandlers.play);\n adListenersBound = false;\n }\n function teardownCurrentPlayback() {\n unbindAdEventListeners();\n clearAdStallTimer();\n clearAdCountdownTimer();\n if (!adVideoElement) return;\n adVideoElement.pause();\n adVideoElement.removeAttribute(\"src\");\n adVideoElement.load();\n }\n function buildVastUrl(durationSeconds) {\n const baseUrl = `https://adstorm.co/api-adstorm-dev/adstorm/nab/vast/pod`;\n return `${baseUrl}?duration=${Math.ceil(durationSeconds)}`;\n }\n function parseVastXml(xmlString) {\n const ads = [];\n try {\n const parser = new DOMParser();\n const xmlDoc = parser.parseFromString(xmlString, \"text/xml\");\n const parserError = xmlDoc.querySelector(\"parsererror\");\n if (parserError) {\n console.error(\"[AdStormPlayer] XML parsing error:\", parserError.textContent);\n return [];\n }\n const adElements = xmlDoc.querySelectorAll(\"Ad\");\n adElements.forEach((adElement) => {\n const adId = adElement.getAttribute(\"id\") || \"unknown\";\n const title = adElement.querySelector(\"AdTitle\")?.textContent || \"Ad\";\n const durationText = adElement.querySelector(\"Duration\")?.textContent || \"00:00:30\";\n const durationParts = durationText.split(\":\");\n const duration = parseInt(durationParts[0] || \"0\", 10) * 3600 + parseInt(durationParts[1] || \"0\", 10) * 60 + parseFloat(durationParts[2] || \"0\");\n const mediaFileElements = adElement.querySelectorAll(\"MediaFile\");\n const mediaFiles = [];\n mediaFileElements.forEach((mf) => {\n const type = mf.getAttribute(\"type\") || \"\";\n let url = mf.textContent?.trim() || \"\";\n const width = parseInt(mf.getAttribute(\"width\") || \"1920\", 10);\n const height = parseInt(mf.getAttribute(\"height\") || \"1080\", 10);\n const bitrate = mf.getAttribute(\"bitrate\") ? parseInt(mf.getAttribute(\"bitrate\"), 10) : void 0;\n if (!url) {\n log(`Skipping empty MediaFile URL`);\n return;\n }\n const originalUrl = url;\n url = replaceFlvExtension(url);\n if (url !== originalUrl) {\n log(`Converted FLV to MP4: ${originalUrl} -> ${url}`);\n }\n if (isUnsupportedFormat(url)) {\n const ext = getFileExtension(url);\n log(`Skipping unsupported format: ${url} (extension: ${ext}, declared type: ${type})`);\n return;\n }\n if (isSupportedFormat(url, type)) {\n mediaFiles.push({ url, type, width, height, bitrate });\n log(`Found media file: ${url} (${type}, ${width}x${height})`);\n } else {\n log(`Skipping incompatible media file: ${url} (type: ${type})`);\n }\n });\n if (mediaFiles.length === 0) {\n log(\"No valid media files found in ad:\", adId);\n return;\n }\n const trackingUrls = {\n impression: [],\n start: [],\n firstQuartile: [],\n midpoint: [],\n thirdQuartile: [],\n complete: [],\n mute: [],\n unmute: [],\n pause: [],\n resume: [],\n error: []\n };\n adElement.querySelectorAll(\"Impression\").forEach((el) => {\n const url = el.textContent?.trim();\n if (url) trackingUrls.impression.push(url);\n });\n adElement.querySelectorAll(\"Tracking\").forEach((el) => {\n const event = el.getAttribute(\"event\");\n const url = el.textContent?.trim();\n if (event && url) {\n const eventKey = event;\n if (trackingUrls[eventKey]) {\n trackingUrls[eventKey].push(url);\n }\n }\n });\n const clickThrough = adElement.querySelector(\"ClickThrough\")?.textContent?.trim();\n ads.push({\n id: adId,\n title,\n duration,\n mediaFiles,\n trackingUrls,\n clickThrough\n });\n log(`Parsed ad: ${title}, duration: ${duration}s, media files: ${mediaFiles.length}`);\n });\n } catch (error) {\n console.error(\"[AdStormPlayer] Error parsing VAST XML:\", error);\n }\n return ads;\n }\n function selectBestMediaFile(mediaFiles) {\n if (mediaFiles.length === 0) return null;\n if (mediaFiles.length === 1) return mediaFiles[0];\n const mp4Files = mediaFiles.filter((mf) => mf.type.includes(\"video/mp4\"));\n const candidates = mp4Files.length > 0 ? mp4Files : mediaFiles;\n const targetWidth = contentVideo.videoWidth || 1280;\n const targetHeight = contentVideo.videoHeight || 720;\n candidates.sort((a, b) => {\n const diffA = Math.abs(a.width - targetWidth) + Math.abs(a.height - targetHeight);\n const diffB = Math.abs(b.width - targetWidth) + Math.abs(b.height - targetHeight);\n return diffA - diffB;\n });\n return candidates[0] || null;\n }\n function createAdVideoElement() {\n const video = document.createElement(\"video\");\n video.style.position = \"absolute\";\n video.style.left = \"0\";\n video.style.top = \"0\";\n video.style.width = \"100%\";\n video.style.height = \"100%\";\n video.style.objectFit = \"contain\";\n video.style.backgroundColor = \"#000\";\n video.style.zIndex = \"1\";\n video.playsInline = true;\n video.preload = \"auto\";\n video.muted = originalMutedState;\n video.volume = originalMutedState ? 0 : originalVolume;\n return video;\n }\n function setAdPlayingFlag(isPlaying) {\n if (isPlaying) {\n contentVideo.dataset.stormcloudAdPlaying = \"true\";\n } else {\n delete contentVideo.dataset.stormcloudAdPlaying;\n }\n }\n function setupAdEventListeners() {\n bindAdEventListeners();\n }\n function handleAdComplete() {\n if (destroyed || tornDown) return;\n log(\"Handling ad completion\");\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n if (adContainerEl) {\n adContainerEl.style.opacity = \"0\";\n adHideTimerId = setTimeout(() => {\n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n }, 300);\n }\n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n currentAd = void 0;\n emit(\"content_resume\");\n emit(\"all_ads_completed\");\n }\n function handleAdError() {\n if (destroyed || tornDown) return;\n log(\"Handling ad error\");\n if (!adPlaying) return;\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n currentAd = void 0;\n emit(\"ad_error\");\n emit(\"content_resume\");\n }\n async function fetchVastOnce(durationSeconds) {\n const vastUrl = buildVastUrl(durationSeconds);\n log(\"Fetching VAST from:\", vastUrl);\n const controller = typeof AbortController !== \"undefined\" ? new AbortController() : null;\n const timeoutId = setTimeout(() => controller?.abort(), REQUEST_TIMEOUT_MS);\n try {\n const requestInit = {\n method: \"GET\",\n mode: \"cors\",\n credentials: \"omit\",\n headers: {\n Accept: \"application/xml, text/xml, */*\"\n },\n referrerPolicy: \"no-referrer-when-downgrade\"\n };\n if (controller) {\n requestInit.signal = controller.signal;\n }\n const response = await fetch(vastUrl, requestInit);\n if (!response.ok) {\n throw new Error(`Failed to fetch VAST: ${response.status} ${response.statusText}`);\n }\n const xmlText = await response.text();\n log(\"VAST response received, length:\", xmlText.length);\n return parseVastXml(xmlText);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n async function fetchVast(durationSeconds) {\n let lastError;\n for (let attempt = 1; attempt <= REQUEST_MAX_RETRIES; attempt++) {\n try {\n const ads = await fetchVastOnce(durationSeconds);\n if (ads.length > 0) return ads;\n log(`No ad returned from VAST on attempt ${attempt}/${REQUEST_MAX_RETRIES}`);\n } catch (error) {\n lastError = error;\n if (error?.name === \"AbortError\") {\n console.warn(\n `[AdStormPlayer] VAST request timed out (${REQUEST_TIMEOUT_MS}ms), attempt ${attempt}/${REQUEST_MAX_RETRIES}`\n );\n } else {\n console.warn(`[AdStormPlayer] VAST request failed on attempt ${attempt}/${REQUEST_MAX_RETRIES}:`, error);\n }\n }\n if (attempt < REQUEST_MAX_RETRIES) {\n const delay = REQUEST_RETRY_BACKOFF_MS * attempt;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n if (lastError instanceof Error) {\n throw lastError;\n }\n return [];\n }\n function getDurationSecondsFromContext(requestContext) {\n if (!requestContext || typeof requestContext !== \"object\") {\n return 30;\n }\n const ctx = requestContext;\n const value = ctx.remainingBreakSec ?? ctx.breakDurationSec;\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n return 30;\n }\n return Math.max(1, Math.ceil(value));\n }\n async function requestAdFromApi(requestContext) {\n const durationSeconds = getDurationSecondsFromContext(requestContext);\n const ads = await fetchVast(durationSeconds);\n return ads[0] || null;\n }\n function assignCurrentAd(ad) {\n currentAd = ad;\n sessionId = generateSessionId();\n trackingFired = {\n impression: false,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false\n };\n fireTrackingPixels2(currentAd.trackingUrls.impression);\n trackingFired.impression = true;\n emit(\"ad_impression\");\n }\n return {\n initialize() {\n log(\"Initializing\");\n if (!adContainerEl) {\n const parent = contentVideo.parentElement;\n if (parent) {\n const computed = window.getComputedStyle(parent).position;\n if (computed === \"static\") {\n parent.style.position = \"relative\";\n parentPositionOverridden = true;\n }\n }\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.left = \"0\";\n container.style.top = \"0\";\n container.style.right = \"0\";\n container.style.bottom = \"0\";\n container.style.display = \"none\";\n container.style.alignItems = \"center\";\n container.style.justifyContent = \"center\";\n container.style.pointerEvents = \"none\";\n container.style.zIndex = AD_LAYER_Z_INDEX;\n container.style.backgroundColor = \"#000\";\n container.style.transition = \"opacity 0.3s ease-in-out\";\n container.style.opacity = \"0\";\n container.style.isolation = \"isolate\";\n const countdown = document.createElement(\"div\");\n countdown.style.position = \"absolute\";\n countdown.style.left = \"12px\";\n countdown.style.top = \"12px\";\n countdown.style.padding = \"4px 8px\";\n countdown.style.borderRadius = \"4px\";\n countdown.style.background = \"rgba(0,0,0,0.75)\";\n countdown.style.color = \"#fff\";\n countdown.style.fontFamily = \"sans-serif\";\n countdown.style.fontSize = \"12px\";\n countdown.style.lineHeight = \"1.2\";\n countdown.style.pointerEvents = \"none\";\n countdown.style.zIndex = COUNTDOWN_Z_INDEX;\n countdown.textContent = \"Ad\";\n container.appendChild(countdown);\n contentVideo.parentElement?.appendChild(container);\n adContainerEl = container;\n adCountdownEl = countdown;\n }\n },\n async requestAds(duration) {\n log(\"Requesting ads for duration:\", duration);\n if (adPlaying) {\n return Promise.reject(new Error(\"Ad already playing\"));\n }\n if (destroyed) {\n return Promise.reject(new Error(\"Player has been destroyed\"));\n }\n try {\n tornDown = false;\n let durationSeconds = 30;\n const parsed = parseInt(duration || \"\", 10);\n if (!isNaN(parsed) && parsed > 0) {\n durationSeconds = parsed;\n }\n const ads = await fetchVast(durationSeconds);\n if (ads.length === 0) {\n log(\"No ads available from VAST response\");\n emit(\"ad_error\");\n return Promise.resolve();\n }\n assignCurrentAd(ads[0]);\n log(`Ad loaded: ${currentAd.title}, duration: ${currentAd.duration}s`);\n return Promise.resolve();\n } catch (error) {\n console.error(\"[AdStormPlayer] Error requesting ads:\", error);\n emit(\"ad_error\");\n return Promise.reject(error);\n }\n },\n async play() {\n if (!currentAd) {\n return Promise.reject(new Error(\"No ad loaded\"));\n }\n if (destroyed) {\n return Promise.reject(new Error(\"Player has been destroyed\"));\n }\n log(\"Starting ad playback\");\n try {\n tornDown = false;\n if (adHideTimerId) {\n clearTimeout(adHideTimerId);\n adHideTimerId = void 0;\n }\n if (!adVideoElement) {\n adVideoElement = createAdVideoElement();\n adContainerEl?.appendChild(adVideoElement);\n } else {\n teardownCurrentPlayback();\n }\n setupAdEventListeners();\n trackingFired = {\n impression: trackingFired.impression,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false\n };\n contentVideo.style.transition = \"opacity 0.3s ease-in-out\";\n contentVideo.style.opacity = \"0\";\n setTimeout(() => {\n contentVideo.style.visibility = \"hidden\";\n }, 300);\n contentVideo.muted = true;\n contentVideo.volume = 0;\n if (!continueLiveStreamDuringAds) {\n contentVideo.pause();\n }\n adPlaying = true;\n setAdPlayingFlag(true);\n if (adVideoElement) {\n adVideoElement.volume = originalMutedState ? 0 : originalVolume;\n adVideoElement.muted = originalMutedState;\n }\n if (adContainerEl) {\n adContainerEl.style.display = \"flex\";\n adContainerEl.style.pointerEvents = \"auto\";\n adContainerEl.offsetHeight;\n adContainerEl.style.opacity = \"1\";\n }\n emit(\"content_pause\");\n const mediaFile = selectBestMediaFile(currentAd.mediaFiles);\n if (!mediaFile) {\n throw new Error(\"No media file available\");\n }\n log(\"Playing media file:\", mediaFile.url);\n adVideoElement.src = mediaFile.url;\n adVideoElement.load();\n await adVideoElement.play();\n return Promise.resolve();\n } catch (error) {\n console.error(\"[AdStormPlayer] Error playing ad:\", error);\n handleAdError();\n return Promise.reject(error);\n }\n },\n async stop() {\n log(\"Stopping ad\");\n tornDown = true;\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n if (adContainerEl) {\n adContainerEl.style.opacity = \"0\";\n adHideTimerId = setTimeout(() => {\n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n }, 300);\n }\n teardownCurrentPlayback();\n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n currentAd = void 0;\n return Promise.resolve();\n },\n pause() {\n if (!adPlaying || !adVideoElement) return;\n try {\n if (!adVideoElement.paused) adVideoElement.pause();\n } catch (error) {\n console.warn(\"[AdStormPlayer] Error pausing ad:\", error);\n }\n },\n resume() {\n if (!adPlaying || !adVideoElement) return;\n try {\n if (adVideoElement.paused) adVideoElement.play().catch(() => {\n });\n } catch (error) {\n console.warn(\"[AdStormPlayer] Error resuming ad:\", error);\n }\n },\n destroy() {\n log(\"Destroying\");\n destroyed = true;\n tornDown = true;\n adPlaying = false;\n setAdPlayingFlag(false);\n clearAdStallTimer();\n clearAdCountdownTimer();\n if (adHideTimerId) {\n clearTimeout(adHideTimerId);\n adHideTimerId = void 0;\n }\n contentVideo.muted = originalMutedState;\n contentVideo.volume = originalVolume;\n contentVideo.style.visibility = \"visible\";\n contentVideo.style.opacity = \"1\";\n teardownCurrentPlayback();\n adVideoElement?.remove();\n adVideoElement = void 0;\n if (adContainerEl?.parentElement) {\n adContainerEl.parentElement.removeChild(adContainerEl);\n }\n adContainerEl = void 0;\n adCountdownEl = void 0;\n currentAd = void 0;\n sessionId = void 0;\n preloadSlots.clear();\n listeners.clear();\n if (parentPositionOverridden && contentVideo.parentElement) {\n contentVideo.parentElement.style.position = \"\";\n parentPositionOverridden = false;\n }\n },\n updateOptions(opts) {\n if (opts.continueLiveStreamDuringAds !== void 0) {\n continueLiveStreamDuringAds = opts.continueLiveStreamDuringAds;\n }\n },\n async playAd(requestContext) {\n if (destroyed) return Promise.reject(new Error(\"Player has been destroyed\"));\n if (!currentAd) {\n const ad = await requestAdFromApi(requestContext);\n if (!ad) {\n emit(\"ad_error\", { message: \"No valid ad from AdStorm API\" });\n return Promise.reject(new Error(\"No valid ad from AdStorm API\"));\n }\n assignCurrentAd(ad);\n }\n return this.play();\n },\n async preloadAd(arg1, arg2) {\n if (destroyed) return;\n const token = typeof arg1 === \"string\" ? arg1 : typeof arg2 === \"string\" ? arg2 : void 0;\n if (!token) return;\n if (currentAd) {\n preloadSlots.set(token, { ad: currentAd });\n currentAd = void 0;\n return;\n }\n const requestContext = typeof arg1 === \"string\" ? arg2 : arg1;\n const ad = await requestAdFromApi(requestContext);\n if (!ad) return;\n preloadSlots.set(token, { ad });\n },\n async playPreloaded(token) {\n if (destroyed) return Promise.reject(new Error(\"Player has been destroyed\"));\n const slot = preloadSlots.get(token);\n if (!slot) {\n return Promise.reject(new Error(`No preloaded ad for token ${token}`));\n }\n preloadSlots.delete(token);\n assignCurrentAd(slot.ad);\n return this.play();\n },\n hasPreloaded(token) {\n return preloadSlots.has(token);\n },\n cancelPreload(token) {\n preloadSlots.delete(token);\n },\n isAdPlaying() {\n return adPlaying;\n },\n resize(width, height) {\n log(`Resizing to ${width}x${height}`);\n if (adContainerEl) {\n adContainerEl.style.width = `${width}px`;\n adContainerEl.style.height = `${height}px`;\n }\n if (adVideoElement) {\n adVideoElement.style.width = `${width}px`;\n adVideoElement.style.height = `${height}px`;\n }\n },\n on(event, listener) {\n if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());\n listeners.get(event).add(listener);\n },\n off(event, listener) {\n listeners.get(event)?.delete(listener);\n },\n updateOriginalMutedState(muted, volume) {\n const nextVolume = typeof volume === \"number\" && !Number.isNaN(volume) ? Math.max(0, Math.min(1, volume)) : originalVolume;\n log(`updateOriginalMutedState: muted=${muted}, volume=${nextVolume}`);\n originalMutedState = muted;\n originalVolume = nextVolume;\n },\n getOriginalMutedState() {\n return originalMutedState;\n },\n getOriginalVolume() {\n return originalVolume;\n },\n setAdVolume(volume) {\n if (adVideoElement && adPlaying) {\n adVideoElement.volume = Math.max(0, Math.min(1, volume));\n adVideoElement.muted = volume === 0;\n }\n },\n getAdVolume() {\n if (adVideoElement && adPlaying) {\n return adVideoElement.volume;\n }\n return 1;\n },\n showPlaceholder() {\n if (!adContainerEl) {\n const parent = contentVideo.parentElement;\n if (parent) {\n const computed = window.getComputedStyle(parent).position;\n if (computed === \"static\") {\n parent.style.position = \"relative\";\n parentPositionOverridden = true;\n }\n }\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.left = \"0\";\n container.style.top = \"0\";\n container.style.right = \"0\";\n container.style.bottom = \"0\";\n container.style.display = \"none\";\n container.style.alignItems = \"center\";\n container.style.justifyContent = \"center\";\n container.style.pointerEvents = \"none\";\n container.style.zIndex = AD_LAYER_Z_INDEX;\n container.style.backgroundColor = \"#000\";\n container.style.isolation = \"isolate\";\n const countdown = document.createElement(\"div\");\n countdown.style.position = \"absolute\";\n countdown.style.left = \"12px\";\n countdown.style.top = \"12px\";\n countdown.style.padding = \"4px 8px\";\n countdown.style.borderRadius = \"4px\";\n countdown.style.background = \"rgba(0,0,0,0.75)\";\n countdown.style.color = \"#fff\";\n countdown.style.fontFamily = \"sans-serif\";\n countdown.style.fontSize = \"12px\";\n countdown.style.lineHeight = \"1.2\";\n countdown.style.pointerEvents = \"none\";\n countdown.style.zIndex = COUNTDOWN_Z_INDEX;\n countdown.textContent = \"Ad\";\n container.appendChild(countdown);\n contentVideo.parentElement?.appendChild(container);\n adContainerEl = container;\n adCountdownEl = countdown;\n }\n if (adContainerEl) {\n adContainerEl.style.display = \"flex\";\n adContainerEl.style.opacity = \"1\";\n adContainerEl.style.pointerEvents = \"auto\";\n }\n },\n hidePlaceholder() {\n if (adContainerEl) {\n adContainerEl.style.opacity = \"0\";\n setTimeout(() => {\n if (adContainerEl) {\n adContainerEl.style.display = \"none\";\n adContainerEl.style.pointerEvents = \"none\";\n }\n }, 300);\n }\n }\n };\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n createAdStormPlayer\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/sdk/vastParser.cjs","../../src/sdk/vastParser.ts"],"names":["Object","getOwnPropertyDescriptor","__defProp","defineProperty","__getOwnPropDesc","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toCommonJS","mod","value","vastParser_exports","createEmptyTrackingState","fetchAndParseVastAd","fireTrackingPixels","parseVastXml","module","exports","isHlsType","type","includes","isMp4Type","xmlString","filter","logPrefix","xmlDoc","parser","DOMParser","parseFromString","parserError","querySelector","console","error","textContent","adElement","warn","adId","getAttribute","title","isNoAdAvailable","toLowerCase","durationText","durationParts","split","duration","parseInt","Math","round","parseFloat","mediaFileElements","querySelectorAll","mediaFiles","log","length"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2DAEuBA,mCAAAA,KAAOC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAD1BC,YAAYF,OAAOG,cAAc;QACjCC,IAAAA,aAAmBJ,GAAAA,yBAAAA,OAAOC,aAAAA,CAAAA,QAAwB,qBAA/BD,8CAAAA,oCAAAA,uBAA+B,WAAA,cAA/BA,wDAAAA,kCAA+B,IAAA;QAClDK,OAAAA,aAAoBL,OAAOM,mBAAmB;YAC9CC,IAAAA,SAAeP,OAAOQ,SAAS,CAACC,cAAc;YAC9CC,OAAAA,SAAW,kBAACC,QAAQC;YACtB,UAAA,EAAK,IAAIC,QAAQD,IACfV,UAAUS,QAAQE,MAAM;0BAAEC,KAAKF,GAAG,CAACC,KAAK;4BAAEE,YAAY;YAAK,cAAA;QAC/D;IACA,EAAIC,OAAAA,OAAc,qBAACC,IAAIC,MAAMC,QAAQC;QACnC,IAAIF,IAAAA,IAAQ,CAAA,CAAA,GAAOA,OAAP,GAAOA,QAAAA,6BAAAA,GAAP,SAAOA,KAAG,MAAM,YAAY,OAAOA,SAAS,YAAY;gBAC7D,kCAAA,2BAAA;;;UAAA,IAAIG,MAAJ,SAAA,UAAA;QAAA,SAAA,iEAAA,OAAA,YAAA,iEAAA;;;;;;oBACH;;wBAAKd,MAAAA,GAAae,IAAI,CAACL,IAAII,QAAQA,QAAQF,QACzCjB,UAAUe,IAAII,KAAK;4CAAEP,KAAK,SAALA;uDAAWI,IAAI,CAACG,IAAI;;8CAAEN,YAAY,CAAEK,CAAAA,OAAOhB,iBAAiBc,MAAMG,IAAG,KAAMD,KAAKL,UAAU;wCAAC;;kCAFpH,QAAK,YAAWV,kBAAkBa,0BAA7B,SAAA,6BAAA,QAAA,yBAAA;;;4BACH,GAAA,CAAI,CAACX;;gCADF,EAAA,MAAA,yBAAA,OAAA,SAAA,UAAA;8BAAA;;;;;;;;uCAAA,iBAAA,YAAA;;;mCAAA,EAAA,SAAA,QAAA;;;;8BAAA;;;;;QAGP,OAAA;QACA,OAAOU,QAAAA;QACT,UAAA;QACIM,eAAe,sBAACC;eAAQR,GAAAA,SAAYd,UAAU,CAAC,GAAG,cAAc;UAAEuB,OAAO;IAAK,IAAID;;;;mBAEtF;;;;;;;;;;;;;;;oCCnBAd,GAAAgB;;wCAAAA,MAAAA,KAAAA,MAAA;4CAAAC,QAAAA,cAAA,SAAAA;mDAAAA;;4CAAAC,WAAAA,MAAA,SAAAA;iDAAAA;;;oCAAAlB;;;;;;;;;oDAAAmB,OAAAA,QAAAA;;;;;;;;;;;;;;;;;2CAAAC,CAAAA,IAAAA,CAAAA,GAAAA,OAAAA,WAAAA,iCAAAA,OAAAA,UAAAA,GAAAA,eAAAA,OAAAA;;;;;;;;;;;;;oBAAAC,KAAAC,OAAA,GAAAT,aAAAG;oBDmBA,UAAA;;;0BAAwB,WAAA,OAAA;;;;;;;;;;;;;;;;oBAAA;;;;;;;;;;;ICiBxB,SAASO,UAAUC,IAAA;;IACjB,KAAA,EAAOA,SAAS,QAAA,IAAA,EAAA,SAAA;QAAA,YAAA,EAA2BA,KAAKC,0DAAAA,GAAA,CAAS;IAC3D,IAAA,CAAA,QAAA,KAAA,MAAA,KAAA,GAAA;IAEA,KAAA,EAASC,KAAAA,CAAAA,SAAAA,GAAUF,IAAA;QACjB,IAAA,GAAOA,SAAS,eAAeA,KAAKC,QAAA,CAAS;YAC/C,IAAA,cAAA;YAEO,GAASL,CAAAA,WAAAA,CACdO,SAAA;gBACAC,SAAAA,KAAAA,GAAAA,OAAAA,aACAC,OADAD,YAAAA,QAAAA,CAAAA,OAAAA,MAAAA,KAAAA,KAA0B,OAC1BC,GAAAA,CAAAA,MAAAA,2DAAY;YAEZ,EAAI;kBAoBYC,KAAAA,UAAAA,QAQZA,KAAAA,mBAkHmBA,mCAAAA;gBA7IrB,IAAMC,SAAS,IAAIC,EAAAA,aAAAA,GAAAA,KAAAA,WAAAA,KAAAA,CAAAA,YACnB,IAAMF,SAASC,OAAOE,eAAA,CAAgBN,WAAW;cAEjD,IAAMO,CAAAA,aAAcJ,OAAOK,aAAA,CAAc;gBACzC,IAAID,MAAAA,IAAAA,CAAa,KAAA,GAAA;oBACfE,OAAAA,CAAQC,EAAAA,GAAA,CACN,GAAY,OAATR,EAGL,OAAO,EAHO,6CACZK,YAAYI,WAAA;gBAGhB,IAAA,GAAA,GAAA;cAEA,IAAMC,YAAYT,OAAOK,aAAA,CAAc;cACvC,IAAI,CAACI,CAAAA,GAAAA,CAAAA,GAAW,OAAXA,GAAW,QAAA,2BAAA,OAAA;gBACdH,CAAAA,OAAQI,IAAA,CAAK,GAAY,OAATX,WAAS;kBACzB,EAAA,IAAA,CAAO,GAAA,OAAA,WAAA,kCAAA;YACT;UAEA,IAAMY,OAAOF,UAAUG,YAAA,CAAa,SAAS;QAC7C,IAAMC,QAAQb,EAAAA,wBAAAA,OAAOK,aAAA,CAAc,wBAArBL,4CAAAA,sBAAiCQ,WAAA,KAAe;QAE9D,IAAMM,kBACJH,SAAS,WACTE,MAAME,KAAAA,MAAA,GAAcpB,QAAA,CAAS,sBAC7BkB,MAAME,WAAA,OAAkB;QAE1B,IAAMC,CAAAA,OAAAA,GAAAA,IACJhB,EAAAA,yBAAAA,OAAOK,aAAA,CAAc,yBAArBL,6CAAAA,uBAAkCQ,WAAA,KAAe;oCACnD,IAAMS,gBAAgBD,aAAaE,KAAA,CAAM;+BACzC,IAAMC,WACJC,SAASH,aAAA,CAAc,EAAC,IAAK,KAAK,MAAM,OACxCG,SAASH,aAAA,CAAc,EAAC,IAAK,KAAK,MAAM,KACxCI,KAAKC,KAAA,CAAMC,WAAWN,aAAA,CAAc,EAAC,IAAK;8BAE5C,IAAMO,oBAAoBxB,OAAOyB,gBAAA,CAAiB;wBAClD,IAAMC,aAA8B,EAAC;SAErCpB,QAAQqB,GAAA,CACN,GAAsBH,OAAnBzB,WAAS,WAAkC,OAAxByB,kBAAkBI,MAAM,EAAA","sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/sdk/vastParser.ts\nvar vastParser_exports = {};\n__export(vastParser_exports, {\n createEmptyTrackingState: () => createEmptyTrackingState,\n fetchAndParseVastAd: () => fetchAndParseVastAd,\n fireTrackingPixels: () => fireTrackingPixels,\n parseVastXml: () => parseVastXml\n});\nmodule.exports = __toCommonJS(vastParser_exports);\nfunction isHlsType(type) {\n return type === \"application/x-mpegURL\" || type.includes(\"m3u8\");\n}\nfunction isMp4Type(type) {\n return type === \"video/mp4\" || type.includes(\"mp4\");\n}\nfunction parseVastXml(xmlString, filter = \"all\", logPrefix = \"[VastParser]\") {\n try {\n const parser = new DOMParser();\n const xmlDoc = parser.parseFromString(xmlString, \"text/xml\");\n const parserError = xmlDoc.querySelector(\"parsererror\");\n if (parserError) {\n console.error(\n `${logPrefix} XML parsing error (malformed VAST XML):`,\n parserError.textContent\n );\n return null;\n }\n const adElement = xmlDoc.querySelector(\"Ad\");\n if (!adElement) {\n console.warn(`${logPrefix} No Ad element found in VAST XML`);\n return null;\n }\n const adId = adElement.getAttribute(\"id\") || \"unknown\";\n const title = xmlDoc.querySelector(\"AdTitle\")?.textContent || \"Ad\";\n const isNoAdAvailable = adId === \"empty\" || title.toLowerCase().includes(\"no ad available\") || title.toLowerCase() === \"no ad available\";\n const durationText = xmlDoc.querySelector(\"Duration\")?.textContent || \"00:00:30\";\n const durationParts = durationText.split(\":\");\n const duration = parseInt(durationParts[0] || \"0\", 10) * 3600 + parseInt(durationParts[1] || \"0\", 10) * 60 + Math.round(parseFloat(durationParts[2] || \"0\"));\n const mediaFileElements = xmlDoc.querySelectorAll(\"MediaFile\");\n const mediaFiles = [];\n console.log(\n `${logPrefix} Found ${mediaFileElements.length} MediaFile element(s) in VAST XML`\n );\n mediaFileElements.forEach((mf, index) => {\n const type = mf.getAttribute(\"type\") || \"\";\n const url = mf.textContent?.trim() || \"\";\n const width = mf.getAttribute(\"width\") || \"\";\n const height = mf.getAttribute(\"height\") || \"\";\n console.log(\n `${logPrefix} MediaFile ${index}: type=\"${type}\", url=\"${url.substring(0, 80)}...\", width=\"${width}\", height=\"${height}\"`\n );\n if (!url) {\n console.warn(`${logPrefix} MediaFile ${index} has empty URL`);\n return;\n }\n const isHls = isHlsType(type);\n const isMp4 = isMp4Type(type);\n let accepted = false;\n if (filter === \"hls-only\") {\n accepted = isHls;\n } else if (filter === \"mp4-first\") {\n accepted = isMp4 || isHls;\n } else {\n accepted = true;\n }\n if (!accepted) {\n console.log(\n `${logPrefix} MediaFile ${index} ignored (type=\"${type}\" not accepted by filter \"${filter}\")`\n );\n return;\n }\n const bitrateAttr = mf.getAttribute(\"bitrate\");\n const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;\n mediaFiles.push({\n url,\n type,\n width: parseInt(width || \"1920\", 10),\n height: parseInt(height || \"1080\", 10),\n bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0\n });\n console.log(`${logPrefix} Added MediaFile: type=\"${type}\" url=\"${url.substring(0, 80)}...\"`);\n });\n if (filter === \"mp4-first\" && mediaFiles.length > 1) {\n mediaFiles.sort((a, b) => {\n const aIsMp4 = isMp4Type(a.type) ? 0 : 1;\n const bIsMp4 = isMp4Type(b.type) ? 0 : 1;\n return aIsMp4 - bIsMp4;\n });\n }\n if (mediaFiles.length === 0) {\n if (isNoAdAvailable) {\n console.warn(\n `${logPrefix} No ads available (VAST response indicates no ads)`\n );\n } else {\n console.warn(`${logPrefix} No compatible media files found in VAST XML`);\n }\n return null;\n }\n const trackingUrls = {\n impression: [],\n start: [],\n firstQuartile: [],\n midpoint: [],\n thirdQuartile: [],\n complete: [],\n mute: [],\n unmute: [],\n pause: [],\n resume: [],\n fullscreen: [],\n exitFullscreen: [],\n skip: [],\n error: []\n };\n xmlDoc.querySelectorAll(\"Impression\").forEach((el) => {\n const url = el.textContent?.trim();\n if (url) trackingUrls.impression.push(url);\n });\n xmlDoc.querySelectorAll(\"Tracking\").forEach((el) => {\n const event = el.getAttribute(\"event\");\n const url = el.textContent?.trim();\n if (event && url) {\n const eventKey = event;\n if (trackingUrls[eventKey]) {\n trackingUrls[eventKey].push(url);\n }\n }\n });\n const clickThrough = xmlDoc.querySelector(\"ClickThrough\")?.textContent?.trim();\n return {\n id: adId,\n title,\n duration,\n mediaFiles,\n trackingUrls,\n clickThrough\n };\n } catch (error) {\n console.error(`${logPrefix} Error parsing VAST XML:`, error);\n return null;\n }\n}\nasync function fetchAndParseVastAd(vastTagUrl, filter = \"all\", logPrefix = \"[VastParser]\") {\n const response = await fetch(vastTagUrl, {\n mode: \"cors\",\n credentials: \"include\",\n headers: {\n Accept: \"application/xml, text/xml, */*\"\n },\n referrerPolicy: \"no-referrer-when-downgrade\"\n });\n if (!response.ok) {\n throw new Error(`Failed to fetch VAST: ${response.statusText}`);\n }\n const vastXml = await response.text();\n console.log(`${logPrefix} VAST XML received`);\n console.log(\n `${logPrefix} VAST XML content (first 2000 chars):`,\n vastXml.substring(0, 2e3)\n );\n return parseVastXml(vastXml, filter, logPrefix);\n}\nfunction createEmptyTrackingState() {\n return {\n impression: false,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false\n };\n}\nasync function firePixelWithRetry(url, retries = 2, delayMs = 500, logPrefix = \"[VastParser]\") {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n await fetch(url, {\n method: \"GET\",\n mode: \"no-cors\",\n cache: \"no-cache\",\n keepalive: true\n });\n return;\n } catch {\n if (attempt < retries) {\n await new Promise((r) => setTimeout(r, delayMs * Math.pow(2, attempt)));\n } else {\n console.warn(`${logPrefix} Tracking pixel failed after ${retries + 1} attempts: ${url}`);\n }\n }\n }\n}\nfunction fireTrackingPixels(urls, sessionId, logPrefix = \"[VastParser]\") {\n if (!urls || urls.length === 0) return;\n urls.forEach((url) => {\n try {\n let trackingUrl = url;\n if (sessionId) {\n trackingUrl = `${trackingUrl}${trackingUrl.includes(\"?\") ? \"&\" : \"?\"}session_id=${sessionId}`;\n }\n if (typeof fetch !== \"undefined\") {\n firePixelWithRetry(trackingUrl, 2, 500, logPrefix).catch(() => {\n });\n } else {\n const img = new Image(1, 1);\n img.onerror = () => {\n };\n img.src = trackingUrl;\n }\n console.log(`${logPrefix} Fired tracking pixel: ${trackingUrl}`);\n } catch (error) {\n console.warn(`${logPrefix} Error firing tracking pixel:`, error);\n }\n });\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n createEmptyTrackingState,\n fetchAndParseVastAd,\n fireTrackingPixels,\n parseVastXml\n});\n","export interface VastMediaFile {\n url: string;\n type: string;\n width: number;\n height: number;\n bitrate?: number | undefined;\n}\n\nexport interface VastTrackingUrls {\n impression: string[];\n start: string[];\n firstQuartile: string[];\n midpoint: string[];\n thirdQuartile: string[];\n complete: string[];\n mute: string[];\n unmute: string[];\n pause: string[];\n resume: string[];\n fullscreen: string[];\n exitFullscreen: string[];\n skip: string[];\n error: string[];\n}\n\nexport interface VastAd {\n id: string;\n title: string;\n duration: number;\n mediaFiles: VastMediaFile[];\n trackingUrls: VastTrackingUrls;\n clickThrough?: string | undefined;\n}\n\nexport type MediaFileFilter = \"hls-only\" | \"mp4-first\" | \"all\";\n\nfunction isHlsType(type: string): boolean {\n return type === \"application/x-mpegURL\" || type.includes(\"m3u8\");\n}\n\nfunction isMp4Type(type: string): boolean {\n return type === \"video/mp4\" || type.includes(\"mp4\");\n}\n\nexport function parseVastXml(\n xmlString: string,\n filter: MediaFileFilter = \"all\",\n logPrefix = \"[VastParser]\"\n): VastAd | null {\n try {\n const parser = new DOMParser();\n const xmlDoc = parser.parseFromString(xmlString, \"text/xml\");\n\n const parserError = xmlDoc.querySelector(\"parsererror\");\n if (parserError) {\n console.error(\n `${logPrefix} XML parsing error (malformed VAST XML):`,\n parserError.textContent\n );\n return null;\n }\n\n const adElement = xmlDoc.querySelector(\"Ad\");\n if (!adElement) {\n console.warn(`${logPrefix} No Ad element found in VAST XML`);\n return null;\n }\n\n const adId = adElement.getAttribute(\"id\") || \"unknown\";\n const title = xmlDoc.querySelector(\"AdTitle\")?.textContent || \"Ad\";\n\n const isNoAdAvailable =\n adId === \"empty\" ||\n title.toLowerCase().includes(\"no ad available\") ||\n title.toLowerCase() === \"no ad available\";\n\n const durationText =\n xmlDoc.querySelector(\"Duration\")?.textContent || \"00:00:30\";\n const durationParts = durationText.split(\":\");\n const duration =\n parseInt(durationParts[0] || \"0\", 10) * 3600 +\n parseInt(durationParts[1] || \"0\", 10) * 60 +\n Math.round(parseFloat(durationParts[2] || \"0\"));\n\n const mediaFileElements = xmlDoc.querySelectorAll(\"MediaFile\");\n const mediaFiles: VastMediaFile[] = [];\n\n console.log(\n `${logPrefix} Found ${mediaFileElements.length} MediaFile element(s) in VAST XML`\n );\n\n mediaFileElements.forEach((mf, index) => {\n const type = mf.getAttribute(\"type\") || \"\";\n const url = mf.textContent?.trim() || \"\";\n const width = mf.getAttribute(\"width\") || \"\";\n const height = mf.getAttribute(\"height\") || \"\";\n\n console.log(\n `${logPrefix} MediaFile ${index}: type=\"${type}\", url=\"${url.substring(0, 80)}...\", width=\"${width}\", height=\"${height}\"`\n );\n\n if (!url) {\n console.warn(`${logPrefix} MediaFile ${index} has empty URL`);\n return;\n }\n\n const isHls = isHlsType(type);\n const isMp4 = isMp4Type(type);\n\n let accepted = false;\n if (filter === \"hls-only\") {\n accepted = isHls;\n } else if (filter === \"mp4-first\") {\n accepted = isMp4 || isHls;\n } else {\n accepted = true;\n }\n\n if (!accepted) {\n console.log(\n `${logPrefix} MediaFile ${index} ignored (type=\"${type}\" not accepted by filter \"${filter}\")`\n );\n return;\n }\n\n const bitrateAttr = mf.getAttribute(\"bitrate\");\n const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : undefined;\n\n mediaFiles.push({\n url,\n type,\n width: parseInt(width || \"1920\", 10),\n height: parseInt(height || \"1080\", 10),\n bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : undefined,\n });\n\n console.log(`${logPrefix} Added MediaFile: type=\"${type}\" url=\"${url.substring(0, 80)}...\"`);\n });\n\n if (filter === \"mp4-first\" && mediaFiles.length > 1) {\n mediaFiles.sort((a, b) => {\n const aIsMp4 = isMp4Type(a.type) ? 0 : 1;\n const bIsMp4 = isMp4Type(b.type) ? 0 : 1;\n return aIsMp4 - bIsMp4;\n });\n }\n\n if (mediaFiles.length === 0) {\n if (isNoAdAvailable) {\n console.warn(\n `${logPrefix} No ads available (VAST response indicates no ads)`\n );\n } else {\n console.warn(`${logPrefix} No compatible media files found in VAST XML`);\n }\n return null;\n }\n\n const trackingUrls: VastTrackingUrls = {\n impression: [],\n start: [],\n firstQuartile: [],\n midpoint: [],\n thirdQuartile: [],\n complete: [],\n mute: [],\n unmute: [],\n pause: [],\n resume: [],\n fullscreen: [],\n exitFullscreen: [],\n skip: [],\n error: [],\n };\n\n xmlDoc.querySelectorAll(\"Impression\").forEach((el) => {\n const url = el.textContent?.trim();\n if (url) trackingUrls.impression.push(url);\n });\n\n xmlDoc.querySelectorAll(\"Tracking\").forEach((el) => {\n const event = el.getAttribute(\"event\");\n const url = el.textContent?.trim();\n if (event && url) {\n const eventKey = event as keyof VastTrackingUrls;\n if (trackingUrls[eventKey]) {\n trackingUrls[eventKey].push(url);\n }\n }\n });\n\n const clickThrough = xmlDoc\n .querySelector(\"ClickThrough\")\n ?.textContent?.trim();\n\n return {\n id: adId,\n title,\n duration,\n mediaFiles,\n trackingUrls,\n clickThrough,\n };\n } catch (error) {\n console.error(`${logPrefix} Error parsing VAST XML:`, error);\n return null;\n }\n}\n\nexport async function fetchAndParseVastAd(\n vastTagUrl: string,\n filter: MediaFileFilter = \"all\",\n logPrefix = \"[VastParser]\"\n): Promise<VastAd | null> {\n const response = await fetch(vastTagUrl, {\n mode: \"cors\",\n credentials: \"include\",\n headers: {\n Accept: \"application/xml, text/xml, */*\",\n },\n referrerPolicy: \"no-referrer-when-downgrade\",\n });\n if (!response.ok) {\n throw new Error(`Failed to fetch VAST: ${response.statusText}`);\n }\n\n const vastXml = await response.text();\n console.log(`${logPrefix} VAST XML received`);\n console.log(\n `${logPrefix} VAST XML content (first 2000 chars):`,\n vastXml.substring(0, 2000)\n );\n\n return parseVastXml(vastXml, filter, logPrefix);\n}\n\nexport function createEmptyTrackingState() {\n return {\n impression: false,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false,\n };\n}\n\nasync function firePixelWithRetry(\n url: string,\n retries = 2,\n delayMs = 500,\n logPrefix = \"[VastParser]\"\n): Promise<void> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n await fetch(url, {\n method: \"GET\",\n mode: \"no-cors\",\n cache: \"no-cache\",\n keepalive: true,\n });\n return;\n } catch {\n if (attempt < retries) {\n await new Promise((r) => setTimeout(r, delayMs * Math.pow(2, attempt)));\n } else {\n console.warn(`${logPrefix} Tracking pixel failed after ${retries + 1} attempts: ${url}`);\n }\n }\n }\n}\n\nexport function fireTrackingPixels(\n urls: string[],\n sessionId?: string,\n logPrefix = \"[VastParser]\"\n): void {\n if (!urls || urls.length === 0) return;\n\n urls.forEach((url) => {\n try {\n let trackingUrl = url;\n\n if (sessionId) {\n trackingUrl = `${trackingUrl}${\n trackingUrl.includes(\"?\") ? \"&\" : \"?\"\n }session_id=${sessionId}`;\n }\n\n if (typeof fetch !== \"undefined\") {\n firePixelWithRetry(trackingUrl, 2, 500, logPrefix).catch(() => {});\n } else {\n const img = new Image(1, 1);\n img.onerror = () => {};\n img.src = trackingUrl;\n }\n\n console.log(`${logPrefix} Fired tracking pixel: ${trackingUrl}`);\n } catch (error) {\n console.warn(`${logPrefix} Error firing tracking pixel:`, error);\n }\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/sdk/vastParser.cjs"],"names":[],"mappings":"AAAA","sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/sdk/vastParser.ts\nvar vastParser_exports = {};\n__export(vastParser_exports, {\n createEmptyTrackingState: () => createEmptyTrackingState,\n fetchAndParseVastAd: () => fetchAndParseVastAd,\n fireTrackingPixels: () => fireTrackingPixels,\n parseVastXml: () => parseVastXml\n});\nmodule.exports = __toCommonJS(vastParser_exports);\nfunction isHlsType(type) {\n return type === \"application/x-mpegURL\" || type.includes(\"m3u8\");\n}\nfunction isMp4Type(type) {\n return type === \"video/mp4\" || type.includes(\"mp4\");\n}\nfunction parseVastXml(xmlString, filter = \"all\", logPrefix = \"[VastParser]\") {\n try {\n const parser = new DOMParser();\n const xmlDoc = parser.parseFromString(xmlString, \"text/xml\");\n const parserError = xmlDoc.querySelector(\"parsererror\");\n if (parserError) {\n console.error(\n `${logPrefix} XML parsing error (malformed VAST XML):`,\n parserError.textContent\n );\n return null;\n }\n const adElement = xmlDoc.querySelector(\"Ad\");\n if (!adElement) {\n console.warn(`${logPrefix} No Ad element found in VAST XML`);\n return null;\n }\n const adId = adElement.getAttribute(\"id\") || \"unknown\";\n const title = xmlDoc.querySelector(\"AdTitle\")?.textContent || \"Ad\";\n const isNoAdAvailable = adId === \"empty\" || title.toLowerCase().includes(\"no ad available\") || title.toLowerCase() === \"no ad available\";\n const durationText = xmlDoc.querySelector(\"Duration\")?.textContent || \"00:00:30\";\n const durationParts = durationText.split(\":\");\n const duration = parseInt(durationParts[0] || \"0\", 10) * 3600 + parseInt(durationParts[1] || \"0\", 10) * 60 + Math.round(parseFloat(durationParts[2] || \"0\"));\n const mediaFileElements = xmlDoc.querySelectorAll(\"MediaFile\");\n const mediaFiles = [];\n console.log(\n `${logPrefix} Found ${mediaFileElements.length} MediaFile element(s) in VAST XML`\n );\n mediaFileElements.forEach((mf, index) => {\n const type = mf.getAttribute(\"type\") || \"\";\n const url = mf.textContent?.trim() || \"\";\n const width = mf.getAttribute(\"width\") || \"\";\n const height = mf.getAttribute(\"height\") || \"\";\n console.log(\n `${logPrefix} MediaFile ${index}: type=\"${type}\", url=\"${url.substring(0, 80)}...\", width=\"${width}\", height=\"${height}\"`\n );\n if (!url) {\n console.warn(`${logPrefix} MediaFile ${index} has empty URL`);\n return;\n }\n const isHls = isHlsType(type);\n const isMp4 = isMp4Type(type);\n let accepted = false;\n if (filter === \"hls-only\") {\n accepted = isHls;\n } else if (filter === \"mp4-first\") {\n accepted = isMp4 || isHls;\n } else {\n accepted = true;\n }\n if (!accepted) {\n console.log(\n `${logPrefix} MediaFile ${index} ignored (type=\"${type}\" not accepted by filter \"${filter}\")`\n );\n return;\n }\n const bitrateAttr = mf.getAttribute(\"bitrate\");\n const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;\n mediaFiles.push({\n url,\n type,\n width: parseInt(width || \"1920\", 10),\n height: parseInt(height || \"1080\", 10),\n bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0\n });\n console.log(`${logPrefix} Added MediaFile: type=\"${type}\" url=\"${url.substring(0, 80)}...\"`);\n });\n if (filter === \"mp4-first\" && mediaFiles.length > 1) {\n mediaFiles.sort((a, b) => {\n const aIsMp4 = isMp4Type(a.type) ? 0 : 1;\n const bIsMp4 = isMp4Type(b.type) ? 0 : 1;\n return aIsMp4 - bIsMp4;\n });\n }\n if (mediaFiles.length === 0) {\n if (isNoAdAvailable) {\n console.warn(\n `${logPrefix} No ads available (VAST response indicates no ads)`\n );\n } else {\n console.warn(`${logPrefix} No compatible media files found in VAST XML`);\n }\n return null;\n }\n const trackingUrls = {\n impression: [],\n start: [],\n firstQuartile: [],\n midpoint: [],\n thirdQuartile: [],\n complete: [],\n mute: [],\n unmute: [],\n pause: [],\n resume: [],\n fullscreen: [],\n exitFullscreen: [],\n skip: [],\n error: []\n };\n xmlDoc.querySelectorAll(\"Impression\").forEach((el) => {\n const url = el.textContent?.trim();\n if (url) trackingUrls.impression.push(url);\n });\n xmlDoc.querySelectorAll(\"Tracking\").forEach((el) => {\n const event = el.getAttribute(\"event\");\n const url = el.textContent?.trim();\n if (event && url) {\n const eventKey = event;\n if (trackingUrls[eventKey]) {\n trackingUrls[eventKey].push(url);\n }\n }\n });\n const clickThrough = xmlDoc.querySelector(\"ClickThrough\")?.textContent?.trim();\n return {\n id: adId,\n title,\n duration,\n mediaFiles,\n trackingUrls,\n clickThrough\n };\n } catch (error) {\n console.error(`${logPrefix} Error parsing VAST XML:`, error);\n return null;\n }\n}\nasync function fetchAndParseVastAd(vastTagUrl, filter = \"all\", logPrefix = \"[VastParser]\") {\n const response = await fetch(vastTagUrl, {\n mode: \"cors\",\n credentials: \"include\",\n headers: {\n Accept: \"application/xml, text/xml, */*\"\n },\n referrerPolicy: \"no-referrer-when-downgrade\"\n });\n if (!response.ok) {\n throw new Error(`Failed to fetch VAST: ${response.statusText}`);\n }\n const vastXml = await response.text();\n console.log(`${logPrefix} VAST XML received`);\n console.log(\n `${logPrefix} VAST XML content (first 2000 chars):`,\n vastXml.substring(0, 2e3)\n );\n return parseVastXml(vastXml, filter, logPrefix);\n}\nfunction createEmptyTrackingState() {\n return {\n impression: false,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false\n };\n}\nasync function firePixelWithRetry(url, retries = 2, delayMs = 500, logPrefix = \"[VastParser]\") {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n await fetch(url, {\n method: \"GET\",\n mode: \"no-cors\",\n cache: \"no-cache\",\n keepalive: true\n });\n return;\n } catch {\n if (attempt < retries) {\n await new Promise((r) => setTimeout(r, delayMs * Math.pow(2, attempt)));\n } else {\n console.warn(`${logPrefix} Tracking pixel failed after ${retries + 1} attempts: ${url}`);\n }\n }\n }\n}\nfunction fireTrackingPixels(urls, sessionId, logPrefix = \"[VastParser]\") {\n if (!urls || urls.length === 0) return;\n urls.forEach((url) => {\n try {\n let trackingUrl = url;\n if (sessionId) {\n trackingUrl = `${trackingUrl}${trackingUrl.includes(\"?\") ? \"&\" : \"?\"}session_id=${sessionId}`;\n }\n if (typeof fetch !== \"undefined\") {\n firePixelWithRetry(trackingUrl, 2, 500, logPrefix).catch(() => {\n });\n } else {\n const img = new Image(1, 1);\n img.onerror = () => {\n };\n img.src = trackingUrl;\n }\n console.log(`${logPrefix} Fired tracking pixel: ${trackingUrl}`);\n } catch (error) {\n console.warn(`${logPrefix} Error firing tracking pixel:`, error);\n }\n });\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n createEmptyTrackingState,\n fetchAndParseVastAd,\n fireTrackingPixels,\n parseVastXml\n});\n"]}
|