stormcloud-video-player 0.7.16 → 0.7.17
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 +66 -12
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +5 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +67 -13
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/players/FilePlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.cjs.map +1 -1
- 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.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +66 -12
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.d.cts +5 -0
- package/lib/utils/browserCompat.cjs.map +1 -1
- 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/ui/OverlayRenderer.cjs"],"names":[],"mappings":"AAAA","sourcesContent":["\"use strict\";\nvar __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n // If the importer is in node compatibility mode or this is not an ESM\n // file that has been converted to a CommonJS file using a Babel-\n // compatible transform (i.e. \"__esModule\" has not been set), then set\n // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n isNodeMode || !mod || !mod.__esModule ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target,\n mod\n));\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/ui/OverlayRenderer.tsx\nvar OverlayRenderer_exports = {};\n__export(OverlayRenderer_exports, {\n OverlayRenderer: () => OverlayRenderer\n});\nmodule.exports = __toCommonJS(OverlayRenderer_exports);\nvar import_react = __toESM(require(\"react\"), 1);\n\n// src/utils/overlays.ts\nvar OVERLAY_API_BASE = \"https://adstorm.co/api-adstorm-dev\";\nfunction timeStringToSeconds(timeStr) {\n if (!timeStr) return 0;\n const parts = timeStr.split(\":\");\n if (parts.length >= 3) {\n const hours = parseInt(parts[0] ?? \"0\", 10) || 0;\n const minutes = parseInt(parts[1] ?? \"0\", 10) || 0;\n const secStr = parts[2] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return hours * 3600 + minutes * 60 + seconds + ms / 1e3;\n }\n if (parts.length === 2) {\n const minutes = parseInt(parts[0] ?? \"0\", 10) || 0;\n const secStr = parts[1] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return minutes * 60 + seconds + ms / 1e3;\n }\n const num = parseFloat(timeStr);\n return isFinite(num) ? Math.max(0, num) : 0;\n}\nfunction isOverlayActive(overlay, currentTime) {\n if (!overlay.visible) return false;\n const startSec = timeStringToSeconds(overlay.start_time);\n const durationSec = timeStringToSeconds(overlay.duration);\n if (durationSec <= 0) return false;\n return currentTime >= startSec && currentTime < startSec + durationSec;\n}\nfunction resolveImageUrl(imageUrl, apiBaseUrl = OVERLAY_API_BASE) {\n if (!imageUrl) return \"\";\n if (imageUrl.startsWith(\"http://\") || imageUrl.startsWith(\"https://\")) {\n return imageUrl;\n }\n if (imageUrl.startsWith(\"/\")) {\n try {\n const url = new URL(apiBaseUrl);\n return `${url.origin}${imageUrl}`;\n } catch {\n return imageUrl;\n }\n }\n return `${apiBaseUrl}/${imageUrl}`;\n}\n\n// src/ui/OverlayRenderer.tsx\nvar import_jsx_runtime = require(\"react/jsx-runtime\");\nfunction computeVideoDimensions(video) {\n const nativeWidth = video.videoWidth;\n const nativeHeight = video.videoHeight;\n if (!nativeWidth || !nativeHeight) return null;\n const displayWidth = video.offsetWidth;\n const displayHeight = video.offsetHeight;\n if (!displayWidth || !displayHeight) return null;\n const videoAspect = nativeWidth / nativeHeight;\n const displayAspect = displayWidth / displayHeight;\n let renderWidth;\n let renderHeight;\n let offsetX;\n let offsetY;\n if (videoAspect > displayAspect) {\n renderWidth = displayWidth;\n renderHeight = displayWidth / videoAspect;\n offsetX = 0;\n offsetY = (displayHeight - renderHeight) / 2;\n } else {\n renderHeight = displayHeight;\n renderWidth = displayHeight * videoAspect;\n offsetX = (displayWidth - renderWidth) / 2;\n offsetY = 0;\n }\n return {\n nativeWidth,\n nativeHeight,\n displayWidth: renderWidth,\n displayHeight: renderHeight,\n offsetX,\n offsetY,\n scaleX: renderWidth / nativeWidth,\n scaleY: renderHeight / nativeHeight\n };\n}\nfunction ImageOverlay({ overlay }) {\n const src = resolveImageUrl(overlay.image_url || \"\");\n if (!src) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"img\",\n {\n src,\n alt: overlay.name,\n draggable: false,\n style: {\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n pointerEvents: \"none\",\n userSelect: \"none\"\n }\n }\n );\n}\nfunction TextOverlay({ overlay }) {\n const text = overlay.content || \"\";\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"#ffffff\",\n fontSize: \"clamp(10px, 1.4vw, 20px)\",\n fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\",\n fontWeight: 600,\n textAlign: \"center\",\n padding: \"4px 8px\",\n boxSizing: \"border-box\",\n wordBreak: \"break-word\",\n textShadow: \"0 1px 4px rgba(0,0,0,0.7)\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n lineHeight: 1.3\n },\n children: text\n }\n );\n}\nasync function fetchRSSItems(rssUrl, maxItems) {\n const resp = await fetch(\n `https://api.allorigins.win/get?url=${encodeURIComponent(rssUrl)}`\n );\n const data = await resp.json();\n if (!data.contents) throw new Error(\"No content from RSS feed\");\n const parser = new DOMParser();\n const doc = parser.parseFromString(data.contents, \"text/xml\");\n if (doc.querySelector(\"parsererror\")) throw new Error(\"Invalid RSS XML\");\n return Array.from(doc.querySelectorAll(\"item\")).map((item) => ({\n title: (item.querySelector(\"title\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n description: (item.querySelector(\"description\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n pubDate: item.querySelector(\"pubDate\")?.textContent || \"\",\n author: item.querySelector(\"author, dc\\\\:creator\")?.textContent || \"\",\n category: item.querySelector(\"category\")?.textContent || \"\"\n })).filter((i) => i.title).slice(0, maxItems);\n}\nfunction ScrollerOverlay({ overlay }) {\n const cfg = overlay.scroller_config;\n const uid = (0, import_react.useId)().replace(/:/g, \"\");\n const [rssItems, setRssItems] = (0, import_react.useState)([]);\n const [rssLoading, setRssLoading] = (0, import_react.useState)(false);\n const rssUrl = cfg?.rss_url || \"\";\n const maxItems = cfg?.max_items ?? 10;\n const autoRefresh = cfg?.auto_refresh !== false;\n const updateInterval = cfg?.update_interval ?? 5;\n (0, import_react.useEffect)(() => {\n if (!rssUrl || cfg?.use_custom_text && cfg?.custom_text) return;\n let cancelled = false;\n setRssLoading(true);\n fetchRSSItems(rssUrl, maxItems).then((items) => {\n if (!cancelled) setRssItems(items);\n }).catch(() => {\n }).finally(() => {\n if (!cancelled) setRssLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [rssUrl, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n (0, import_react.useEffect)(() => {\n if (!rssUrl || !autoRefresh || cfg?.use_custom_text && cfg?.custom_text) return;\n const interval = setInterval(() => {\n fetchRSSItems(rssUrl, maxItems).then(setRssItems).catch(() => {\n });\n }, updateInterval * 60 * 1e3);\n return () => clearInterval(interval);\n }, [rssUrl, autoRefresh, updateInterval, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n const sep = cfg?.separator_char ?? \"\\u25C6\";\n let segments;\n if (cfg?.use_custom_text && cfg?.custom_text) {\n segments = [cfg.custom_text];\n } else if (rssItems.length > 0) {\n segments = rssItems.map((item) => {\n const parts = [];\n if (cfg?.show_title !== false && item.title) parts.push(item.title);\n if (cfg?.show_description && item.description) parts.push(item.description);\n if (cfg?.show_timestamp && item.pubDate) {\n try {\n parts.push(new Date(item.pubDate).toLocaleDateString());\n } catch {\n }\n }\n if (cfg?.show_author && item.author) parts.push(`\\u2014 ${item.author}`);\n if (cfg?.show_category && item.category) parts.push(`[${item.category}]`);\n return parts.join(\" \");\n });\n } else if (rssLoading) {\n segments = [\"Loading feed\\u2026\"];\n } else if (overlay.content) {\n segments = [overlay.content];\n } else {\n segments = rssUrl ? [\"Fetching RSS feed\\u2026\"] : [\"RSS Ticker\"];\n }\n const scrollSpeed = cfg?.scroll_speed ?? 40;\n const direction = cfg?.direction ?? \"left\";\n const fontSize = cfg?.font_size ?? 15;\n const fontFamily = cfg?.font_family || \"Roboto, 'Segoe UI', Arial, sans-serif\";\n const fontWeight = cfg?.font_weight || \"700\";\n const textColor = cfg?.text_color || \"#ffffff\";\n const bgColor = cfg?.background_color || \"#0d0d1a\";\n const bgOpacity = cfg?.background_opacity !== void 0 ? cfg.background_opacity / 100 : 0.95;\n const borderRadius = cfg?.border_radius ?? 0;\n const itemSpacing = cfg?.item_spacing ?? 60;\n const label = cfg?.label ?? \"NEWS\";\n const labelLine2 = cfg?.label_line2 ?? \"\";\n const labelColor = cfg?.label_color ?? \"#f97316\";\n const labelTextColor = cfg?.label_text_color ?? \"#ffffff\";\n const accentColor = cfg?.accent_color ?? labelColor;\n const showAccentLine = cfg?.show_accent_line !== false;\n const isHorizontal = direction === \"left\" || direction === \"right\";\n const isReverse = direction === \"right\" || direction === \"down\";\n const fullText = segments.join(` ${sep} `);\n const durationSec = Math.max(6, fullText.length * 9 / scrollSpeed);\n const animId = `sc-ticker-${overlay.id}-${uid}`;\n const keyframes = isHorizontal ? `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateX(-50%); } 100% { transform: translateX(0%); }\" : \"0% { transform: translateX(0); } 100% { transform: translateX(-50%); }\"}\n }` : `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateY(-50%); } 100% { transform: translateY(0%); }\" : \"0% { transform: translateY(0); } 100% { transform: translateY(-50%); }\"}\n }`;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"style\", { children: keyframes }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRadius: borderRadius > 0 ? `${borderRadius}px` : void 0,\n backgroundColor: `rgba(${hexToRgb(bgColor)}, ${bgOpacity})`,\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight,\n color: textColor,\n pointerEvents: \"none\",\n userSelect: \"none\"\n },\n children: [\n showAccentLine && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { height: 3, background: accentColor, flexShrink: 0, width: \"100%\" } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", flex: 1, overflow: \"hidden\", minHeight: 0 }, children: [\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n background: labelColor,\n color: labelTextColor,\n padding: \"0 14px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n minWidth: 72,\n textAlign: \"center\",\n gap: 1\n },\n children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 800,\n fontSize: \"0.82em\",\n letterSpacing: \"0.05em\",\n lineHeight: 1.1,\n textTransform: \"uppercase\",\n whiteSpace: \"nowrap\"\n },\n children: label\n }\n ),\n labelLine2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 500,\n fontSize: \"0.62em\",\n letterSpacing: \"0.03em\",\n lineHeight: 1.1,\n opacity: 0.85,\n whiteSpace: \"nowrap\"\n },\n children: labelLine2\n }\n )\n ]\n }\n ),\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: 3, background: accentColor, flexShrink: 0 } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n flex: 1,\n overflow: \"hidden\",\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\"\n },\n children: isHorizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"inline-flex\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map((copy) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { paddingRight: `${itemSpacing}px` }, children: segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { opacity: 0.5, margin: \"0 8px\" }, children: sep }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { textShadow: \"0 1px 3px rgba(0,0,0,0.6)\" }, children: seg })\n ] }, i)) }, copy))\n }\n ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"flex\",\n flexDirection: \"column\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map(\n (copy) => segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { paddingBottom: `${itemSpacing / 4}px` }, children: seg }, `${copy}-${i}`))\n )\n }\n )\n }\n )\n ] })\n ]\n }\n )\n ] });\n}\nfunction parseConfig(content) {\n if (!content) return null;\n try {\n return JSON.parse(content);\n } catch {\n return null;\n }\n}\nfunction ScoreBugOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.058);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", alignItems: \"center\", padding: `0 ${f * 0.8}px`, gap: f * 0.4 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.homeTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.homeScore })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.8em\", textAlign: \"center\", opacity: 0.7, padding: `0 ${f * 0.4}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.period }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.clock })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.awayTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.awayScore })\n ] })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", textAlign: \"center\", opacity: 0.6, padding: `${f * 0.2}px ${f * 0.4}px`, borderTop: `1px solid ${cfg.accentColor}40`, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction LowerThirdOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", flexDirection: \"column\", justifyContent: \"flex-end\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: Math.max(2, size.h * 0.06), backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.5}px ${f * 1.2}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.4em\", fontWeight: 700, lineHeight: 1.2, textShadow: \"0 1px 4px rgba(0,0,0,0.5)\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", opacity: 0.7, marginTop: f * 0.2 }, children: cfg.subtitle })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.5, padding: `0 ${f * 1.2}px ${f * 0.4}px`, display: \"flex\", alignItems: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction QrCodeOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const qrSide = Math.max(32, Math.min(size.w, size.h) * 0.55);\n const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${Math.round(qrSide * 2)}x${Math.round(qrSide * 2)}&data=${encodeURIComponent(cfg.url || \"https://example.com\")}`;\n const f = Math.max(6, size.w * 0.06);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", padding: f * 0.6, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", overflow: \"hidden\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, background: \"#fff\", borderRadius: Math.max(2, qrSide * 0.06), padding: Math.max(2, qrSide * 0.06), lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: qrUrl, alt: \"QR Code\", style: { width: `${qrSide}px`, height: `${qrSide}px`, display: \"block\" } }) }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 1.1}px`, fontWeight: 700, textAlign: \"center\", color: cfg.accentColor, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.ctaText }),\n cfg.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 0.75}px`, opacity: 0.6, textAlign: \"center\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.description })\n ] });\n}\nfunction ComingUpNextOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: Math.max(2, size.w * 0.015), flexShrink: 0, backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.6}px ${f * 1}px`, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor }, children: \"Coming Up Next\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.5em\", fontWeight: 700, lineHeight: 1.2, marginTop: f * 0.2, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.title }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.6, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.subtitle }),\n cfg.scheduledTime && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 600, marginTop: f * 0.4, color: cfg.accentColor }, children: cfg.scheduledTime })\n ] }),\n cfg.thumbnailUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, width: Math.max(40, size.h * 0.75), overflow: \"hidden\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.thumbnailUrl, alt: \"thumbnail\", style: { width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" } }) })\n ] });\n}\nfunction ContextualTriggerOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const icons = { alert: \"\\u26A0\\uFE0F\", celebration: \"\\u{1F389}\", info: \"\\u2139\\uFE0F\", warning: \"\\u{1F514}\" };\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", alignItems: \"center\", gap: f * 0.8, padding: `0 ${f * 1.2}px`, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", borderLeft: `${Math.max(2, size.w * 0.02)}px solid ${cfg.accentColor}`, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontSize: \"2em\", flexShrink: 0 }, children: icons[cfg.iconType] || \"\\u26A1\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.message })\n ] })\n ] });\n}\nfunction OddsBettingOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.052);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventTitle }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", gap: f * 0.2, justifyContent: \"center\" }, children: (cfg.options || []).slice(0, 5).map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: `${f * 0.2}px ${f * 0.6}px`, borderRadius: Math.max(2, f * 0.3), background: `${cfg.accentColor}15`, fontSize: \"1em\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", flex: 1 }, children: opt.label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontWeight: 700, marginLeft: f * 0.8, flexShrink: 0, color: cfg.accentColor }, children: opt.odds })\n ] }, i)) }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.4, textAlign: \"center\", marginTop: f * 0.4 }, children: cfg.sponsorText })\n ] });\n}\nfunction BreakingNewsOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const urgencyColors = { breaking: \"#dc2626\", urgent: \"#ea580c\", normal: \"#2563eb\" };\n const labelBg = urgencyColors[cfg.urgency] || urgencyColors.normal;\n const label = cfg.urgency === \"breaking\" ? \"BREAKING\" : cfg.urgency === \"urgent\" ? \"URGENT\" : \"NEWS\";\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", alignItems: \"center\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { padding: `0 ${f * 0.8}px`, height: \"100%\", display: \"flex\", alignItems: \"center\", background: labelBg, color: \"#fff\", fontSize: \"1em\", fontWeight: 900, textTransform: \"uppercase\", letterSpacing: \"0.05em\", flexShrink: 0 }, children: label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, padding: `0 ${f * 1}px`, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n cfg.body && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.body })\n ] })\n ] });\n}\nfunction calcCountdownRemaining(targetTime) {\n const diff = Math.max(0, new Date(targetTime).getTime() - Date.now());\n return {\n d: Math.floor(diff / 864e5),\n h: Math.floor(diff % 864e5 / 36e5),\n m: Math.floor(diff % 36e5 / 6e4),\n s: Math.floor(diff % 6e4 / 1e3),\n expired: diff === 0\n };\n}\nfunction CountdownOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n const targetTime = cfg?.targetTime ?? \"\";\n const [remaining, setRemaining] = (0, import_react.useState)(\n () => targetTime ? calcCountdownRemaining(targetTime) : { d: 0, h: 0, m: 0, s: 0, expired: false }\n );\n (0, import_react.useEffect)(() => {\n if (!targetTime) return;\n setRemaining(calcCountdownRemaining(targetTime));\n const id = setInterval(() => setRemaining(calcCountdownRemaining(targetTime)), 1e3);\n return () => clearInterval(id);\n }, [targetTime]);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n const pad = (n) => String(n).padStart(2, \"0\");\n const units = [\n { show: cfg.showDays, value: pad(remaining.d), label: \"DAYS\" },\n { show: cfg.showHours, value: pad(remaining.h), label: \"HRS\" },\n { show: cfg.showMinutes, value: pad(remaining.m), label: \"MIN\" },\n { show: cfg.showSeconds, value: pad(remaining.s), label: \"SEC\" }\n ];\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventName }),\n remaining.expired ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700, opacity: 0.6 }, children: cfg.message || \"Event ended\" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { display: \"flex\", gap: f * 0.6, alignItems: \"center\" }, children: units.filter((u) => u.show).map((u, i, arr) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"2em\", fontWeight: 900, lineHeight: 1, borderRadius: Math.max(2, f * 0.4), padding: `${f * 0.2}px ${f * 0.4}px`, background: `${cfg.accentColor}20` }, children: u.value }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.5em\", opacity: 0.5, marginTop: f * 0.2 }, children: u.label })\n ] }),\n i < arr.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 700, opacity: 0.3 }, children: \":\" })\n ] }, u.label)) }),\n !remaining.expired && cfg.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", opacity: 0.6, marginTop: f * 0.4, textAlign: \"center\" }, children: cfg.message })\n ] });\n}\nfunction ShapeOverlay({ overlay, size }) {\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.03), background: \"rgba(99, 102, 241, 0.2)\", border: \"2px solid rgba(99, 102, 241, 0.4)\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", pointerEvents: \"none\", userSelect: \"none\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f}px`, fontWeight: 500, color: \"rgba(163, 163, 163, 0.8)\", textTransform: \"uppercase\" }, children: overlay.name }) });\n}\nfunction hexToRgb(hex) {\n if (!hex || !hex.startsWith(\"#\")) return \"0,0,0\";\n const clean = hex.slice(1);\n const num = parseInt(clean.length === 3 ? clean.replace(/./g, \"$&$&\") : clean, 16);\n return `${num >> 16 & 255},${num >> 8 & 255},${num & 255}`;\n}\nvar FADE_DURATION_MS = 1e3;\nvar OverlayRenderer = ({\n overlays,\n currentTime,\n videoRef,\n coordinateSpace\n}) => {\n const [dims, setDims] = (0, import_react.useState)(null);\n const rafRef = (0, import_react.useRef)(null);\n const [fadeMap, setFadeMap] = (0, import_react.useState)(/* @__PURE__ */ new Map());\n const removeTimers = (0, import_react.useRef)(/* @__PURE__ */ new Map());\n const updateDims = (0, import_react.useCallback)(() => {\n const video = videoRef.current;\n if (video) {\n const computed = computeVideoDimensions(video);\n setDims((prev) => {\n if (!computed || prev && prev.nativeWidth === computed.nativeWidth && prev.nativeHeight === computed.nativeHeight && prev.displayWidth === computed.displayWidth && prev.displayHeight === computed.displayHeight && prev.offsetX === computed.offsetX && prev.offsetY === computed.offsetY) {\n return prev;\n }\n return computed;\n });\n }\n }, [videoRef]);\n (0, import_react.useEffect)(() => {\n updateDims();\n const interval = setInterval(updateDims, 500);\n const handleResize = () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(updateDims);\n };\n window.addEventListener(\"resize\", handleResize);\n return () => {\n clearInterval(interval);\n window.removeEventListener(\"resize\", handleResize);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [updateDims]);\n const activeOverlays = (0, import_react.useMemo)(\n () => overlays.filter((o) => isOverlayActive(o, currentTime)),\n [overlays, currentTime]\n );\n (0, import_react.useEffect)(() => {\n const activeIds = new Set(activeOverlays.map((o) => o.id));\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const overlay of activeOverlays) {\n if (!next.has(overlay.id)) {\n next.set(overlay.id, { overlay, visible: false });\n } else {\n const existing = next.get(overlay.id);\n next.set(overlay.id, { ...existing, overlay });\n }\n }\n for (const [id, state] of next) {\n if (!activeIds.has(id) && state.visible) {\n next.set(id, { ...state, visible: false });\n if (!removeTimers.current.has(id)) {\n const timer = setTimeout(() => {\n setFadeMap((m) => {\n const updated = new Map(m);\n updated.delete(id);\n return updated;\n });\n removeTimers.current.delete(id);\n }, FADE_DURATION_MS);\n removeTimers.current.set(id, timer);\n }\n } else if (!activeIds.has(id) && !state.visible) {\n }\n }\n return next;\n });\n }, [activeOverlays]);\n (0, import_react.useEffect)(() => {\n const toFadeIn = [];\n for (const [id, state] of fadeMap) {\n if (!state.visible) {\n const isActive = activeOverlays.some((o) => o.id === id);\n if (isActive) toFadeIn.push(id);\n }\n }\n if (toFadeIn.length === 0) return;\n const raf = requestAnimationFrame(() => {\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const id of toFadeIn) {\n const state = next.get(id);\n if (state) next.set(id, { ...state, visible: true });\n }\n return next;\n });\n });\n return () => cancelAnimationFrame(raf);\n }, [fadeMap, activeOverlays]);\n (0, import_react.useEffect)(() => {\n return () => {\n for (const timer of removeTimers.current.values()) clearTimeout(timer);\n };\n }, []);\n if (!dims || fadeMap.size === 0) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n \"aria-hidden\": \"true\",\n style: {\n position: \"absolute\",\n left: `${dims.offsetX}px`,\n top: `${dims.offsetY}px`,\n width: `${dims.displayWidth}px`,\n height: `${dims.displayHeight}px`,\n pointerEvents: \"none\",\n overflow: \"hidden\",\n zIndex: 8\n },\n children: [...fadeMap.values()].map(({ overlay, visible }) => {\n const scaleX = coordinateSpace?.width ? dims.displayWidth / coordinateSpace.width : dims.scaleX;\n const scaleY = coordinateSpace?.height ? dims.displayHeight / coordinateSpace.height : dims.scaleY;\n const left = overlay.x * scaleX;\n const top = overlay.y * scaleY;\n const width = overlay.width * scaleX;\n const height = overlay.height * scaleY;\n const baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;\n const opacity = visible ? baseOpacity : 0;\n const sz = { w: width, h: height };\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n position: \"absolute\",\n left: `${left}px`,\n top: `${top}px`,\n width: `${width}px`,\n height: `${height}px`,\n opacity,\n transition: `opacity ${FADE_DURATION_MS}ms ease`,\n zIndex: overlay.z_index,\n overflow: \"hidden\"\n },\n children: [\n overlay.type === \"image\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImageOverlay, { overlay }),\n overlay.type === \"text\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextOverlay, { overlay }),\n overlay.type === \"scroller\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollerOverlay, { overlay }),\n overlay.type === \"shape\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShapeOverlay, { overlay, size: sz }),\n overlay.type === \"score_bug\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScoreBugOverlay, { overlay, size: sz }),\n overlay.type === \"lower_third\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LowerThirdOverlay, { overlay, size: sz }),\n overlay.type === \"qr_code\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(QrCodeOverlay, { overlay, size: sz }),\n overlay.type === \"coming_up_next\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComingUpNextOverlay, { overlay, size: sz }),\n overlay.type === \"contextual_trigger\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextualTriggerOverlay, { overlay, size: sz }),\n overlay.type === \"odds_betting\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OddsBettingOverlay, { overlay, size: sz }),\n overlay.type === \"breaking_news\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BreakingNewsOverlay, { overlay, size: sz }),\n overlay.type === \"countdown\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CountdownOverlay, { overlay, size: sz })\n ]\n },\n overlay.id\n );\n })\n }\n );\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n OverlayRenderer\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/ui/OverlayRenderer.cjs","../../src/ui/OverlayRenderer.tsx","../../src/utils/overlays.ts"],"names":["__create","Object","create","__defProp","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__getProtoOf","getPrototypeOf","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toESM","mod","isNodeMode","__esModule","value","__toCommonJS","OverlayRenderer_exports","OverlayRenderer","import_react","require","OVERLAY_API_BASE","timeStringToSeconds","timeStr","split","parts","parseInt","secStr","indexOf","dotIdx","substring","msFrag","padEnd","hours","minutes","seconds","ms","length","parseFloat","num","Math","max","visible","startSec","overlay","start_time","durationSec","currentTime","resolveImageUrl","imageUrl","apiBaseUrl","startsWith","url","URL","origin","import_jsx_runtime","computeVideoDimensions","video","videoWidth","nativeHeight","videoHeight","nativeWidth","displayWidth","offsetWidth","displayHeight","offsetHeight","videoAspect","displayAspect","renderWidth","renderHeight","offsetX","offsetY","scaleX","scaleY","ImageOverlay","src","image_url","jsx","alt","draggable","style","width","height","objectFit","display","pointerEvents","userSelect","TextOverlay","text","content","alignItems","justifyContent","color","fontSize","fontFamily","fontWeight","textAlign","padding","boxSizing","wordBreak","textShadow","lineHeight","children","fetchRSSItems","rssUrl","maxItems","resp","data","parser","doc","fetch","encodeURIComponent","json","contents","Error","DOMParser","parseFromString","querySelector","Array","querySelectorAll","map","item","title","textContent","replace","trim","description","pubDate","author","category","filter","i","slice","ScrollerOverlay","cfg","scroller_config","uid","useId","useState","rssItems","setRssItems","rssLoading","setRssLoading","rss_url","max_items","autoRefresh","auto_refresh","updateInterval","update_interval","useEffect","use_custom_text","custom_text","cancelled","then","items","catch","finally","interval","setInterval","clearInterval","sep","separator_char","segments","show_title","push","show_description","show_timestamp","Date","toLocaleDateString","show_author","show_category","join","scrollSpeed","direction","scroll_speed","font_size","font_weight","textColor","text_color","bgColor","background_color","bgOpacity","background_opacity","borderRadius","border_radius","itemSpacing","item_spacing","label","labelLine2","label_line2","labelColor","label_color","labelTextColor","label_text_color","accentColor","accent_color","showAccentLine","show_accent_line","isHorizontal","isReverse","fullText","animId","id","keyframes","jsxs","Fragment","flexDirection","overflow","backgroundColor","hexToRgb","background","flexShrink","flex","minHeight","minWidth","gap","letterSpacing","textTransform","whiteSpace","opacity","position","animation","willChange","copy","paddingRight","seg","React","margin"],"mappings":"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","sourcesContent":["\"use strict\";\nvar __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n // If the importer is in node compatibility mode or this is not an ESM\n // file that has been converted to a CommonJS file using a Babel-\n // compatible transform (i.e. \"__esModule\" has not been set), then set\n // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n isNodeMode || !mod || !mod.__esModule ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target,\n mod\n));\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/ui/OverlayRenderer.tsx\nvar OverlayRenderer_exports = {};\n__export(OverlayRenderer_exports, {\n OverlayRenderer: () => OverlayRenderer\n});\nmodule.exports = __toCommonJS(OverlayRenderer_exports);\nvar import_react = __toESM(require(\"react\"), 1);\n\n// src/utils/overlays.ts\nvar OVERLAY_API_BASE = \"https://adstorm.co/api-adstorm-dev\";\nfunction timeStringToSeconds(timeStr) {\n if (!timeStr) return 0;\n const parts = timeStr.split(\":\");\n if (parts.length >= 3) {\n const hours = parseInt(parts[0] ?? \"0\", 10) || 0;\n const minutes = parseInt(parts[1] ?? \"0\", 10) || 0;\n const secStr = parts[2] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return hours * 3600 + minutes * 60 + seconds + ms / 1e3;\n }\n if (parts.length === 2) {\n const minutes = parseInt(parts[0] ?? \"0\", 10) || 0;\n const secStr = parts[1] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return minutes * 60 + seconds + ms / 1e3;\n }\n const num = parseFloat(timeStr);\n return isFinite(num) ? Math.max(0, num) : 0;\n}\nfunction isOverlayActive(overlay, currentTime) {\n if (!overlay.visible) return false;\n const startSec = timeStringToSeconds(overlay.start_time);\n const durationSec = timeStringToSeconds(overlay.duration);\n if (durationSec <= 0) return false;\n return currentTime >= startSec && currentTime < startSec + durationSec;\n}\nfunction resolveImageUrl(imageUrl, apiBaseUrl = OVERLAY_API_BASE) {\n if (!imageUrl) return \"\";\n if (imageUrl.startsWith(\"http://\") || imageUrl.startsWith(\"https://\")) {\n return imageUrl;\n }\n if (imageUrl.startsWith(\"/\")) {\n try {\n const url = new URL(apiBaseUrl);\n return `${url.origin}${imageUrl}`;\n } catch {\n return imageUrl;\n }\n }\n return `${apiBaseUrl}/${imageUrl}`;\n}\n\n// src/ui/OverlayRenderer.tsx\nvar import_jsx_runtime = require(\"react/jsx-runtime\");\nfunction computeVideoDimensions(video) {\n const nativeWidth = video.videoWidth;\n const nativeHeight = video.videoHeight;\n if (!nativeWidth || !nativeHeight) return null;\n const displayWidth = video.offsetWidth;\n const displayHeight = video.offsetHeight;\n if (!displayWidth || !displayHeight) return null;\n const videoAspect = nativeWidth / nativeHeight;\n const displayAspect = displayWidth / displayHeight;\n let renderWidth;\n let renderHeight;\n let offsetX;\n let offsetY;\n if (videoAspect > displayAspect) {\n renderWidth = displayWidth;\n renderHeight = displayWidth / videoAspect;\n offsetX = 0;\n offsetY = (displayHeight - renderHeight) / 2;\n } else {\n renderHeight = displayHeight;\n renderWidth = displayHeight * videoAspect;\n offsetX = (displayWidth - renderWidth) / 2;\n offsetY = 0;\n }\n return {\n nativeWidth,\n nativeHeight,\n displayWidth: renderWidth,\n displayHeight: renderHeight,\n offsetX,\n offsetY,\n scaleX: renderWidth / nativeWidth,\n scaleY: renderHeight / nativeHeight\n };\n}\nfunction ImageOverlay({ overlay }) {\n const src = resolveImageUrl(overlay.image_url || \"\");\n if (!src) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"img\",\n {\n src,\n alt: overlay.name,\n draggable: false,\n style: {\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n pointerEvents: \"none\",\n userSelect: \"none\"\n }\n }\n );\n}\nfunction TextOverlay({ overlay }) {\n const text = overlay.content || \"\";\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"#ffffff\",\n fontSize: \"clamp(10px, 1.4vw, 20px)\",\n fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\",\n fontWeight: 600,\n textAlign: \"center\",\n padding: \"4px 8px\",\n boxSizing: \"border-box\",\n wordBreak: \"break-word\",\n textShadow: \"0 1px 4px rgba(0,0,0,0.7)\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n lineHeight: 1.3\n },\n children: text\n }\n );\n}\nasync function fetchRSSItems(rssUrl, maxItems) {\n const resp = await fetch(\n `https://api.allorigins.win/get?url=${encodeURIComponent(rssUrl)}`\n );\n const data = await resp.json();\n if (!data.contents) throw new Error(\"No content from RSS feed\");\n const parser = new DOMParser();\n const doc = parser.parseFromString(data.contents, \"text/xml\");\n if (doc.querySelector(\"parsererror\")) throw new Error(\"Invalid RSS XML\");\n return Array.from(doc.querySelectorAll(\"item\")).map((item) => ({\n title: (item.querySelector(\"title\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n description: (item.querySelector(\"description\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n pubDate: item.querySelector(\"pubDate\")?.textContent || \"\",\n author: item.querySelector(\"author, dc\\\\:creator\")?.textContent || \"\",\n category: item.querySelector(\"category\")?.textContent || \"\"\n })).filter((i) => i.title).slice(0, maxItems);\n}\nfunction ScrollerOverlay({ overlay }) {\n const cfg = overlay.scroller_config;\n const uid = (0, import_react.useId)().replace(/:/g, \"\");\n const [rssItems, setRssItems] = (0, import_react.useState)([]);\n const [rssLoading, setRssLoading] = (0, import_react.useState)(false);\n const rssUrl = cfg?.rss_url || \"\";\n const maxItems = cfg?.max_items ?? 10;\n const autoRefresh = cfg?.auto_refresh !== false;\n const updateInterval = cfg?.update_interval ?? 5;\n (0, import_react.useEffect)(() => {\n if (!rssUrl || cfg?.use_custom_text && cfg?.custom_text) return;\n let cancelled = false;\n setRssLoading(true);\n fetchRSSItems(rssUrl, maxItems).then((items) => {\n if (!cancelled) setRssItems(items);\n }).catch(() => {\n }).finally(() => {\n if (!cancelled) setRssLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [rssUrl, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n (0, import_react.useEffect)(() => {\n if (!rssUrl || !autoRefresh || cfg?.use_custom_text && cfg?.custom_text) return;\n const interval = setInterval(() => {\n fetchRSSItems(rssUrl, maxItems).then(setRssItems).catch(() => {\n });\n }, updateInterval * 60 * 1e3);\n return () => clearInterval(interval);\n }, [rssUrl, autoRefresh, updateInterval, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n const sep = cfg?.separator_char ?? \"\\u25C6\";\n let segments;\n if (cfg?.use_custom_text && cfg?.custom_text) {\n segments = [cfg.custom_text];\n } else if (rssItems.length > 0) {\n segments = rssItems.map((item) => {\n const parts = [];\n if (cfg?.show_title !== false && item.title) parts.push(item.title);\n if (cfg?.show_description && item.description) parts.push(item.description);\n if (cfg?.show_timestamp && item.pubDate) {\n try {\n parts.push(new Date(item.pubDate).toLocaleDateString());\n } catch {\n }\n }\n if (cfg?.show_author && item.author) parts.push(`\\u2014 ${item.author}`);\n if (cfg?.show_category && item.category) parts.push(`[${item.category}]`);\n return parts.join(\" \");\n });\n } else if (rssLoading) {\n segments = [\"Loading feed\\u2026\"];\n } else if (overlay.content) {\n segments = [overlay.content];\n } else {\n segments = rssUrl ? [\"Fetching RSS feed\\u2026\"] : [\"RSS Ticker\"];\n }\n const scrollSpeed = cfg?.scroll_speed ?? 40;\n const direction = cfg?.direction ?? \"left\";\n const fontSize = cfg?.font_size ?? 15;\n const fontFamily = cfg?.font_family || \"Roboto, 'Segoe UI', Arial, sans-serif\";\n const fontWeight = cfg?.font_weight || \"700\";\n const textColor = cfg?.text_color || \"#ffffff\";\n const bgColor = cfg?.background_color || \"#0d0d1a\";\n const bgOpacity = cfg?.background_opacity !== void 0 ? cfg.background_opacity / 100 : 0.95;\n const borderRadius = cfg?.border_radius ?? 0;\n const itemSpacing = cfg?.item_spacing ?? 60;\n const label = cfg?.label ?? \"NEWS\";\n const labelLine2 = cfg?.label_line2 ?? \"\";\n const labelColor = cfg?.label_color ?? \"#f97316\";\n const labelTextColor = cfg?.label_text_color ?? \"#ffffff\";\n const accentColor = cfg?.accent_color ?? labelColor;\n const showAccentLine = cfg?.show_accent_line !== false;\n const isHorizontal = direction === \"left\" || direction === \"right\";\n const isReverse = direction === \"right\" || direction === \"down\";\n const fullText = segments.join(` ${sep} `);\n const durationSec = Math.max(6, fullText.length * 9 / scrollSpeed);\n const animId = `sc-ticker-${overlay.id}-${uid}`;\n const keyframes = isHorizontal ? `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateX(-50%); } 100% { transform: translateX(0%); }\" : \"0% { transform: translateX(0); } 100% { transform: translateX(-50%); }\"}\n }` : `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateY(-50%); } 100% { transform: translateY(0%); }\" : \"0% { transform: translateY(0); } 100% { transform: translateY(-50%); }\"}\n }`;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"style\", { children: keyframes }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRadius: borderRadius > 0 ? `${borderRadius}px` : void 0,\n backgroundColor: `rgba(${hexToRgb(bgColor)}, ${bgOpacity})`,\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight,\n color: textColor,\n pointerEvents: \"none\",\n userSelect: \"none\"\n },\n children: [\n showAccentLine && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { height: 3, background: accentColor, flexShrink: 0, width: \"100%\" } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", flex: 1, overflow: \"hidden\", minHeight: 0 }, children: [\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n background: labelColor,\n color: labelTextColor,\n padding: \"0 14px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n minWidth: 72,\n textAlign: \"center\",\n gap: 1\n },\n children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 800,\n fontSize: \"0.82em\",\n letterSpacing: \"0.05em\",\n lineHeight: 1.1,\n textTransform: \"uppercase\",\n whiteSpace: \"nowrap\"\n },\n children: label\n }\n ),\n labelLine2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 500,\n fontSize: \"0.62em\",\n letterSpacing: \"0.03em\",\n lineHeight: 1.1,\n opacity: 0.85,\n whiteSpace: \"nowrap\"\n },\n children: labelLine2\n }\n )\n ]\n }\n ),\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: 3, background: accentColor, flexShrink: 0 } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n flex: 1,\n overflow: \"hidden\",\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\"\n },\n children: isHorizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"inline-flex\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map((copy) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { paddingRight: `${itemSpacing}px` }, children: segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { opacity: 0.5, margin: \"0 8px\" }, children: sep }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { textShadow: \"0 1px 3px rgba(0,0,0,0.6)\" }, children: seg })\n ] }, i)) }, copy))\n }\n ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"flex\",\n flexDirection: \"column\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map(\n (copy) => segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { paddingBottom: `${itemSpacing / 4}px` }, children: seg }, `${copy}-${i}`))\n )\n }\n )\n }\n )\n ] })\n ]\n }\n )\n ] });\n}\nfunction parseConfig(content) {\n if (!content) return null;\n try {\n return JSON.parse(content);\n } catch {\n return null;\n }\n}\nfunction ScoreBugOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.058);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", alignItems: \"center\", padding: `0 ${f * 0.8}px`, gap: f * 0.4 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.homeTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.homeScore })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.8em\", textAlign: \"center\", opacity: 0.7, padding: `0 ${f * 0.4}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.period }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.clock })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.awayTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.awayScore })\n ] })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", textAlign: \"center\", opacity: 0.6, padding: `${f * 0.2}px ${f * 0.4}px`, borderTop: `1px solid ${cfg.accentColor}40`, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction LowerThirdOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", flexDirection: \"column\", justifyContent: \"flex-end\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: Math.max(2, size.h * 0.06), backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.5}px ${f * 1.2}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.4em\", fontWeight: 700, lineHeight: 1.2, textShadow: \"0 1px 4px rgba(0,0,0,0.5)\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", opacity: 0.7, marginTop: f * 0.2 }, children: cfg.subtitle })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.5, padding: `0 ${f * 1.2}px ${f * 0.4}px`, display: \"flex\", alignItems: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction QrCodeOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const qrSide = Math.max(32, Math.min(size.w, size.h) * 0.55);\n const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${Math.round(qrSide * 2)}x${Math.round(qrSide * 2)}&data=${encodeURIComponent(cfg.url || \"https://example.com\")}`;\n const f = Math.max(6, size.w * 0.06);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", padding: f * 0.6, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", overflow: \"hidden\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, background: \"#fff\", borderRadius: Math.max(2, qrSide * 0.06), padding: Math.max(2, qrSide * 0.06), lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: qrUrl, alt: \"QR Code\", style: { width: `${qrSide}px`, height: `${qrSide}px`, display: \"block\" } }) }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 1.1}px`, fontWeight: 700, textAlign: \"center\", color: cfg.accentColor, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.ctaText }),\n cfg.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 0.75}px`, opacity: 0.6, textAlign: \"center\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.description })\n ] });\n}\nfunction ComingUpNextOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: Math.max(2, size.w * 0.015), flexShrink: 0, backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.6}px ${f * 1}px`, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor }, children: \"Coming Up Next\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.5em\", fontWeight: 700, lineHeight: 1.2, marginTop: f * 0.2, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.title }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.6, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.subtitle }),\n cfg.scheduledTime && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 600, marginTop: f * 0.4, color: cfg.accentColor }, children: cfg.scheduledTime })\n ] }),\n cfg.thumbnailUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, width: Math.max(40, size.h * 0.75), overflow: \"hidden\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.thumbnailUrl, alt: \"thumbnail\", style: { width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" } }) })\n ] });\n}\nfunction ContextualTriggerOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const icons = { alert: \"\\u26A0\\uFE0F\", celebration: \"\\u{1F389}\", info: \"\\u2139\\uFE0F\", warning: \"\\u{1F514}\" };\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", alignItems: \"center\", gap: f * 0.8, padding: `0 ${f * 1.2}px`, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", borderLeft: `${Math.max(2, size.w * 0.02)}px solid ${cfg.accentColor}`, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontSize: \"2em\", flexShrink: 0 }, children: icons[cfg.iconType] || \"\\u26A1\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.message })\n ] })\n ] });\n}\nfunction OddsBettingOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.052);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventTitle }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", gap: f * 0.2, justifyContent: \"center\" }, children: (cfg.options || []).slice(0, 5).map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: `${f * 0.2}px ${f * 0.6}px`, borderRadius: Math.max(2, f * 0.3), background: `${cfg.accentColor}15`, fontSize: \"1em\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", flex: 1 }, children: opt.label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontWeight: 700, marginLeft: f * 0.8, flexShrink: 0, color: cfg.accentColor }, children: opt.odds })\n ] }, i)) }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.4, textAlign: \"center\", marginTop: f * 0.4 }, children: cfg.sponsorText })\n ] });\n}\nfunction BreakingNewsOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const urgencyColors = { breaking: \"#dc2626\", urgent: \"#ea580c\", normal: \"#2563eb\" };\n const labelBg = urgencyColors[cfg.urgency] || urgencyColors.normal;\n const label = cfg.urgency === \"breaking\" ? \"BREAKING\" : cfg.urgency === \"urgent\" ? \"URGENT\" : \"NEWS\";\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", alignItems: \"center\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { padding: `0 ${f * 0.8}px`, height: \"100%\", display: \"flex\", alignItems: \"center\", background: labelBg, color: \"#fff\", fontSize: \"1em\", fontWeight: 900, textTransform: \"uppercase\", letterSpacing: \"0.05em\", flexShrink: 0 }, children: label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, padding: `0 ${f * 1}px`, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n cfg.body && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.body })\n ] })\n ] });\n}\nfunction calcCountdownRemaining(targetTime) {\n const diff = Math.max(0, new Date(targetTime).getTime() - Date.now());\n return {\n d: Math.floor(diff / 864e5),\n h: Math.floor(diff % 864e5 / 36e5),\n m: Math.floor(diff % 36e5 / 6e4),\n s: Math.floor(diff % 6e4 / 1e3),\n expired: diff === 0\n };\n}\nfunction CountdownOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n const targetTime = cfg?.targetTime ?? \"\";\n const [remaining, setRemaining] = (0, import_react.useState)(\n () => targetTime ? calcCountdownRemaining(targetTime) : { d: 0, h: 0, m: 0, s: 0, expired: false }\n );\n (0, import_react.useEffect)(() => {\n if (!targetTime) return;\n setRemaining(calcCountdownRemaining(targetTime));\n const id = setInterval(() => setRemaining(calcCountdownRemaining(targetTime)), 1e3);\n return () => clearInterval(id);\n }, [targetTime]);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n const pad = (n) => String(n).padStart(2, \"0\");\n const units = [\n { show: cfg.showDays, value: pad(remaining.d), label: \"DAYS\" },\n { show: cfg.showHours, value: pad(remaining.h), label: \"HRS\" },\n { show: cfg.showMinutes, value: pad(remaining.m), label: \"MIN\" },\n { show: cfg.showSeconds, value: pad(remaining.s), label: \"SEC\" }\n ];\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventName }),\n remaining.expired ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700, opacity: 0.6 }, children: cfg.message || \"Event ended\" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { display: \"flex\", gap: f * 0.6, alignItems: \"center\" }, children: units.filter((u) => u.show).map((u, i, arr) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"2em\", fontWeight: 900, lineHeight: 1, borderRadius: Math.max(2, f * 0.4), padding: `${f * 0.2}px ${f * 0.4}px`, background: `${cfg.accentColor}20` }, children: u.value }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.5em\", opacity: 0.5, marginTop: f * 0.2 }, children: u.label })\n ] }),\n i < arr.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 700, opacity: 0.3 }, children: \":\" })\n ] }, u.label)) }),\n !remaining.expired && cfg.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", opacity: 0.6, marginTop: f * 0.4, textAlign: \"center\" }, children: cfg.message })\n ] });\n}\nfunction ShapeOverlay({ overlay, size }) {\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.03), background: \"rgba(99, 102, 241, 0.2)\", border: \"2px solid rgba(99, 102, 241, 0.4)\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", pointerEvents: \"none\", userSelect: \"none\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f}px`, fontWeight: 500, color: \"rgba(163, 163, 163, 0.8)\", textTransform: \"uppercase\" }, children: overlay.name }) });\n}\nfunction hexToRgb(hex) {\n if (!hex || !hex.startsWith(\"#\")) return \"0,0,0\";\n const clean = hex.slice(1);\n const num = parseInt(clean.length === 3 ? clean.replace(/./g, \"$&$&\") : clean, 16);\n return `${num >> 16 & 255},${num >> 8 & 255},${num & 255}`;\n}\nvar FADE_DURATION_MS = 1e3;\nvar OverlayRenderer = ({\n overlays,\n currentTime,\n videoRef,\n coordinateSpace\n}) => {\n const [dims, setDims] = (0, import_react.useState)(null);\n const rafRef = (0, import_react.useRef)(null);\n const [fadeMap, setFadeMap] = (0, import_react.useState)(/* @__PURE__ */ new Map());\n const removeTimers = (0, import_react.useRef)(/* @__PURE__ */ new Map());\n const updateDims = (0, import_react.useCallback)(() => {\n const video = videoRef.current;\n if (video) {\n const computed = computeVideoDimensions(video);\n setDims((prev) => {\n if (!computed || prev && prev.nativeWidth === computed.nativeWidth && prev.nativeHeight === computed.nativeHeight && prev.displayWidth === computed.displayWidth && prev.displayHeight === computed.displayHeight && prev.offsetX === computed.offsetX && prev.offsetY === computed.offsetY) {\n return prev;\n }\n return computed;\n });\n }\n }, [videoRef]);\n (0, import_react.useEffect)(() => {\n updateDims();\n const interval = setInterval(updateDims, 500);\n const handleResize = () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(updateDims);\n };\n window.addEventListener(\"resize\", handleResize);\n return () => {\n clearInterval(interval);\n window.removeEventListener(\"resize\", handleResize);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [updateDims]);\n const activeOverlays = (0, import_react.useMemo)(\n () => overlays.filter((o) => isOverlayActive(o, currentTime)),\n [overlays, currentTime]\n );\n (0, import_react.useEffect)(() => {\n const activeIds = new Set(activeOverlays.map((o) => o.id));\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const overlay of activeOverlays) {\n if (!next.has(overlay.id)) {\n next.set(overlay.id, { overlay, visible: false });\n } else {\n const existing = next.get(overlay.id);\n next.set(overlay.id, { ...existing, overlay });\n }\n }\n for (const [id, state] of next) {\n if (!activeIds.has(id) && state.visible) {\n next.set(id, { ...state, visible: false });\n if (!removeTimers.current.has(id)) {\n const timer = setTimeout(() => {\n setFadeMap((m) => {\n const updated = new Map(m);\n updated.delete(id);\n return updated;\n });\n removeTimers.current.delete(id);\n }, FADE_DURATION_MS);\n removeTimers.current.set(id, timer);\n }\n } else if (!activeIds.has(id) && !state.visible) {\n }\n }\n return next;\n });\n }, [activeOverlays]);\n (0, import_react.useEffect)(() => {\n const toFadeIn = [];\n for (const [id, state] of fadeMap) {\n if (!state.visible) {\n const isActive = activeOverlays.some((o) => o.id === id);\n if (isActive) toFadeIn.push(id);\n }\n }\n if (toFadeIn.length === 0) return;\n const raf = requestAnimationFrame(() => {\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const id of toFadeIn) {\n const state = next.get(id);\n if (state) next.set(id, { ...state, visible: true });\n }\n return next;\n });\n });\n return () => cancelAnimationFrame(raf);\n }, [fadeMap, activeOverlays]);\n (0, import_react.useEffect)(() => {\n return () => {\n for (const timer of removeTimers.current.values()) clearTimeout(timer);\n };\n }, []);\n if (!dims || fadeMap.size === 0) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n \"aria-hidden\": \"true\",\n style: {\n position: \"absolute\",\n left: `${dims.offsetX}px`,\n top: `${dims.offsetY}px`,\n width: `${dims.displayWidth}px`,\n height: `${dims.displayHeight}px`,\n pointerEvents: \"none\",\n overflow: \"hidden\",\n zIndex: 8\n },\n children: [...fadeMap.values()].map(({ overlay, visible }) => {\n const scaleX = coordinateSpace?.width ? dims.displayWidth / coordinateSpace.width : dims.scaleX;\n const scaleY = coordinateSpace?.height ? dims.displayHeight / coordinateSpace.height : dims.scaleY;\n const left = overlay.x * scaleX;\n const top = overlay.y * scaleY;\n const width = overlay.width * scaleX;\n const height = overlay.height * scaleY;\n const baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;\n const opacity = visible ? baseOpacity : 0;\n const sz = { w: width, h: height };\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n position: \"absolute\",\n left: `${left}px`,\n top: `${top}px`,\n width: `${width}px`,\n height: `${height}px`,\n opacity,\n transition: `opacity ${FADE_DURATION_MS}ms ease`,\n zIndex: overlay.z_index,\n overflow: \"hidden\"\n },\n children: [\n overlay.type === \"image\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImageOverlay, { overlay }),\n overlay.type === \"text\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextOverlay, { overlay }),\n overlay.type === \"scroller\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollerOverlay, { overlay }),\n overlay.type === \"shape\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShapeOverlay, { overlay, size: sz }),\n overlay.type === \"score_bug\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScoreBugOverlay, { overlay, size: sz }),\n overlay.type === \"lower_third\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LowerThirdOverlay, { overlay, size: sz }),\n overlay.type === \"qr_code\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(QrCodeOverlay, { overlay, size: sz }),\n overlay.type === \"coming_up_next\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComingUpNextOverlay, { overlay, size: sz }),\n overlay.type === \"contextual_trigger\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextualTriggerOverlay, { overlay, size: sz }),\n overlay.type === \"odds_betting\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OddsBettingOverlay, { overlay, size: sz }),\n overlay.type === \"breaking_news\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BreakingNewsOverlay, { overlay, size: sz }),\n overlay.type === \"countdown\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CountdownOverlay, { overlay, size: sz })\n ]\n },\n overlay.id\n );\n })\n }\n );\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n OverlayRenderer\n});\n","import React, { useEffect, useRef, useState, useCallback, useMemo, useId } from \"react\";\nimport {\n type SwirlOverlay,\n isOverlayActive,\n resolveImageUrl,\n} from \"../utils/overlays\";\n\ninterface VideoDimensions {\n nativeWidth: number;\n nativeHeight: number;\n displayWidth: number;\n displayHeight: number;\n offsetX: number;\n offsetY: number;\n scaleX: number;\n scaleY: number;\n}\n\ninterface OverlayRendererProps {\n overlays: SwirlOverlay[];\n currentTime: number;\n videoRef: React.RefObject<HTMLVideoElement | null>;\n coordinateSpace?: { width: number; height: number } | null;\n}\n\nfunction computeVideoDimensions(\n video: HTMLVideoElement\n): VideoDimensions | null {\n const nativeWidth = video.videoWidth;\n const nativeHeight = video.videoHeight;\n if (!nativeWidth || !nativeHeight) return null;\n\n const displayWidth = video.offsetWidth;\n const displayHeight = video.offsetHeight;\n if (!displayWidth || !displayHeight) return null;\n\n const videoAspect = nativeWidth / nativeHeight;\n const displayAspect = displayWidth / displayHeight;\n\n let renderWidth: number;\n let renderHeight: number;\n let offsetX: number;\n let offsetY: number;\n\n if (videoAspect > displayAspect) {\n renderWidth = displayWidth;\n renderHeight = displayWidth / videoAspect;\n offsetX = 0;\n offsetY = (displayHeight - renderHeight) / 2;\n } else {\n renderHeight = displayHeight;\n renderWidth = displayHeight * videoAspect;\n offsetX = (displayWidth - renderWidth) / 2;\n offsetY = 0;\n }\n\n return {\n nativeWidth,\n nativeHeight,\n displayWidth: renderWidth,\n displayHeight: renderHeight,\n offsetX,\n offsetY,\n scaleX: renderWidth / nativeWidth,\n scaleY: renderHeight / nativeHeight,\n };\n}\n\nfunction ImageOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const src = resolveImageUrl(overlay.image_url || \"\");\n if (!src) return null;\n return (\n <img\n src={src}\n alt={overlay.name}\n draggable={false}\n style={{\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n />\n );\n}\n\nfunction TextOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const text = overlay.content || \"\";\n return (\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"#ffffff\",\n fontSize: \"clamp(10px, 1.4vw, 20px)\",\n fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\",\n fontWeight: 600,\n textAlign: \"center\",\n padding: \"4px 8px\",\n boxSizing: \"border-box\",\n wordBreak: \"break-word\",\n textShadow: \"0 1px 4px rgba(0,0,0,0.7)\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n lineHeight: 1.3,\n }}\n >\n {text}\n </div>\n );\n}\n\ninterface RSSItem {\n title: string;\n description: string;\n pubDate: string;\n author: string;\n category: string;\n}\n\nasync function fetchRSSItems(rssUrl: string, maxItems: number): Promise<RSSItem[]> {\n const resp = await fetch(\n `https://api.allorigins.win/get?url=${encodeURIComponent(rssUrl)}`\n );\n const data = await resp.json();\n if (!data.contents) throw new Error(\"No content from RSS feed\");\n const parser = new DOMParser();\n const doc = parser.parseFromString(data.contents, \"text/xml\");\n if (doc.querySelector(\"parsererror\")) throw new Error(\"Invalid RSS XML\");\n return Array.from(doc.querySelectorAll(\"item\"))\n .map((item) => ({\n title: (item.querySelector(\"title\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n description: (item.querySelector(\"description\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n pubDate: item.querySelector(\"pubDate\")?.textContent || \"\",\n author: item.querySelector(\"author, dc\\\\:creator\")?.textContent || \"\",\n category: item.querySelector(\"category\")?.textContent || \"\",\n }))\n .filter((i) => i.title)\n .slice(0, maxItems);\n}\n\nfunction ScrollerOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const cfg = overlay.scroller_config;\n const uid = useId().replace(/:/g, \"\");\n\n const [rssItems, setRssItems] = useState<RSSItem[]>([]);\n const [rssLoading, setRssLoading] = useState(false);\n\n const rssUrl = cfg?.rss_url || \"\";\n const maxItems = cfg?.max_items ?? 10;\n const autoRefresh = cfg?.auto_refresh !== false;\n const updateInterval = cfg?.update_interval ?? 5;\n\n useEffect(() => {\n if (!rssUrl || (cfg?.use_custom_text && cfg?.custom_text)) return;\n let cancelled = false;\n setRssLoading(true);\n fetchRSSItems(rssUrl, maxItems)\n .then((items) => { if (!cancelled) setRssItems(items); })\n .catch(() => { /* silent fail — show placeholder */ })\n .finally(() => { if (!cancelled) setRssLoading(false); });\n return () => { cancelled = true; };\n }, [rssUrl, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n\n useEffect(() => {\n if (!rssUrl || !autoRefresh || (cfg?.use_custom_text && cfg?.custom_text)) return;\n const interval = setInterval(() => {\n fetchRSSItems(rssUrl, maxItems)\n .then(setRssItems)\n .catch(() => { /* silent */ });\n }, updateInterval * 60 * 1000);\n return () => clearInterval(interval);\n }, [rssUrl, autoRefresh, updateInterval, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n\n // ── Build text segments ──────────────────────────────────────────────────\n const sep = cfg?.separator_char ?? \"◆\";\n\n let segments: string[];\n if (cfg?.use_custom_text && cfg?.custom_text) {\n segments = [cfg.custom_text];\n } else if (rssItems.length > 0) {\n segments = rssItems.map((item) => {\n const parts: string[] = [];\n if (cfg?.show_title !== false && item.title) parts.push(item.title);\n if (cfg?.show_description && item.description) parts.push(item.description);\n if (cfg?.show_timestamp && item.pubDate) {\n try { parts.push(new Date(item.pubDate).toLocaleDateString()); } catch { /* ignore */ }\n }\n if (cfg?.show_author && item.author) parts.push(`— ${item.author}`);\n if (cfg?.show_category && item.category) parts.push(`[${item.category}]`);\n return parts.join(\" \");\n });\n } else if (rssLoading) {\n segments = [\"Loading feed…\"];\n } else if (overlay.content) {\n segments = [overlay.content];\n } else {\n segments = rssUrl ? [\"Fetching RSS feed…\"] : [\"RSS Ticker\"];\n }\n\n // ── Style ────────────────────────────────────────────────────────────────\n const scrollSpeed = cfg?.scroll_speed ?? 40;\n const direction = cfg?.direction ?? \"left\";\n const fontSize = cfg?.font_size ?? 15;\n const fontFamily = cfg?.font_family || \"Roboto, 'Segoe UI', Arial, sans-serif\";\n const fontWeight = cfg?.font_weight || \"700\";\n const textColor = cfg?.text_color || \"#ffffff\";\n const bgColor = cfg?.background_color || \"#0d0d1a\";\n const bgOpacity = cfg?.background_opacity !== undefined ? cfg.background_opacity / 100 : 0.95;\n const borderRadius = cfg?.border_radius ?? 0;\n const itemSpacing = cfg?.item_spacing ?? 60;\n\n const label = cfg?.label ?? \"NEWS\";\n const labelLine2 = cfg?.label_line2 ?? \"\";\n const labelColor = cfg?.label_color ?? \"#f97316\";\n const labelTextColor = cfg?.label_text_color ?? \"#ffffff\";\n const accentColor = cfg?.accent_color ?? labelColor;\n const showAccentLine = cfg?.show_accent_line !== false;\n\n const isHorizontal = direction === \"left\" || direction === \"right\";\n const isReverse = direction === \"right\" || direction === \"down\";\n\n // Duration: ~9px per char / speed px/s\n const fullText = segments.join(` ${sep} `);\n const durationSec = Math.max(6, (fullText.length * 9) / scrollSpeed);\n\n const animId = `sc-ticker-${overlay.id}-${uid}`;\n const keyframes = isHorizontal\n ? `@keyframes ${animId} {\n ${isReverse\n ? \"0% { transform: translateX(-50%); } 100% { transform: translateX(0%); }\"\n : \"0% { transform: translateX(0); } 100% { transform: translateX(-50%); }\"}\n }`\n : `@keyframes ${animId} {\n ${isReverse\n ? \"0% { transform: translateY(-50%); } 100% { transform: translateY(0%); }\"\n : \"0% { transform: translateY(0); } 100% { transform: translateY(-50%); }\"}\n }`;\n\n return (\n <>\n <style>{keyframes}</style>\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRadius: borderRadius > 0 ? `${borderRadius}px` : undefined,\n backgroundColor: `rgba(${hexToRgb(bgColor)}, ${bgOpacity})`,\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight,\n color: textColor,\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n >\n {/* Top accent line */}\n {showAccentLine && (\n <div style={{ height: 3, background: accentColor, flexShrink: 0, width: \"100%\" }} />\n )}\n\n {/* Main row */}\n <div style={{ display: \"flex\", flex: 1, overflow: \"hidden\", minHeight: 0 }}>\n {/* Label badge */}\n {label && (\n <div\n style={{\n background: labelColor,\n color: labelTextColor,\n padding: \"0 14px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n minWidth: 72,\n textAlign: \"center\",\n gap: 1,\n }}\n >\n <span\n style={{\n fontWeight: 800,\n fontSize: \"0.82em\",\n letterSpacing: \"0.05em\",\n lineHeight: 1.1,\n textTransform: \"uppercase\",\n whiteSpace: \"nowrap\",\n }}\n >\n {label}\n </span>\n {labelLine2 && (\n <span\n style={{\n fontWeight: 500,\n fontSize: \"0.62em\",\n letterSpacing: \"0.03em\",\n lineHeight: 1.1,\n opacity: 0.85,\n whiteSpace: \"nowrap\",\n }}\n >\n {labelLine2}\n </span>\n )}\n </div>\n )}\n\n {/* Accent divider */}\n {label && (\n <div style={{ width: 3, background: accentColor, flexShrink: 0 }} />\n )}\n\n {/* Scrolling text */}\n <div\n style={{\n flex: 1,\n overflow: \"hidden\",\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\",\n }}\n >\n {isHorizontal ? (\n <div\n style={{\n display: \"inline-flex\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\",\n }}\n >\n {[0, 1].map((copy) => (\n <span key={copy} style={{ paddingRight: `${itemSpacing}px` }}>\n {segments.map((seg, i) => (\n <React.Fragment key={i}>\n {i > 0 && (\n <span style={{ opacity: 0.5, margin: \"0 8px\" }}>{sep}</span>\n )}\n <span style={{ textShadow: \"0 1px 3px rgba(0,0,0,0.6)\" }}>{seg}</span>\n </React.Fragment>\n ))}\n </span>\n ))}\n </div>\n ) : (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\",\n }}\n >\n {[0, 1].map((copy) =>\n segments.map((seg, i) => (\n <div key={`${copy}-${i}`} style={{ paddingBottom: `${itemSpacing / 4}px` }}>\n {seg}\n </div>\n ))\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n </>\n );\n}\n\nfunction parseConfig<T>(content?: string): T | null {\n if (!content) return null;\n try { return JSON.parse(content) as T; } catch { return null; }\n}\n\ninterface OverlaySize { w: number; h: number; }\n\ninterface ScoreBugCfg { homeTeam: string; awayTeam: string; homeScore: number; awayScore: number; period: string; clock: string; sponsorText: string; sponsorImageUrl: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface LowerThirdCfg { headline: string; subtitle: string; sponsorText: string; sponsorImageUrl: string; backgroundColor: string; textColor: string; accentColor: string; style: string; }\ninterface QrCodeCfg { url: string; ctaText: string; description: string; size: number; backgroundColor: string; textColor: string; accentColor: string; }\ninterface ComingUpNextCfg { title: string; subtitle: string; scheduledTime: string; thumbnailUrl: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface ContextualTriggerCfg { triggerType: string; headline: string; message: string; iconType: string; backgroundColor: string; textColor: string; accentColor: string; animationStyle: string; }\ninterface OddsBettingCfg { eventTitle: string; options: Array<{ label: string; odds: string }>; sponsorText: string; backgroundColor: string; textColor: string; accentColor: string; oddsFormat: string; }\ninterface BreakingNewsCfg { headline: string; body: string; urgency: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface CountdownCfg { eventName: string; targetTime: string; message: string; showDays: boolean; showHours: boolean; showMinutes: boolean; showSeconds: boolean; backgroundColor: string; textColor: string; accentColor: string; }\n\nfunction ScoreBugOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ScoreBugCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.058);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ flex: 1, display: \"flex\", alignItems: \"center\", padding: `0 ${f * 0.8}px`, gap: f * 0.4 }}>\n <div style={{ flex: 1, textAlign: \"center\" }}>\n <div style={{ fontSize: \"1em\", fontWeight: 700 }}>{cfg.homeTeam}</div>\n <div style={{ fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }}>{cfg.homeScore}</div>\n </div>\n <div style={{ fontSize: \"0.8em\", textAlign: \"center\", opacity: 0.7, padding: `0 ${f * 0.4}px` }}>\n <div>{cfg.period}</div>\n <div>{cfg.clock}</div>\n </div>\n <div style={{ flex: 1, textAlign: \"center\" }}>\n <div style={{ fontSize: \"1em\", fontWeight: 700 }}>{cfg.awayTeam}</div>\n <div style={{ fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }}>{cfg.awayScore}</div>\n </div>\n </div>\n {(cfg.sponsorText || cfg.sponsorImageUrl) && (\n <div style={{ fontSize: \"0.7em\", textAlign: \"center\", opacity: 0.6, padding: `${f * 0.2}px ${f * 0.4}px`, borderTop: `1px solid ${cfg.accentColor}40`, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, overflow: \"hidden\" }}>\n {cfg.sponsorImageUrl && <img src={cfg.sponsorImageUrl} alt=\"sponsor\" style={{ height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 }} />}\n {cfg.sponsorText && <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.sponsorText}</span>}\n </div>\n )}\n </div>\n );\n}\n\nfunction LowerThirdOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<LowerThirdCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", flexDirection: \"column\", justifyContent: \"flex-end\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ width: \"100%\", height: Math.max(2, size.h * 0.06), backgroundColor: cfg.accentColor }} />\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.5}px ${f * 1.2}px` }}>\n <div style={{ fontSize: \"1.4em\", fontWeight: 700, lineHeight: 1.2, textShadow: \"0 1px 4px rgba(0,0,0,0.5)\" }}>{cfg.headline}</div>\n <div style={{ fontSize: \"1em\", opacity: 0.7, marginTop: f * 0.2 }}>{cfg.subtitle}</div>\n </div>\n {(cfg.sponsorText || cfg.sponsorImageUrl) && (\n <div style={{ fontSize: \"0.7em\", opacity: 0.5, padding: `0 ${f * 1.2}px ${f * 0.4}px`, display: \"flex\", alignItems: \"center\", gap: f * 0.4, overflow: \"hidden\" }}>\n {cfg.sponsorImageUrl && <img src={cfg.sponsorImageUrl} alt=\"sponsor\" style={{ height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 }} />}\n {cfg.sponsorText && <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.sponsorText}</span>}\n </div>\n )}\n </div>\n );\n}\n\nfunction QrCodeOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<QrCodeCfg>(overlay.content);\n if (!cfg) return null;\n const qrSide = Math.max(32, Math.min(size.w, size.h) * 0.55);\n const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${Math.round(qrSide * 2)}x${Math.round(qrSide * 2)}&data=${encodeURIComponent(cfg.url || \"https://example.com\")}`;\n const f = Math.max(6, size.w * 0.06);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", padding: f * 0.6, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", overflow: \"hidden\" }}>\n <div style={{ flexShrink: 0, background: \"#fff\", borderRadius: Math.max(2, qrSide * 0.06), padding: Math.max(2, qrSide * 0.06), lineHeight: 0 }}>\n <img src={qrUrl} alt=\"QR Code\" style={{ width: `${qrSide}px`, height: `${qrSide}px`, display: \"block\" }} />\n </div>\n <div style={{ fontSize: `${f * 1.1}px`, fontWeight: 700, textAlign: \"center\", color: cfg.accentColor, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }}>{cfg.ctaText}</div>\n {cfg.description && <div style={{ fontSize: `${f * 0.75}px`, opacity: 0.6, textAlign: \"center\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }}>{cfg.description}</div>}\n </div>\n );\n}\n\nfunction ComingUpNextOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ComingUpNextCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ width: Math.max(2, size.w * 0.015), flexShrink: 0, backgroundColor: cfg.accentColor }} />\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.6}px ${f * 1.0}px`, minWidth: 0 }}>\n <div style={{ fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor }}>Coming Up Next</div>\n <div style={{ fontSize: \"1.5em\", fontWeight: 700, lineHeight: 1.2, marginTop: f * 0.2, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.title}</div>\n <div style={{ fontSize: \"0.9em\", opacity: 0.6, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.subtitle}</div>\n {cfg.scheduledTime && <div style={{ fontSize: \"1em\", fontWeight: 600, marginTop: f * 0.4, color: cfg.accentColor }}>{cfg.scheduledTime}</div>}\n </div>\n {cfg.thumbnailUrl && (\n <div style={{ flexShrink: 0, width: Math.max(40, size.h * 0.75), overflow: \"hidden\" }}>\n <img src={cfg.thumbnailUrl} alt=\"thumbnail\" style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }} />\n </div>\n )}\n </div>\n );\n}\n\nfunction ContextualTriggerOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ContextualTriggerCfg>(overlay.content);\n if (!cfg) return null;\n const icons: Record<string, string> = { alert: \"\\u26A0\\uFE0F\", celebration: \"\\uD83C\\uDF89\", info: \"\\u2139\\uFE0F\", warning: \"\\uD83D\\uDD14\" };\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", alignItems: \"center\", gap: f * 0.8, padding: `0 ${f * 1.2}px`, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", borderLeft: `${Math.max(2, size.w * 0.02)}px solid ${cfg.accentColor}`, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <span style={{ fontSize: \"2em\", flexShrink: 0 }}>{icons[cfg.iconType] || \"\\u26A1\"}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.headline}</div>\n <div style={{ fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.message}</div>\n </div>\n </div>\n );\n}\n\nfunction OddsBettingOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<OddsBettingCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.052);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ fontSize: \"0.9em\", fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }}>{cfg.eventTitle}</div>\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", gap: f * 0.2, justifyContent: \"center\" }}>\n {(cfg.options || []).slice(0, 5).map((opt, i) => (\n <div key={i} style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: `${f * 0.2}px ${f * 0.6}px`, borderRadius: Math.max(2, f * 0.3), background: `${cfg.accentColor}15`, fontSize: \"1em\" }}>\n <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", flex: 1 }}>{opt.label}</span>\n <span style={{ fontWeight: 700, marginLeft: f * 0.8, flexShrink: 0, color: cfg.accentColor }}>{opt.odds}</span>\n </div>\n ))}\n </div>\n {cfg.sponsorText && <div style={{ fontSize: \"0.7em\", opacity: 0.4, textAlign: \"center\", marginTop: f * 0.4 }}>{cfg.sponsorText}</div>}\n </div>\n );\n}\n\nfunction BreakingNewsOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<BreakingNewsCfg>(overlay.content);\n if (!cfg) return null;\n const urgencyColors: Record<string, string> = { breaking: \"#dc2626\", urgent: \"#ea580c\", normal: \"#2563eb\" };\n const labelBg = urgencyColors[cfg.urgency] || urgencyColors.normal;\n const label = cfg.urgency === \"breaking\" ? \"BREAKING\" : cfg.urgency === \"urgent\" ? \"URGENT\" : \"NEWS\";\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", alignItems: \"center\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ padding: `0 ${f * 0.8}px`, height: \"100%\", display: \"flex\", alignItems: \"center\", background: labelBg, color: \"#fff\", fontSize: \"1em\", fontWeight: 900, textTransform: \"uppercase\", letterSpacing: \"0.05em\", flexShrink: 0 }}>{label}</div>\n <div style={{ flex: 1, padding: `0 ${f * 1.0}px`, minWidth: 0 }}>\n <div style={{ fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.headline}</div>\n {cfg.body && <div style={{ fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.body}</div>}\n </div>\n </div>\n );\n}\n\nfunction calcCountdownRemaining(targetTime: string) {\n const diff = Math.max(0, new Date(targetTime).getTime() - Date.now());\n return {\n d: Math.floor(diff / 86400000),\n h: Math.floor((diff % 86400000) / 3600000),\n m: Math.floor((diff % 3600000) / 60000),\n s: Math.floor((diff % 60000) / 1000),\n expired: diff === 0,\n };\n}\n\nfunction CountdownOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<CountdownCfg>(overlay.content);\n const targetTime = cfg?.targetTime ?? \"\";\n const [remaining, setRemaining] = useState(() =>\n targetTime ? calcCountdownRemaining(targetTime) : { d: 0, h: 0, m: 0, s: 0, expired: false }\n );\n\n useEffect(() => {\n if (!targetTime) return;\n setRemaining(calcCountdownRemaining(targetTime));\n const id = setInterval(() => setRemaining(calcCountdownRemaining(targetTime)), 1000);\n return () => clearInterval(id);\n }, [targetTime]);\n\n if (!cfg) return null;\n\n const f = Math.max(6, size.w * 0.055);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const units: Array<{ show: boolean; value: string; label: string }> = [\n { show: cfg.showDays, value: pad(remaining.d), label: \"DAYS\" },\n { show: cfg.showHours, value: pad(remaining.h), label: \"HRS\" },\n { show: cfg.showMinutes, value: pad(remaining.m), label: \"MIN\" },\n { show: cfg.showSeconds, value: pad(remaining.s), label: \"SEC\" },\n ];\n\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }}>{cfg.eventName}</div>\n {remaining.expired ? (\n <div style={{ fontSize: \"1em\", fontWeight: 700, opacity: 0.6 }}>{cfg.message || \"Event ended\"}</div>\n ) : (\n <div style={{ display: \"flex\", gap: f * 0.6, alignItems: \"center\" }}>\n {units.filter(u => u.show).map((u, i, arr) => (\n <React.Fragment key={u.label}>\n <div style={{ textAlign: \"center\" }}>\n <div style={{ fontSize: \"2em\", fontWeight: 900, lineHeight: 1, borderRadius: Math.max(2, f * 0.4), padding: `${f * 0.2}px ${f * 0.4}px`, background: `${cfg.accentColor}20` }}>{u.value}</div>\n <div style={{ fontSize: \"0.5em\", opacity: 0.5, marginTop: f * 0.2 }}>{u.label}</div>\n </div>\n {i < arr.length - 1 && <div style={{ fontSize: \"1.8em\", fontWeight: 700, opacity: 0.3 }}>:</div>}\n </React.Fragment>\n ))}\n </div>\n )}\n {!remaining.expired && cfg.message && <div style={{ fontSize: \"0.8em\", opacity: 0.6, marginTop: f * 0.4, textAlign: \"center\" }}>{cfg.message}</div>}\n </div>\n );\n}\n\nfunction ShapeOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.03), background: \"rgba(99, 102, 241, 0.2)\", border: \"2px solid rgba(99, 102, 241, 0.4)\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", pointerEvents: \"none\", userSelect: \"none\" }}>\n <div style={{ fontSize: `${f}px`, fontWeight: 500, color: \"rgba(163, 163, 163, 0.8)\", textTransform: \"uppercase\" }}>{overlay.name}</div>\n </div>\n );\n}\n\nfunction hexToRgb(hex: string): string {\n if (!hex || !hex.startsWith(\"#\")) return \"0,0,0\";\n const clean = hex.slice(1);\n const num = parseInt(clean.length === 3 ? clean.replace(/./g, \"$&$&\") : clean, 16);\n return `${(num >> 16) & 255},${(num >> 8) & 255},${num & 255}`;\n}\n\ninterface OverlayFadeState {\n overlay: SwirlOverlay;\n visible: boolean;\n}\n\nconst FADE_DURATION_MS = 1000;\n\nexport const OverlayRenderer: React.FC<OverlayRendererProps> = ({\n overlays,\n currentTime,\n videoRef,\n coordinateSpace,\n}) => {\n const [dims, setDims] = useState<VideoDimensions | null>(null);\n const rafRef = useRef<number | null>(null);\n const [fadeMap, setFadeMap] = useState<Map<number, OverlayFadeState>>(new Map());\n const removeTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());\n\n const updateDims = useCallback(() => {\n const video = videoRef.current;\n if (video) {\n const computed = computeVideoDimensions(video);\n setDims((prev) => {\n if (\n !computed ||\n (prev &&\n prev.nativeWidth === computed.nativeWidth &&\n prev.nativeHeight === computed.nativeHeight &&\n prev.displayWidth === computed.displayWidth &&\n prev.displayHeight === computed.displayHeight &&\n prev.offsetX === computed.offsetX &&\n prev.offsetY === computed.offsetY)\n ) {\n return prev;\n }\n return computed;\n });\n }\n }, [videoRef]);\n\n useEffect(() => {\n updateDims();\n const interval = setInterval(updateDims, 500);\n\n const handleResize = () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(updateDims);\n };\n window.addEventListener(\"resize\", handleResize);\n\n return () => {\n clearInterval(interval);\n window.removeEventListener(\"resize\", handleResize);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [updateDims]);\n\n const activeOverlays = useMemo(\n () => overlays.filter((o) => isOverlayActive(o, currentTime)),\n [overlays, currentTime]\n );\n\n useEffect(() => {\n const activeIds = new Set(activeOverlays.map((o) => o.id));\n\n setFadeMap((prev) => {\n const next = new Map(prev);\n\n for (const overlay of activeOverlays) {\n if (!next.has(overlay.id)) {\n next.set(overlay.id, { overlay, visible: false });\n } else {\n const existing = next.get(overlay.id)!;\n next.set(overlay.id, { ...existing, overlay });\n }\n }\n\n for (const [id, state] of next) {\n if (!activeIds.has(id) && state.visible) {\n next.set(id, { ...state, visible: false });\n if (!removeTimers.current.has(id)) {\n const timer = setTimeout(() => {\n setFadeMap((m) => {\n const updated = new Map(m);\n updated.delete(id);\n return updated;\n });\n removeTimers.current.delete(id);\n }, FADE_DURATION_MS);\n removeTimers.current.set(id, timer);\n }\n } else if (!activeIds.has(id) && !state.visible) {\n }\n }\n\n return next;\n });\n }, [activeOverlays]);\n\n useEffect(() => {\n const toFadeIn: number[] = [];\n for (const [id, state] of fadeMap) {\n if (!state.visible) {\n const isActive = activeOverlays.some((o) => o.id === id);\n if (isActive) toFadeIn.push(id);\n }\n }\n if (toFadeIn.length === 0) return;\n\n const raf = requestAnimationFrame(() => {\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const id of toFadeIn) {\n const state = next.get(id);\n if (state) next.set(id, { ...state, visible: true });\n }\n return next;\n });\n });\n return () => cancelAnimationFrame(raf);\n }, [fadeMap, activeOverlays]);\n\n useEffect(() => {\n return () => {\n for (const timer of removeTimers.current.values()) clearTimeout(timer);\n };\n }, []);\n\n if (!dims || fadeMap.size === 0) return null;\n\n return (\n <div\n aria-hidden=\"true\"\n style={{\n position: \"absolute\",\n left: `${dims.offsetX}px`,\n top: `${dims.offsetY}px`,\n width: `${dims.displayWidth}px`,\n height: `${dims.displayHeight}px`,\n pointerEvents: \"none\",\n overflow: \"hidden\",\n zIndex: 8,\n }}\n >\n {[...fadeMap.values()].map(({ overlay, visible }) => {\n const scaleX =\n coordinateSpace?.width\n ? dims.displayWidth / coordinateSpace.width\n : dims.scaleX;\n const scaleY =\n coordinateSpace?.height\n ? dims.displayHeight / coordinateSpace.height\n : dims.scaleY;\n const left = overlay.x * scaleX;\n const top = overlay.y * scaleY;\n const width = overlay.width * scaleX;\n const height = overlay.height * scaleY;\n const baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;\n const opacity = visible ? baseOpacity : 0;\n const sz: OverlaySize = { w: width, h: height };\n\n return (\n <div\n key={overlay.id}\n style={{\n position: \"absolute\",\n left: `${left}px`,\n top: `${top}px`,\n width: `${width}px`,\n height: `${height}px`,\n opacity,\n transition: `opacity ${FADE_DURATION_MS}ms ease`,\n zIndex: overlay.z_index,\n overflow: \"hidden\",\n }}\n >\n {overlay.type === \"image\" && <ImageOverlay overlay={overlay} />}\n {overlay.type === \"text\" && <TextOverlay overlay={overlay} />}\n {overlay.type === \"scroller\" && <ScrollerOverlay overlay={overlay} />}\n {overlay.type === \"shape\" && <ShapeOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"score_bug\" && <ScoreBugOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"lower_third\" && <LowerThirdOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"qr_code\" && <QrCodeOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"coming_up_next\" && <ComingUpNextOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"contextual_trigger\" && <ContextualTriggerOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"odds_betting\" && <OddsBettingOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"breaking_news\" && <BreakingNewsOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"countdown\" && <CountdownOverlay overlay={overlay} size={sz} />}\n </div>\n );\n })}\n </div>\n );\n};\n","const OVERLAY_API_BASE = \"https://adstorm.co/api-adstorm-dev\";\n\nexport interface OverlayCoordinateSpace {\n width: number;\n height: number;\n}\n\nexport interface SwirlScrollerConfig {\n rss_url?: string;\n update_interval?: number;\n scroll_speed?: number;\n direction?: string;\n font_size?: number;\n font_family?: string;\n font_weight?: string;\n text_color?: string;\n background_color?: string;\n background_opacity?: number;\n border_color?: string;\n border_width?: number;\n border_radius?: number;\n padding?: number;\n margin?: number;\n show_title?: boolean;\n show_description?: boolean;\n show_timestamp?: boolean;\n show_author?: boolean;\n show_category?: boolean;\n max_items?: number;\n item_spacing?: number;\n fade_in_out?: boolean;\n fade_distance?: number;\n auto_refresh?: boolean;\n use_custom_text?: boolean;\n custom_text?: string;\n // Broadcast ticker branding\n label?: string;\n label_line2?: string;\n label_color?: string;\n label_text_color?: string;\n accent_color?: string;\n show_accent_line?: boolean;\n separator_char?: string;\n preset?: string;\n}\n\nexport type SwirlOverlayType =\n | \"image\"\n | \"text\"\n | \"scroller\"\n | \"shape\"\n | \"score_bug\"\n | \"lower_third\"\n | \"qr_code\"\n | \"coming_up_next\"\n | \"contextual_trigger\"\n | \"odds_betting\"\n | \"breaking_news\"\n | \"countdown\";\n\nexport interface SwirlOverlay {\n id: number;\n project_id: number;\n name: string;\n type: SwirlOverlayType | string;\n visible: boolean;\n x: number;\n y: number;\n width: number;\n height: number;\n opacity: number;\n start_time: string;\n duration: string;\n content?: string;\n image_url?: string;\n scroller_config?: SwirlScrollerConfig;\n z_index: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport function timeStringToSeconds(timeStr: string): number {\n if (!timeStr) return 0;\n\n const parts = timeStr.split(\":\");\n\n if (parts.length >= 3) {\n const hours = parseInt(parts[0] ?? \"0\", 10) || 0;\n const minutes = parseInt(parts[1] ?? \"0\", 10) || 0;\n const secStr = parts[2] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds =\n parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return hours * 3600 + minutes * 60 + seconds + ms / 1000;\n }\n\n if (parts.length === 2) {\n const minutes = parseInt(parts[0] ?? \"0\", 10) || 0;\n const secStr = parts[1] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds =\n parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return minutes * 60 + seconds + ms / 1000;\n }\n\n const num = parseFloat(timeStr);\n return isFinite(num) ? Math.max(0, num) : 0;\n}\n\nexport function isOverlayActive(\n overlay: SwirlOverlay,\n currentTime: number\n): boolean {\n if (!overlay.visible) return false;\n const startSec = timeStringToSeconds(overlay.start_time);\n const durationSec = timeStringToSeconds(overlay.duration);\n if (durationSec <= 0) return false;\n return currentTime >= startSec && currentTime < startSec + durationSec;\n}\n\nexport async function fetchProjectOverlays(\n projectId: number,\n apiBaseUrl: string = OVERLAY_API_BASE\n): Promise<SwirlOverlay[]> {\n const response = await fetch(\n `${apiBaseUrl}/adstorm/swirl/projects/${projectId}/overlays`\n );\n if (!response.ok) {\n throw new Error(\n `Failed to fetch overlays: ${response.status} ${response.statusText}`\n );\n }\n const data = await response.json();\n return Array.isArray(data) ? data : [];\n}\n\nexport function resolveImageUrl(\n imageUrl: string,\n apiBaseUrl: string = OVERLAY_API_BASE\n): string {\n if (!imageUrl) return \"\";\n if (imageUrl.startsWith(\"http://\") || imageUrl.startsWith(\"https://\")) {\n return imageUrl;\n }\n if (imageUrl.startsWith(\"/\")) {\n try {\n const url = new URL(apiBaseUrl);\n return `${url.origin}${imageUrl}`;\n } catch {\n return imageUrl;\n }\n }\n return `${apiBaseUrl}/${imageUrl}`;\n}\n"]}
|
|
@@ -7209,7 +7209,7 @@ var PANEL_BASE_RIGHT_OFFSET = 10;
|
|
|
7209
7209
|
var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props) {
|
|
7210
7210
|
var _ref;
|
|
7211
7211
|
var _aiLiveContext_context;
|
|
7212
|
-
var src = props.src, autoplay = props.autoplay, muted = props.muted, lowLatencyMode = props.lowLatencyMode, allowNativeHls = props.allowNativeHls, driftToleranceMs = props.driftToleranceMs, immediateManifestAds = props.immediateManifestAds, debugAdTiming = props.debugAdTiming, showCustomControls = props.showCustomControls, hideLoadingIndicator = props.hideLoadingIndicator, onVolumeToggle = props.onVolumeToggle, onFullscreenToggle = props.onFullscreenToggle, onControlClick = props.onControlClick, onReady = props.onReady, wrapperClassName = props.wrapperClassName, wrapperStyle = props.wrapperStyle, className = props.className, style = props.style, controls = props.controls, playsInline = props.playsInline, preload = props.preload, poster = props.poster, children = props.children, licenseKey = props.licenseKey, minSegmentsBeforePlay = props.minSegmentsBeforePlay, disableAds = props.disableAds, disableFiller = props.disableFiller, swirlProjectId = props.swirlProjectId, restVideoAttrs = _object_without_properties(props, [
|
|
7212
|
+
var src = props.src, autoplay = props.autoplay, muted = props.muted, lowLatencyMode = props.lowLatencyMode, allowNativeHls = props.allowNativeHls, driftToleranceMs = props.driftToleranceMs, immediateManifestAds = props.immediateManifestAds, debugAdTiming = props.debugAdTiming, showCustomControls = props.showCustomControls, hideLoadingIndicator = props.hideLoadingIndicator, onVolumeToggle = props.onVolumeToggle, onFullscreenToggle = props.onFullscreenToggle, onControlClick = props.onControlClick, onReady = props.onReady, wrapperClassName = props.wrapperClassName, wrapperStyle = props.wrapperStyle, className = props.className, style = props.style, controls = props.controls, playsInline = props.playsInline, preload = props.preload, poster = props.poster, children = props.children, licenseKey = props.licenseKey, minSegmentsBeforePlay = props.minSegmentsBeforePlay, disableAds = props.disableAds, disableFiller = props.disableFiller, swirlProjectId = props.swirlProjectId, disableOverlays = props.disableOverlays, showAdsControl = props.showAdsControl, showOverlaysControl = props.showOverlaysControl, showAiContextControl = props.showAiContextControl, showDebugControl = props.showDebugControl, restVideoAttrs = _object_without_properties(props, [
|
|
7213
7213
|
"src",
|
|
7214
7214
|
"autoplay",
|
|
7215
7215
|
"muted",
|
|
@@ -7237,7 +7237,12 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
|
|
|
7237
7237
|
"minSegmentsBeforePlay",
|
|
7238
7238
|
"disableAds",
|
|
7239
7239
|
"disableFiller",
|
|
7240
|
-
"swirlProjectId"
|
|
7240
|
+
"swirlProjectId",
|
|
7241
|
+
"disableOverlays",
|
|
7242
|
+
"showAdsControl",
|
|
7243
|
+
"showOverlaysControl",
|
|
7244
|
+
"showAiContextControl",
|
|
7245
|
+
"showDebugControl"
|
|
7241
7246
|
]);
|
|
7242
7247
|
var videoRef = (0, import_react2.useRef)(null);
|
|
7243
7248
|
var playerRef = (0, import_react2.useRef)(null);
|
|
@@ -7274,12 +7279,13 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
|
|
|
7274
7279
|
var _import_react2_default_useState22 = _sliced_to_array(import_react2.default.useState([]), 2), debugMarkers = _import_react2_default_useState22[0], setDebugMarkers = _import_react2_default_useState22[1];
|
|
7275
7280
|
var _import_react2_default_useState23 = _sliced_to_array(import_react2.default.useState(false), 2), showAiPanel = _import_react2_default_useState23[0], setShowAiPanel = _import_react2_default_useState23[1];
|
|
7276
7281
|
var _import_react2_default_useState24 = _sliced_to_array(import_react2.default.useState(disableAds !== null && disableAds !== void 0 ? disableAds : false), 2), adsDisabled = _import_react2_default_useState24[0], setAdsDisabled = _import_react2_default_useState24[1];
|
|
7277
|
-
var _import_react2_default_useState25 = _sliced_to_array(import_react2.default.useState(
|
|
7282
|
+
var _import_react2_default_useState25 = _sliced_to_array(import_react2.default.useState(disableOverlays !== null && disableOverlays !== void 0 ? disableOverlays : false), 2), overlaysDisabled = _import_react2_default_useState25[0], setOverlaysDisabled = _import_react2_default_useState25[1];
|
|
7283
|
+
var _import_react2_default_useState26 = _sliced_to_array(import_react2.default.useState({
|
|
7278
7284
|
context: null,
|
|
7279
7285
|
isLoading: false,
|
|
7280
7286
|
error: null,
|
|
7281
7287
|
lastPolledAt: null
|
|
7282
|
-
}), 2), aiLiveContext =
|
|
7288
|
+
}), 2), aiLiveContext = _import_react2_default_useState26[0], setAiLiveContext = _import_react2_default_useState26[1];
|
|
7283
7289
|
var getResponsiveScale = function getResponsiveScale() {
|
|
7284
7290
|
if (viewportWidth < 480) return 0.7;
|
|
7285
7291
|
if (viewportWidth < 768) return 0.8;
|
|
@@ -7954,7 +7960,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
|
|
|
7954
7960
|
}, restVideoAttrs), {
|
|
7955
7961
|
children: children
|
|
7956
7962
|
})),
|
|
7957
|
-
overlays.length > 0 && !adStatus.showAds && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(OverlayRenderer, {
|
|
7963
|
+
overlays.length > 0 && !adStatus.showAds && !overlaysDisabled && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(OverlayRenderer, {
|
|
7958
7964
|
overlays: overlays,
|
|
7959
7965
|
currentTime: currentTime,
|
|
7960
7966
|
videoRef: videoRef,
|
|
@@ -8766,7 +8772,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
|
|
|
8766
8772
|
gap: "".concat(8 * responsiveScale, "px")
|
|
8767
8773
|
},
|
|
8768
8774
|
children: [
|
|
8769
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
8775
|
+
(showAdsControl !== null && showAdsControl !== void 0 ? showAdsControl : true) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
8770
8776
|
className: "sc-ctrl-btn",
|
|
8771
8777
|
onClick: function onClick() {
|
|
8772
8778
|
var _playerRef_current;
|
|
@@ -8791,7 +8797,29 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
|
|
|
8791
8797
|
title: adsDisabled ? "Enable ads" : "Disable ads",
|
|
8792
8798
|
children: "AD"
|
|
8793
8799
|
}),
|
|
8794
|
-
swirlProjectId && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
8800
|
+
swirlProjectId && (showOverlaysControl !== null && showOverlaysControl !== void 0 ? showOverlaysControl : true) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
8801
|
+
className: "sc-ctrl-btn",
|
|
8802
|
+
onClick: function onClick() {
|
|
8803
|
+
setOverlaysDisabled(function(prev) {
|
|
8804
|
+
return !prev;
|
|
8805
|
+
});
|
|
8806
|
+
resetControlsTimer();
|
|
8807
|
+
},
|
|
8808
|
+
style: {
|
|
8809
|
+
padding: "".concat(8 * responsiveScale, "px"),
|
|
8810
|
+
borderRadius: "50%",
|
|
8811
|
+
minWidth: "".concat(36 * responsiveScale, "px"),
|
|
8812
|
+
minHeight: "".concat(36 * responsiveScale, "px"),
|
|
8813
|
+
background: overlaysDisabled ? "rgba(239, 68, 68, 0.28)" : "transparent",
|
|
8814
|
+
color: overlaysDisabled ? "rgba(252, 165, 165, 0.9)" : "rgba(255, 255, 255, 0.75)",
|
|
8815
|
+
opacity: overlaysDisabled ? 1 : 0.9
|
|
8816
|
+
},
|
|
8817
|
+
title: overlaysDisabled ? "Enable overlays" : "Disable overlays",
|
|
8818
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_fa.FaLayerGroup, {
|
|
8819
|
+
size: Math.max(14, 18 * responsiveScale)
|
|
8820
|
+
})
|
|
8821
|
+
}),
|
|
8822
|
+
swirlProjectId && (showAiContextControl !== null && showAiContextControl !== void 0 ? showAiContextControl : true) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
8795
8823
|
className: "sc-ctrl-btn",
|
|
8796
8824
|
onClick: function onClick() {
|
|
8797
8825
|
setShowAiPanel(function(prev) {
|
|
@@ -8813,7 +8841,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
|
|
|
8813
8841
|
title: showAiPanel ? "Hide AI context" : "Show AI context",
|
|
8814
8842
|
children: "AI"
|
|
8815
8843
|
}),
|
|
8816
|
-
debugAdTiming && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
8844
|
+
debugAdTiming && (showDebugControl !== null && showDebugControl !== void 0 ? showDebugControl : true) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
8817
8845
|
className: "sc-ctrl-btn",
|
|
8818
8846
|
onClick: function onClick() {
|
|
8819
8847
|
setShowDebugPanel(function(prev) {
|
|
@@ -9084,7 +9112,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
|
|
|
9084
9112
|
})
|
|
9085
9113
|
]
|
|
9086
9114
|
}),
|
|
9087
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
9115
|
+
(showAdsControl !== null && showAdsControl !== void 0 ? showAdsControl : true) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
9088
9116
|
className: "sc-ctrl-btn",
|
|
9089
9117
|
onClick: function onClick() {
|
|
9090
9118
|
var _playerRef_current;
|
|
@@ -9109,7 +9137,28 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
|
|
|
9109
9137
|
title: adsDisabled ? "Enable ads" : "Disable ads",
|
|
9110
9138
|
children: "AD"
|
|
9111
9139
|
}),
|
|
9112
|
-
swirlProjectId && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
9140
|
+
swirlProjectId && (showOverlaysControl !== null && showOverlaysControl !== void 0 ? showOverlaysControl : true) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
9141
|
+
className: "sc-ctrl-btn",
|
|
9142
|
+
onClick: function onClick() {
|
|
9143
|
+
setOverlaysDisabled(function(prev) {
|
|
9144
|
+
return !prev;
|
|
9145
|
+
});
|
|
9146
|
+
resetControlsTimer();
|
|
9147
|
+
},
|
|
9148
|
+
style: {
|
|
9149
|
+
padding: "".concat(8 * responsiveScale, "px"),
|
|
9150
|
+
borderRadius: "50%",
|
|
9151
|
+
minWidth: "".concat(36 * responsiveScale, "px"),
|
|
9152
|
+
minHeight: "".concat(36 * responsiveScale, "px"),
|
|
9153
|
+
background: overlaysDisabled ? "rgba(239, 68, 68, 0.28)" : "rgba(0, 0, 0, 0.6)",
|
|
9154
|
+
color: overlaysDisabled ? "rgba(252, 165, 165, 0.9)" : "rgba(255, 255, 255, 0.75)"
|
|
9155
|
+
},
|
|
9156
|
+
title: overlaysDisabled ? "Enable overlays" : "Disable overlays",
|
|
9157
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_fa.FaLayerGroup, {
|
|
9158
|
+
size: Math.max(14, 18 * responsiveScale)
|
|
9159
|
+
})
|
|
9160
|
+
}),
|
|
9161
|
+
swirlProjectId && (showAiContextControl !== null && showAiContextControl !== void 0 ? showAiContextControl : true) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
9113
9162
|
className: "sc-ctrl-btn",
|
|
9114
9163
|
onClick: function onClick() {
|
|
9115
9164
|
setShowAiPanel(function(prev) {
|
|
@@ -9131,7 +9180,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
|
|
|
9131
9180
|
title: showAiPanel ? "Hide AI context" : "Show AI context",
|
|
9132
9181
|
children: "AI"
|
|
9133
9182
|
}),
|
|
9134
|
-
debugAdTiming && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
9183
|
+
debugAdTiming && (showDebugControl !== null && showDebugControl !== void 0 ? showDebugControl : true) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
|
|
9135
9184
|
className: "sc-ctrl-btn",
|
|
9136
9185
|
onClick: function onClick() {
|
|
9137
9186
|
setShowDebugPanel(function(prev) {
|
|
@@ -9233,7 +9282,12 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
|
|
|
9233
9282
|
"preload",
|
|
9234
9283
|
"poster",
|
|
9235
9284
|
"children",
|
|
9236
|
-
"swirlProjectId"
|
|
9285
|
+
"swirlProjectId",
|
|
9286
|
+
"disableOverlays",
|
|
9287
|
+
"showAdsControl",
|
|
9288
|
+
"showOverlaysControl",
|
|
9289
|
+
"showAiContextControl",
|
|
9290
|
+
"showDebugControl"
|
|
9237
9291
|
];
|
|
9238
9292
|
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
9239
9293
|
try {
|