@zezosoft/react-player 0.0.7 → 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.
- package/dist/VideoPlayer/MediaControls/BottomControls.d.ts +5 -0
- package/dist/VideoPlayer/MediaControls/ControlsHeader.d.ts +5 -0
- package/dist/VideoPlayer/MediaControls/MiddleControls.d.ts +3 -0
- package/dist/VideoPlayer/MediaControls/VideoPlayerControls.d.ts +4 -0
- package/dist/VideoPlayer/hooks/useVideoEvents.d.ts +1 -1
- package/dist/VideoPlayer/hooks/useVideoSource.d.ts +14 -1
- package/dist/VideoPlayer/types/VideoPlayerTypes.d.ts +4 -0
- package/dist/VideoPlayer/utils/index.d.ts +1 -0
- package/dist/VideoPlayer/utils/qualityManager.d.ts +78 -0
- package/dist/index.js +254 -85
- package/dist/store/types/StoreTypes.d.ts +21 -4
- package/package.json +7 -7
|
@@ -1 +1,14 @@
|
|
|
1
|
-
|
|
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;
|
|
@@ -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;
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { IoMdClose } from 'react-icons/io';
|
|
|
6
6
|
import { Settings as Settings$1, ChevronRight, Check, Loader, ArrowRight } from 'lucide-react';
|
|
7
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 .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-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) => ({
|
|
@@ -71,15 +72,21 @@ const createVideoControlsSlice = (set) => ({
|
|
|
71
72
|
setControls: (controls) => set({ controls }),
|
|
72
73
|
isFullscreen: false,
|
|
73
74
|
setIsFullscreen: (isFullscreen) => set({ isFullscreen }),
|
|
75
|
+
controlsVisible: true,
|
|
76
|
+
setControlsVisible: (visible) => set({ controlsVisible: visible }),
|
|
74
77
|
});
|
|
75
78
|
|
|
76
79
|
const createVideoQualitySlice = (set) => ({
|
|
77
80
|
hlsInstance: undefined,
|
|
78
81
|
setHlsInstance: (hlsInstance) => set({ hlsInstance }),
|
|
82
|
+
dashInstance: undefined,
|
|
83
|
+
setDashInstance: (dashInstance) => set({ dashInstance }),
|
|
79
84
|
qualityLevels: undefined,
|
|
80
85
|
setQualityLevels: (qualityLevels) => set({ qualityLevels }),
|
|
81
86
|
activeQuality: "auto",
|
|
82
87
|
setActiveQuality: (activeQuality) => set({ activeQuality }),
|
|
88
|
+
streamType: "mp4",
|
|
89
|
+
setStreamType: (streamType) => set({ streamType }),
|
|
83
90
|
});
|
|
84
91
|
|
|
85
92
|
const createSubtitlesSlice = (set) => ({
|
|
@@ -185,6 +192,9 @@ const getExtensionFromUrl = (url) => {
|
|
|
185
192
|
if (extension === "m3u8") {
|
|
186
193
|
return "hls";
|
|
187
194
|
}
|
|
195
|
+
if (extension === "mpd") {
|
|
196
|
+
return "dash";
|
|
197
|
+
}
|
|
188
198
|
return extension;
|
|
189
199
|
};
|
|
190
200
|
|
|
@@ -450,7 +460,7 @@ const Tooltip = ({ children, title, position = "top", }) => {
|
|
|
450
460
|
};
|
|
451
461
|
return (React__default.createElement("div", { className: "relative inline-block cursor-pointer", onMouseEnter: () => setVisible(true), onMouseLeave: () => setVisible(false) },
|
|
452
462
|
children,
|
|
453
|
-
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))));
|
|
454
464
|
};
|
|
455
465
|
|
|
456
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";
|
|
@@ -760,7 +770,7 @@ const ControlsHeader = ({ config }) => {
|
|
|
760
770
|
: "Fullscreen", className: `${iconClassName} ${isPipActive ? "opacity-50 cursor-not-allowed" : ""}` },
|
|
761
771
|
React.createElement("div", { onClick: handleFullscreen, className: isPipActive ? "pointer-events-none" : "" },
|
|
762
772
|
React.createElement(FullScreenToggle, { isFullScreen: isFullscreen, className: iconClassName }))),
|
|
763
|
-
React.createElement(Tooltip, { title: isPipActive ? "Exit PiP" : "Enter PiP" },
|
|
773
|
+
React.createElement(Tooltip, { className: "whitespace-nowrap", title: isPipActive ? "Exit PiP" : "Enter PiP" },
|
|
764
774
|
React.createElement("div", { onClick: handlePipToggle },
|
|
765
775
|
React.createElement(PiPictureInPictureToggle, { className: iconClassName }))),
|
|
766
776
|
config?.onClose && (React.createElement(React.Fragment, null,
|
|
@@ -770,63 +780,50 @@ const ControlsHeader = ({ config }) => {
|
|
|
770
780
|
React.createElement(IoMdClose, { className: iconClassName }))))))));
|
|
771
781
|
};
|
|
772
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));
|
|
773
784
|
const MiddleControls = () => {
|
|
774
|
-
const { videoRef, isPlaying, setIsPlaying
|
|
775
|
-
const
|
|
776
|
-
|
|
785
|
+
const { videoRef, isPlaying, setIsPlaying } = useVideoStore();
|
|
786
|
+
const [isBuffering, setIsBuffering] = useState(false);
|
|
787
|
+
const videoElement = videoRef;
|
|
788
|
+
const handlePlayPause = useCallback(() => {
|
|
789
|
+
if (!videoElement)
|
|
777
790
|
return;
|
|
778
|
-
if (
|
|
779
|
-
|
|
791
|
+
if (videoElement.paused) {
|
|
792
|
+
videoElement
|
|
780
793
|
.play()
|
|
781
|
-
.
|
|
782
|
-
|
|
794
|
+
.then(() => setIsPlaying(true))
|
|
795
|
+
.catch((err) => console.error("Error playing video:", err));
|
|
783
796
|
}
|
|
784
797
|
else {
|
|
785
|
-
|
|
798
|
+
videoElement.pause();
|
|
786
799
|
setIsPlaying(false);
|
|
787
800
|
}
|
|
788
|
-
};
|
|
789
|
-
const
|
|
790
|
-
if (!
|
|
801
|
+
}, [videoElement, setIsPlaying]);
|
|
802
|
+
const handleBackward = useCallback(() => {
|
|
803
|
+
if (!videoElement)
|
|
791
804
|
return;
|
|
792
|
-
|
|
793
|
-
};
|
|
794
|
-
const
|
|
795
|
-
if (!
|
|
805
|
+
videoElement.currentTime = Math.max(0, videoElement.currentTime - 10);
|
|
806
|
+
}, [videoElement]);
|
|
807
|
+
const handleForward = useCallback(() => {
|
|
808
|
+
if (!videoElement)
|
|
796
809
|
return;
|
|
797
|
-
|
|
798
|
-
};
|
|
810
|
+
videoElement.currentTime = Math.min(videoElement.duration, videoElement.currentTime + 10);
|
|
811
|
+
}, [videoElement]);
|
|
799
812
|
useEffect(() => {
|
|
800
|
-
if (!
|
|
813
|
+
if (!videoElement)
|
|
801
814
|
return;
|
|
802
|
-
const handleWaiting = () =>
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
};
|
|
807
|
-
const handlePlaying = () => {
|
|
808
|
-
setIsBuffering(false);
|
|
809
|
-
};
|
|
810
|
-
const handleCanPlay = () => {
|
|
811
|
-
setIsBuffering(false);
|
|
812
|
-
};
|
|
813
|
-
const handlePause = () => {
|
|
814
|
-
setIsBuffering(false);
|
|
815
|
-
};
|
|
816
|
-
videoRef.addEventListener("waiting", handleWaiting);
|
|
817
|
-
videoRef.addEventListener("playing", handlePlaying);
|
|
818
|
-
videoRef.addEventListener("canplay", handleCanPlay);
|
|
819
|
-
videoRef.addEventListener("pause", handlePause);
|
|
815
|
+
const handleWaiting = () => setIsBuffering(true);
|
|
816
|
+
const handlePlaying = () => setIsBuffering(false);
|
|
817
|
+
videoElement.addEventListener("waiting", handleWaiting);
|
|
818
|
+
videoElement.addEventListener("playing", handlePlaying);
|
|
820
819
|
return () => {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
videoRef.removeEventListener("canplay", handleCanPlay);
|
|
824
|
-
videoRef.removeEventListener("pause", handlePause);
|
|
820
|
+
videoElement.removeEventListener("waiting", handleWaiting);
|
|
821
|
+
videoElement.removeEventListener("playing", handlePlaying);
|
|
825
822
|
};
|
|
826
|
-
}, [
|
|
823
|
+
}, [videoElement]);
|
|
827
824
|
useEffect(() => {
|
|
828
825
|
const handleKeyDown = (e) => {
|
|
829
|
-
if (!
|
|
826
|
+
if (!videoElement)
|
|
830
827
|
return;
|
|
831
828
|
switch (e.code) {
|
|
832
829
|
case "Space":
|
|
@@ -834,29 +831,29 @@ const MiddleControls = () => {
|
|
|
834
831
|
handlePlayPause();
|
|
835
832
|
break;
|
|
836
833
|
case "ArrowLeft":
|
|
837
|
-
|
|
834
|
+
e.preventDefault();
|
|
835
|
+
handleBackward();
|
|
838
836
|
break;
|
|
839
837
|
case "ArrowRight":
|
|
840
|
-
|
|
838
|
+
e.preventDefault();
|
|
839
|
+
handleForward();
|
|
841
840
|
break;
|
|
842
841
|
}
|
|
843
842
|
};
|
|
844
843
|
window.addEventListener("keydown", handleKeyDown);
|
|
845
844
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
846
|
-
}, [
|
|
845
|
+
}, [videoElement, handlePlayPause, handleBackward, handleForward]);
|
|
847
846
|
return (React__default.createElement("div", { className: "flex justify-center items-center" },
|
|
848
|
-
React__default.createElement(
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: "icon-class", fill: "currentColor", viewBox: "0 0 67 67" },
|
|
856
|
-
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" })) })));
|
|
857
854
|
};
|
|
858
855
|
|
|
859
|
-
const VideoPlayerControls = ({ config
|
|
856
|
+
const VideoPlayerControls = ({ config }) => {
|
|
860
857
|
return (React.createElement("div", { className: "px-20" },
|
|
861
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" },
|
|
862
859
|
React.createElement(ControlsHeader, { config: config?.headerConfig?.config }),
|
|
@@ -881,23 +878,35 @@ const Overlay = ({ config }) => {
|
|
|
881
878
|
const controlsTimerRef = useRef(null);
|
|
882
879
|
const { setControls, controls, showCountdown, countdownTime, setShowCountdown, setAutoPlayNext, setCurrentEpisodeIndex, episodeList, setCountdownTime, videoRef, currentEpisodeIndex, } = useVideoStore();
|
|
883
880
|
const { onClose } = config?.headerConfig?.config || {};
|
|
884
|
-
const
|
|
885
|
-
|
|
886
|
-
if (videoPlayerControls) {
|
|
887
|
-
videoPlayerControls.classList.remove("noCursor");
|
|
888
|
-
}
|
|
889
|
-
setControls(true);
|
|
881
|
+
const HIDE_DELAY = 2000;
|
|
882
|
+
const resetControlsTimer = useCallback(() => {
|
|
890
883
|
if (controlsTimerRef.current) {
|
|
891
884
|
clearTimeout(controlsTimerRef.current);
|
|
892
885
|
}
|
|
893
886
|
controlsTimerRef.current = setTimeout(() => {
|
|
894
887
|
setControls(false);
|
|
888
|
+
const videoPlayerControls = document?.getElementById("videoPlayerControls");
|
|
895
889
|
if (videoPlayerControls) {
|
|
896
890
|
videoPlayerControls.classList.add("noCursor");
|
|
897
891
|
}
|
|
898
|
-
},
|
|
892
|
+
}, HIDE_DELAY);
|
|
899
893
|
}, [setControls]);
|
|
900
|
-
|
|
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(() => {
|
|
901
910
|
let timer;
|
|
902
911
|
if (showCountdown && countdownTime > 0 && episodeList.length > 0) {
|
|
903
912
|
timer = setInterval(() => {
|
|
@@ -920,6 +929,8 @@ const Overlay = ({ config }) => {
|
|
|
920
929
|
.catch((err) => console.error("Manual play failed:", err));
|
|
921
930
|
setShowCountdown(false);
|
|
922
931
|
setCountdownTime(10);
|
|
932
|
+
setControls(true);
|
|
933
|
+
resetControlsTimer();
|
|
923
934
|
}
|
|
924
935
|
else if (onClose) {
|
|
925
936
|
onClose();
|
|
@@ -1046,39 +1057,195 @@ const SubtitleOverlay = ({ styleConfig }) => {
|
|
|
1046
1057
|
return React__default.createElement("div", { style: subtitleStyle }, currentSubtitle);
|
|
1047
1058
|
};
|
|
1048
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
|
+
*/
|
|
1049
1073
|
const useVideoSource = (trackSrc, type) => {
|
|
1050
|
-
const { videoRef, setQualityLevels, setHlsInstance } = useVideoStore();
|
|
1074
|
+
const { videoRef, setQualityLevels, setHlsInstance, setDashInstance, setStreamType } = useVideoStore();
|
|
1051
1075
|
useEffect(() => {
|
|
1052
1076
|
if (!videoRef)
|
|
1053
1077
|
return;
|
|
1054
1078
|
const getVideoExtension = getExtensionFromUrl(trackSrc);
|
|
1055
1079
|
const contentType = type || getVideoExtension;
|
|
1056
|
-
|
|
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") {
|
|
1057
1084
|
videoRef.src = trackSrc;
|
|
1058
1085
|
setQualityLevels([]);
|
|
1086
|
+
return;
|
|
1059
1087
|
}
|
|
1060
|
-
|
|
1088
|
+
// Handle HLS streams
|
|
1089
|
+
if (contentType === "hls") {
|
|
1090
|
+
// Native HLS support (Safari/iOS)
|
|
1061
1091
|
if (videoRef?.canPlayType("application/vnd.apple.mpegurl")) {
|
|
1092
|
+
console.log('📱 Using native HLS support');
|
|
1062
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
|
+
};
|
|
1063
1126
|
}
|
|
1127
|
+
// HLS.js support (Chrome/Firefox/etc)
|
|
1064
1128
|
else if (Hls.isSupported()) {
|
|
1065
|
-
|
|
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
|
+
});
|
|
1066
1136
|
hls.loadSource(trackSrc);
|
|
1067
1137
|
hls.attachMedia(videoRef);
|
|
1138
|
+
console.log('✅ HLS.js instance created and attached');
|
|
1068
1139
|
setHlsInstance(hls);
|
|
1140
|
+
// Extract quality levels when manifest is parsed
|
|
1069
1141
|
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
1070
|
-
|
|
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);
|
|
1071
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
|
|
1072
1159
|
return () => {
|
|
1073
1160
|
hls.destroy();
|
|
1161
|
+
console.log('🧹 HLS.js instance destroyed');
|
|
1074
1162
|
};
|
|
1075
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
|
+
}
|
|
1076
1179
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
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
|
+
}
|
|
1080
1244
|
}
|
|
1081
|
-
|
|
1245
|
+
// Fallback for unsupported formats
|
|
1246
|
+
videoRef.src = trackSrc;
|
|
1247
|
+
setQualityLevels([]);
|
|
1248
|
+
}, [trackSrc, videoRef, type, setQualityLevels, setHlsInstance, setDashInstance, setStreamType]);
|
|
1082
1249
|
};
|
|
1083
1250
|
|
|
1084
1251
|
const useSubtitles = (subtitles) => {
|
|
@@ -1239,8 +1406,7 @@ const useVideoTracking = (tracking, episodeList, currentEpisodeIndex, onClose) =
|
|
|
1239
1406
|
setShowCountdown,
|
|
1240
1407
|
]);
|
|
1241
1408
|
useEffect(() => {
|
|
1242
|
-
const handleUnload = (
|
|
1243
|
-
e.preventDefault();
|
|
1409
|
+
const handleUnload = () => {
|
|
1244
1410
|
if (startTime.current) {
|
|
1245
1411
|
const elapsedTime = (Date.now() - startTime.current) / 1000;
|
|
1246
1412
|
const getCurrentTime = localStorage.getItem("current_time");
|
|
@@ -1254,10 +1420,8 @@ const useVideoTracking = (tracking, episodeList, currentEpisodeIndex, onClose) =
|
|
|
1254
1420
|
}
|
|
1255
1421
|
localStorage.setItem("current_time", "0");
|
|
1256
1422
|
};
|
|
1257
|
-
window.addEventListener("beforeunload", handleUnload);
|
|
1258
1423
|
window.addEventListener("unload", handleUnload);
|
|
1259
1424
|
return () => {
|
|
1260
|
-
window.removeEventListener("beforeunload", handleUnload);
|
|
1261
1425
|
window.removeEventListener("unload", handleUnload);
|
|
1262
1426
|
};
|
|
1263
1427
|
}, [tracking]);
|
|
@@ -1379,7 +1543,7 @@ const useVideoEvents = () => {
|
|
|
1379
1543
|
const onPause = () => {
|
|
1380
1544
|
setIsPlaying(false);
|
|
1381
1545
|
};
|
|
1382
|
-
const onEnded = () => {
|
|
1546
|
+
const onEnded = (e) => {
|
|
1383
1547
|
setIsPlaying(false);
|
|
1384
1548
|
};
|
|
1385
1549
|
return {
|
|
@@ -1397,7 +1561,7 @@ const useVideoEvents = () => {
|
|
|
1397
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";
|
|
1398
1562
|
styleInject(css_248z,{"insertAt":"top"});
|
|
1399
1563
|
|
|
1400
|
-
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, }) => {
|
|
1401
1565
|
const { setVideoRef, setVideoWrapperRef, videoRef } = useVideoStore();
|
|
1402
1566
|
useVideoSource(trackSrc, type);
|
|
1403
1567
|
useSubtitles(subtitles);
|
|
@@ -1405,11 +1569,16 @@ const VideoPlayer = ({ trackSrc, trackTitle, intro, onClose, trackPoster, isTrai
|
|
|
1405
1569
|
useVideoTracking(tracking, episodeList, currentEpisodeIndex, onClose);
|
|
1406
1570
|
const { showSkipIntro, handleSkipIntro } = useIntroSkip(intro);
|
|
1407
1571
|
useEpisodes(episodeList, currentEpisodeIndex, nextEpisodeConfig);
|
|
1408
|
-
const { onSeeked, onTimeUpdate, onLoadedMetadata, onProgress, onPlay, onPause, onEnded } = useVideoEvents();
|
|
1572
|
+
const { onSeeked, onTimeUpdate, onLoadedMetadata, onProgress, onPlay, onPause, onEnded: onEndedHook, } = useVideoEvents();
|
|
1409
1573
|
return (React__default.createElement("div", { ref: setVideoWrapperRef, className: `video-player ${height || "h-full"} ${width || "w-full"} mx-auto absolute` },
|
|
1410
1574
|
trackPoster && (React__default.createElement("div", { className: "pip-poster absolute inset-0 bg-center bg-cover hidden", style: { backgroundImage: `url(${trackPoster})` } })),
|
|
1411
|
-
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:
|
|
1412
|
-
|
|
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: {
|
|
1413
1582
|
headerConfig: {
|
|
1414
1583
|
config: {
|
|
1415
1584
|
isTrailer: isTrailer,
|
|
@@ -1427,7 +1596,7 @@ const VideoPlayer = ({ trackSrc, trackTitle, intro, onClose, trackPoster, isTrai
|
|
|
1427
1596
|
},
|
|
1428
1597
|
},
|
|
1429
1598
|
},
|
|
1430
|
-
} }),
|
|
1599
|
+
} })),
|
|
1431
1600
|
React__default.createElement(SubtitleOverlay, { styleConfig: subtitleStyle }),
|
|
1432
1601
|
showSkipIntro && (React__default.createElement(VideoActionButton, { text: "Skip Intro", onClick: handleSkipIntro, position: "left" }))));
|
|
1433
1602
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Hls from "hls.js";
|
|
2
|
+
import * as dashjs from "dashjs";
|
|
2
3
|
export interface VideoRefsState {
|
|
3
4
|
videoRef: HTMLVideoElement | null;
|
|
4
5
|
setVideoRef: (ref: HTMLVideoElement) => void;
|
|
@@ -30,14 +31,30 @@ export interface VideoControlsState {
|
|
|
30
31
|
setControls: (controls: boolean) => void;
|
|
31
32
|
isFullscreen: boolean;
|
|
32
33
|
setIsFullscreen: (isFullscreen: boolean) => void;
|
|
34
|
+
controlsVisible: boolean;
|
|
35
|
+
setControlsVisible: (visible: boolean) => void;
|
|
33
36
|
}
|
|
34
37
|
export interface VideoQualityState {
|
|
35
|
-
hlsInstance?: Hls;
|
|
36
|
-
setHlsInstance: (hlsInstance: Hls) => void;
|
|
37
|
-
|
|
38
|
-
|
|
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;
|
|
39
54
|
activeQuality: string;
|
|
40
55
|
setActiveQuality: (activeQuality: string) => void;
|
|
56
|
+
streamType: "hls" | "dash" | "mp4" | "other";
|
|
57
|
+
setStreamType: (streamType: "hls" | "dash" | "mp4" | "other") => void;
|
|
41
58
|
}
|
|
42
59
|
export interface SubtitleTrack {
|
|
43
60
|
lang: string;
|
package/package.json
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zezosoft/react-player",
|
|
3
|
-
"version": "0.0.
|
|
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
|
],
|
|
@@ -57,7 +52,12 @@
|
|
|
57
52
|
"zustand": "^5.0.3"
|
|
58
53
|
},
|
|
59
54
|
"dependencies": {
|
|
55
|
+
"dashjs": "^5.0.3",
|
|
60
56
|
"react-icons": "^5.5.0",
|
|
61
57
|
"screenfull": "^6.0.2"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "npx rollup -c",
|
|
61
|
+
"dev": "npx rollup -c -w"
|
|
62
62
|
}
|
|
63
|
-
}
|
|
63
|
+
}
|