{"version":3,"sources":["../src/index.ts","#style-inject:#style-inject","../src/styles.css","../src/Player.tsx","../src/hooks/useShakaPlayer.ts","../src/utils/devices.ts","../package.json","../src/utils/licenseCache.ts","../src/hooks/usePlayerInitialization.ts","../src/hooks/useEventHandlers.ts","../src/hooks/useAdEvents.ts","../src/hooks/useSystem73.ts","../src/hooks/useAdsInitialization.ts","../src/hooks/useMuxAnalytics.ts","../src/hooks/useQualityControl.ts","../src/hooks/useSkipControls.ts","../src/hooks/useKeyboardControls.ts","../src/hooks/useLiveIndicator.ts","../src/hooks/useShakaUI.ts","../src/icons/SkipBackIcon.tsx","../src/icons/SkipForwardIcon.tsx","../src/icons/BigPlayIcon.tsx","../src/utils/renderIcon.ts","../src/hooks/useScriptLoader.ts","../src/utils/scriptLoader.ts","../src/hooks/useDisableNativeControls.ts","../src/hooks/usePlayerStoreSync.ts","../src/store/PlayerStore.tsx","../src/hooks/usePosterFallback.ts","../src/hooks/useConcurrencySession.ts","../src/api/video.ts","../src/utils/recentPurchase.ts","../src/api/transport.ts","../src/utils/deviceId.ts","../src/utils/playlist.ts","../src/utils/playerStyles.ts","../src/components/Loading.tsx","../src/components/ErrorScreen.tsx","../src/components/Title.tsx","../src/components/TitleAndDescription.tsx","../src/messages/useMessages.tsx","../src/messages/en.json","../src/messages/es.json","../src/messages/ar.json","../src/messages/de.json","../src/messages/fr.json","../src/messages/it.json","../src/messages/pt.json","../src/messages/zh.json","../src/messages/nl.json","../src/messages/fa.json","../src/Video.tsx","../src/api/event.ts","../src/api/creative-work.ts","../src/helper.ts","../src/Event.tsx","../src/CreativeWork.tsx","../src/QueryProvider.tsx"],"sourcesContent":["// Import Shaka Player default control styles to ensure proper styling without needing external CDN links\nimport 'shaka-player/dist/controls.css';\n// Import library specific (Tailwind-based) overrides after the base Shaka styles so that our custom\n// styles take precedence.\nimport './styles.css';\nexport { Player } from './Player';\nexport { Video } from './Video';\nexport { Event } from './Event';\nexport { CreativeWork } from './CreativeWork';\nexport { QueryProvider, queryClient } from './QueryProvider';\nexport { SkipBackIcon, SkipForwardIcon, BigPlayIcon } from './icons';\nexport { PlayerProvider, usePlayerStore, usePlayerStoreApi } from './store/PlayerStore';\nexport type { PlayerProps, PlayerEvents, IconSizes, TimeUpdateData } from './types';\nexport type { PlayerStore, PlayerStoreState, PlayerStoreActions } from './store/PlayerStore';\nexport type { VideoProps } from './Video';\nexport type { EventProps } from './Event';\nexport type { CreativeWorkProps } from './CreativeWork';\nexport type { VideoData, ConcurrencySession, ConcurrencyServerMessage, ConcurrencyClientMessage } from './api/video';\nexport { ConcurrencyConnectionState } from './api/video';\nexport type { EventData, EventsSortDirection } from './api/event';\nexport type { CreativeWorkData, CreativeWorksSortDirection } from './api/creative-work';\n// Concurrency session hook types (for custom integrations)\nexport type { SessionStartedInfo, ConcurrencySessionState, UseConcurrencySessionOptions } from './hooks/useConcurrencySession';\nexport { useConcurrencySession } from './hooks/useConcurrencySession';\n// Device ID utility (for custom integrations)\nexport { getDeviceId, clearDeviceId } from './utils/deviceId'; ","\n          export default function styleInject(css, { insertAt } = {}) {\n            if (!css || typeof document === 'undefined') return\n          \n            const head = document.head || document.getElementsByTagName('head')[0]\n            const style = document.createElement('style')\n            style.type = 'text/css'\n          \n            if (insertAt === 'top') {\n              if (head.firstChild) {\n                head.insertBefore(style, head.firstChild)\n              } else {\n                head.appendChild(style)\n              }\n            } else {\n              head.appendChild(style)\n            }\n          \n            if (style.styleSheet) {\n              style.styleSheet.cssText = css\n            } else {\n              style.appendChild(document.createTextNode(css))\n            }\n          }\n          ","import styleInject from '#style-inject';styleInject(\"@layer components {\\n  video::-webkit-media-controls {\\n    display: none !important;\\n  }\\n  video::-webkit-media-controls-panel {\\n    display: none !important;\\n  }\\n  video::-webkit-media-controls-play-button {\\n    display: none !important;\\n  }\\n  video::-webkit-media-controls-timeline {\\n    display: none !important;\\n  }\\n  video::-webkit-media-controls-current-time-display {\\n    display: none !important;\\n  }\\n  video::-webkit-media-controls-time-remaining-display {\\n    display: none !important;\\n  }\\n  video::-webkit-media-controls-mute-button {\\n    display: none !important;\\n  }\\n  video::-webkit-media-controls-volume-slider {\\n    display: none !important;\\n  }\\n  video::-webkit-media-controls-fullscreen-button {\\n    display: none !important;\\n  }\\n  video::-webkit-media-controls-overlay-play-button {\\n    display: none !important;\\n  }\\n  video::-moz-media-controls {\\n    display: none !important;\\n  }\\n  video {\\n    outline: none !important;\\n  }\\n  video[controls] {\\n    -webkit-appearance: none !important;\\n    appearance: none !important;\\n  }\\n  video::-webkit-media-controls-enclosure {\\n    display: none !important;\\n  }\\n  video::-webkit-media-controls-start-playback-button {\\n    display: none !important;\\n  }\\n  video[controls]::-webkit-media-controls,\\n  video[controls]::-webkit-media-controls-panel,\\n  video[controls]::-webkit-media-controls-play-button,\\n  video[controls]::-webkit-media-controls-timeline,\\n  video[controls]::-webkit-media-controls-current-time-display,\\n  video[controls]::-webkit-media-controls-time-remaining-display,\\n  video[controls]::-webkit-media-controls-mute-button,\\n  video[controls]::-webkit-media-controls-volume-slider,\\n  video[controls]::-webkit-media-controls-fullscreen-button,\\n  video[controls]::-webkit-media-controls-overlay-play-button,\\n  video[controls]::-webkit-media-controls-enclosure,\\n  video[controls]::-webkit-media-controls-start-playback-button {\\n    display: none !important;\\n    visibility: hidden !important;\\n    opacity: 0 !important;\\n    pointer-events: none !important;\\n  }\\n  video[controls]::-moz-media-controls {\\n    display: none !important;\\n    visibility: hidden !important;\\n    opacity: 0 !important;\\n  }\\n  .motto-video-container {\\n    @apply relative w-full;\\n    min-height: 300px;\\n  }\\n  @supports (aspect-ratio: 16/9) {\\n    .motto-video-container {\\n      min-height: auto;\\n    }\\n  }\\n  .motto-video-responsive {\\n    @apply absolute top-0 left-0 w-full h-full;\\n  }\\n  .motto-skip-button {\\n    @apply absolute top-1/2 -translate-y-1/2 bg-black/70 text-white border-0 rounded-full w-16 h-16 text-2xl cursor-pointer flex items-center justify-center transition-all duration-200 z-10 opacity-80 hover:opacity-100 hover:scale-110 active:scale-95;\\n  }\\n  .motto-skip-button-back {\\n    @apply left-5;\\n  }\\n  .motto-skip-button-forward {\\n    @apply right-5;\\n  }\\n}\\n.shaka-seek-bar-container {\\n  height: 6px !important;\\n  width: 100% !important;\\n  margin: 8px 0 !important;\\n  border-radius: 4px !important;\\n  position: relative !important;\\n  border-top: none !important;\\n  border-bottom: none !important;\\n  box-shadow: none !important;\\n}\\n.shaka-seek-bar {\\n  height: 6px !important;\\n  width: 100% !important;\\n  -webkit-appearance: none !important;\\n  appearance: none !important;\\n  background: transparent !important;\\n  cursor: pointer !important;\\n  border: none !important;\\n  outline: none !important;\\n  position: absolute !important;\\n  top: 0 !important;\\n  left: 0 !important;\\n  border-radius: 4px !important;\\n}\\n.shaka-seek-bar::-webkit-slider-runnable-track {\\n  height: 6px !important;\\n  background: transparent !important;\\n  border-radius: 4px !important;\\n  border: none !important;\\n}\\n.shaka-seek-bar::-moz-range-track {\\n  height: 6px !important;\\n  background: transparent !important;\\n  border-radius: 4px !important;\\n  border: none !important;\\n}\\n.shaka-seek-bar::-webkit-slider-thumb {\\n  -webkit-appearance: none !important;\\n  appearance: none !important;\\n  width: 16px !important;\\n  height: 16px !important;\\n  border-radius: 50% !important;\\n  background: #ffffff !important;\\n  cursor: pointer !important;\\n  border: 2px solid #ffffff !important;\\n  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3) !important;\\n  margin-top: -4px !important;\\n}\\n.shaka-seek-bar::-moz-range-thumb {\\n  width: 16px !important;\\n  height: 16px !important;\\n  border-radius: 50% !important;\\n  background: #ffffff !important;\\n  cursor: pointer !important;\\n  border: 2px solid #ffffff !important;\\n  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3) !important;\\n  margin-top: -4px !important;\\n}\\n.motto-skip-back-button,\\n.motto-skip-forward-button,\\n.motto-native-skip-button {\\n  background: transparent !important;\\n  border: none !important;\\n  padding: 4px !important;\\n  margin: 0px !important;\\n  cursor: pointer !important;\\n  color: #ffffff !important;\\n  transition: all 0.2s ease !important;\\n  min-width: 32px !important;\\n  height: 32px !important;\\n  display: flex !important;\\n  align-items: center !important;\\n  justify-content: center !important;\\n  border-radius: 4px !important;\\n  width: 25px;\\n}\\n.motto-skip-back-button:hover,\\n.motto-skip-forward-button:hover,\\n.motto-native-skip-button:hover {\\n  opacity: 0.8 !important;\\n  background: transparent !important;\\n  transform: scale(1.05) !important;\\n}\\n.motto-skip-back-button:active,\\n.motto-skip-forward-button:active,\\n.motto-native-skip-button:active {\\n  transform: scale(0.95) !important;\\n}\\n.motto-skip-back-button svg,\\n.motto-skip-forward-button svg,\\n.motto-native-skip-button svg {\\n  width: 24px !important;\\n  height: 24px !important;\\n}\\n.shaka-spinner-svg {\\n  color: white !important;\\n  fill: white !important;\\n}\\n.shaka-spinner-path {\\n  stroke: white !important;\\n  fill: none !important;\\n}\\n.shaka-spinner-container {\\n  color: white !important;\\n}\\n.shaka-buffering-spinner {\\n  color: white !important;\\n  fill: white !important;\\n}\\n.shaka-buffering-spinner svg {\\n  color: white !important;\\n  fill: white !important;\\n}\\n.shaka-buffering-spinner path {\\n  stroke: white !important;\\n  fill: none !important;\\n}\\n[data-shaka-player-container] .shaka-spinner,\\n[data-shaka-player-container] .spinner {\\n  color: white !important;\\n  border-color: white !important;\\n}\\n.material-icons.shaka-spinner {\\n  color: white !important;\\n}\\n.shaka-controls-container .shaka-spinner,\\n.shaka-video-container .shaka-spinner {\\n  color: white !important;\\n  fill: white !important;\\n}\\n.shaka-controls-container .shaka-spinner svg,\\n.shaka-video-container .shaka-spinner svg {\\n  color: white !important;\\n  fill: white !important;\\n}\\n.shaka-controls-container .shaka-spinner path,\\n.shaka-video-container .shaka-spinner path {\\n  stroke: white !important;\\n}\\n.motto-video-loading-overlay {\\n  position: absolute;\\n  top: 0;\\n  left: 0;\\n  width: 100%;\\n  height: 100%;\\n  background:\\n    linear-gradient(\\n      135deg,\\n      #1a1a1a 0%,\\n      #2d2d2d 100%);\\n  display: flex;\\n  flex-direction: column;\\n  align-items: center;\\n  justify-content: center;\\n  z-index: 10;\\n  transition: opacity 0.3s ease;\\n}\\n.motto-video-loading-overlay.hidden {\\n  opacity: 0;\\n  pointer-events: none;\\n}\\n.motto-video-loading-content {\\n  text-align: center;\\n  color: white;\\n}\\n.motto-video-loading-icon {\\n  width: 64px;\\n  height: 64px;\\n  margin-bottom: 16px;\\n  opacity: 0.7;\\n}\\n.motto-video-loading-text {\\n  font-size: 16px;\\n  font-weight: 500;\\n  margin-bottom: 8px;\\n}\\n.motto-video-loading-subtext {\\n  font-size: 14px;\\n  opacity: 0.7;\\n}\\n@keyframes pulse-live {\\n  0% {\\n    opacity: 1;\\n    transform: scale(1);\\n  }\\n  50% {\\n    opacity: 0.7;\\n    transform: scale(1.1);\\n  }\\n  100% {\\n    opacity: 1;\\n    transform: scale(1);\\n  }\\n}\\n.shaka-play-button {\\n  background: rgba(255, 255, 255, 0.1) !important;\\n  border: none !important;\\n  color: white !important;\\n  padding: 10px !important;\\n  border-radius: 100% !important;\\n  transition: all 0.2s ease !important;\\n  display: flex !important;\\n  align-items: center !important;\\n  justify-content: center !important;\\n  min-width: 55px !important;\\n  height: 55px !important;\\n}\\n.shaka-play-button-container {\\n  background: transparent;\\n  transition: all 0.2s ease !important;\\n}\\n.motto-video-container:not(.no-cursor) .shaka-play-button-container {\\n  background: rgba(0, 0, 0, 0.3);\\n  transition: all 0.s ease !important;\\n}\\n.shaka-play-button:hover {\\n  background: rgba(255, 255, 255, 0.2) !important;\\n  transform: scale(1.05) !important;\\n}\\n.shaka-play-button:active {\\n  transform: scale(0.95) !important;\\n}\\n.shaka-play-button > * {\\n  display: none !important;\\n}\\n.shaka-play-button::after {\\n  content: \\\"\\\" !important;\\n  width: 35px !important;\\n  height: 35px !important;\\n  background-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"white\\\"><path fill-rule=\\\"evenodd\\\" d=\\\"M4.5 5.653c0-1.427 1.529-2.33 2.779-1.643l11.54 6.347c1.295.712 1.295 2.573 0 3.286L7.28 19.99c-1.25.687-2.779-.217-2.779-1.643V5.653Z\\\" clip-rule=\\\"evenodd\\\" /></svg>') !important;\\n  background-repeat: no-repeat !important;\\n  background-size: contain !important;\\n  background-position: center !important;\\n  display: block !important;\\n}\\n.shaka-play-button[aria-label*=Play]::after {\\n  background-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"white\\\"><path fill-rule=\\\"evenodd\\\" d=\\\"M4.5 5.653c0-1.427 1.529-2.33 2.779-1.643l11.54 6.347c1.295.712 1.295 2.573 0 3.286L7.28 19.99c-1.25.687-2.779-.217-2.779-1.643V5.653Z\\\" clip-rule=\\\"evenodd\\\" /></svg>') !important;\\n}\\n.shaka-play-button[aria-label*=Pause]::after {\\n  background-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"white\\\"><path fill-rule=\\\"evenodd\\\" d=\\\"M6.75 5.25a.75.75 0 0 1 .75-.75H9a.75.75 0 0 1 .75.75v13.5a.75.75 0 0 1-.75.75H7.5a.75.75 0 0 1-.75-.75V5.25Zm7.5 0A.75.75 0 0 1 15 4.5h1.5a.75.75 0 0 1 .75.75v13.5a.75.75 0 0 1-.75.75H15a.75.75 0 0 1-.75-.75V5.25Z\\\" clip-rule=\\\"evenodd\\\" /></svg>') !important;\\n}\\n.motto-video-container {\\n  background: #111111;\\n}\\n.motto-video-container video {\\n  width: 100% !important;\\n  height: 100% !important;\\n  margin-left: auto !important;\\n  margin-right: auto !important;\\n}\\nhtml[dir=rtl] .shaka-controls-container,\\nhtml[dir=rtl] .shaka-bottom-controls,\\nhtml[dir=rtl] .shaka-controls-button-panel {\\n  direction: ltr !important;\\n}\\nhtml[dir=rtl] .shaka-overflow-menu,\\nhtml[dir=rtl] .shaka-settings-menu {\\n  direction: rtl !important;\\n  text-align: right !important;\\n}\\nhtml[dir=rtl] .shaka-overflow-menu .shaka-overflow-button .material-svg-icon:not(:first-child) {\\n  margin-right: 12px !important;\\n  margin-left: 0 !important;\\n}\\nhtml[dir=rtl] .shaka-overflow-menu .shaka-overflow-button .material-svg-icon:first-child {\\n  margin-right: 0 !important;\\n  margin-left: 12px !important;\\n}\\n.shaka-hidden-fast-forward-container,\\n.shaka-hidden-rewind-container {\\n  pointer-events: none !important;\\n  display: none !important;\\n}\\n\")","import { forwardRef, useCallback, useEffect, useRef, useImperativeHandle, useState } from 'react';\nimport { PlayerProps } from './types';\nimport {\n  useShakaPlayer,\n  useQualityControl,\n  useSkipControls,\n  useMuxAnalytics,\n  useShakaUI,\n  useEventHandlers,\n  useLiveIndicator,\n  useKeyboardControls,\n  useAdEvents,\n  usePlayerStoreSync,\n  usePlayerInitialization,\n  useDisableNativeControls,\n  useScriptLoader,\n  useSystem73,\n  useAdsInitialization\n} from './hooks';\nimport './styles.css';\nimport { hasPlaylistUrl } from './utils/playlist';\nimport { getPlayerStyles } from './utils/playerStyles';\nimport { Loading, ErrorScreen } from './components';\nimport { BigPlayIcon } from './icons';\nimport useMessages, { type Translations } from './messages/useMessages';\n\n// Declare google namespace for IMA SDK\ndeclare global {\n  namespace google {\n    namespace ima {\n      class AdsRequest {\n        adTagUrl: string;\n      }\n    }\n  }\n}\n\nexport const Player = forwardRef<HTMLVideoElement, PlayerProps>(\n  ({ \n    src, \n    managedMode = false,\n    autoPlay = false, \n    loop = false, \n    muted = false, \n    controls = true,\n    poster,\n    width,\n    height,\n    aspectRatio = 16/9,\n    shakaConfig,\n    drmConfig,\n    muxConfig,\n    system73Config,\n    imaConfig,\n    chromecastConfig,\n    qualityConfig,\n    seekbarConfig,\n    iconSizes,\n    events,\n    locale = 'en',\n    containerClassName,\n    liveThresholdSeconds = 15,\n    publicKey,\n    auth,\n    ...videoProps\n  }, ref) => {\n    const videoRef = useRef<HTMLVideoElement>(null);\n    const containerRef = useRef<HTMLDivElement>(null);\n    const [isInitialLoading, setIsInitialLoading] = useState(true);\n    const [hasStartedPlaying, setHasStartedPlaying] = useState(false);\n    const [bfResetKey, setBfResetKey] = useState(0);\n    // Translation key of a playback-blocking segment error (X-Error-Code) to\n    // display over the player; null while playback is allowed.\n    const [blockedMessageKey, setBlockedMessageKey] = useState<string | null>(null);\n    // Playback died with no more specific screen to show (e.g. a failed\n    // load); shows the default error screen instead of an endless spinner.\n    const [hasFatalError, setHasFatalError] = useState(false);\n    const { t } = useMessages(locale);\n\n    const handlePlaybackBlocked = useCallback((messageKey: string) => {\n      setBlockedMessageKey(messageKey);\n      events?.onError?.(new Error(messageKey));\n    }, [events]);\n\n    const handleFatalError = useCallback(() => {\n      setHasFatalError(true);\n    }, []);\n\n    // Load external scripts (IMA SDK, System73)\n    const isScriptsLoaded = useScriptLoader({\n      hasAds: !!imaConfig?.adTagUrl,\n      hasSystem73: !!system73Config?.apiKey\n    });\n\n    // Managed mode optimization: prevents unnecessary reinitialization\n    // When wrapper components (Video, Event, CreativeWork) refetch data, the src object\n    // reference changes even if the playlist URL is the same. In managedMode, we only\n    // reinitialize when hasPlaylist changes (boolean: playlist exists or doesn't exist),\n    // rather than on every src object reference change.\n    const hasPlaylist = hasPlaylistUrl(src);\n\n    // Core Shaka Player hook - provides low-level player management\n    // Returns methods for initialization, manifest loading, and cleanup\n    // IMPORTANT: hasAds and hasSystem73 flags control automatic manifest loading\n    // - When true: initializePlayer() skips auto-load, allowing System73/ads to initialize first\n    // - When false: initializePlayer() loads manifest automatically\n    // See ARCHITECTURE.md for detailed initialization sequence\n    const { playerRef, initializePlayer, loadManifest, destroyPlayer, handleMuxError } = useShakaPlayer({\n      src,\n      shakaConfig,\n      drmConfig,\n      onError: events?.onError,\n      onPlaybackBlocked: handlePlaybackBlocked,\n      onFatalError: handleFatalError,\n      onPlayerReady: events?.onPlayerReady,\n      muxConfig,\n      onMuxReady: events?.onMuxReady,\n      onMuxDataUpdate: events?.onMuxDataUpdate,\n      publicKey,\n      mottoToken: auth?.mottoToken,\n      hasAds: !!imaConfig?.adTagUrl,        // Skip auto-load if ads configured\n      hasSystem73: !!system73Config?.apiKey, // Skip auto-load if System73 configured\n      apiToken: auth?.apiToken\n    });\n\n    const {\n      updateMuxData,\n      destroyMux\n    } = useMuxAnalytics(playerRef, events?.onMuxDataUpdate);\n\n    // Forward reference pattern to break circular hook dependency:\n    // - useQualityControl needs to call syncQualityChange in its callback\n    // - usePlayerStoreSync (which provides syncQualityChange) needs setQuality from useQualityControl\n    // Solution: Create ref now, populate it after usePlayerStoreSync, callback uses ref\n    const syncQualityChangeRef = useRef<((quality: { height: number; bandwidth: number }) => void) | null>(null);\n\n    const {\n      getAvailableQualities,\n      setQuality,\n      setupQualityTracking,\n      configureQuality\n    } = useQualityControl(playerRef, qualityConfig, (quality) => {\n      // Sync to store via ref (populated below after usePlayerStoreSync)\n      syncQualityChangeRef.current?.(quality);\n      // Call user's onQualityChange callback\n      events?.onQualityChange?.(quality);\n    });\n\n    // Skip controls - core logic for jumping forward/backward in video\n    // Separated from input binding to allow multiple input methods (keyboard, UI buttons, store, API)\n    const { \n      skipBack, \n      skipForward, \n    } = useSkipControls(videoRef, events?.onSkipBack, events?.onSkipForward);\n\n    // Keyboard input binding - maps arrow keys to skip functions\n    useKeyboardControls(videoRef, {\n      skipBack,\n      skipForward,\n      enabled: true\n    });\n\n    const { setupEventListeners, cleanupEventListeners } = useEventHandlers(videoRef, playerRef, events ?? {});\n\n    const { uiRef, initializeUI, destroyUI } = useShakaUI(\n      playerRef,\n      containerRef,\n      videoRef,\n      controls,\n      chromecastConfig,\n      seekbarConfig,\n      events?.onSkipBack,\n      events?.onSkipForward,\n      iconSizes,\n      locale\n    );\n\n    const { getLiveStatus, seekToLiveEdge } = useLiveIndicator(containerRef, playerRef, {\n      enabled: true,\n      indicatorColor: '#ff0000',\n      indicatorSize: 8,\n      showPulseAnimation: true,\n      liveThresholdSeconds,\n      onLiveStatusChange: events?.onLiveStatusChange\n    });\n\n    const { setupAdEventListeners, cleanupAdEventListeners } = useAdEvents(playerRef, events ?? {});\n\n    // Store synchronization (only active when wrapped in PlayerProvider)\n    const { syncQualityChange, updateAvailableQualities } = usePlayerStoreSync({\n      videoRef,\n      playerRef,\n      getLiveStatus,\n      skipForward,\n      skipBack,\n      setQuality,\n      getAvailableQualities,\n      seekToLiveEdge,\n    });\n\n    // Populate the ref to complete the forward reference pattern (see syncQualityChangeRef above)\n    syncQualityChangeRef.current = syncQualityChange;\n\n    const { initializeSystem73 } = useSystem73(system73Config);\n\n    const { initializeAds } = useAdsInitialization({\n      imaConfig,\n      autoPlay,\n      playerRef,\n      videoRef,\n      uiRef,\n      setupAdEventListeners,\n      loadManifest\n    });\n\n    // Reinitialize on back/forward cache restore without double-initializing on first load\n    useEffect(() => {\n      const onPageShow = (e: any) => {\n        if (e && e.persisted) {\n          setBfResetKey((k) => k + 1);\n        }\n      };\n      window.addEventListener('pageshow', onPageShow);\n      return () => window.removeEventListener('pageshow', onPageShow);\n    }, []);\n\n    // Main initialization - orchestrates all player setup in the correct sequence\n    // See usePlayerInitialization.ts for detailed documentation on initialization order\n    usePlayerInitialization({\n      videoRef,\n      playerRef,\n      isScriptsLoaded,\n      setIsInitialLoading,\n      system73Config,\n      shakaConfig,\n      imaConfig,\n      initializeSystem73,\n      initializePlayer,\n      loadManifest,\n      setupEventListeners,\n      setupQualityTracking,\n      configureQuality,\n      updateAvailableQualities,\n      initializeUI,\n      initializeAds,\n      handleMuxError,\n      cleanupEventListeners,\n      cleanupAdEventListeners,\n      destroyUI,\n      destroyMux,\n      destroyPlayer,\n      src,\n      hasPlaylist,\n      managedMode,\n      bfResetKey,\n    });\n\n    // Update video properties when they change\n    useEffect(() => {\n      const video = videoRef.current;\n      if (!video) return;\n\n      video.autoplay = autoPlay;\n      video.loop = loop;\n      if (poster) video.poster = poster;\n    }, [autoPlay, loop, muted, poster, imaConfig?.adTagUrl]);\n\n    // Track first play so poster overlay hides when playback starts\n    useEffect(() => {\n      const video = videoRef.current;\n      if (!video || !poster) return;\n      const onPlay = () => setHasStartedPlaying(true);\n      video.addEventListener('play', onPlay);\n      return () => video.removeEventListener('play', onPlay);\n    }, [poster]);\n\n    // Reset poster overlay and any blocking error when src changes\n    useEffect(() => {\n      setHasStartedPlaying(false);\n      setBlockedMessageKey(null);\n      setHasFatalError(false);\n    }, [src]);\n\n    // Prevent native controls from conflicting with Shaka UI\n    useDisableNativeControls(videoRef);\n\n    // Expose methods via ref\n    useImperativeHandle(ref, () => ({\n      ...videoRef.current!,\n      // Custom methods for quality control\n      getAvailableQualities,\n      setQuality,\n      // Skip methods\n      skipBack,\n      skipForward,\n      // Mux methods\n      updateMuxData,\n      // Live status methods\n      getLiveStatus,\n      seekToLiveEdge,\n      // Access to underlying instances\n      getPlayer: () => playerRef.current,\n      getMuxMonitor: () => null\n    }), [getAvailableQualities, setQuality, skipBack, skipForward, updateMuxData, getLiveStatus, seekToLiveEdge]);\n\n    const { isResponsive, containerClasses, containerStyle, videoClasses, videoStyle } = getPlayerStyles({\n      width,\n      height,\n      aspectRatio,\n      containerClassName\n    });\n\n    // Filter out controls prop to prevent conflicts with native HTML5 controls\n    const filteredVideoProps = { ...videoProps };\n    delete (filteredVideoProps as any).controls;\n\n    return (\n      <div \n        ref={containerRef}\n        className={containerClasses}\n        style={containerStyle}\n      >\n        <video\n          ref={videoRef}\n          className={videoClasses}\n          width={isResponsive ? undefined : width}\n          height={isResponsive ? undefined : height}\n          style={videoStyle}\n          controls={false}\n          playsInline={true}\n          {...filteredVideoProps}\n        />\n\n        {poster && !hasStartedPlaying && (\n          <div className=\"absolute inset-0\" style={{ zIndex: 10 }}>\n            <img src={poster} alt=\"\" className=\"w-full h-full object-cover\" />\n            <button\n              className=\"absolute inset-0 flex items-center justify-center w-full h-full bg-transparent border-0 cursor-pointer\"\n              onClick={() => videoRef.current?.play()}\n              aria-label=\"Play\"\n            >\n              <div className=\"flex items-center justify-center w-16 h-16 rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors\">\n                <BigPlayIcon size={32} />\n              </div>\n            </button>\n          </div>\n        )}\n\n        {isInitialLoading && !blockedMessageKey && !hasFatalError && (\n          <div className=\"absolute inset-0 flex items-center justify-center z-20 pointer-events-none\">\n            <Loading className=\"bg-transparent\" />\n          </div>\n        )}\n\n        {blockedMessageKey && (\n          <div className=\"absolute inset-0 z-30\">\n            <ErrorScreen\n              title={t(blockedMessageKey as keyof Translations)}\n              description={t(`${blockedMessageKey}_DESCRIPTION` as keyof Translations)}\n            />\n          </div>\n        )}\n\n        {hasFatalError && !blockedMessageKey && (\n          <div className=\"absolute inset-0 z-30\">\n            <ErrorScreen\n              title={t('DEFAULT_ERROR')}\n              description={t('DEFAULT_ERROR_DESCRIPTION')}\n            />\n          </div>\n        )}\n      </div>\n    );\n  }\n);\n\nPlayer.displayName = 'Player'; ","import { useRef, useCallback, useState } from 'react';\nimport shaka from 'shaka-player/dist/shaka-player.ui';\nimport { isAppleDevice, isPlayReadySupported, supportsWidevinePersistentLicenses } from '../utils/devices';\nimport { Playlist } from '../api';\nimport { MuxAnalyticsConfig } from '../types';\nimport initShakaPlayerMux from '@mux/mux-data-shakaplayer';\nimport { version } from '../../package.json';\nimport {\n  PersistentSessionMetadata,\n  storePersistentLicense,\n  retrievePersistentLicense,\n  clearAllPersistentLicenses\n} from '../utils/licenseCache';\n\n/**\n * Removes the query string (and hash) from a URL, dropping signed CDN tokens\n * before the URL is reported to analytics. Returns the input unchanged if it\n * cannot be parsed.\n */\nconst stripUrlQuery = (url: string): string => {\n  if (!url) return url;\n  const queryIndex = url.search(/[?#]/);\n  return queryIndex === -1 ? url : url.slice(0, queryIndex);\n};\n\n/**\n * Wraps a Shaka player in a Proxy that scrubs signed CDN tokens from the two\n * URL sources Mux reads — getAssetUri() (video_source_url) and each\n * response.uri (request_url/request_hostname) — WITHOUT touching the real\n * player. The signature must stay on every URL Shaka uses; stripping the real\n * response.uri made Shaka refresh the live media playlist unsigned (HTTP 500).\n * Only the Mux-facing view is scrubbed:\n *  - getAssetUri() returns the de-tokenized load URL.\n *  - the networking engine's registerResponseFilter wraps Mux's filter so it\n *    receives a shallow clone of each response with a stripped uri, leaving the\n *    real response object (and Shaka's playback) untouched.\n */\nconst createMuxScrubbingPlayer = (player: any): any =>\n  new Proxy(player, {\n    get(target, prop) {\n      if (prop === 'getAssetUri') {\n        return () => stripUrlQuery(target.getAssetUri?.() ?? '');\n      }\n      if (prop === 'getNetworkingEngine') {\n        return () => {\n          const engine = target.getNetworkingEngine();\n          if (!engine) return engine;\n          return new Proxy(engine, {\n            get(engineTarget, engineProp) {\n              if (engineProp === 'registerResponseFilter') {\n                return (filter: any) =>\n                  engineTarget.registerResponseFilter(\n                    (type: any, response: any, ...rest: any[]) =>\n                      filter(\n                        type,\n                        { ...response, uri: stripUrlQuery(response?.uri) },\n                        ...rest\n                      )\n                  );\n              }\n              const value = engineTarget[engineProp];\n              return typeof value === 'function' ? value.bind(engineTarget) : value;\n            },\n          });\n        };\n      }\n      // Return `constructor` unbound: Mux validates the player via\n      // `player.constructor.version`, and a bound function loses the original's\n      // static `version` property — which made initShakaPlayerMux reject the\n      // proxy (\"You must provide a valid shakaPlayer\") and skip all analytics.\n      const value = target[prop];\n      if (typeof value !== 'function' || prop === 'constructor') return value;\n      return value.bind(target);\n    },\n  });\n\n/**\n * Manages the Shaka Player lifecycle, configuration, and low-level operations.\n * \n * RESPONSIBILITIES:\n * ----------------\n * - Create/destroy Shaka Player instances with proper lifecycle coordination\n * - Configure DRM systems (Widevine, PlayReady, FairPlay) with platform-specific settings\n * - Handle persistent license caching and stale license recovery\n * - Setup network filters for authentication headers and FairPlay base64 decoding\n * - Error handling with automatic retry logic for DRM failures\n * - Mux Analytics integration for monitoring and debugging\n * \n * IMPORTANT: Conditional Manifest Loading\n * ---------------------------------------\n * This hook skips automatic manifest loading when hasAds=true or hasSystem73=true.\n * This is critical because:\n * \n * - System73 SDK must wrap the player BEFORE manifest loads to detect all ABR profiles\n * - Google IMA ads need to intercept loading to serve pre-roll ads\n * \n * When skipped, the caller (typically usePlayerInitialization) must explicitly call\n * loadManifest() after the third-party integrations are ready.\n * \n * RELATIONSHIP TO OTHER HOOKS:\n * ---------------------------\n * This hook provides the \"HOW\" - it knows how to configure and manage Shaka Player.\n * It does NOT control \"WHEN\" - that's handled by usePlayerInitialization, which\n * orchestrates the initialization sequence across all integrations.\n * \n * EXPORTED METHODS:\n * ----------------\n * - initializePlayer(video): Creates and configures player, but may skip manifest load\n * - loadManifest(): Explicitly loads the manifest (called after System73/ads setup)\n * - destroyPlayer(): Cleans up player instance, listeners, and resources\n * \n * REFS & STATE:\n * ------------\n * - playerRef: Reference to Shaka Player instance\n * - videoElementRef: Reference to HTMLVideoElement for retry functionality\n * - isRetrying: State flag to prevent multiple simultaneous retry attempts\n * - Multiple DRM-related refs for persistent license management and cleanup\n */\n\ninterface DRMConfig {\n  clearKeys?: { [keyId: string]: string };\n  servers?: { [keySystem: string]: string };\n}\n\n// Maps the CDN's X-Error-Code segment response header to the translation key\n// shown on the player's blocking error screen. 940 (playback token expired,\n// e.g. after a long pause) is handled separately by reloading the page so a\n// fresh token is issued.\nconst SEGMENT_ERROR_MESSAGE_KEYS: Record<string, string> = {\n  '965': 'ERROR_CODE_IP_CHANGED',\n  '970': 'ERROR_CODE_VPN_DETECTED',\n  '971': 'ERROR_CODE_ANONYMOUS_PROXY',\n};\n\ninterface UseShakaPlayerProps {\n  src: Playlist | string;\n  shakaConfig?: any;\n  drmConfig?: DRMConfig;\n  onError?: (error: Error) => void;\n  /** Fired when a segment response carries an X-Error-Code that blocks\n   * playback (IP change, VPN, anonymous proxy). Receives the translation key\n   * of the message to display. */\n  onPlaybackBlocked?: (messageKey: string) => void;\n  /** Fired when playback cannot continue and no more specific handling\n   * applies (blocked segments go through onPlaybackBlocked instead), so the\n   * player can show a default error screen rather than load forever. */\n  onFatalError?: () => void;\n  onPlayerReady?: (player: any) => void;\n  muxConfig?: MuxAnalyticsConfig;\n  onMuxReady?: () => void;\n  onMuxDataUpdate?: (data: any) => void;\n  publicKey?: string;\n  mottoToken?: string;\n  hasAds?: boolean;\n  hasSystem73?: boolean;\n  apiToken?: string;\n}\n\n// Helper to normalize src to a Playlist-like object\nconst normalizeSource = (src: Playlist | string): Playlist => {\n  if (typeof src === 'string') {\n    return {\n      id: '',\n      url: src,\n      format: 'auto',\n      drm: {}\n    } as Playlist;\n  }\n  return src;\n};\n\nexport const useShakaPlayer = ({\n  src,\n  shakaConfig,\n  drmConfig,\n  onError,\n  onPlaybackBlocked,\n  onFatalError,\n  onPlayerReady,\n  muxConfig,\n  onMuxReady,\n  onMuxDataUpdate,\n  publicKey,\n  mottoToken,\n  hasAds = false,\n  hasSystem73 = false,\n  apiToken\n}: UseShakaPlayerProps) => {\n  const playerRef = useRef<any>(null);\n  const [isRetrying, setIsRetrying] = useState(false);\n  const videoElementRef = useRef<HTMLVideoElement | null>(null);\n\n  // Monitor returned by initShakaPlayerMux. Needed for loadErrorHandler:\n  // errors thrown by player.load() reject the promise and never fire Shaka's\n  // 'error' event, so automaticErrorTracking cannot see them.\n  const muxMonitorRef = useRef<any>(null);\n\n  // Lifecycle coordination to prevent init/destroy races\n  const destroyInProgressRef = useRef<Promise<void> | null>(null);\n  const isDestroyingRef = useRef<boolean>(false);\n  const initSequenceRef = useRef<number>(0);\n  const activeInitIdRef = useRef<number | null>(null);\n\n  /* DRM related state */\n  const waitingForKeyTimerRef = useRef<number | null>(null);\n  const waitingForKeyHandlerRef = useRef<((e: Event) => void) | null>(null);\n  const playbackResumedHandlerRef = useRef<((e: Event) => void) | null>(null);\n  const usingPersistentLicenseRef = useRef<boolean>(false);\n  const storedPersistentThisLoadRef = useRef<boolean>(false);\n  const drmExpirationHandlerRef = useRef<((e: Event) => void) | null>(null);\n\n  // Ensures an X-Error-Code on a segment response is acted on only once per\n  // load: segment requests keep coming (and retrying) while blocked, so\n  // without this guard we would reload the page or re-raise the error screen\n  // repeatedly.\n  const segmentErrorHandledRef = useRef<boolean>(false);\n\n  // Helper function to determine the correct manifest URL based on DRM support\n  const getManifestUrl = useCallback(() => {\n    const playlistSrc = normalizeSource(src);\n    let manifestUrl = playlistSrc.url;\n    const isDRM = Boolean(playlistSrc.drm?.widevine || playlistSrc.drm?.fairplay || playlistSrc.drm?.playready);\n\n    if (isDRM) {\n      const isPlayReady = isPlayReadySupported();\n      if(isAppleDevice() && playlistSrc.drm?.fairplay?.certificateUrl) {\n        manifestUrl = playlistSrc.drm.fairplay.playlistUrl;\n      } else if(isPlayReady && playlistSrc.drm?.playready?.licenseUrl) {\n        manifestUrl = playlistSrc.drm.playready.playlistUrl;\n      } else {\n        manifestUrl = playlistSrc.drm?.widevine?.playlistUrl || \"\";\n      }\n    }\n\n    return manifestUrl;\n  }, [src]);\n\n  // Acts on the X-Error-Code header of a segment response. Returns true when\n  // the code was recognized and handled (or already handled this load).\n  // The CDN also writes the code into the plain-text body of error responses\n  // (\"error 971\"); responseBody is the fallback for when CORS hides the\n  // header (Access-Control-Expose-Headers missing on the error response).\n  const handleSegmentErrorCode = useCallback((errorCode: unknown, responseBody?: unknown): boolean => {\n    let code = errorCode === undefined || errorCode === null ? '' : String(errorCode).trim();\n    if (!code && typeof responseBody === 'string') {\n      code = /^error\\s+(\\d+)$/i.exec(responseBody.trim())?.[1] ?? '';\n    }\n    if (!code) return false;\n    if (code !== '940' && !SEGMENT_ERROR_MESSAGE_KEYS[code]) return false;\n    if (segmentErrorHandledRef.current) return true;\n    segmentErrorHandledRef.current = true;\n\n    if (code === '940') {\n      // Playback token expired (e.g. paused for too long) — a reload re-issues it.\n      console.warn('Segment response returned X-Error-Code 940 (token expired), reloading page...');\n      window.location.reload();\n      return true;\n    }\n\n    console.warn(`Segment response returned X-Error-Code ${code}, blocking playback`);\n    try {\n      videoElementRef.current?.pause();\n    } catch {}\n    onPlaybackBlocked?.(SEGMENT_ERROR_MESSAGE_KEYS[code]);\n    return true;\n  }, [onPlaybackBlocked]);\n\n  // Checks a Shaka BAD_HTTP_STATUS (1001) error for a segment response\n  // carrying a blocking X-Error-Code. Non-2xx segment responses never reach\n  // response filters, so the code arrives via the error's headers (data[3])\n  // or response body (data[2]). Reaches us through Shaka's 'error' event\n  // mid-playback, or as a load() rejection when the init segment fails.\n  const handleSegmentHttpError = useCallback((error: any): boolean => {\n    if (error?.code !== 1001 || !Array.isArray(error?.data)) return false;\n    if (error.data[4] !== (shaka as any).net.NetworkingEngine.RequestType.SEGMENT) return false;\n    return handleSegmentErrorCode(error.data[3]?.['x-error-code'], error.data[2]);\n  }, [handleSegmentErrorCode]);\n\n  // Internal function to handle the actual player initialization logic\n  const initializePlayerInternal = useCallback(async (video: HTMLVideoElement) => {\n    try {\n      // ============================================================\n      // LIFECYCLE COORDINATION & PLAYER CREATION\n      // ============================================================\n      // Serialize with any pending destroy to avoid MediaSource races\n      if (destroyInProgressRef.current) {\n        try {\n          await destroyInProgressRef.current;\n        } catch {\n          // Ignore destroy errors here; we'll proceed with a clean init\n        }\n      }\n\n      const myInitId = ++initSequenceRef.current;\n      activeInitIdRef.current = myInitId;\n\n      // Store video element reference for retry functionality\n      videoElementRef.current = video;\n      // Install polyfills\n      shaka.polyfill.installAll();\n\n      // Check for browser support\n      if (!shaka.Player.isBrowserSupported()) {\n        throw new Error('Browser not supported by Shaka Player');\n      }\n      \n      // If a destroy was requested meanwhile, abort early\n      if (isDestroyingRef.current) {\n        return;\n      }\n\n      // Create player instance first, then attach the video element.\n      const player = new (shaka.Player as any)();\n      playerRef.current = player;\n\n      // Attach to the video element (recommended approach since Shaka v4).\n      await (player as any).attach(video);\n\n      // ============================================================\n      // PLAYER CONFIGURATION (DVR, Streaming Settings)\n      // ============================================================\n      // Configure default settings for expanding DVR window in live streams\n      // This ensures the DVR window grows from the origin rather than sliding forward\n      const defaultConfig = {\n        manifest: {\n          // Override availability window to allow DVR window to grow from start\n          // Set to a very large value (24 hours in seconds) to effectively allow unlimited growth\n          availabilityWindowOverride: 86400, // 24 hours in seconds\n        },\n        streaming: {\n          // Allow seeking to any point within the availability window\n          safeSeekOffset: 5, // 5 seconds from live edge to prevent buffering\n          // Increase tolerance for manifest timing inaccuracies in live streams\n          inaccurateManifestTolerance: 2,\n        }\n      };\n\n      // Apply default config first\n      player.configure(defaultConfig);\n\n      // Configure player with user-provided config (can override defaults)\n      if (shakaConfig) {\n        player.configure(shakaConfig);\n      }\n      \n      const playlistSrc = normalizeSource(src);\n      const manifestUrl = getManifestUrl();\n      let playlistId = playlistSrc.id;\n      const isDRM = Boolean(playlistSrc.drm?.widevine || playlistSrc.drm?.fairplay || playlistSrc.drm?.playready);\n\n      // Reset per-load guards\n      storedPersistentThisLoadRef.current = false;\n      segmentErrorHandledRef.current = false;\n\n      // If init was superseded, tear down the created player and exit\n      if (activeInitIdRef.current !== myInitId || isDestroyingRef.current) {\n        try { await player.destroy(); } catch {}\n        if (playerRef.current === player) playerRef.current = null;\n        return;\n      }\n\n      // ============================================================\n      // DRM CONFIGURATION\n      // ============================================================\n      \n      // Handle persistent licenses for DRM content\n      let storedSessionsMetadata: PersistentSessionMetadata[] = [];\n      if (isDRM && playlistId) {\n        storedSessionsMetadata = retrievePersistentLicense(playlistId, playlistSrc.drm?.licenseCacheKey ?? \"\");\n      }\n\n      if (isDRM) {\n        // --- DRM Server & Key System Configuration ---\n        const drmConfig: any = {\n          servers: {\n            'com.widevine.alpha': playlistSrc.drm?.widevine?.licenseUrl,\n            'com.microsoft.playready': playlistSrc.drm?.playready?.licenseUrl,\n            'com.apple.fps': playlistSrc.drm?.fairplay?.licenseUrl,\n          },\n          keySystemsMapping: {\n            // Fall back or reroute to the platform's recommended PlayReady CDM if needed\n            'com.microsoft.playready': 'com.microsoft.playready.recommendation'\n          },\n          ...(playlistSrc.drm?.fairplay && {\n            advanced: {\n              'com.apple.fps': {\n                serverCertificateUri: playlistSrc.drm.fairplay.certificateUrl,\n              }\n            }\n          })\n        };\n\n        drmConfig.advanced = {\n          ...drmConfig.advanced,\n          'com.widevine.alpha': {\n            ...drmConfig.advanced?.['com.widevine.alpha'],\n            sessionType: supportsWidevinePersistentLicenses() ? 'persistent-license' : 'temporary'\n          },\n          'com.microsoft.playready': {\n            ...drmConfig.advanced?.['com.microsoft.playready'],\n            sessionType: 'persistent-license' // PlayReady seems to always require persistent-license (temporary won't work, not compatible with license server response)\n          },\n          'com.apple.fps': {\n            ...drmConfig.advanced?.['com.apple.fps'],\n            sessionType: 'temporary' // FairPlay always uses temporary sessions - Safari won't play with persistent-license\n          }\n        };\n\n        // Configure persistent session settings if we have stored sessions\n        if (storedSessionsMetadata.length > 0) {\n          drmConfig.persistentSessionOnlinePlayback = true;\n          drmConfig.persistentSessionsMetadata = storedSessionsMetadata.map(session => ({\n            sessionId: session.sessionId,\n            initData: new Uint8Array(session.initData),\n            initDataType: session.initDataType\n          }));\n        }\n\n        usingPersistentLicenseRef.current = storedSessionsMetadata.length > 0;\n\n        player.configure({ drm: drmConfig });\n\n        // --- Stale License Recovery: 'waitingforkey' Handler ---\n        // Detect situations where the browser is waiting for a decryption key but none becomes usable\n        // This can happen when a persisted license is invalid/stale. We listen for 'waitingforkey'\n        // and, if playback does not resume shortly, clear cached licenses and reload.\n        const clearWaitingForKeyTimer = () => {\n          if (waitingForKeyTimerRef.current !== null) {\n            window.clearTimeout(waitingForKeyTimerRef.current);\n            waitingForKeyTimerRef.current = null;\n          }\n        };\n\n        const onPlaybackResumed = () => {\n          clearWaitingForKeyTimer();\n        };\n\n        const onWaitingForKey = () => {\n          // Only attempt recovery when we intentionally tried to use a cached persistent license\n          if (!usingPersistentLicenseRef.current) return;\n          // Avoid stacking multiple timers or looping retries\n          if (isRetrying) return;\n          clearWaitingForKeyTimer();\n\n          // Give the player a short window to provide/refresh keys normally\n          waitingForKeyTimerRef.current = window.setTimeout(async () => {\n            try {\n              if (isRetrying) return;\n\n              // If we are still stuck without keys, clear cached persistent licenses and retry\n              console.warn('Stuck waiting for decryption key; attempting license cache reset and reload...');\n              const clearedCount = clearAllPersistentLicenses();\n\n              if (clearedCount > 0 && playerRef.current) {\n                setIsRetrying(true);\n                await playerRef.current.load(getManifestUrl());\n                console.log('Reloaded manifest after clearing cached licenses');\n              } else {\n                console.warn('No cached licenses found to clear, skipping reload');\n              }\n            } catch (e) {\n              console.error('Failed during recovery from waitingforkey state:', e);\n              onError?.(e as Error);\n            } finally {\n              setIsRetrying(false);\n              clearWaitingForKeyTimer();\n            }\n          }, 3000);\n        };\n\n        // Attach listeners to the video element\n        try {\n          (video as any).addEventListener('waitingforkey', onWaitingForKey as any);\n          video.addEventListener('playing', onPlaybackResumed);\n          video.addEventListener('ended', onPlaybackResumed);\n          video.addEventListener('pause', onPlaybackResumed);\n          waitingForKeyHandlerRef.current = onWaitingForKey;\n          playbackResumedHandlerRef.current = onPlaybackResumed;\n        } catch (e) {\n          console.warn('Failed to attach waitingforkey/playback listeners:', e);\n        }\n\n        // --- Network Engine Filters: Auth Headers & FairPlay Decoding ---\n        const netEngine = player.getNetworkingEngine();\n        if (netEngine) {\n          netEngine.registerRequestFilter((type: any, request: any) => {\n            switch (type) {\n              case (shaka as any).net.NetworkingEngine.RequestType.LICENSE:\n                if (publicKey) {\n                  request.headers[\"authorization\"] = `Bearer ${publicKey}`;\n                }\n                if (mottoToken) {\n                  request.headers[\"x-motto-token\"] = mottoToken;\n                }\n                break;\n              case (shaka as any).net.NetworkingEngine.RequestType.MANIFEST:\n              case (shaka as any).net.NetworkingEngine.RequestType.SEGMENT:\n                // Allow cross-site credentials so that cookies can be set from the manifest to the segments for JWT validation\n                request.allowCrossSiteCredentials = true\n                break;\n            }\n          });\n\n          // Register response filter for FairPlay base64 decoding\n          netEngine.registerResponseFilter((type: any, response: any) => {\n            if (type === (shaka as any).net.NetworkingEngine.RequestType.LICENSE) {\n              const ks = player.keySystem && player.keySystem();\n              if (ks === 'com.apple.fps') {\n                const responseText = (shaka as any).util.StringUtils.fromUTF8(response.data);\n                response.data = (shaka as any).util.Uint8ArrayUtils.fromBase64(responseText).buffer;\n              }\n            }\n          });\n        }\n      }\n      // ============================================================\n      // END DRM CONFIGURATION\n      // ============================================================\n\n      // --- Segment X-Error-Code Handling (DRM & Non-DRM) ---\n      // The CDN reports playback-blocking conditions (expired token, IP\n      // change, VPN, anonymous proxy) via the X-Error-Code header on segment\n      // responses. Response filters only run for 2xx responses; segments that\n      // fail with a non-2xx status carry the same header through the\n      // BAD_HTTP_STATUS branch of the 'error' listener below.\n      const segmentNetEngine = player.getNetworkingEngine();\n      if (segmentNetEngine) {\n        segmentNetEngine.registerResponseFilter((type: any, response: any) => {\n          if (type === (shaka as any).net.NetworkingEngine.RequestType.SEGMENT) {\n            handleSegmentErrorCode(response?.headers?.['x-error-code']);\n          }\n        });\n      }\n\n      // --- API Token Authorization (DRM & Non-DRM) ---\n      if (apiToken) {\n        const netEngine = player.getNetworkingEngine();\n        if (netEngine) {\n          netEngine.registerRequestFilter((type: any, request: any) => {\n            if (type === (shaka as any).net.NetworkingEngine.RequestType.MANIFEST) {\n              request.headers[\"authorization\"] = `Bearer ${apiToken}`;\n            }\n          });\n        }\n      }\n\n      // --- License Persistence: Cache Valid Licenses ---\n      // Persist licenses after Shaka signals license expiration updates, which occur\n      // only after successful license acquisition/refresh. This avoids caching on errors\n      // or during the initial clear (unencrypted) segment playback.\n      if (isDRM && playlistId) {\n        const onDRMSessionUpdate = () => {\n          try {\n            if (storedPersistentThisLoadRef.current) return;\n            const activeDrmSessions = player.getActiveSessionsMetadata?.();\n            if (!activeDrmSessions) return;\n\n            const persistentSessions = activeDrmSessions.filter(\n              (session: any) => session.sessionType === 'persistent-license'\n            );\n\n            if (persistentSessions.length > 0) {\n              const sessionsToStore: PersistentSessionMetadata[] = persistentSessions.map((session: any) => ({\n                sessionId: session.sessionId,\n                initData: session.initData,\n                initDataType: session.initDataType,\n                keySystem: session.keySystem || 'com.widevine.alpha'\n              }));\n              storePersistentLicense(playlistId, playlistSrc.drm?.licenseCacheKey ?? \"\", sessionsToStore);\n              storedPersistentThisLoadRef.current = true;\n            }\n          } catch (e) {\n            console.warn('Failed to persist licenses on expiration update:', e);\n          }\n        };\n\n        try {\n          player.addEventListener('drmsessionupdate', onDRMSessionUpdate as any);\n          drmExpirationHandlerRef.current = onDRMSessionUpdate as any;\n        } catch (e) {\n          console.warn('Failed to attach drmsessionupdate listener:', e);\n        }\n      }\n      \n      // ============================================================\n      // ERROR HANDLING & DRM RETRY LOGIC\n      // ============================================================\n      player?.addEventListener('error', (event: any) => {\n        const error = event.detail;\n\n        // Ignore Shaka's LOAD_INTERRUPTED (code 7000) since it indicates a\n        // benign race between load/unload calls when the source changes.\n        if (error?.code === 7000) {\n          return;\n        }\n\n        // Segment responses with a non-2xx status never reach response\n        // filters; Shaka raises BAD_HTTP_STATUS (code 1001) instead. Check it\n        // for a blocking X-Error-Code.\n        if (handleSegmentHttpError(error)) {\n          return;\n        }\n\n        // Check if error code is in the 6000-6999 range (DRM-related errors)\n        // and trigger license clearing and retry\n        if (error?.code >= 6000 && error?.code < 7000 && !isRetrying && videoElementRef.current) {\n          console.warn(`DRM error detected (code: ${error.code}), checking for cached licenses...`);\n          \n          // Clear all cached licenses and check if any were actually cleared\n          const clearedCount = clearAllPersistentLicenses();\n          \n          // Only retry if we actually cleared some licenses\n          if (clearedCount > 0) {\n            console.warn(`Cleared ${clearedCount} cached licenses, retrying...`);\n            setIsRetrying(true);\n            \n            // Use setTimeout to avoid blocking and allow the error to be processed\n            setTimeout(async () => {\n              try {\n                // Get the video element and current manifest URL\n                const video = videoElementRef.current;\n                const currentPlayer = playerRef.current;\n                \n                if (video && currentPlayer) {\n                  console.log('Reloading manifest after license cache clear...');\n                  \n                  // Simply reload the manifest with the current player instance\n                  // This will trigger new license requests without the cached licenses\n                  await currentPlayer.load(getManifestUrl());\n                  \n                  console.log('Manifest reloaded successfully');\n                }\n              } catch (retryError) {\n                console.error('Failed to retry after license clear:', retryError);\n                onError?.(retryError as Error);\n                onFatalError?.();\n              } finally {\n                setIsRetrying(false);\n              }\n            }, 500); // Give a bit more time for the error to be processed\n            return;\n          } else {\n            console.warn(`No cached licenses found to clear for error code ${error.code}, not retrying to avoid infinite loop`);\n          }\n        }\n\n        console.error('Shaka Player Error:', error);\n        onError?.(new Error(`Shaka Player Error: ${error.message || 'Unknown error'}`));\n\n        // Recoverable errors (severity 1) are ones Shaka keeps retrying\n        // (e.g. a temporarily disabled stream); only give up on critical ones.\n        if (error?.severity === (shaka as any).util.Error.Severity.CRITICAL) {\n          onFatalError?.();\n        }\n      });\n\n      // ============================================================\n      // MUX ANALYTICS INITIALIZATION\n      // ============================================================\n      // Initialize Mux BEFORE loading so it can hook into networking engine\n      if (muxConfig) {\n        try {\n          const playerInitTime = initShakaPlayerMux.utils.now();\n          \n          const muxOptions = {\n            debug: muxConfig.debug || false,\n            disableCookies: muxConfig.disableCookies || false,\n            respectDoNotTrack: muxConfig.respectDoNotTrack || false,\n            automaticErrorTracking: muxConfig.automaticErrorTracking !== false,\n            ...(muxConfig.beaconCollectionDomain && { beaconCollectionDomain: muxConfig.beaconCollectionDomain }),\n            ...(muxConfig.errorTranslator && { errorTranslator: muxConfig.errorTranslator }),\n            data: {\n              env_key: muxConfig.envKey,\n              player_name: muxConfig?.metadata?.player_name,\n              player_version: version,\n              player_init_time: playerInitTime,\n              video_title: muxConfig?.metadata?.video_title ?? \"\",\n              video_id: muxConfig?.metadata?.video_id ?? \"\",\n              viewer_user_id: muxConfig?.metadata?.viewer_user_id ?? \"\",\n              ...muxConfig.metadata,\n            }\n          };\n\n          // Give Mux a token-scrubbing view of the player so signed CDN tokens\n          // never reach analytics, while the real player keeps full signed URLs\n          // for playback (manifest refreshes, segment fetches).\n          muxMonitorRef.current = initShakaPlayerMux(createMuxScrubbingPlayer(player), muxOptions, shaka);\n          onMuxReady?.();\n        } catch (error) {\n          console.error('Failed to initialize Mux Analytics:', error);\n        }\n      }\n      \n      // ============================================================\n      // MANIFEST LOADING (Conditional)\n      // ============================================================\n      // Final guard before load to avoid racing with teardown\n      if (activeInitIdRef.current !== myInitId || isDestroyingRef.current) {\n        try { await player.destroy(); } catch {}\n        if (playerRef.current === player) playerRef.current = null;\n        return;\n      }\n      \n      // IMPORTANT: Skip automatic manifest loading if System73 or ads are configured\n      // \n      // Why skip for System73?\n      // - System73 SDK must wrap the player BEFORE manifest loads\n      // - This allows it to intercept manifest parsing and detect all ABR profiles\n      // - If we load now, System73 would only see the single bitrate Shaka selects\n      // - Result: streams incorrectly report as mono-bitrate in analytics\n      //\n      // Why skip for ads?\n      // - Google IMA needs to intercept media loading to serve pre-roll ads\n      // - The IMA loader will call loadManifest() after ads complete\n      //\n      // When skipped: usePlayerInitialization will call loadManifest() explicitly\n      // after System73/ads are ready\n      if (!hasAds && !hasSystem73) {\n        // console.log('📺 [Shaka] Auto-loading manifest:', manifestUrl);\n        await player.load(manifestUrl);\n        // console.log('✅ [Shaka] Manifest auto-loaded successfully');\n      } else {\n        // console.log('⏭️  [Shaka] Skipping auto-load (hasAds:', hasAds, ', hasSystem73:', hasSystem73, ')');\n      }\n      \n      onPlayerReady?.(player);\n\n      return player;\n    } catch (error) {\n      // Filter out benign load interruptions that occur when the component\n      // switches source before the previous load completes.\n      if ((error as any)?.code === 7000) {\n        return;\n      }\n\n      // A blocked init segment rejects load() instead of firing the 'error'\n      // event; the blocking screen is already up via onPlaybackBlocked.\n      // Rethrow so the caller still reports the failed load to Mux.\n      if (handleSegmentHttpError(error)) {\n        throw error;\n      }\n\n      console.error('Error initializing Shaka Player:', error);\n      onError?.(error as Error);\n      onFatalError?.();\n      throw error;\n    }\n  }, [shakaConfig, drmConfig, src, onError, onFatalError, onPlayerReady, muxConfig, onMuxReady, isRetrying, getManifestUrl, apiToken, handleSegmentErrorCode, handleSegmentHttpError]);\n\n  // Public initializePlayer function that components will use\n  const initializePlayer = useCallback(async (video: HTMLVideoElement) => {\n    return initializePlayerInternal(video);\n  }, [initializePlayerInternal]);\n\n  // Report errors from load()/init to Mux. Playback errors after a successful\n  // load reach Mux by itself via the player's 'error' event; load failures only\n  // reach it through this handler.\n  const handleMuxError = useCallback((error: unknown) => {\n    try {\n      muxMonitorRef.current?.loadErrorHandler?.(error);\n    } catch (e) {\n      console.warn('Failed to report load error to Mux:', e);\n    }\n  }, []);\n\n  // Public method to load the manifest after ads are set up\n  const loadManifest = useCallback(async () => {\n    const player = playerRef.current;\n    if (!player) {\n      console.warn('⚠️  [Shaka] Cannot load manifest: player not initialized');\n      return;\n    }\n\n    try {\n      const manifestUrl = getManifestUrl();\n      // console.log('📺 [Shaka] Loading manifest:', manifestUrl);\n      await player.load(manifestUrl);\n      // console.log('✅ [Shaka] Manifest loaded successfully');\n    } catch (error) {\n      // Filter out benign load interruptions\n      if ((error as any)?.code === 7000) {\n        return;\n      }\n      // See the matching check in initializePlayerInternal's catch.\n      if (handleSegmentHttpError(error)) {\n        throw error;\n      }\n      console.error('❌ [Shaka] Error loading manifest:', error);\n      onError?.(error as Error);\n      onFatalError?.();\n      throw error;\n    }\n  }, [getManifestUrl, onError, onFatalError, handleSegmentHttpError]);\n\n  const destroyPlayer = useCallback(async () => {\n    const playerInstance = playerRef.current;\n    if (playerInstance) {\n      try {\n        isDestroyingRef.current = true;\n        activeInitIdRef.current = null;\n        // Clean up video element listeners and timers\n        if (videoElementRef.current) {\n          try {\n            if (waitingForKeyHandlerRef.current) {\n              (videoElementRef.current as any).removeEventListener('waitingforkey', waitingForKeyHandlerRef.current as any);\n            }\n            if (playbackResumedHandlerRef.current) {\n              videoElementRef.current.removeEventListener('playing', playbackResumedHandlerRef.current);\n              videoElementRef.current.removeEventListener('ended', playbackResumedHandlerRef.current);\n              videoElementRef.current.removeEventListener('pause', playbackResumedHandlerRef.current);\n            }\n          } catch (e) {\n            console.warn('Error removing media event listeners:', e);\n          }\n        }\n        if (waitingForKeyTimerRef.current !== null) {\n          window.clearTimeout(waitingForKeyTimerRef.current);\n          waitingForKeyTimerRef.current = null;\n        }\n\n        // Clean up DRM-related player listeners\n        try {\n          if (drmExpirationHandlerRef.current && playerInstance.removeEventListener) {\n            playerInstance.removeEventListener('drmsessionupdate', drmExpirationHandlerRef.current as any);\n          }\n        } catch (e) {\n          console.warn('Error removing DRM expiration listener:', e);\n        } finally {\n          drmExpirationHandlerRef.current = null;\n        }\n        // Ensure only one destroy runs and let init await it\n        const performDestroy = async () => {\n          try {\n            await playerInstance.destroy();\n          } finally {\n            // After Shaka teardown, also clear the HTMLVideoElement src to drop any object URL\n            try {\n              if (videoElementRef.current) {\n                videoElementRef.current.removeAttribute('src');\n                // Calling load() ensures the element releases previous MediaSource eagerly\n                videoElementRef.current.load();\n              }\n            } catch {}\n          }\n        };\n        destroyInProgressRef.current = performDestroy();\n        await destroyInProgressRef.current;\n      } catch (error) {\n        console.warn('Error destroying Shaka Player:', error);\n      } finally {\n        if (playerRef.current === playerInstance) {\n          playerRef.current = null;\n        }\n        muxMonitorRef.current = null;\n        storedPersistentThisLoadRef.current = false;\n        isDestroyingRef.current = false;\n        destroyInProgressRef.current = null;\n      }\n    }\n  }, [playerRef]);\n\n  return {\n    playerRef,\n    initializePlayer,\n    loadManifest,\n    destroyPlayer,\n    handleMuxError,\n    isRetrying\n  };\n};","export const isAppleDevice = () => {\n    if (typeof navigator === 'undefined') return false;\n    const ua = navigator.userAgent || '';\n    const isIOS = /iPad|iPhone|iPod/.test(ua) || (navigator.platform === 'MacIntel' && (navigator as any).maxTouchPoints > 1);\n    const isSafari = /Safari/.test(ua) && !/Chrome|CriOS|FxiOS|Edg/.test(ua);\n    const isMacSafari = /Macintosh/.test(ua) && isSafari;\n    return isIOS || isMacSafari;\n};\n\nexport const isPlayReadySupported = (): boolean => {\n  // Check if we're in a browser environment\n  if (typeof navigator === 'undefined' || typeof window === 'undefined') {\n    return false;\n  }\n\n  // Check if EME API is supported\n  if (!navigator.requestMediaKeySystemAccess) {\n    return false;\n  }\n\n  // Check if we're on a browser that actually supports PlayReady\n  const userAgent = navigator.userAgent || '';\n  const isXbox = /Xbox/.test(userAgent);\n  const isEdge = /Edg/.test(userAgent);\n  const isIE = /Trident|MSIE/.test(userAgent);\n  const isWindows = /Windows/.test(userAgent);\n  \n  // PlayReady is only supported on Windows with Microsoft browsers or Xbox\n  // Edge on macOS does NOT support PlayReady, only Edge on Windows does\n  // Chrome on any platform uses Widevine, not PlayReady\n  return isXbox || ((isEdge || isIE) && isWindows);\n};\n\n/**\n * Detects if the current browser is Chrome 64+ on macOS, Windows.\n * Persistent license caching for Widevine only works on these specific browsers\n * @returns true if Chrome 64+ on macOS/Windows/Android, false otherwise\n *\n * IMPORTANT UPDATE: Temporarily disabled everywhere, since there are some issues with persistent licenses on web.\n * Specifically, sometimes the page needs to be reloaded multiple times before playback works.\n * We don't know why yet, and have no time to investigate.\n */\nexport const supportsWidevinePersistentLicenses = (): boolean => {\n  // TODO: Re-enable once issues are resolved\n  return false;\n\n  // Check if we're in a browser environment\n  if (typeof navigator === 'undefined') {\n    return false;\n  }\n\n  const userAgent = navigator.userAgent || '';\n  \n  // Check if it's Chrome (not Chromium-based browsers like Edge)\n  const isChromeMatch = userAgent.match(/Chrome\\/(\\d+)/);\n  if (!isChromeMatch) {\n    return false;\n  }\n  \n  // Make sure it's not Edge, Opera, or other Chromium-based browsers\n  if (/Edg|OPR|Opera/.test(userAgent)) {\n    return false;\n  }\n  \n  // Extract Chrome version\n  const chromeVersion = parseInt(isChromeMatch[1], 10);\n  if (chromeVersion < 64) {\n    return false;\n  }\n  \n  // Check if it's macOS, Windows, or Android\n  const isMacOS = /Mac OS X|Macintosh/.test(userAgent);\n  const isWindows = /Windows/.test(userAgent);\n\n  // Tested on Android, with mixed results. Sometimes it works, sometimes it doesn't.\n\n  return isMacOS || isWindows;\n};","{\n  \"name\": \"@mottosports/motto-video-player\",\n  \"version\": \"1.1.0\",\n  \"description\": \"React video player component for the Motto platform, powered by Shaka Player\",\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.mjs\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"import\": \"./dist/index.mjs\",\n      \"require\": \"./dist/index.js\"\n    }\n  },\n  \"files\": [\n    \"dist\",\n    \"README.md\"\n  ],\n  \"scripts\": {\n    \"build\": \"tsup\",\n    \"dev\": \"NODE_ENV=development tsup --watch\",\n    \"dev:hot\": \"NODE_ENV=development tsup --watch --onSuccess \\\"echo 'Hot reload ready! Package rebuilt successfully.'\\\"\",\n    \"clean\": \"rm -rf dist\",\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"keywords\": [\n    \"video\",\n    \"player\",\n    \"react\",\n    \"component\",\n    \"motto\",\n    \"shaka\",\n    \"adaptive-streaming\",\n    \"dash\",\n    \"hls\",\n    \"drm\"\n  ],\n  \"author\": \"Motto UI Components\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@buf/motto_cda.bufbuild_es\": \"^2.11.0-20260702135849-6e5b189e7924.1\",\n    \"@bufbuild/protobuf\": \"^2.11.0\",\n    \"@connectrpc/connect\": \"^2.1.2\",\n    \"@connectrpc/connect-web\": \"^2.1.2\",\n    \"@mux/mux-data-shakaplayer\": \"^5.14.7\",\n    \"@tanstack/react-query\": \"^5.80.3\",\n    \"shaka-player\": \"^4.16.0\",\n    \"tailwind-merge\": \"^3.3.0\",\n    \"zustand\": \"^5.0.2\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^22.15.29\",\n    \"@types/react\": \"^18.2.79\",\n    \"@types/react-dom\": \"^19.1.5\",\n    \"tsup\": \"^8.5.0\",\n    \"typescript\": \"^5.8.3\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16.8.0\",\n    \"react-dom\": \">=16.8.0\",\n    \"tailwindcss\": \">=3.0.0\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  }\n}\n","// Persistent license storage utilities with LRU eviction for quota management\n\nexport interface PersistentSessionMetadata {\n  sessionId: string;\n  initData: ArrayBuffer;\n  initDataType: string;\n  keySystem: string;\n}\n\nexport interface StoredSessionData {\n  sessionId: string;\n  initData: string; // Base64 encoded\n  initDataType: string;\n  keySystem: string;\n  timestamp: number;\n}\n\ninterface LicenseCacheEntry {\n  key: string;\n  data: StoredSessionData[];\n}\n\nconst PERSISTENT_LICENSE_PREFIX = 'motto_lic_';\nconst LICENSE_EXPIRY_MS = 2 * 60 * 60 * 1000; // 2 hours\nconst LICENSE_MAX_RETRY_ATTEMPTS = 10; // Maximum number of LRU evictions to attempt\n\n/**\n * Gets all license cache keys from localStorage\n */\nconst getAllLicenseCacheKeys = (): string[] => {\n  const keys: string[] = [];\n  for (let i = 0; i < localStorage.length; i++) {\n    const key = localStorage.key(i);\n    if (key?.startsWith(PERSISTENT_LICENSE_PREFIX)) {\n      keys.push(key);\n    }\n  }\n  return keys;\n};\n\n/**\n * Gets all license cache entries\n */\nconst getAllLicenseCacheEntries = (): LicenseCacheEntry[] => {\n  const keys = getAllLicenseCacheKeys();\n  const entries: LicenseCacheEntry[] = [];\n  \n  for (const key of keys) {\n    try {\n      const stored = localStorage.getItem(key);\n      if (stored) {\n        const data: StoredSessionData[] = JSON.parse(stored);\n        entries.push({ key, data });\n      }\n    } catch (error) {\n      console.warn(`Failed to parse license cache entry for key ${key}:`, error);\n      // Remove corrupted entries\n      localStorage.removeItem(key);\n    }\n  }\n  \n  return entries;\n};\n\n/**\n * Removes the least recently used license cache entry\n * @returns true if an entry was removed, false if no entries exist\n */\nconst evictLRUEntry = (): boolean => {\n  const entries = getAllLicenseCacheEntries();\n  \n  if (entries.length === 0) {\n    return false;\n  }\n  \n  // Sort by oldest timestamp in each entry (oldest first)\n  entries.sort((a, b) => {\n    const oldestA = Math.min(...a.data.map(session => session.timestamp));\n    const oldestB = Math.min(...b.data.map(session => session.timestamp));\n    return oldestA - oldestB;\n  });\n  \n  // Remove the oldest entry\n  const lruEntry = entries[0];\n  localStorage.removeItem(lruEntry.key);\n  \n  console.log(`Evicted LRU license cache entry: ${lruEntry.key}`);\n  return true;\n};\n\n/**\n * Attempts to store data in localStorage with LRU eviction on quota exceeded\n */\nconst storeWithQuotaHandling = (key: string, data: string): boolean => {\n  let attempts = 0;\n  \n  while (attempts < LICENSE_MAX_RETRY_ATTEMPTS) {\n    try {\n      localStorage.setItem(key, data);\n      return true;\n    } catch (error) {\n      // Check if this is a quota exceeded error\n      if (error instanceof DOMException && (\n        error.code === 22 || // QUOTA_EXCEEDED_ERR\n        error.name === 'QuotaExceededError' ||\n        error.name === 'NS_ERROR_DOM_QUOTA_REACHED'\n      )) {\n        console.warn(`QuotaExceededError on attempt ${attempts + 1}, attempting LRU eviction...`);\n        \n        // Try to evict an LRU entry\n        if (!evictLRUEntry()) {\n          console.error('No more entries to evict, storage operation failed');\n          return false;\n        }\n        \n        attempts++;\n      } else {\n        // Non-quota error, don't retry\n        console.error('Failed to store license cache data:', error);\n        return false;\n      }\n    }\n  }\n  \n  console.error(`Failed to store license cache data after ${LICENSE_MAX_RETRY_ATTEMPTS} eviction attempts`);\n  return false;\n};\n\n/**\n * Shared function to manage persistent license storage by:\n * 1. Loading existing sessions from localStorage\n * 2. Filtering out expired sessions\n * 3. Merging with new sessions (if provided)\n * 4. Updating localStorage with the cleaned data (with quota handling)\n */\nexport const managePersistentLicenseStorage = (\n  playlistId: string,\n  licenseCacheKey: string,\n  newSessionMetadata?: PersistentSessionMetadata[]\n): StoredSessionData[] => {\n  try {\n    const storageKey = `${PERSISTENT_LICENSE_PREFIX}${playlistId}_${licenseCacheKey}`;\n    const stored = localStorage.getItem(storageKey);\n    let existingSessions: StoredSessionData[] = [];\n\n    if (stored) {\n      try {\n        existingSessions = JSON.parse(stored);\n      } catch (parseError) {\n        console.warn('Failed to parse existing persistent license data:', parseError);\n        existingSessions = [];\n      }\n    }\n    \n    const now = Date.now();\n    \n    // Filter out expired sessions\n    let validSessions = existingSessions.filter(session => \n      now - session.timestamp < LICENSE_EXPIRY_MS\n    );\n    \n    // If new sessions are provided, merge them with existing valid sessions\n    if (newSessionMetadata && newSessionMetadata.length > 0) {\n      const newSessions: StoredSessionData[] = newSessionMetadata.map(session => {\n        const uint8Array = new Uint8Array(session.initData);\n        const binaryString = Array.from(uint8Array).map(byte => String.fromCharCode(byte)).join('');\n        return {\n          sessionId: session.sessionId,\n          initData: btoa(binaryString),\n          initDataType: session.initDataType,\n          keySystem: session.keySystem,\n          timestamp: now\n        };\n      });\n      \n      // Remove any existing sessions with the same sessionId to avoid duplicates\n      const newSessionIds = new Set(newSessions.map(s => s.sessionId));\n      validSessions = validSessions.filter(session => !newSessionIds.has(session.sessionId));\n      \n      // Add new sessions\n      validSessions = [...validSessions, ...newSessions];\n    }\n    \n    // Update localStorage with the cleaned/merged data using quota handling\n    if (validSessions.length === 0) {\n      localStorage.removeItem(storageKey);\n    } else {\n      const dataToStore = JSON.stringify(validSessions);\n      const success = storeWithQuotaHandling(storageKey, dataToStore);\n      \n      if (!success) {\n        console.error(`Failed to store license cache for ${storageKey} after quota handling`);\n        // Return the sessions even if storage failed\n        return validSessions;\n      }\n    }\n    \n    return validSessions;\n  } catch (error) {\n    console.warn('Failed to manage persistent license storage:', error);\n    return [];\n  }\n};\n\n/**\n * Stores persistent license sessions for a playlist\n */\nexport const storePersistentLicense = (\n  playlistId: string,\n  licenseCacheKey: string,\n  sessionMetadata: PersistentSessionMetadata[]\n): void => {\n  managePersistentLicenseStorage(playlistId, licenseCacheKey, sessionMetadata);\n};\n\n/**\n * Retrieves persistent license sessions for a playlist\n */\nexport const retrievePersistentLicense = (\n  playlistId: string,\n  licenseCacheKey: string\n): PersistentSessionMetadata[] => {\n  try {\n    const validSessions = managePersistentLicenseStorage(playlistId, licenseCacheKey);\n    \n    return validSessions.map(session => ({\n      sessionId: session.sessionId,\n      initData: Uint8Array.from(atob(session.initData), c => c.charCodeAt(0)).buffer,\n      initDataType: session.initDataType,\n      keySystem: session.keySystem\n    }));\n  } catch (error) {\n    console.warn('Failed to retrieve persistent license:', error);\n    return [];\n  }\n};\n\n/**\n * Clears all cached persistent licenses from localStorage\n * @returns The number of license cache entries that were cleared\n */\nexport const clearAllPersistentLicenses = (): number => {\n  try {\n    const keys = getAllLicenseCacheKeys();\n    \n    for (const key of keys) {\n      localStorage.removeItem(key);\n    }\n    \n    console.log(`Cleared ${keys.length} persistent license cache entries`);\n    return keys.length;\n  } catch (error) {\n    console.error('Failed to clear persistent licenses:', error);\n    return 0;\n  }\n};\n","import { RefObject, useEffect } from 'react';\n\n/**\n * Orchestrates the initialization sequence for the video player and all integrations.\n * \n * INITIALIZATION ORDER (critical for proper functionality):\n * --------------------------------------------------------\n * 1. Wait for external scripts (IMA SDK, System73) to load\n * 2. Initialize System73 wrapper (if configured)\n * 3. Initialize Shaka Player (skips auto-load if System73/ads are present)\n * 4. Wrap player with System73 (MUST happen before manifest loads)\n * 5. Load manifest explicitly (after System73 wrapper is ready)\n * 6. Setup event listeners & quality tracking\n * 7. Initialize Shaka UI\n * 8. Initialize Google IMA ads (if configured)\n * \n * WHY THIS ORDER MATTERS:\n * ----------------------\n * - System73 needs to wrap the player BEFORE manifest loads to intercept\n *   manifest parsing and detect all ABR (adaptive bitrate) profiles. If it\n *   wraps after load, it only sees the single bitrate that Shaka initially\n *   selected, causing streams to report as mono-bitrate.\n * \n * - Ads need to intercept media loading for pre-roll playback. The IMA SDK\n *   must be initialized after the player and UI are ready but before content\n *   starts playing.\n * \n * - Event listeners and quality tracking need the player to be fully loaded\n *   with a manifest to detect available tracks and qualities.\n * \n * RELATIONSHIP TO OTHER HOOKS:\n * ---------------------------\n * - useShakaPlayer: Provides the low-level methods (initializePlayer, loadManifest)\n *   but doesn't control timing. This hook orchestrates WHEN to call them.\n * \n * - useSystem73/useAds: Provide initialization methods for third-party SDKs.\n *   This hook ensures they're called in the correct sequence.\n */\n\ninterface UsePlayerInitializationProps {\n  videoRef: RefObject<HTMLVideoElement>;\n  playerRef: RefObject<any>;\n  isScriptsLoaded: boolean;\n  setIsInitialLoading: (loading: boolean) => void;\n\n  // Configs\n  system73Config?: { apiKey?: string; contentSteeringEndpoint?: string; channelId?: string };\n  shakaConfig?: any;\n  imaConfig?: { adTagUrl?: string };\n\n  // Initialization functions\n  initializeSystem73: (playerConfig: any) => any;\n  initializePlayer: (video: HTMLVideoElement) => Promise<void>;\n  loadManifest: () => Promise<void>;\n  setupEventListeners: () => void;\n  setupQualityTracking: () => (() => void) | undefined;\n  configureQuality: () => void;\n  updateAvailableQualities: () => void;\n  initializeUI: () => Promise<unknown>;\n  initializeAds: () => Promise<void>;\n  handleMuxError: (error: unknown) => void;\n\n  // Cleanup functions\n  cleanupEventListeners: () => void;\n  cleanupAdEventListeners: () => void;\n  destroyUI: () => void;\n  destroyMux: () => void;\n  destroyPlayer: () => void;\n\n  // Dependencies for re-initialization\n  src: any;\n  hasPlaylist: boolean;\n  managedMode: boolean;\n  bfResetKey: number;\n}\n\nexport const usePlayerInitialization = ({\n  videoRef,\n  playerRef,\n  isScriptsLoaded,\n  setIsInitialLoading,\n  system73Config,\n  shakaConfig,\n  imaConfig,\n  initializeSystem73,\n  initializePlayer,\n  loadManifest,\n  setupEventListeners,\n  setupQualityTracking,\n  configureQuality,\n  updateAvailableQualities,\n  initializeUI,\n  initializeAds,\n  handleMuxError,\n  cleanupEventListeners,\n  cleanupAdEventListeners,\n  destroyUI,\n  destroyMux,\n  destroyPlayer,\n  src,\n  hasPlaylist,\n  managedMode,\n  bfResetKey,\n}: UsePlayerInitializationProps) => {\n  useEffect(() => {\n    const video = videoRef.current;\n    if (!video || !isScriptsLoaded) return;\n\n    // Loading state handlers\n    const onLoadStart = () => setIsInitialLoading(true);\n    const onCanPlay = () => setIsInitialLoading(false);\n    const onPlaying = () => setIsInitialLoading(false);\n\n    video.addEventListener('loadstart', onLoadStart);\n    video.addEventListener('canplay', onCanPlay);\n    video.addEventListener('playing', onPlaying);\n\n    let currentShakaConfig = shakaConfig;\n\n    const initialize = async () => {\n      try {\n        setIsInitialLoading(true);\n\n        // ============================================================\n        // STEP 1: Initialize System73 Wrapper (if configured)\n        // ============================================================\n        // Create the wrapper first so it can modify the player config before\n        // the Shaka Player instance is created\n        let system73Wrapper = null;\n        if (system73Config?.apiKey && window.S73ShakaPlayerWrapper) {\n          const playerConfig = { ...currentShakaConfig };\n          system73Wrapper = initializeSystem73(playerConfig);\n\n          if (system73Wrapper) {\n            currentShakaConfig = playerConfig;\n          }\n        }\n\n        // ============================================================\n        // STEP 2: Initialize Shaka Player (without loading manifest)\n        // ============================================================\n        // Note: initializePlayer() will skip automatic manifest loading when\n        // System73 or ads are configured (hasSystem73=true or hasAds=true)\n        await initializePlayer(video);\n\n        // ============================================================\n        // STEP 3: Wrap Player with System73 & Load Manifest\n        // ============================================================\n        // CRITICAL: wrapPlayer() MUST be called before loadManifest()\n        // so System73 can intercept manifest parsing and detect all ABR profiles\n        if (system73Wrapper && playerRef.current) {\n          system73Wrapper.wrapPlayer(playerRef.current);\n\n          // Load manifest now that System73 is ready (unless ads need to load first)\n          if (!imaConfig?.adTagUrl) {\n            await loadManifest();\n          }\n        }\n\n        // ============================================================\n        // STEP 4: Setup Event Handlers & Quality Tracking\n        // ============================================================\n        setupEventListeners();\n        setupQualityTracking();\n        configureQuality();\n\n        // Give player time to detect qualities after manifest loads\n        setTimeout(() => {\n          updateAvailableQualities();\n        }, 100);\n\n        // ============================================================\n        // STEP 5: Initialize Shaka UI\n        // ============================================================\n        await initializeUI();\n\n        // ============================================================\n        // STEP 6: Initialize Google IMA Ads (if configured)\n        // ============================================================\n        // Ads are initialized last because they need the player, UI, and\n        // all event listeners to be ready. The ads loader will call\n        // loadManifest() after pre-roll ads complete.\n        if (imaConfig?.adTagUrl && window.google?.ima) {\n          await initializeAds();\n        }\n      } catch (error) {\n        console.error('Player initialization error:', error);\n        handleMuxError(error);\n      }\n    };\n\n    initialize();\n\n    return () => {\n      video.removeEventListener('loadstart', onLoadStart);\n      video.removeEventListener('canplay', onCanPlay);\n      video.removeEventListener('playing', onPlaying);\n      cleanupEventListeners();\n      cleanupAdEventListeners();\n      destroyUI();\n      destroyMux();\n      destroyPlayer();\n    };\n  // Dependency array optimization:\n  // - managedMode=false: Reinitialize on every src change (direct Player usage)\n  // - managedMode=true: Only reinitialize when hasPlaylist boolean changes (wrapper usage)\n  // This prevents unnecessary reinitialization when wrapper components refetch data\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [managedMode ? hasPlaylist : src, isScriptsLoaded, bfResetKey, managedMode]);\n};\n","import { useCallback, useRef } from 'react';\nimport { TimeUpdateData } from '../types';\n\ninterface EventHandlers {\n  onPlay?: () => void;\n  onPause?: () => void;\n  onEnded?: () => void;\n  onLoadStart?: () => void;\n  onCanPlay?: () => void;\n  onTimeUpdate?: (timeData: TimeUpdateData) => void;\n}\n\nexport const useEventHandlers = (\n  videoRef: React.RefObject<HTMLVideoElement>,\n  playerRef: React.RefObject<any>,\n  handlers: EventHandlers\n) => {\n  const timeUpdateHandlerRef = useRef<(() => void) | null>(null);\n\n  const setupEventListeners = useCallback(() => {\n    const video = videoRef.current;\n    if (!video) return;\n\n    const { onPlay, onPause, onEnded, onLoadStart, onCanPlay, onTimeUpdate } = handlers;\n\n    if (onPlay) video.addEventListener('play', onPlay);\n    if (onPause) video.addEventListener('pause', onPause);\n    if (onEnded) video.addEventListener('ended', onEnded);\n    if (onLoadStart) video.addEventListener('loadstart', onLoadStart);\n    if (onCanPlay) video.addEventListener('canplay', onCanPlay);\n    \n    // Handle timeupdate with comprehensive data\n    if (onTimeUpdate) {\n      const handleTimeUpdate = () => {\n        const player = playerRef.current;\n        const currentTime = video.currentTime;\n        const duration = video.duration;\n        \n        // Check if it's a live stream\n        const isLive = player && typeof player.isLive === 'function' ? player.isLive() : false;\n        \n        const timeData: TimeUpdateData = {\n          currentTime,\n          duration,\n          isLive\n        };\n        \n        if (isLive && player && typeof player.seekRange === 'function') {\n          // For live streams, provide additional data\n          const seekRange = player.seekRange();\n          if (seekRange && seekRange.end !== undefined) {\n            timeData.liveEdge = seekRange.end;\n            timeData.timeBehindLive = seekRange.end - currentTime;\n          }\n        } else if (duration && isFinite(duration) && duration > 0) {\n          // For regular videos, provide progress percentage\n          timeData.progress = (currentTime / duration) * 100;\n        }\n        \n        onTimeUpdate(timeData);\n      };\n      \n      timeUpdateHandlerRef.current = handleTimeUpdate;\n      video.addEventListener('timeupdate', handleTimeUpdate);\n    }\n  }, [videoRef, playerRef, handlers]);\n\n  const cleanupEventListeners = useCallback(() => {\n    const video = videoRef.current;\n    if (!video) return;\n\n    const { onPlay, onPause, onEnded, onLoadStart, onCanPlay } = handlers;\n\n    if (onPlay) video.removeEventListener('play', onPlay);\n    if (onPause) video.removeEventListener('pause', onPause);\n    if (onEnded) video.removeEventListener('ended', onEnded);\n    if (onLoadStart) video.removeEventListener('loadstart', onLoadStart);\n    if (onCanPlay) video.removeEventListener('canplay', onCanPlay);\n    \n    if (timeUpdateHandlerRef.current) {\n      video.removeEventListener('timeupdate', timeUpdateHandlerRef.current);\n      timeUpdateHandlerRef.current = null;\n    }\n  }, [videoRef, handlers]);\n\n  return {\n    setupEventListeners,\n    cleanupEventListeners\n  };\n}; ","import { useCallback, useRef } from 'react';\n\ninterface AdEventHandlers {\n  onAdStart?: () => void;\n  onAdComplete?: () => void;\n  onAdError?: (error: any) => void;\n  onAdSkipped?: () => void;\n  onAdPaused?: () => void;\n  onAdResumed?: () => void;\n  onAdProgress?: (adProgressData: any) => void;\n  onAllAdsCompleted?: () => void;\n}\n\nexport const useAdEvents = (\n  playerRef: React.RefObject<any>,\n  handlers: AdEventHandlers\n) => {\n  // Store listener references for cleanup\n  const listenersRef = useRef<Array<{ event: string; listener: (event: any) => void }>>([]);\n\n  const setupAdEventListeners = useCallback(() => {\n    const player = playerRef.current;\n    if (!player) return;\n\n    const adManager = player.getAdManager();\n    if (!adManager) return;\n\n    // Get the AdsManager instance to listen to ad events\n    // Note: We need to listen to the LOADED event first to get access to the AdsManager\n    const onAdsManagerLoaded = (event: any) => {\n      const adsManager = event.getAdsManager();\n      \n      // Ad started\n      const onAdStarted = () => {\n        console.log('Ad started');\n        handlers.onAdStart?.();\n      };\n\n      // Ad completed\n      const onAdComplete = () => {\n        console.log('Ad completed');\n        handlers.onAdComplete?.();\n      };\n\n      // Ad error\n      const onAdError = (adErrorEvent: any) => {\n        console.error('Ad error:', adErrorEvent.getError());\n        handlers.onAdError?.(adErrorEvent.getError());\n      };\n\n      // Ad skipped\n      const onAdSkipped = () => {\n        console.log('Ad skipped');\n        handlers.onAdSkipped?.();\n      };\n\n      // Ad paused\n      const onAdPaused = () => {\n        console.log('Ad paused');\n        handlers.onAdPaused?.();\n      };\n\n      // Ad resumed\n      const onAdResumed = () => {\n        console.log('Ad resumed');\n        handlers.onAdResumed?.();\n      };\n\n      // Ad progress\n      const onAdProgress = (adProgressData: any) => {\n        handlers.onAdProgress?.(adProgressData);\n      };\n\n      // All ads completed\n      const onAllAdsCompleted = () => {\n        console.log('All ads completed');\n        handlers.onAllAdsCompleted?.();\n      };\n\n      // Store listeners for cleanup\n      const adEventListeners: Array<{ event: any; listener: any }> = [];\n\n      // Register event listeners if handlers are provided\n      if (window.google?.ima) {\n        const AdEvent = window.google.ima.AdEvent.Type;\n        const AdErrorEvent = window.google.ima.AdErrorEvent.Type;\n\n        if (handlers.onAdStart) {\n          adsManager.addEventListener(AdEvent.STARTED, onAdStarted);\n          adEventListeners.push({ event: AdEvent.STARTED, listener: onAdStarted });\n        }\n\n        if (handlers.onAdComplete) {\n          adsManager.addEventListener(AdEvent.COMPLETE, onAdComplete);\n          adEventListeners.push({ event: AdEvent.COMPLETE, listener: onAdComplete });\n        }\n\n        if (handlers.onAdError) {\n          adsManager.addEventListener(AdErrorEvent.AD_ERROR, onAdError);\n          adEventListeners.push({ event: AdErrorEvent.AD_ERROR, listener: onAdError });\n        }\n\n        if (handlers.onAdSkipped) {\n          adsManager.addEventListener(AdEvent.SKIPPED, onAdSkipped);\n          adEventListeners.push({ event: AdEvent.SKIPPED, listener: onAdSkipped });\n        }\n\n        if (handlers.onAdPaused) {\n          adsManager.addEventListener(AdEvent.PAUSED, onAdPaused);\n          adEventListeners.push({ event: AdEvent.PAUSED, listener: onAdPaused });\n        }\n\n        if (handlers.onAdResumed) {\n          adsManager.addEventListener(AdEvent.RESUMED, onAdResumed);\n          adEventListeners.push({ event: AdEvent.RESUMED, listener: onAdResumed });\n        }\n\n        if (handlers.onAdProgress) {\n          adsManager.addEventListener(AdEvent.AD_PROGRESS, onAdProgress);\n          adEventListeners.push({ event: AdEvent.AD_PROGRESS, listener: onAdProgress });\n        }\n\n        if (handlers.onAllAdsCompleted) {\n          adsManager.addEventListener(AdEvent.ALL_ADS_COMPLETED, onAllAdsCompleted);\n          adEventListeners.push({ event: AdEvent.ALL_ADS_COMPLETED, listener: onAllAdsCompleted });\n        }\n\n        // Store the adsManager and its listeners for cleanup\n        listenersRef.current.push({\n          adsManager,\n          listeners: adEventListeners\n        } as any);\n      }\n    };\n\n    // Listen for when ads are loaded\n    try {\n      // Shaka's ad manager uses events for tracking\n      player.addEventListener('ad-started', () => {\n        console.log('Shaka ad-started event');\n        handlers.onAdStart?.();\n      });\n\n      player.addEventListener('ad-complete', () => {\n        console.log('Shaka ad-complete event');\n        handlers.onAdComplete?.();\n      });\n\n      player.addEventListener('ad-skipped', () => {\n        console.log('Shaka ad-skipped event');\n        handlers.onAdSkipped?.();\n      });\n\n      player.addEventListener('ad-error', (event: any) => {\n        console.error('Shaka ad-error event:', event);\n        handlers.onAdError?.(event);\n      });\n\n      // Store Shaka listeners\n      listenersRef.current.push({\n        type: 'shaka',\n        player: player,\n        events: ['ad-started', 'ad-complete', 'ad-skipped', 'ad-error']\n      } as any);\n\n    } catch (error) {\n      console.warn('Error setting up ad event listeners:', error);\n    }\n  }, [playerRef, handlers]);\n\n  const cleanupAdEventListeners = useCallback(() => {\n    try {\n      // Clean up all stored listeners\n      listenersRef.current.forEach((listenerGroup: any) => {\n        if (listenerGroup.type === 'shaka' && listenerGroup.player) {\n          // Clean up Shaka player listeners\n          listenerGroup.events.forEach((eventName: string) => {\n            try {\n              listenerGroup.player.removeEventListener(eventName, () => {});\n            } catch (e) {\n              console.warn(`Error removing ${eventName} listener:`, e);\n            }\n          });\n        } else if (listenerGroup.adsManager && listenerGroup.listeners) {\n          // Clean up IMA AdsManager listeners\n          listenerGroup.listeners.forEach(({ event, listener }: any) => {\n            try {\n              listenerGroup.adsManager.removeEventListener(event, listener);\n            } catch (e) {\n              console.warn('Error removing ad listener:', e);\n            }\n          });\n        }\n      });\n\n      // Clear the listeners array\n      listenersRef.current = [];\n    } catch (error) {\n      console.warn('Error cleaning up ad event listeners:', error);\n    }\n  }, []);\n\n  return {\n    setupAdEventListeners,\n    cleanupAdEventListeners\n  };\n};\n\n","import { useCallback } from 'react';\nimport shaka from 'shaka-player/dist/shaka-player.ui';\n\ninterface System73Config {\n  apiKey?: string;\n  contentSteeringEndpoint?: string;\n  channelId?: string;\n}\n\n/**\n * Integration hook for System73 CDN optimization.\n * \n * System73 provides intelligent CDN switching and content steering to optimize\n * video delivery based on real-time network conditions and CDN performance.\n * \n * CRITICAL INTEGRATION REQUIREMENT:\n * --------------------------------\n * The System73 SDK MUST wrap the Shaka Player BEFORE the manifest is loaded.\n * This allows the SDK to intercept manifest parsing and detect all available\n * ABR (adaptive bitrate) profiles. If wrapped after loading, it only sees the\n * single bitrate Shaka initially selected, causing streams to report incorrectly\n * as mono-bitrate.\n * \n * CORRECT SEQUENCE (handled by usePlayerInitialization):\n * 1. Initialize System73 wrapper: initializeSystem73(playerConfig)\n * 2. Initialize Shaka Player (skips auto-load): initializePlayer(video)\n * 3. Wrap the player: system73Wrapper.wrapPlayer(playerRef.current)\n * 4. Load manifest: loadManifest()\n * \n * CONFIGURATION:\n * -------------\n * - apiKey: System73 API key for authentication\n * - contentSteeringEndpoint: Optional custom endpoint for content steering\n * - channelId: Optional identifier for analytics and monitoring\n * \n * The SDK is loaded via CDN in useScriptLoader and attached to window.S73ShakaPlayerWrapper\n */\nexport const useSystem73 = (system73Config?: System73Config) => {\n  const initializeSystem73 = useCallback((playerConfig: any) => {\n    if (!system73Config?.apiKey || !window.S73ShakaPlayerWrapper) {\n      return null;\n    }\n\n    try {\n      const s73Config = {\n        apiKey: system73Config.apiKey,\n        contentSteeringEndpoint: system73Config.contentSteeringEndpoint,\n        channelId: system73Config.channelId\n      };\n\n      const wrapper = window.S73ShakaPlayerWrapper(s73Config, { shaka });\n      wrapper.wrapPlayerConfig(playerConfig);\n\n      return wrapper;\n    } catch (error) {\n      console.error('System73 SDK initialization error:', error);\n      return null;\n    }\n  }, [system73Config]);\n\n  return { initializeSystem73 };\n};\n","import { useCallback, RefObject } from 'react';\n\ninterface ImaConfig {\n  adTagUrl?: string;\n  adsRenderingSettings?: any;\n}\n\ninterface UseAdsInitializationProps {\n  imaConfig?: ImaConfig;\n  autoPlay: boolean;\n  playerRef: RefObject<any>;\n  videoRef: RefObject<HTMLVideoElement>;\n  uiRef: RefObject<any>;\n  setupAdEventListeners: () => void;\n  loadManifest: () => Promise<void>;\n}\n\n/**\n * Hook for Google IMA SDK ads initialization with Shaka Player.\n * \n * CRITICAL TIMING REQUIREMENT:\n * This hook must be called LAST in the initialization sequence (after player, System73, and UI).\n * The manifest must NOT be loaded before initializeAds() is called, otherwise IMA cannot\n * intercept playback for pre-roll ads.\n * \n * INITIALIZATION FLOW:\n * 1. Shaka Player is created (with hasAds=true, skipping auto-load)\n * 2. System73 wraps player (if configured)\n * 3. Shaka UI is initialized\n * 4. This initializeAds() is called:\n *    a. Sets up IMA SDK with ad container from Shaka UI\n *    b. Requests ads from VAST tag (async)\n *    c. Waits for VAST to load\n *    d. Loads manifest (IMA intercepts to play pre-roll)\n * \n * See ARCHITECTURE.md for complete initialization sequence details.\n */\nexport const useAdsInitialization = ({\n  imaConfig,\n  autoPlay,\n  playerRef,\n  videoRef,\n  uiRef,\n  setupAdEventListeners,\n  loadManifest\n}: UseAdsInitializationProps) => {\n  const initializeAds = useCallback(async () => {\n    if (!imaConfig?.adTagUrl || !playerRef.current || !videoRef.current || !uiRef.current) {\n      return;\n    }\n\n    if (!window.google?.ima) {\n      console.error('Google IMA SDK not available when trying to initialize ads');\n      return;\n    }\n\n    try {\n      const player = playerRef.current;\n      const video = videoRef.current;\n      const ui = uiRef.current;\n\n      // Get the proper ad container from Shaka UI controls\n      const controls = ui.getControls();\n      const container = controls.getClientSideAdContainer();\n\n      const adManager = player.getAdManager();\n      if (!adManager) {\n        console.error('Ad manager not available');\n        return;\n      }\n\n      const google = window.google;\n\n      // Configure ads rendering settings\n      // When autoplay is enabled, we need to restore playback state after ad breaks\n      // so the content resumes automatically after the ad completes\n      let adsRenderingSettings = null;\n      if (imaConfig.adsRenderingSettings) {\n        adsRenderingSettings = imaConfig.adsRenderingSettings;\n      } else if (autoPlay) {\n        adsRenderingSettings = new (google.ima as any).AdsRenderingSettings();\n        adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;\n      }\n\n      // STEP 1: Initialize IMA SDK with Shaka's ad container and video element\n      // Must be called before requestClientSideAds() to set up the rendering environment\n      adManager.initClientSide(container, video, adsRenderingSettings);\n\n      // STEP 2: Request ads from the VAST tag URL (async operation)\n      // This initiates the VAST XML fetch but doesn't block\n      const adsRequest = new google.ima.AdsRequest();\n      adsRequest.adTagUrl = imaConfig.adTagUrl;\n      adManager.requestClientSideAds(adsRequest);\n\n      // STEP 3: Setup event listeners to track ad lifecycle\n      setupAdEventListeners();\n\n      // STEP 4: Wait for VAST XML to load\n      // Without this delay, the manifest might load before VAST is ready,\n      // preventing IMA from intercepting playback for pre-roll ads\n      await new Promise(resolve => setTimeout(resolve, 1000));\n\n      // STEP 5: Load the manifest (finally!)\n      // At this point, IMA is ready and will intercept playback to show pre-roll ads\n      // This is why useShakaPlayer skips auto-load when hasAds=true\n      await loadManifest();\n    } catch (error) {\n      console.error('Error initializing ads:', error);\n    }\n  }, [imaConfig, autoPlay, playerRef, videoRef, uiRef, setupAdEventListeners, loadManifest]);\n\n  return { initializeAds };\n};\n","import { useCallback } from 'react';\n\n/**\n * Helpers around the `player.mux` API that initShakaPlayerMux attaches to the\n * player instance.\n *\n * Mux initialization itself lives in useShakaPlayer, which hands the SDK a\n * token-scrubbing Proxy of the player (see createMuxScrubbingPlayer) so signed\n * CDN URLs never reach analytics. Do NOT initialize Mux from anywhere else:\n * calling initShakaPlayerMux with the raw player would leak signed tokens in\n * video_source_url / request_url. Load-error reporting (loadErrorHandler) also\n * lives in useShakaPlayer, next to the monitor instance it needs.\n */\nexport const useMuxAnalytics = (\n  playerRef: React.RefObject<any>,\n  onMuxDataUpdate?: (data: any) => void,\n) => {\n  // Update Mux view metadata mid-view (e.g. attach viewer_user_id / custom_1\n  // after an analytics-consent grant). Applies to subsequent beacons of the\n  // current view; to clear a field, pass \"\" rather than undefined.\n  const updateMuxData = useCallback((data: any) => {\n    if ((playerRef.current as any)?.mux?.updateData) {\n      try {\n        (playerRef.current as any).mux.updateData(data);\n        onMuxDataUpdate?.(data);\n      } catch (error) {\n        console.error('Failed to update Mux data:', error);\n      }\n    }\n  }, [onMuxDataUpdate, playerRef]);\n\n  const destroyMux = useCallback(() => {\n    // Clean up Mux monitoring\n    if ((playerRef.current as any)?.mux?.destroy) {\n      try {\n        (playerRef.current as any).mux.destroy();\n      } catch (error) {\n        console.error('Error destroying Mux:', error);\n      }\n    }\n  }, [playerRef]);\n\n  return {\n    updateMuxData,\n    destroyMux\n  };\n};\n","import { useCallback, useRef } from 'react';\n\ninterface QualityConfig {\n  enableAdaptation?: boolean;\n  selectedQuality?: number;\n  availableQualities?: Array<{\n    height: number;\n    bandwidth: number;\n    label: string;\n  }>;\n}\n\ninterface Quality {\n  height: number;\n  bandwidth: number;\n  label: string;\n}\n\nexport const useQualityControl = (\n  playerRef: React.RefObject<any>,\n  qualityConfig?: QualityConfig,\n  onQualityChange?: (quality: { height: number; bandwidth: number }) => void\n) => {\n  const getAvailableQualities = useCallback((): Quality[] => {\n    if (!playerRef.current) return [];\n    \n    const tracks = playerRef.current.getVariantTracks();\n    const qualities = tracks\n      .filter((track: any, index: any, self: any) => \n        index === self.findIndex((t: any) => t.height === track.height)\n      )\n      .map((track: any) => ({\n        height: track.height || 0,\n        bandwidth: track.bandwidth || 0,\n        label: `${track.height}p`\n      }))\n      .sort((a: any, b: any) => b.height - a.height);\n    \n    return qualities;\n  }, [playerRef]);\n\n  const setQuality = useCallback((height: number) => {\n    if (!playerRef.current) return;\n    \n    if (height === 0) {\n      // Enable adaptive streaming\n      playerRef.current.configure({\n        abr: { enabled: true, \n          switchInterval: 2,     // quicker re-checks\n          clearBufferSwitch: true,\n          safeMarginSwitch: 10   // leave ~10 s in front of the playhead }\n        }\n      })\n    } else {\n      // Disable adaptive streaming and select specific quality\n      playerRef.current.configure({\n        abr: { enabled: false,\n          switchInterval: 2,     // quicker re-checks\n          clearBufferSwitch: true,\n          safeMarginSwitch: 10   // leave ~10 s in front of the playhead }  \n        }\n      });\n      \n      const tracks = playerRef.current.getVariantTracks();\n      const targetTrack = tracks.find((track: any) => track.height === height);\n      if (targetTrack) {\n        playerRef.current.selectVariantTrack(targetTrack, /* clearBuffer= */ true);\n      }\n    }\n  }, [playerRef]);\n\n  const setupQualityTracking = useCallback(() => {\n    if (!playerRef.current || !onQualityChange) return;\n\n    const handleQualityChange = () => {\n      const activeTrack = playerRef.current.getVariantTracks().find((track: any) => track.active);\n      if (activeTrack) {\n        onQualityChange({\n          height: activeTrack.height || 0,\n          bandwidth: activeTrack.bandwidth || 0\n        });\n      }\n    };\n\n    playerRef.current.addEventListener('variantchanged', handleQualityChange);\n    \n    return () => {\n      playerRef.current?.removeEventListener('variantchanged', handleQualityChange);\n    };\n  }, [playerRef, onQualityChange]);\n\n  const configureQuality = useCallback(() => {\n    if (!playerRef.current || !qualityConfig) return;\n\n    if (qualityConfig.enableAdaptation !== undefined) {\n      playerRef.current.configure({\n        abr: { enabled: qualityConfig.enableAdaptation }\n      });\n    }\n\n    if (qualityConfig.selectedQuality !== undefined) {\n      setQuality(qualityConfig.selectedQuality);\n    }\n  }, [playerRef, qualityConfig, setQuality]);\n\n  return {\n    getAvailableQualities,\n    setQuality,\n    setupQualityTracking,\n    configureQuality\n  };\n}; ","import { useCallback } from 'react';\n\nexport const useSkipControls = (\n  videoRef: React.RefObject<HTMLVideoElement>,\n  onSkipBack?: (newTime: number) => void,\n  onSkipForward?: (newTime: number) => void\n) => {\n  // Hardcoded skip duration of 15 seconds\n  const skipDuration = 15;\n\n  const skipBack = useCallback(() => {\n    const video = videoRef.current;\n    if (!video) return;\n    \n    const newTime = Math.max(0, video.currentTime - skipDuration);\n    video.currentTime = newTime;\n    onSkipBack?.(newTime);\n  }, [videoRef, onSkipBack]);\n\n  const skipForward = useCallback(() => {\n    const video = videoRef.current;\n    if (!video) return;\n    \n    const newTime = Math.min(video.duration || 0, video.currentTime + skipDuration);\n    video.currentTime = newTime;\n    onSkipForward?.(newTime);\n  }, [videoRef, onSkipForward]);\n\n  // Always show skip controls\n  const shouldShowSkipControls = useCallback(() => {\n    return true;\n  }, []);\n\n  return {\n    skipBack,\n    skipForward,\n    skipDuration,\n    shouldShowSkipControls\n  };\n}; ","import { useCallback, useEffect } from 'react';\n\ninterface UseKeyboardControlsOptions {\n  skipBack?: () => void;\n  skipForward?: () => void;\n  enabled?: boolean;\n}\n\nexport const useKeyboardControls = (\n  videoRef: React.RefObject<HTMLVideoElement>,\n  options: UseKeyboardControlsOptions = {}\n) => {\n  const { skipBack, skipForward, enabled = true } = options;\n\n  // Detect if we're on desktop (not mobile)\n  const isDesktop = useCallback(() => {\n    return window.innerWidth > 767 && !/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);\n  }, []);\n\n  const handleKeydown = useCallback((event: KeyboardEvent) => {\n    if (!enabled || !isDesktop() || !videoRef.current) return;\n\n    // Don't handle keyboard events if user is typing in an input\n    const activeElement = document.activeElement;\n    if (activeElement && (\n      activeElement.tagName === 'INPUT' ||\n      activeElement.tagName === 'TEXTAREA' ||\n      (activeElement as HTMLElement).isContentEditable\n    )) {\n      return;\n    }\n\n    switch (event.key) {\n      case 'ArrowLeft':\n        event.preventDefault();\n        skipBack?.();\n        break;\n      \n      case 'ArrowRight':\n        event.preventDefault();\n        skipForward?.();\n        break;\n      \n      case ' ':\n      case 'Space':\n        event.preventDefault();\n        if (videoRef.current.paused) {\n          videoRef.current.play();\n        } else {\n          videoRef.current.pause();\n        }\n        break;\n    }\n  }, [enabled, videoRef, skipBack, skipForward, isDesktop]);\n\n  useEffect(() => {\n    if (!enabled || !isDesktop()) return;\n\n    // Add event listener to document to catch keyboard events globally\n    document.addEventListener('keydown', handleKeydown);\n\n    return () => {\n      document.removeEventListener('keydown', handleKeydown);\n    };\n  }, [handleKeydown, enabled, isDesktop]);\n\n  return {\n    isDesktop: isDesktop()\n  };\n}; ","import { useEffect, useRef } from 'react';\n\ninterface UseLiveIndicatorOptions {\n  enabled?: boolean;\n  indicatorColor?: string;\n  indicatorSize?: number;\n  showPulseAnimation?: boolean;\n  liveThresholdSeconds?: number;\n  onLiveStatusChange?: (status: LiveStatus) => void;\n}\n\nexport interface LiveStatus {\n  isLive: boolean;\n  isNearEdge: boolean;\n  liveEdge?: number;\n}\n\nexport const useLiveIndicator = (\n  containerRef: React.RefObject<HTMLElement>,\n  playerRef: React.RefObject<any>,\n  options: UseLiveIndicatorOptions = {}\n) => {\n  const observerRef = useRef<MutationObserver | null>(null);\n  const intervalRef = useRef<NodeJS.Timeout | null>(null);\n  const lastStatusRef = useRef<{ isLive: boolean; isNearEdge: boolean } | null>(null);\n  const {\n    enabled = true,\n    indicatorColor = '#ff0000',\n    indicatorSize = 8,\n    showPulseAnimation = true,\n    liveThresholdSeconds = 15,\n    onLiveStatusChange\n  } = options;\n\n  const getLiveStatus = (player: any): LiveStatus => {\n    if (!player) {\n      return { isLive: false, isNearEdge: false };\n    }\n\n    try {\n      // Use stable public API to check if stream is live\n      if (typeof player.isLive !== 'function') {\n        return { isLive: false, isNearEdge: false };\n      }\n\n      const isLiveStream = player.isLive();\n      if (!isLiveStream) return { isLive: false, isNearEdge: false };\n\n      // Get the seek range using stable public API\n      const videoElement = player.getMediaElement?.();\n      if (!videoElement) return { isLive: true, isNearEdge: false };\n\n      // Use stable public API to get seek range\n      if (typeof player.seekRange !== 'function') {\n        return { isLive: true, isNearEdge: false };\n      }\n\n      const seekRange = player.seekRange();\n      if (!seekRange || seekRange.end === undefined) {\n        return { isLive: true, isNearEdge: false };\n      }\n\n      const liveEdge = seekRange.end;\n      const currentTime = videoElement.currentTime;\n      const timeBehindLive = liveEdge - currentTime;\n\n      // Consider \"near live edge\" if within the threshold\n      return {\n        isLive: true,\n        isNearEdge: timeBehindLive <= liveThresholdSeconds,\n        liveEdge\n      };\n    } catch (error) {\n      console.error('Error checking live status:', error);\n      return { isLive: false, isNearEdge: false };\n    }\n  };\n\n  const seekToLiveEdge = () => {\n    const player = playerRef.current;\n    if (!player) return;\n\n    try {\n      // Use stable public API to get seek range\n      if (typeof player.seekRange !== 'function') return;\n\n      const seekRange = player.seekRange();\n      if (!seekRange || seekRange.end === undefined) return;\n\n      const liveEdge = seekRange.end;\n      const videoElement = player.getMediaElement?.();\n      if (videoElement) {\n        videoElement.currentTime = liveEdge;\n      }\n    } catch (error) {\n      console.error('Error seeking to live edge:', error);\n    }\n  };\n\n  useEffect(() => {\n    if (!containerRef.current || !enabled) {\n      return;\n    }\n\n    const updateLiveIndicator = (currentTimeElement: Element, status: LiveStatus) => {\n      const parent = currentTimeElement.parentElement;\n      if (!parent) return;\n\n      const existingContainer = parent.querySelector('.live-indicator-container');\n      const isCurrentlyNearEdge = existingContainer?.getAttribute('data-near-edge') === 'true';\n\n      // If status matches current state, no update needed\n      if (existingContainer && status.isNearEdge === isCurrentlyNearEdge) {\n        return;\n      }\n\n      // Remove existing container if present\n      if (existingContainer) {\n        existingContainer.remove();\n      }\n\n      // Create a container for indicator + text\n      const container = document.createElement('span');\n      container.className = 'live-indicator-container';\n      container.setAttribute('data-near-edge', status.isNearEdge.toString());\n      container.style.cssText = `\n        display: inline-flex;\n        align-items: center;\n        width: 80px;\n        ${!status.isNearEdge ? 'cursor: pointer;' : ''}\n      `;\n\n      // Create the circle indicator\n      const indicator = document.createElement('span');\n      indicator.className = 'live-indicator-dot';\n      indicator.style.cssText = `\n        display: inline-block;\n        width: ${indicatorSize}px;\n        height: ${indicatorSize}px;\n        background-color: ${status.isNearEdge ? indicatorColor : '#888888'};\n        border-radius: 50%;\n        margin-right: 6px;\n        ${status.isNearEdge && showPulseAnimation ? 'animation: pulse-live 2s infinite;' : ''}\n      `;\n\n      // Create text\n      const liveText = document.createElement('span');\n      liveText.className = 'live-indicator-text';\n      liveText.textContent = status.isNearEdge ? 'LIVE' : 'GO TO LIVE';\n\n      container.appendChild(indicator);\n      container.appendChild(liveText);\n\n      // Add click handler for \"GO TO LIVE\"\n      if (!status.isNearEdge) {\n        container.addEventListener('click', seekToLiveEdge);\n      }\n\n      // Insert container as sibling - before time when LIVE, after time when GO TO LIVE\n      if (status.isNearEdge) {\n        parent.insertBefore(container, currentTimeElement);\n        (currentTimeElement as HTMLElement).style.display = 'none';\n      } else {\n        // Insert after the time element\n        currentTimeElement.insertAdjacentElement('afterend', container);\n        (currentTimeElement as HTMLElement).style.display = 'none';\n      }\n    };\n\n    const removeLiveIndicator = (currentTimeElement: Element) => {\n      const parent = currentTimeElement.parentElement;\n      if (!parent) return;\n\n      const container = parent.querySelector('.live-indicator-container');\n      if (container) {\n        container.remove();\n        (currentTimeElement as HTMLElement).style.display = '';\n      }\n    };\n\n    const checkForLiveContent = () => {\n      const currentTimeElements = containerRef.current?.querySelectorAll('.shaka-current-time');\n      const status = getLiveStatus(playerRef.current);\n\n      // Call callback if status changed\n      const lastStatus = lastStatusRef.current;\n      if (\n        onLiveStatusChange &&\n        (!lastStatus || lastStatus.isLive !== status.isLive || lastStatus.isNearEdge !== status.isNearEdge)\n      ) {\n        onLiveStatusChange(status);\n        lastStatusRef.current = { isLive: status.isLive, isNearEdge: status.isNearEdge };\n      }\n\n      currentTimeElements?.forEach((element) => {\n        if (status.isLive) {\n          updateLiveIndicator(element, status);\n        } else {\n          removeLiveIndicator(element);\n        }\n      });\n    };\n\n    // Initial check\n    checkForLiveContent();\n\n    // Set up interval to check periodically (since we need to check live edge position)\n    intervalRef.current = setInterval(checkForLiveContent, 1000);\n\n    // Set up mutation observer to watch for changes in the DOM\n    observerRef.current = new MutationObserver((mutations) => {\n      let shouldCheck = false;\n      \n      mutations.forEach((mutation) => {\n        // Check if any nodes were added or text content changed\n        if (mutation.type === 'childList' || mutation.type === 'characterData') {\n          shouldCheck = true;\n        }\n        \n        // Check if any of the added nodes contain current time elements\n        mutation.addedNodes.forEach((node) => {\n          if (node.nodeType === Node.ELEMENT_NODE) {\n            const element = node as Element;\n            if (element.classList?.contains('shaka-current-time') || \n                element.querySelector?.('.shaka-current-time')) {\n              shouldCheck = true;\n            }\n          }\n        });\n      });\n      \n      if (shouldCheck) {\n        // Debounce the check to avoid excessive calls\n        setTimeout(checkForLiveContent, 100);\n      }\n    });\n\n    // Start observing\n    observerRef.current.observe(containerRef.current, {\n      childList: true,\n      subtree: true,\n      characterData: true,\n      characterDataOldValue: true\n    });\n\n    return () => {\n      if (observerRef.current) {\n        observerRef.current.disconnect();\n      }\n      if (intervalRef.current) {\n        clearInterval(intervalRef.current);\n      }\n    };\n  }, [containerRef, enabled, indicatorColor, indicatorSize, showPulseAnimation, liveThresholdSeconds]);\n\n  return {\n    seekToLiveEdge,\n    getLiveStatus: () => getLiveStatus(playerRef.current)\n  };\n}; ","import { useCallback, useRef } from 'react';\nimport { ui as ShakaUI } from 'shaka-player/dist/shaka-player.ui';\nimport { SkipBackIcon, SkipForwardIcon, BigPlayIcon } from '../icons';\nimport { renderIcon } from '../utils/renderIcon';\n\ninterface ChromecastConfig {\n  receiverApplicationId?: string;\n}\n\ninterface IconSizes {\n  skipButtons?: number;\n  bigPlayButton?: number;\n}\n\n// Custom Skip Back Button Element\nclass SkipBackButton {\n  private button_: HTMLButtonElement;\n  private parent: HTMLElement;\n  private controls: any;\n  private eventManager: any;\n\n  constructor(parent: HTMLElement, controls: any, onSkipBack?: (newTime: number) => void, iconSize = 24) {\n    this.parent = parent;\n    this.controls = controls;\n    this.eventManager = { listen: (element: HTMLElement, event: string, handler: () => void) => {\n      element.addEventListener(event, handler);\n    }};\n\n    this.button_ = document.createElement('button');\n    this.button_.className = 'shaka-button motto-native-skip-button';\n    this.button_.innerHTML = renderIcon(SkipBackIcon, { size: iconSize });\n    this.button_.title = 'Skip back 15 seconds';\n    this.button_.setAttribute('aria-label', 'Skip back 15 seconds');\n    \n    this.parent.appendChild(this.button_);\n\n    this.eventManager.listen(this.button_, 'click', () => {\n      const video = this.controls.getVideo();\n      if (video) {\n        const newTime = Math.max(0, video.currentTime - 15);\n        video.currentTime = newTime;\n        onSkipBack?.(newTime);\n      }\n    });\n  }\n\n  // Shaka UI will call `release` when the controls are destroyed.\n  // Provide a no-op implementation to avoid TypeErrors.\n  public release() {\n    /* Optionally detach listeners / clean up DOM here */\n  }\n}\n\n// Custom Skip Forward Button Element  \nclass SkipForwardButton {\n  private button_: HTMLButtonElement;\n  private parent: HTMLElement;\n  private controls: any;\n  private eventManager: any;\n\n  constructor(parent: HTMLElement, controls: any, onSkipForward?: (newTime: number) => void, iconSize = 24) {\n    this.parent = parent;\n    this.controls = controls;\n    this.eventManager = { listen: (element: HTMLElement, event: string, handler: () => void) => {\n      element.addEventListener(event, handler);\n    }};\n\n    this.button_ = document.createElement('button');\n    this.button_.className = 'shaka-button motto-native-skip-button';\n    this.button_.innerHTML = renderIcon(SkipForwardIcon, { size: iconSize });\n    this.button_.title = 'Skip forward 15 seconds';\n    this.button_.setAttribute('aria-label', 'Skip forward 15 seconds');\n    \n    this.parent.appendChild(this.button_);\n\n    this.eventManager.listen(this.button_, 'click', () => {\n      const video = this.controls.getVideo();\n      if (video) {\n        const newTime = Math.min(video.duration || 0, video.currentTime + 15);\n        video.currentTime = newTime;\n        onSkipForward?.(newTime);\n      }\n    });\n  }\n\n  public release() {\n    /* Clean-up logic if needed */\n  }\n}\n\n// Factory classes for the custom elements\nclass SkipBackButtonFactory {\n  constructor(private onSkipBack?: (newTime: number) => void, private iconSize?: number) {}\n  \n  create(rootElement: HTMLElement, controls: any) {\n    return new SkipBackButton(rootElement, controls, this.onSkipBack, this.iconSize);\n  }\n}\n\nclass SkipForwardButtonFactory {\n  constructor(private onSkipForward?: (newTime: number) => void, private iconSize?: number) {}\n  \n  create(rootElement: HTMLElement, controls: any) {\n    return new SkipForwardButton(rootElement, controls, this.onSkipForward, this.iconSize);\n  }\n}\n\ninterface SeekbarColors {\n  base?: string;\n  buffered?: string;\n  played?: string;\n}\n\nexport const useShakaUI = (\n  playerRef: React.RefObject<any>,\n  containerRef: React.RefObject<HTMLDivElement>,\n  videoRef: React.RefObject<HTMLVideoElement>,\n  controls: boolean,\n  chromecastConfig?: ChromecastConfig,\n  seekbarColors?: SeekbarColors,\n  onSkipBack?: (newTime: number) => void,\n  onSkipForward?: (newTime: number) => void,\n  iconSizes?: IconSizes,\n  locale: string = 'en'\n) => {\n  const uiRef = useRef<any>(null);\n  const registeredElements = useRef<Set<string>>(new Set());\n\n  const initializeUI = useCallback(async () => {\n    if (!controls || !containerRef.current || !playerRef.current || !videoRef.current) {\n      return null;\n    }\n\n    // Register custom elements if not already registered\n    if (!registeredElements.current.has('skip_back_button')) {\n      ShakaUI.Controls.registerElement('skip_back_button', new SkipBackButtonFactory(onSkipBack, iconSizes?.skipButtons));\n      registeredElements.current.add('skip_back_button');\n    }\n    \n    if (!registeredElements.current.has('skip_forward_button')) {\n      ShakaUI.Controls.registerElement('skip_forward_button', new SkipForwardButtonFactory(onSkipForward, iconSizes?.skipButtons));\n      registeredElements.current.add('skip_forward_button');\n    }\n\n    // Create UI with Shaka Player controls using default configuration\n    const ui = new ShakaUI.Overlay(playerRef.current, containerRef.current, videoRef.current);\n    uiRef.current = ui;\n    \n    // Configure localization if locale is not 'en'\n    if (locale !== 'en') {\n      try {\n        ui.getControls().getLocalization().changeLocale([locale])\n        console.log(`Set locale to '${locale}'`);\n\n      } catch (error) {\n        console.warn(`Failed to set locale to '${locale}', falling back to 'en':`, error);\n      }\n    }\n\n    // Detect mobile to conditionally include volume control\n    const isMobile = window.innerWidth <= 767;\n    \n    const controlPanelElements = [\n      ...(isMobile ? [] : ['skip_back_button']), \n      ...(isMobile ? [] : ['play_pause']), \n      ...(isMobile ? [] : ['skip_forward_button']), \n      'mute',\n      ...(isMobile ? [] : ['volume']), // Only include volume on desktop\n      'time_and_duration',\n      'spacer',\n      'fullscreen',\n      'cast', // Always show cast button\n      'overflow_menu',\n    ];\n\n    // Configure UI settings with custom control panel layout\n    const uiConfig: any = {\n      seekBarColors: {\n        base: seekbarColors?.base || 'rgba(255, 255, 255, 0.3)',     // Unbuffered track\n        buffered: seekbarColors?.buffered || 'rgba(255, 255, 255, 0.5)', // Buffered but not played\n        played: seekbarColors?.played || '#ffffff',                     // Progress/played portion (white)\n      },\n      controlPanelElements: controlPanelElements,\n      addBigPlayButton: isMobile,\n      // For now, Chromecast via Shaka must use the Motto receiver that runs on the legacy cast SDK (2.0), because\n      // Shaka does not support the newer Cast SDK (3.0+) in its native integration.\n      castReceiverAppId: 'EC900D4D',\n      castAndroidReceiverCompatible: true, // Enable Android TV compatibility\n      overflowMenuButtons: [\n        'quality',\n        'captions',\n        'picture_in_picture',\n        'playback_rate'\n      ]\n    };\n\n    ui.configure(uiConfig);\n\n    // Custom SVG replacement for big play button on mobile\n    if (isMobile) {\n      const customizeBigPlayButton = () => {\n        const bigPlayButton = containerRef.current?.querySelector('.shaka-big-play-button');\n        if (bigPlayButton && !bigPlayButton.hasAttribute('data-customized')) {\n          // Use custom size if provided, otherwise use default\n          const buttonSize = iconSizes?.bigPlayButton || 40;\n          \n          // Clear existing content and add custom SVG\n          bigPlayButton.innerHTML = renderIcon(BigPlayIcon, { size: buttonSize });\n          \n          // Mark as customized to avoid re-processing\n          bigPlayButton.setAttribute('data-customized', 'true');\n          \n          // Ensure the button is properly styled\n          const buttonElement = bigPlayButton as HTMLElement;\n          buttonElement.style.display = 'flex';\n          buttonElement.style.alignItems = 'center';\n          buttonElement.style.justifyContent = 'center';\n        }\n      };\n\n      // Apply customization\n      setTimeout(customizeBigPlayButton, 100);\n\n      // Watch for changes and reapply customization\n      const observer = new MutationObserver(() => {\n        customizeBigPlayButton();\n      });\n\n      if (containerRef.current) {\n        observer.observe(containerRef.current, {\n          childList: true,\n          subtree: true,\n          attributes: false\n        });\n      }\n    }\n\n    // Seekbar styling is now handled by CSS with proper background\n\n    return ui;\n  }, [controls, containerRef, playerRef, videoRef, chromecastConfig, seekbarColors, onSkipBack, onSkipForward, iconSizes, locale]);\n\n  const destroyUI = useCallback(async () => {\n    const uiInstance = uiRef.current;\n    if (uiInstance) {\n      try {\n        await uiInstance.destroy();\n      } catch (error) {\n        console.error('Error destroying UI:', error);\n      } finally {\n        if (uiRef.current === uiInstance) {\n          uiRef.current = null;\n        }\n      }\n    }\n  }, []);\n\n  return {\n    uiRef,\n    initializeUI,\n    destroyUI\n  };\n}; ","import React from 'react';\n\ninterface SkipBackIconProps {\n  size?: number;\n  className?: string;\n}\n\nexport const SkipBackIcon: React.FC<SkipBackIconProps> = ({ size = 24, className = '' }) => {\n  return (\n    <svg \n      width={size} \n      height={size} \n      strokeWidth=\"2\" \n      viewBox=\"0 0 24 24\" \n      fill=\"none\" \n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path \n        d=\"M3 13C3 17.9706 7.02944 22 12 22C16.9706 22 21 17.9706 21 13C21 8.02944 16.9706 4 12 4\" \n        stroke=\"currentColor\" \n        strokeWidth=\"2\" \n        strokeLinecap=\"round\" \n        strokeLinejoin=\"round\"\n      />\n      <path \n        d=\"M9 9L9 16\" \n        stroke=\"currentColor\" \n        strokeWidth=\"2\" \n        strokeLinecap=\"round\" \n        strokeLinejoin=\"round\"\n      />\n      <path \n        d=\"M15 9L13 9C12.4477 9 12 9.44772 12 10L12 11.5C12 12.0523 12.4477 12.5 13 12.5L14 12.5C14.5523 12.5 15 12.9477 15 13.5L15 15C15 15.5523 14.5523 16 14 16L12 16\" \n        stroke=\"currentColor\" \n        strokeWidth=\"2\" \n        strokeLinecap=\"round\" \n        strokeLinejoin=\"round\"\n      />\n      <path \n        d=\"M12 4L4.5 4M4.5 4L6.5 2M4.5 4L6.5 6\" \n        stroke=\"currentColor\" \n        strokeWidth=\"2\" \n        strokeLinecap=\"round\" \n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}; ","import React from 'react';\n\ninterface SkipForwardIconProps {\n  size?: number;\n  className?: string;\n}\n\nexport const SkipForwardIcon: React.FC<SkipForwardIconProps> = ({ size = 24, className = '' }) => {\n  return (\n    <svg \n      width={size} \n      height={size} \n      strokeWidth=\"2\" \n      viewBox=\"0 0 24 24\" \n      fill=\"none\" \n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path \n        d=\"M21 13C21 17.9706 16.9706 22 12 22C7.02944 22 3 17.9706 3 13C3 8.02944 7.02944 4 12 4\" \n        stroke=\"currentColor\" \n        strokeLinecap=\"round\" \n        strokeLinejoin=\"round\"\n      />\n      <path \n        d=\"M12 4H19.5M19.5 4L17.5 2M19.5 4L17.5 6\" \n        stroke=\"currentColor\" \n        strokeLinecap=\"round\" \n        strokeLinejoin=\"round\"\n      />\n      <path \n        d=\"M9 9L9 16\" \n        stroke=\"currentColor\" \n        strokeLinecap=\"round\" \n        strokeLinejoin=\"round\"\n      />\n      <path \n        d=\"M15 9L13 9C12.4477 9 12 9.44772 12 10L12 11.5C12 12.0523 12.4477 12.5 13 12.5L14 12.5C14.5523 12.5 15 12.9477 15 13.5L15 15C15 15.5523 14.5523 16 14 16L12 16\" \n        stroke=\"currentColor\" \n        strokeLinecap=\"round\" \n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}; ","import React from 'react';\n\ninterface BigPlayIconProps {\n  size?: number;\n  className?: string;\n}\n\nexport const BigPlayIcon: React.FC<BigPlayIconProps> = ({ size = 40, className = '' }) => {\n  return (\n    <svg \n      width={size} \n      height={size} \n      viewBox=\"0 0 24 24\" \n      fill=\"currentColor\" \n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path \n        fillRule=\"evenodd\" \n        d=\"M4.5 5.653c0-1.427 1.529-2.33 2.779-1.643l11.54 6.347c1.295.712 1.295 2.573 0 3.286L7.28 19.99c-1.25.687-2.779-.217-2.779-1.643V5.653Z\" \n        clipRule=\"evenodd\" \n      />\n    </svg>\n  );\n}; ","import { renderToStaticMarkup } from 'react-dom/server';\nimport { createElement } from 'react';\n\n/**\n * Renders a React component to an HTML string for use in Shaka UI buttons\n */\nexport const renderIcon = (Component: React.ComponentType<any>, props: any = {}) => {\n  return renderToStaticMarkup(createElement(Component, props));\n}; ","import { useState, useEffect } from 'react';\nimport { getRequiredScriptLoaders, loadScripts, waitForGlobals } from '../utils/scriptLoader';\n\ninterface UseScriptLoaderOptions {\n  hasAds: boolean;\n  hasSystem73: boolean;\n}\n\n/**\n * Hook to load external scripts required by the player (IMA SDK, System73).\n * Returns true when all required scripts are loaded and their globals are available.\n */\nexport const useScriptLoader = ({ hasAds, hasSystem73 }: UseScriptLoaderOptions): boolean => {\n  const [isLoaded, setIsLoaded] = useState(false);\n\n  useEffect(() => {\n    const loadRequiredScripts = async () => {\n      try {\n        const scriptLoaders = getRequiredScriptLoaders(hasAds, hasSystem73);\n        await loadScripts(scriptLoaders);\n\n        const globalsToWait: string[] = [];\n        if (hasAds) {\n          globalsToWait.push('google');\n        }\n        if (hasSystem73) {\n          globalsToWait.push('S73ShakaPlayerWrapper');\n        }\n\n        if (globalsToWait.length > 0) {\n          await waitForGlobals(globalsToWait);\n        }\n\n        setIsLoaded(true);\n      } catch (error) {\n        console.error('Error loading required scripts:', error);\n        // Set as loaded anyway to prevent blocking\n        setIsLoaded(true);\n      }\n    };\n\n    loadRequiredScripts();\n  }, [hasAds, hasSystem73]);\n\n  return isLoaded;\n};\n","/**\n * Script loader utility for dynamically loading external scripts\n */\n\ninterface ScriptConfig {\n    src: string;\n    id: string;\n    type?: string;\n  }\n  \n  const SCRIPT_CONFIGS: Record<string, ScriptConfig> = {\n    ima: {\n      src: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js',\n      id: 'ima-sdk',\n      type: 'text/javascript'\n    },\n    system73: {\n      src: '//cdn.s73cloud.com/4/s73-sdk-shakaplayer.js',\n      id: 'system73-sdk',\n      type: 'application/javascript'\n    }\n  };\n  \n  /**\n   * Load a script dynamically\n   */\n  function loadScript(config: ScriptConfig): Promise<void> {\n    return new Promise((resolve, reject) => {\n      // Check if script is already loaded\n      if (document.getElementById(config.id)) {\n        resolve();\n        return;\n      }\n  \n      const script = document.createElement('script');\n      script.id = config.id;\n      script.src = config.src;\n      script.type = config.type || 'text/javascript';\n      script.async = true;\n  \n      script.onload = () => resolve();\n      script.onerror = () => reject(new Error(`Failed to load script: ${config.src}`));\n  \n      document.head.appendChild(script);\n    });\n  }\n  \n  /**\n   * Get required script loaders based on configuration\n   */\n  export function getRequiredScriptLoaders(needsIma: boolean, needsSystem73: boolean): Promise<void>[] {\n    const loaders: Promise<void>[] = [];\n  \n    if (needsIma) {\n      loaders.push(loadScript(SCRIPT_CONFIGS.ima));\n    }\n  \n    if (needsSystem73) {\n      loaders.push(loadScript(SCRIPT_CONFIGS.system73));\n    }\n  \n    return loaders;\n  }\n  \n  /**\n   * Load multiple scripts in parallel\n   */\n  export async function loadScripts(scriptLoaders: Promise<void>[]): Promise<void> {\n    if (scriptLoaders.length === 0) {\n      return;\n    }\n  \n    try {\n      await Promise.all(scriptLoaders);\n    } catch (error) {\n      console.error('Error loading scripts:', error);\n      throw error;\n    }\n  }\n  \n  /**\n   * Check if a script is already loaded\n   */\n  export function isScriptLoaded(scriptName: keyof typeof SCRIPT_CONFIGS): boolean {\n    const config = SCRIPT_CONFIGS[scriptName];\n    return !!document.getElementById(config.id);\n  }\n  \n  /**\n   * Wait for a global variable to be available (useful for waiting for SDK initialization)\n   */\n  export function waitForGlobal(globalName: string, timeout: number = 5000): Promise<any> {\n    return new Promise((resolve, reject) => {\n      const startTime = Date.now();\n  \n      function checkGlobal() {\n        const global = (window as any)[globalName];\n        if (global) {\n          resolve(global);\n          return;\n        }\n  \n        if (Date.now() - startTime > timeout) {\n          reject(new Error(`Timeout waiting for global: ${globalName}`));\n          return;\n        }\n  \n        setTimeout(checkGlobal, 100);\n      }\n  \n      checkGlobal();\n    });\n  }\n  \n  /**\n   * Wait for multiple globals to be available\n   */\n  export async function waitForGlobals(globalNames: string[], timeout: number = 5000): Promise<void> {\n    const promises = globalNames.map(name => waitForGlobal(name, timeout));\n    await Promise.all(promises);\n  }  ","import { RefObject, useEffect } from 'react';\n\n/**\n * Hook to aggressively disable native HTML5 video controls.\n * Prevents conflicts with Shaka UI custom controls by using a MutationObserver\n * to immediately remove the controls attribute if it's ever added back.\n */\nexport const useDisableNativeControls = (videoRef: RefObject<HTMLVideoElement>) => {\n  useEffect(() => {\n    const video = videoRef.current;\n    if (!video) return;\n\n    video.controls = false;\n    video.removeAttribute('controls');\n\n    const observer = new MutationObserver((mutations) => {\n      mutations.forEach((mutation) => {\n        if (mutation.type === 'attributes' && mutation.attributeName === 'controls') {\n          if (video.hasAttribute('controls')) {\n            video.removeAttribute('controls');\n            video.controls = false;\n          }\n        }\n      });\n    });\n\n    observer.observe(video, {\n      attributes: true,\n      attributeFilter: ['controls']\n    });\n\n    return () => {\n      observer.disconnect();\n    };\n  }, [videoRef]);\n};\n","import { useEffect, useRef, useCallback } from 'react';\nimport { usePlayerStoreApi } from '../store/PlayerStore';\n\nexport interface PlayerStoreSyncOptions {\n  videoRef: React.RefObject<HTMLVideoElement | null>;\n  playerRef: React.RefObject<any>;\n  getLiveStatus: () => {\n    isLive: boolean;\n    isNearEdge: boolean;\n    liveEdge?: number;\n  };\n  skipForward: () => void;\n  skipBack: () => void;\n  setQuality: (height: number) => void;\n  getAvailableQualities: () => Array<{ height: number; bandwidth: number; label: string }>;\n  seekToLiveEdge: () => void;\n}\n\n/**\n * Hook to sync Player state with the PlayerStore when wrapped in a PlayerProvider.\n *\n * This hook handles:\n * - Registering video element and Shaka player with the store\n * - Registering player methods (skip, quality, live controls)\n * - Syncing playback state (time, play/pause, volume, etc.)\n * - Syncing live stream state\n *\n * @returns Object with store instance and callbacks for additional sync operations\n */\nexport function usePlayerStoreSync({\n  videoRef,\n  playerRef,\n  getLiveStatus,\n  skipForward,\n  skipBack,\n  setQuality,\n  getAvailableQualities,\n  seekToLiveEdge,\n}: PlayerStoreSyncOptions) {\n  const store = usePlayerStoreApi();\n\n  // Track registered player to avoid redundant updates\n  const registeredPlayerRef = useRef<any>(null);\n\n  // Register video element with store\n  useEffect(() => {\n    if (!store) return;\n\n    const video = videoRef.current;\n    if (video) {\n      store.getState()._registerVideoElement(video);\n    }\n\n    return () => {\n      store.getState()._registerVideoElement(null);\n    };\n  }, [store, videoRef]);\n\n  // Register Shaka player with store\n  useEffect(() => {\n    if (!store) return;\n\n    const player = playerRef.current;\n    // Only register if player changed\n    if (player !== registeredPlayerRef.current) {\n      registeredPlayerRef.current = player;\n      store.getState()._registerShakaPlayer(player);\n    }\n\n    return () => {\n      store.getState()._registerShakaPlayer(null);\n      registeredPlayerRef.current = null;\n    };\n  }, [store, playerRef]);\n\n  // Wrapper for getLiveStatus that matches store's expected type\n  const getLiveStatusForStore = useCallback(() => {\n    const status = getLiveStatus();\n    return {\n      isLive: status.isLive,\n      isNearEdge: status.isNearEdge,\n      liveEdge: status.liveEdge,\n    };\n  }, [getLiveStatus]);\n\n  // Register methods with store\n  useEffect(() => {\n    if (!store) return;\n\n    store.getState()._registerMethods({\n      skipForward,\n      skipBack,\n      setQuality,\n      getAvailableQualities,\n      seekToLiveEdge,\n      getLiveStatus: getLiveStatusForStore,\n    });\n  }, [store, skipForward, skipBack, setQuality, getAvailableQualities, seekToLiveEdge, getLiveStatusForStore]);\n\n  // Sync video playback state to store\n  useEffect(() => {\n    const video = videoRef.current;\n    if (!video || !store) return;\n\n    const updatePlaybackState = () => {\n      store.getState()._updateState({\n        currentTime: video.currentTime,\n        duration: video.duration || 0,\n        isPlaying: !video.paused && !video.ended,\n        isPaused: video.paused,\n        isEnded: video.ended,\n        isMuted: video.muted,\n        volume: video.volume,\n        isLoading: video.readyState < 3, // HAVE_FUTURE_DATA\n      });\n    };\n\n    const onPlay = () => {\n      store.getState()._updateState({ isPlaying: true, isPaused: false, isEnded: false });\n    };\n\n    const onPause = () => {\n      store.getState()._updateState({ isPlaying: false, isPaused: true });\n    };\n\n    const onEnded = () => {\n      store.getState()._updateState({ isPlaying: false, isPaused: true, isEnded: true });\n    };\n\n    const onTimeUpdate = () => {\n      const liveStatus = getLiveStatus();\n      store.getState()._updateState({\n        currentTime: video.currentTime,\n        isLive: liveStatus.isLive,\n        isNearLiveEdge: liveStatus.isNearEdge,\n        liveEdge: liveStatus.liveEdge,\n        timeBehindLive: liveStatus.liveEdge ? liveStatus.liveEdge - video.currentTime : undefined,\n      });\n    };\n\n    const onDurationChange = () => {\n      store.getState()._updateState({ duration: video.duration || 0 });\n    };\n\n    const onVolumeChange = () => {\n      store.getState()._updateState({ isMuted: video.muted, volume: video.volume });\n    };\n\n    const onLoadStart = () => {\n      store.getState()._updateState({ isLoading: true });\n    };\n\n    const onCanPlay = () => {\n      store.getState()._updateState({ isLoading: false });\n    };\n\n    const onError = () => {\n      store.getState()._updateState({ error: new Error(video.error?.message || 'Video error') });\n    };\n\n    // Add event listeners\n    video.addEventListener('play', onPlay);\n    video.addEventListener('pause', onPause);\n    video.addEventListener('ended', onEnded);\n    video.addEventListener('timeupdate', onTimeUpdate);\n    video.addEventListener('durationchange', onDurationChange);\n    video.addEventListener('volumechange', onVolumeChange);\n    video.addEventListener('loadstart', onLoadStart);\n    video.addEventListener('canplay', onCanPlay);\n    video.addEventListener('error', onError);\n\n    // Initial state update\n    updatePlaybackState();\n\n    return () => {\n      video.removeEventListener('play', onPlay);\n      video.removeEventListener('pause', onPause);\n      video.removeEventListener('ended', onEnded);\n      video.removeEventListener('timeupdate', onTimeUpdate);\n      video.removeEventListener('durationchange', onDurationChange);\n      video.removeEventListener('volumechange', onVolumeChange);\n      video.removeEventListener('loadstart', onLoadStart);\n      video.removeEventListener('canplay', onCanPlay);\n      video.removeEventListener('error', onError);\n    };\n  }, [store, videoRef, getLiveStatus]);\n\n  // Callback for syncing quality changes to store\n  const syncQualityChange = useCallback((quality: { height: number; bandwidth: number }) => {\n    if (store) {\n      store.getState()._updateState({ currentQuality: quality });\n    }\n  }, [store]);\n\n  // Callback for updating available qualities in store\n  const updateAvailableQualities = useCallback(() => {\n    if (store) {\n      store.getState()._updateState({ availableQualities: getAvailableQualities() });\n    }\n  }, [store, getAvailableQualities]);\n\n  return {\n    store,\n    syncQualityChange,\n    updateAvailableQualities,\n  };\n}\n","import { create, StoreApi, UseBoundStore } from 'zustand';\nimport React, { createContext, useContext, useRef, ReactNode } from 'react';\n\n/**\n * Player methods interface - shared between Player and Store\n */\nexport interface PlayerMethods {\n  skipForward: () => void;\n  skipBack: () => void;\n  setQuality: (height: number) => void;\n  getAvailableQualities: () => Array<{ height: number; bandwidth: number; label: string }>;\n  seekToLiveEdge: () => void;\n  getLiveStatus: () => { isLive: boolean; isNearEdge: boolean; liveEdge?: number };\n}\n\n/**\n * Player store state\n */\nexport interface PlayerStoreState {\n  // Video element and player refs\n  videoElement: HTMLVideoElement | null;\n  shakaPlayer: any | null;\n  \n  // Playback state\n  currentTime: number;\n  duration: number;\n  isPlaying: boolean;\n  isPaused: boolean;\n  isEnded: boolean;\n  isMuted: boolean;\n  volume: number;\n  isLoading: boolean;\n  \n  // Live stream state\n  isLive: boolean;\n  isNearLiveEdge: boolean;\n  liveEdge: number | undefined;\n  timeBehindLive: number | undefined;\n  \n  // Quality state\n  currentQuality: { height: number; bandwidth: number } | null;\n  availableQualities: Array<{ height: number; bandwidth: number; label: string }>;\n  \n  // Error state\n  error: Error | null;\n  \n  // Internal methods ref (stored outside Zustand state to avoid triggering re-renders)\n  _methodsRef: { current: PlayerMethods | null };\n}\n\n/**\n * Player store actions/methods\n */\nexport interface PlayerStoreActions {\n  // Playback controls\n  play: () => Promise<void>;\n  pause: () => void;\n  seek: (timeInSeconds: number) => void;\n  setVolume: (volume: number) => void;\n  toggleMute: () => void;\n  \n  // Skip controls\n  skipForward: () => void;\n  skipBack: () => void;\n  \n  // Quality controls\n  setQuality: (height: number) => void;\n  getAvailableQualities: () => Array<{ height: number; bandwidth: number; label: string }>;\n  \n  // Live controls\n  seekToLiveEdge: () => void;\n  getLiveStatus: () => { isLive: boolean; isNearEdge: boolean; liveEdge?: number };\n  \n  // Internal - used by Player component to register/update state\n  _registerVideoElement: (video: HTMLVideoElement | null) => void;\n  _registerShakaPlayer: (player: any | null) => void;\n  _updateState: (updates: Partial<PlayerStoreState>) => void;\n  _registerMethods: (methods: PlayerMethods) => void;\n}\n\n/**\n * Complete player store (state + actions)\n */\nexport type PlayerStore = PlayerStoreState & PlayerStoreActions;\n\n/**\n * Factory function to create a player store\n */\nfunction createPlayerStore() {\n  return create<PlayerStore>((set, get) => ({\n    // Initial state\n    videoElement: null,\n    shakaPlayer: null,\n    currentTime: 0,\n    duration: 0,\n    isPlaying: false,\n    isPaused: true,\n    isEnded: false,\n    isMuted: false,\n    volume: 1,\n    isLoading: true,\n    isLive: false,\n    isNearLiveEdge: false,\n    liveEdge: undefined,\n    timeBehindLive: undefined,\n    currentQuality: null,\n    availableQualities: [],\n    error: null,\n    _methodsRef: { current: null },\n\n    // Playback controls\n    play: async () => {\n      const { videoElement } = get();\n      if (videoElement) {\n        try {\n          await videoElement.play();\n        } catch (error) {\n          console.error('Error playing video:', error);\n        }\n      }\n    },\n\n    pause: () => {\n      const { videoElement } = get();\n      if (videoElement) {\n        videoElement.pause();\n      }\n    },\n\n    seek: (timeInSeconds: number) => {\n      const { videoElement } = get();\n      if (videoElement) {\n        const clampedTime = Math.max(0, Math.min(timeInSeconds, videoElement.duration || Infinity));\n        videoElement.currentTime = clampedTime;\n      }\n    },\n\n    setVolume: (volume: number) => {\n      const { videoElement } = get();\n      if (videoElement) {\n        const clampedVolume = Math.max(0, Math.min(1, volume));\n        videoElement.volume = clampedVolume;\n      }\n    },\n\n    toggleMute: () => {\n      const { videoElement } = get();\n      if (videoElement) {\n        videoElement.muted = !videoElement.muted;\n      }\n    },\n\n    // Skip controls - delegate to methods registered by Player\n    skipForward: () => {\n      const { _methodsRef } = get();\n      _methodsRef.current?.skipForward();\n    },\n\n    skipBack: () => {\n      const { _methodsRef } = get();\n      _methodsRef.current?.skipBack();\n    },\n\n    // Quality controls - delegate to methods registered by Player\n    setQuality: (height: number) => {\n      const { _methodsRef } = get();\n      _methodsRef.current?.setQuality(height);\n    },\n\n    getAvailableQualities: () => {\n      const { _methodsRef } = get();\n      return _methodsRef.current?.getAvailableQualities() || [];\n    },\n\n    // Live controls - delegate to methods registered by Player\n    seekToLiveEdge: () => {\n      const { _methodsRef } = get();\n      _methodsRef.current?.seekToLiveEdge();\n    },\n\n    getLiveStatus: () => {\n      const { _methodsRef } = get();\n      return _methodsRef.current?.getLiveStatus() || { isLive: false, isNearEdge: false };\n    },\n\n    // Internal methods\n    _registerVideoElement: (video: HTMLVideoElement | null) => {\n      set({ videoElement: video });\n    },\n\n    _registerShakaPlayer: (player: any | null) => {\n      set({ shakaPlayer: player });\n    },\n\n    _updateState: (updates: Partial<PlayerStoreState>) => {\n      set(updates);\n    },\n\n    _registerMethods: (methods: PlayerMethods) => {\n      // Update the ref without triggering re-renders\n      get()._methodsRef.current = methods;\n    },\n  }));\n}\n\n// Type for the store\ntype PlayerStoreApi = UseBoundStore<StoreApi<PlayerStore>>;\n\n// Context for provider-based stores\nconst PlayerStoreContext = createContext<PlayerStoreApi | null>(null);\n\n/**\n * PlayerProvider - Wrapper for isolated store instances\n * \n * Use this when you need multiple player instances on the same page,\n * or when you want to isolate player state from other parts of your app.\n * \n * @example\n * ```tsx\n * function App() {\n *   return (\n *     <PlayerProvider>\n *       <Video videoId=\"...\" publicKey=\"...\" />\n *       <CustomControls />\n *     </PlayerProvider>\n *   );\n * }\n * ```\n */\nexport function PlayerProvider({ children }: { children: ReactNode }) {\n  // Create a store instance for this provider (only once)\n  const storeRef = useRef<PlayerStoreApi | null>(null);\n  \n  if (!storeRef.current) {\n    storeRef.current = createPlayerStore();\n  }\n\n  return (\n    <PlayerStoreContext.Provider value={storeRef.current}>\n      {children}\n    </PlayerStoreContext.Provider>\n  );\n}\n\n/**\n * usePlayerStore hook - Access the player store state and methods\n * \n * When used inside a PlayerProvider, it uses the provider's isolated store.\n * When used outside, it throws an error (store requires a provider).\n * \n * @example\n * ```tsx\n * function CustomControls() {\n *   const { play, pause, seek, currentTime, duration, isPlaying } = usePlayerStore();\n *   \n *   return (\n *     <div>\n *       <button onClick={play}>Play</button>\n *       <button onClick={pause}>Pause</button>\n *       <button onClick={() => seek(120)}>Jump to 2min</button>\n *       <span>{currentTime} / {duration}</span>\n *     </div>\n *   );\n * }\n * ```\n */\nexport function usePlayerStore(): PlayerStore {\n  const store = useContext(PlayerStoreContext);\n  \n  if (!store) {\n    throw new Error(\n      'usePlayerStore must be used within a PlayerProvider. ' +\n      'Wrap your component tree with <PlayerProvider> to use the player store.'\n    );\n  }\n  \n  return store();\n}\n\n/**\n * Hook for Player.tsx to access the store API directly\n * Returns the store instance (not the state) for registering elements\n */\nexport function usePlayerStoreApi(): PlayerStoreApi | null {\n  return useContext(PlayerStoreContext);\n}\n\n// Export the context for advanced use cases\nexport { PlayerStoreContext };\n","import { useEffect, useState, useRef } from 'react';\n\ninterface UsePosterFallbackOptions {\n  enabled?: boolean;\n  fallbackPoster?: string;\n  showLoadingOverlay?: boolean;\n}\n\nexport const usePosterFallback = (\n  playerRef: React.RefObject<any>,\n  options: UsePosterFallbackOptions = {}\n) => {\n  const [isLoading, setIsLoading] = useState(true);\n  const [showOverlay, setShowOverlay] = useState(true);\n  const [hasStartedPlaying, setHasStartedPlaying] = useState(false);\n  const { enabled = true, fallbackPoster, showLoadingOverlay = true } = options;\n\n  useEffect(() => {\n    if (!playerRef.current || !enabled) {\n      return;\n    }\n\n    const handleLoadStart = () => {\n      setIsLoading(true);\n      setShowOverlay(true);\n    };\n\n    const handleCanPlay = () => {\n      setIsLoading(false);\n      // Keep overlay visible until user interaction or autoplay starts\n      if (!hasStartedPlaying) {\n        setShowOverlay(true);\n      }\n    };\n\n    const handlePlay = () => {\n      setHasStartedPlaying(true);\n      setShowOverlay(false);\n    };\n\n    const handleLoadedMetadata = () => {\n      setIsLoading(false);\n    };\n\n    // Add event listeners\n    const video = playerRef.current;\n    if (video) {\n      video.addEventListener('loadstart', handleLoadStart);\n      video.addEventListener('canplay', handleCanPlay);\n      video.addEventListener('play', handlePlay);\n      video.addEventListener('loadedmetadata', handleLoadedMetadata);\n\n      return () => {\n        video.removeEventListener('loadstart', handleLoadStart);\n        video.removeEventListener('canplay', handleCanPlay);\n        video.removeEventListener('play', handlePlay);\n        video.removeEventListener('loadedmetadata', handleLoadedMetadata);\n      };\n    }\n  }, [playerRef, enabled, hasStartedPlaying]);\n\n  const hideOverlay = () => {\n    setShowOverlay(false);\n  };\n\n  return {\n    isLoading,\n    showOverlay: showLoadingOverlay && showOverlay,\n    hideOverlay,\n    hasStartedPlaying\n  };\n}; ","import { useEffect, useRef, useState, useCallback } from 'react';\nimport type { ConcurrencySession, ConcurrencyServerMessage } from '../api/video';\nimport { ConcurrencyConnectionState } from '../api/video';\nimport { getDeviceId } from '../utils/deviceId';\n\n// ============================================================================\n// Configuration Constants\n// ============================================================================\n\n/** Interval for sending heartbeat messages to keep the connection alive */\nconst HEARTBEAT_INTERVAL_MS = 30_000; // 30 seconds\n\n/** Maximum number of reconnection attempts before kicking the user */\nconst MAX_RECONNECT_ATTEMPTS = 5;\n\n/** Base delay for exponential backoff reconnection (2s, 4s, 8s, 16s, 32s) */\nconst RECONNECT_BASE_DELAY_MS = 2_000; // 2 seconds\n\n/** Timeout for establishing initial WebSocket connection */\nconst CONNECTION_TIMEOUT_MS = 10_000; // 10 seconds\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface SessionStartedInfo {\n  sessionId: string;\n  activeSessions: number;\n  limit: number;\n  kickedSessions: number;\n}\n\nexport interface ConcurrencySessionState {\n  /** Current connection state */\n  connectionState: ConcurrencyConnectionState;\n\n  /** Whether the WebSocket is connected */\n  isConnected: boolean;\n\n  /**\n   * SECURITY: Whether playback is allowed.\n   *\n   * This is the primary flag that should be used to control video playback.\n   * When false, playback MUST be stopped to enforce concurrency limits.\n   *\n   * Returns false when:\n   * - Concurrency session is required but WebSocket is not connected\n   * - Session was kicked by server or due to connection failure\n   *\n   * Returns true when:\n   * - No concurrency session is required (content doesn't need limit enforcement)\n   * - WebSocket is connected with valid session\n   * - During reconnection attempts (grace period to handle network glitches)\n   */\n  isPlaybackAllowed: boolean;\n\n  /** Current session ID (null if not connected) */\n  sessionId: string | null;\n\n  /** Number of active sessions for this user */\n  activeSessions: number;\n\n  /** Maximum allowed concurrent sessions */\n  limit: number;\n\n  /** Last error that occurred */\n  error: Error | null;\n\n  /** Whether this session was kicked */\n  wasKicked: boolean;\n\n  /** Reason for being kicked (if applicable) */\n  kickedReason: string | null;\n\n  /** Message associated with being kicked */\n  kickedMessage: string | null;\n\n  /** Manually disconnect the WebSocket */\n  disconnect: () => void;\n}\n\nexport interface UseConcurrencySessionOptions {\n  /** Called when connection is established and playback can begin */\n  onSessionStarted?: (info: SessionStartedInfo) => void;\n\n  /**\n   * Called when this session is kicked - MUST stop playback immediately.\n   * This can be triggered by:\n   * - Server sending a 'kicked' message (another session started, admin action)\n   * - WebSocket close codes 4000, 4029, 4030\n   * - Max reconnection attempts exhausted\n   */\n  onKicked?: (reason: string, message: string) => void;\n\n  /** Called when session count changes (for UI updates) */\n  onSessionCountChanged?: (activeSessions: number, limit: number) => void;\n\n  /** Called on connection state changes (for UI feedback) */\n  onStateChanged?: (state: ConcurrencyConnectionState) => void;\n\n  /** Called on errors */\n  onError?: (error: Error) => void;\n\n  /**\n   * Optional client-provided device ID (e.g., user ID).\n   * If not provided, one will be generated and persisted in localStorage.\n   */\n  deviceId?: string;\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\nexport const useConcurrencySession = (\n  concurrencySession: ConcurrencySession | undefined,\n  options: UseConcurrencySessionOptions = {}\n): ConcurrencySessionState => {\n  // ---- Refs for WebSocket and timers (don't trigger re-renders) ----\n  const wsRef = useRef<WebSocket | null>(null);\n  const heartbeatTimerRef = useRef<number | null>(null);\n  const reconnectTimerRef = useRef<number | null>(null);\n  const connectionTimeoutRef = useRef<number | null>(null);\n  const reconnectAttemptsRef = useRef<number>(0);\n\n  /**\n   * Flag to distinguish between intentional disconnects (user action, cleanup)\n   * and unexpected disconnects (network issues). Only unexpected disconnects\n   * should trigger reconnection attempts.\n   */\n  const isIntentionallyClosedRef = useRef<boolean>(false);\n\n  /** Track the URL we're connected to, to avoid duplicate connections */\n  const connectedUrlRef = useRef<string | null>(null);\n\n  /** Guard to prevent duplicate connection attempts during the same render cycle */\n  const isConnectingRef = useRef<boolean>(false);\n\n  /** Keep options ref updated to avoid stale closures in callbacks */\n  const optionsRef = useRef(options);\n\n  // ---- State ----\n  const [connectionState, setConnectionState] = useState<ConcurrencyConnectionState>(\n    ConcurrencyConnectionState.DISCONNECTED\n  );\n  const [sessionId, setSessionId] = useState<string | null>(null);\n  const [activeSessions, setActiveSessions] = useState<number>(0);\n  const [limit, setLimit] = useState<number>(concurrencySession?.limit ?? 0);\n  const [error, setError] = useState<Error | null>(null);\n  const [wasKicked, setWasKicked] = useState<boolean>(false);\n  const [kickedReason, setKickedReason] = useState<string | null>(null);\n  const [kickedMessage, setKickedMessage] = useState<string | null>(null);\n  const [reconnectTrigger, setReconnectTrigger] = useState<number>(0);\n\n  // Keep options ref updated\n  optionsRef.current = options;\n\n  const wsUrl = concurrencySession?.wsUrl;\n  const token = concurrencySession?.token;\n\n  // Get device ID (client-provided or auto-generated from localStorage)\n  const deviceId = getDeviceId(options.deviceId);\n\n  // ============================================================================\n  // Playback Allowed Logic\n  // ============================================================================\n  /**\n   * Determines if playback should be allowed based on kicked state.\n   *\n   * Playback is blocked ONLY when the user was explicitly kicked by the server\n   * (concurrency limit exceeded, admin action, max reconnection attempts exhausted).\n   *\n   * Network issues and temporary disconnections do NOT block playback - we allow\n   * the video to continue playing while attempting to reconnect.\n   */\n  const isPlaybackAllowed = !wasKicked;\n\n  // ============================================================================\n  // Timer Management Utilities\n  // ============================================================================\n\n  /** Stop the heartbeat interval timer */\n  const stopHeartbeat = useCallback(() => {\n    if (heartbeatTimerRef.current !== null) {\n      window.clearInterval(heartbeatTimerRef.current);\n      heartbeatTimerRef.current = null;\n    }\n  }, []);\n\n  /** Send a heartbeat message to keep the session alive */\n  const sendHeartbeat = useCallback(() => {\n    if (wsRef.current?.readyState === WebSocket.OPEN) {\n      wsRef.current.send(JSON.stringify({ type: 'heartbeat' }));\n    }\n  }, []);\n\n  /** Start the heartbeat interval timer */\n  const startHeartbeat = useCallback(() => {\n    stopHeartbeat();\n    heartbeatTimerRef.current = window.setInterval(sendHeartbeat, HEARTBEAT_INTERVAL_MS);\n  }, [stopHeartbeat, sendHeartbeat]);\n\n  /** Clear the connection timeout timer */\n  const clearConnectionTimeout = useCallback(() => {\n    if (connectionTimeoutRef.current !== null) {\n      window.clearTimeout(connectionTimeoutRef.current);\n      connectionTimeoutRef.current = null;\n    }\n  }, []);\n\n  /** Clear the reconnection timer */\n  const clearReconnectTimer = useCallback(() => {\n    if (reconnectTimerRef.current !== null) {\n      window.clearTimeout(reconnectTimerRef.current);\n      reconnectTimerRef.current = null;\n    }\n  }, []);\n\n  // ============================================================================\n  // State Update Utilities\n  // ============================================================================\n\n  /** Update connection state and notify listener */\n  const updateConnectionState = useCallback((state: ConcurrencyConnectionState) => {\n    setConnectionState(state);\n    optionsRef.current.onStateChanged?.(state);\n  }, []);\n\n  /**\n   * Kick the user due to connection failure.\n   * This is called when:\n   * - Max reconnection attempts are exhausted\n   * - Server sends specific close codes (4000, 4029, 4030)\n   *\n   * Note: We only set the reason code here, not a hardcoded message.\n   * The UI layer should use the reason to look up the appropriate localized message.\n   */\n  const kickDueToConnectionFailure = useCallback((reason: string) => {\n    setWasKicked(true);\n    setKickedReason(reason);\n    setKickedMessage(null);\n    optionsRef.current.onKicked?.(reason, '');\n  }, []);\n\n  // ============================================================================\n  // Cleanup and Disconnect\n  // ============================================================================\n\n  /** Clean up all timers and close WebSocket connection */\n  const cleanup = useCallback(() => {\n    stopHeartbeat();\n    clearConnectionTimeout();\n    clearReconnectTimer();\n\n    if (wsRef.current) {\n      // Close with normal closure code if still open/connecting\n      if (wsRef.current.readyState === WebSocket.OPEN || wsRef.current.readyState === WebSocket.CONNECTING) {\n        wsRef.current.close(1000, 'Client disconnecting');\n      }\n      wsRef.current = null;\n    }\n\n    connectedUrlRef.current = null;\n    setSessionId(null);\n    updateConnectionState(ConcurrencyConnectionState.DISCONNECTED);\n  }, [stopHeartbeat, clearConnectionTimeout, clearReconnectTimer, updateConnectionState]);\n\n  /** Public method to manually disconnect (marks as intentional to prevent reconnection) */\n  const disconnect = useCallback(() => {\n    isIntentionallyClosedRef.current = true;\n    cleanup();\n  }, [cleanup]);\n\n  // ============================================================================\n  // WebSocket Message Handler\n  // ============================================================================\n\n  /** Handle incoming messages from the concurrency server */\n  const handleMessage = useCallback((data: string) => {\n    let message: ConcurrencyServerMessage;\n    try {\n      message = JSON.parse(data);\n    } catch {\n      console.error('[ConcurrencySession] Failed to parse message:', data);\n      const parseError = new Error('Invalid JSON from server');\n      setError(parseError);\n      optionsRef.current.onError?.(parseError);\n      return;\n    }\n\n    switch (message.type) {\n      case 'session_started':\n        // Session successfully established - playback can begin\n        setSessionId(message.sessionId);\n        setActiveSessions(message.activeSessions);\n        setLimit(message.limit);\n        setError(null);\n        // Clear any previous kicked state\n        setWasKicked(false);\n        setKickedReason(null);\n        setKickedMessage(null);\n        updateConnectionState(ConcurrencyConnectionState.CONNECTED);\n        startHeartbeat();\n\n        optionsRef.current.onSessionStarted?.({\n          sessionId: message.sessionId,\n          activeSessions: message.activeSessions,\n          limit: message.limit,\n          kickedSessions: message.kickedSessions,\n        });\n        break;\n\n      case 'heartbeat_ack':\n        // Heartbeat acknowledged - connection is healthy\n        setActiveSessions(message.activeSessions);\n        break;\n\n      case 'session_count_changed':\n        // Another session connected or disconnected (for UI updates)\n        setActiveSessions(message.activeSessions);\n        setLimit(message.limit);\n        optionsRef.current.onSessionCountChanged?.(message.activeSessions, message.limit);\n        break;\n\n      case 'kicked':\n        // CRITICAL: This session was kicked - MUST stop playback immediately\n        // Mark as intentional to prevent reconnection attempts\n        isIntentionallyClosedRef.current = true;\n        setWasKicked(true);\n        setKickedReason(message.reason);\n        setKickedMessage(message.message);\n        optionsRef.current.onKicked?.(message.reason, message.message);\n        cleanup();\n        break;\n\n      case 'error':\n        // Server reported an error\n        const serverError = new Error(message.message);\n        setError(serverError);\n        optionsRef.current.onError?.(serverError);\n        break;\n    }\n  }, [updateConnectionState, startHeartbeat, cleanup]);\n\n  // ============================================================================\n  // Reconnection Logic\n  // ============================================================================\n\n  /**\n   * Schedule a reconnection attempt with exponential backoff.\n   *\n   * SECURITY: If max attempts are exhausted, the user is kicked to prevent\n   * bypassing concurrency limits by staying in a perpetual \"reconnecting\" state.\n   */\n  const scheduleReconnect = useCallback(() => {\n    clearReconnectTimer();\n\n    // Check if we've exhausted all reconnection attempts\n    if (reconnectAttemptsRef.current >= MAX_RECONNECT_ATTEMPTS) {\n      // SECURITY: Kick the user - can't maintain valid session\n      kickDueToConnectionFailure('connection_failed');\n      updateConnectionState(ConcurrencyConnectionState.DISCONNECTED);\n      const maxRetriesError = new Error('Max reconnection attempts reached');\n      setError(maxRetriesError);\n      optionsRef.current.onError?.(maxRetriesError);\n      return;\n    }\n\n    // Calculate delay with exponential backoff: 2s, 4s, 8s, 16s, 32s\n    const delay = RECONNECT_BASE_DELAY_MS * Math.pow(2, reconnectAttemptsRef.current);\n    reconnectAttemptsRef.current++;\n\n    updateConnectionState(ConcurrencyConnectionState.RECONNECTING);\n\n    reconnectTimerRef.current = window.setTimeout(() => {\n      // Trigger reconnect by clearing the connected URL and incrementing the trigger\n      connectedUrlRef.current = null;\n      isConnectingRef.current = false;\n      // Force re-run of the connection effect\n      setReconnectTrigger((prev) => prev + 1);\n    }, delay);\n  }, [clearReconnectTimer, kickDueToConnectionFailure, updateConnectionState]);\n\n  // ============================================================================\n  // WebSocket Close Handler\n  // ============================================================================\n\n  /**\n   * Handle WebSocket close events.\n   *\n   * Different close codes have different meanings:\n   * - 1000: Normal closure (intentional)\n   * - 4000: Replaced by same device\n   * - 4029: Concurrency limit exceeded\n   * - 4030: Kicked by admin\n   * - Other: Network error - attempt reconnection\n   */\n  const handleClose = useCallback((code: number) => {\n    stopHeartbeat();\n    clearConnectionTimeout();\n\n    // Handle specific close codes that indicate definitive rejection (no reconnection)\n    switch (code) {\n      case 4029:\n        // Concurrency limit exceeded on connect - kick user\n        updateConnectionState(ConcurrencyConnectionState.DISCONNECTED);\n        kickDueToConnectionFailure('concurrency_limit_exceeded');\n        return;\n\n      case 4030:\n        // Kicked by admin - kick user\n        updateConnectionState(ConcurrencyConnectionState.DISCONNECTED);\n        kickDueToConnectionFailure('admin_action');\n        return;\n\n      case 4000:\n        // Replaced by same device (shouldn't happen normally)\n        updateConnectionState(ConcurrencyConnectionState.DISCONNECTED);\n        kickDueToConnectionFailure('session_replaced');\n        return;\n\n      case 1000:\n        // Normal closure (intentional disconnect)\n        updateConnectionState(ConcurrencyConnectionState.DISCONNECTED);\n        return;\n    }\n\n    // For other close codes (network issues, server restart, etc.),\n    // attempt reconnection ONLY if this wasn't an intentional close\n    if (!isIntentionallyClosedRef.current && wsUrl && token) {\n      scheduleReconnect();\n    } else {\n      updateConnectionState(ConcurrencyConnectionState.DISCONNECTED);\n    }\n  }, [stopHeartbeat, clearConnectionTimeout, updateConnectionState, kickDueToConnectionFailure, scheduleReconnect, wsUrl, token]);\n\n  // ============================================================================\n  // WebSocket Connection Effect\n  // ============================================================================\n\n  useEffect(() => {\n    // If no concurrency session, clean up any existing connection\n    if (!wsUrl || !token) {\n      if (wsRef.current) {\n        isIntentionallyClosedRef.current = true;\n        cleanup();\n      }\n      isConnectingRef.current = false;\n      return;\n    }\n\n    // CRITICAL: If user was kicked, do NOT attempt to reconnect\n    // This prevents the flickering error screen issue\n    if (wasKicked) {\n      return;\n    }\n\n    // Build the full URL with token as query parameter\n    const fullUrl = `${wsUrl}?token=${encodeURIComponent(token)}`;\n\n    // Skip if already connected to the same URL\n    if (connectedUrlRef.current === fullUrl && wsRef.current?.readyState === WebSocket.OPEN) {\n      return;\n    }\n\n    // Skip if WebSocket is already in the process of connecting\n    if (wsRef.current?.readyState === WebSocket.CONNECTING) {\n      return;\n    }\n\n    // Guard against duplicate connection attempts in the same render cycle\n    // This prevents double connections caused by React StrictMode or rapid re-renders\n    if (isConnectingRef.current) {\n      return;\n    }\n    isConnectingRef.current = true;\n\n    // Close existing connection if connecting to a different URL\n    if (wsRef.current) {\n      wsRef.current.close();\n      wsRef.current = null;\n    }\n\n    // Only reset error state if we weren't kicked\n    // This ensures we don't try to reconnect after being kicked\n    if (!isIntentionallyClosedRef.current) {\n      setError(null);\n    }\n    updateConnectionState(ConcurrencyConnectionState.CONNECTING);\n\n    try {\n      const ws = new WebSocket(fullUrl);\n\n      // Set connection timeout - if connection doesn't establish in time, try reconnecting\n      connectionTimeoutRef.current = window.setTimeout(() => {\n        if (ws.readyState !== WebSocket.OPEN) {\n          isConnectingRef.current = false;\n          ws.close();\n          // Connection timeout - try to reconnect\n          if (!isIntentionallyClosedRef.current) {\n            scheduleReconnect();\n          }\n        }\n      }, CONNECTION_TIMEOUT_MS);\n\n      ws.onopen = () => {\n        clearConnectionTimeout();\n        connectedUrlRef.current = fullUrl;\n        reconnectAttemptsRef.current = 0;\n        isConnectingRef.current = false;\n        // Note: Don't update state to CONNECTED here\n        // Wait for 'session_started' message from server to confirm session is valid\n      };\n\n      ws.onmessage = (event) => {\n        handleMessage(event.data);\n      };\n\n      ws.onerror = (event) => {\n        console.error('[ConcurrencySession] WebSocket error:', event);\n        const wsError = new Error('WebSocket connection error');\n        setError(wsError);\n        optionsRef.current.onError?.(wsError);\n      };\n\n      ws.onclose = (event) => {\n        clearConnectionTimeout();\n        connectedUrlRef.current = null;\n        isConnectingRef.current = false;\n        handleClose(event.code);\n      };\n\n      wsRef.current = ws;\n    } catch (err) {\n      isConnectingRef.current = false;\n      const connectionError = err instanceof Error ? err : new Error('Failed to connect to WebSocket');\n      setError(connectionError);\n      optionsRef.current.onError?.(connectionError);\n      // Connection failed - try to reconnect\n      if (!isIntentionallyClosedRef.current) {\n        scheduleReconnect();\n      }\n    }\n  }, [wsUrl, token, deviceId, wasKicked, reconnectTrigger, cleanup, handleMessage, handleClose, clearConnectionTimeout, updateConnectionState, scheduleReconnect]);\n\n  // ============================================================================\n  // Browser Event Handlers\n  // ============================================================================\n\n  /**\n   * Handle page unload (tab close, navigation away).\n   * Gracefully disconnect to free up the session slot.\n   */\n  useEffect(() => {\n    if (!concurrencySession) return;\n\n    const handleBeforeUnload = () => {\n      disconnect();\n    };\n\n    window.addEventListener('beforeunload', handleBeforeUnload);\n    return () => window.removeEventListener('beforeunload', handleBeforeUnload);\n  }, [disconnect, concurrencySession]);\n\n  /**\n   * Cleanup on component unmount.\n   * Mark as intentional close to prevent reconnection attempts.\n   */\n  useEffect(() => {\n    return () => {\n      isIntentionallyClosedRef.current = true;\n      cleanup();\n    };\n  }, [cleanup]);\n\n  // ============================================================================\n  // Return State\n  // ============================================================================\n\n  return {\n    connectionState,\n    isConnected: connectionState === ConcurrencyConnectionState.CONNECTED,\n    isPlaybackAllowed,\n    sessionId,\n    activeSessions,\n    limit,\n    error,\n    wasKicked,\n    kickedReason,\n    kickedMessage,\n    disconnect,\n  };\n};\n","import { createClient } from '@connectrpc/connect';\nimport { timestampFromDate } from '@bufbuild/protobuf/wkt';\nimport { VideoService } from '@buf/motto_cda.bufbuild_es/motto/cda/streaming/video/v1/service_pb';\nimport {\n  ErrorCodeSchema,\n  PlaylistFormatSchema,\n  type Video,\n  type Playlist as PlaylistMessage,\n  type DRM as DRMMessage,\n} from '@buf/motto_cda.bufbuild_es/motto/cda/streaming/video/v1/video_pb';\nimport type { DescEnum } from '@bufbuild/protobuf';\nimport { getRecentPurchaseTime } from '../utils/recentPurchase';\nimport { cdaTransport, authHeaders } from './transport';\n\nexport interface ConcurrencySession {\n  /** Signed JWT token for WebSocket authentication */\n  token: string;\n  /** WebSocket URL to connect to */\n  wsUrl: string;\n  /** Maximum allowed concurrent streams (for UI display) */\n  limit: number;\n}\n\n/** Connection state for concurrency session */\nexport enum ConcurrencyConnectionState {\n  DISCONNECTED = 'disconnected',\n  CONNECTING = 'connecting',\n  CONNECTED = 'connected',\n  RECONNECTING = 'reconnecting',\n}\n\n/** Messages FROM Server TO Client */\nexport type ConcurrencyServerMessage =\n  | {\n      type: 'session_started';\n      sessionId: string;\n      activeSessions: number;\n      limit: number;\n      kickedSessions: number;\n    }\n  | {\n      type: 'heartbeat_ack';\n      timestamp: number;\n      activeSessions: number;\n      limit: number;\n    }\n  | {\n      type: 'session_count_changed';\n      activeSessions: number;\n      limit: number;\n    }\n  | {\n      type: 'kicked';\n      reason: 'new_session_started' | 'admin_action' | 'session_replaced';\n      message: string;\n    }\n  | {\n      type: 'status';\n      activeSessions: number;\n      limit: number;\n      sessions: Array<{\n        sessionId: string;\n        deviceId: string;\n        videoId: string;\n        connectedAt: number;\n        isCurrentSession: boolean;\n      }>;\n    }\n  | {\n      type: 'error';\n      message: string;\n    };\n\n/** Messages FROM Client TO Server */\nexport type ConcurrencyClientMessage =\n  | { type: 'heartbeat' }\n  | { type: 'get_status' };\n\nexport interface VideoData {\n  id: string;\n  name?: string;\n  description?: string;\n  playlists?: Playlist[];\n  error?: string;\n  ad?: {\n    adTagUrl?: string;\n  };\n  concurrencySession?: ConcurrencySession;\n}\n\nexport interface DVRSettings {\n  window_size_seconds: number;\n}\n\nexport interface Widevine {\n  playlistUrl: string;\n  licenseUrl: string;\n}\n\nexport interface Fairplay {\n  certificateUrl: string;\n  licenseUrl: string;\n  playlistUrl: string;\n}\n\nexport interface Playready {\n  playlistUrl: string;\n  licenseUrl: string;\n}\n\nexport interface DRM {\n  token?: string;\n  licenseCacheKey?: string;\n  widevine?: Widevine;\n  fairplay?: Fairplay;\n  playready?: Playready;\n}\n\nexport interface Playlist {\n  id: string;\n  url: string;\n  format: string;\n  dvr_settings?: DVRSettings;\n  drm: DRM;\n};\n\nconst videoClient = createClient(VideoService, cdaTransport);\n\n// Proto enum number -> proto value name (e.g. 1 -> \"ERROR_CODE_GEO_BLOCKED\"),\n// matching what the previous JSON responses contained and what errorTypes /\n// findHLSPlaylist compare against.\nconst enumName = (desc: DescEnum, value: number): string | undefined =>\n  desc.values.find((v) => v.number === value)?.name;\n\nconst toDRM = (drm?: DRMMessage): DRM => ({\n  token: drm?.token || undefined,\n  licenseCacheKey: drm?.licenseCacheKey || undefined,\n  widevine: drm?.widevine\n    ? { playlistUrl: drm.widevine.playlistUrl, licenseUrl: drm.widevine.licenseUrl }\n    : undefined,\n  fairplay: drm?.fairplay\n    ? {\n        playlistUrl: drm.fairplay.playlistUrl,\n        licenseUrl: drm.fairplay.licenseUrl,\n        certificateUrl: drm.fairplay.certificateUrl,\n      }\n    : undefined,\n  playready: drm?.playready\n    ? { playlistUrl: drm.playready.playlistUrl, licenseUrl: drm.playready.licenseUrl }\n    : undefined,\n});\n\nconst toPlaylist = (playlist: PlaylistMessage): Playlist => ({\n  id: playlist.id,\n  url: playlist.url,\n  format: enumName(PlaylistFormatSchema, playlist.format) ?? '',\n  dvr_settings: playlist.dvrSettings\n    ? { window_size_seconds: playlist.dvrSettings.windowSizeSeconds }\n    : undefined,\n  drm: toDRM(playlist.drm),\n});\n\nconst toVideoData = (video: Video): VideoData => ({\n  id: video.id,\n  name: video.name || undefined,\n  playlists: video.playlists.map(toPlaylist),\n  error: video.error ? enumName(ErrorCodeSchema, video.error) : undefined,\n  ad: video.ad ? { adTagUrl: video.ad.adTagUrl || undefined } : undefined,\n  concurrencySession: video.concurrencySession\n    ? {\n        token: video.concurrencySession.token,\n        wsUrl: video.concurrencySession.wsUrl,\n        limit: video.concurrencySession.limit,\n      }\n    : undefined,\n});\n\n/**\n * Fetches video data from the Motto Streaming API\n * @param videoId - The ID of the video to fetch\n * @param publicKey - The public key for authentication\n * @param mottoToken - Optional motto token for authenticated requests\n * @param adsEnabled - Optional flag to enable ads\n * @param locale - Optional locale for localization\n * @param deviceId - Optional device ID for concurrency session tracking\n * @returns Promise<VideoData> - The video data\n */\nexport const fetchVideoData = async (\n  videoId: string,\n  publicKey: string,\n  mottoToken?: string,\n  adsEnabled: boolean = false,\n  locale: string = 'en',\n  deviceId?: string\n): Promise<VideoData> => {\n  const recentPurchaseTime = getRecentPurchaseTime();\n\n  const response = await videoClient.getVideo(\n    {\n      videoId,\n      enableAds: adsEnabled,\n      locale,\n      deviceId: deviceId ?? '',\n      ...(recentPurchaseTime\n        ? { recentPurchaseTime: timestampFromDate(new Date(recentPurchaseTime)) }\n        : {}),\n    },\n    { headers: authHeaders(publicKey, mottoToken) }\n  );\n\n  if (!response.video) {\n    throw new Error('Failed to fetch video: empty response');\n  }\n  return toVideoData(response.video);\n};\n\n/**\n * Fetches multiple videos data from the Motto Streaming API\n * @param publicKey - The public key for authentication\n * @param videoIds - Array of video IDs to fetch\n * @param mottoToken - Optional motto token for authenticated requests\n * @param skip - Number of videos to skip\n * @param limit - Maximum number of videos to return\n * @param adsEnabled - Optional flag to enable ads\n * @param locale - Optional locale for localization\n * @param deviceId - Optional device ID for concurrency session tracking\n * @returns Promise<VideoData[]> - Array of video data\n */\nexport async function fetchVideosList(\n  publicKey: string,\n  videoIds: string[],\n  mottoToken?: string,\n  skip: number = 0,\n  limit: number = 0,\n  adsEnabled: boolean = true,\n  locale: string = 'en',\n  deviceId?: string\n): Promise<VideoData[]> {\n  if (!videoIds || videoIds.length === 0) {\n    return [];\n  }\n\n  const recentPurchaseTime = getRecentPurchaseTime();\n\n  const response = await videoClient.batchGetVideos(\n    {\n      videoIds,\n      enableAds: adsEnabled,\n      locale,\n      deviceId: deviceId ?? '',\n      ...(recentPurchaseTime\n        ? { recentPurchaseTime: timestampFromDate(new Date(recentPurchaseTime)) }\n        : {}),\n    },\n    { headers: authHeaders(publicKey, mottoToken) }\n  );\n\n  return response.videos.map(toVideoData);\n}","// Reads a recent-purchase timestamp written by the host app immediately after a\n// purchase. When present, video API calls (GetVideo / BatchGetVideos) send it as\n// recent_purchase_time so the backend knows to look for an entitlement that may\n// not have fully propagated yet.\n//\n// Contract (shared with the host app's writer):\n//   localStorage key: \"recentPurchaseTime\"\n//   value: JSON { time: RFC3339 string, expiresAt: epoch ms }\n\nconst RECENT_PURCHASE_KEY = 'recentPurchaseTime';\n\ninterface StoredRecentPurchase {\n  time: string;\n  expiresAt: number;\n}\n\n// Returns the RFC3339 purchase timestamp if a non-expired recent purchase exists,\n// otherwise undefined (clearing the stale entry on the way out).\nexport const getRecentPurchaseTime = (): string | undefined => {\n  if (typeof window === 'undefined') return undefined;\n  try {\n    const raw = localStorage.getItem(RECENT_PURCHASE_KEY);\n    if (!raw) return undefined;\n    const parsed = JSON.parse(raw) as StoredRecentPurchase;\n    if (!parsed?.time || !parsed?.expiresAt || Date.now() > parsed.expiresAt) {\n      localStorage.removeItem(RECENT_PURCHASE_KEY);\n      return undefined;\n    }\n    return parsed.time;\n  } catch {\n    return undefined;\n  }\n};\n","import { createConnectTransport } from '@connectrpc/connect-web';\n\n// Shared Connect transport for all CDA calls. Every RPC we use is declared\n// NO_SIDE_EFFECTS, so useHttpGet keeps requests as cacheable GETs\n// (?encoding=json&message=...), matching the previous hand-rolled fetch calls.\nexport const cdaTransport = createConnectTransport({\n  baseUrl: 'https://cda.mottostreaming.com',\n  useHttpGet: true,\n  jsonOptions: { ignoreUnknownFields: true },\n});\n\nexport const authHeaders = (publicKey: string, mottoToken?: string): HeadersInit => ({\n  Authorization: `Bearer ${publicKey}`,\n  ...(mottoToken ? { 'x-motto-token': mottoToken } : {}),\n});\n","const STORAGE_KEY = 'motto_concurrency_device_id';\n\n/**\n * Generates a UUID v4\n */\nconst generateUUID = (): string => {\n  if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n    return crypto.randomUUID();\n  }\n  // Fallback for older browsers\n  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n    const r = (Math.random() * 16) | 0;\n    const v = c === 'x' ? r : (r & 0x3) | 0x8;\n    return v.toString(16);\n  });\n};\n\n/**\n * Gets or creates a device identifier.\n *\n * @param clientProvidedId - Optional ID provided by the client (e.g., custom device ID)\n * @returns The device ID to use for concurrency tracking\n *\n * Priority:\n * 1. If clientProvidedId is provided, use it directly (client manages it)\n * 2. Otherwise, retrieve from localStorage (shared across tabs)\n * 3. If nothing in localStorage, generate a new UUID and store it\n */\nexport const getDeviceId = (clientProvidedId?: string): string => {\n  // If client provides an ID, use it directly (don't store - client manages it)\n  // let deviceId = localStorage.getItem(STORAGE_KEY);\n  if (clientProvidedId) {\n    localStorage.setItem(STORAGE_KEY, clientProvidedId);\n    return clientProvidedId;\n  }\n\n  try {\n    // Get from localStorage or generate new\n    let deviceId = localStorage.getItem(STORAGE_KEY);\n\n    if (!deviceId) {\n      deviceId = `device_${generateUUID()}`;\n      localStorage.setItem(STORAGE_KEY, deviceId);\n    }\n\n    return deviceId;\n  } catch {\n    // localStorage may be blocked (e.g., in incognito mode)\n    return `device_${generateUUID()}`;\n  }\n};\n\n/**\n * Clears the stored device ID from localStorage\n * (useful for testing or user logout scenarios)\n */\nexport const clearDeviceId = (): void => {\n  if (typeof window === 'undefined' || typeof localStorage === 'undefined') {\n    return;\n  }\n\n  try {\n    localStorage.removeItem(STORAGE_KEY);\n  } catch {\n    // Ignore errors\n  }\n};\n","import { Playlist } from '../api';\n\n/**\n * Checks if a source has a valid playlist URL.\n * Used to determine if the player should (re)initialize based on playlist presence.\n */\nexport const hasPlaylistUrl = (src: Playlist | string | undefined): boolean => {\n  if (!src) return false;\n  if (typeof src === 'string') return true;\n\n  return !!(\n    src.url ||\n    src.drm?.widevine?.playlistUrl ||\n    src.drm?.playready?.playlistUrl ||\n    src.drm?.fairplay?.playlistUrl\n  );\n};\n","import { CSSProperties } from 'react';\nimport { twMerge } from 'tailwind-merge';\n\ninterface PlayerStylesOptions {\n  width?: number | string;\n  height?: number | string;\n  aspectRatio: number;\n  containerClassName?: string;\n}\n\ninterface PlayerStyles {\n  isResponsive: boolean;\n  containerClasses: string;\n  containerStyle: CSSProperties;\n  videoClasses: string;\n  videoStyle: CSSProperties;\n}\n\n/**\n * Computes container and video element styles based on sizing mode.\n * Supports both responsive (aspect-ratio based) and fixed (width/height) sizing.\n */\nexport const getPlayerStyles = ({\n  width,\n  height,\n  aspectRatio,\n  containerClassName\n}: PlayerStylesOptions): PlayerStyles => {\n  const isResponsive = !width && !height;\n\n  const containerClasses = twMerge(\n    containerClassName,\n    'motto-video-container relative bg-[#111111]'\n  );\n\n  const containerStyle: CSSProperties = isResponsive\n    ? { aspectRatio: aspectRatio.toString() }\n    : { width, height };\n\n  const videoClasses = isResponsive\n    ? 'motto-video-responsive w-full'\n    : 'w-full h-full';\n\n  const videoStyle: CSSProperties = isResponsive\n    ? {}\n    : { width, height };\n\n  return {\n    isResponsive,\n    containerClasses,\n    containerStyle,\n    videoClasses,\n    videoStyle\n  };\n};\n","import React from 'react';\nimport { twMerge } from 'tailwind-merge';\n\ninterface LoadingProps {\n  className?: string;\n}\n\nexport const Loading: React.FC<LoadingProps> = ({ className }) => (\n  <div\n    className={twMerge(\n      \"relative bg-[#151515] md:rounded-2xl! overflow-hidden aspect-video text-white w-full h-full flex justify-center items-center text-[10px]\",\n      className\n    )}\n    role=\"status\"\n  >\n    {/* Spinner inspired by Shaka Player's buffering indicator */}\n    <div className=\" flex justify-center items-center\">\n      <svg\n        className=\"shaka-spinner-svg animate-spin h-12 w-12\"\n        viewBox=\"0 0 64 64\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <circle\n          className=\"shaka-spinner-path\"\n          cx=\"32\"\n          cy=\"32\"\n          r=\"28\"\n          strokeWidth=\"4\"\n          strokeLinecap=\"round\"\n          stroke=\"currentColor\"\n          fill=\"none\"\n          /* Create a gap so rotation is visible */\n          strokeDasharray=\"176\"\n          strokeDashoffset=\"120\"\n        />\n      </svg>\n    </div>\n    {/* <span className=\"sr-only\">Loading...</span> */}\n  </div>\n); ","import React from 'react';\n\ninterface ErrorScreenProps {\n  title?: string;\n  description?: string;\n}\n\nexport const ErrorScreen: React.FC<ErrorScreenProps> = ({ title, description }) => (\n  <div className=\"w-full h-full md:rounded-2xl! aspect-video bg-black flex\">\n    <div className=\"bg-[#151515] text-white w-full h-full flex justify-center items-center p-4\">\n      <svg\n        className=\"shrink-0 w-10 h-10 m-3 md:w-24 md:h-24 md:m-6\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeWidth=\"2\"\n        viewBox=\"0 0 24 24\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <path\n          d=\"M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z\"\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n        />\n      </svg>\n      <div>\n        <h3 className=\"text-base md:text-2xl mb-1 md:mb-2\">{title || \"Playback Error\"}</h3>\n        <div className=\"text-sm md:text-lg\">{description || \"Unable to play the video. Please try again later.\"}</div>\n      </div>\n    </div>\n  </div>\n); ","import React from 'react';\n\ninterface TitleProps {\n  title: string;\n}\n\nexport const Title: React.FC<TitleProps> = ({ title }) => (\n  <div className=\"absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-4\">\n    <h2 className=\"text-white text-xl font-semibold\">{title}</h2>\n  </div>\n); ","import React from 'react';\nimport { twMerge } from 'tailwind-merge';\n\n// Title block shown under the Event and CreativeWork players and countdown\n// cards. dateTime is the event start time or creative work release time.\nexport const TitleAndDescription = ({\n  title,\n  description,\n  dateTime,\n  locale = 'en',\n  className,\n}: {\n  title: string;\n  description: string;\n  dateTime: string;\n  locale?: string;\n  className?: string;\n}) => {\n  return (\n    <div className={twMerge(\"mt-3 mb-6 m-event-details-ctn px-4 text-left w-full\", className)}>\n      <div className=\"text-base md:text-xl m-event-title text-base-content font-medium\">{title}</div>\n      {dateTime ? (\n        <div className=\"text-sm md:text-base text-base-content/70 m-event-start-time\">\n          {new Date(dateTime || \"\").toLocaleDateString(locale || \"default\", {\n            month: \"long\",\n            year: \"numeric\",\n            day: \"numeric\",\n          })}{\" \"}\n          - {new Date(dateTime || \"\").toLocaleTimeString(locale || \"default\", {\n            hour: \"2-digit\",\n            minute: \"2-digit\",\n          })}\n        </div>\n      ) : null}\n      {description && (\n        <div className=\"text-xs md:text-sm text-base-content/60 uppercase\">{description}</div>\n      )}\n    </div>\n  );\n};\n","import { useState, useEffect } from 'react';\n\n// Import JSON files as modules\nimport en from './en.json';\nimport es from './es.json';\nimport ar from './ar.json';\nimport de from './de.json';\nimport fr from './fr.json';\nimport it from './it.json';\nimport pt from './pt.json';\nimport zh from './zh.json';\nimport nl from './nl.json';\nimport fa from './fa.json';\n\n// Define the available languages\nconst availableLanguages = {\n    en,\n    es,\n    ar,\n    de,\n    fr,\n    it,\n    pt,\n    zh,\n    nl,\n    fa\n};\n\ntype Language = keyof typeof availableLanguages;\n\n// Base translations type - this represents all possible keys that can exist\nexport type Translations = {\n  \"DAYS\": string\n  \"HOURS\": string\n  \"MINUTES\": string\n  \"SECONDS\": string\n  \"STARTING_SOON\": string\n  \"EVENT_NOT_STARTED\": string\n  \"DEFAULT_ERROR\": string\n  \"DEFAULT_ERROR_DESCRIPTION\": string\n  \"API_ERROR\": string\n  \"API_ERROR_DESCRIPTION\": string\n  \"ERROR_CODE_NOT_AUTHENTICATED\": string\n  \"ERROR_CODE_NOT_AUTHENTICATED_DESCRIPTION\": string\n  \"ERROR_CODE_GEO_BLOCKED\": string\n  \"ERROR_CODE_GEO_BLOCKED_DESCRIPTION\": string\n  \"ERROR_CODE_NOT_ENTITLED\": string\n  \"ERROR_CODE_NOT_ENTITLED_DESCRIPTION\": string\n  \"ERROR_CODE_VPN_BLOCKED\": string\n  \"ERROR_CODE_VPN_BLOCKED_DESCRIPTION\": string\n  \"ERROR_CODE_IP_CHANGED\": string\n  \"ERROR_CODE_IP_CHANGED_DESCRIPTION\": string\n  \"ERROR_CODE_VPN_DETECTED\": string\n  \"ERROR_CODE_VPN_DETECTED_DESCRIPTION\": string\n  \"ERROR_CODE_ANONYMOUS_PROXY\": string\n  \"ERROR_CODE_ANONYMOUS_PROXY_DESCRIPTION\": string\n  \"CONCURRENCY_KICKED\": string\n  \"CONCURRENCY_KICKED_DESCRIPTION\": string\n  \"CONCURRENCY_CONNECTION_FAILED\": string\n  \"CONCURRENCY_CONNECTION_FAILED_DESCRIPTION\": string\n}\n\n// Partial translations type for individual language files (allows missing keys)\ntype PartialTranslations = Partial<Translations>;\n\n// Function to get the browser's language setting\nconst getBrowserLanguage = (): Language => {\n  const language = navigator.language.split('-')[0] as Language;\n  return availableLanguages[language] ? language : 'en';\n};\n\nconst useMessages = (locale: string) => {\n  const [language, setLanguage] = useState<Language>('en');\n  const [translations, setTranslations] = useState<PartialTranslations>(availableLanguages.en);\n\n  useEffect(() => {\n    const lang = !!availableLanguages?.[locale as Language] ? locale as Language :  getBrowserLanguage();;\n    setLanguage(lang);\n    setTranslations(availableLanguages[lang] as PartialTranslations);\n  }, [locale]);\n\n  // Translation function with English fallback\n  const t = (key: keyof Translations): string => {\n    // Try current language first\n    if (translations[key]) {\n      return translations[key];\n    }\n    \n    // Fallback to English if key not found in current language\n    if (language !== 'en' && availableLanguages.en[key]) {\n      return availableLanguages.en[key];\n    }\n    \n    // Return empty string if not found in either current language or English\n    return '';\n  };\n\n  // Function to change the language manually\n  const changeLanguage = (lng: Language) => {\n    if (availableLanguages[lng]) {\n      setLanguage(lng);\n      setTranslations(availableLanguages[lng] as PartialTranslations);\n    }\n  };\n\n  return { t, language, changeLanguage };\n};\n\nexport default useMessages;\n","{\n    \"DAYS\": \"days\",\n    \"HOURS\": \"hours\",\n    \"MINUTES\": \"min\",\n    \"SECONDS\": \"sec\",\n    \"STARTING_SOON\": \"Starting soon...\",\n    \"EVENT_NOT_STARTED\": \"This event has not started...\",\n    \"DEFAULT_ERROR\": \"Playback Error\",\n    \"DEFAULT_ERROR_DESCRIPTION\": \"Unable to play the video. Please try again later.\",\n    \"API_ERROR\": \"Failed to Retrieve Video Info\",\n    \"API_ERROR_DESCRIPTION\": \"Error fetching video details. Check your videoId and internet connection and try again.\",\n    \"ERROR_CODE_NOT_AUTHENTICATED\": \"Authorisation\",\n    \"ERROR_CODE_NOT_AUTHENTICATED_DESCRIPTION\": \"You don't have permission to play this video.\",\n    \"ERROR_CODE_GEO_BLOCKED\": \"Geoblocking Restriction\",\n    \"ERROR_CODE_GEO_BLOCKED_DESCRIPTION\": \"Sorry, this video is not available in your region due to geoblocking restrictions.\",\n    \"ERROR_CODE_NOT_ENTITLED\": \"Not entitled\",\n    \"ERROR_CODE_NOT_ENTITLED_DESCRIPTION\": \"Sorry, you are not entitled to play this video.\",\n    \"ERROR_CODE_VPN_BLOCKED\": \"VPN BLOCKED\",\n    \"ERROR_CODE_VPN_BLOCKED_DESCRIPTION\": \"This video is not available due to the use of a VPN.\",\n    \"ERROR_CODE_IP_CHANGED\": \"Network Changed\",\n    \"ERROR_CODE_IP_CHANGED_DESCRIPTION\": \"You have switched to different network unexpectedly. Please try again.\",\n    \"ERROR_CODE_VPN_DETECTED\": \"VPN Detected\",\n    \"ERROR_CODE_VPN_DETECTED_DESCRIPTION\": \"We've detected a VPN. Please turn this off and try again.\",\n    \"ERROR_CODE_ANONYMOUS_PROXY\": \"Anonymous Proxy Detected\",\n    \"ERROR_CODE_ANONYMOUS_PROXY_DESCRIPTION\": \"We've detected an anonymous proxy or server. Please turn this off and try again.\",\n    \"CONCURRENCY_KICKED\": \"Device Limit Reached\",\n    \"CONCURRENCY_KICKED_DESCRIPTION\": \"You have exceeded the limit of devices allowed to watch this video at the same time.\",\n    \"CONCURRENCY_CONNECTION_FAILED\": \"Connection Error\",\n    \"CONCURRENCY_CONNECTION_FAILED_DESCRIPTION\": \"Unable to establish a connection. Please try again.\"\n}\n","{\n    \"DAYS\": \"días\",\n    \"HOURS\": \"horas\",\n    \"MINUTES\": \"min\",\n    \"SECONDS\": \"seg\",\n    \"STARTING_SOON\": \"En instantes...\",\n    \"EVENT_NOT_STARTED\": \"Este evento aún no ha comenzado...\",\n    \"DEFAULT_ERROR\": \"Error de reproducción\",\n    \"DEFAULT_ERROR_DESCRIPTION\": \"No se puede reproducir el video. Por favor, inténtelo de nuevo más tarde.\",\n    \"API_ERROR\": \"Error al recuperar información del video\",\n    \"API_ERROR_DESCRIPTION\": \"Error al obtener detalles del video. Verifique su videoId y conexión a internet e inténtelo de nuevo.\",\n    \"ERROR_CODE_NOT_AUTHENTICATED\": \"Autorización\",\n    \"ERROR_CODE_NOT_AUTHENTICATED_DESCRIPTION\": \"No tienes permiso para reproducir este video.\",\n    \"ERROR_CODE_GEO_BLOCKED\": \"Restricción geográfica\",\n    \"ERROR_CODE_GEO_BLOCKED_DESCRIPTION\": \"Lo siento, este video no está disponible en tu región debido a restricciones geográficas.\",\n    \"ERROR_CODE_NOT_ENTITLED\": \"No autorizado\",\n    \"ERROR_CODE_NOT_ENTITLED_DESCRIPTION\": \"Lo siento, no estás autorizado para reproducir este video.\",\n    \"ERROR_CODE_VPN_BLOCKED\": \"VPN BLOQUEADO\",\n    \"ERROR_CODE_VPN_BLOCKED_DESCRIPTION\": \"Este video no está disponible debido al uso de una VPN.\",\n    \"ERROR_CODE_IP_CHANGED\": \"Cambio de red\",\n    \"ERROR_CODE_IP_CHANGED_DESCRIPTION\": \"Ha cambiado de red inesperadamente. Por favor, inténtelo de nuevo.\",\n    \"ERROR_CODE_VPN_DETECTED\": \"VPN detectada\",\n    \"ERROR_CODE_VPN_DETECTED_DESCRIPTION\": \"Hemos detectado una VPN. Desactívela e inténtelo de nuevo.\",\n    \"ERROR_CODE_ANONYMOUS_PROXY\": \"Proxy anónimo detectado\",\n    \"ERROR_CODE_ANONYMOUS_PROXY_DESCRIPTION\": \"Hemos detectado un proxy anónimo o un servidor. Desactívelo e inténtelo de nuevo.\"\n}\n","{\n    \"DAYS\": \"أيام\",\n    \"HOURS\": \"ساعات\",\n    \"MINUTES\": \"دقائق\",\n    \"SECONDS\": \"ثواني\",\n    \"STARTING_SOON\": \"يبدأ قريبا...\",\n    \"EVENT_NOT_STARTED\": \"هذا الحدث لم يبدأ بعد...\",\n    \"DEFAULT_ERROR\": \"خطأ في التشغيل\",\n    \"DEFAULT_ERROR_DESCRIPTION\": \"غير قادر على تشغيل الفيديو. يرجى المحاولة مرة أخرى لاحقًا.\",\n    \"API_ERROR\": \"فشل في استرجاع معلومات الفيديو\",\n    \"API_ERROR_DESCRIPTION\": \"خطأ في جلب تفاصيل الفيديو. تحقق من معرف الفيديو واتصال الإنترنت وحاول مرة أخرى.\",\n    \"ERROR_CODE_NOT_AUTHENTICATED\": \"تفويض\",\n    \"ERROR_CODE_NOT_AUTHENTICATED_DESCRIPTION\": \"ليس لديك إذن لتشغيل هذا الفيديو.\",\n    \"ERROR_CODE_GEO_BLOCKED\": \"قيود الجغرافيا\",\n    \"ERROR_CODE_GEO_BLOCKED_DESCRIPTION\": \"عذرًا، هذا الفيديو غير متاح في منطقتك بسبب قيود الجغرافيا.\",\n    \"ERROR_CODE_NOT_ENTITLED\": \"غير مخول\",\n    \"ERROR_CODE_NOT_ENTITLED_DESCRIPTION\": \"عذرًا، ليس لديك الحق في تشغيل هذا الفيديو.\",\n    \"ERROR_CODE_VPN_BLOCKED\": \"تم حظر VPN\",\n    \"ERROR_CODE_VPN_BLOCKED_DESCRIPTION\": \"هذا الفيديو غير متاح بسبب استخدام VPN.\",\n    \"ERROR_CODE_IP_CHANGED\": \"تغيير الشبكة\",\n    \"ERROR_CODE_IP_CHANGED_DESCRIPTION\": \"لقد انتقلت إلى شبكة مختلفة بشكل غير متوقع. يرجى المحاولة مرة أخرى.\",\n    \"ERROR_CODE_VPN_DETECTED\": \"تم اكتشاف VPN\",\n    \"ERROR_CODE_VPN_DETECTED_DESCRIPTION\": \"لقد اكتشفنا وجود VPN. يرجى إيقاف تشغيله والمحاولة مرة أخرى.\",\n    \"ERROR_CODE_ANONYMOUS_PROXY\": \"تم اكتشاف بروكسي مجهول\",\n    \"ERROR_CODE_ANONYMOUS_PROXY_DESCRIPTION\": \"لقد اكتشفنا بروكسي مجهولاً أو خادماً. يرجى إيقاف تشغيله والمحاولة مرة أخرى.\",\n    \"CONCURRENCY_KICKED\": \"تم الوصول إلى الحد الأقصى للأجهزة\",\n    \"CONCURRENCY_KICKED_DESCRIPTION\": \"لقد تجاوزت الحد الأقصى للأجهزة المسموح لها بمشاهدة هذا الفيديو في نفس الوقت.\",\n    \"CONCURRENCY_CONNECTION_FAILED\": \"خطأ في الاتصال\",\n    \"CONCURRENCY_CONNECTION_FAILED_DESCRIPTION\": \"تعذر إنشاء اتصال. يرجى المحاولة مرة أخرى.\"\n}\n","{\n    \"DAYS\": \"Tage\",\n    \"HOURS\": \"Stunden\",\n    \"MINUTES\": \"Min\",\n    \"SECONDS\": \"Sek\",\n    \"STARTING_SOON\": \"Beginnt bald...\",\n    \"DEFAULT_ERROR\": \"Wiedergabefehler\",\n    \"DEFAULT_ERROR_DESCRIPTION\": \"Das Video kann nicht abgespielt werden. Bitte versuchen Sie es später erneut.\",\n    \"API_ERROR\": \"Fehler beim Abrufen der Videoinformationen\",\n    \"API_ERROR_DESCRIPTION\": \"Fehler beim Abrufen der Videodetails. Überprüfen Sie Ihre videoId und Internetverbindung und versuchen Sie es erneut.\",\n    \"ERROR_CODE_NOT_AUTHENTICATED\": \"Autorisierung\",\n    \"ERROR_CODE_NOT_AUTHENTICATED_DESCRIPTION\": \"Sie haben keine Berechtigung, dieses Video abzuspielen.\",\n    \"ERROR_CODE_GEO_BLOCKED\": \"Geoblocking-Beschränkung\",\n    \"ERROR_CODE_GEO_BLOCKED_DESCRIPTION\": \"Entschuldigung, dieses Video ist in Ihrer Region aufgrund von Geoblocking-Beschränkungen nicht verfügbar.\",\n    \"ERROR_CODE_NOT_ENTITLED\": \"Nicht berechtigt\",\n    \"ERROR_CODE_NOT_ENTITLED_DESCRIPTION\": \"Entschuldigung, Sie sind nicht berechtigt, dieses Video abzuspielen.\",\n    \"ERROR_CODE_VPN_BLOCKED\": \"VPN BLOCKIERT\",\n    \"ERROR_CODE_VPN_BLOCKED_DESCRIPTION\": \"Dieses Video ist aufgrund der Verwendung eines VPN nicht verfügbar.\",\n    \"ERROR_CODE_IP_CHANGED\": \"Netzwerk gewechselt\",\n    \"ERROR_CODE_IP_CHANGED_DESCRIPTION\": \"Sie haben unerwartet das Netzwerk gewechselt. Bitte versuchen Sie es erneut.\",\n    \"ERROR_CODE_VPN_DETECTED\": \"VPN erkannt\",\n    \"ERROR_CODE_VPN_DETECTED_DESCRIPTION\": \"Wir haben ein VPN erkannt. Bitte deaktivieren Sie es und versuchen Sie es erneut.\",\n    \"ERROR_CODE_ANONYMOUS_PROXY\": \"Anonymer Proxy erkannt\",\n    \"ERROR_CODE_ANONYMOUS_PROXY_DESCRIPTION\": \"Wir haben einen anonymen Proxy oder Server erkannt. Bitte deaktivieren Sie ihn und versuchen Sie es erneut.\",\n    \"CONCURRENCY_KICKED\": \"Gerätelimit erreicht\",\n    \"CONCURRENCY_KICKED_DESCRIPTION\": \"Sie haben die maximale Anzahl an Geräten überschritten, die dieses Video gleichzeitig ansehen dürfen.\",\n    \"CONCURRENCY_CONNECTION_FAILED\": \"Verbindungsfehler\",\n    \"CONCURRENCY_CONNECTION_FAILED_DESCRIPTION\": \"Verbindung konnte nicht hergestellt werden. Bitte versuchen Sie es erneut.\"\n}\n","{\n    \"DAYS\": \"jours\",\n    \"HOURS\": \"heures\",\n    \"MINUTES\": \"minutes\",\n    \"SECONDS\": \"secondes\",\n    \"STARTING_SOON\": \"Commence bientôt...\",\n    \"EVENT_NOT_STARTED\": \"Cet événement n'a pas commencé...\",\n    \"DEFAULT_ERROR\": \"Erreur de lecture\",\n    \"DEFAULT_ERROR_DESCRIPTION\": \"Impossible de lire la vidéo. Veuillez réessayer plus tard.\",\n    \"API_ERROR\": \"Échec de la récupération des informations vidéo\",\n    \"API_ERROR_DESCRIPTION\": \"Erreur lors de la récupération des détails de la vidéo. Vérifiez votre videoId et votre connexion Internet, puis réessayez.\",\n    \"ERROR_CODE_NOT_AUTHENTICATED\": \"Autorisation\",\n    \"ERROR_CODE_NOT_AUTHENTICATED_DESCRIPTION\": \"Vous n'avez pas la permission de lire cette vidéo.\",\n    \"ERROR_CODE_GEO_BLOCKED\": \"Restriction de géoblocage\",\n    \"ERROR_CODE_GEO_BLOCKED_DESCRIPTION\": \"Désolé, cette vidéo n'est pas disponible dans votre région en raison de restrictions de géoblocage.\",\n    \"ERROR_CODE_NOT_ENTITLED\": \"Non autorisé\",\n    \"ERROR_CODE_NOT_ENTITLED_DESCRIPTION\": \"Désolé, vous n'êtes pas autorisé à lire cette vidéo.\",\n    \"ERROR_CODE_VPN_BLOCKED\": \"VPN BLOQUÉ\",\n    \"ERROR_CODE_VPN_BLOCKED_DESCRIPTION\": \"Cette vidéo n'est pas disponible en raison de l'utilisation d'un VPN.\",\n    \"ERROR_CODE_IP_CHANGED\": \"Changement de réseau\",\n    \"ERROR_CODE_IP_CHANGED_DESCRIPTION\": \"Vous avez changé de réseau de manière inattendue. Veuillez réessayer.\",\n    \"ERROR_CODE_VPN_DETECTED\": \"VPN détecté\",\n    \"ERROR_CODE_VPN_DETECTED_DESCRIPTION\": \"Nous avons détecté un VPN. Veuillez le désactiver et réessayer.\",\n    \"ERROR_CODE_ANONYMOUS_PROXY\": \"Proxy anonyme détecté\",\n    \"ERROR_CODE_ANONYMOUS_PROXY_DESCRIPTION\": \"Nous avons détecté un proxy anonyme ou un serveur. Veuillez le désactiver et réessayer.\"\n}\n","{\n    \"DAYS\": \"giorni\",\n    \"HOURS\": \"ore\",\n    \"MINUTES\": \"minuti\",\n    \"SECONDS\": \"secondi\",\n    \"STARTING_SOON\": \"Inizia presto...\",\n    \"DEFAULT_ERROR\": \"Errore di riproduzione\",\n    \"DEFAULT_ERROR_DESCRIPTION\": \"Impossibile riprodurre il video. Per favore riprova più tardi.\",\n    \"API_ERROR\": \"Errore nel recupero delle informazioni video\",\n    \"API_ERROR_DESCRIPTION\": \"Errore nel recupero dei dettagli del video. Controlla il tuo videoId e la connessione internet e riprova.\",\n    \"ERROR_CODE_NOT_AUTHENTICATED\": \"Autorizzazione\",\n    \"ERROR_CODE_NOT_AUTHENTICATED_DESCRIPTION\": \"Non hai il permesso di riprodurre questo video.\",\n    \"ERROR_CODE_GEO_BLOCKED\": \"Restrizione geoblocking\",\n    \"ERROR_CODE_GEO_BLOCKED_DESCRIPTION\": \"Spiacenti, questo video non è disponibile nella tua regione a causa di restrizioni geografiche.\",\n    \"ERROR_CODE_NOT_ENTITLED\": \"Non autorizzato\",\n    \"ERROR_CODE_NOT_ENTITLED_DESCRIPTION\": \"Spiacenti, non sei autorizzato a riprodurre questo video.\",\n    \"ERROR_CODE_VPN_BLOCKED\": \"VPN BLOCCATA\",\n    \"ERROR_CODE_VPN_BLOCKED_DESCRIPTION\": \"Questo video non è disponibile a causa dell'uso di una VPN.\",\n    \"ERROR_CODE_IP_CHANGED\": \"Cambio di rete\",\n    \"ERROR_CODE_IP_CHANGED_DESCRIPTION\": \"Sei passato inaspettatamente a un'altra rete. Per favore riprova.\",\n    \"ERROR_CODE_VPN_DETECTED\": \"VPN rilevata\",\n    \"ERROR_CODE_VPN_DETECTED_DESCRIPTION\": \"Abbiamo rilevato una VPN. Disattivala e riprova.\",\n    \"ERROR_CODE_ANONYMOUS_PROXY\": \"Proxy anonimo rilevato\",\n    \"ERROR_CODE_ANONYMOUS_PROXY_DESCRIPTION\": \"Abbiamo rilevato un proxy anonimo o un server. Disattivalo e riprova.\"\n}\n","{\n    \"DAYS\": \"dias\",\n    \"HOURS\": \"horas\",\n    \"MINUTES\": \"minutos\",\n    \"SECONDS\": \"segundos\",\n    \"STARTING_SOON\": \"Começando em breve...\",\n    \"DEFAULT_ERROR\": \"Playback Error\",\n    \"DEFAULT_ERROR_DESCRIPTION\": \"Não foi possível reproduzir o vídeo. Por favor, tente novamente mais tarde.\",\n    \"API_ERROR\": \"Falha ao Recuperar Informações do Vídeo\",\n    \"API_ERROR_DESCRIPTION\": \"Erro ao buscar detalhes do vídeo. Verifique seu videoId e conexão com a internet e tente novamente.\",\n    \"ERROR_CODE_NOT_AUTHENTICATED\": \"Autorização\",\n    \"ERROR_CODE_NOT_AUTHENTICATED_DESCRIPTION\": \"Você não tem permissão para reproduzir este vídeo.\",\n    \"ERROR_CODE_GEO_BLOCKED\": \"Restrição de Geoblocking\",\n    \"ERROR_CODE_GEO_BLOCKED_DESCRIPTION\": \"Desculpe, este vídeo não está disponível em sua região devido a restrições de geoblocking.\",\n    \"ERROR_CODE_NOT_ENTITLED\": \"Não autorizado\",\n    \"ERROR_CODE_NOT_ENTITLED_DESCRIPTION\": \"Desculpe, você não está autorizado a reproduzir este vídeo.\",\n    \"ERROR_CODE_VPN_BLOCKED\": \"VPN BLOQUEADO\",\n    \"ERROR_CODE_VPN_BLOCKED_DESCRIPTION\": \"Este vídeo não está disponível devido ao uso de uma VPN.\",\n    \"ERROR_CODE_IP_CHANGED\": \"Mudança de Rede\",\n    \"ERROR_CODE_IP_CHANGED_DESCRIPTION\": \"Você mudou de rede inesperadamente. Por favor, tente novamente.\",\n    \"ERROR_CODE_VPN_DETECTED\": \"VPN Detectada\",\n    \"ERROR_CODE_VPN_DETECTED_DESCRIPTION\": \"Detectamos uma VPN. Por favor, desative-a e tente novamente.\",\n    \"ERROR_CODE_ANONYMOUS_PROXY\": \"Proxy Anônimo Detectado\",\n    \"ERROR_CODE_ANONYMOUS_PROXY_DESCRIPTION\": \"Detectamos um proxy anônimo ou servidor. Por favor, desative-o e tente novamente.\"\n}\n","{\n    \"DAYS\": \"天\",\n    \"HOURS\": \"小时\",\n    \"MINUTES\": \"分钟\",\n    \"SECONDS\": \"秒\",\n    \"STARTING_SOON\": \"即将开始...\",\n    \"DEFAULT_ERROR\": \"播放错误\",\n    \"DEFAULT_ERROR_DESCRIPTION\": \"无法播放视频。请稍后再试。\",\n    \"API_ERROR\": \"获取视频信息失败\",\n    \"API_ERROR_DESCRIPTION\": \"获取视频详情时出错。请检查您的视频ID和互联网连接，然后重试。\",\n    \"ERROR_CODE_NOT_AUTHENTICATED\": \"授权\",\n    \"ERROR_CODE_NOT_AUTHENTICATED_DESCRIPTION\": \"您没有权限播放此视频。\",\n    \"ERROR_CODE_GEO_BLOCKED\": \"地理封锁限制\",\n    \"ERROR_CODE_GEO_BLOCKED_DESCRIPTION\": \"抱歉，由于地理封锁限制，此视频在您的地区不可用。\",\n    \"ERROR_CODE_NOT_ENTITLED\": \"无权播放\",\n    \"ERROR_CODE_NOT_ENTITLED_DESCRIPTION\": \"抱歉，您无权播放此视频。\",\n    \"ERROR_CODE_VPN_BLOCKED\": \"VPN被封锁\",\n    \"ERROR_CODE_VPN_BLOCKED_DESCRIPTION\": \"由于使用VPN，此视频不可用。\",\n    \"ERROR_CODE_IP_CHANGED\": \"网络已更改\",\n    \"ERROR_CODE_IP_CHANGED_DESCRIPTION\": \"您意外切换到了其他网络。请重试。\",\n    \"ERROR_CODE_VPN_DETECTED\": \"检测到VPN\",\n    \"ERROR_CODE_VPN_DETECTED_DESCRIPTION\": \"我们检测到VPN。请将其关闭后重试。\",\n    \"ERROR_CODE_ANONYMOUS_PROXY\": \"检测到匿名代理\",\n    \"ERROR_CODE_ANONYMOUS_PROXY_DESCRIPTION\": \"我们检测到匿名代理或服务器。请将其关闭后重试。\"\n}\n","{\n    \"DAYS\": \"dagen\",\n    \"HOURS\": \"uren\",\n    \"MINUTES\": \"min\",\n    \"SECONDS\": \"sec\",\n    \"STARTING_SOON\": \"Begint binnenkort...\",\n    \"EVENT_NOT_STARTED\": \"Dit evenement is nog niet begonnen...\",\n    \"DEFAULT_ERROR\": \"Afspelfout\",\n    \"DEFAULT_ERROR_DESCRIPTION\": \"Kan de video niet afspelen. Probeer het later opnieuw.\",\n    \"API_ERROR\": \"Kan videogegevens niet ophalen\",\n    \"API_ERROR_DESCRIPTION\": \"Fout bij het ophalen van videogegevens. Controleer uw video-ID en internetverbinding en probeer het opnieuw.\",\n    \"ERROR_CODE_NOT_AUTHENTICATED\": \"Autorisatie\",\n    \"ERROR_CODE_NOT_AUTHENTICATED_DESCRIPTION\": \"U heeft geen toestemming om deze video af te spelen.\",\n    \"ERROR_CODE_GEO_BLOCKED\": \"Geoblocking Beperking\",\n    \"ERROR_CODE_GEO_BLOCKED_DESCRIPTION\": \"Sorry, deze video is niet beschikbaar in uw regio vanwege geoblocking beperkingen.\",\n    \"ERROR_CODE_NOT_ENTITLED\": \"Geen recht\",\n    \"ERROR_CODE_NOT_ENTITLED_DESCRIPTION\": \"Sorry, u heeft geen recht om deze video af te spelen.\",\n    \"ERROR_CODE_VPN_BLOCKED\": \"VPN GEBLOKKEERD\",\n    \"ERROR_CODE_VPN_BLOCKED_DESCRIPTION\": \"Deze video is niet beschikbaar vanwege het gebruik van een VPN.\",\n    \"ERROR_CODE_IP_CHANGED\": \"Netwerk gewijzigd\",\n    \"ERROR_CODE_IP_CHANGED_DESCRIPTION\": \"U bent onverwacht overgeschakeld naar een ander netwerk. Probeer het opnieuw.\",\n    \"ERROR_CODE_VPN_DETECTED\": \"VPN gedetecteerd\",\n    \"ERROR_CODE_VPN_DETECTED_DESCRIPTION\": \"We hebben een VPN gedetecteerd. Schakel deze uit en probeer het opnieuw.\",\n    \"ERROR_CODE_ANONYMOUS_PROXY\": \"Anonieme proxy gedetecteerd\",\n    \"ERROR_CODE_ANONYMOUS_PROXY_DESCRIPTION\": \"We hebben een anonieme proxy of server gedetecteerd. Schakel deze uit en probeer het opnieuw.\"\n}\n","{\n    \"DAYS\": \"روزها\",\n    \"HOURS\": \"ساعت‌ها\",\n    \"MINUTES\": \"دقیقه‌ها\",\n    \"SECONDS\": \"ثانیه‌ها\",\n    \"STARTING_SOON\": \"به زودی شروع می‌شود...\",\n    \"EVENT_NOT_STARTED\": \"این رویداد هنوز شروع نشده است...\",\n    \"DEFAULT_ERROR\": \"خطای پخش\",\n    \"DEFAULT_ERROR_DESCRIPTION\": \"امکان پخش ویدیو وجود ندارد. لطفاً بعداً دوباره تلاش کنید.\",\n    \"API_ERROR\": \"خطا در بازیابی اطلاعات ویدیو\",\n    \"API_ERROR_DESCRIPTION\": \"خطا در دریافت جزئیات ویدیو. ویدیوId و اتصال اینترنت خود را بررسی کرده و دوباره تلاش کنید.\",\n    \"ERROR_CODE_NOT_AUTHENTICATED\": \"مجوز\",\n    \"ERROR_CODE_NOT_AUTHENTICATED_DESCRIPTION\": \"شما اجازه پخش این ویدیو را ندارید.\",\n    \"ERROR_CODE_GEO_BLOCKED\": \"محدودیت جغرافیایی\",\n    \"ERROR_CODE_GEO_BLOCKED_DESCRIPTION\": \"متاسفیم، این ویدیو به دلیل محدودیت‌های جغرافیایی در منطقه شما در دسترس نیست.\",\n    \"ERROR_CODE_NOT_ENTITLED\": \"عدم دسترسی\",\n    \"ERROR_CODE_NOT_ENTITLED_DESCRIPTION\": \"متاسفیم، شما اجازه پخش این ویدیو را ندارید.\",\n    \"ERROR_CODE_VPN_BLOCKED\": \"مسدود شدن VPN\",\n    \"ERROR_CODE_VPN_BLOCKED_DESCRIPTION\": \"این ویدیو به دلیل استفاده از VPN در دسترس نیست.\",\n    \"ERROR_CODE_IP_CHANGED\": \"تغییر شبکه\",\n    \"ERROR_CODE_IP_CHANGED_DESCRIPTION\": \"شما به طور غیرمنتظره به شبکه دیگری متصل شده‌اید. لطفاً دوباره تلاش کنید.\",\n    \"ERROR_CODE_VPN_DETECTED\": \"VPN شناسایی شد\",\n    \"ERROR_CODE_VPN_DETECTED_DESCRIPTION\": \"ما یک VPN شناسایی کرده‌ایم. لطفاً آن را خاموش کرده و دوباره تلاش کنید.\",\n    \"ERROR_CODE_ANONYMOUS_PROXY\": \"پروکسی ناشناس شناسایی شد\",\n    \"ERROR_CODE_ANONYMOUS_PROXY_DESCRIPTION\": \"ما یک پروکسی ناشناس یا سرور شناسایی کرده‌ایم. لطفاً آن را خاموش کرده و دوباره تلاش کنید.\"\n}\n","import React, { useEffect } from 'react';\nimport { twMerge } from 'tailwind-merge';\nimport { useQuery } from '@tanstack/react-query';\nimport { Player } from './Player';\nimport type { PlayerProps } from './types';\nimport { fetchVideoData, type VideoData } from './api';\nimport { Loading, ErrorScreen, Title } from './components';\n\n// Import helper functions\nimport { findHLSPlaylist, getErrorType } from './helper';\nimport useMessages, { type Translations } from './messages/useMessages';\n\nexport interface VideoProps extends Omit<PlayerProps, 'src'> {\n  videoId?: string;\n  publicKey?: string;\n  videoData?: VideoData;\n  refetchInterval?: number;\n  playerName?: string;\n  locale?: string;\n  events?: {\n    onVideoData?: (video: VideoData) => void;\n    onEmptyPlaylists?: () => void;\n    onError?: (error: Error) => void;\n    onPlay?: () => void;\n    onPause?: () => void;\n    onEnded?: () => void;\n    onLoadStart?: () => void;\n    onCanPlay?: () => void;\n    /** Fires once the Shaka player instance is created (before manifest load).\n     * The instance exposes `player.mux.updateData(...)` for mid-view metadata\n     * updates (e.g. attaching consent-gated ids after a CMP grant). */\n    onPlayerReady?: (player: any) => void;\n    onAdStart?: () => void;\n    onAdComplete?: () => void;\n    onAdError?: (error: any) => void;\n    onAdSkipped?: () => void;\n    onAdPaused?: () => void;\n    onAdResumed?: () => void;\n    onAdProgress?: (adProgressData: any) => void;\n    onAllAdsCompleted?: () => void;\n  };\n  children?: React.ReactNode;\n  className?: string;\n  auth?: {\n    mottoToken?: string;\n    userId?: string;\n  };\n  settings?: {\n    backgroundImageUrl?: string;\n  };\n  // TanStack Query options\n  queryOptions?: {\n    enabled?: boolean;\n    staleTime?: number;\n    cacheTime?: number;\n    retry?: number;\n    retryDelay?: number;\n  };\n  adsEnabled?: boolean;\n}\n\nexport const Video: React.FC<VideoProps> = ({\n  videoId,\n  publicKey,\n  videoData: providedVideoData,\n  refetchInterval = 0,\n  playerName,\n  locale = 'en',\n  events,\n  children,\n  className,\n  auth,\n  settings,\n  queryOptions = {},\n  adsEnabled = false,\n  ...props\n}) => {\n  // Use TanStack Query for data fetching\n  const {\n    data,\n    isLoading,\n    error,\n    refetch\n  } = useQuery<VideoData>({\n    queryKey: ['video', videoId, publicKey, auth?.mottoToken, adsEnabled, locale],\n    queryFn: () => fetchVideoData(videoId!, publicKey!, auth?.mottoToken, adsEnabled, locale),\n    enabled: !!videoId && !!publicKey && !providedVideoData,\n    refetchInterval: refetchInterval > 0 ? refetchInterval : false,\n    staleTime: queryOptions.staleTime ?? 5 * 60 * 1000, // 5 minutes\n    gcTime: queryOptions.cacheTime ?? 10 * 60 * 1000, // 10 minutes (was cacheTime in v4)\n    retry: queryOptions.retry ?? 3,\n    retryDelay: queryOptions.retryDelay ?? ((attemptIndex: number) => Math.min(1000 * 2 ** attemptIndex, 30000))\n  });\n\n  const video = providedVideoData || data;\n  const { t } = useMessages(locale);\n  \n  // Find HLS playlist using helper function\n  const activePlaylist = findHLSPlaylist(video);\n  const activePlaylistUrl = (\n      activePlaylist?.url ??\n      activePlaylist?.drm?.widevine?.playlistUrl ??\n      activePlaylist?.drm?.playready?.playlistUrl ??\n      activePlaylist?.drm?.fairplay?.playlistUrl);\n  const activePlaylistHasUrl = !!activePlaylistUrl;\n\n  // Notify parent of video data\n  useEffect(() => {\n    if (events?.onVideoData && video) {\n      events.onVideoData(video);\n    }\n  }, [video, events]);\n\n  // Loading state\n  if (isLoading || (!providedVideoData && !video)) {\n    return (\n      <div className={twMerge(\"md:rounded-2xl overflow-hidden aspect-video\", className)}>\n        <div className=\"relative w-full h-full bg-[#151515]\">\n          <Loading />\n        </div>\n      </div>\n    );\n  }\n\n  // Handle empty playlists\n  if (!isLoading && video && !activePlaylistHasUrl && events?.onEmptyPlaylists) {\n    events.onEmptyPlaylists();\n  }\n\n  // Error state\n  if (error || video?.error) {\n    const errorKey = video?.error ? getErrorType(undefined, video) : 'API_ERROR';\n    const errorObj = error || new Error(video?.error || 'Unknown error');\n    \n    if (events?.onError) {\n      events.onError(errorObj);\n    }\n\n    // Use localized error messages\n    const title = t(errorKey as keyof Translations) || t('DEFAULT_ERROR');\n    const description = t(`${errorKey}_DESCRIPTION` as keyof Translations) || t('DEFAULT_ERROR_DESCRIPTION');\n\n    return (\n      <div className={twMerge(\"md:rounded-2xl overflow-hidden aspect-video\", className)}>\n        <div className=\"relative w-full h-full\">\n          <ErrorScreen\n            title={title}\n            description={description}\n          />\n          {children}\n        </div>\n      </div>\n    );\n  }\n\n  // No playable source or missing playlist\n  if (!activePlaylist || !activePlaylistHasUrl) {\n    return (\n      <div className={twMerge(\"md:rounded-2xl overflow-hidden aspect-video\", className)}>\n        <div className=\"relative w-full h-full bg-[#151515]\">\n          <Title title={video?.name || \"\"} />\n          {children}\n        </div>\n      </div>\n    );\n  }\n\n  // Render video player with source\n  return (\n    <div className={twMerge(\"md:rounded-2xl overflow-hidden aspect-video\", className)}>\n      <div className=\"relative w-full h-full\">\n        <Player\n          {...props}\n          src={activePlaylist}\n          managedMode={true}\n          className={twMerge(\"video-player-container\", className)}\n          events={events}\n          locale={locale}\n          containerClassName=\"w-full h-full\"\n          publicKey={publicKey}\n          auth={auth}\n          {...(adsEnabled && video?.ad?.adTagUrl ? { imaConfig: { adTagUrl: video?.ad?.adTagUrl } } : {})}\n        >\n          {children}\n        </Player>\n      </div>\n    </div>\n  );\n}; ","import { createClient } from '@connectrpc/connect';\nimport { timestampDate } from '@bufbuild/protobuf/wkt';\nimport { EventService } from '@buf/motto_cda.bufbuild_es/motto/cda/cms/event/v1/service_pb';\nimport { cdaTransport, authHeaders } from './transport';\n\nexport interface EventData {\n  id: string;\n  title: string;\n  description?: string;\n  startTime: string;\n  endTime?: string;\n  posterUrl?: string;\n  videoIds?: string[];\n  error?: string;\n}\n\n\n\nexport enum EventsSortDirection {\n  ASC = 'asc',\n  DESC = 'desc'\n}\n\nconst eventClient = createClient(EventService, cdaTransport);\n\nexport async function fetchEventData(\n  publicKey: string,\n  eventId: string,\n  unused?: any,\n  filter?: string,\n  order?: EventsSortDirection,\n  locale?: string\n): Promise<EventData> {\n  // filter/order are not part of GetEventRequest in the CDA schema; the old\n  // fetch-based code sent them but the server ignored them.\n  const response = await eventClient.getEvent(\n    {\n      eventId,\n      ...(locale?.length ? { locale } : {}),\n    },\n    { headers: authHeaders(publicKey) }\n  );\n\n  const event = response.event;\n  if (!event) {\n    throw new Error('Failed to fetch event data: empty response');\n  }\n\n  return {\n    id: event.id,\n    title: event.title,\n    description: event.description || undefined,\n    startTime: event.startTime ? timestampDate(event.startTime).toISOString() : '',\n    posterUrl: event.posterUrl || undefined,\n    videoIds: event.videoIds,\n  };\n}\n","import { createClient } from '@connectrpc/connect';\nimport { timestampDate } from '@bufbuild/protobuf/wkt';\nimport { CreativeWorkService } from '@buf/motto_cda.bufbuild_es/motto/cda/cms/creative_work/v1/service_pb';\nimport { cdaTransport, authHeaders } from './transport';\n\nexport interface CreativeWorkData {\n  id: string;\n  title: string;\n  description?: string;\n  releaseTime: string;\n  endTime?: string;\n  posterUrl?: string;\n  videoIds?: string[];\n  error?: string;\n}\n\nexport enum CreativeWorksSortDirection {\n  ASC = 'asc',\n  DESC = 'desc'\n}\n\nconst creativeWorkClient = createClient(CreativeWorkService, cdaTransport);\n\nexport async function fetchCreativeWorkData(\n  publicKey: string,\n  creativeWorkId: string,\n  unused?: any,\n  filter?: string,\n  order?: CreativeWorksSortDirection,\n  locale?: string\n): Promise<CreativeWorkData> {\n  // filter/order are not part of GetCreativeWorkRequest in the CDA schema; the\n  // old fetch-based code sent them but the server ignored them.\n  const response = await creativeWorkClient.getCreativeWork(\n    {\n      creativeWorkId,\n      ...(locale?.length ? { locale } : {}),\n    },\n    { headers: authHeaders(publicKey) }\n  );\n\n  const creativeWork = response.creativeWork;\n  if (!creativeWork) {\n    throw new Error('Failed to fetch creative work data: empty response');\n  }\n\n  return {\n    id: creativeWork.id,\n    title: creativeWork.title,\n    description: creativeWork.description || undefined,\n    releaseTime: creativeWork.releaseTime ? timestampDate(creativeWork.releaseTime).toISOString() : '',\n    posterUrl: creativeWork.posterUrl || undefined,\n    videoIds: creativeWork.videoIds,\n  };\n}\n","import type { Playlist, VideoData } from './api';\n\nconst defaultError = {\n  title: \"Playback Error\",\n  description: \"Unable to play the video. Please try again later.\",\n};\n\nexport const errorTypes: Record<\n  \"api\" | \"default\" | string,\n  {\n    title: string;\n    description: string;\n  }\n> = {\n  api: {\n    title: \"Failed to Retrieve Video Info\",\n    description:\n      \"Error fetching video details. Check your videoId and internet connection and try again.\",\n  },\n  ERROR_CODE_NOT_AUTHENTICATED: {\n    title: \"Authorisation\",\n    description: \"You don't have permission to play this video. \",\n  },\n  ERROR_CODE_GEO_BLOCKED: {\n    title: \"Geoblocking Restriction\",\n    description:\n      \"Sorry, this video is not available in your region due to geoblocking restrictions.\",\n  },\n  ERROR_CODE_NOT_ENTITLED: {\n    title: \"Not entitled\",\n    description: \"Sorry, you are not entitled to play this video.\",\n  },\n  ERROR_CODE_VPN_BLOCKED: {\n    title: \"VPN BLOCKED\",\n    description: \"This video is not available due to the use of a VPN.\",\n  },\n  ERROR_CODE_UNSPECIFIED: defaultError,\n  ERROR_CODE_SIGNATURE_MISMATCH: defaultError,\n  ERROR_CODE_INVALID_DOMAIN: defaultError,\n  default: defaultError,\n};\n\nexport const getMediaTypeFromSource = (\n  source: string\n): \"video/mp4\" | \"application/vnd.apple.mpegurl\" | \"video/x-flv\" => {\n  if (source.includes(\".mp4\")) {\n    return \"video/mp4\";\n  }\n  if (source.includes(\".m3u8\")) {\n    return \"application/vnd.apple.mpegurl\";\n  }\n  return \"video/x-flv\"; // Default return\n};\n\nexport const getSourceObject = ({\n  source,\n}: {\n  source: string;\n}): {\n  src: string;\n  type: string;\n  withCredentials: boolean;\n} => {\n  const mediaType = getMediaTypeFromSource(source);\n\n  return {\n    src: source,\n    type: mediaType,\n    withCredentials: false, // Set to false as per the original comment about CORS issues\n  };\n};\n\nexport const formatTime = (seconds = 0): string => {\n  const ss = seconds < 0 ? 0 : seconds;\n  let s: number | string = Math.floor(ss % 60);\n  let m: number | string = Math.floor((ss / 60) % 60);\n  let h: number | string = Math.floor(ss / 3600);\n\n  if (isNaN(ss) || ss === Infinity) {\n    s = \"-\";\n    m = \"-\";\n    h = \"-\";\n  }\n\n  h = Number(h) > 0 ? `${h}:` : \"\";\n  m = `${Number(m) < 10 ? `0${m}` : m}:`;\n  s = Number(s) < 10 ? `0${s}` : s;\n\n  return h + m + s;\n};\n\nexport const findHLSPlaylist = (video?: VideoData): Playlist | undefined => {\n  return video?.playlists?.find((playlist) => {\n    return playlist.format === 'HLS' || \n           playlist.format === 'hls' || \n           playlist.format === 'PLAYLIST_FORMAT_HLS';\n  });\n};\n\nexport const getErrorType = (error?: string | Error, video?: VideoData) => {\n  if (video?.error && errorTypes[video.error]) {\n    return video.error;\n  }\n  return 'DEFAULT_ERROR';\n};\n","import React, { CSSProperties, useCallback, useEffect, useMemo, useState } from 'react';\nimport { twMerge } from 'tailwind-merge';\nimport { useQuery } from '@tanstack/react-query';\nimport { Player } from './Player';\nimport type { PlayerProps } from './types';\nimport { fetchEventData, type EventData, EventsSortDirection } from './api/event';\nimport { fetchVideosList, Playlist, type VideoData, type ConcurrencySession, ConcurrencyConnectionState } from './api/video';\nimport { Loading, ErrorScreen, TitleAndDescription } from './components';\nimport { findHLSPlaylist } from './helper';\nimport useMessages, { type Translations } from './messages/useMessages';\nimport { useConcurrencySession, type SessionStartedInfo } from './hooks/useConcurrencySession';\nimport { getDeviceId } from './utils/deviceId';\n\n\nexport interface EventProps extends Omit<PlayerProps, 'src' | 'drmConfig'> {\n  publicKey: string;\n  eventId: string;\n  hideTitle?: boolean;\n  locale?: string;\n  filter?: string;\n  order?: EventsSortDirection;\n  events?: {\n    onEventData?: (event: EventData) => void;\n    onVideoData?: (video: VideoData) => void;\n    onEmptyPlaylists?: () => void;\n    onError?: (error: Error) => void;\n    onPlay?: () => void;\n    onPause?: () => void;\n    onEnded?: () => void;\n    onLoadStart?: () => void;\n    onCanPlay?: () => void;\n    /** Fires once the Shaka player instance is created (before manifest load).\n     * The instance exposes `player.mux.updateData(...)` for mid-view metadata\n     * updates (e.g. attaching consent-gated ids after a CMP grant). */\n    onPlayerReady?: (player: any) => void;\n    onAdStart?: () => void;\n    onAdComplete?: () => void;\n    onAdError?: (error: any) => void;\n    onAdSkipped?: () => void;\n    onAdPaused?: () => void;\n    onAdResumed?: () => void;\n    onAdProgress?: (adProgressData: any) => void;\n    onAllAdsCompleted?: () => void;\n    // Concurrency session events\n    /** Called when concurrency session is established and playback can begin */\n    onConcurrencySessionStarted?: (info: SessionStartedInfo) => void;\n    /** Called when session is kicked - playback will be stopped */\n    onConcurrencyKicked?: (reason: string, message: string) => void;\n    /** Called when the number of active sessions changes */\n    onConcurrencySessionCountChanged?: (activeSessions: number, limit: number) => void;\n    /** Called when concurrency connection state changes */\n    onConcurrencyStateChanged?: (state: ConcurrencyConnectionState) => void;\n  };\n  className?: string;\n  settings?: {\n    backgroundImageUrl?: string;\n    showCountdownAnimation?: boolean;\n  };\n  auth?: {\n    mottoToken?: string;\n    userId?: string;\n  };\n  // TanStack Query options\n  queryOptions?: {\n    enabled?: boolean;\n    staleTime?: number;\n    cacheTime?: number;\n    retry?: number;\n    retryDelay?: number;\n  };\n  adsEnabled?: boolean;\n}\n\nexport const Event: React.FC<EventProps> = ({\n  publicKey,\n  eventId,\n  events,\n  hideTitle,\n  locale = 'en',\n  order,\n  filter,\n  className,\n  settings,\n  auth,\n  deviceId,\n  queryOptions = {},\n  adsEnabled = false,\n  ...props\n}) => {\n  // Get the effective deviceId - use provided one or auto-generate\n  // This must be consistent between the API call and WebSocket connection\n  const effectiveDeviceId = useMemo(() => getDeviceId(deviceId), [deviceId]);\n\n  // Event data fetching\n  const {\n    data: eventData,\n    isLoading: isEventLoading,\n    error: eventError\n  } = useQuery<EventData>({\n    queryKey: ['event', publicKey, eventId, filter, order, locale],\n    queryFn: () => fetchEventData(publicKey, eventId, undefined, filter, order, locale),\n    enabled: !!publicKey && !!eventId,\n    staleTime: queryOptions.staleTime ?? 5 * 60 * 1000, // 5 minutes\n    gcTime: queryOptions.cacheTime ?? 10 * 60 * 1000, // 10 minutes\n    retry: queryOptions.retry ?? 3,\n    retryDelay: queryOptions.retryDelay ?? ((attemptIndex: number) => Math.min(1000 * 2 ** attemptIndex, 30000))\n  });\n\n  const [activePlaylist, setActivePlaylist] = useState<Playlist | null>();\n  const [activeVideoId, setActiveVideoId] = useState<string | null>();\n  const [activeConcurrencySession, setActiveConcurrencySession] = useState<ConcurrencySession | undefined>();\n  const videoIds = eventData?.videoIds ?? [];\n  // Videos list fetching\n  const {\n    data: videosData,\n    isLoading: videosIsLoading,\n    error: videosError,\n  } = useQuery<VideoData[]>({\n    queryKey: ['videos-list', publicKey, videoIds, auth?.mottoToken, adsEnabled, locale, effectiveDeviceId],\n    queryFn: () => fetchVideosList(publicKey, videoIds, auth?.mottoToken, 0, 0, adsEnabled, locale, effectiveDeviceId),\n    enabled: !!publicKey && videoIds.length > 0,\n    refetchInterval: activePlaylist === null ? 30000 : false,\n    staleTime: queryOptions.staleTime ?? 5 * 60 * 1000,\n    gcTime: queryOptions.cacheTime ?? 10 * 60 * 1000,\n    retry: queryOptions.retry ?? 3,\n    retryDelay: queryOptions.retryDelay ?? ((attemptIndex: number) => Math.min(1000 * 2 ** attemptIndex, 30000))\n  });\n\n  const [loadingApisState, setLoadingApisState] = useState(true);\n\n  // Process videos data to find active playlist\n  useEffect(() => {\n    if (videosData !== undefined) {\n      setLoadingApisState(false);\n\n      const videosWithPlaylists = videosData.filter(\n        (video) => video.playlists && video.playlists.length > 0\n      );\n\n      if (videosWithPlaylists.length > 0) {\n        let hlsPlaylistFound = false;\n        // Iterate to find the first HLS playlist\n        for (const video of videosWithPlaylists) {\n          const activePlaylist = findHLSPlaylist(video);\n          const activePlaylistUrl = (\n              activePlaylist?.url ??\n              activePlaylist?.drm?.widevine?.playlistUrl ??\n              activePlaylist?.drm?.playready?.playlistUrl ??\n              activePlaylist?.drm?.fairplay?.playlistUrl);\n          const activePlaylistHasUrl = !!activePlaylistUrl;\n\n          if (activePlaylist && activePlaylistHasUrl) {\n            setActivePlaylist(activePlaylist);\n            setActiveVideoId(video.id);\n            setActiveConcurrencySession(video.concurrencySession);\n            hlsPlaylistFound = true;\n            break;\n          }\n        }\n        if (!hlsPlaylistFound) {\n          setActivePlaylist(null);\n          setActiveVideoId(null);\n          setActiveConcurrencySession(undefined);\n        }\n      } else {\n        setActivePlaylist(null);\n        setActiveVideoId(null);\n        setActiveConcurrencySession(undefined);\n      }\n    } else if (eventData && (!eventData.videoIds || eventData.videoIds.length === 0)) {\n      setLoadingApisState(false);\n      setActivePlaylist(null);\n      setActiveVideoId(null);\n      setActiveConcurrencySession(undefined);\n    }\n  }, [videosData, eventData]);\n  \n  const { t } = useMessages(locale);\n\n  // ============================================================================\n  // Concurrency Session Management\n  // ============================================================================\n\n  /**\n   * Connect to concurrency session WebSocket if required.\n   *\n   * Security: The hook will enforce that playback is only allowed when\n   * the WebSocket connection is established (if concurrency control is required).\n   * If connection fails or user is kicked, isPlaybackAllowed will be false.\n   */\n  const concurrencyOptions = useMemo(() => ({\n    // Use the same deviceId that was sent to the API\n    deviceId: effectiveDeviceId,\n    // Forward concurrency events to parent\n    onSessionStarted: events?.onConcurrencySessionStarted,\n    onKicked: events?.onConcurrencyKicked,\n    onSessionCountChanged: events?.onConcurrencySessionCountChanged,\n    onStateChanged: events?.onConcurrencyStateChanged,\n    onError: (err: Error) => {\n      console.error('[Event] Concurrency session error:', err);\n      events?.onError?.(err);\n    },\n  }), [effectiveDeviceId, events]);\n\n  const {\n    isPlaybackAllowed,\n    wasKicked,\n    kickedReason,\n    connectionState: concurrencyConnectionState,\n  } = useConcurrencySession(activeConcurrencySession, concurrencyOptions);\n\n  // Notify parent of event data\n  useEffect(() => {\n    if (events?.onEventData && eventData) {\n      events.onEventData(eventData);\n    }\n  }, [eventData, events]);\n\n  // Notify parent of video data\n  useEffect(() => {\n    if (events?.onVideoData && activeVideoId && videosData) {\n      const activeVideo = videosData.find((video) => video.id === activeVideoId);\n      if (activeVideo) {\n        events.onVideoData(activeVideo);\n      }\n    }\n  }, [activeVideoId, videosData, events]);\n\n  // All state and hooks must be defined before any conditional returns\n  const [error, setError] = useState<Error | null>(null);\n  const [loadingPlaylist, setLoadingPlaylist] = useState(true);\n  const videosDataError = videosData?.some((video) => !!video.error);\n\n  // Error handling\n  useEffect(() => {\n    // Defer error evaluation until loading states settle to avoid flashing\n    // error. Query-level errors must NOT be deferred: they are terminal\n    // (retries exhausted) and nothing clears loadingApisState when a fetch\n    // fails, so waiting on it would show the loading screen forever.\n    const hasFetchError = Boolean(eventError || videosError);\n    if (!hasFetchError && (isEventLoading || videosIsLoading || loadingApisState)) {\n      return;\n    }\n\n    const hasPlayablePlaylist = Boolean(activePlaylist);\n\n    if (eventError || videosError || (videosDataError && !hasPlayablePlaylist)) {\n      const firstVideoError = videosData?.find((video) => !!video.error)?.error;\n      const errorObj =\n        eventError ||\n        videosError ||\n        (firstVideoError && new Error(firstVideoError)) ||\n        new Error('default');\n\n      setError(errorObj);\n      if (events?.onError) {\n        events.onError(errorObj);\n      }\n    } else {\n      setError(null);\n    }\n  }, [\n    eventError,\n    videosError,\n    videosDataError,\n    videosData,\n    events,\n    activePlaylist,\n    isEventLoading,\n    videosIsLoading,\n    loadingApisState\n  ]);\n  \n  // Loading state management\n  useEffect(() => {\n    const eventLoadedWithNoVideos =\n      !isEventLoading &&\n      eventData &&\n      eventData.videoIds &&\n      (!eventData.videoIds || eventData?.videoIds?.length === 0) &&\n      !loadingApisState;\n\n    const allApisLoadedWithPotentialVideos =\n      !isEventLoading &&\n      !videosIsLoading &&\n      eventData &&\n      !loadingApisState;\n\n    if (eventLoadedWithNoVideos || allApisLoadedWithPotentialVideos) {\n      setLoadingPlaylist(false);\n    }\n  }, [isEventLoading, videosIsLoading, eventData, loadingApisState]);\n\n  // The error screen takes precedence over the pre-event card: if the event's\n  // videos are errored we surface the error rather than the countdown.\n  // NOTE: all early returns below must stay after every hook above so the hook\n  // order never changes between renders (React error #300).\n  const startTimeMs = eventData?.startTime\n    ? new Date(eventData.startTime).getTime()\n    : NaN;\n  // Show the pre-event card only when there is no playable playlist yet. If a\n  // playlist is already available we play it, even if the scheduled start time\n  // is still in the future. While the videos list is still loading we don't\n  // know either way, so hold off (the Loading screen renders instead) to\n  // avoid flashing the card before the player mounts. A videos fetch error\n  // never settles these flags, but that path is covered by the error screen\n  // above taking precedence.\n  const videosSettled = !videosIsLoading && !loadingApisState;\n  const hasPreEventCard =\n    !!eventData && startTimeMs > Date.now() && !activePlaylist && videosSettled;\n  \n\n  // Error state — takes precedence over the pre-event countdown.\n  if (error) {\n    const title = t(error.message as keyof Translations)?.length ?\n      t(error.message as keyof Translations) : t(\"DEFAULT_ERROR\");\n    const description = t(`${error.message}_DESCRIPTION` as keyof Translations)?.length ?\n      t(`${error.message}_DESCRIPTION` as keyof Translations) : t(\"DEFAULT_ERROR_DESCRIPTION\");\n\n    return (\n      <div className={twMerge(\"md:rounded-2xl overflow-hidden aspect-video\", className)}>\n        <div className=\"relative w-full h-full\">\n          <ErrorScreen\n            title={title}\n            description={description}\n          />\n        </div>\n      </div>\n    );\n  }\n\n  if (hasPreEventCard && eventData) {\n    return (\n      <div className={twMerge(\"\", className)}>\n        <div className=\"relative w-full h-full\">\n          <PreEvent\n            event={eventData}\n            hideTitle={hideTitle}\n            locale={locale}\n            backgroundImageUrl={settings?.backgroundImageUrl}\n            showAnimation={settings?.showCountdownAnimation}\n          />\n        </div>\n      </div>\n    );\n  }\n  \n  // Handle empty playlists\n  if (!loadingPlaylist && eventData && !activePlaylist && events?.onEmptyPlaylists) {\n    events.onEmptyPlaylists();\n  }\n\n  // Loading state\n  if (loadingPlaylist) {\n    return (\n      <div className={twMerge(\"\", className)}>\n        <div className=\"relative w-full aspect-video bg-[#151515]\">\n          <Loading />\n        </div>\n      </div>\n    );\n  }\n\n  // ============================================================================\n  // Concurrency Control Check (SECURITY)\n  // ============================================================================\n  /**\n   * If concurrency control is required but playback is not allowed,\n   * show an error screen. This prevents bypassing concurrency limits\n   * by blocking the WebSocket connection.\n   */\n  if (activeConcurrencySession && !isPlaybackAllowed) {\n    // Determine the appropriate error message\n    const isConnecting = concurrencyConnectionState === ConcurrencyConnectionState.CONNECTING;\n\n    // If still connecting, show loading instead of error\n    if (isConnecting) {\n      return (\n        <div className={twMerge(\"\", className)}>\n          <div className=\"relative w-full aspect-video bg-[#151515]\">\n            <Loading />\n          </div>\n        </div>\n      );\n    }\n\n    // User was kicked or connection failed - show error\n    // Map kickedReason to appropriate translation keys\n    const isConnectionFailure = kickedReason === 'connection_failed';\n    const errorTitle = isConnectionFailure\n      ? t(\"CONCURRENCY_CONNECTION_FAILED\" as keyof Translations) || \"Connection Error\"\n      : t(\"CONCURRENCY_KICKED\" as keyof Translations) || \"Playback Stopped\";\n\n    const errorDescription = isConnectionFailure\n      ? t(\"CONCURRENCY_CONNECTION_FAILED_DESCRIPTION\" as keyof Translations) || \"Unable to establish a connection. Please try again.\"\n      : t(\"CONCURRENCY_KICKED_DESCRIPTION\" as keyof Translations) || \"Your session was ended. This may happen if you started watching on another device.\";\n\n    return (\n      <div className={twMerge(\"md:rounded-2xl overflow-hidden aspect-video\", className)}>\n        <div className=\"relative w-full h-full\">\n          <ErrorScreen\n            title={errorTitle}\n            description={errorDescription}\n          />\n        </div>\n      </div>\n    );\n  }\n\n  // Active video player\n  if (activePlaylist && activeVideoId && videosData) {\n    const activeVideo = videosData.find((video) => video.id === activeVideoId);\n    return (\n      <div className={twMerge(\"\", className)}>\n        <div className=\"relative w-full h-full\">\n          <Player\n            poster={eventData?.posterUrl}\n            {...props}\n            src={activePlaylist}\n            managedMode={true}\n            className={twMerge(className, \"peer aspect-video\")}\n            events={events}\n            locale={locale}\n            containerClassName=\"w-full h-full\"\n            publicKey={publicKey}\n            auth={auth}\n            {...(adsEnabled && activeVideo?.ad?.adTagUrl ? { imaConfig: { adTagUrl: activeVideo.ad.adTagUrl } } : {})}\n          />\n        </div>\n          {!hideTitle && eventData && (\n            <TitleAndDescription\n              title={eventData.title}\n              description={eventData.description || ''}\n              dateTime={eventData.startTime}\n              locale={locale}\n            />\n          )}\n      </div>\n    );\n  }\n\n  // Pre-event countdown\n  if (eventData) {\n    return (\n      <div className={twMerge(\"\", className)}>\n        <div className=\"relative w-full h-full\">\n          <PreEvent \n            event={eventData} \n            hideTitle={hideTitle} \n            locale={locale} \n            backgroundImageUrl={settings?.backgroundImageUrl} \n            showAnimation={settings?.showCountdownAnimation} \n          />\n        </div>\n      </div>\n    );\n  }\n\n  return null;\n};\n\n// Pre-event countdown component\nfunction PreEvent({ \n  event, \n  locale = 'en', \n  hideTitle, \n  backgroundImageUrl, \n  showAnimation = true \n}: { \n  event: EventData; \n  locale?: string; \n  hideTitle?: boolean; \n  backgroundImageUrl?: string; \n  showAnimation?: boolean;\n}): JSX.Element {\n  const date = new Date(event.startTime);\n  const now = new Date();\n  const [remainingTime, setRemainingTime] = useState(\n    date.getTime() - now.getTime()\n  );\n  const shouldBeStarted = remainingTime < 0;\n  const { t } = useMessages(locale);\n  useEffect(() => {\n    const interval = setInterval(() => {\n      if (remainingTime < 0) {\n        clearInterval(interval);\n      } else {\n        setRemainingTime(date.getTime() - new Date().getTime());\n      }\n    }, 1000);\n\n    return () => clearInterval(interval);\n  }, [date, remainingTime]);\n\n  const renderCountdown = useCallback(() => {\n    if (shouldBeStarted) {\n      return <span className=\"text-base-content text-xl\">{t(\"EVENT_NOT_STARTED\")}</span>;\n    }\n\n    const seconds = Math.floor(remainingTime / 1000) % 60;\n    const minutes = Math.floor(remainingTime / 1000 / 60) % 60;\n    const hours = Math.floor(remainingTime / 1000 / 60 / 60) % 24;\n    const days = Math.floor(remainingTime / 1000 / 60 / 60 / 24);\n\n    return (\n      <div className=\"grid grid-flow-col gap-2 md:gap-5 text-center auto-cols-max\">\n        <div className=\"flex flex-col\">\n          {/* daisyUI's `countdown` only supports 2 digits, so days (which can be\n              3+) is plain text. Mimic countdown's box model (inline-flex,\n              line-height:1em, child height:1em) so it aligns with the others. */}\n          <span className=\"font-mono text-lg md:text-5xl inline-flex leading-[1em] justify-center\">\n            <span\n              className=\"inline-block h-[1em]\"\n              aria-live=\"polite\"\n              aria-label={days.toString()}\n            >\n              {days.toString()}\n            </span>\n          </span>\n          <span className=\"text-xs uppercase tracking-widest mt-1\">{t(\"DAYS\")}</span>\n        </div>\n        <div className=\"flex flex-col\">\n          <span className=\"countdown font-mono text-lg md:text-5xl justify-center\">\n            <span \n              style={{ \"--value\": hours } as CSSProperties} \n              aria-live=\"polite\" \n              aria-label={hours.toString()}\n            >\n              {hours?.toString()?.padStart(2, '0')}\n            </span>\n          </span>\n          <span className=\"text-xs uppercase tracking-widest mt-1\">{t(\"HOURS\")}</span>\n        </div>\n        <div className=\"flex flex-col\">\n          <span className=\"countdown font-mono text-lg md:text-5xl justify-center\">\n            <span \n              style={{ \"--value\": minutes } as CSSProperties} \n              aria-live=\"polite\" \n              aria-label={minutes.toString()}\n            >\n              {minutes?.toString()?.padStart(2, '0')}\n            </span>\n          </span>\n          <span className=\"text-xs uppercase tracking-widest mt-1\">{t(\"MINUTES\")}</span>\n        </div>\n        <div className=\"flex flex-col\">\n          <span className=\"countdown font-mono text-lg md:text-5xl justify-center\">\n            <span \n              style={{ \"--value\": seconds } as CSSProperties} \n              aria-live=\"polite\" \n              aria-label={seconds.toString()}\n            >\n              {seconds?.toString()?.padStart(2, '0')}\n            </span>\n          </span>\n          <span className=\"text-xs uppercase tracking-widest mt-1\">{t(\"SECONDS\")}</span>\n        </div>\n      </div>\n    );\n  }, [remainingTime, shouldBeStarted, t]);\n\n  return (\n    <>\n      {event?.posterUrl ? (\n        <>\n          <div\n            className=\"relative overflow-hidden bg-base-200 aspect-video text-base-content w-full h-full flex justify-center items-center flex-col bg-no-repeat bg-cover md:rounded-2xl\"\n            style={{\n              backgroundImage: `url(${event.posterUrl})`,\n              backgroundRepeat: 'no-repeat',\n              backgroundSize: 'cover'\n            }}\n          >\n            {/* <div className=\"absolute inset-0 bg-black bg-opacity-40\"></div> */}\n            <div className=\"relative z-10\">\n              {renderCountdown()}\n            </div>\n          </div>\n          {!hideTitle && (\n            <TitleAndDescription\n              title={event.title}\n              description={event.description || ''}\n              dateTime={event.startTime}\n              locale={locale}\n            />\n          )}\n        </>\n      ) : (\n        <>\n          <div\n            className=\"relative overflow-hidden md:rounded-2xl! bg-base-200 aspect-video text-base-content flex flex-col justify-center items-center w-full h-full bg-cover bg-center bg-no-repeat\"\n            style={{\n              backgroundImage: backgroundImageUrl ? `url(${backgroundImageUrl})` : '',\n            }}\n          >\n            {/* {backgroundImageUrl && <div className=\"absolute inset-0 bg-black bg-opacity-40\"></div>} */}\n            <div className=\"relative z-10\">\n              {renderCountdown()}\n            </div>\n          </div>\n          {!hideTitle && (\n            <TitleAndDescription\n              title={event.title}\n              description={event.description || ''}\n              dateTime={event.startTime}\n              locale={locale}\n            />\n          )}\n        </>\n      )}\n    </>\n  );\n}","import React, { CSSProperties, useEffect, useMemo, useState } from 'react';\nimport { twMerge } from 'tailwind-merge';\nimport { useQuery } from '@tanstack/react-query';\nimport { Player } from './Player';\nimport type { PlayerProps } from './types';\nimport { fetchCreativeWorkData, type CreativeWorkData, CreativeWorksSortDirection } from './api/creative-work';\nimport { fetchVideosList, Playlist, type VideoData, type ConcurrencySession, ConcurrencyConnectionState } from './api/video';\nimport { Loading, ErrorScreen, TitleAndDescription } from './components';\nimport { findHLSPlaylist } from './helper';\nimport useMessages, { type Translations } from './messages/useMessages';\nimport { useConcurrencySession, type SessionStartedInfo } from './hooks/useConcurrencySession';\nimport { getDeviceId } from './utils/deviceId';\n\nexport interface CreativeWorkProps extends Omit<PlayerProps, 'src' | 'drmConfig'> {\n  publicKey: string;\n  creativeWorkId: string;\n  hideTitle?: boolean;\n  locale?: string;\n  filter?: string;\n  order?: CreativeWorksSortDirection;\n  events?: {\n    onCreativeWorkData?: (creativeWork: CreativeWorkData) => void;\n    onVideoData?: (video: VideoData) => void;\n    onEmptyPlaylists?: () => void;\n    onError?: (error: Error) => void;\n    onPlay?: () => void;\n    onPause?: () => void;\n    onEnded?: () => void;\n    onLoadStart?: () => void;\n    onCanPlay?: () => void;\n    /** Fires once the Shaka player instance is created (before manifest load).\n     * The instance exposes `player.mux.updateData(...)` for mid-view metadata\n     * updates (e.g. attaching consent-gated ids after a CMP grant). */\n    onPlayerReady?: (player: any) => void;\n    onAdStart?: () => void;\n    onAdComplete?: () => void;\n    onAdError?: (error: any) => void;\n    onAdSkipped?: () => void;\n    onAdPaused?: () => void;\n    onAdResumed?: () => void;\n    onAdProgress?: (adProgressData: any) => void;\n    onAllAdsCompleted?: () => void;\n    // Concurrency session events\n    /** Called when concurrency session is established and playback can begin */\n    onConcurrencySessionStarted?: (info: SessionStartedInfo) => void;\n    /** Called when session is kicked - playback will be stopped */\n    onConcurrencyKicked?: (reason: string, message: string) => void;\n    /** Called when the number of active sessions changes */\n    onConcurrencySessionCountChanged?: (activeSessions: number, limit: number) => void;\n    /** Called when concurrency connection state changes */\n    onConcurrencyStateChanged?: (state: ConcurrencyConnectionState) => void;\n  };\n  className?: string;\n  settings?: {\n    backgroundImageUrl?: string;\n    showCountdownAnimation?: boolean;\n  };\n  auth?: {\n    mottoToken?: string;\n    userId?: string;\n  };\n  // TanStack Query options\n  queryOptions?: {\n    enabled?: boolean;\n    staleTime?: number;\n    cacheTime?: number;\n    retry?: number;\n    retryDelay?: number;\n  };\n  adsEnabled?: boolean;\n}\n\nexport const CreativeWork: React.FC<CreativeWorkProps> = ({\n  publicKey,\n  creativeWorkId,\n  events,\n  hideTitle,\n  locale = 'en',\n  order,\n  filter,\n  className,\n  settings,\n  auth,\n  deviceId,\n  queryOptions = {},\n  adsEnabled = false,\n  ...props\n}) => {\n  // Get the effective deviceId - use provided one or auto-generate\n  // This must be consistent between the API call and WebSocket connection\n  const effectiveDeviceId = useMemo(() => getDeviceId(deviceId), [deviceId]);\n\n  // CreativeWork data fetching\n  const {\n    data: creativeWorkData,\n    isLoading: isCreativeWorkLoading,\n    error: creativeWorkError\n  } = useQuery<CreativeWorkData>({\n    queryKey: ['creative-work', publicKey, creativeWorkId, filter, order, locale],\n    queryFn: () => fetchCreativeWorkData(publicKey, creativeWorkId, undefined, filter, order, locale),\n    enabled: !!publicKey && !!creativeWorkId,\n    staleTime: queryOptions.staleTime ?? 5 * 60 * 1000, // 5 minutes\n    gcTime: queryOptions.cacheTime ?? 10 * 60 * 1000, // 10 minutes\n    retry: queryOptions.retry ?? 3,\n    retryDelay: queryOptions.retryDelay ?? ((attemptIndex: number) => Math.min(1000 * 2 ** attemptIndex, 30000))\n  });\n\n  const [activePlaylist, setActivePlaylist] = useState<Playlist | null>();\n  const [activeVideoId, setActiveVideoId] = useState<string | null>();\n  const [activeConcurrencySession, setActiveConcurrencySession] = useState<ConcurrencySession | undefined>();\n  const [showCountDown, setShowCountDown] = useState(false);\n  const videoIds = creativeWorkData?.videoIds ?? [];\n\n  // Videos list fetching\n  const {\n    data: videosData,\n    isLoading: videosIsLoading,\n    error: videosError,    \n  } = useQuery<VideoData[]>({\n    queryKey: ['videos-list', publicKey, videoIds, auth?.mottoToken, adsEnabled, locale, effectiveDeviceId],\n    queryFn: () => fetchVideosList(publicKey, videoIds, auth?.mottoToken, 0, 0, adsEnabled, locale, effectiveDeviceId),\n    enabled: !!publicKey && videoIds.length > 0,\n    refetchInterval: activePlaylist === null ? 30000 : false,\n    staleTime: queryOptions.staleTime ?? 5 * 60 * 1000,\n    gcTime: queryOptions.cacheTime ?? 10 * 60 * 1000,\n    retry: queryOptions.retry ?? 3,\n    retryDelay: queryOptions.retryDelay ?? ((attemptIndex: number) => Math.min(1000 * 2 ** attemptIndex, 30000))\n  });\n\n  const [loadingApisState, setLoadingApisState] = useState(true);\n\n  // Process videos data to find active playlist\n  useEffect(() => {\n    if (videosData !== undefined) {\n      setLoadingApisState(false);\n\n      const videosWithPlaylists = videosData.filter(\n        (video) => video.playlists && video.playlists.length > 0\n      );\n\n      if (videosWithPlaylists.length > 0) {\n        let hlsPlaylistFound = false;\n        // Iterate to find the first HLS playlist\n        for (const video of videosWithPlaylists) {\n          const activePlaylist = findHLSPlaylist(video);\n          const activePlaylistUrl = (\n              activePlaylist?.url ??\n              activePlaylist?.drm?.widevine?.playlistUrl ??\n              activePlaylist?.drm?.playready?.playlistUrl ??\n              activePlaylist?.drm?.fairplay?.playlistUrl);\n          const activePlaylistHasUrl = !!activePlaylistUrl;\n\n          if (activePlaylist && activePlaylistHasUrl) {\n            setActivePlaylist(activePlaylist);\n            setActiveVideoId(video.id);\n            setActiveConcurrencySession(video.concurrencySession);\n            hlsPlaylistFound = true;\n            break;\n          }\n        }\n        if (!hlsPlaylistFound) {\n          setActivePlaylist(null);\n          setActiveVideoId(null);\n          setActiveConcurrencySession(undefined);\n        }\n      } else {\n        setActivePlaylist(null);\n        setActiveVideoId(null);\n        setActiveConcurrencySession(undefined);\n      }\n    } else if (creativeWorkData && (!creativeWorkData.videoIds || creativeWorkData.videoIds.length === 0)) {\n      setLoadingApisState(false);\n      setActivePlaylist(null);\n      setActiveVideoId(null);\n      setActiveConcurrencySession(undefined);\n    }\n  }, [videosData, creativeWorkData]);\n\n  const { t } = useMessages(locale);\n\n  // ============================================================================\n  // Concurrency Session Management\n  // ============================================================================\n\n  /**\n   * Connect to concurrency session WebSocket if required.\n   *\n   * Security: The hook will enforce that playback is only allowed when\n   * the WebSocket connection is established (if concurrency control is required).\n   * If connection fails or user is kicked, isPlaybackAllowed will be false.\n   */\n  const concurrencyOptions = useMemo(() => ({\n    // Use the same deviceId that was sent to the API\n    deviceId: effectiveDeviceId,\n    // Forward concurrency events to parent\n    onSessionStarted: events?.onConcurrencySessionStarted,\n    onKicked: events?.onConcurrencyKicked,\n    onSessionCountChanged: events?.onConcurrencySessionCountChanged,\n    onStateChanged: events?.onConcurrencyStateChanged,\n    onError: (err: Error) => {\n      console.error('[CreativeWork] Concurrency session error:', err);\n      events?.onError?.(err);\n    },\n  }), [effectiveDeviceId, events]);\n\n  const {\n    isPlaybackAllowed,\n    wasKicked,\n    kickedReason,\n    connectionState: concurrencyConnectionState,\n  } = useConcurrencySession(activeConcurrencySession, concurrencyOptions);\n\n  // Notify parent of creative work data\n  useEffect(() => {\n    if (events?.onCreativeWorkData && creativeWorkData) {\n      events.onCreativeWorkData(creativeWorkData);\n    }\n    if (creativeWorkData && !creativeWorkData?.videoIds?.length) {\n      setShowCountDown(true);\n    }\n  }, [creativeWorkData, events]);\n\n  // Notify parent of video data\n  useEffect(() => {\n    if (events?.onVideoData && activeVideoId && videosData) {\n      const activeVideo = videosData.find((video) => video.id === activeVideoId);\n      if (activeVideo) {\n        events.onVideoData(activeVideo);\n      }\n    }\n  }, [activeVideoId, videosData, events]);\n\n  const [error, setError] = useState<Error | null>(null);\n  const videosDataError = videosData?.some((video) => !!video.error);\n\n  // Error handling\n  useEffect(() => {\n    // Defer error evaluation until loading states settle to avoid flashing\n    // error. Query-level errors must NOT be deferred: they are terminal\n    // (retries exhausted) and nothing clears loadingApisState when a fetch\n    // fails, so waiting on it would show the loading screen forever.\n    const hasFetchError = Boolean(creativeWorkError || videosError);\n    if (!hasFetchError && (isCreativeWorkLoading || videosIsLoading || loadingApisState)) {\n      return;\n    }\n\n    const hasPlayablePlaylist = Boolean(activePlaylist);\n\n    if (creativeWorkError || videosError || (videosDataError && !hasPlayablePlaylist)) {\n      const firstVideoError = videosData?.find((video) => !!video.error)?.error;\n      const errorObj =\n        creativeWorkError ||\n        videosError ||\n        (firstVideoError && new Error(firstVideoError)) ||\n        new Error('default');\n\n      setError(errorObj);\n      if (events?.onError) {\n        events.onError(errorObj);\n      }\n    } else {\n      setError(null);\n    }\n  }, [\n    creativeWorkError,\n    videosError,\n    videosDataError,\n    videosData,\n    events,\n    activePlaylist,\n    isCreativeWorkLoading,\n    videosIsLoading,\n    loadingApisState\n  ]);\n\n  const [loadingPlaylist, setLoadingPlaylist] = useState(true);\n\n  // Loading state management\n  useEffect(() => {\n    const creativeWorkLoadedWithNoVideos =\n      !isCreativeWorkLoading &&\n      creativeWorkData &&\n      creativeWorkData.videoIds &&\n      creativeWorkData.videoIds.length === 0;\n\n    const creativeWorkLoadedWithNoData =\n      !isCreativeWorkLoading && creativeWorkData && !creativeWorkData.videoIds;\n\n    const isEventsFinished =\n      !videosIsLoading &&\n      videosData &&\n      videosData.length > 0 &&\n      videosData.every((video) => video.playlists && video.playlists.length === 0);\n\n    if (\n      creativeWorkLoadedWithNoVideos ||\n      creativeWorkLoadedWithNoData ||\n      isEventsFinished\n    ) {\n      setLoadingPlaylist(false);\n      if (events?.onEmptyPlaylists) {\n        events.onEmptyPlaylists();\n      }\n    } else if (activePlaylist) {\n      setLoadingPlaylist(false);\n    }\n  }, [\n    isCreativeWorkLoading,\n    creativeWorkData,\n    videosIsLoading,\n    videosData,\n    activePlaylist,\n    events,\n  ]);\n\n  // The error screen takes precedence over the pre-event card: if the creative\n  // work's videos are errored we surface the error rather than the countdown.\n  // NOTE: all early returns below must stay after every hook above so the hook\n  // order never changes between renders (React error #300).\n  const releaseTimeMs = creativeWorkData?.releaseTime\n    ? new Date(creativeWorkData.releaseTime).getTime()\n    : NaN;\n  const hasPreEventCard =\n    !!creativeWorkData && (showCountDown || releaseTimeMs > Date.now());\n\n  // Error state — takes precedence over the pre-event countdown.\n  if (error) {\n    const title = t(error.message as keyof Translations)?.length ?\n      t(error.message as keyof Translations) : t(\"DEFAULT_ERROR\");\n    const description = t(`${error.message}_DESCRIPTION` as keyof Translations)?.length ?\n      t(`${error.message}_DESCRIPTION` as keyof Translations) : t(\"DEFAULT_ERROR_DESCRIPTION\");\n\n    return (\n      <div className={twMerge(\"md:rounded-2xl overflow-hidden aspect-video\", className)}>\n        <div className=\"relative w-full h-full\">\n          <ErrorScreen\n            title={title}\n            description={description}\n          />\n        </div>\n      </div>\n    );\n  }\n\n  if (hasPreEventCard && creativeWorkData) {\n    return (\n      <div className={twMerge(\"\", className)}>\n        <div className=\"relative w-full h-full bg-base-200 text-base-content flex justify-center items-center flex-col\">\n          <PreCreativeWork\n            creativeWork={creativeWorkData}\n            locale={locale}\n            hideTitle={hideTitle}\n            backgroundImageUrl={settings?.backgroundImageUrl}\n            showAnimation={settings?.showCountdownAnimation}\n          />\n        </div>\n      </div>\n    );\n  }\n\n  // Loading state\n  if (isCreativeWorkLoading || videosIsLoading || loadingApisState) {\n    return (\n      <div className={twMerge(\"\", className)}>\n        <div className=\"relative w-full aspect-video bg-[#151515]\">\n          <Loading />\n        </div>\n      </div>\n    );\n  }\n\n  // ============================================================================\n  // Concurrency Control Check (SECURITY)\n  // ============================================================================\n  /**\n   * If concurrency control is required but playback is not allowed,\n   * show an error screen. This prevents bypassing concurrency limits\n   * by blocking the WebSocket connection.\n   */\n  if (activeConcurrencySession && !isPlaybackAllowed) {\n    // Determine the appropriate error message\n    const isConnecting = concurrencyConnectionState === ConcurrencyConnectionState.CONNECTING;\n\n    // If still connecting, show loading instead of error\n    if (isConnecting) {\n      return (\n        <div className={twMerge(\"\", className)}>\n          <div className=\"relative w-full aspect-video bg-[#151515]\">\n            <Loading />\n          </div>\n        </div>\n      );\n    }\n\n    // User was kicked or connection failed - show error\n    // Map kickedReason to appropriate translation keys\n    const isConnectionFailure = kickedReason === 'connection_failed';\n    const errorTitle = isConnectionFailure\n      ? t(\"CONCURRENCY_CONNECTION_FAILED\" as keyof Translations) || \"Connection Error\"\n      : t(\"CONCURRENCY_KICKED\" as keyof Translations) || \"Playback Stopped\";\n\n    const errorDescription = isConnectionFailure\n      ? t(\"CONCURRENCY_CONNECTION_FAILED_DESCRIPTION\" as keyof Translations) || \"Unable to establish a connection. Please try again.\"\n      : t(\"CONCURRENCY_KICKED_DESCRIPTION\" as keyof Translations) || \"Your session was ended. This may happen if you started watching on another device.\";\n\n    return (\n      <div className={twMerge(\"md:rounded-2xl overflow-hidden aspect-video\", className)}>\n        <div className=\"relative w-full h-full\">\n          <ErrorScreen\n            title={errorTitle}\n            description={errorDescription}\n          />\n        </div>\n      </div>\n    );\n  }\n\n  // Show player if we have an active playlist\n  if (activeVideoId && activePlaylist && !loadingPlaylist) {\n    const activeVideo = videosData?.find((video) => video.id === activeVideoId);\n    \n    return (\n      <div className={twMerge(\"\", className)}>\n        <div className=\"relative w-full h-full\">\n          <Player\n            {...props}\n            className={twMerge(className, \"peer aspect-video\")}\n            managedMode={true}\n            events={{\n              ...events,\n            }}\n            src={activePlaylist}\n            locale={locale}\n            containerClassName=\"w-full h-full\"\n            publicKey={publicKey}\n            auth={auth}\n            {...(adsEnabled && activeVideo?.ad?.adTagUrl ? { imaConfig: { adTagUrl: activeVideo?.ad?.adTagUrl } } : {})}\n          />\n          {!hideTitle && (\n            <TitleAndDescription\n              title={creativeWorkData?.title || ''}\n              description={creativeWorkData?.description || ''}\n              dateTime={creativeWorkData?.releaseTime || ''}\n              locale={locale}\n            />\n          )}\n        </div>\n      </div>\n    );\n  }\n\n  // Loading state for playlist\n  if (loadingPlaylist) {\n    return (\n      <div className={twMerge(\"\", className)}>\n        <div className=\"relative w-full aspect-video bg-[#151515]\">\n          <Loading />\n        </div>\n      </div>\n    );\n  }\n\n  // Empty state\n  return null;\n};\n\nfunction PreCreativeWork({ \n  creativeWork, \n  locale = 'en', \n  hideTitle, \n  backgroundImageUrl, \n  showAnimation = true \n}: { \n  creativeWork: CreativeWorkData; \n  locale?: string; \n  hideTitle?: boolean; \n  backgroundImageUrl?: string; \n  showAnimation?: boolean;\n}): JSX.Element {\n  const date = new Date(creativeWork.releaseTime);\n  const now = new Date();\n  const [remainingTime, setRemainingTime] = useState(\n    date.getTime() - now.getTime()\n  );\n  const shouldBeStarted = remainingTime < 0;\n  const { t } = useMessages(locale);\n\n  useEffect(() => {\n    const interval = setInterval(() => {\n      if (remainingTime < 0) {\n        clearInterval(interval);\n      } else {\n        setRemainingTime(date.getTime() - new Date().getTime());\n      }\n    }, 1000);\n\n    return () => { clearInterval(interval); };\n  }, [date, remainingTime]);\n\n  const renderCountdown = () => {\n    if (shouldBeStarted) {\n      return <span className=\"text-base-content text-xl\">{t(\"EVENT_NOT_STARTED\")}</span>;\n    }\n\n    const seconds = Math.floor(remainingTime / 1000) % 60;\n    const minutes = Math.floor(remainingTime / 1000 / 60) % 60;\n    const hours = Math.floor(remainingTime / 1000 / 60 / 60) % 24;\n    const days = Math.floor(remainingTime / 1000 / 60 / 60 / 24);\n\n    return (\n      <div className=\"grid grid-flow-col md:gap-5 gap-2 text-center auto-cols-max\">\n        <div className=\"flex flex-col\">\n          {/* daisyUI's `countdown` only supports 2 digits, so days (which can be\n              3+) is plain text. Mimic countdown's box model (inline-flex,\n              line-height:1em, child height:1em) so it aligns with the others. */}\n          <span className=\"font-mono text-lg md:text-5xl inline-flex leading-[1em] justify-center\">\n            <span\n              className=\"inline-block h-[1em]\"\n              aria-live=\"polite\"\n              aria-label={days.toString()}\n            >\n              {days.toString()}\n            </span>\n          </span>\n          <span className=\"text-xs uppercase tracking-widest mt-1\">{t(\"DAYS\")}</span>\n        </div>\n        <div className=\"flex flex-col\">\n          <span className=\"countdown font-mono text-lg md:text-5xl justify-center\">\n            <span \n              style={{ \"--value\": hours } as CSSProperties} \n              aria-live=\"polite\" \n              aria-label={hours.toString()}\n            >\n              {hours?.toString()?.padStart(2, '0')}\n            </span>\n          </span>\n          <span className=\"text-xs uppercase tracking-widest mt-1\">{t(\"HOURS\")}</span>\n        </div>\n        <div className=\"flex flex-col\">\n          <span className=\"countdown font-mono text-lg md:text-5xl justify-center\">\n            <span \n              style={{ \"--value\": minutes } as CSSProperties} \n              aria-live=\"polite\" \n              aria-label={minutes.toString()}\n            >\n              {minutes?.toString()?.padStart(2, '0')}\n            </span>\n          </span>\n          <span className=\"text-xs uppercase tracking-widest mt-1\">{t(\"MINUTES\")}</span>\n        </div>\n        <div className=\"flex flex-col\">\n          <span className=\"countdown font-mono text-lg md:text-5xl justify-center\">\n            <span \n              style={{ \"--value\": seconds } as CSSProperties} \n              aria-live=\"polite\" \n              aria-label={seconds.toString()}\n            >\n              {seconds?.toString()?.padStart(2, '0')}\n            </span>\n          </span>\n          <span className=\"text-xs uppercase tracking-widest mt-1\">{t(\"SECONDS\")}</span>\n        </div>\n      </div>\n    );\n  };\n\n  return (\n    <>\n      <div\n        className=\"relative overflow-hidden md:rounded-2xl bg-base-200 aspect-video text-base-content flex flex-col justify-center items-center w-full h-full bg-cover bg-center bg-no-repeat\"\n        style={{\n          backgroundImage: backgroundImageUrl ? `url(${backgroundImageUrl})` : '',\n        }}\n      >\n        {backgroundImageUrl && <div className=\"absolute inset-0 bg-black bg-opacity-40\"></div>}\n        <div className=\"relative z-10\">\n          {renderCountdown()}\n        </div>\n      </div>\n      {!hideTitle && (\n        <TitleAndDescription\n          title={creativeWork.title}\n          description={creativeWork.description || ''}\n          dateTime={creativeWork.releaseTime}\n          locale={locale}\n        />\n      )}\n    </>\n  );\n}","import React from 'react';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\n\n// Create a client\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      staleTime: 5 * 60 * 1000, // 5 minutes\n      gcTime: 10 * 60 * 1000, // 10 minutes\n      retry: 3,\n      refetchOnWindowFocus: false,\n    },\n  },\n});\n\ninterface QueryProviderProps {\n  children: React.ReactNode;\n}\n\nexport const QueryProvider: React.FC<QueryProviderProps> = ({ children }) => {\n  return (\n    <QueryClientProvider client={queryClient}>\n      {children}\n    </QueryClientProvider>\n  );\n};\n\nexport { queryClient }; "],"mappings":"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","names":["useCallback","useEffect","useRef","useState","value","drmConfig","video","useCallback","useRef","useCallback","useRef","useCallback","shaka","useCallback","useCallback","useCallback","useCallback","useCallback","useEffect","useEffect","useRef","useCallback","useRef","jsx","jsxs","jsx","useRef","useCallback","useState","useEffect","useState","useEffect","useEffect","useEffect","useRef","useCallback","useRef","jsx","useRef","useEffect","useCallback","useEffect","useState","useEffect","useRef","useState","useCallback","ConcurrencyConnectionState","useRef","useState","useCallback","useEffect","twMerge","jsx","jsx","jsxs","jsx","twMerge","jsx","jsxs","useState","useEffect","useState","useEffect","jsx","jsxs","useRef","useState","useCallback","useEffect","useEffect","twMerge","createClient","createClient","createClient","timestampDate","createClient","timestampDate","jsx","jsxs","useEffect","twMerge","useCallback","useEffect","useState","twMerge","useQuery","jsx","jsxs","useQuery","useState","useEffect","activePlaylist","twMerge","useCallback","useEffect","useMemo","useState","twMerge","useQuery","Fragment","jsx","jsxs","useMemo","useQuery","useState","useEffect","activePlaylist","twMerge","jsx"]}