@mottosports/motto-video-player
Version:
React video player component for the Motto platform, powered by Shaka Player
1,634 lines (1,624 loc) • 199 kB
JavaScript
"use client";
// src/index.ts
import "shaka-player/dist/controls.css";
// #style-inject:#style-inject
function styleInject(css, { insertAt } = {}) {
if (!css || typeof document === "undefined") return;
const head = document.head || document.getElementsByTagName("head")[0];
const style = document.createElement("style");
style.type = "text/css";
if (insertAt === "top") {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
// src/index.css
styleInject(`/*! tailwindcss v4.1.8 | MIT License | https://tailwindcss.com */
@layer properties;
@layer theme, base, components, utilities;
@layer theme {
:root,
:host {
--font-sans:
ui-sans-serif,
system-ui,
sans-serif,
"Apple Color Emoji",
"Segoe UI Emoji",
"Segoe UI Symbol",
"Noto Color Emoji";
--font-mono:
ui-monospace,
SFMono-Regular,
Menlo,
Monaco,
Consolas,
"Liberation Mono",
"Courier New",
monospace;
--color-red-600: oklch(57.7% 0.245 27.325);
--color-black: #000;
--color-white: #fff;
--spacing: 0.25rem;
--text-xs: 0.75rem;
--text-xs--line-height: calc(1 / 0.75);
--text-sm: 0.875rem;
--text-sm--line-height: calc(1.25 / 0.875);
--text-base: 1rem;
--text-base--line-height: calc(1.5 / 1);
--text-lg: 1.125rem;
--text-lg--line-height: calc(1.75 / 1.125);
--text-xl: 1.25rem;
--text-xl--line-height: calc(1.75 / 1.25);
--text-2xl: 1.5rem;
--text-2xl--line-height: calc(2 / 1.5);
--text-5xl: 3rem;
--text-5xl--line-height: 1;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--tracking-wide: 0.025em;
--tracking-widest: 0.1em;
--radius-md: 0.375rem;
--radius-2xl: 1rem;
--animate-spin: spin 1s linear infinite;
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
--aspect-video: 16 / 9;
--default-transition-duration: 150ms;
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
--default-font-family: var(--font-sans);
--default-mono-font-family: var(--font-mono);
}
}
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
box-sizing: border-box;
margin: 0;
padding: 0;
border: 0 solid;
}
html,
:host {
line-height: 1.5;
-webkit-text-size-adjust: 100%;
tab-size: 4;
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");
font-feature-settings: var(--default-font-feature-settings, normal);
font-variation-settings: var(--default-font-variation-settings, normal);
-webkit-tap-highlight-color: transparent;
}
hr {
height: 0;
color: inherit;
border-top-width: 1px;
}
abbr:where([title]) {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
a {
color: inherit;
-webkit-text-decoration: inherit;
text-decoration: inherit;
}
b,
strong {
font-weight: bolder;
}
code,
kbd,
samp,
pre {
font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
font-feature-settings: var(--default-mono-font-feature-settings, normal);
font-variation-settings: var(--default-mono-font-variation-settings, normal);
font-size: 1em;
}
small {
font-size: 80%;
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
table {
text-indent: 0;
border-color: inherit;
border-collapse: collapse;
}
:-moz-focusring {
outline: auto;
}
progress {
vertical-align: baseline;
}
summary {
display: list-item;
}
ol,
ul,
menu {
list-style: none;
}
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block;
vertical-align: middle;
}
img,
video {
max-width: 100%;
height: auto;
}
button,
input,
select,
optgroup,
textarea,
::file-selector-button {
font: inherit;
font-feature-settings: inherit;
font-variation-settings: inherit;
letter-spacing: inherit;
color: inherit;
border-radius: 0;
background-color: transparent;
opacity: 1;
}
:where(select:is([multiple], [size])) optgroup {
font-weight: bolder;
}
:where(select:is([multiple], [size])) optgroup option {
padding-inline-start: 20px;
}
::file-selector-button {
margin-inline-end: 4px;
}
::placeholder {
opacity: 1;
}
@supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
::placeholder {
color: currentcolor;
@supports (color: color-mix(in lab, red, red)) {
color: color-mix(in oklab, currentcolor 50%, transparent);
}
}
}
textarea {
resize: vertical;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-date-and-time-value {
min-height: 1lh;
text-align: inherit;
}
::-webkit-datetime-edit {
display: inline-flex;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}
::-webkit-datetime-edit,
::-webkit-datetime-edit-year-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-minute-field,
::-webkit-datetime-edit-second-field,
::-webkit-datetime-edit-millisecond-field,
::-webkit-datetime-edit-meridiem-field {
padding-block: 0;
}
:-moz-ui-invalid {
box-shadow: none;
}
button,
input:where([type=button], [type=reset], [type=submit]),
::file-selector-button {
appearance: button;
}
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
[hidden]:where(:not([hidden=until-found])) {
display: none !important;
}
}
@layer utilities {
.pointer-events-none {
pointer-events: none;
}
.visible {
visibility: visible;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.absolute {
position: absolute;
}
.fixed {
position: fixed;
}
.relative {
position: relative;
}
.static {
position: static;
}
.inset-0 {
inset: calc(var(--spacing) * 0);
}
.top-4 {
top: calc(var(--spacing) * 4);
}
.right-0 {
right: calc(var(--spacing) * 0);
}
.right-4 {
right: calc(var(--spacing) * 4);
}
.bottom-0 {
bottom: calc(var(--spacing) * 0);
}
.bottom-4 {
bottom: calc(var(--spacing) * 4);
}
.left-0 {
left: calc(var(--spacing) * 0);
}
.left-4 {
left: calc(var(--spacing) * 4);
}
.z-10 {
z-index: 10;
}
.z-20 {
z-index: 20;
}
.z-50 {
z-index: 50;
}
.container {
width: 100%;
@media (width >= 40rem) {
max-width: 40rem;
}
@media (width >= 48rem) {
max-width: 48rem;
}
@media (width >= 64rem) {
max-width: 64rem;
}
@media (width >= 80rem) {
max-width: 80rem;
}
@media (width >= 96rem) {
max-width: 96rem;
}
}
.m-6 {
margin: calc(var(--spacing) * 6);
}
.mt-1 {
margin-top: calc(var(--spacing) * 1);
}
.mt-3 {
margin-top: calc(var(--spacing) * 3);
}
.mb-2 {
margin-bottom: calc(var(--spacing) * 2);
}
.mb-6 {
margin-bottom: calc(var(--spacing) * 6);
}
.flex {
display: flex;
}
.grid {
display: grid;
}
.aspect-video {
aspect-ratio: var(--aspect-video);
}
.h-2 {
height: calc(var(--spacing) * 2);
}
.h-12 {
height: calc(var(--spacing) * 12);
}
.h-24 {
height: calc(var(--spacing) * 24);
}
.h-full {
height: 100%;
}
.w-2 {
width: calc(var(--spacing) * 2);
}
.w-12 {
width: calc(var(--spacing) * 12);
}
.w-24 {
width: calc(var(--spacing) * 24);
}
.w-full {
width: 100%;
}
.grow {
flex-grow: 1;
}
.animate-pulse {
animation: var(--animate-pulse);
}
.animate-spin {
animation: var(--animate-spin);
}
.auto-cols-max {
grid-auto-columns: max-content;
}
.grid-flow-col {
grid-auto-flow: column;
}
.flex-col {
flex-direction: column;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.justify-stretch {
justify-content: stretch;
}
.gap-1 {
gap: calc(var(--spacing) * 1);
}
.overflow-hidden {
overflow: hidden;
}
.rounded-full {
border-radius: calc(infinity * 1px);
}
.rounded-md {
border-radius: var(--radius-md);
}
.bg-\\[\\#111111\\] {
background-color: #111111;
}
.bg-\\[\\#151515\\] {
background-color: #151515;
}
.bg-black {
background-color: var(--color-black);
}
.bg-red-600 {
background-color: var(--color-red-600);
}
.bg-transparent {
background-color: transparent;
}
.bg-white {
background-color: var(--color-white);
}
.bg-gradient-to-t {
--tw-gradient-position: to top in oklab;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.from-black\\/70 {
--tw-gradient-from: color-mix(in srgb, #000 70%, transparent);
@supports (color: color-mix(in lab, red, red)) {
--tw-gradient-from: color-mix(in oklab, var(--color-black) 70%, transparent);
}
--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));
}
.to-transparent {
--tw-gradient-to: transparent;
--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));
}
.bg-cover {
background-size: cover;
}
.bg-center {
background-position: center;
}
.bg-no-repeat {
background-repeat: no-repeat;
}
.p-2 {
padding: calc(var(--spacing) * 2);
}
.p-4 {
padding: calc(var(--spacing) * 4);
}
.px-2 {
padding-inline: calc(var(--spacing) * 2);
}
.px-4 {
padding-inline: calc(var(--spacing) * 4);
}
.py-1 {
padding-block: calc(var(--spacing) * 1);
}
.text-center {
text-align: center;
}
.text-left {
text-align: left;
}
.font-mono {
font-family: var(--font-mono);
}
.text-2xl {
font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
}
.text-base {
font-size: var(--text-base);
line-height: var(--tw-leading, var(--text-base--line-height));
}
.text-lg {
font-size: var(--text-lg);
line-height: var(--tw-leading, var(--text-lg--line-height));
}
.text-sm {
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
}
.text-xl {
font-size: var(--text-xl);
line-height: var(--tw-leading, var(--text-xl--line-height));
}
.text-xs {
font-size: var(--text-xs);
line-height: var(--tw-leading, var(--text-xs--line-height));
}
.text-\\[10px\\] {
font-size: 10px;
}
.leading-none {
--tw-leading: 1;
line-height: 1;
}
.font-bold {
--tw-font-weight: var(--font-weight-bold);
font-weight: var(--font-weight-bold);
}
.font-medium {
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
}
.font-semibold {
--tw-font-weight: var(--font-weight-semibold);
font-weight: var(--font-weight-semibold);
}
.tracking-wide {
--tw-tracking: var(--tracking-wide);
letter-spacing: var(--tracking-wide);
}
.tracking-widest {
--tw-tracking: var(--tracking-widest);
letter-spacing: var(--tracking-widest);
}
.text-white {
color: var(--color-white);
}
.uppercase {
text-transform: uppercase;
}
.shadow-lg {
--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));
box-shadow:
var(--tw-inset-shadow),
var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow),
var(--tw-ring-shadow),
var(--tw-shadow);
}
.filter {
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,);
}
.md\\:gap-5 {
@media (width >= 48rem) {
gap: calc(var(--spacing) * 5);
}
}
.md\\:rounded-2xl {
@media (width >= 48rem) {
border-radius: var(--radius-2xl);
}
}
.md\\:rounded-2xl\\! {
@media (width >= 48rem) {
border-radius: var(--radius-2xl) !important;
}
}
.md\\:text-5xl {
@media (width >= 48rem) {
font-size: var(--text-5xl);
line-height: var(--tw-leading, var(--text-5xl--line-height));
}
}
.md\\:text-base {
@media (width >= 48rem) {
font-size: var(--text-base);
line-height: var(--tw-leading, var(--text-base--line-height));
}
}
.md\\:text-sm {
@media (width >= 48rem) {
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
}
}
.md\\:text-xl {
@media (width >= 48rem) {
font-size: var(--text-xl);
line-height: var(--tw-leading, var(--text-xl--line-height));
}
}
.md\\:text-xs {
@media (width >= 48rem) {
font-size: var(--text-xs);
line-height: var(--tw-leading, var(--text-xs--line-height));
}
}
}
@layer components {
video::-webkit-media-controls {
display: none !important;
}
video::-webkit-media-controls-panel {
display: none !important;
}
video::-webkit-media-controls-play-button {
display: none !important;
}
video::-webkit-media-controls-timeline {
display: none !important;
}
video::-webkit-media-controls-current-time-display {
display: none !important;
}
video::-webkit-media-controls-time-remaining-display {
display: none !important;
}
video::-webkit-media-controls-mute-button {
display: none !important;
}
video::-webkit-media-controls-volume-slider {
display: none !important;
}
video::-webkit-media-controls-fullscreen-button {
display: none !important;
}
video::-webkit-media-controls-overlay-play-button {
display: none !important;
}
video::-moz-media-controls {
display: none !important;
}
video {
outline: none !important;
}
video[controls] {
-webkit-appearance: none !important;
appearance: none !important;
}
video::-webkit-media-controls-enclosure {
display: none !important;
}
video::-webkit-media-controls-start-playback-button {
display: none !important;
}
video[controls]::-webkit-media-controls,
video[controls]::-webkit-media-controls-panel,
video[controls]::-webkit-media-controls-play-button,
video[controls]::-webkit-media-controls-timeline,
video[controls]::-webkit-media-controls-current-time-display,
video[controls]::-webkit-media-controls-time-remaining-display,
video[controls]::-webkit-media-controls-mute-button,
video[controls]::-webkit-media-controls-volume-slider,
video[controls]::-webkit-media-controls-fullscreen-button,
video[controls]::-webkit-media-controls-overlay-play-button,
video[controls]::-webkit-media-controls-enclosure,
video[controls]::-webkit-media-controls-start-playback-button {
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
}
video[controls]::-moz-media-controls {
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
}
.motto-video-container {
position: relative;
width: 100%;
min-height: 300px;
}
@supports (aspect-ratio: 16/9) {
.motto-video-container {
min-height: auto;
}
}
.motto-video-responsive {
position: absolute;
top: calc(var(--spacing) * 0);
left: calc(var(--spacing) * 0);
height: 100%;
width: 100%;
}
.motto-skip-button {
position: absolute;
top: calc(1/2 * 100%);
z-index: 10;
display: flex;
height: calc(var(--spacing) * 16);
width: calc(var(--spacing) * 16);
--tw-translate-y: calc(calc(1/2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
cursor: pointer;
align-items: center;
justify-content: center;
border-radius: calc(infinity * 1px);
border-style: var(--tw-border-style);
border-width: 0px;
background-color: color-mix(in srgb, #000 70%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-black) 70%, transparent);
}
font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
color: var(--color-white);
opacity: 80%;
transition-property: all;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
--tw-duration: 200ms;
transition-duration: 200ms;
&:hover {
@media (hover: hover) {
--tw-scale-x: 110%;
--tw-scale-y: 110%;
--tw-scale-z: 110%;
scale: var(--tw-scale-x) var(--tw-scale-y);
}
}
&:hover {
@media (hover: hover) {
opacity: 100%;
}
}
&:active {
--tw-scale-x: 95%;
--tw-scale-y: 95%;
--tw-scale-z: 95%;
scale: var(--tw-scale-x) var(--tw-scale-y);
}
}
.motto-skip-button-back {
left: calc(var(--spacing) * 5);
}
.motto-skip-button-forward {
right: calc(var(--spacing) * 5);
}
}
.shaka-seek-bar-container {
height: 6px !important;
width: 100% !important;
margin: 8px 0 !important;
border-radius: 4px !important;
position: relative !important;
border-top: none !important;
border-bottom: none !important;
box-shadow: none !important;
}
.shaka-seek-bar {
height: 6px !important;
width: 100% !important;
-webkit-appearance: none !important;
appearance: none !important;
background: transparent !important;
cursor: pointer !important;
border: none !important;
outline: none !important;
position: absolute !important;
top: 0 !important;
left: 0 !important;
border-radius: 4px !important;
}
.shaka-seek-bar::-webkit-slider-runnable-track {
height: 6px !important;
background: transparent !important;
border-radius: 4px !important;
border: none !important;
}
.shaka-seek-bar::-moz-range-track {
height: 6px !important;
background: transparent !important;
border-radius: 4px !important;
border: none !important;
}
.shaka-seek-bar::-webkit-slider-thumb {
-webkit-appearance: none !important;
appearance: none !important;
width: 16px !important;
height: 16px !important;
border-radius: 50% !important;
background: #ffffff !important;
cursor: pointer !important;
border: 2px solid #ffffff !important;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3) !important;
margin-top: -4px !important;
}
.shaka-seek-bar::-moz-range-thumb {
width: 16px !important;
height: 16px !important;
border-radius: 50% !important;
background: #ffffff !important;
cursor: pointer !important;
border: 2px solid #ffffff !important;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3) !important;
margin-top: -4px !important;
}
.motto-skip-back-button,
.motto-skip-forward-button,
.motto-native-skip-button {
background: transparent !important;
border: none !important;
padding: 4px !important;
margin: 0px !important;
cursor: pointer !important;
color: #ffffff !important;
transition: all 0.2s ease !important;
min-width: 32px !important;
height: 32px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
border-radius: 4px !important;
width: 25px;
}
.motto-skip-back-button:hover,
.motto-skip-forward-button:hover,
.motto-native-skip-button:hover {
opacity: 0.8 !important;
background: transparent !important;
transform: scale(1.05) !important;
}
.motto-skip-back-button:active,
.motto-skip-forward-button:active,
.motto-native-skip-button:active {
transform: scale(0.95) !important;
}
.motto-skip-back-button svg,
.motto-skip-forward-button svg,
.motto-native-skip-button svg {
width: 24px !important;
height: 24px !important;
}
.shaka-spinner-svg {
color: white !important;
fill: white !important;
}
.shaka-spinner-path {
stroke: white !important;
fill: none !important;
}
.shaka-spinner-container {
color: white !important;
}
.shaka-buffering-spinner {
color: white !important;
fill: white !important;
}
.shaka-buffering-spinner svg {
color: white !important;
fill: white !important;
}
.shaka-buffering-spinner path {
stroke: white !important;
fill: none !important;
}
[data-shaka-player-container] .shaka-spinner,
[data-shaka-player-container] .spinner {
color: white !important;
border-color: white !important;
}
.material-icons.shaka-spinner {
color: white !important;
}
.shaka-controls-container .shaka-spinner,
.shaka-video-container .shaka-spinner {
color: white !important;
fill: white !important;
}
.shaka-controls-container .shaka-spinner svg,
.shaka-video-container .shaka-spinner svg {
color: white !important;
fill: white !important;
}
.shaka-controls-container .shaka-spinner path,
.shaka-video-container .shaka-spinner path {
stroke: white !important;
}
.motto-video-loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
linear-gradient(
135deg,
#1a1a1a 0%,
#2d2d2d 100%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
transition: opacity 0.3s ease;
}
.motto-video-loading-overlay.hidden {
opacity: 0;
pointer-events: none;
}
.motto-video-loading-content {
text-align: center;
color: white;
}
.motto-video-loading-icon {
width: 64px;
height: 64px;
margin-bottom: 16px;
opacity: 0.7;
}
.motto-video-loading-text {
font-size: 16px;
font-weight: 500;
margin-bottom: 8px;
}
.motto-video-loading-subtext {
font-size: 14px;
opacity: 0.7;
}
@keyframes pulse-live {
0% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.7;
transform: scale(1.1);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.shaka-play-button {
background: rgba(255, 255, 255, 0.1) !important;
border: none !important;
color: white !important;
padding: 10px !important;
border-radius: 100% !important;
transition: all 0.2s ease !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
min-width: 55px !important;
height: 55px !important;
}
.shaka-play-button-container {
background: transparent;
transition: all 0.2s ease !important;
}
.motto-video-container:not(.no-cursor) .shaka-play-button-container {
background: rgba(0, 0, 0, 0.3);
transition: all 0.s ease !important;
}
.shaka-play-button:hover {
background: rgba(255, 255, 255, 0.2) !important;
transform: scale(1.05) !important;
}
.shaka-play-button:active {
transform: scale(0.95) !important;
}
.shaka-play-button > * {
display: none !important;
}
.shaka-play-button::after {
content: "" !important;
width: 35px !important;
height: 35px !important;
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;
background-repeat: no-repeat !important;
background-size: contain !important;
background-position: center !important;
display: block !important;
}
.shaka-play-button[aria-label*=Play]::after {
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;
}
.shaka-play-button[aria-label*=Pause]::after {
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;
}
.motto-video-container {
background: #111111;
}
.motto-video-container video {
width: 100% !important;
height: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
}
html[dir=rtl] .shaka-controls-container,
html[dir=rtl] .shaka-bottom-controls,
html[dir=rtl] .shaka-controls-button-panel {
direction: ltr !important;
}
html[dir=rtl] .shaka-overflow-menu,
html[dir=rtl] .shaka-settings-menu {
direction: rtl !important;
text-align: right !important;
}
html[dir=rtl] .shaka-overflow-menu .shaka-overflow-button .material-svg-icon:not(:first-child) {
margin-right: 12px !important;
margin-left: 0 !important;
}
html[dir=rtl] .shaka-overflow-menu .shaka-overflow-button .material-svg-icon:first-child {
margin-right: 0 !important;
margin-left: 12px !important;
}
.shaka-hidden-fast-forward-container,
.shaka-hidden-rewind-container {
pointer-events: none !important;
display: none !important;
}
@property --tw-gradient-position { syntax: "*"; inherits: false; }
@property --tw-gradient-from { syntax: "<color>"; inherits: false; initial-value: #0000; }
@property --tw-gradient-via { syntax: "<color>"; inherits: false; initial-value: #0000; }
@property --tw-gradient-to { syntax: "<color>"; inherits: false; initial-value: #0000; }
@property --tw-gradient-stops { syntax: "*"; inherits: false; }
@property --tw-gradient-via-stops { syntax: "*"; inherits: false; }
@property --tw-gradient-from-position { syntax: "<length-percentage>"; inherits: false; initial-value: 0%; }
@property --tw-gradient-via-position { syntax: "<length-percentage>"; inherits: false; initial-value: 50%; }
@property --tw-gradient-to-position { syntax: "<length-percentage>"; inherits: false; initial-value: 100%; }
@property --tw-leading { syntax: "*"; inherits: false; }
@property --tw-font-weight { syntax: "*"; inherits: false; }
@property --tw-tracking { syntax: "*"; inherits: false; }
@property --tw-shadow { syntax: "*"; inherits: false; initial-value: 0 0 #0000; }
@property --tw-shadow-color { syntax: "*"; inherits: false; }
@property --tw-shadow-alpha { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --tw-inset-shadow { syntax: "*"; inherits: false; initial-value: 0 0 #0000; }
@property --tw-inset-shadow-color { syntax: "*"; inherits: false; }
@property --tw-inset-shadow-alpha { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --tw-ring-color { syntax: "*"; inherits: false; }
@property --tw-ring-shadow { syntax: "*"; inherits: false; initial-value: 0 0 #0000; }
@property --tw-inset-ring-color { syntax: "*"; inherits: false; }
@property --tw-inset-ring-shadow { syntax: "*"; inherits: false; initial-value: 0 0 #0000; }
@property --tw-ring-inset { syntax: "*"; inherits: false; }
@property --tw-ring-offset-width { syntax: "<length>"; inherits: false; initial-value: 0px; }
@property --tw-ring-offset-color { syntax: "*"; inherits: false; initial-value: #fff; }
@property --tw-ring-offset-shadow { syntax: "*"; inherits: false; initial-value: 0 0 #0000; }
@property --tw-blur { syntax: "*"; inherits: false; }
@property --tw-brightness { syntax: "*"; inherits: false; }
@property --tw-contrast { syntax: "*"; inherits: false; }
@property --tw-grayscale { syntax: "*"; inherits: false; }
@property --tw-hue-rotate { syntax: "*"; inherits: false; }
@property --tw-invert { syntax: "*"; inherits: false; }
@property --tw-opacity { syntax: "*"; inherits: false; }
@property --tw-saturate { syntax: "*"; inherits: false; }
@property --tw-sepia { syntax: "*"; inherits: false; }
@property --tw-drop-shadow { syntax: "*"; inherits: false; }
@property --tw-drop-shadow-color { syntax: "*"; inherits: false; }
@property --tw-drop-shadow-alpha { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --tw-drop-shadow-size { syntax: "*"; inherits: false; }
@property --tw-translate-x { syntax: "*"; inherits: false; initial-value: 0; }
@property --tw-translate-y { syntax: "*"; inherits: false; initial-value: 0; }
@property --tw-translate-z { syntax: "*"; inherits: false; initial-value: 0; }
@property --tw-border-style { syntax: "*"; inherits: false; initial-value: solid; }
@property --tw-duration { syntax: "*"; inherits: false; }
@property --tw-scale-x { syntax: "*"; inherits: false; initial-value: 1; }
@property --tw-scale-y { syntax: "*"; inherits: false; initial-value: 1; }
@property --tw-scale-z { syntax: "*"; inherits: false; initial-value: 1; }
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@keyframes pulse {
50% {
opacity: 0.5;
}
}
@layer properties {
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
*,
::before,
::after,
::backdrop {
--tw-gradient-position: initial;
--tw-gradient-from: #0000;
--tw-gradient-via: #0000;
--tw-gradient-to: #0000;
--tw-gradient-stops: initial;
--tw-gradient-via-stops: initial;
--tw-gradient-from-position: 0%;
--tw-gradient-via-position: 50%;
--tw-gradient-to-position: 100%;
--tw-leading: initial;
--tw-font-weight: initial;
--tw-tracking: initial;
--tw-shadow: 0 0 #0000;
--tw-shadow-color: initial;
--tw-shadow-alpha: 100%;
--tw-inset-shadow: 0 0 #0000;
--tw-inset-shadow-color: initial;
--tw-inset-shadow-alpha: 100%;
--tw-ring-color: initial;
--tw-ring-shadow: 0 0 #0000;
--tw-inset-ring-color: initial;
--tw-inset-ring-shadow: 0 0 #0000;
--tw-ring-inset: initial;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-offset-shadow: 0 0 #0000;
--tw-blur: initial;
--tw-brightness: initial;
--tw-contrast: initial;
--tw-grayscale: initial;
--tw-hue-rotate: initial;
--tw-invert: initial;
--tw-opacity: initial;
--tw-saturate: initial;
--tw-sepia: initial;
--tw-drop-shadow: initial;
--tw-drop-shadow-color: initial;
--tw-drop-shadow-alpha: 100%;
--tw-drop-shadow-size: initial;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-translate-z: 0;
--tw-border-style: solid;
--tw-duration: initial;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-scale-z: 1;
}
}
}
`);
// src/Player.tsx
import { forwardRef, useEffect as useEffect5, useRef as useRef9, useImperativeHandle, useCallback as useCallback9, useState as useState4 } from "react";
import shaka3 from "shaka-player/dist/shaka-player.ui";
// src/hooks/useShakaPlayer.ts
import { useRef, useCallback, useState } from "react";
import shaka from "shaka-player/dist/shaka-player.ui";
// src/utils/devices.ts
var isAppleDevice = () => {
if (typeof navigator === "undefined") return false;
const ua = navigator.userAgent || "";
const isIOS = /iPad|iPhone|iPod/.test(ua) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
const isSafari = /Safari/.test(ua) && !/Chrome|CriOS|FxiOS|Edg/.test(ua);
const isMacSafari = /Macintosh/.test(ua) && isSafari;
return isIOS || isMacSafari;
};
var isPlayReadySupported = () => {
if (typeof navigator === "undefined" || typeof window === "undefined") {
return false;
}
if (!navigator.requestMediaKeySystemAccess) {
return false;
}
const userAgent = navigator.userAgent || "";
const isXbox = /Xbox/.test(userAgent);
const isEdge = /Edg/.test(userAgent);
const isIE = /Trident|MSIE/.test(userAgent);
const isWindows = /Windows/.test(userAgent);
return isXbox || (isEdge || isIE) && isWindows;
};
var supportsWidevinePersistentLicenses = () => {
return false;
if (typeof navigator === "undefined") {
return false;
}
const userAgent = navigator.userAgent || "";
const isChromeMatch = userAgent.match(/Chrome\/(\d+)/);
if (!isChromeMatch) {
return false;
}
if (/Edg|OPR|Opera/.test(userAgent)) {
return false;
}
const chromeVersion = parseInt(isChromeMatch[1], 10);
if (chromeVersion < 64) {
return false;
}
const isMacOS = /Mac OS X|Macintosh/.test(userAgent);
const isWindows = /Windows/.test(userAgent);
return isMacOS || isWindows;
};
// src/hooks/useShakaPlayer.ts
import initShakaPlayerMux from "@mux/mux-data-shakaplayer";
// package.json
var version = "1.0.1-rc.73";
// src/utils/licenseCache.ts
var PERSISTENT_LICENSE_PREFIX = "motto_lic_";
var LICENSE_EXPIRY_MS = 2 * 60 * 60 * 1e3;
var LICENSE_MAX_RETRY_ATTEMPTS = 10;
var getAllLicenseCacheKeys = () => {
const keys = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key?.startsWith(PERSISTENT_LICENSE_PREFIX)) {
keys.push(key);
}
}
return keys;
};
var getAllLicenseCacheEntries = () => {
const keys = getAllLicenseCacheKeys();
const entries = [];
for (const key of keys) {
try {
const stored = localStorage.getItem(key);
if (stored) {
const data = JSON.parse(stored);
entries.push({ key, data });
}
} catch (error) {
console.warn(`Failed to parse license cache entry for key ${key}:`, error);
localStorage.removeItem(key);
}
}
return entries;
};
var evictLRUEntry = () => {
const entries = getAllLicenseCacheEntries();
if (entries.length === 0) {
return false;
}
entries.sort((a, b) => {
const oldestA = Math.min(...a.data.map((session) => session.timestamp));
const oldestB = Math.min(...b.data.map((session) => session.timestamp));
return oldestA - oldestB;
});
const lruEntry = entries[0];
localStorage.removeItem(lruEntry.key);
console.log(`Evicted LRU license cache entry: ${lruEntry.key}`);
return true;
};
var storeWithQuotaHandling = (key, data) => {
let attempts = 0;
while (attempts < LICENSE_MAX_RETRY_ATTEMPTS) {
try {
localStorage.setItem(key, data);
return true;
} catch (error) {
if (error instanceof DOMException && (error.code === 22 || // QUOTA_EXCEEDED_ERR
error.name === "QuotaExceededError" || error.name === "NS_ERROR_DOM_QUOTA_REACHED")) {
console.warn(`QuotaExceededError on attempt ${attempts + 1}, attempting LRU eviction...`);
if (!evictLRUEntry()) {
console.error("No more entries to evict, storage operation failed");
return false;
}
attempts++;
} else {
console.error("Failed to store license cache data:", error);
return false;
}
}
}
console.error(`Failed to store license cache data after ${LICENSE_MAX_RETRY_ATTEMPTS} eviction attempts`);
return false;
};
var managePersistentLicenseStorage = (playlistId, licenseCacheKey, newSessionMetadata) => {
try {
const storageKey = `${PERSISTENT_LICENSE_PREFIX}${playlistId}_${licenseCacheKey}`;
const stored = localStorage.getItem(storageKey);
let existingSessions = [];
if (stored) {
try {
existingSessions = JSON.parse(stored);
} catch (parseError) {
console.warn("Failed to parse existing persistent license data:", parseError);
existingSessions = [];
}
}
const now = Date.now();
let validSessions = existingSessions.filter(
(session) => now - session.timestamp < LICENSE_EXPIRY_MS
);
if (newSessionMetadata && newSessionMetadata.length > 0) {
const newSessions = newSessionMetadata.map((session) => {
const uint8Array = new Uint8Array(session.initData);
const binaryString = Array.from(uint8Array).map((byte) => String.fromCharCode(byte)).join("");
return {
sessionId: session.sessionId,
initData: btoa(binaryString),
initDataType: session.initDataType,
keySystem: session.keySystem,
timestamp: now
};
});
const newSessionIds = new Set(newSessions.map((s) => s.sessionId));
validSessions = validSessions.filter((session) => !newSessionIds.has(session.sessionId));
validSessions = [...validSessions, ...newSessions];
}
if (validSessions.length === 0) {
localStorage.removeItem(storageKey);
} else {
const dataToStore = JSON.stringify(validSessions);
const success = storeWithQuotaHandling(storageKey, dataToStore);
if (!success) {
console.error(`Failed to store license cache for ${storageKey} after quota handling`);
return validSessions;
}
}
return validSessions;
} catch (error) {
console.warn("Failed to manage persistent license storage:", error);
return [];
}
};
var storePersistentLicense = (playlistId, licenseCacheKey, sessionMetadata) => {
managePersistentLicenseStorage(playlistId, licenseCacheKey, sessionMetadata);
};
var retrievePersistentLicense = (playlistId, licenseCacheKey) => {
try {
const validSessions = managePersistentLicenseStorage(playlistId, licenseCacheKey);
return validSessions.map((session) => ({
sessionId: session.sessionId,
initData: Uint8Array.from(atob(session.initData), (c) => c.charCodeAt(0)).buffer,
initDataType: session.initDataType,
keySystem: session.keySystem
}));
} catch (error) {
console.warn("Failed to retrieve persistent license:", error);
return [];
}
};
var clearAllPersistentLicenses = () => {
try {
const keys = getAllLicenseCacheKeys();
for (const key of keys) {
localStorage.removeItem(key);
}
console.log(`Cleared ${keys.length} persistent license cache entries`);
return keys.length;
} catch (error) {
console.error("Failed to clear persistent licenses:", error);
return 0;
}
};
// src/hooks/useShakaPlayer.ts
var useShakaPlayer = ({
src,
shakaConfig,
drmConfig,
onError,
onPlayerReady,
muxConfig,
onMuxReady,
onMuxDataUpdate,
publicKey,
mottoToken,
hasAds = false
}) => {
const playerRef = useRef(null);
const [isRetrying, setIsRetrying] = useState(false);
const videoElementRef = useRef(null);
const destroyInProgressRef = useRef(null);
const isDestroyingRef = useRef(false);
const initSequenceRef = useRef(0);
const activeInitIdRef = useRef(null);
const waitingForKeyTimerRef = useRef(null);
const waitingForKeyHandlerRef = useRef(null);
const playbackResumedHandlerRef = useRef(null);
const usingPersistentLicenseRef = useRef(false);
const storedPersistentThisLoadRef = useRef(false);
const drmExpirationHandlerRef = useRef(null);
const getManifestUrl = useCallback(() => {
let manifestUrl = src.url;
const isDRM = Boolean(src.drm);
if (isDRM) {
const isPlayReady = isPlayReadySupported();
if (isAppleDevice() && src.drm.fairplay?.certificateUrl) {
manifestUrl = src.drm.fairplay.playlistUrl;
} else if (isPlayReady && src.drm.playready?.licenseUrl) {
manifestUrl = src.drm.playready.playlistUrl;
} else {
manifestUrl = src.drm?.widevine?.playlistUrl || "";
}
}
return manifestUrl;
}, [src]);
const initializePlayerInternal = useCallback(async (video) => {
try {
if (destroyInProgressRef.current) {
try {
await destroyInProgressRef.current;
} catch {
}
}
const myInitId = ++initSequenceRef.current;
activeInitIdRef.current = myInitId;
videoElementRef.current = video;
shaka.polyfill.installAll();
if (!shaka.Player.isBrowserSupported()) {
throw new Error("Browser not supported by Shaka Player");
}
if (isDestroyingRef.current) {
return;
}
const player = new shaka.Player();
playerRef.current = player;
await player.attach(video);
const defaultConfig = {
manifest: {
// Override availability window to allow DVR window to grow from start
// Set to a very large value (24 hours in seconds) to effectively allow unlimited growth
availabilityWindowOverride: 86400
// 24 hours in seconds
},
streaming: {
// Allow seeking to any point within the availability window
safeSeekOffset: 5,
// 5 seconds from live edge to prevent buffering
// Increase tolerance for manifest timing inaccuracies in live streams
inaccurateManifestTolerance: 2
}
};
player.configure(defaultConfig);
if (shakaConfig) {
player.configure(shakaConfig);
}
const manifestUrl = getManifestUrl();
let playlistId = src.id;
const isDRM = Boolean(src.drm);
storedPersistentThisLoadRef.current = false;
if (activeInitIdRef.current !== myInitId || isDestroyingRef.current) {
try {
await player.destroy();
} catch {
}
if (playerRef.current === player) playerRef.current = null;
return;
}
let storedSessionsMetadata = [];
if (isDRM && playlistId) {
storedSessionsMetadata = retrievePersistentLicense(playlistId, src.drm.licenseCacheKey ?? "");
}
if (isDRM) {
const drmConfig2 = {
servers: {
"com.widevine.alpha": src.drm.widevine?.licenseUrl,
"com.microsoft.playready": src.drm.playready?.licenseUrl,
"com.apple.fps": src.drm.fairplay?.licenseUrl
},
keySystemsMapping: {
// Fall back or reroute to the platform’s recommended PlayReady CDM if needed
"com.microsoft.playready": "com.microsoft.playready.recommendation"
},
...src.drm.fairplay && {
advanced: {
"com.apple.fps": {
serverCertificateUri: src.drm.fairplay.certificateUrl
}
}
}
};
drmConfig2.advanced = {
...drmConfig2.advanced,
"com.widevine.alpha": {
...drmConfig2.advanced?.["com.widevine.alpha"],
sessionType: supportsWidevinePersistentLicenses() ? "persistent-license" : "temporary"
},
"com.microsoft.playready": {
...drmConfig2.advanced?.["com.microsoft.playready"],
sessionType: "persistent-license"
// PlayReady seems to always require persistent-license (temporary won't work, not compatible with license server response)
},
"com.apple.fps": {
...drmConfig2.advanced?.["com.apple.fps"],
sessionType: "temporary"
// FairPlay always uses temporary sessions - Safari won't play with persistent-license
}
};
if (storedSessionsMetadata.length > 0) {
drmConfig2.persistentSessionOnlinePlayback = true;
drmConfig2.persistentSessionsMetadata = storedSessionsMetadata.map((session) => ({
sessionId: session.sessionId,
initData: new Uint8Array(session.initData),
initDataType: session.initDataType
}));
}
usingPersistentLicenseRef.current = storedSessionsMetadata.length > 0;
player.configure({ drm: drmConfig2 });
const clearWaitingForKeyTimer = () => {
if (waitingForKeyTimerRef.current !== null) {
window.clearTimeout(waitingForKeyTimerRef.current);
waitingForKeyTimerRef.current = null;
}
};
const onPlaybackResumed = () => {
clearWaitingForKeyTimer();
};
const onWaitingForKey = () => {
if (!usingPersistentLicenseRef.current) return;
if (isRetrying) return;
clearWaitingForKeyTimer();
waitingForKeyTimerRef.current = window.setTimeout(async () => {
try {
if (isRetrying) return;
console.warn("Stuck waiting for decryption key; attempting license cache reset and reload...");
const clearedCount = clearAllPersistentLicenses();
if (clearedCount > 0 && playerRef.current) {
setIsRetrying(true);
await playerRef.current.load(getManifestUrl());
console.log("Reloaded manifest after clearing cached licenses");
} else {
console.warn("No cached licenses found to clear, skipping reload");
}
} catch (e) {
console.error("Failed during recovery from waitingforkey state:", e);
onError?.(e);
} finally {
setIsRetrying(false);
clearWaitingForKeyTimer();
}
}, 3e3);
};
try {
video.addEventListener("waitingforkey", onWaitingForKey);
video.addEventListener("playing", onPlaybackResumed);
video.addEventListener("ended", onPlaybackResumed);
video.addEventListener("pause", onPlaybackResumed);
waitingForKeyHandlerRef.current = onWaitingForKey;
playbackResumedHandlerRef.current = onPlaybackResumed;
} catch (e) {
console.warn("Failed to attach waitingforkey/playback listeners:", e);
}
const netEngine = player.getNetworkingEngine();
if (netEngine) {
netEngine.registerRequestFilter((type, request) => {
switch (type) {
case shaka.net.NetworkingEngine.RequestType.LICENSE:
if (publicKey) {
request.headers["authorization"] = `Bearer ${publicKey}`;
}
if (mottoToken) {
request.headers["x-motto-token"] = mottoToken;
}
break;
case shaka.net.NetworkingEngine.RequestType.MANIFEST:
case shaka.net.NetworkingEngine.RequestType.SEGMENT:
request.allowCrossSiteCredentials = true;
break;
}
});
netEngine.registerResponseFilter((type, response) => {
if (type === shaka.net.NetworkingEngine.RequestType.LICENSE) {
const ks = player.keySystem && player.keySystem();
if (ks === "com.apple.fps") {
const responseText = shaka.util.StringUtils.fromUTF8(response.data);
response.data = shaka.util.Uint8ArrayUtils.fromBase64(responseText).buffer;
}
}
});
}
}
if (isDRM && playlistId) {
const onDRMSessionUpdate = () => {
try {
if (storedPersistentThisLoadRef.current) return;
const activeDrmSessions = player.getActiveSessionsMetadata?.();
if (!activeDrmSessions) return;
const persistentSessions = activeDrmSessions.filter(
(session) => session.sessionType === "persistent-license"
);
if (persistentSessions.length > 0) {
const sessionsToStore = persistentSessions.map((session) => ({
sessionId: session.sessionId,
initData: session.initData,
initDataType: session.initDataType,
keySystem: session.keySystem || "com.widevine.alpha"
}));
storePersistentLicense(playlistId, src.drm.licenseCacheKey ?? "", sessionsToStore);
storedPersistentThisLoadRef.current = true;
}
} catch (e) {
console.warn("Failed to persist licenses on expiration update:", e);
}
};
try {
player.addEventListener("drmsessionupdate", onDRMSessionUpdate);
drmExpirationHandlerRef.current = onDRMSessionUpdate;
} catch (e) {
console.warn("Failed to attach drmsessionupdate listener:", e);
}
}
player?.addEventListener("error", (event) => {
const error = event.detail;
if (error?.code === 7e3) {
return;
}
if (error?.code >= 6e3 && error?.code < 7e3 && !isRetrying && videoElementRef.current) {
console.warn(`DRM error detected (code: ${error.code}), checking for cached licenses...`);
const clearedCount = clearAllPersistentLicenses();
if (clearedCount > 0) {
console.warn(`Cleared ${clearedCount} cached licenses, retrying...`);
setIsRetrying(true);
setTimeout(async () => {
try {
const video2 = videoElementRef.current;
const currentPlayer = playerRef.current;
if (video2 && currentPlayer) {
console.log("Reloading manifest after license cache clear...");
await currentPlayer.load(getManifestUrl());
console.log("Manifest reloaded successfully");
}
} catch (retryError) {
console.error("Failed to retry after license clear:", retryError);
onError?.(retryError);
} finally {
setIsRetrying(false);
}
}, 500);
return;
}