@mottosports/motto-video-player
Version:
React video player component for the Motto platform, powered by Shaka Player
1 lines • 250 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","#style-inject:#style-inject","../src/index.css","../src/Player.tsx","../src/hooks/useShakePlayer.ts","../src/hooks/useQualityControl.ts","../src/hooks/useSkipControls.ts","../src/hooks/useMuxAnalytics.ts","../package.json","../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/components/Loading.tsx","../src/components/ErrorScreen.tsx","../src/components/Title.tsx","../src/components/LiveBadge.tsx","../src/styles.css","../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, VideoListItem } 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-auto {\\n pointer-events: auto;\\n }\\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-0 {\\n top: 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-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 .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 .gap-5 {\\n gap: calc(var(--spacing) * 5);\\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-\\\\[\\\\#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-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-5xl {\\n font-size: var(--text-5xl);\\n line-height: var(--tw-leading, var(--text-5xl--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 .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\\\\: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-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}\\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: black;\\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}\\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-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-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 } from 'react';\nimport shaka from 'shaka-player/dist/shaka-player.ui';\nimport { PlayerProps } from './types';\nimport { \n useShakePlayer, \n useQualityControl, \n useSkipControls, \n useMuxAnalytics, \n useShakaUI,\n useEventHandlers,\n useLiveBadge,\n useLiveIndicator,\n useKeyboardControls\n} from './hooks';\nimport { LiveBadge } from './components';\nimport './styles.css';\nimport { twMerge } from 'tailwind-merge';\n\nexport const Player = forwardRef<HTMLVideoElement, PlayerProps>(\n ({ \n src, \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 imaConfig,\n chromecastConfig,\n qualityConfig,\n seekbarConfig,\n iconSizes,\n events,\n containerClassName,\n ...videoProps \n }, ref) => {\n const videoRef = useRef<HTMLVideoElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const adContainerRef = useRef<HTMLDivElement>(null);\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, destroyPlayer } = useShakePlayer({\n src,\n shakaConfig,\n drmConfig,\n onError: events?.onError,\n onPlayerReady: events?.onPlayerReady\n });\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 { \n initializeMux, \n updateMuxData, \n handleMuxError, \n destroyMux \n } = useMuxAnalytics(playerRef, muxConfig, events?.onMuxReady, events?.onMuxDataUpdate);\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 );\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 // Initialize ads (simplified - would normally be in a separate hook)\n const initializeAds = () => {\n if (!imaConfig || !playerRef.current || !videoRef.current) return;\n\n const adManager = playerRef.current.getAdManager();\n if (!adManager) return;\n\n // Set up ad event listeners\n adManager.addEventListener((shaka as any).ads?.Utils?.AD_STARTED, () => {\n events?.onAdStart?.();\n });\n\n adManager.addEventListener((shaka as any).ads?.Utils?.AD_COMPLETE, () => {\n events?.onAdComplete?.();\n });\n\n adManager.addEventListener((shaka as any).ads?.Utils?.AD_ERROR, (event: any) => {\n events?.onAdError?.(event.detail);\n });\n\n // Initialize client-side ads if ad tag URL is provided\n if (imaConfig.adTagUrl) {\n const adContainer = adContainerRef.current;\n if (adContainer) {\n adManager.initClientSide(adContainer, videoRef.current, imaConfig.adsRenderingSettings);\n \n const adsRequest = {\n adTagUrl: imaConfig.adTagUrl,\n linearAdSlotWidth: imaConfig.linearAdSlotWidth || width as number || 640,\n linearAdSlotHeight: imaConfig.linearAdSlotHeight || height as number || 360,\n nonLinearAdSlotWidth: imaConfig.nonLinearAdSlotWidth || width as number || 640,\n nonLinearAdSlotHeight: imaConfig.nonLinearAdSlotHeight || 150\n };\n \n adManager.requestClientSideAds(adsRequest as any);\n }\n }\n };\n\n // Initialize chromecast (simplified - would normally be in a separate hook)\n const initializeChromecast = () => {\n if (!chromecastConfig?.enabled) {\n return;\n }\n\n try {\n if (events?.onCastStateChange) {\n setTimeout(() => events.onCastStateChange!(false), 100);\n }\n } catch (error) {\n console.warn('Chromecast initialization failed:', error);\n }\n };\n\n // Main initialization\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const initialize = async () => {\n try {\n // Initialize player\n await initializePlayer(video);\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 (seekbar colors are now configured directly in initializeUI)\n await initializeUI();\n \n // Initialize other features\n initializeMux();\n initializeAds();\n initializeChromecast();\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 destroyUI();\n destroyMux();\n destroyPlayer();\n };\n }, [src]);\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 video.muted = muted;\n video.controls = false; // Always disable native controls since we use Shaka UI\n if (poster) video.poster = poster;\n }, [autoPlay, loop, muted, poster]);\n\n // Additional safeguard to ensure native controls are always disabled\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n \n // Aggressively disable native controls to prevent overlapping with Shaka UI\n video.controls = false;\n video.setAttribute('controls', 'false');\n \n // Remove the controls attribute completely\n video.removeAttribute('controls');\n \n // Set up a MutationObserver to watch for the controls attribute being added back\n const observer = new MutationObserver((mutations) => {\n mutations.forEach((mutation) => {\n if (mutation.type === 'attributes' && mutation.attributeName === 'controls') {\n // If controls attribute is added back, immediately remove it\n if (video.hasAttribute('controls')) {\n video.removeAttribute('controls');\n video.controls = false;\n }\n }\n });\n });\n \n // Start observing\n observer.observe(video, {\n attributes: true,\n attributeFilter: ['controls']\n });\n \n // Cleanup function\n return () => {\n observer.disconnect();\n };\n }, []);\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 // Access to underlying instances\n getPlayer: () => playerRef.current,\n getMuxMonitor: () => null\n }), [getAvailableQualities, setQuality, skipBack, skipForward, updateMuxData]);\n\n // Determine if we're using responsive sizing\n const isResponsive = !width && !height;\n \n // Container classes for responsive or fixed sizing\n const containerClasses = twMerge(containerClassName, 'motto-video-container bg-black');\n \n // Container styles - handle both responsive and fixed sizing\n const containerStyle: React.CSSProperties = isResponsive \n ? { \n aspectRatio: aspectRatio.toString(),\n }\n : { width, height };\n\n // Video classes for responsive or fixed sizing \n const videoClasses = isResponsive\n ? 'motto-video-responsive w-full'\n : 'w-full h-full ';\n \n // Video styles for fixed sizing\n const videoStyle: React.CSSProperties = isResponsive\n ? {}\n : { width, height };\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 data-shaka-player-container\n data-shaka-player-cast-receiver-id={chromecastConfig?.receiverApplicationId}\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 {/* Live Badge */}\n <LiveBadge isVisible={isLiveBadgeVisible} />\n {/* <LiveBadge isVisible={true} /> */}\n \n {/* Ad container for IMA */}\n {imaConfig && (\n <div\n ref={adContainerRef}\n className=\"absolute top-0 left-0 w-full h-full pointer-events-auto\"\n />\n )}\n </div>\n );\n }\n);\n\nPlayer.displayName = 'Player'; ","import { useRef, useCallback } from 'react';\nimport { Player as ShakaPlayer, polyfill } from 'shaka-player/dist/shaka-player.ui';\n\ninterface DRMConfig {\n clearKeys?: { [keyId: string]: string };\n servers?: { [keySystem: string]: string };\n}\n\ninterface UseShakePlayerProps {\n src: string;\n shakaConfig?: any;\n drmConfig?: DRMConfig;\n onError?: (error: Error) => void;\n onPlayerReady?: (player: any) => void;\n}\n\nexport const useShakePlayer = ({\n src,\n shakaConfig,\n drmConfig,\n onError,\n onPlayerReady\n}: UseShakePlayerProps) => {\n const playerRef = useRef<any>(null);\n\n const initializePlayer = useCallback(async (video: HTMLVideoElement) => {\n try {\n // Install polyfills\n polyfill.installAll();\n\n // Check for browser support\n if (!ShakaPlayer.isBrowserSupported()) {\n throw new Error('Browser not supported by Shaka Player');\n }\n \n // Create player instance first, then attach the video element.\n // Passing the video element to the constructor **and** calling `attach` afterwards\n // results in a double-attachment which can trigger MediaSource errors on subsequent\n // re‐initialisations (e.g. when the page is resized and the component remounts).\n const player = new (ShakaPlayer 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 // Configure player\n if (shakaConfig) {\n player.configure(shakaConfig);\n }\n\n // Configure DRM\n if (drmConfig) {\n if (drmConfig.clearKeys) {\n player.configure({ 'drm.clearKeys': drmConfig.clearKeys } as any);\n }\n \n if (drmConfig.servers) {\n player.configure({ 'drm.servers': drmConfig.servers } as any);\n }\n }\n\n // Set up error handling\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 console.error('Shaka Player Error:', error);\n onError?.(new Error(`Shaka Player Error: ${error.message || 'Unknown error'}`));\n });\n\n // Load the manifest\n await player.load(src);\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 console.error('Error initializing Shaka Player:', error);\n onError?.(error as Error);\n throw error;\n }\n }, [shakaConfig, drmConfig, src, onError, onPlayerReady]);\n\n const destroyPlayer = useCallback(async () => {\n if (playerRef.current) {\n try {\n await playerRef.current.destroy();\n } catch (error) {\n console.warn('Error destroying Shaka Player:', error);\n } finally {\n playerRef.current = null;\n }\n }\n }, []);\n\n return {\n playerRef,\n initializePlayer,\n destroyPlayer\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[] =>