UNPKG

@mottosports/motto-video-player

Version:

React video player component for the Motto platform, powered by Shaka Player

1 lines 326 kB
{"version":3,"sources":["../src/index.ts","#style-inject:#style-inject","../src/index.css","../src/Player.tsx","../src/hooks/useShakaPlayer.ts","../src/utils/devices.ts","../package.json","../src/utils/licenseCache.ts","../src/hooks/useQualityControl.ts","../src/hooks/useSkipControls.ts","../src/hooks/useMuxAnalytics.ts","../src/hooks/useShakaUI.ts","../src/icons/SkipBackIcon.tsx","../src/icons/SkipForwardIcon.tsx","../src/icons/BigPlayIcon.tsx","../src/utils/renderIcon.ts","../src/hooks/useEventHandlers.ts","../src/hooks/useLiveBadge.ts","../src/hooks/usePosterFallback.ts","../src/hooks/useLiveIndicator.ts","../src/hooks/useKeyboardControls.ts","../src/hooks/useAdEvents.ts","../src/components/Loading.tsx","../src/components/ErrorScreen.tsx","../src/components/Title.tsx","../src/components/LiveBadge.tsx","../src/styles.css","../src/utils/scriptLoader.ts","../src/Video.tsx","../src/api/video.ts","../src/api/event.ts","../src/api/creative-work.ts","../src/helper.ts","../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/ja.json","../src/messages/ko.json","../src/messages/pt.json","../src/messages/ru.json","../src/messages/zh.json","../src/messages/nl.json","../src/messages/fa.json","../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 './index.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 type { PlayerProps, PlayerEvents, IconSizes } from './types';\nexport type { VideoProps } from './Video';\nexport type { EventProps } from './Event';\nexport type { CreativeWorkProps } from './CreativeWork';\nexport type { VideoData } from './api/video';\nexport type { EventData, EventsSortDirection } from './api/event';\nexport type { CreativeWorkData, CreativeWorksSortDirection } from './api/creative-work'; ","\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(\"/*! tailwindcss v4.1.8 | MIT License | https://tailwindcss.com */\\n@layer properties;\\n@layer theme, base, components, utilities;\\n@layer theme {\\n :root,\\n :host {\\n --font-sans:\\n ui-sans-serif,\\n system-ui,\\n sans-serif,\\n \\\"Apple Color Emoji\\\",\\n \\\"Segoe UI Emoji\\\",\\n \\\"Segoe UI Symbol\\\",\\n \\\"Noto Color Emoji\\\";\\n --font-mono:\\n ui-monospace,\\n SFMono-Regular,\\n Menlo,\\n Monaco,\\n Consolas,\\n \\\"Liberation Mono\\\",\\n \\\"Courier New\\\",\\n monospace;\\n --color-red-600: oklch(57.7% 0.245 27.325);\\n --color-black: #000;\\n --color-white: #fff;\\n --spacing: 0.25rem;\\n --text-xs: 0.75rem;\\n --text-xs--line-height: calc(1 / 0.75);\\n --text-sm: 0.875rem;\\n --text-sm--line-height: calc(1.25 / 0.875);\\n --text-base: 1rem;\\n --text-base--line-height: calc(1.5 / 1);\\n --text-lg: 1.125rem;\\n --text-lg--line-height: calc(1.75 / 1.125);\\n --text-xl: 1.25rem;\\n --text-xl--line-height: calc(1.75 / 1.25);\\n --text-2xl: 1.5rem;\\n --text-2xl--line-height: calc(2 / 1.5);\\n --text-5xl: 3rem;\\n --text-5xl--line-height: 1;\\n --font-weight-medium: 500;\\n --font-weight-semibold: 600;\\n --font-weight-bold: 700;\\n --tracking-wide: 0.025em;\\n --tracking-widest: 0.1em;\\n --radius-md: 0.375rem;\\n --radius-2xl: 1rem;\\n --animate-spin: spin 1s linear infinite;\\n --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\\n --aspect-video: 16 / 9;\\n --default-transition-duration: 150ms;\\n --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\\n --default-font-family: var(--font-sans);\\n --default-mono-font-family: var(--font-mono);\\n }\\n}\\n@layer base {\\n *,\\n ::after,\\n ::before,\\n ::backdrop,\\n ::file-selector-button {\\n box-sizing: border-box;\\n margin: 0;\\n padding: 0;\\n border: 0 solid;\\n }\\n html,\\n :host {\\n line-height: 1.5;\\n -webkit-text-size-adjust: 100%;\\n tab-size: 4;\\n font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, \\\"Apple Color Emoji\\\", \\\"Segoe UI Emoji\\\", \\\"Segoe UI Symbol\\\", \\\"Noto Color Emoji\\\");\\n font-feature-settings: var(--default-font-feature-settings, normal);\\n font-variation-settings: var(--default-font-variation-settings, normal);\\n -webkit-tap-highlight-color: transparent;\\n }\\n hr {\\n height: 0;\\n color: inherit;\\n border-top-width: 1px;\\n }\\n abbr:where([title]) {\\n -webkit-text-decoration: underline dotted;\\n text-decoration: underline dotted;\\n }\\n h1,\\n h2,\\n h3,\\n h4,\\n h5,\\n h6 {\\n font-size: inherit;\\n font-weight: inherit;\\n }\\n a {\\n color: inherit;\\n -webkit-text-decoration: inherit;\\n text-decoration: inherit;\\n }\\n b,\\n strong {\\n font-weight: bolder;\\n }\\n code,\\n kbd,\\n samp,\\n pre {\\n font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \\\"Liberation Mono\\\", \\\"Courier New\\\", monospace);\\n font-feature-settings: var(--default-mono-font-feature-settings, normal);\\n font-variation-settings: var(--default-mono-font-variation-settings, normal);\\n font-size: 1em;\\n }\\n small {\\n font-size: 80%;\\n }\\n sub,\\n sup {\\n font-size: 75%;\\n line-height: 0;\\n position: relative;\\n vertical-align: baseline;\\n }\\n sub {\\n bottom: -0.25em;\\n }\\n sup {\\n top: -0.5em;\\n }\\n table {\\n text-indent: 0;\\n border-color: inherit;\\n border-collapse: collapse;\\n }\\n :-moz-focusring {\\n outline: auto;\\n }\\n progress {\\n vertical-align: baseline;\\n }\\n summary {\\n display: list-item;\\n }\\n ol,\\n ul,\\n menu {\\n list-style: none;\\n }\\n img,\\n svg,\\n video,\\n canvas,\\n audio,\\n iframe,\\n embed,\\n object {\\n display: block;\\n vertical-align: middle;\\n }\\n img,\\n video {\\n max-width: 100%;\\n height: auto;\\n }\\n button,\\n input,\\n select,\\n optgroup,\\n textarea,\\n ::file-selector-button {\\n font: inherit;\\n font-feature-settings: inherit;\\n font-variation-settings: inherit;\\n letter-spacing: inherit;\\n color: inherit;\\n border-radius: 0;\\n background-color: transparent;\\n opacity: 1;\\n }\\n :where(select:is([multiple], [size])) optgroup {\\n font-weight: bolder;\\n }\\n :where(select:is([multiple], [size])) optgroup option {\\n padding-inline-start: 20px;\\n }\\n ::file-selector-button {\\n margin-inline-end: 4px;\\n }\\n ::placeholder {\\n opacity: 1;\\n }\\n @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {\\n ::placeholder {\\n color: currentcolor;\\n @supports (color: color-mix(in lab, red, red)) {\\n color: color-mix(in oklab, currentcolor 50%, transparent);\\n }\\n }\\n }\\n textarea {\\n resize: vertical;\\n }\\n ::-webkit-search-decoration {\\n -webkit-appearance: none;\\n }\\n ::-webkit-date-and-time-value {\\n min-height: 1lh;\\n text-align: inherit;\\n }\\n ::-webkit-datetime-edit {\\n display: inline-flex;\\n }\\n ::-webkit-datetime-edit-fields-wrapper {\\n padding: 0;\\n }\\n ::-webkit-datetime-edit,\\n ::-webkit-datetime-edit-year-field,\\n ::-webkit-datetime-edit-month-field,\\n ::-webkit-datetime-edit-day-field,\\n ::-webkit-datetime-edit-hour-field,\\n ::-webkit-datetime-edit-minute-field,\\n ::-webkit-datetime-edit-second-field,\\n ::-webkit-datetime-edit-millisecond-field,\\n ::-webkit-datetime-edit-meridiem-field {\\n padding-block: 0;\\n }\\n :-moz-ui-invalid {\\n box-shadow: none;\\n }\\n button,\\n input:where([type=button], [type=reset], [type=submit]),\\n ::file-selector-button {\\n appearance: button;\\n }\\n ::-webkit-inner-spin-button,\\n ::-webkit-outer-spin-button {\\n height: auto;\\n }\\n [hidden]:where(:not([hidden=until-found])) {\\n display: none !important;\\n }\\n}\\n@layer utilities {\\n .pointer-events-none {\\n pointer-events: none;\\n }\\n .visible {\\n visibility: visible;\\n }\\n .sr-only {\\n position: absolute;\\n width: 1px;\\n height: 1px;\\n padding: 0;\\n margin: -1px;\\n overflow: hidden;\\n clip: rect(0, 0, 0, 0);\\n white-space: nowrap;\\n border-width: 0;\\n }\\n .absolute {\\n position: absolute;\\n }\\n .fixed {\\n position: fixed;\\n }\\n .relative {\\n position: relative;\\n }\\n .static {\\n position: static;\\n }\\n .inset-0 {\\n inset: calc(var(--spacing) * 0);\\n }\\n .top-4 {\\n top: calc(var(--spacing) * 4);\\n }\\n .right-0 {\\n right: calc(var(--spacing) * 0);\\n }\\n .right-4 {\\n right: calc(var(--spacing) * 4);\\n }\\n .bottom-0 {\\n bottom: calc(var(--spacing) * 0);\\n }\\n .bottom-4 {\\n bottom: calc(var(--spacing) * 4);\\n }\\n .left-0 {\\n left: calc(var(--spacing) * 0);\\n }\\n .left-4 {\\n left: calc(var(--spacing) * 4);\\n }\\n .z-10 {\\n z-index: 10;\\n }\\n .z-20 {\\n z-index: 20;\\n }\\n .z-50 {\\n z-index: 50;\\n }\\n .container {\\n width: 100%;\\n @media (width >= 40rem) {\\n max-width: 40rem;\\n }\\n @media (width >= 48rem) {\\n max-width: 48rem;\\n }\\n @media (width >= 64rem) {\\n max-width: 64rem;\\n }\\n @media (width >= 80rem) {\\n max-width: 80rem;\\n }\\n @media (width >= 96rem) {\\n max-width: 96rem;\\n }\\n }\\n .m-6 {\\n margin: calc(var(--spacing) * 6);\\n }\\n .mt-1 {\\n margin-top: calc(var(--spacing) * 1);\\n }\\n .mt-3 {\\n margin-top: calc(var(--spacing) * 3);\\n }\\n .mb-2 {\\n margin-bottom: calc(var(--spacing) * 2);\\n }\\n .mb-6 {\\n margin-bottom: calc(var(--spacing) * 6);\\n }\\n .flex {\\n display: flex;\\n }\\n .grid {\\n display: grid;\\n }\\n .aspect-video {\\n aspect-ratio: var(--aspect-video);\\n }\\n .h-2 {\\n height: calc(var(--spacing) * 2);\\n }\\n .h-12 {\\n height: calc(var(--spacing) * 12);\\n }\\n .h-24 {\\n height: calc(var(--spacing) * 24);\\n }\\n .h-full {\\n height: 100%;\\n }\\n .w-2 {\\n width: calc(var(--spacing) * 2);\\n }\\n .w-12 {\\n width: calc(var(--spacing) * 12);\\n }\\n .w-24 {\\n width: calc(var(--spacing) * 24);\\n }\\n .w-full {\\n width: 100%;\\n }\\n .grow {\\n flex-grow: 1;\\n }\\n .animate-pulse {\\n animation: var(--animate-pulse);\\n }\\n .animate-spin {\\n animation: var(--animate-spin);\\n }\\n .auto-cols-max {\\n grid-auto-columns: max-content;\\n }\\n .grid-flow-col {\\n grid-auto-flow: column;\\n }\\n .flex-col {\\n flex-direction: column;\\n }\\n .items-center {\\n align-items: center;\\n }\\n .justify-center {\\n justify-content: center;\\n }\\n .justify-stretch {\\n justify-content: stretch;\\n }\\n .gap-1 {\\n gap: calc(var(--spacing) * 1);\\n }\\n .overflow-hidden {\\n overflow: hidden;\\n }\\n .rounded-full {\\n border-radius: calc(infinity * 1px);\\n }\\n .rounded-md {\\n border-radius: var(--radius-md);\\n }\\n .bg-\\\\[\\\\#111111\\\\] {\\n background-color: #111111;\\n }\\n .bg-\\\\[\\\\#151515\\\\] {\\n background-color: #151515;\\n }\\n .bg-black {\\n background-color: var(--color-black);\\n }\\n .bg-red-600 {\\n background-color: var(--color-red-600);\\n }\\n .bg-transparent {\\n background-color: transparent;\\n }\\n .bg-white {\\n background-color: var(--color-white);\\n }\\n .bg-gradient-to-t {\\n --tw-gradient-position: to top in oklab;\\n background-image: linear-gradient(var(--tw-gradient-stops));\\n }\\n .from-black\\\\/70 {\\n --tw-gradient-from: color-mix(in srgb, #000 70%, transparent);\\n @supports (color: color-mix(in lab, red, red)) {\\n --tw-gradient-from: color-mix(in oklab, var(--color-black) 70%, transparent);\\n }\\n --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));\\n }\\n .to-transparent {\\n --tw-gradient-to: transparent;\\n --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));\\n }\\n .bg-cover {\\n background-size: cover;\\n }\\n .bg-center {\\n background-position: center;\\n }\\n .bg-no-repeat {\\n background-repeat: no-repeat;\\n }\\n .p-2 {\\n padding: calc(var(--spacing) * 2);\\n }\\n .p-4 {\\n padding: calc(var(--spacing) * 4);\\n }\\n .px-2 {\\n padding-inline: calc(var(--spacing) * 2);\\n }\\n .px-4 {\\n padding-inline: calc(var(--spacing) * 4);\\n }\\n .py-1 {\\n padding-block: calc(var(--spacing) * 1);\\n }\\n .text-center {\\n text-align: center;\\n }\\n .text-left {\\n text-align: left;\\n }\\n .font-mono {\\n font-family: var(--font-mono);\\n }\\n .text-2xl {\\n font-size: var(--text-2xl);\\n line-height: var(--tw-leading, var(--text-2xl--line-height));\\n }\\n .text-base {\\n font-size: var(--text-base);\\n line-height: var(--tw-leading, var(--text-base--line-height));\\n }\\n .text-lg {\\n font-size: var(--text-lg);\\n line-height: var(--tw-leading, var(--text-lg--line-height));\\n }\\n .text-sm {\\n font-size: var(--text-sm);\\n line-height: var(--tw-leading, var(--text-sm--line-height));\\n }\\n .text-xl {\\n font-size: var(--text-xl);\\n line-height: var(--tw-leading, var(--text-xl--line-height));\\n }\\n .text-xs {\\n font-size: var(--text-xs);\\n line-height: var(--tw-leading, var(--text-xs--line-height));\\n }\\n .text-\\\\[10px\\\\] {\\n font-size: 10px;\\n }\\n .leading-none {\\n --tw-leading: 1;\\n line-height: 1;\\n }\\n .font-bold {\\n --tw-font-weight: var(--font-weight-bold);\\n font-weight: var(--font-weight-bold);\\n }\\n .font-medium {\\n --tw-font-weight: var(--font-weight-medium);\\n font-weight: var(--font-weight-medium);\\n }\\n .font-semibold {\\n --tw-font-weight: var(--font-weight-semibold);\\n font-weight: var(--font-weight-semibold);\\n }\\n .tracking-wide {\\n --tw-tracking: var(--tracking-wide);\\n letter-spacing: var(--tracking-wide);\\n }\\n .tracking-widest {\\n --tw-tracking: var(--tracking-widest);\\n letter-spacing: var(--tracking-widest);\\n }\\n .text-white {\\n color: var(--color-white);\\n }\\n .uppercase {\\n text-transform: uppercase;\\n }\\n .shadow-lg {\\n --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\\n box-shadow:\\n var(--tw-inset-shadow),\\n var(--tw-inset-ring-shadow),\\n var(--tw-ring-offset-shadow),\\n var(--tw-ring-shadow),\\n var(--tw-shadow);\\n }\\n .filter {\\n filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);\\n }\\n .md\\\\:gap-5 {\\n @media (width >= 48rem) {\\n gap: calc(var(--spacing) * 5);\\n }\\n }\\n .md\\\\:rounded-2xl {\\n @media (width >= 48rem) {\\n border-radius: var(--radius-2xl);\\n }\\n }\\n .md\\\\:rounded-2xl\\\\! {\\n @media (width >= 48rem) {\\n border-radius: var(--radius-2xl) !important;\\n }\\n }\\n .md\\\\:text-5xl {\\n @media (width >= 48rem) {\\n font-size: var(--text-5xl);\\n line-height: var(--tw-leading, var(--text-5xl--line-height));\\n }\\n }\\n .md\\\\:text-base {\\n @media (width >= 48rem) {\\n font-size: var(--text-base);\\n line-height: var(--tw-leading, var(--text-base--line-height));\\n }\\n }\\n .md\\\\:text-sm {\\n @media (width >= 48rem) {\\n font-size: var(--text-sm);\\n line-height: var(--tw-leading, var(--text-sm--line-height));\\n }\\n }\\n .md\\\\:text-xl {\\n @media (width >= 48rem) {\\n font-size: var(--text-xl);\\n line-height: var(--tw-leading, var(--text-xl--line-height));\\n }\\n }\\n .md\\\\:text-xs {\\n @media (width >= 48rem) {\\n font-size: var(--text-xs);\\n line-height: var(--tw-leading, var(--text-xs--line-height));\\n }\\n }\\n}\\n@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 position: relative;\\n width: 100%;\\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 position: absolute;\\n top: calc(var(--spacing) * 0);\\n left: calc(var(--spacing) * 0);\\n height: 100%;\\n width: 100%;\\n }\\n .motto-skip-button {\\n position: absolute;\\n top: calc(1/2 * 100%);\\n z-index: 10;\\n display: flex;\\n height: calc(var(--spacing) * 16);\\n width: calc(var(--spacing) * 16);\\n --tw-translate-y: calc(calc(1/2 * 100%) * -1);\\n translate: var(--tw-translate-x) var(--tw-translate-y);\\n cursor: pointer;\\n align-items: center;\\n justify-content: center;\\n border-radius: calc(infinity * 1px);\\n border-style: var(--tw-border-style);\\n border-width: 0px;\\n background-color: color-mix(in srgb, #000 70%, transparent);\\n @supports (color: color-mix(in lab, red, red)) {\\n background-color: color-mix(in oklab, var(--color-black) 70%, transparent);\\n }\\n font-size: var(--text-2xl);\\n line-height: var(--tw-leading, var(--text-2xl--line-height));\\n color: var(--color-white);\\n opacity: 80%;\\n transition-property: all;\\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\\n transition-duration: var(--tw-duration, var(--default-transition-duration));\\n --tw-duration: 200ms;\\n transition-duration: 200ms;\\n &:hover {\\n @media (hover: hover) {\\n --tw-scale-x: 110%;\\n --tw-scale-y: 110%;\\n --tw-scale-z: 110%;\\n scale: var(--tw-scale-x) var(--tw-scale-y);\\n }\\n }\\n &:hover {\\n @media (hover: hover) {\\n opacity: 100%;\\n }\\n }\\n &:active {\\n --tw-scale-x: 95%;\\n --tw-scale-y: 95%;\\n --tw-scale-z: 95%;\\n scale: var(--tw-scale-x) var(--tw-scale-y);\\n }\\n }\\n .motto-skip-button-back {\\n left: calc(var(--spacing) * 5);\\n }\\n .motto-skip-button-forward {\\n right: calc(var(--spacing) * 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@property --tw-gradient-position { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-gradient-from { syntax: \\\"<color>\\\"; inherits: false; initial-value: #0000; }\\n@property --tw-gradient-via { syntax: \\\"<color>\\\"; inherits: false; initial-value: #0000; }\\n@property --tw-gradient-to { syntax: \\\"<color>\\\"; inherits: false; initial-value: #0000; }\\n@property --tw-gradient-stops { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-gradient-via-stops { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-gradient-from-position { syntax: \\\"<length-percentage>\\\"; inherits: false; initial-value: 0%; }\\n@property --tw-gradient-via-position { syntax: \\\"<length-percentage>\\\"; inherits: false; initial-value: 50%; }\\n@property --tw-gradient-to-position { syntax: \\\"<length-percentage>\\\"; inherits: false; initial-value: 100%; }\\n@property --tw-leading { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-font-weight { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-tracking { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-shadow { syntax: \\\"*\\\"; inherits: false; initial-value: 0 0 #0000; }\\n@property --tw-shadow-color { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-shadow-alpha { syntax: \\\"<percentage>\\\"; inherits: false; initial-value: 100%; }\\n@property --tw-inset-shadow { syntax: \\\"*\\\"; inherits: false; initial-value: 0 0 #0000; }\\n@property --tw-inset-shadow-color { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-inset-shadow-alpha { syntax: \\\"<percentage>\\\"; inherits: false; initial-value: 100%; }\\n@property --tw-ring-color { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-ring-shadow { syntax: \\\"*\\\"; inherits: false; initial-value: 0 0 #0000; }\\n@property --tw-inset-ring-color { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-inset-ring-shadow { syntax: \\\"*\\\"; inherits: false; initial-value: 0 0 #0000; }\\n@property --tw-ring-inset { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-ring-offset-width { syntax: \\\"<length>\\\"; inherits: false; initial-value: 0px; }\\n@property --tw-ring-offset-color { syntax: \\\"*\\\"; inherits: false; initial-value: #fff; }\\n@property --tw-ring-offset-shadow { syntax: \\\"*\\\"; inherits: false; initial-value: 0 0 #0000; }\\n@property --tw-blur { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-brightness { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-contrast { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-grayscale { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-hue-rotate { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-invert { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-opacity { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-saturate { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-sepia { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-drop-shadow { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-drop-shadow-color { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-drop-shadow-alpha { syntax: \\\"<percentage>\\\"; inherits: false; initial-value: 100%; }\\n@property --tw-drop-shadow-size { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-translate-x { syntax: \\\"*\\\"; inherits: false; initial-value: 0; }\\n@property --tw-translate-y { syntax: \\\"*\\\"; inherits: false; initial-value: 0; }\\n@property --tw-translate-z { syntax: \\\"*\\\"; inherits: false; initial-value: 0; }\\n@property --tw-border-style { syntax: \\\"*\\\"; inherits: false; initial-value: solid; }\\n@property --tw-duration { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-scale-x { syntax: \\\"*\\\"; inherits: false; initial-value: 1; }\\n@property --tw-scale-y { syntax: \\\"*\\\"; inherits: false; initial-value: 1; }\\n@property --tw-scale-z { syntax: \\\"*\\\"; inherits: false; initial-value: 1; }\\n@keyframes spin {\\n to {\\n transform: rotate(360deg);\\n }\\n}\\n@keyframes pulse {\\n 50% {\\n opacity: 0.5;\\n }\\n}\\n@layer properties {\\n @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {\\n *,\\n ::before,\\n ::after,\\n ::backdrop {\\n --tw-gradient-position: initial;\\n --tw-gradient-from: #0000;\\n --tw-gradient-via: #0000;\\n --tw-gradient-to: #0000;\\n --tw-gradient-stops: initial;\\n --tw-gradient-via-stops: initial;\\n --tw-gradient-from-position: 0%;\\n --tw-gradient-via-position: 50%;\\n --tw-gradient-to-position: 100%;\\n --tw-leading: initial;\\n --tw-font-weight: initial;\\n --tw-tracking: initial;\\n --tw-shadow: 0 0 #0000;\\n --tw-shadow-color: initial;\\n --tw-shadow-alpha: 100%;\\n --tw-inset-shadow: 0 0 #0000;\\n --tw-inset-shadow-color: initial;\\n --tw-inset-shadow-alpha: 100%;\\n --tw-ring-color: initial;\\n --tw-ring-shadow: 0 0 #0000;\\n --tw-inset-ring-color: initial;\\n --tw-inset-ring-shadow: 0 0 #0000;\\n --tw-ring-inset: initial;\\n --tw-ring-offset-width: 0px;\\n --tw-ring-offset-color: #fff;\\n --tw-ring-offset-shadow: 0 0 #0000;\\n --tw-blur: initial;\\n --tw-brightness: initial;\\n --tw-contrast: initial;\\n --tw-grayscale: initial;\\n --tw-hue-rotate: initial;\\n --tw-invert: initial;\\n --tw-opacity: initial;\\n --tw-saturate: initial;\\n --tw-sepia: initial;\\n --tw-drop-shadow: initial;\\n --tw-drop-shadow-color: initial;\\n --tw-drop-shadow-alpha: 100%;\\n --tw-drop-shadow-size: initial;\\n --tw-translate-x: 0;\\n --tw-translate-y: 0;\\n --tw-translate-z: 0;\\n --tw-border-style: solid;\\n --tw-duration: initial;\\n --tw-scale-x: 1;\\n --tw-scale-y: 1;\\n --tw-scale-z: 1;\\n }\\n }\\n}\\n\")","import React, { forwardRef, useEffect, useRef, useImperativeHandle, useCallback, useState } from 'react';\nimport shaka from 'shaka-player/dist/shaka-player.ui';\nimport { PlayerProps } from './types';\nimport { \n useShakaPlayer, \n useQualityControl, \n useSkipControls, \n useMuxAnalytics, \n useShakaUI,\n useEventHandlers,\n useLiveBadge,\n useLiveIndicator,\n useKeyboardControls,\n useAdEvents\n} from './hooks';\nimport { LiveBadge } from './components';\nimport './styles.css';\nimport { twMerge } from 'tailwind-merge';\nimport { getRequiredScriptLoaders, loadScripts, waitForGlobals } from './utils/scriptLoader';\nimport { Loading } from './components';\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 publicKey,\n auth,\n ...videoProps \n }, ref) => {\n const videoRef = useRef<HTMLVideoElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const [isScriptsLoaded, setIsScriptsLoaded] = useState(false);\n const [isInitialLoading, setIsInitialLoading] = useState(true);\n const [bfResetKey, setBfResetKey] = useState(0);\n\n // Compute playlist presence for managed mode optimization\n // In managed mode, we only reinitialize when playlist presence changes, not on URL changes\n const hasPlaylist = !!src && !!(\n src.url || \n src.drm?.widevine?.playlistUrl || \n src.drm?.playready?.playlistUrl || \n src.drm?.fairplay?.playlistUrl\n );\n\n // Assign the ref early so parent components can access the video element\n useImperativeHandle(ref, () => videoRef.current!, []);\n\n // Initialize custom hooks\n const { playerRef, initializePlayer, loadManifest, destroyPlayer, isRetrying } = useShakaPlayer({\n src,\n shakaConfig,\n drmConfig,\n onError: events?.onError,\n onPlayerReady: events?.onPlayerReady,\n muxConfig,\n onMuxReady: events?.onMuxReady,\n onMuxDataUpdate: events?.onMuxDataUpdate,\n publicKey,\n mottoToken: auth?.mottoToken,\n hasAds: !!imaConfig?.adTagUrl\n });\n\n const { \n initializeMux, \n updateMuxData, \n handleMuxError, \n destroyMux \n } = useMuxAnalytics(playerRef, muxConfig, events?.onMuxReady, events?.onMuxDataUpdate);\n\n const { \n getAvailableQualities, \n setQuality, \n setupQualityTracking, \n configureQuality \n } = useQualityControl(playerRef, qualityConfig, events?.onQualityChange);\n\n const { \n skipBack, \n skipForward, \n skipDuration, \n shouldShowSkipControls \n } = useSkipControls(videoRef, events?.onSkipBack, events?.onSkipForward);\n\n // Keyboard controls for desktop (arrow keys + spacebar)\n useKeyboardControls(videoRef, {\n skipBack,\n skipForward,\n enabled: true\n });\n\n const { setupEventListeners, cleanupEventListeners } = useEventHandlers(videoRef, {\n onPlay: events?.onPlay,\n onPause: events?.onPause,\n onEnded: events?.onEnded,\n onLoadStart: events?.onLoadStart,\n onCanPlay: events?.onCanPlay\n });\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 // Live badge hook\n const { isLive, isVisible: isLiveBadgeVisible } = useLiveBadge(playerRef, {\n enabled: true,\n liveThresholdSeconds: 15,\n onLiveStateChange: (isLive) => {\n events?.onLiveStateChange?.(isLive);\n }\n });\n\n // Live indicator hook\n useLiveIndicator(containerRef, playerRef, {\n enabled: true,\n indicatorColor: '#ff0000',\n indicatorSize: 8,\n showPulseAnimation: true,\n liveThresholdSeconds: 15\n });\n\n // Ad events hook\n const { setupAdEventListeners, cleanupAdEventListeners } = useAdEvents(playerRef, {\n onAdStart: events?.onAdStart,\n onAdComplete: events?.onAdComplete,\n onAdError: events?.onAdError,\n onAdSkipped: events?.onAdSkipped,\n onAdPaused: events?.onAdPaused,\n onAdResumed: events?.onAdResumed,\n onAdProgress: events?.onAdProgress,\n onAllAdsCompleted: events?.onAllAdsCompleted\n });\n\n // System73 wrapper initialization\n const initializeSystem73 = useCallback((playerConfig: any) => {\n if (!system73Config?.apiKey || !window.S73ShakaPlayerWrapper) {\n return null;\n }\n\n console.log('Initializing System73 SDK...');\n \n try {\n // Create System73 wrapper using the API key from props\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 console.log('System73 SDK initialized with config:', s73Config);\n return wrapper;\n \n } catch (error) {\n console.error('Error initializing System73 SDK:', error);\n return null;\n }\n }, [system73Config]);\n\n // Initialize ads using proper Shaka UI ad container - following official docs\n const initializeAds = useCallback(async () => {\n if (!imaConfig?.adTagUrl || !playerRef.current || !videoRef.current || !uiRef.current) {\n return;\n }\n\n // Double-check that google.ima is available\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 - as per official docs\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 // Get google reference\n const google = window.google;\n \n // Configure ads rendering settings for autoplay if needed\n let adsRenderingSettings = null;\n if (imaConfig.adsRenderingSettings) {\n adsRenderingSettings = imaConfig.adsRenderingSettings;\n } else if (autoPlay) {\n // Create default settings optimized for autoplay\n adsRenderingSettings = new (google.ima as any).AdsRenderingSettings();\n adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;\n }\n \n // Initialize client-side ads with proper container and settings\n adManager.initClientSide(container, video, adsRenderingSettings);\n \n // Request ads (this starts the async VAST fetch)\n const adsRequest = new google.ima.AdsRequest();\n adsRequest.adTagUrl = imaConfig.adTagUrl;\n adManager.requestClientSideAds(adsRequest);\n \n // Setup detailed ad event listeners\n setupAdEventListeners();\n \n // Wait to allow VAST to load before loading manifest\n // This ensures the ad request starts before video content loads\n await new Promise(resolve => setTimeout(resolve, 1000));\n \n // Load the manifest - IMA will intercept playback for preroll ads\n await loadManifest();\n \n } catch (error) {\n console.error('Error initializing ads:', error);\n }\n }, [imaConfig, autoPlay, setupAdEventListeners, loadManifest]);\n\n // Load required scripts first\n useEffect(() => {\n const loadRequiredScripts = async () => {\n try {\n const scriptLoaders = getRequiredScriptLoaders(!!imaConfig?.adTagUrl, !!system73Config?.apiKey);\n await loadScripts(scriptLoaders);\n \n // Wait for global variables to be available\n const globalsToWait = [];\n if (imaConfig?.adTagUrl) {\n globalsToWait.push('google');\n }\n if (system73Config?.apiKey) {\n globalsToWait.push('S73ShakaPlayerWrapper');\n }\n \n if (globalsToWait.length > 0) {\n await waitForGlobals(globalsToWait);\n }\n \n setIsScriptsLoaded(true);\n } catch (error) {\n console.error('Error loading required scripts:', error);\n // Set as loaded anyway to prevent blocking, but log the error\n setIsScriptsLoaded(true);\n }\n };\n\n loadRequiredScripts();\n }, [imaConfig?.adTagUrl, system73Config?.apiKey]);\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 - only runs after scripts are loaded\n useEffect(() => {\n const video = videoRef.current;\n if (!video || !isScriptsLoaded) return;\n\n const initialize = async () => {\n try {\n // Reset initial loading state on (re)initialize\n setIsInitialLoading(true);\n\n // Initialize System73 wrapper first if configured\n let system73Wrapper = null;\n if (system73Config?.apiKey && window.S73ShakaPlayerWrapper) {\n // Create a copy of shakaConfig to avoid modifying the original\n const playerConfig = { ...shakaConfig };\n system73Wrapper = initializeSystem73(playerConfig);\n \n // If System73 wrapper was created successfully, update shakaConfig\n if (system73Wrapper) {\n // The wrapper has already modified the playerConfig\n shakaConfig = playerConfig;\n }\n }\n\n // Initialize player\n await initializePlayer(video);\n \n // Wrap the player with System73 after initialization\n if (system73Wrapper && playerRef.current) {\n system73Wrapper.wrapPlayer(playerRef.current);\n console.log('System73 player wrapper applied');\n }\n \n // Setup event listeners\n setupEventListeners();\n \n // Setup quality tracking\n const cleanupQuality = setupQualityTracking();\n \n // Configure quality settings\n configureQuality();\n \n // Initialize UI\n await initializeUI();\n \n // Initialize ads after UI is ready (ads will load manifest after VAST loads)\n if (imaConfig?.adTagUrl && window.google?.ima) {\n await initializeAds();\n }\n\n } catch (error) {\n console.error('Error during player initialization:', error);\n handleMuxError(error);\n }\n };\n\n initialize();\n\n // Cleanup function\n return () => {\n cleanupEventListeners();\n cleanupAdEventListeners();\n destroyUI();\n destroyMux();\n destroyPlayer();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [managedMode ? hasPlaylist : src, isScriptsLoaded, bfResetKey, managedMode]);\n\n // Manage initial loading spinner using media events (attach once)\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const onLoadStart = () => {\n setIsInitialLoading(true);\n };\n const onCanPlay = () => {\n setIsInitialLoading(false);\n };\n const onPlaying = () => {\n setIsInitialLoading(false);\n };\n\n video.addEventListener('loadstart', onLoadStart);\n video.addEventListener('canplay', onCanPlay);\n video.addEventListener('playing', onPlaying);\n\n return () => {\n video.removeEventListener('loadstart', onLoadStart);\n video.removeEventListener('canplay', onCanPlay);\n video.removeEventListener('playing', onPlaying);\n };\n }, []);\n\n