@zezosoft/react-player 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ import React from "react";
2
+ import "../_components/TimeLine/time-line.css";
3
+ import { IControlsBottomProps } from "../../types";
4
+ declare const BottomControls: React.FC<IControlsBottomProps>;
5
+ export default BottomControls;
@@ -0,0 +1,5 @@
1
+ import * as React from "react";
2
+ import { IControlsHeaderProps } from "../../types";
3
+ import "../_components/styles/video-controls.css";
4
+ declare const ControlsHeader: React.FC<IControlsHeaderProps>;
5
+ export default ControlsHeader;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ declare const MiddleControls: React.FC;
3
+ export default MiddleControls;
@@ -0,0 +1,4 @@
1
+ import * as React from "react";
2
+ import { IPlayerConfig } from "../../types";
3
+ declare const VideoPlayerControls: React.FC<IPlayerConfig>;
4
+ export default VideoPlayerControls;
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { VideoPlayerProps } from "./types/VideoPlayerTypes";
3
- import "../../src/index.css";
3
+ import "../index.css";
4
4
  import "./styles/subtitles.css";
5
5
  declare const VideoPlayer: React.FC<VideoPlayerProps>;
6
6
  export default VideoPlayer;
@@ -3,4 +3,8 @@ export declare const useVideoEvents: () => {
3
3
  onSeeked: (e: React.SyntheticEvent<HTMLVideoElement>) => void;
4
4
  onTimeUpdate: (e: React.SyntheticEvent<HTMLVideoElement>) => void;
5
5
  onLoadedMetadata: (e: React.SyntheticEvent<HTMLVideoElement>) => void;
6
+ onProgress: (e: React.SyntheticEvent<HTMLVideoElement>) => void;
7
+ onPlay: () => void;
8
+ onPause: () => void;
9
+ onEnded: (e: unknown) => void;
6
10
  };
@@ -1 +1,14 @@
1
- export declare const useVideoSource: (trackSrc: string, type?: "hls" | "mp4" | "other" | "youtube" | undefined) => void;
1
+ /**
2
+ * Video Source Hook
3
+ *
4
+ * Manages video source loading and streaming technology detection
5
+ * Supports HLS.js, DASH.js, and native HTML5 video
6
+ *
7
+ * Features:
8
+ * - Automatic stream type detection
9
+ * - HLS.js fallback for older browsers
10
+ * - DASH.js support with proper initialization
11
+ * - Quality level extraction for all stream types
12
+ * - Error handling and cleanup
13
+ */
14
+ export declare const useVideoSource: (trackSrc: string, type?: "hls" | "mp4" | "dash" | "other" | "youtube" | undefined) => void;
@@ -3,6 +3,10 @@ import { IOnWatchTimeUpdated } from "../../types";
3
3
  import { SubtitleStyleConfig } from "../hooks/useSubtitleStyling";
4
4
  export interface VideoPlayerProps {
5
5
  trackSrc: string;
6
+ showControls?: boolean;
7
+ isMute?: boolean;
8
+ onEnded?: (e: React.SyntheticEvent<HTMLVideoElement>) => void;
9
+ onError?: (e?: React.SyntheticEvent<HTMLVideoElement, Event>) => void;
6
10
  trackTitle?: string;
7
11
  trackPoster?: string;
8
12
  isTrailer?: boolean;
@@ -27,3 +27,4 @@ export declare const millisecondsToSeconds: (milliseconds: number) => number;
27
27
  * @returns
28
28
  */
29
29
  export declare const getExtensionFromUrl: (url: string) => string | undefined;
30
+ export { QualityManager } from './qualityManager';
@@ -0,0 +1,78 @@
1
+ import Hls from 'hls.js';
2
+ import * as dashjs from 'dashjs';
3
+ export type StreamType = 'hls' | 'dash' | 'mp4' | 'other';
4
+ /**
5
+ * Video Quality Management Utility
6
+ * Provides a unified interface for quality switching across HLS.js and DASH.js
7
+ *
8
+ * This utility follows OTT-grade best practices for smooth quality switching:
9
+ * - Immediate quality changes without playback interruption
10
+ * - Proper ABR (Adaptive Bitrate) control
11
+ * - Error handling and fallback mechanisms
12
+ * - Support for both manual and auto quality modes
13
+ */
14
+ export declare class QualityManager {
15
+ /**
16
+ * Set video quality for HLS streams with OTT-grade smoothness
17
+ *
18
+ * Best practices implemented:
19
+ * 1. Use currentLevel for immediate quality change
20
+ * 2. Use autoLevelCapping to prevent ABR from switching back
21
+ * 3. Use nextLevel to ensure next segment uses selected quality
22
+ * 4. Handle edge cases and errors gracefully
23
+ *
24
+ * @param hlsInstance HLS.js instance (null for native HLS, undefined when not available)
25
+ * @param levelIndex Quality level index (-1 for auto)
26
+ */
27
+ static setHlsQuality(hlsInstance: Hls | null | undefined, levelIndex: number): void;
28
+ /**
29
+ * Set video quality for DASH streams with OTT-grade smoothness
30
+ *
31
+ * Best practices implemented:
32
+ * 1. Use autoSwitchBitrate settings to control ABR behavior
33
+ * 2. Use setRepresentationForTypeById for immediate quality change
34
+ * 3. Handle representation discovery and selection properly
35
+ * 4. Provide visual feedback through console logs
36
+ *
37
+ * @param dashInstance DASH.js instance
38
+ * @param qualityId Quality level ID (undefined/null for auto)
39
+ */
40
+ static setDashQuality(dashInstance: dashjs.MediaPlayerClass, qualityId: string | null | undefined): void;
41
+ /**
42
+ * Get available quality levels for HLS with proper error handling
43
+ *
44
+ * @param hlsInstance HLS.js instance
45
+ * @returns Array of quality level objects
46
+ */
47
+ static getHlsQualityLevels(hlsInstance: Hls): Array<{
48
+ height: number;
49
+ bitrate?: number;
50
+ originalIndex: number;
51
+ }>;
52
+ /**
53
+ * Get available quality levels for DASH with proper error handling
54
+ *
55
+ * @param dashInstance DASH.js instance
56
+ * @returns Array of quality level objects
57
+ */
58
+ static getDashQualityLevels(dashInstance: dashjs.MediaPlayerClass): Array<{
59
+ height: number;
60
+ bitrate?: number;
61
+ id: string;
62
+ }>;
63
+ /**
64
+ * Unified quality setting function that works with both HLS and DASH
65
+ * Provides a single interface for quality management across streaming technologies
66
+ *
67
+ * @param streamType Type of stream (hls, dash, etc.)
68
+ * @param qualityIdentifier Quality level identifier (index for HLS, ID for DASH)
69
+ */
70
+ static setQuality(streamType: StreamType, qualityIdentifier: string | number): void;
71
+ /**
72
+ * Enable auto quality switching for the current stream type
73
+ *
74
+ * @param streamType Type of stream (hls, dash, etc.)
75
+ */
76
+ static setAutoQuality(streamType: StreamType): void;
77
+ }
78
+ export default QualityManager;
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  interface TooltipProps {
3
3
  children: React.ReactNode;
4
- title: string;
4
+ title: React.ReactNode;
5
5
  position?: "top" | "bottom" | "left" | "right";
6
6
  className?: string;
7
7
  }
package/dist/index.js CHANGED
@@ -3,9 +3,10 @@ import React__default, { memo, useCallback, useEffect, useRef, useState } from '
3
3
  import { create } from 'zustand';
4
4
  import { IoVolumeMuteOutline, IoVolumeHighOutline } from 'react-icons/io5';
5
5
  import { IoMdClose } from 'react-icons/io';
6
- import { Settings as Settings$1, ChevronRight, Check, Loader } from 'lucide-react';
7
- import { FaGooglePlay } from 'react-icons/fa';
6
+ import { Settings as Settings$1, ChevronRight, Check, Loader, ArrowRight } from 'lucide-react';
7
+ import screenfull from 'screenfull';
8
8
  import Hls from 'hls.js';
9
+ import * as dashjs from 'dashjs';
9
10
 
10
11
  function styleInject(css, ref) {
11
12
  if ( ref === void 0 ) ref = {};
@@ -34,7 +35,7 @@ function styleInject(css, ref) {
34
35
  }
35
36
  }
36
37
 
37
- var css_248z$3 = "/*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */\n@layer properties;\n@layer theme, base, components, utilities;\n@layer theme {\n :root, :host {\n --font-sans: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\",\n \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n \"Courier New\", monospace;\n --color-green-500: oklch(72.3% 0.219 149.579);\n --color-blue-500: oklch(62.3% 0.214 259.815);\n --color-purple-500: oklch(62.7% 0.265 303.9);\n --color-gray-200: oklch(92.8% 0.006 264.531);\n --color-gray-300: oklch(87.2% 0.01 258.338);\n --color-gray-400: oklch(70.7% 0.022 261.325);\n --color-gray-500: oklch(55.1% 0.027 264.364);\n --color-gray-600: oklch(44.6% 0.03 256.802);\n --color-gray-900: oklch(21% 0.034 264.665);\n --color-black: #000;\n --color-white: #fff;\n --spacing: 0.25rem;\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-3xl: 1.875rem;\n --text-3xl--line-height: calc(2.25 / 1.875);\n --font-weight-normal: 400;\n --font-weight-semibold: 600;\n --font-weight-bold: 700;\n --radius-md: 0.375rem;\n --radius-lg: 0.5rem;\n --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);\n --animate-spin: spin 1s linear infinite;\n --blur-sm: 8px;\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 *, ::after, ::before, ::backdrop, ::file-selector-button {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0 solid;\n }\n html, :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, h2, h3, h4, h5, 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, strong {\n font-weight: bolder;\n }\n code, kbd, samp, 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, 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, ul, menu {\n list-style: none;\n }\n img, svg, video, canvas, audio, iframe, embed, object {\n display: block;\n vertical-align: middle;\n }\n img, video {\n max-width: 100%;\n height: auto;\n }\n button, input, select, optgroup, textarea, ::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, ::-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 {\n padding-block: 0;\n }\n ::-webkit-calendar-picker-indicator {\n line-height: 1;\n }\n :-moz-ui-invalid {\n box-shadow: none;\n }\n button, input:where([type=\"button\"], [type=\"reset\"], [type=\"submit\"]), ::file-selector-button {\n appearance: button;\n }\n ::-webkit-inner-spin-button, ::-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 .absolute {\n position: absolute;\n }\n .relative {\n position: relative;\n }\n .inset-0 {\n inset: calc(var(--spacing) * 0);\n }\n .-top-2 {\n top: calc(var(--spacing) * -2);\n }\n .top-0 {\n top: calc(var(--spacing) * 0);\n }\n .top-1\\/2 {\n top: calc(1/2 * 100%);\n }\n .top-full {\n top: 100%;\n }\n .right-0 {\n right: calc(var(--spacing) * 0);\n }\n .right-32 {\n right: calc(var(--spacing) * 32);\n }\n .right-full {\n right: 100%;\n }\n .bottom-36 {\n bottom: calc(var(--spacing) * 36);\n }\n .bottom-full {\n bottom: 100%;\n }\n .left-0 {\n left: calc(var(--spacing) * 0);\n }\n .left-1\\/2 {\n left: calc(1/2 * 100%);\n }\n .left-32 {\n left: calc(var(--spacing) * 32);\n }\n .left-full {\n left: 100%;\n }\n .z-50 {\n z-index: 50;\n }\n .z-\\[-1\\] {\n z-index: -1;\n }\n .mx-2 {\n margin-inline: calc(var(--spacing) * 2);\n }\n .mx-auto {\n margin-inline: auto;\n }\n .mt-1 {\n margin-top: calc(var(--spacing) * 1);\n }\n .mt-2 {\n margin-top: calc(var(--spacing) * 2);\n }\n .mr-2 {\n margin-right: calc(var(--spacing) * 2);\n }\n .mb-1 {\n margin-bottom: calc(var(--spacing) * 1);\n }\n .mb-2 {\n margin-bottom: calc(var(--spacing) * 2);\n }\n .mb-4 {\n margin-bottom: calc(var(--spacing) * 4);\n }\n .ml-2 {\n margin-left: calc(var(--spacing) * 2);\n }\n .block {\n display: block;\n }\n .flex {\n display: flex;\n }\n .hidden {\n display: none;\n }\n .inline {\n display: inline;\n }\n .inline-block {\n display: inline-block;\n }\n .h-3 {\n height: calc(var(--spacing) * 3);\n }\n .h-5 {\n height: calc(var(--spacing) * 5);\n }\n .h-6 {\n height: calc(var(--spacing) * 6);\n }\n .h-10 {\n height: calc(var(--spacing) * 10);\n }\n .h-24 {\n height: calc(var(--spacing) * 24);\n }\n .h-full {\n height: 100%;\n }\n .max-h-80 {\n max-height: calc(var(--spacing) * 80);\n }\n .w-3 {\n width: calc(var(--spacing) * 3);\n }\n .w-5 {\n width: calc(var(--spacing) * 5);\n }\n .w-6 {\n width: calc(var(--spacing) * 6);\n }\n .w-24 {\n width: calc(var(--spacing) * 24);\n }\n .w-80 {\n width: calc(var(--spacing) * 80);\n }\n .w-\\[2px\\] {\n width: 2px;\n }\n .w-\\[10vw\\] {\n width: 10vw;\n }\n .w-\\[15vw\\] {\n width: 15vw;\n }\n .w-\\[720px\\] {\n width: 720px;\n }\n .w-fit {\n width: fit-content;\n }\n .w-full {\n width: 100%;\n }\n .-translate-x-1\\/2 {\n --tw-translate-x: calc(calc(1/2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .-translate-y-1\\/2 {\n --tw-translate-y: calc(calc(1/2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .rotate-45 {\n rotate: 45deg;\n }\n .rotate-180 {\n rotate: 180deg;\n }\n .transform {\n transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);\n }\n .animate-spin {\n animation: var(--animate-spin);\n }\n .cursor-not-allowed {\n cursor: not-allowed;\n }\n .cursor-pointer {\n cursor: pointer;\n }\n .flex-col {\n flex-direction: column;\n }\n .items-center {\n align-items: center;\n }\n .items-start {\n align-items: flex-start;\n }\n .justify-between {\n justify-content: space-between;\n }\n .justify-center {\n justify-content: center;\n }\n .gap-3 {\n gap: calc(var(--spacing) * 3);\n }\n .gap-4 {\n gap: calc(var(--spacing) * 4);\n }\n .gap-7 {\n gap: calc(var(--spacing) * 7);\n }\n .space-y-0 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-3 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .overflow-hidden {\n overflow: hidden;\n }\n .overflow-y-auto {\n overflow-y: auto;\n }\n .rounded {\n border-radius: 0.25rem;\n }\n .rounded-\\[5px\\] {\n border-radius: 5px;\n }\n .rounded-\\[7px\\] {\n border-radius: 7px;\n }\n .rounded-lg {\n border-radius: var(--radius-lg);\n }\n .rounded-md {\n border-radius: var(--radius-md);\n }\n .border {\n border-style: var(--tw-border-style);\n border-width: 1px;\n }\n .border-t {\n border-top-style: var(--tw-border-style);\n border-top-width: 1px;\n }\n .border-b {\n border-bottom-style: var(--tw-border-style);\n border-bottom-width: 1px;\n }\n .border-l {\n border-left-style: var(--tw-border-style);\n border-left-width: 1px;\n }\n .border-gray-600 {\n border-color: var(--color-gray-600);\n }\n .border-white\\/10 {\n border-color: color-mix(in srgb, #fff 10%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n border-color: color-mix(in oklab, var(--color-white) 10%, transparent);\n }\n }\n .bg-\\[\\#3a4049\\] {\n background-color: #3a4049;\n }\n .bg-\\[\\#454545\\] {\n background-color: #454545;\n }\n .bg-\\[rgba\\(0\\,0\\,0\\,0\\.5\\)\\] {\n background-color: rgba(0,0,0,0.5);\n }\n .bg-blue-500 {\n background-color: var(--color-blue-500);\n }\n .bg-gray-500 {\n background-color: var(--color-gray-500);\n }\n .bg-gray-900 {\n background-color: var(--color-gray-900);\n }\n .bg-green-500 {\n background-color: var(--color-green-500);\n }\n .bg-purple-500 {\n background-color: var(--color-purple-500);\n }\n .bg-white\\/10 {\n background-color: color-mix(in srgb, #fff 10%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 10%, transparent);\n }\n }\n .bg-white\\/80 {\n background-color: color-mix(in srgb, #fff 80%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 80%, transparent);\n }\n }\n .bg-gradient-to-b {\n --tw-gradient-position: to bottom in oklab;\n background-image: linear-gradient(var(--tw-gradient-stops));\n }\n .from-black {\n --tw-gradient-from: var(--color-black);\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 .p-0 {\n padding: calc(var(--spacing) * 0);\n }\n .p-1 {\n padding: calc(var(--spacing) * 1);\n }\n .p-2 {\n padding: calc(var(--spacing) * 2);\n }\n .p-4 {\n padding: calc(var(--spacing) * 4);\n }\n .p-10 {\n padding: calc(var(--spacing) * 10);\n }\n .px-3 {\n padding-inline: calc(var(--spacing) * 3);\n }\n .px-4 {\n padding-inline: calc(var(--spacing) * 4);\n }\n .px-6 {\n padding-inline: calc(var(--spacing) * 6);\n }\n .px-10 {\n padding-inline: calc(var(--spacing) * 10);\n }\n .px-20 {\n padding-inline: calc(var(--spacing) * 20);\n }\n .py-1 {\n padding-block: calc(var(--spacing) * 1);\n }\n .py-2 {\n padding-block: calc(var(--spacing) * 2);\n }\n .py-3 {\n padding-block: calc(var(--spacing) * 3);\n }\n .py-4 {\n padding-block: calc(var(--spacing) * 4);\n }\n .pt-6 {\n padding-top: calc(var(--spacing) * 6);\n }\n .pb-10 {\n padding-bottom: calc(var(--spacing) * 10);\n }\n .pb-16 {\n padding-bottom: calc(var(--spacing) * 16);\n }\n .text-left {\n text-align: left;\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 .font-bold {\n --tw-font-weight: var(--font-weight-bold);\n font-weight: var(--font-weight-bold);\n }\n .font-normal {\n --tw-font-weight: var(--font-weight-normal);\n font-weight: var(--font-weight-normal);\n }\n .font-semibold {\n --tw-font-weight: var(--font-weight-semibold);\n font-weight: var(--font-weight-semibold);\n }\n .text-black {\n color: var(--color-black);\n }\n .text-gray-200 {\n color: var(--color-gray-200);\n }\n .text-gray-300 {\n color: var(--color-gray-300);\n }\n .text-gray-400 {\n color: var(--color-gray-400);\n }\n .text-gray-500 {\n color: var(--color-gray-500);\n }\n .text-gray-900 {\n color: var(--color-gray-900);\n }\n .text-white {\n color: var(--color-white);\n }\n .opacity-50 {\n opacity: 50%;\n }\n .shadow-2xl {\n --tw-shadow: 0 25px 50px -12px var(--tw-shadow-color, rgb(0 0 0 / 0.25));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n .shadow-md {\n --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n .backdrop-blur-sm {\n --tw-backdrop-blur: blur(var(--blur-sm));\n -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n }\n .transition {\n transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .transition-all {\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 }\n .transition-colors {\n transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .transition-opacity {\n transition-property: opacity;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .duration-200 {\n --tw-duration: 200ms;\n transition-duration: 200ms;\n }\n .ease-in-out {\n --tw-ease: var(--ease-in-out);\n transition-timing-function: var(--ease-in-out);\n }\n .hover\\:bg-gray-300 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--color-gray-300);\n }\n }\n }\n .hover\\:bg-white\\/5 {\n &:hover {\n @media (hover: hover) {\n background-color: color-mix(in srgb, #fff 5%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 5%, transparent);\n }\n }\n }\n }\n .hover\\:bg-white\\/10 {\n &:hover {\n @media (hover: hover) {\n background-color: color-mix(in srgb, #fff 10%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 10%, transparent);\n }\n }\n }\n }\n .hover\\:bg-white\\/90 {\n &:hover {\n @media (hover: hover) {\n background-color: color-mix(in srgb, #fff 90%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 90%, transparent);\n }\n }\n }\n }\n .hover\\:text-gray-200 {\n &:hover {\n @media (hover: hover) {\n color: var(--color-gray-200);\n }\n }\n }\n .focus\\:ring-2 {\n &:focus {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n }\n .focus\\:ring-gray-400 {\n &:focus {\n --tw-ring-color: var(--color-gray-400);\n }\n }\n .focus\\:ring-offset-1 {\n &:focus {\n --tw-ring-offset-width: 1px;\n --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n }\n }\n .focus\\:outline-none {\n &:focus {\n --tw-outline-style: none;\n outline-style: none;\n }\n }\n .disabled\\:cursor-not-allowed {\n &:disabled {\n cursor: not-allowed;\n }\n }\n .disabled\\:bg-white\\/50 {\n &:disabled {\n background-color: color-mix(in srgb, #fff 50%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 50%, transparent);\n }\n }\n }\n .disabled\\:opacity-50 {\n &:disabled {\n opacity: 50%;\n }\n }\n .lg\\:h-32 {\n @media (width >= 64rem) {\n height: calc(var(--spacing) * 32);\n }\n }\n .lg\\:w-32 {\n @media (width >= 64rem) {\n width: calc(var(--spacing) * 32);\n }\n }\n .lg\\:pb-12 {\n @media (width >= 64rem) {\n padding-bottom: calc(var(--spacing) * 12);\n }\n }\n .lg\\:text-2xl {\n @media (width >= 64rem) {\n font-size: var(--text-2xl);\n line-height: var(--tw-leading, var(--text-2xl--line-height));\n }\n }\n .lg\\:text-3xl {\n @media (width >= 64rem) {\n font-size: var(--text-3xl);\n line-height: var(--tw-leading, var(--text-3xl--line-height));\n }\n }\n .lg\\:text-base {\n @media (width >= 64rem) {\n font-size: var(--text-base);\n line-height: var(--tw-leading, var(--text-base--line-height));\n }\n }\n}\n.noCursor {\n cursor: none !important;\n}\n.icon-class {\n height: calc(var(--spacing) * 14);\n width: calc(var(--spacing) * 14);\n cursor: pointer;\n color: var(--color-gray-400);\n transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;\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 color: var(--color-gray-200);\n }\n }\n @media (width >= 64rem) {\n height: calc(var(--spacing) * 18);\n }\n @media (width >= 64rem) {\n width: calc(var(--spacing) * 18);\n }\n}\n@property --tw-translate-x {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-y {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-z {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-rotate-x {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-rotate-y {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-rotate-z {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-skew-x {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-skew-y {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-space-y-reverse {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-border-style {\n syntax: \"*\";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-gradient-position {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-gradient-from {\n syntax: \"<color>\";\n inherits: false;\n initial-value: #0000;\n}\n@property --tw-gradient-via {\n syntax: \"<color>\";\n inherits: false;\n initial-value: #0000;\n}\n@property --tw-gradient-to {\n syntax: \"<color>\";\n inherits: false;\n initial-value: #0000;\n}\n@property --tw-gradient-stops {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-gradient-via-stops {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-gradient-from-position {\n syntax: \"<length-percentage>\";\n inherits: false;\n initial-value: 0%;\n}\n@property --tw-gradient-via-position {\n syntax: \"<length-percentage>\";\n inherits: false;\n initial-value: 50%;\n}\n@property --tw-gradient-to-position {\n syntax: \"<length-percentage>\";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-font-weight {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-shadow-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-shadow-alpha {\n syntax: \"<percentage>\";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-inset-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-shadow-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-inset-shadow-alpha {\n syntax: \"<percentage>\";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-ring-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-ring-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-ring-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-inset-ring-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-ring-inset {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-ring-offset-width {\n syntax: \"<length>\";\n inherits: false;\n initial-value: 0px;\n}\n@property --tw-ring-offset-color {\n syntax: \"*\";\n inherits: false;\n initial-value: #fff;\n}\n@property --tw-ring-offset-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-backdrop-blur {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-brightness {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-contrast {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-grayscale {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-hue-rotate {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-invert {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-opacity {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-saturate {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-sepia {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-duration {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-ease {\n syntax: \"*\";\n inherits: false;\n}\n@keyframes spin {\n to {\n transform: rotate(360deg);\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 *, ::before, ::after, ::backdrop {\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-translate-z: 0;\n --tw-rotate-x: initial;\n --tw-rotate-y: initial;\n --tw-rotate-z: initial;\n --tw-skew-x: initial;\n --tw-skew-y: initial;\n --tw-space-y-reverse: 0;\n --tw-border-style: solid;\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-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-backdrop-blur: initial;\n --tw-backdrop-brightness: initial;\n --tw-backdrop-contrast: initial;\n --tw-backdrop-grayscale: initial;\n --tw-backdrop-hue-rotate: initial;\n --tw-backdrop-invert: initial;\n --tw-backdrop-opacity: initial;\n --tw-backdrop-saturate: initial;\n --tw-backdrop-sepia: initial;\n --tw-duration: initial;\n --tw-ease: initial;\n }\n }\n}\n";
38
+ var css_248z$3 = "/*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */\n@layer properties;\n@layer theme, base, components, utilities;\n@layer theme {\n :root, :host {\n --font-sans: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\",\n \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n \"Courier New\", monospace;\n --color-green-500: oklch(72.3% 0.219 149.579);\n --color-blue-500: oklch(62.3% 0.214 259.815);\n --color-purple-500: oklch(62.7% 0.265 303.9);\n --color-gray-200: oklch(92.8% 0.006 264.531);\n --color-gray-300: oklch(87.2% 0.01 258.338);\n --color-gray-400: oklch(70.7% 0.022 261.325);\n --color-gray-500: oklch(55.1% 0.027 264.364);\n --color-gray-600: oklch(44.6% 0.03 256.802);\n --color-gray-900: oklch(21% 0.034 264.665);\n --color-black: #000;\n --color-white: #fff;\n --spacing: 0.25rem;\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-3xl: 1.875rem;\n --text-3xl--line-height: calc(2.25 / 1.875);\n --font-weight-normal: 400;\n --font-weight-semibold: 600;\n --font-weight-bold: 700;\n --radius-md: 0.375rem;\n --radius-lg: 0.5rem;\n --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);\n --animate-spin: spin 1s linear infinite;\n --blur-sm: 8px;\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 *, ::after, ::before, ::backdrop, ::file-selector-button {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0 solid;\n }\n html, :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, h2, h3, h4, h5, 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, strong {\n font-weight: bolder;\n }\n code, kbd, samp, 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, 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, ul, menu {\n list-style: none;\n }\n img, svg, video, canvas, audio, iframe, embed, object {\n display: block;\n vertical-align: middle;\n }\n img, video {\n max-width: 100%;\n height: auto;\n }\n button, input, select, optgroup, textarea, ::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, ::-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 {\n padding-block: 0;\n }\n ::-webkit-calendar-picker-indicator {\n line-height: 1;\n }\n :-moz-ui-invalid {\n box-shadow: none;\n }\n button, input:where([type=\"button\"], [type=\"reset\"], [type=\"submit\"]), ::file-selector-button {\n appearance: button;\n }\n ::-webkit-inner-spin-button, ::-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 .absolute {\n position: absolute;\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-2 {\n top: calc(var(--spacing) * -2);\n }\n .top-0 {\n top: calc(var(--spacing) * 0);\n }\n .top-1\\/2 {\n top: calc(1/2 * 100%);\n }\n .top-full {\n top: 100%;\n }\n .right-0 {\n right: calc(var(--spacing) * 0);\n }\n .right-32 {\n right: calc(var(--spacing) * 32);\n }\n .right-full {\n right: 100%;\n }\n .bottom-36 {\n bottom: calc(var(--spacing) * 36);\n }\n .bottom-full {\n bottom: 100%;\n }\n .left-0 {\n left: calc(var(--spacing) * 0);\n }\n .left-1\\/2 {\n left: calc(1/2 * 100%);\n }\n .left-32 {\n left: calc(var(--spacing) * 32);\n }\n .left-full {\n left: 100%;\n }\n .z-50 {\n z-index: 50;\n }\n .z-\\[-1\\] {\n z-index: -1;\n }\n .mx-2 {\n margin-inline: calc(var(--spacing) * 2);\n }\n .mx-auto {\n margin-inline: auto;\n }\n .mt-1 {\n margin-top: calc(var(--spacing) * 1);\n }\n .mt-2 {\n margin-top: calc(var(--spacing) * 2);\n }\n .mr-2 {\n margin-right: calc(var(--spacing) * 2);\n }\n .mb-1 {\n margin-bottom: calc(var(--spacing) * 1);\n }\n .mb-2 {\n margin-bottom: calc(var(--spacing) * 2);\n }\n .mb-4 {\n margin-bottom: calc(var(--spacing) * 4);\n }\n .ml-2 {\n margin-left: calc(var(--spacing) * 2);\n }\n .block {\n display: block;\n }\n .flex {\n display: flex;\n }\n .hidden {\n display: none;\n }\n .inline {\n display: inline;\n }\n .inline-block {\n display: inline-block;\n }\n .h-3 {\n height: calc(var(--spacing) * 3);\n }\n .h-5 {\n height: calc(var(--spacing) * 5);\n }\n .h-6 {\n height: calc(var(--spacing) * 6);\n }\n .h-10 {\n height: calc(var(--spacing) * 10);\n }\n .h-24 {\n height: calc(var(--spacing) * 24);\n }\n .h-full {\n height: 100%;\n }\n .max-h-80 {\n max-height: calc(var(--spacing) * 80);\n }\n .w-3 {\n width: calc(var(--spacing) * 3);\n }\n .w-5 {\n width: calc(var(--spacing) * 5);\n }\n .w-6 {\n width: calc(var(--spacing) * 6);\n }\n .w-24 {\n width: calc(var(--spacing) * 24);\n }\n .w-80 {\n width: calc(var(--spacing) * 80);\n }\n .w-\\[2px\\] {\n width: 2px;\n }\n .w-\\[10vw\\] {\n width: 10vw;\n }\n .w-\\[15vw\\] {\n width: 15vw;\n }\n .w-\\[720px\\] {\n width: 720px;\n }\n .w-fit {\n width: fit-content;\n }\n .w-full {\n width: 100%;\n }\n .-translate-x-1\\/2 {\n --tw-translate-x: calc(calc(1/2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .-translate-y-1\\/2 {\n --tw-translate-y: calc(calc(1/2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .rotate-45 {\n rotate: 45deg;\n }\n .rotate-180 {\n rotate: 180deg;\n }\n .transform {\n transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);\n }\n .animate-spin {\n animation: var(--animate-spin);\n }\n .cursor-not-allowed {\n cursor: not-allowed;\n }\n .cursor-pointer {\n cursor: pointer;\n }\n .flex-col {\n flex-direction: column;\n }\n .items-center {\n align-items: center;\n }\n .items-start {\n align-items: flex-start;\n }\n .justify-between {\n justify-content: space-between;\n }\n .justify-center {\n justify-content: center;\n }\n .gap-3 {\n gap: calc(var(--spacing) * 3);\n }\n .gap-4 {\n gap: calc(var(--spacing) * 4);\n }\n .gap-7 {\n gap: calc(var(--spacing) * 7);\n }\n .space-y-0 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-3 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .overflow-hidden {\n overflow: hidden;\n }\n .overflow-y-auto {\n overflow-y: auto;\n }\n .rounded {\n border-radius: 0.25rem;\n }\n .rounded-\\[5px\\] {\n border-radius: 5px;\n }\n .rounded-\\[7px\\] {\n border-radius: 7px;\n }\n .rounded-lg {\n border-radius: var(--radius-lg);\n }\n .rounded-md {\n border-radius: var(--radius-md);\n }\n .border {\n border-style: var(--tw-border-style);\n border-width: 1px;\n }\n .border-t {\n border-top-style: var(--tw-border-style);\n border-top-width: 1px;\n }\n .border-b {\n border-bottom-style: var(--tw-border-style);\n border-bottom-width: 1px;\n }\n .border-l {\n border-left-style: var(--tw-border-style);\n border-left-width: 1px;\n }\n .border-gray-600 {\n border-color: var(--color-gray-600);\n }\n .border-white\\/10 {\n border-color: color-mix(in srgb, #fff 10%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n border-color: color-mix(in oklab, var(--color-white) 10%, transparent);\n }\n }\n .bg-\\[\\#3a4049\\] {\n background-color: #3a4049;\n }\n .bg-\\[\\#454545\\] {\n background-color: #454545;\n }\n .bg-\\[rgba\\(0\\,0\\,0\\,0\\.5\\)\\] {\n background-color: rgba(0,0,0,0.5);\n }\n .bg-blue-500 {\n background-color: var(--color-blue-500);\n }\n .bg-gray-500 {\n background-color: var(--color-gray-500);\n }\n .bg-gray-900 {\n background-color: var(--color-gray-900);\n }\n .bg-green-500 {\n background-color: var(--color-green-500);\n }\n .bg-purple-500 {\n background-color: var(--color-purple-500);\n }\n .bg-white\\/10 {\n background-color: color-mix(in srgb, #fff 10%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 10%, transparent);\n }\n }\n .bg-white\\/80 {\n background-color: color-mix(in srgb, #fff 80%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 80%, transparent);\n }\n }\n .bg-gradient-to-b {\n --tw-gradient-position: to bottom in oklab;\n background-image: linear-gradient(var(--tw-gradient-stops));\n }\n .from-black {\n --tw-gradient-from: var(--color-black);\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 .p-0 {\n padding: calc(var(--spacing) * 0);\n }\n .p-1 {\n padding: calc(var(--spacing) * 1);\n }\n .p-2 {\n padding: calc(var(--spacing) * 2);\n }\n .p-4 {\n padding: calc(var(--spacing) * 4);\n }\n .p-10 {\n padding: calc(var(--spacing) * 10);\n }\n .px-3 {\n padding-inline: calc(var(--spacing) * 3);\n }\n .px-4 {\n padding-inline: calc(var(--spacing) * 4);\n }\n .px-6 {\n padding-inline: calc(var(--spacing) * 6);\n }\n .px-10 {\n padding-inline: calc(var(--spacing) * 10);\n }\n .px-20 {\n padding-inline: calc(var(--spacing) * 20);\n }\n .py-1 {\n padding-block: calc(var(--spacing) * 1);\n }\n .py-2 {\n padding-block: calc(var(--spacing) * 2);\n }\n .py-3 {\n padding-block: calc(var(--spacing) * 3);\n }\n .py-4 {\n padding-block: calc(var(--spacing) * 4);\n }\n .pt-6 {\n padding-top: calc(var(--spacing) * 6);\n }\n .pb-10 {\n padding-bottom: calc(var(--spacing) * 10);\n }\n .pb-16 {\n padding-bottom: calc(var(--spacing) * 16);\n }\n .text-left {\n text-align: left;\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 .font-bold {\n --tw-font-weight: var(--font-weight-bold);\n font-weight: var(--font-weight-bold);\n }\n .font-normal {\n --tw-font-weight: var(--font-weight-normal);\n font-weight: var(--font-weight-normal);\n }\n .font-semibold {\n --tw-font-weight: var(--font-weight-semibold);\n font-weight: var(--font-weight-semibold);\n }\n .whitespace-nowrap {\n white-space: nowrap;\n }\n .text-gray-200 {\n color: var(--color-gray-200);\n }\n .text-gray-300 {\n color: var(--color-gray-300);\n }\n .text-gray-400 {\n color: var(--color-gray-400);\n }\n .text-gray-500 {\n color: var(--color-gray-500);\n }\n .text-gray-900 {\n color: var(--color-gray-900);\n }\n .text-white {\n color: var(--color-white);\n }\n .opacity-50 {\n opacity: 50%;\n }\n .shadow-2xl {\n --tw-shadow: 0 25px 50px -12px var(--tw-shadow-color, rgb(0 0 0 / 0.25));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n .shadow-md {\n --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n .backdrop-blur-sm {\n --tw-backdrop-blur: blur(var(--blur-sm));\n -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n }\n .transition {\n transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .transition-all {\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 }\n .transition-colors {\n transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .transition-opacity {\n transition-property: opacity;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .duration-200 {\n --tw-duration: 200ms;\n transition-duration: 200ms;\n }\n .ease-in-out {\n --tw-ease: var(--ease-in-out);\n transition-timing-function: var(--ease-in-out);\n }\n .hover\\:bg-gray-300 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--color-gray-300);\n }\n }\n }\n .hover\\:bg-white\\/5 {\n &:hover {\n @media (hover: hover) {\n background-color: color-mix(in srgb, #fff 5%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 5%, transparent);\n }\n }\n }\n }\n .hover\\:bg-white\\/10 {\n &:hover {\n @media (hover: hover) {\n background-color: color-mix(in srgb, #fff 10%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 10%, transparent);\n }\n }\n }\n }\n .hover\\:bg-white\\/90 {\n &:hover {\n @media (hover: hover) {\n background-color: color-mix(in srgb, #fff 90%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 90%, transparent);\n }\n }\n }\n }\n .hover\\:text-gray-200 {\n &:hover {\n @media (hover: hover) {\n color: var(--color-gray-200);\n }\n }\n }\n .focus\\:ring-2 {\n &:focus {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n }\n .focus\\:ring-gray-400 {\n &:focus {\n --tw-ring-color: var(--color-gray-400);\n }\n }\n .focus\\:ring-offset-1 {\n &:focus {\n --tw-ring-offset-width: 1px;\n --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n }\n }\n .focus\\:outline-none {\n &:focus {\n --tw-outline-style: none;\n outline-style: none;\n }\n }\n .disabled\\:cursor-not-allowed {\n &:disabled {\n cursor: not-allowed;\n }\n }\n .disabled\\:bg-white\\/50 {\n &:disabled {\n background-color: color-mix(in srgb, #fff 50%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 50%, transparent);\n }\n }\n }\n .disabled\\:opacity-50 {\n &:disabled {\n opacity: 50%;\n }\n }\n .lg\\:h-32 {\n @media (width >= 64rem) {\n height: calc(var(--spacing) * 32);\n }\n }\n .lg\\:w-32 {\n @media (width >= 64rem) {\n width: calc(var(--spacing) * 32);\n }\n }\n .lg\\:pb-12 {\n @media (width >= 64rem) {\n padding-bottom: calc(var(--spacing) * 12);\n }\n }\n .lg\\:text-2xl {\n @media (width >= 64rem) {\n font-size: var(--text-2xl);\n line-height: var(--tw-leading, var(--text-2xl--line-height));\n }\n }\n .lg\\:text-3xl {\n @media (width >= 64rem) {\n font-size: var(--text-3xl);\n line-height: var(--tw-leading, var(--text-3xl--line-height));\n }\n }\n .lg\\:text-base {\n @media (width >= 64rem) {\n font-size: var(--text-base);\n line-height: var(--tw-leading, var(--text-base--line-height));\n }\n }\n}\n.noCursor {\n cursor: none !important;\n}\n.icon-class {\n height: calc(var(--spacing) * 14);\n width: calc(var(--spacing) * 14);\n cursor: pointer;\n color: var(--color-gray-400);\n transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;\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 color: var(--color-gray-200);\n }\n }\n @media (width >= 64rem) {\n height: calc(var(--spacing) * 18);\n }\n @media (width >= 64rem) {\n width: calc(var(--spacing) * 18);\n }\n}\n@property --tw-translate-x {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-y {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-z {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-rotate-x {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-rotate-y {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-rotate-z {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-skew-x {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-skew-y {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-space-y-reverse {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-border-style {\n syntax: \"*\";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-gradient-position {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-gradient-from {\n syntax: \"<color>\";\n inherits: false;\n initial-value: #0000;\n}\n@property --tw-gradient-via {\n syntax: \"<color>\";\n inherits: false;\n initial-value: #0000;\n}\n@property --tw-gradient-to {\n syntax: \"<color>\";\n inherits: false;\n initial-value: #0000;\n}\n@property --tw-gradient-stops {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-gradient-via-stops {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-gradient-from-position {\n syntax: \"<length-percentage>\";\n inherits: false;\n initial-value: 0%;\n}\n@property --tw-gradient-via-position {\n syntax: \"<length-percentage>\";\n inherits: false;\n initial-value: 50%;\n}\n@property --tw-gradient-to-position {\n syntax: \"<length-percentage>\";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-font-weight {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-shadow-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-shadow-alpha {\n syntax: \"<percentage>\";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-inset-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-shadow-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-inset-shadow-alpha {\n syntax: \"<percentage>\";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-ring-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-ring-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-ring-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-inset-ring-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-ring-inset {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-ring-offset-width {\n syntax: \"<length>\";\n inherits: false;\n initial-value: 0px;\n}\n@property --tw-ring-offset-color {\n syntax: \"*\";\n inherits: false;\n initial-value: #fff;\n}\n@property --tw-ring-offset-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-backdrop-blur {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-brightness {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-contrast {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-grayscale {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-hue-rotate {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-invert {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-opacity {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-saturate {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-sepia {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-duration {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-ease {\n syntax: \"*\";\n inherits: false;\n}\n@keyframes spin {\n to {\n transform: rotate(360deg);\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 *, ::before, ::after, ::backdrop {\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-translate-z: 0;\n --tw-rotate-x: initial;\n --tw-rotate-y: initial;\n --tw-rotate-z: initial;\n --tw-skew-x: initial;\n --tw-skew-y: initial;\n --tw-space-y-reverse: 0;\n --tw-border-style: solid;\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-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-backdrop-blur: initial;\n --tw-backdrop-brightness: initial;\n --tw-backdrop-contrast: initial;\n --tw-backdrop-grayscale: initial;\n --tw-backdrop-hue-rotate: initial;\n --tw-backdrop-invert: initial;\n --tw-backdrop-opacity: initial;\n --tw-backdrop-saturate: initial;\n --tw-backdrop-sepia: initial;\n --tw-duration: initial;\n --tw-ease: initial;\n }\n }\n}\n";
38
39
  styleInject(css_248z$3,{"insertAt":"top"});
39
40
 
40
41
  const createVideoRefsSlice = (set) => ({
@@ -62,6 +63,8 @@ const createVideoTimingSlice = (set) => ({
62
63
  setCurrentTime: (currentTime) => set({ currentTime }),
63
64
  duration: 0,
64
65
  setDuration: (duration) => set({ duration }),
66
+ bufferedProgress: 0,
67
+ setBufferedProgress: (progress) => set({ bufferedProgress: progress }),
65
68
  });
66
69
 
67
70
  const createVideoControlsSlice = (set) => ({
@@ -69,15 +72,21 @@ const createVideoControlsSlice = (set) => ({
69
72
  setControls: (controls) => set({ controls }),
70
73
  isFullscreen: false,
71
74
  setIsFullscreen: (isFullscreen) => set({ isFullscreen }),
75
+ controlsVisible: true,
76
+ setControlsVisible: (visible) => set({ controlsVisible: visible }),
72
77
  });
73
78
 
74
79
  const createVideoQualitySlice = (set) => ({
75
80
  hlsInstance: undefined,
76
81
  setHlsInstance: (hlsInstance) => set({ hlsInstance }),
82
+ dashInstance: undefined,
83
+ setDashInstance: (dashInstance) => set({ dashInstance }),
77
84
  qualityLevels: undefined,
78
85
  setQualityLevels: (qualityLevels) => set({ qualityLevels }),
79
86
  activeQuality: "auto",
80
87
  setActiveQuality: (activeQuality) => set({ activeQuality }),
88
+ streamType: "mp4",
89
+ setStreamType: (streamType) => set({ streamType }),
81
90
  });
82
91
 
83
92
  const createSubtitlesSlice = (set) => ({
@@ -105,22 +114,47 @@ const createIntroSlice = (set) => ({
105
114
  setShowIntroSkip: (show) => set({ showIntroSkip: show }),
106
115
  });
107
116
 
108
- const useVideoStore = create()((...a) => ({
109
- ...createVideoRefsSlice(...a),
110
- ...createVideoPlaybackSlice(...a),
111
- ...createVideoTimingSlice(...a),
112
- ...createVideoControlsSlice(...a),
113
- ...createVideoQualitySlice(...a),
114
- ...createSubtitlesSlice(...a),
115
- ...createEpisodesSlice(...a),
116
- ...createIntroSlice(...a),
117
+ const createResetSlice = (set, get) => ({
118
+ resetStore: () => {
119
+ set({
120
+ videoRef: null,
121
+ videoWrapperRef: null,
122
+ playing: false,
123
+ isBuffering: false,
124
+ isPlaying: false,
125
+ muted: false,
126
+ volume: 1,
127
+ currentTime: 0,
128
+ duration: 0,
129
+ bufferedProgress: 0,
130
+ controls: false,
131
+ isFullscreen: false,
132
+ hlsInstance: undefined,
133
+ qualityLevels: undefined,
134
+ activeQuality: "auto",
135
+ activeSubtitle: null,
136
+ subtitles: [],
137
+ episodeList: [],
138
+ currentEpisodeIndex: 0,
139
+ showCountdown: false,
140
+ countdownTime: 10,
141
+ autoPlayNext: false,
142
+ showIntroSkip: false,
143
+ });
144
+ },
145
+ });
146
+
147
+ const useVideoStore = create()((set, get, store) => ({
148
+ ...createVideoRefsSlice(set),
149
+ ...createVideoPlaybackSlice(set),
150
+ ...createVideoTimingSlice(set),
151
+ ...createVideoControlsSlice(set),
152
+ ...createVideoQualitySlice(set),
153
+ ...createSubtitlesSlice(set),
154
+ ...createEpisodesSlice(set),
155
+ ...createIntroSlice(set),
156
+ ...createResetSlice(set),
117
157
  }));
118
- // Previous version before recent edits code changes in panding
119
- // ...createVideoRefsSlice(...a),
120
- // ...createVideoPlaybackSlice(...a),
121
- // ...createVideoTimingSlice(...a),
122
- // ...createEpisodesSlice(...a),
123
- // ...createIntroSlice(...a),
124
158
 
125
159
  /**
126
160
  * @description
@@ -158,6 +192,9 @@ const getExtensionFromUrl = (url) => {
158
192
  if (extension === "m3u8") {
159
193
  return "hls";
160
194
  }
195
+ if (extension === "mpd") {
196
+ return "dash";
197
+ }
161
198
  return extension;
162
199
  };
163
200
 
@@ -395,14 +432,14 @@ const VideoSeekSlider = ({ max = 1000, currentTime = 0, bufferTime = 0, hideThum
395
432
  React__default.createElement(Thumb, { max: max, currentTime: currentTime, isThumbActive: isThumbActive, trackColor: trackColor })));
396
433
  };
397
434
 
398
- var css_248z$2 = ".ui-video-seek-slider {\n position: relative;\n touch-action: none;\n}\n.ui-video-seek-slider:focus {\n outline: none;\n}\n.ui-video-seek-slider .track {\n padding: 0;\n cursor: pointer;\n outline: none;\n}\n.ui-video-seek-slider .track:focus {\n border: 0;\n outline: none;\n}\n.ui-video-seek-slider .track .main {\n width: 100%;\n outline: none;\n height: 18px;\n top: 0;\n position: absolute;\n display: flex;\n align-items: center;\n box-sizing: border-box;\n}\n.ui-video-seek-slider .track .main:before {\n content: \"\";\n position: absolute;\n width: 100%;\n height: 3px;\n background-color: rgba(255, 255, 255, 0.2);\n overflow: hidden;\n transition: height 0.1s;\n outline: none;\n}\n.ui-video-seek-slider .track .main .inner-seek-block {\n position: absolute;\n width: 100%;\n height: 3px;\n transition: height 0.1s, opacity 0.4s;\n transform-origin: 0 0;\n}\n.ui-video-seek-slider .track .main:focus {\n border: 0;\n outline: none;\n}\n.ui-video-seek-slider .track .main .buffered {\n background-color: rgba(255, 255, 255, 0.3);\n z-index: 2;\n}\n.ui-video-seek-slider .track .main .seek-hover {\n background-color: rgba(255, 255, 255, 0.5);\n z-index: 1;\n}\n.ui-video-seek-slider .track .main .connect {\n background-color: #ff0000;\n z-index: 3;\n transform-origin: 0 0;\n}\n.ui-video-seek-slider .track .main.with-gap .inner-seek-block,\n.ui-video-seek-slider .track .main.with-gap:before {\n width: calc(100% - 2px);\n margin: 0 auto;\n}\n@media (hover) {\n .ui-video-seek-slider .track .main:hover:before {\n height: 8px;\n }\n .ui-video-seek-slider .track .main:hover .inner-seek-block {\n height: 8px;\n }\n}\n.ui-video-seek-slider .thumb {\n pointer-events: none;\n position: absolute;\n width: 12px;\n height: 12px;\n left: -6px;\n z-index: 4;\n top: 3px;\n}\n.ui-video-seek-slider .thumb .handler {\n border-radius: 100%;\n width: 100%;\n height: 100%;\n background-color: #ff0000;\n opacity: 0;\n transform: scale(0.4);\n transition: transform 0.2s, opacity 0.2s;\n}\n.ui-video-seek-slider .thumb.active .handler {\n opacity: 1;\n transform: scale(1);\n}\n.ui-video-seek-slider .hover-time {\n text-shadow: 1px 1px 1px #000;\n position: absolute;\n line-height: 18px;\n font-size: 16px;\n color: #ddd;\n bottom: 5px;\n left: 0;\n padding: 5px 10px;\n opacity: 0;\n pointer-events: none;\n text-align: center;\n}\n.ui-video-seek-slider .hover-time.active {\n opacity: 1;\n}\n.ui-video-seek-slider .hover-time .preview-screen {\n background-repeat: no-repeat;\n background-size: cover;\n background-position: center;\n width: 200px;\n height: 110px;\n border-radius: 5px;\n background-color: #000;\n margin: 0 auto 10px;\n}\n.ui-video-seek-slider:hover .track .main .seek-hover {\n opacity: 1;\n}\n";
435
+ var css_248z$2 = ".ui-video-seek-slider {\n position: relative;\n touch-action: none;\n}\n.ui-video-seek-slider:focus {\n outline: none;\n}\n.ui-video-seek-slider .track {\n padding: 0;\n cursor: pointer;\n outline: none;\n}\n.ui-video-seek-slider .track:focus {\n border: 0;\n outline: none;\n}\n.ui-video-seek-slider .track .main {\n width: 100%;\n outline: none;\n height: 18px;\n top: 0;\n position: absolute;\n display: flex;\n align-items: center;\n box-sizing: border-box;\n}\n.ui-video-seek-slider .track .main:before {\n content: \"\";\n position: absolute;\n width: 100%;\n height: 3px;\n background-color: rgba(255, 255, 255, 0.2);\n overflow: hidden;\n transition: height 0.1s;\n outline: none;\n}\n.ui-video-seek-slider .track .main .inner-seek-block {\n position: absolute;\n width: 100%;\n height: 3px;\n transition: height 0.1s, opacity 0.4s, transform 0.2s ease-out;\n transform-origin: 0 0;\n}\n.ui-video-seek-slider .track .main:focus {\n border: 0;\n outline: none;\n}\n.ui-video-seek-slider .track .main .buffered {\n background-color: rgba(255, 255, 255, 0.3);\n z-index: 2;\n transition: transform 0.2s ease-out;\n}\n.ui-video-seek-slider .track .main .seek-hover {\n background-color: rgba(255, 255, 255, 0.5);\n z-index: 1;\n}\n.ui-video-seek-slider .track .main .connect {\n background-color: #ff0000;\n z-index: 3;\n transform-origin: 0 0;\n}\n.ui-video-seek-slider .track .main.with-gap .inner-seek-block,\n.ui-video-seek-slider .track .main.with-gap:before {\n width: calc(100% - 2px);\n margin: 0 auto;\n}\n@media (hover) {\n .ui-video-seek-slider .track .main:hover:before {\n height: 8px;\n }\n .ui-video-seek-slider .track .main:hover .inner-seek-block {\n height: 8px;\n }\n}\n.ui-video-seek-slider .thumb {\n pointer-events: none;\n position: absolute;\n width: 12px;\n height: 12px;\n left: -6px;\n z-index: 4;\n top: 3px;\n}\n.ui-video-seek-slider .thumb .handler {\n border-radius: 100%;\n width: 100%;\n height: 100%;\n background-color: #ff0000;\n opacity: 0;\n transform: scale(0.4);\n transition: transform 0.2s, opacity 0.2s;\n}\n.ui-video-seek-slider .thumb.active .handler {\n opacity: 1;\n transform: scale(1);\n}\n.ui-video-seek-slider .hover-time {\n text-shadow: 1px 1px 1px #000;\n position: absolute;\n line-height: 18px;\n font-size: 16px;\n color: #ddd;\n bottom: 5px;\n left: 0;\n padding: 5px 10px;\n opacity: 0;\n pointer-events: none;\n text-align: center;\n}\n.ui-video-seek-slider .hover-time.active {\n opacity: 1;\n}\n.ui-video-seek-slider .hover-time .preview-screen {\n background-repeat: no-repeat;\n background-size: cover;\n background-position: center;\n width: 200px;\n height: 110px;\n border-radius: 5px;\n background-color: #000;\n margin: 0 auto 10px;\n}\n.ui-video-seek-slider:hover .track .main .seek-hover {\n opacity: 1;\n}\n";
399
436
  styleInject(css_248z$2,{"insertAt":"top"});
400
437
 
401
438
  const BottomControls = ({ config }) => {
402
- const { videoRef, currentTime, isFullscreen } = useVideoStore();
439
+ const { videoRef, currentTime, isFullscreen, bufferedProgress } = useVideoStore();
403
440
  const duration = videoRef?.duration;
404
441
  return (React__default.createElement("div", { className: "px-10" },
405
- React__default.createElement(VideoSeekSlider, { max: secondsToMilliseconds(duration || 0), currentTime: secondsToMilliseconds(currentTime || 0), bufferTime: secondsToMilliseconds(0), onChange: (currentTime) => {
442
+ React__default.createElement(VideoSeekSlider, { max: secondsToMilliseconds(duration || 0), currentTime: secondsToMilliseconds(currentTime || 0), bufferTime: secondsToMilliseconds((duration || 0) * (bufferedProgress / 100)), onChange: (currentTime) => {
406
443
  if (videoRef) {
407
444
  videoRef.currentTime = currentTime / 1000;
408
445
  }
@@ -423,7 +460,7 @@ const Tooltip = ({ children, title, position = "top", }) => {
423
460
  };
424
461
  return (React__default.createElement("div", { className: "relative inline-block cursor-pointer", onMouseEnter: () => setVisible(true), onMouseLeave: () => setVisible(false) },
425
462
  children,
426
- visible && (React__default.createElement("div", { className: `absolute z-50 px-3 py-1 text-sm text-white bg-gray-900 rounded-md shadow-md transition-opacity duration-200 ease-in-out ${positionStyles[position]}` }, title))));
463
+ visible && (React__default.createElement("div", { className: `absolute z-50 px-3 py-1 text-sm text-white bg-gray-900 rounded-md shadow-md transition-opacity duration-200 ease-in-out whitespace-nowrap ${positionStyles[position]}` }, title))));
427
464
  };
428
465
 
429
466
  var css_248z$1 = ".icon-button {\n width: 20px;\n height: 20px;\n cursor: pointer;\n color: #9ca3af;\n transition: color 0.2s ease-in-out;\n}\n\n.icon-button:hover {\n color: #e5e7eb;\n}\n\n@media (min-width: 1024px) {\n .icon-button {\n width: 32px;\n height: 32px;\n }\n}\n\n/* styles/fullscreen-toggle.css */\n.fullscreen-icon {\n width: 20px;\n height: 20px;\n cursor: pointer;\n color: #9ca3af;\n transition: color 0.2s ease-in-out;\n}\n\n.fullscreen-icon:hover {\n color: #e5e7eb;\n}\n\n@media (min-width: 1024px) {\n .fullscreen-icon {\n width: 32px;\n height: 32px;\n }\n}\n\n/* styles/pi-picture-in-picture-toggle.css */\n.pip-toggle {\n cursor: pointer;\n color: #9ca3af;\n transition: color 0.2s ease-in-out;\n}\n\n.pip-toggle:hover {\n color: #e5e7eb;\n}\n\n.pip-icon {\n width: 15px;\n height: 15px;\n}\n\n@media (min-width: 1024px) {\n .pip-icon {\n width: 28px;\n height: 28px;\n }\n}\n";
@@ -655,50 +692,52 @@ const Settings = ({ iconClassName }) => {
655
692
 
656
693
  const ControlsHeader = ({ config }) => {
657
694
  const iconClassName = "icon-button";
658
- const { videoWrapperRef, videoRef, episodeList, currentEpisodeIndex } = useVideoStore();
695
+ const { videoWrapperRef, videoRef, episodeList, currentEpisodeIndex, resetStore, } = useVideoStore();
659
696
  const [isPipActive, setIsPipActive] = React.useState(false);
660
- const isFullscreen = document.fullscreenElement !== null;
697
+ const [isFullscreen, setIsFullscreen] = React.useState(false);
661
698
  const handleFullscreen = () => {
662
- if (isPipActive)
699
+ if (!screenfull.isEnabled || isPipActive)
663
700
  return;
664
- if (document.fullscreenElement) {
665
- document.exitFullscreen();
701
+ if (screenfull.isFullscreen) {
702
+ screenfull.exit();
666
703
  }
667
- else {
668
- videoWrapperRef?.requestFullscreen();
704
+ else if (videoWrapperRef) {
705
+ screenfull.request(videoWrapperRef);
669
706
  }
670
707
  };
708
+ React.useEffect(() => {
709
+ if (!screenfull.isEnabled)
710
+ return;
711
+ const changeHandler = () => setIsFullscreen(screenfull.isFullscreen);
712
+ screenfull.on("change", changeHandler);
713
+ return () => {
714
+ screenfull.off("change", changeHandler);
715
+ };
716
+ }, []);
671
717
  const handleMute = () => {
672
718
  if (videoRef) {
673
719
  videoRef.muted = !videoRef.muted;
674
720
  }
675
721
  };
676
722
  const handlePipToggle = async () => {
677
- if (videoRef) {
723
+ if (!videoRef)
724
+ return;
725
+ try {
678
726
  if (!document.pictureInPictureElement && !isPipActive) {
679
- try {
680
- await videoRef.requestPictureInPicture();
681
- setIsPipActive(true);
682
- }
683
- catch (error) {
684
- console.error("PiP mode failed:", error);
685
- }
727
+ await videoRef.requestPictureInPicture();
728
+ setIsPipActive(true);
686
729
  }
687
730
  else if (document.pictureInPictureElement && isPipActive) {
688
- try {
689
- await document.exitPictureInPicture();
690
- setIsPipActive(false);
691
- }
692
- catch (error) {
693
- console.error("Exit PiP failed:", error);
694
- }
731
+ await document.exitPictureInPicture();
732
+ setIsPipActive(false);
695
733
  }
696
734
  }
735
+ catch (error) {
736
+ console.error("PiP toggle failed:", error);
737
+ }
697
738
  };
698
739
  React.useEffect(() => {
699
- const handlePipChange = () => {
700
- setIsPipActive(!!document.pictureInPictureElement);
701
- };
740
+ const handlePipChange = () => setIsPipActive(!!document.pictureInPictureElement);
702
741
  document.addEventListener("enterpictureinpicture", handlePipChange);
703
742
  document.addEventListener("leavepictureinpicture", handlePipChange);
704
743
  return () => {
@@ -706,6 +745,12 @@ const ControlsHeader = ({ config }) => {
706
745
  document.removeEventListener("leavepictureinpicture", handlePipChange);
707
746
  };
708
747
  }, []);
748
+ const handleClose = () => {
749
+ resetStore();
750
+ if (config?.onClose) {
751
+ config.onClose();
752
+ }
753
+ };
709
754
  return (React.createElement("div", { className: "flex items-center justify-between p-10 bg-gradient-to-b from-black" },
710
755
  React.createElement("div", { className: "flex" },
711
756
  React.createElement("div", null,
@@ -721,79 +766,64 @@ const ControlsHeader = ({ config }) => {
721
766
  React.createElement(Tooltip, { title: isPipActive
722
767
  ? "Disabled in PiP"
723
768
  : isFullscreen
724
- ? "Exit"
769
+ ? "Exit Fullscreen"
725
770
  : "Fullscreen", className: `${iconClassName} ${isPipActive ? "opacity-50 cursor-not-allowed" : ""}` },
726
771
  React.createElement("div", { onClick: handleFullscreen, className: isPipActive ? "pointer-events-none" : "" },
727
772
  React.createElement(FullScreenToggle, { isFullScreen: isFullscreen, className: iconClassName }))),
728
- React.createElement(Tooltip, { title: isPipActive ? "Exit PiP" : "Enter PiP" },
773
+ React.createElement(Tooltip, { className: "whitespace-nowrap", title: isPipActive ? "Exit PiP" : "Enter PiP" },
729
774
  React.createElement("div", { onClick: handlePipToggle },
730
775
  React.createElement(PiPictureInPictureToggle, { className: iconClassName }))),
731
776
  config?.onClose && (React.createElement(React.Fragment, null,
732
777
  React.createElement("div", { className: "w-[2px] h-10 bg-gray-500 hover:bg-gray-300 mx-2" }),
733
- React.createElement("div", { onClick: config.onClose },
778
+ React.createElement("div", { onClick: handleClose },
734
779
  React.createElement(Tooltip, { title: "Close" },
735
780
  React.createElement(IoMdClose, { className: iconClassName }))))))));
736
781
  };
737
782
 
783
+ const ControlButton = ({ onClick, icon, className, }) => (React__default.createElement("button", { onClick: onClick, className: `flex justify-center items-center h-full cursor-pointer ${className}` }, icon));
738
784
  const MiddleControls = () => {
739
- const { videoRef, isPlaying, setIsPlaying, isBuffering, setIsBuffering } = useVideoStore();
740
- const handlePlayPause = () => {
741
- if (!videoRef)
785
+ const { videoRef, isPlaying, setIsPlaying } = useVideoStore();
786
+ const [isBuffering, setIsBuffering] = useState(false);
787
+ const videoElement = videoRef;
788
+ const handlePlayPause = useCallback(() => {
789
+ if (!videoElement)
742
790
  return;
743
- if (videoRef.paused) {
744
- videoRef
791
+ if (videoElement.paused) {
792
+ videoElement
745
793
  .play()
746
- .catch((error) => console.error("Error playing video:", error));
747
- setIsPlaying(true);
794
+ .then(() => setIsPlaying(true))
795
+ .catch((err) => console.error("Error playing video:", err));
748
796
  }
749
797
  else {
750
- videoRef.pause();
798
+ videoElement.pause();
751
799
  setIsPlaying(false);
752
800
  }
753
- };
754
- const handleBackword = () => {
755
- if (!videoRef)
801
+ }, [videoElement, setIsPlaying]);
802
+ const handleBackward = useCallback(() => {
803
+ if (!videoElement)
756
804
  return;
757
- videoRef.currentTime -= 10;
758
- };
759
- const handleForword = () => {
760
- if (!videoRef)
761
- return;
762
- videoRef.currentTime += 10;
763
- };
764
- // Handle buffering state
765
- useEffect(() => {
766
- if (!videoRef)
805
+ videoElement.currentTime = Math.max(0, videoElement.currentTime - 10);
806
+ }, [videoElement]);
807
+ const handleForward = useCallback(() => {
808
+ if (!videoElement)
767
809
  return;
768
- const handleWaiting = () => {
769
- setIsBuffering(true);
770
- };
771
- const handlePlaying = () => {
772
- setIsBuffering(false);
773
- };
774
- videoRef.addEventListener("waiting", handleWaiting);
775
- videoRef.addEventListener("playing", handlePlaying);
776
- return () => {
777
- videoRef.removeEventListener("waiting", handleWaiting);
778
- videoRef.removeEventListener("playing", handlePlaying);
779
- };
780
- }, [videoRef, setIsBuffering]);
810
+ videoElement.currentTime = Math.min(videoElement.duration, videoElement.currentTime + 10);
811
+ }, [videoElement]);
781
812
  useEffect(() => {
782
- if (!videoRef)
813
+ if (!videoElement)
783
814
  return;
784
815
  const handleWaiting = () => setIsBuffering(true);
785
816
  const handlePlaying = () => setIsBuffering(false);
786
- videoRef.addEventListener("waiting", handleWaiting);
787
- videoRef.addEventListener("playing", handlePlaying);
817
+ videoElement.addEventListener("waiting", handleWaiting);
818
+ videoElement.addEventListener("playing", handlePlaying);
788
819
  return () => {
789
- videoRef.removeEventListener("waiting", handleWaiting);
790
- videoRef.removeEventListener("playing", handlePlaying);
820
+ videoElement.removeEventListener("waiting", handleWaiting);
821
+ videoElement.removeEventListener("playing", handlePlaying);
791
822
  };
792
- }, [videoRef, setIsBuffering]);
793
- // keyboard controls
823
+ }, [videoElement]);
794
824
  useEffect(() => {
795
825
  const handleKeyDown = (e) => {
796
- if (!videoRef)
826
+ if (!videoElement)
797
827
  return;
798
828
  switch (e.code) {
799
829
  case "Space":
@@ -801,31 +831,31 @@ const MiddleControls = () => {
801
831
  handlePlayPause();
802
832
  break;
803
833
  case "ArrowLeft":
804
- handleBackword();
834
+ e.preventDefault();
835
+ handleBackward();
805
836
  break;
806
837
  case "ArrowRight":
807
- handleForword();
838
+ e.preventDefault();
839
+ handleForward();
808
840
  break;
809
841
  }
810
842
  };
811
843
  window.addEventListener("keydown", handleKeyDown);
812
844
  return () => window.removeEventListener("keydown", handleKeyDown);
813
- }, [videoRef, isPlaying]);
845
+ }, [videoElement, handlePlayPause, handleBackward, handleForward]);
814
846
  return (React__default.createElement("div", { className: "flex justify-center items-center" },
815
- React__default.createElement("div", { onClick: handleBackword, className: "w-[15vw] flex justify-center items-center h-full cursor-pointer" },
816
- React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: "icon-class", fill: "currentColor", viewBox: "0 0 67 67" },
817
- React__default.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M33.5 0C52 0 67 15 67 33.5S52 67 33.5 67 0 52 0 33.5c.03-1.4 1.17-2.53 2.58-2.53 1.4 0 2.55 1.13 2.57 2.53 0 15.65 12.7 28.35 28.35 28.35 15.66 0 28.35-12.7 28.35-28.35 0-15.66-12.69-28.35-28.35-28.35h-.04c-7 0-13.76 2.61-18.94 7.3-.46.42-.91.85-1.34 1.29h6.58c1.42 0 2.57 1.16 2.57 2.58 0 1.42-1.15 2.58-2.57 2.58H6.01c-1.42 0-2.57-1.16-2.57-2.58V2.58C3.44 1.15 4.59 0 6.01 0c1.43 0 2.58 1.15 2.58 2.58v8.52c.78-.86 1.61-1.7 2.47-2.47A33.407 33.407 0 0 1 33.46 0h.04zM33.98 41.34c-1.6-2.21-2-5.2-2-7.85 0-2.65.4-5.63 2-7.83 1.44-1.97 3.47-2.84 5.88-2.84 2.41 0 4.42.87 5.86 2.84 1.61 2.21 2.03 5.16 2.03 7.83 0 2.66-.4 5.64-2 7.85-1.43 1.97-3.47 2.84-5.89 2.84-2.41 0-4.45-.86-5.88-2.84zm-9.73-12.77l-5 1.58v-4.21l5.87-2.65h4.28v20.47h-5.15V28.57zm17.61 9.96c.61-1.33.68-3.6.68-5.04s-.07-3.7-.68-5.02c-.4-.86-1.04-1.29-2-1.29-.95 0-1.59.42-1.99 1.29-.61 1.32-.68 3.58-.68 5.02 0 1.44.07 3.71.68 5.04.4.87 1.04 1.29 1.99 1.29.96 0 1.6-.42 2-1.29z" }))),
818
- React__default.createElement("div", { onClick: handlePlayPause, className: "w-[10vw] flex justify-center items-center h-full cursor-pointer" }, isBuffering ? (React__default.createElement(Loader, { className: "w-24 h-24 lg:w-32 lg:h-32 animate-spin text-white" })) : isPlaying ? (React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: "icon-class", fill: "currentColor", viewBox: "0 0 67 67" },
819
- React__default.createElement("path", { fillRule: "evenodd", d: "M46.332 5.773a4.125 4.125 0 0 0-4.125 4.125v46.75a4.127 4.127 0 0 0 4.125 4.125 4.127 4.127 0 0 0 4.125-4.125V9.898a4.125 4.125 0 0 0-4.125-4.125zM25.707 9.898v46.75a4.125 4.125 0 1 1-8.25 0V9.898a4.123 4.123 0 0 1 4.125-4.125 4.123 4.123 0 0 1 4.125 4.125z", clipRule: "evenodd" }))) : (React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: "icon-class", fill: "currentColor", viewBox: "0 0 67 67" },
820
- React__default.createElement("path", { d: "M20.28 9.65c-2.205-1.268-4.026-.228-4.026 2.307v43.805c0 2.535 1.82 3.574 4.027 2.307l38.471-21.903a2.556 2.556 0 0 0 1.094-.935 2.514 2.514 0 0 0 0-2.743 2.556 2.556 0 0 0-1.093-.936L20.28 9.65z" })))),
821
- React__default.createElement("div", { onClick: handleForword, className: "w-[15vw] flex justify-center items-center h-full cursor-pointer" },
822
- React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: "icon-class", fill: "currentColor", viewBox: "0 0 67 67" },
823
- React__default.createElement("path", { fillRule: "evenodd", d: "M33.5 0C15 0 0 15 0 33.5S15 67 33.5 67 67 52 67 33.5a2.583 2.583 0 0 0-2.58-2.53c-1.4 0-2.55 1.13-2.57 2.53 0 15.66-12.69 28.35-28.35 28.35-15.65 0-28.35-12.7-28.35-28.35 0-15.66 12.7-28.35 28.35-28.35 7.3 0 13.96 2.76 18.99 7.3.46.42.9.85 1.34 1.29h-6.59a2.58 2.58 0 0 0 0 5.16h13.75c1.42 0 2.57-1.16 2.57-2.58V2.58c0-1.43-1.15-2.58-2.57-2.58-1.43 0-2.58 1.15-2.58 2.58v8.52c-.78-.87-1.61-1.7-2.47-2.48A33.446 33.446 0 0 0 33.54 0h-.04zM33.98 41.34c-1.6-2.21-2-5.2-2-7.85 0-2.65.4-5.63 2-7.83 1.44-1.97 3.47-2.84 5.88-2.84 2.41 0 4.42.87 5.86 2.84 1.61 2.21 2.03 5.16 2.03 7.83 0 2.66-.4 5.64-2 7.85-1.43 1.97-3.47 2.84-5.89 2.84-2.41 0-4.45-.87-5.88-2.84zm-9.73-12.77l-5 1.58v-4.21l5.87-2.65h4.28v20.47h-5.15V28.57zm17.61 9.96c.61-1.33.68-3.6.68-5.04s-.07-3.7-.68-5.02c-.4-.87-1.04-1.29-2-1.29-.95 0-1.59.42-1.99 1.29-.61 1.32-.68 3.58-.68 5.02 0 1.44.07 3.71.68 5.04.4.86 1.04 1.28 1.99 1.28.96 0 1.6-.42 2-1.28z" })))));
847
+ React__default.createElement(ControlButton, { onClick: handleBackward, className: "w-[15vw]", icon: React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: "icon-class", fill: "currentColor", viewBox: "0 0 67 67" },
848
+ React__default.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M33.5 0C52 0 67 15 67 33.5S52 67 33.5 67 0 52 0 33.5c.03-1.4 1.17-2.53 2.58-2.53 1.4 0 2.55 1.13 2.57 2.53 0 15.65 12.7 28.35 28.35 28.35 15.66 0 28.35-12.7 28.35-28.35 0-15.66-12.69-28.35-28.35-28.35h-.04c-7 0-13.76 2.61-18.94 7.3-.46.42-.91.85-1.34 1.29h6.58c1.42 0 2.57 1.16 2.57 2.58 0 1.42-1.15 2.58-2.57 2.58H6.01c-1.42 0-2.57-1.16-2.57-2.58V2.58C3.44 1.15 4.59 0 6.01 0c1.43 0 2.58 1.15 2.58 2.58v8.52c.78-.86 1.61-1.7 2.47-2.47A33.407 33.407 0 0 1 33.46 0h.04zM33.98 41.34c-1.6-2.21-2-5.2-2-7.85 0-2.65.4-5.63 2-7.83 1.44-1.97 3.47-2.84 5.88-2.84 2.41 0 4.42.87 5.86 2.84 1.61 2.21 2.03 5.16 2.03 7.83 0 2.66-.4 5.64-2 7.85-1.43 1.97-3.47 2.84-5.89 2.84-2.41 0-4.45-.86-5.88-2.84zm-9.73-12.77l-5 1.58v-4.21l5.87-2.65h4.28v20.47h-5.15V28.57zm17.61 9.96c.61-1.33.68-3.6.68-5.04s-.07-3.7-.68-5.02c-.4-.86-1.04-1.29-2-1.29-.95 0-1.59.42-1.99 1.29-.61 1.32-.68 3.58-.68 5.02 0 1.44.07 3.71.68 5.04.4.87 1.04 1.29 1.99 1.29.96 0 1.6-.42 2-1.29z" })) }),
849
+ React__default.createElement(ControlButton, { onClick: handlePlayPause, className: "w-[10vw]", icon: isBuffering ? (React__default.createElement(Loader, { className: "w-24 h-24 lg:w-32 lg:h-32 animate-spin text-white" })) : isPlaying ? (React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: "icon-class", fill: "currentColor", viewBox: "0 0 67 67" },
850
+ React__default.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M46.332 5.773a4.125 4.125 0 0 0-4.125 4.125v46.75a4.127 4.127 0 0 0 4.125 4.125 4.127 4.127 0 0 0 4.125-4.125V9.898a4.125 4.125 0 0 0-4.125-4.125zM25.707 9.898v46.75a4.125 4.125 0 1 1-8.25 0V9.898a4.123 4.123 0 0 1 4.125-4.125 4.123 4.123 0 0 1 4.125 4.125z" }))) : (React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: "icon-class", fill: "currentColor", viewBox: "0 0 67 67" },
851
+ React__default.createElement("path", { d: "M20.28 9.65c-2.205-1.268-4.026-.228-4.026 2.307v43.805c0 2.535 1.82 3.574 4.027 2.307l38.471-21.903a2.556 2.556 0 0 0 1.094-.935 2.514 2.514 0 0 0 0-2.743 2.556 2.556 0 0 0-1.093-.936L20.28 9.65z" }))) }),
852
+ React__default.createElement(ControlButton, { onClick: handleForward, className: "w-[15vw]", icon: React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: "icon-class", fill: "currentColor", viewBox: "0 0 67 67" },
853
+ React__default.createElement("path", { fillRule: "evenodd", d: "M33.5 0C15 0 0 15 0 33.5S15 67 33.5 67 67 52 67 33.5a2.583 2.583 0 0 0-2.58-2.53c-1.4 0-2.55 1.13-2.57 2.53 0 15.66-12.69 28.35-28.35 28.35-15.65 0-28.35-12.7-28.35-28.35 0-15.66 12.7-28.35 28.35-28.35 7.3 0 13.96 2.76 18.99 7.3.46.42.9.85 1.34 1.29h-6.59a2.58 2.58 0 0 0 0 5.16h13.75c1.42 0 2.57-1.16 2.57-2.58V2.58c0-1.43-1.15-2.58-2.57-2.58-1.43 0-2.58 1.15-2.58 2.58v8.52c-.78-.87-1.61-1.7-2.47-2.48A33.446 33.446 0 0 0 33.54 0h-.04zM33.98 41.34c-1.6-2.21-2-5.2-2-7.85 0-2.65.4-5.63 2-7.83 1.44-1.97 3.47-2.84 5.88-2.84 2.41 0 4.42.87 5.86 2.84 1.61 2.21 2.03 5.16 2.03 7.83 0 2.66-.4 5.64-2 7.85-1.43 1.97-3.47 2.84-5.89 2.84-2.41 0-4.45-.87-5.88-2.84zm-9.73-12.77l-5 1.58v-4.21l5.87-2.65h4.28v20.47h-5.15V28.57zm17.61 9.96c.61-1.33.68-3.6.68-5.04s-.07-3.7-.68-5.02c-.4-.87-1.04-1.29-2-1.29-.95 0-1.59.42-1.99 1.29-.61 1.32-.68 3.58-.68 5.02 0 1.44.07 3.71.68 5.04.4.86 1.04 1.28 1.99 1.28.96 0 1.6-.42 2-1.28z" })) })));
824
854
  };
825
855
 
826
- const VideoPlayerControls = ({ config, height, width, }) => {
856
+ const VideoPlayerControls = ({ config }) => {
827
857
  return (React.createElement("div", { className: "px-20" },
828
- React.createElement("div", { className: `absolute top-0 left-0 ${height || "h-full"} ${width || "w-full"} bg-[rgba(0,0,0,0.5)] flex flex-col justify-between` },
858
+ React.createElement("div", { className: "absolute top-0 left-0 h-full w-full bg-[rgba(0,0,0,0.5)] flex flex-col justify-between" },
829
859
  React.createElement(ControlsHeader, { config: config?.headerConfig?.config }),
830
860
  React.createElement(MiddleControls, null),
831
861
  React.createElement(BottomControls, { config: config?.bottomConfig?.config }))));
@@ -848,71 +878,59 @@ const Overlay = ({ config }) => {
848
878
  const controlsTimerRef = useRef(null);
849
879
  const { setControls, controls, showCountdown, countdownTime, setShowCountdown, setAutoPlayNext, setCurrentEpisodeIndex, episodeList, setCountdownTime, videoRef, currentEpisodeIndex, } = useVideoStore();
850
880
  const { onClose } = config?.headerConfig?.config || {};
851
- const handleMouseEnter = useCallback(() => {
852
- const videoPlayerControls = document?.getElementById("videoPlayerControls");
853
- if (videoPlayerControls) {
854
- videoPlayerControls.classList.remove("noCursor");
855
- }
856
- setControls(true);
881
+ const HIDE_DELAY = 2000;
882
+ const resetControlsTimer = useCallback(() => {
857
883
  if (controlsTimerRef.current) {
858
884
  clearTimeout(controlsTimerRef.current);
859
885
  }
860
886
  controlsTimerRef.current = setTimeout(() => {
861
887
  setControls(false);
888
+ const videoPlayerControls = document?.getElementById("videoPlayerControls");
862
889
  if (videoPlayerControls) {
863
890
  videoPlayerControls.classList.add("noCursor");
864
891
  }
865
- }, 2000);
892
+ }, HIDE_DELAY);
866
893
  }, [setControls]);
867
- React.useEffect(() => {
894
+ const handleMouseEnter = useCallback(() => {
895
+ const videoPlayerControls = document?.getElementById("videoPlayerControls");
896
+ if (videoPlayerControls) {
897
+ videoPlayerControls.classList.remove("noCursor");
898
+ }
899
+ setControls(true);
900
+ resetControlsTimer();
901
+ }, [setControls, resetControlsTimer]);
902
+ useEffect(() => {
903
+ return () => {
904
+ if (controlsTimerRef.current) {
905
+ clearTimeout(controlsTimerRef.current);
906
+ }
907
+ };
908
+ }, []);
909
+ useEffect(() => {
868
910
  let timer;
869
911
  if (showCountdown && countdownTime > 0 && episodeList.length > 0) {
870
912
  timer = setInterval(() => {
871
913
  setCountdownTime(countdownTime - 1);
872
914
  }, 1000);
873
915
  }
874
- else if (showCountdown && countdownTime === 0 && episodeList.length > 0) {
875
- const nextIndex = currentEpisodeIndex + 1;
876
- if (nextIndex < episodeList.length) {
877
- setCurrentEpisodeIndex(nextIndex);
878
- setAutoPlayNext(true);
879
- if (videoRef && episodeList[nextIndex]) {
880
- videoRef.src = episodeList[nextIndex].url;
881
- videoRef
882
- .play()
883
- .catch((err) => console.error("Auto-play failed:", err));
884
- }
885
- }
886
- else {
887
- if (onClose)
888
- onClose();
889
- }
890
- setShowCountdown(false);
891
- }
892
916
  return () => {
893
917
  if (timer)
894
918
  clearInterval(timer);
895
919
  };
896
- }, [
897
- showCountdown,
898
- countdownTime,
899
- episodeList.length,
900
- setCountdownTime,
901
- setCurrentEpisodeIndex,
902
- currentEpisodeIndex,
903
- setAutoPlayNext,
904
- videoRef,
905
- episodeList,
906
- onClose,
907
- ]);
920
+ }, [showCountdown, countdownTime, episodeList.length, setCountdownTime]);
908
921
  const handleNextEpisodeManually = () => {
909
922
  const nextIndex = currentEpisodeIndex + 1;
910
923
  if (nextIndex < episodeList.length && videoRef && episodeList[nextIndex]) {
911
924
  setCurrentEpisodeIndex(nextIndex);
912
925
  setAutoPlayNext(true);
913
926
  videoRef.src = episodeList[nextIndex].url;
914
- videoRef.play().catch((err) => console.error("Manual play failed:", err));
927
+ videoRef
928
+ .play()
929
+ .catch((err) => console.error("Manual play failed:", err));
915
930
  setShowCountdown(false);
931
+ setCountdownTime(10);
932
+ setControls(true);
933
+ resetControlsTimer();
916
934
  }
917
935
  else if (onClose) {
918
936
  onClose();
@@ -922,7 +940,7 @@ const Overlay = ({ config }) => {
922
940
  controls && React.createElement(VideoPlayerControls, { config: config }),
923
941
  showCountdown &&
924
942
  episodeList.length > 0 &&
925
- currentEpisodeIndex + 1 < episodeList.length && (React.createElement(VideoActionButton, { text: "Next Episode", onClick: handleNextEpisodeManually, icon: React.createElement(FaGooglePlay, { className: "text-black" }), disabled: currentEpisodeIndex + 1 >= episodeList.length, position: "right" }))));
943
+ currentEpisodeIndex + 1 < episodeList.length && (React.createElement(VideoActionButton, { text: "Next Episode", onClick: handleNextEpisodeManually, icon: React.createElement(ArrowRight, { className: "h-5 w-5 text-gray-900" }), disabled: currentEpisodeIndex + 1 >= episodeList.length, position: "right" }))));
926
944
  };
927
945
 
928
946
  const SubtitleOverlay = ({ styleConfig }) => {
@@ -1039,39 +1057,195 @@ const SubtitleOverlay = ({ styleConfig }) => {
1039
1057
  return React__default.createElement("div", { style: subtitleStyle }, currentSubtitle);
1040
1058
  };
1041
1059
 
1060
+ /**
1061
+ * Video Source Hook
1062
+ *
1063
+ * Manages video source loading and streaming technology detection
1064
+ * Supports HLS.js, DASH.js, and native HTML5 video
1065
+ *
1066
+ * Features:
1067
+ * - Automatic stream type detection
1068
+ * - HLS.js fallback for older browsers
1069
+ * - DASH.js support with proper initialization
1070
+ * - Quality level extraction for all stream types
1071
+ * - Error handling and cleanup
1072
+ */
1042
1073
  const useVideoSource = (trackSrc, type) => {
1043
- const { videoRef, setQualityLevels, setHlsInstance } = useVideoStore();
1074
+ const { videoRef, setQualityLevels, setHlsInstance, setDashInstance, setStreamType } = useVideoStore();
1044
1075
  useEffect(() => {
1045
1076
  if (!videoRef)
1046
1077
  return;
1047
1078
  const getVideoExtension = getExtensionFromUrl(trackSrc);
1048
1079
  const contentType = type || getVideoExtension;
1049
- if (contentType === "mp4") {
1080
+ // Set stream type in store for quality manager
1081
+ setStreamType(contentType);
1082
+ // Handle MP4 and other simple formats
1083
+ if (contentType === "mp4" || contentType === "other") {
1050
1084
  videoRef.src = trackSrc;
1051
1085
  setQualityLevels([]);
1086
+ return;
1052
1087
  }
1053
- else if (contentType === "hls") {
1088
+ // Handle HLS streams
1089
+ if (contentType === "hls") {
1090
+ // Native HLS support (Safari/iOS)
1054
1091
  if (videoRef?.canPlayType("application/vnd.apple.mpegurl")) {
1092
+ console.log('📱 Using native HLS support');
1055
1093
  videoRef.src = trackSrc;
1094
+ // For native HLS, we can't control quality directly, but we can still extract info
1095
+ const handleLoadedMetadata = () => {
1096
+ const videoElement = videoRef;
1097
+ if (videoElement.videoTracks && videoElement.videoTracks.length > 0) {
1098
+ // Extract quality levels from native HLS
1099
+ const tracks = Array.from(videoElement.videoTracks).map((track, index) => ({
1100
+ height: track.height || 720,
1101
+ bitrate: track.bandwidth || 0,
1102
+ originalIndex: index
1103
+ }));
1104
+ setQualityLevels(tracks);
1105
+ console.log('✅ Native HLS quality levels:', tracks);
1106
+ }
1107
+ else {
1108
+ // Fallback quality levels for native HLS
1109
+ const defaultLevels = [
1110
+ { height: 360, bitrate: 800000, originalIndex: 0 },
1111
+ { height: 480, bitrate: 1400000, originalIndex: 1 },
1112
+ { height: 720, bitrate: 2800000, originalIndex: 2 },
1113
+ { height: 1080, bitrate: 5000000, originalIndex: 3 },
1114
+ ];
1115
+ setQualityLevels(defaultLevels);
1116
+ console.log('✅ Native HLS fallback quality levels:', defaultLevels);
1117
+ }
1118
+ // Even for native HLS, set a mock HLS instance to indicate it's HLS
1119
+ // This allows the quality manager to know we're dealing with HLS
1120
+ setHlsInstance(null); // null indicates native HLS, not HLS.js
1121
+ };
1122
+ videoRef.addEventListener('loadedmetadata', handleLoadedMetadata);
1123
+ return () => {
1124
+ videoRef.removeEventListener('loadedmetadata', handleLoadedMetadata);
1125
+ };
1056
1126
  }
1127
+ // HLS.js support (Chrome/Firefox/etc)
1057
1128
  else if (Hls.isSupported()) {
1058
- const hls = new Hls();
1129
+ console.log('🔧 Using HLS.js for HLS streaming');
1130
+ const hls = new Hls({
1131
+ // HLS.js configuration for optimal performance
1132
+ enableWorker: true,
1133
+ lowLatencyMode: true,
1134
+ backBufferLength: 90
1135
+ });
1059
1136
  hls.loadSource(trackSrc);
1060
1137
  hls.attachMedia(videoRef);
1138
+ console.log('✅ HLS.js instance created and attached');
1061
1139
  setHlsInstance(hls);
1140
+ // Extract quality levels when manifest is parsed
1062
1141
  hls.on(Hls.Events.MANIFEST_PARSED, () => {
1063
- setQualityLevels(hls.levels);
1142
+ const levels = hls.levels.map((level, index) => ({
1143
+ height: level.height,
1144
+ bitrate: level.bitrate,
1145
+ originalIndex: index
1146
+ }));
1147
+ setQualityLevels(levels);
1148
+ console.log('✅ HLS.js quality levels:', levels);
1064
1149
  });
1150
+ // Log level switches for debugging
1151
+ hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
1152
+ console.log('🔄 HLS level switched to:', data.level, hls.levels?.[data.level]);
1153
+ });
1154
+ // Error handling
1155
+ hls.on(Hls.Events.ERROR, (event, data) => {
1156
+ console.error('❌ HLS.js error:', data);
1157
+ });
1158
+ // Cleanup
1065
1159
  return () => {
1066
1160
  hls.destroy();
1161
+ console.log('🧹 HLS.js instance destroyed');
1067
1162
  };
1068
1163
  }
1164
+ else {
1165
+ // Fallback when HLS.js is not supported
1166
+ console.log('📱 Using fallback HLS (direct src)');
1167
+ videoRef.src = trackSrc;
1168
+ setHlsInstance(null); // null indicates native HLS fallback
1169
+ // Set fallback quality levels
1170
+ const defaultLevels = [
1171
+ { height: 360, bitrate: 800000, originalIndex: 0 },
1172
+ { height: 480, bitrate: 1400000, originalIndex: 1 },
1173
+ { height: 720, bitrate: 2800000, originalIndex: 2 },
1174
+ { height: 1080, bitrate: 5000000, originalIndex: 3 },
1175
+ ];
1176
+ setQualityLevels(defaultLevels);
1177
+ console.log('✅ HLS fallback quality levels:', defaultLevels);
1178
+ }
1069
1179
  }
1070
- else {
1071
- videoRef.src = trackSrc;
1072
- setQualityLevels([]);
1180
+ // Handle DASH streams
1181
+ else if (contentType === "dash") {
1182
+ // DASH.js support
1183
+ if (dashjs.supportsMediaSource()) {
1184
+ console.log('🔧 Using DASH.js for DASH streaming');
1185
+ const player = dashjs.MediaPlayer().create();
1186
+ // DASH.js configuration for optimal performance
1187
+ player.updateSettings({
1188
+ streaming: {
1189
+ buffer: {
1190
+ fastSwitchEnabled: true, // Enable fast quality switching
1191
+ bufferTimeAtTopQuality: 30, // Buffer 30s at top quality
1192
+ bufferTimeAtTopQualityLongForm: 60 // Buffer 60s for long content
1193
+ },
1194
+ // Note: Some ABR settings may vary by DASH.js version
1195
+ // Check documentation for your specific version
1196
+ }
1197
+ });
1198
+ player.initialize(videoRef, trackSrc, true);
1199
+ console.log('✅ DASH.js instance created and initialized');
1200
+ setDashInstance(player);
1201
+ // Extract quality levels when manifest is loaded
1202
+ const handleManifestLoaded = () => {
1203
+ try {
1204
+ const representations = player.getRepresentationsByType('video');
1205
+ if (representations && representations.length > 0) {
1206
+ const levels = representations.map((rep, index) => ({
1207
+ height: rep.height || Math.round(rep.bandwidth / 1000) || 0,
1208
+ bitrate: rep.bandwidth,
1209
+ originalIndex: index,
1210
+ id: rep.id
1211
+ }));
1212
+ setQualityLevels(levels);
1213
+ console.log('✅ DASH.js quality levels:', levels);
1214
+ }
1215
+ else {
1216
+ console.warn('⚠️ No DASH video representations found');
1217
+ setQualityLevels([]);
1218
+ }
1219
+ }
1220
+ catch (error) {
1221
+ console.error('❌ Error getting DASH quality levels:', error);
1222
+ setQualityLevels([]);
1223
+ }
1224
+ };
1225
+ // Listen for manifest loaded event
1226
+ player.on('manifestLoaded', handleManifestLoaded);
1227
+ // Log quality changes for debugging
1228
+ player.on('qualityChange', (e) => {
1229
+ console.log('🔄 DASH quality changed to:', e.newQuality, e);
1230
+ });
1231
+ // Error handling
1232
+ player.on('error', (e) => {
1233
+ console.error('❌ DASH.js error:', e);
1234
+ });
1235
+ // Cleanup
1236
+ return () => {
1237
+ player.reset();
1238
+ console.log('🧹 DASH.js instance reset');
1239
+ };
1240
+ }
1241
+ else {
1242
+ console.warn('⚠️ DASH.js not supported in this browser');
1243
+ }
1073
1244
  }
1074
- }, [trackSrc, videoRef, type, setQualityLevels, setHlsInstance]);
1245
+ // Fallback for unsupported formats
1246
+ videoRef.src = trackSrc;
1247
+ setQualityLevels([]);
1248
+ }, [trackSrc, videoRef, type, setQualityLevels, setHlsInstance, setDashInstance, setStreamType]);
1075
1249
  };
1076
1250
 
1077
1251
  const useSubtitles = (subtitles) => {
@@ -1232,8 +1406,7 @@ const useVideoTracking = (tracking, episodeList, currentEpisodeIndex, onClose) =
1232
1406
  setShowCountdown,
1233
1407
  ]);
1234
1408
  useEffect(() => {
1235
- const handleUnload = (e) => {
1236
- e.preventDefault();
1409
+ const handleUnload = () => {
1237
1410
  if (startTime.current) {
1238
1411
  const elapsedTime = (Date.now() - startTime.current) / 1000;
1239
1412
  const getCurrentTime = localStorage.getItem("current_time");
@@ -1247,10 +1420,8 @@ const useVideoTracking = (tracking, episodeList, currentEpisodeIndex, onClose) =
1247
1420
  }
1248
1421
  localStorage.setItem("current_time", "0");
1249
1422
  };
1250
- window.addEventListener("beforeunload", handleUnload);
1251
1423
  window.addEventListener("unload", handleUnload);
1252
1424
  return () => {
1253
- window.removeEventListener("beforeunload", handleUnload);
1254
1425
  window.removeEventListener("unload", handleUnload);
1255
1426
  };
1256
1427
  }, [tracking]);
@@ -1326,7 +1497,7 @@ const useEpisodes = (episodeList, currentEpisodeIndex, nextEpisodeConfig) => {
1326
1497
  };
1327
1498
 
1328
1499
  const useVideoEvents = () => {
1329
- const { setCurrentTime, setDuration } = useVideoStore();
1500
+ const { setCurrentTime, setDuration, setBufferedProgress, setIsPlaying } = useVideoStore();
1330
1501
  const onRightClick = (e) => {
1331
1502
  e.preventDefault();
1332
1503
  };
@@ -1346,18 +1517,51 @@ const useVideoEvents = () => {
1346
1517
  setDuration(e?.currentTarget?.duration);
1347
1518
  }
1348
1519
  };
1520
+ const onProgress = (e) => {
1521
+ const video = e.currentTarget;
1522
+ if (video.buffered.length > 0 &&
1523
+ video.duration > 0 &&
1524
+ !isNaN(video.duration)) {
1525
+ let bufferedEnd = 0;
1526
+ for (let i = 0; i < video.buffered.length; i++) {
1527
+ if (video.currentTime >= video.buffered.start(i) &&
1528
+ video.currentTime <= video.buffered.end(i)) {
1529
+ bufferedEnd = video.buffered.end(i);
1530
+ break;
1531
+ }
1532
+ }
1533
+ if (bufferedEnd === 0 && video.buffered.length > 0) {
1534
+ bufferedEnd = video.buffered.end(video.buffered.length - 1);
1535
+ }
1536
+ const bufferedProgress = Math.min((bufferedEnd / video.duration) * 100, 100);
1537
+ setBufferedProgress(bufferedProgress);
1538
+ }
1539
+ };
1540
+ const onPlay = () => {
1541
+ setIsPlaying(true);
1542
+ };
1543
+ const onPause = () => {
1544
+ setIsPlaying(false);
1545
+ };
1546
+ const onEnded = (e) => {
1547
+ setIsPlaying(false);
1548
+ };
1349
1549
  return {
1350
1550
  onRightClick,
1351
1551
  onSeeked,
1352
1552
  onTimeUpdate,
1353
1553
  onLoadedMetadata,
1554
+ onProgress,
1555
+ onPlay,
1556
+ onPause,
1557
+ onEnded,
1354
1558
  };
1355
1559
  };
1356
1560
 
1357
1561
  var css_248z = ".video-player video::cue {\n display: none !important;\n opacity: 0 !important;\n visibility: hidden !important;\n}\n\n.custom-subtitle-overlay {\n position: absolute;\n bottom: 10%;\n left: 50%;\n transform: translateX(-50%);\n\n font-size: 1.5rem;\n font-weight: 600;\n line-height: 1.4;\n text-align: center;\n\n color: #000;\n background: rgba(255, 255, 255, 0.6);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n\n padding: 10px 16px;\n border-radius: 10px;\n border: 1px solid rgba(0, 0, 0, 0.1);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);\n\n max-width: 70%;\n min-width: fit-content;\n\n transition: all 0.2s ease-in-out;\n}\n\n.custom-subtitle-overlay:hover {\n transform: translateX(-50%) scale(1.02);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);\n}\n\n@media (max-width: 768px) {\n .custom-subtitle-overlay {\n font-size: 1.25rem;\n padding: 8px 14px;\n bottom: 8%;\n max-width: 85%;\n }\n}\n\n@media (max-width: 480px) {\n .custom-subtitle-overlay {\n font-size: 1rem;\n padding: 6px 10px;\n bottom: 6%;\n max-width: 90%;\n }\n}\n\n@media (prefers-contrast: high) {\n .custom-subtitle-overlay {\n background: #ffff00;\n color: #000;\n border: 3px solid #000;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-subtitle-overlay {\n transition: none;\n }\n\n .custom-subtitle-overlay:hover {\n transform: translateX(-50%);\n }\n}\n";
1358
1562
  styleInject(css_248z,{"insertAt":"top"});
1359
1563
 
1360
- const VideoPlayer = ({ trackSrc, trackTitle, intro, onClose, trackPoster, isTrailer, className, type, height, width, timeCodes, getPreviewScreenUrl, tracking, subtitles, episodeList, currentEpisodeIndex = 0, nextEpisodeConfig, subtitleStyle, }) => {
1564
+ const VideoPlayer = ({ trackSrc, trackTitle, intro, onClose, onError, trackPoster, isTrailer, className, type, height, width, timeCodes, getPreviewScreenUrl, tracking, subtitles, episodeList, currentEpisodeIndex = 0, onEnded, nextEpisodeConfig, subtitleStyle, showControls = true, isMute = false, }) => {
1361
1565
  const { setVideoRef, setVideoWrapperRef, videoRef } = useVideoStore();
1362
1566
  useVideoSource(trackSrc, type);
1363
1567
  useSubtitles(subtitles);
@@ -1365,11 +1569,16 @@ const VideoPlayer = ({ trackSrc, trackTitle, intro, onClose, trackPoster, isTrai
1365
1569
  useVideoTracking(tracking, episodeList, currentEpisodeIndex, onClose);
1366
1570
  const { showSkipIntro, handleSkipIntro } = useIntroSkip(intro);
1367
1571
  useEpisodes(episodeList, currentEpisodeIndex, nextEpisodeConfig);
1368
- const { onSeeked, onTimeUpdate, onLoadedMetadata } = useVideoEvents();
1572
+ const { onSeeked, onTimeUpdate, onLoadedMetadata, onProgress, onPlay, onPause, onEnded: onEndedHook, } = useVideoEvents();
1369
1573
  return (React__default.createElement("div", { ref: setVideoWrapperRef, className: `video-player ${height || "h-full"} ${width || "w-full"} mx-auto absolute` },
1370
1574
  trackPoster && (React__default.createElement("div", { className: "pip-poster absolute inset-0 bg-center bg-cover hidden", style: { backgroundImage: `url(${trackPoster})` } })),
1371
- React__default.createElement("video", { autoPlay: true, muted: true, playsInline: true, preload: "metadata", ref: setVideoRef, onSeeked: onSeeked, poster: trackPoster, crossOrigin: "anonymous", controls: false, disableRemotePlayback: true, controlsList: "nodownload", onContextMenu: (e) => e.preventDefault(), onTimeUpdate: onTimeUpdate, onLoadedMetadata: onLoadedMetadata, className: `w-full h-full relative ${className}` }),
1372
- React__default.createElement(Overlay, { config: {
1575
+ React__default.createElement("video", { autoPlay: true, playsInline: true, preload: "metadata", ref: setVideoRef, onSeeked: onSeeked, poster: trackPoster, crossOrigin: "anonymous", controls: false, disableRemotePlayback: true, controlsList: "nodownload", onContextMenu: (e) => e.preventDefault(), onTimeUpdate: onTimeUpdate, onLoadedMetadata: onLoadedMetadata, onProgress: onProgress, onPlay: onPlay, onPause: onPause, onEnded: (e) => {
1576
+ onEndedHook(e);
1577
+ onEnded?.(e);
1578
+ }, onError: (e) => {
1579
+ onError?.(e);
1580
+ }, muted: isMute, className: `w-full h-full relative ${className}` }),
1581
+ showControls && (React__default.createElement(Overlay, { config: {
1373
1582
  headerConfig: {
1374
1583
  config: {
1375
1584
  isTrailer: isTrailer,
@@ -1387,7 +1596,7 @@ const VideoPlayer = ({ trackSrc, trackTitle, intro, onClose, trackPoster, isTrai
1387
1596
  },
1388
1597
  },
1389
1598
  },
1390
- } }),
1599
+ } })),
1391
1600
  React__default.createElement(SubtitleOverlay, { styleConfig: subtitleStyle }),
1392
1601
  showSkipIntro && (React__default.createElement(VideoActionButton, { text: "Skip Intro", onClick: handleSkipIntro, position: "left" }))));
1393
1602
  };
@@ -6,3 +6,4 @@ export { createVideoQualitySlice } from "./videoQualitySlice";
6
6
  export { createSubtitlesSlice } from "./subtitlesSlice";
7
7
  export { createEpisodesSlice } from "./episodesSlice";
8
8
  export { createIntroSlice } from "./introSlice";
9
+ export { createResetSlice } from "./resetSlice";
@@ -0,0 +1,5 @@
1
+ import { StateCreator } from "zustand";
2
+ import { StoreResetState, VideoState } from "../types/StoreTypes";
3
+ export declare const createResetSlice: StateCreator<VideoState, [
4
+ ], [
5
+ ], StoreResetState>;
@@ -1,5 +1,5 @@
1
1
  import Hls from "hls.js";
2
- import { Dispatch, SetStateAction } from "react";
2
+ import * as dashjs from "dashjs";
3
3
  export interface VideoRefsState {
4
4
  videoRef: HTMLVideoElement | null;
5
5
  setVideoRef: (ref: HTMLVideoElement) => void;
@@ -7,8 +7,8 @@ export interface VideoRefsState {
7
7
  setVideoWrapperRef: (ref: HTMLDivElement) => void;
8
8
  }
9
9
  export interface VideoPlaybackState {
10
- playing: boolean | ((prevState: boolean) => boolean);
11
- setPlaying: Dispatch<SetStateAction<boolean>>;
10
+ playing: boolean;
11
+ setPlaying: (playing: boolean) => void;
12
12
  isBuffering: boolean;
13
13
  setIsBuffering: (isBuffering: boolean) => void;
14
14
  isPlaying: boolean;
@@ -23,20 +23,38 @@ export interface VideoTimingState {
23
23
  setCurrentTime: (currentTime: number) => void;
24
24
  duration: number;
25
25
  setDuration: (duration: number) => void;
26
+ bufferedProgress: number;
27
+ setBufferedProgress: (progress: number) => void;
26
28
  }
27
29
  export interface VideoControlsState {
28
30
  controls: boolean;
29
31
  setControls: (controls: boolean) => void;
30
32
  isFullscreen: boolean;
31
33
  setIsFullscreen: (isFullscreen: boolean) => void;
34
+ controlsVisible: boolean;
35
+ setControlsVisible: (visible: boolean) => void;
32
36
  }
33
37
  export interface VideoQualityState {
34
- hlsInstance?: Hls;
35
- setHlsInstance: (hlsInstance: Hls) => void;
36
- qualityLevels?: Hls["levels"];
37
- setQualityLevels: (qualityLevels: Hls["levels"]) => void;
38
+ hlsInstance?: Hls | null;
39
+ setHlsInstance: (hlsInstance: Hls | null) => void;
40
+ dashInstance?: dashjs.MediaPlayerClass;
41
+ setDashInstance: (dashInstance: dashjs.MediaPlayerClass) => void;
42
+ qualityLevels?: Array<{
43
+ height: number;
44
+ bitrate?: number;
45
+ originalIndex: number;
46
+ id?: string;
47
+ }>;
48
+ setQualityLevels: (qualityLevels: Array<{
49
+ height: number;
50
+ bitrate?: number;
51
+ originalIndex: number;
52
+ id?: string;
53
+ }>) => void;
38
54
  activeQuality: string;
39
55
  setActiveQuality: (activeQuality: string) => void;
56
+ streamType: "hls" | "dash" | "mp4" | "other";
57
+ setStreamType: (streamType: "hls" | "dash" | "mp4" | "other") => void;
40
58
  }
41
59
  export interface SubtitleTrack {
42
60
  lang: string;
@@ -70,5 +88,8 @@ export interface IntroState {
70
88
  showIntroSkip: boolean;
71
89
  setShowIntroSkip: (show: boolean) => void;
72
90
  }
73
- export interface VideoState extends VideoRefsState, VideoPlaybackState, VideoTimingState, VideoControlsState, VideoQualityState, SubtitlesState, EpisodesState, IntroState {
91
+ export interface StoreResetState {
92
+ resetStore: () => void;
93
+ }
94
+ export interface VideoState extends VideoRefsState, VideoPlaybackState, VideoTimingState, VideoControlsState, VideoQualityState, SubtitlesState, EpisodesState, IntroState, StoreResetState {
74
95
  }
package/package.json CHANGED
@@ -1,14 +1,9 @@
1
1
  {
2
2
  "name": "@zezosoft/react-player",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "types": "dist/index.d.ts",
7
- "scripts": {
8
- "build": "npx rollup -c",
9
- "dev": "npx rollup -c -w",
10
- "prepare": "npm run build"
11
- },
12
7
  "files": [
13
8
  "dist"
14
9
  ],
@@ -36,8 +31,8 @@
36
31
  "devDependencies": {
37
32
  "@rollup/plugin-typescript": "^12.1.2",
38
33
  "@tailwindcss/postcss": "^4.0.14",
39
- "@types/react": "^19.0.10",
40
34
  "@types/node": "^24.4.0",
35
+ "@types/react": "^19.0.10",
41
36
  "hls.js": "^1.5.20",
42
37
  "lucide-react": "^0.481.0",
43
38
  "postcss": "^8.5.3",
@@ -57,6 +52,12 @@
57
52
  "zustand": "^5.0.3"
58
53
  },
59
54
  "dependencies": {
60
- "react-icons": "^5.5.0"
55
+ "dashjs": "^5.0.3",
56
+ "react-icons": "^5.5.0",
57
+ "screenfull": "^6.0.2"
58
+ },
59
+ "scripts": {
60
+ "build": "npx rollup -c",
61
+ "dev": "npx rollup -c -w"
61
62
  }
62
- }
63
+ }