@zezosoft/react-player 0.0.1

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.
Files changed (27) hide show
  1. package/README.md +97 -0
  2. package/dist/VideoPlayer/VideoPlayer.d.ts +21 -0
  3. package/dist/VideoPlayer/_components/BottomControls.d.ts +5 -0
  4. package/dist/VideoPlayer/_components/ControlsHeader.d.ts +4 -0
  5. package/dist/VideoPlayer/_components/MiddleControls.d.ts +3 -0
  6. package/dist/VideoPlayer/_components/Overlay.d.ts +4 -0
  7. package/dist/VideoPlayer/_components/TimeLine/TimeLine.d.ts +20 -0
  8. package/dist/VideoPlayer/_components/TimeLine/_components/hoverTimeWithPreview.d.ts +16 -0
  9. package/dist/VideoPlayer/_components/TimeLine/_components/thumb.d.ts +9 -0
  10. package/dist/VideoPlayer/_components/TimeLine/_components/timeCodeItem.d.ts +21 -0
  11. package/dist/VideoPlayer/_components/TimeLine/_components/timeCodes.d.ts +15 -0
  12. package/dist/VideoPlayer/_components/TimeLine/utils/getEndTimeByIndex.d.ts +2 -0
  13. package/dist/VideoPlayer/_components/TimeLine/utils/getHoverTimePosition.d.ts +3 -0
  14. package/dist/VideoPlayer/_components/TimeLine/utils/getPositionPercent.d.ts +1 -0
  15. package/dist/VideoPlayer/_components/TimeLine/utils/getTimeScale.d.ts +1 -0
  16. package/dist/VideoPlayer/_components/TimeLine/utils/isInRange.d.ts +1 -0
  17. package/dist/VideoPlayer/_components/TimeLine/utils/positionToMs.d.ts +1 -0
  18. package/dist/VideoPlayer/_components/TimeLine/utils/secondsToTime.d.ts +6 -0
  19. package/dist/VideoPlayer/_components/TimeLine/utils/timeToTimeString.d.ts +1 -0
  20. package/dist/VideoPlayer/_components/VideoPlayerControls.d.ts +4 -0
  21. package/dist/VideoPlayer/utils/index.d.ts +29 -0
  22. package/dist/components/ui/Popover.d.ts +6 -0
  23. package/dist/components/ui/tooltip.d.ts +8 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.js +648 -0
  26. package/dist/store/VideoState.d.ts +25 -0
  27. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # Zezo React Player
2
+
3
+ ## Installation
4
+
5
+ To install the `zezo-react-player` package, use the following command:
6
+
7
+ ```sh
8
+ npm install zezo-react-player
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Import and use the `VideoPlayer` component in your React project:
14
+
15
+ ```tsx
16
+ import { useCallback, useRef } from "react";
17
+ import { VideoPlayer } from "zezo-react-player";
18
+
19
+ function App() {
20
+ const previewImage = useRef("");
21
+ const updatePreviewImage = (hoverTime: number) => {
22
+ const url = `https://fakeimg.pl/720x405?text=${hoverTime}`;
23
+ const image = document.createElement("img");
24
+ image.src = url;
25
+ image.onload = () => {
26
+ previewImage.current = url;
27
+ };
28
+ };
29
+
30
+ const handleGettingPreview = useCallback((hoverTime: number) => {
31
+ // FIND AND RETURN LOADED!!! VIDEO PREVIEW ACCORDING TO the hoverTime TIME
32
+ updatePreviewImage(hoverTime);
33
+ return previewImage.current;
34
+ }, []);
35
+
36
+ return (
37
+ <div className="w-[720px]">
38
+ <VideoPlayer
39
+ trackPoster="https://i.ytimg.com/vi/Uh60VDKg348/maxresdefault.jpg"
40
+ trackSrc="https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8"
41
+ trackTitle="Mehmaan"
42
+ isTrailer={false}
43
+ width="120px"
44
+ height="180px"
45
+ timeCodes={[
46
+ {
47
+ fromMs: 0,
48
+ description: "This is a very long first part label you could use",
49
+ },
50
+ { fromMs: 130000, description: "This is the second part" },
51
+ { fromMs: 270000, description: "One more part label" },
52
+ { fromMs: 440000, description: "Final battle" },
53
+ { fromMs: 600000, description: "Cast" },
54
+ ]}
55
+ getPreviewScreenUrl={handleGettingPreview}
56
+ />
57
+ </div>
58
+ );
59
+ }
60
+
61
+ export default App;
62
+ ```
63
+
64
+ ## Props
65
+
66
+ | Prop Name | Type | Description |
67
+ | --------------------- | ------------------------------------------------ | ------------------------------------------------------- |
68
+ | `trackPoster` | `string` | URL of the video poster image. |
69
+ | `trackSrc` | `string` | Video source URL (MP4, HLS, etc.). |
70
+ | `trackTitle` | `string` | Title of the video. |
71
+ | `isTrailer` | `boolean` | Specifies if the video is a trailer. |
72
+ | `width` | `string` | Width of the video player. |
73
+ | `height` | `string` | Height of the video player. |
74
+ | `timeCodes` | `Array<{ fromMs: number, description: string }>` | List of time-based markers with descriptions. |
75
+ | `getPreviewScreenUrl` | `(timeMs: number) => string` | Function to generate preview screen URLs based on time. |
76
+
77
+ ## Example
78
+
79
+ ```tsx
80
+ <VideoPlayer
81
+ trackPoster="https://example.com/poster.jpg"
82
+ trackSrc="https://example.com/video.mp4"
83
+ trackTitle="Sample Video"
84
+ isTrailer={true}
85
+ width="640px"
86
+ height="360px"
87
+ timeCodes={[
88
+ { fromMs: 0, description: "Intro" },
89
+ { fromMs: 60000, description: "Main Scene" },
90
+ ]}
91
+ getPreviewScreenUrl={(timeMs) => `https://example.com/preview?time=${timeMs}`}
92
+ />
93
+ ```
94
+
95
+ ## License
96
+
97
+ MIT License.
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { TimeCode } from "./_components/TimeLine/TimeLine";
3
+ import { IOnWatchTimeUpdated } from "../types";
4
+ interface Props {
5
+ trackSrc: string;
6
+ trackTitle?: string;
7
+ trackPoster?: string;
8
+ isTrailer?: boolean;
9
+ className?: string;
10
+ type?: "hls" | "mp4" | "other";
11
+ width?: string;
12
+ height?: string;
13
+ timeCodes?: TimeCode[];
14
+ getPreviewScreenUrl?: (hoverTimeValue: number) => string;
15
+ tracking?: {
16
+ onViewed?: () => void;
17
+ onWatchTimeUpdated?: (e: IOnWatchTimeUpdated) => void;
18
+ };
19
+ }
20
+ declare const VideoPlayer: React.FC<Props>;
21
+ export default VideoPlayer;
@@ -0,0 +1,5 @@
1
+ import React from "react";
2
+ import "./TimeLine/time-line.css";
3
+ import { IControlsBottomProps } from "../../types";
4
+ declare const BottomControls: React.FC<IControlsBottomProps>;
5
+ export default BottomControls;
@@ -0,0 +1,4 @@
1
+ import * as React from "react";
2
+ import { IControlsHeaderProps } from "../../types";
3
+ declare const ControlsHeader: React.FC<IControlsHeaderProps>;
4
+ 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 Overlay: React.FC<IPlayerConfig>;
4
+ export default Overlay;
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ export interface TimeCode {
3
+ fromMs: number;
4
+ description: string;
5
+ }
6
+ export interface Props {
7
+ max: number;
8
+ currentTime: number;
9
+ bufferTime?: number;
10
+ offset?: number;
11
+ timeCodes?: TimeCode[];
12
+ hideThumbTooltip?: boolean;
13
+ limitTimeTooltipBySides?: boolean;
14
+ secondsPrefix?: string;
15
+ minutesPrefix?: string;
16
+ onChange: (time: number, offsetTime: number) => void;
17
+ getPreviewScreenUrl?: (hoverTimeValue: number) => string;
18
+ trackColor?: string;
19
+ }
20
+ export declare const VideoSeekSlider: React.FC<Props>;
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ interface Props {
3
+ max: number;
4
+ hoverTimeValue: number;
5
+ trackWidth: number;
6
+ seekHoverPosition: number;
7
+ offset: number;
8
+ isThumbActive: boolean;
9
+ limitTimeTooltipBySides: boolean;
10
+ label: string;
11
+ secondsPrefix?: string;
12
+ minutesPrefix?: string;
13
+ getPreviewScreenUrl?: (hoverTimeValue: number) => string;
14
+ }
15
+ export declare const HoverTimeWithPreview: React.FC<Props>;
16
+ export {};
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ interface Props {
3
+ max: number;
4
+ currentTime: number;
5
+ isThumbActive: boolean;
6
+ trackColor?: string;
7
+ }
8
+ export declare const Thumb: React.FC<Props>;
9
+ export {};
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ export interface TimeCode {
3
+ fromMs: number;
4
+ description: string;
5
+ }
6
+ export interface Props {
7
+ currentTime: number;
8
+ seekHoverTime: number;
9
+ bufferTime: number;
10
+ startTime: number;
11
+ endTime: number;
12
+ maxTime: number;
13
+ label?: string;
14
+ isTimePassed?: boolean;
15
+ isBufferPassed?: boolean;
16
+ isHoverPassed?: boolean;
17
+ onHover?: (label: string) => void;
18
+ withGap?: boolean;
19
+ trackColor?: string;
20
+ }
21
+ export declare const TimeCodeItem: React.FC<Props>;
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { TimeCode } from "./timeCodeItem";
3
+ export interface Props {
4
+ max: number;
5
+ currentTime: number;
6
+ bufferTime: number;
7
+ seekHoverPosition: number;
8
+ timeCodes: TimeCode[] | undefined;
9
+ trackWidth: number;
10
+ mobileSeeking: boolean;
11
+ label: string;
12
+ setLabel: React.Dispatch<React.SetStateAction<string>>;
13
+ trackColor?: string;
14
+ }
15
+ export declare const TimeCodes: React.FC<Props>;
@@ -0,0 +1,2 @@
1
+ import { TimeCode } from "../_components/timeCodeItem";
2
+ export declare const getEndTimeByIndex: (timeCodes: TimeCode[], index: number, max: number) => number;
@@ -0,0 +1,3 @@
1
+ export declare function getHoverTimePosition(seekHoverPosition: number, hoverTimeElement: HTMLDivElement | null, trackWidth: number, limitTimeTooltipBySides: boolean): {
2
+ transform: string;
3
+ };
@@ -0,0 +1 @@
1
+ export declare function getPositionPercent(max: number, current: number): number;
@@ -0,0 +1 @@
1
+ export declare function getTimeScale(currentTime: number, startTime: number, endTime: number, isTimePassed: boolean): number;
@@ -0,0 +1 @@
1
+ export declare const isInRange: (time: number, start: number, end: number) => boolean;
@@ -0,0 +1 @@
1
+ export declare function positionToMs(max: number, position: number, trackWidth: number): number;
@@ -0,0 +1,6 @@
1
+ export interface Time {
2
+ hh: string;
3
+ mm: string;
4
+ ss: string;
5
+ }
6
+ export declare function millisecondsToTime(ms: number, offset?: number): Time;
@@ -0,0 +1 @@
1
+ export declare function timeToTimeString(max: number, seekHoverTime: number, offset?: number, minutesPrefix?: string, secondsPrefix?: string): string;
@@ -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;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @description Converts seconds to hh:mm:ss
3
+ * @param seconds
4
+ * @returns
5
+ */
6
+ export declare const timeFormat: (seconds: number) => string;
7
+ /**
8
+ * @description Converts seconds to hh:mm
9
+ * @param seconds
10
+ */
11
+ export declare const timeFormatForContent: (seconds: number) => string;
12
+ /**
13
+ * @description Converts seconds to milliseconds
14
+ * @param seconds
15
+ * @returns
16
+ */
17
+ export declare const secondsToMilliseconds: (seconds: number) => number;
18
+ /**
19
+ * @description Converts milliseconds to seconds
20
+ * @param milliseconds
21
+ * @returns
22
+ */
23
+ export declare const millisecondsToSeconds: (milliseconds: number) => number;
24
+ /**
25
+ * @description get extension from url
26
+ * @param url
27
+ * @returns string | undefined
28
+ */
29
+ export declare const getExtensionFromUrl: (url: string) => string | undefined;
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ declare const Popover: ({ button, children, }: {
3
+ button: React.ReactNode;
4
+ children: React.ReactNode;
5
+ }) => React.JSX.Element;
6
+ export default Popover;
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ interface TooltipProps {
3
+ children: React.ReactNode;
4
+ title: string;
5
+ position?: "top" | "bottom" | "left" | "right";
6
+ }
7
+ declare const Tooltip: React.FC<TooltipProps>;
8
+ export default Tooltip;
@@ -0,0 +1,3 @@
1
+ import "./index.css";
2
+ export { default as VideoPlayer } from "./VideoPlayer/VideoPlayer";
3
+ export { useVideoStore } from "./store/VideoState";
package/dist/index.js ADDED
@@ -0,0 +1,648 @@
1
+ import * as React from 'react';
2
+ import React__default, { memo, useCallback, useEffect, useRef, useState } from 'react';
3
+ import { create } from 'zustand';
4
+ import { Settings, Check, VolumeOff, Volume2, Shrink, Maximize, X } from 'lucide-react';
5
+ import Hls from 'hls.js';
6
+
7
+ function styleInject(css, ref) {
8
+ if ( ref === void 0 ) ref = {};
9
+ var insertAt = ref.insertAt;
10
+
11
+ if (!css || typeof document === 'undefined') { return; }
12
+
13
+ var head = document.head || document.getElementsByTagName('head')[0];
14
+ var style = document.createElement('style');
15
+ style.type = 'text/css';
16
+
17
+ if (insertAt === 'top') {
18
+ if (head.firstChild) {
19
+ head.insertBefore(style, head.firstChild);
20
+ } else {
21
+ head.appendChild(style);
22
+ }
23
+ } else {
24
+ head.appendChild(style);
25
+ }
26
+
27
+ if (style.styleSheet) {
28
+ style.styleSheet.cssText = css;
29
+ } else {
30
+ style.appendChild(document.createTextNode(css));
31
+ }
32
+ }
33
+
34
+ var css_248z$1 = "/*! tailwindcss v4.0.14 | MIT License | https://tailwindcss.com */\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-gray-200: oklch(0.928 0.006 264.531);\n --color-gray-900: oklch(0.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-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 --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 --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-font-feature-settings: var(--font-sans--font-feature-settings);\n --default-font-variation-settings: var(\n --font-sans--font-variation-settings\n );\n --default-mono-font-family: var(--font-mono);\n --default-mono-font-feature-settings: var(\n --font-mono--font-feature-settings\n );\n --default-mono-font-variation-settings: var(\n --font-mono--font-variation-settings\n );\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 body {\n line-height: inherit;\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 color: color-mix(in oklab, currentColor 50%, transparent);\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 :-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 .absolute {\n position: absolute;\n }\n .relative {\n position: relative;\n }\n .inset-0 {\n inset: calc(var(--spacing) * 0);\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-full {\n right: 100%;\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-full {\n left: 100%;\n }\n .z-50 {\n z-index: 50;\n }\n .mx-1 {\n margin-inline: calc(var(--spacing) * 1);\n }\n .mx-auto {\n margin-inline: auto;\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-2 {\n margin-bottom: calc(var(--spacing) * 2);\n }\n .ml-2 {\n margin-left: calc(var(--spacing) * 2);\n }\n .flex {\n display: flex;\n }\n .inline-block {\n display: inline-block;\n }\n .size-10 {\n width: calc(var(--spacing) * 10);\n height: calc(var(--spacing) * 10);\n }\n .h-5 {\n height: calc(var(--spacing) * 5);\n }\n .h-10 {\n height: calc(var(--spacing) * 10);\n }\n .h-full {\n height: 100%;\n }\n .w-5 {\n width: calc(var(--spacing) * 5);\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 .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 .cursor-pointer {\n cursor: pointer;\n }\n .flex-col {\n flex-direction: column;\n }\n .items-center {\n align-items: center;\n }\n .justify-between {\n justify-content: space-between;\n }\n .justify-center {\n justify-content: center;\n }\n .gap-1\\.5 {\n gap: calc(var(--spacing) * 1.5);\n }\n .gap-2 {\n gap: calc(var(--spacing) * 2);\n }\n .gap-7 {\n gap: calc(var(--spacing) * 7);\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-gray-200 {\n border-color: var(--color-gray-200);\n }\n .bg-\\[rgba\\(0\\,0\\,0\\,0\\.5\\)\\] {\n background-color: rgba(0,0,0,0.5);\n }\n .bg-gray-900 {\n background-color: var(--color-gray-900);\n }\n .bg-white {\n background-color: var(--color-white);\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 .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-10 {\n padding-inline: calc(var(--spacing) * 10);\n }\n .py-1 {\n padding-block: calc(var(--spacing) * 1);\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-sm {\n font-size: var(--text-sm);\n line-height: var(--tw-leading, var(--text-sm--line-height));\n }\n .font-bold {\n --tw-font-weight: var(--font-weight-bold);\n font-weight: var(--font-weight-bold);\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-white {\n color: var(--color-white);\n }\n .shadow-lg {\n --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: 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 .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 .lg\\:size-12 {\n @media (width >= 64rem) {\n width: calc(var(--spacing) * 12);\n height: calc(var(--spacing) * 12);\n }\n }\n .lg\\:size-15 {\n @media (width >= 64rem) {\n width: calc(var(--spacing) * 15);\n height: calc(var(--spacing) * 15);\n }\n }\n .lg\\:h-8 {\n @media (width >= 64rem) {\n height: calc(var(--spacing) * 8);\n }\n }\n .lg\\:w-8 {\n @media (width >= 64rem) {\n width: calc(var(--spacing) * 8);\n }\n }\n .lg\\:pb-10 {\n @media (width >= 64rem) {\n padding-bottom: calc(var(--spacing) * 10);\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-lg {\n @media (width >= 64rem) {\n font-size: var(--text-lg);\n line-height: var(--tw-leading, var(--text-lg--line-height));\n }\n }\n .lg\\:text-xl {\n @media (width >= 64rem) {\n font-size: var(--text-xl);\n line-height: var(--tw-leading, var(--text-xl--line-height));\n }\n }\n}\n.noCursor {\n cursor: none !important;\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 initial-value: rotateX(0);\n}\n@property --tw-rotate-y {\n syntax: \"*\";\n inherits: false;\n initial-value: rotateY(0);\n}\n@property --tw-rotate-z {\n syntax: \"*\";\n inherits: false;\n initial-value: rotateZ(0);\n}\n@property --tw-skew-x {\n syntax: \"*\";\n inherits: false;\n initial-value: skewX(0);\n}\n@property --tw-skew-y {\n syntax: \"*\";\n inherits: false;\n initial-value: skewY(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-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-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-duration {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-ease {\n syntax: \"*\";\n inherits: false;\n}";
35
+ styleInject(css_248z$1,{"insertAt":"top"});
36
+
37
+ const useVideoStore = create((set) => ({
38
+ videoRef: null,
39
+ setVideoRef: (ref) => set({ videoRef: ref }),
40
+ videoWrapperRef: null,
41
+ setVideoWrapperRef: (ref) => set({ videoWrapperRef: ref }),
42
+ isPlaying: false,
43
+ setIsPlaying: (isPlaying) => set({ isPlaying }),
44
+ controls: false,
45
+ setControls: (controls) => set({ controls }),
46
+ currentTime: 0,
47
+ setCurrentTime: (currentTime) => set({ currentTime }),
48
+ hlsInstance: undefined,
49
+ setHlsInstance: (hlsInstance) => set({ hlsInstance }),
50
+ qualityLevels: undefined,
51
+ setQualityLevels: (qualityLevels) => set({ qualityLevels }),
52
+ activeQuality: "auto",
53
+ setActiveQuality: (activeQuality) => set({ activeQuality }),
54
+ isFullscreen: false,
55
+ setIsFullscreen: (isFullscreen) => set({ isFullscreen }),
56
+ duration: 0,
57
+ setDuration: (duration) => set({ duration }),
58
+ }));
59
+
60
+ /**
61
+ * @description Converts seconds to hh:mm:ss
62
+ * @param seconds
63
+ * @returns
64
+ */
65
+ const timeFormat = (seconds) => {
66
+ if (isNaN(seconds)) {
67
+ return `00:00`;
68
+ }
69
+ const date = new Date(seconds * 1000);
70
+ const hh = date.getUTCHours();
71
+ const mm = date.getUTCMinutes();
72
+ const ss = date.getUTCSeconds().toString().padStart(2, "0");
73
+ if (hh) {
74
+ return `${hh}:${mm.toString().padStart(2, "0")}:${ss}`;
75
+ }
76
+ return `${mm}:${ss}`;
77
+ };
78
+ /**
79
+ * @description Converts seconds to milliseconds
80
+ * @param seconds
81
+ * @returns
82
+ */
83
+ const secondsToMilliseconds = (seconds) => {
84
+ return seconds * 1000;
85
+ };
86
+ /**
87
+ * @description get extension from url
88
+ * @param url
89
+ * @returns string | undefined
90
+ */
91
+ const getExtensionFromUrl = (url) => {
92
+ const extension = url?.split(".")?.pop();
93
+ if (extension === "m3u8") {
94
+ return "hls";
95
+ }
96
+ return extension;
97
+ };
98
+
99
+ function getPositionPercent(max, current) {
100
+ const divider = max || -1; // prevent division by zero
101
+ return (current * 100) / divider;
102
+ }
103
+
104
+ const isInRange = (time, start, end) => time >= start && time <= end;
105
+
106
+ function getTimeScale(currentTime, startTime, endTime, isTimePassed) {
107
+ const isActiveTime = isInRange(currentTime, startTime, endTime);
108
+ const timeDiff = endTime - startTime;
109
+ const timeDiffWithCurrent = currentTime - startTime;
110
+ const currentScalePercent = isActiveTime ? timeDiffWithCurrent / timeDiff : 0;
111
+ return isTimePassed ? 1 : currentScalePercent;
112
+ }
113
+
114
+ const TimeCodeItem = memo(({ label = "", startTime, maxTime, endTime, currentTime, seekHoverTime, bufferTime, isTimePassed = false, isBufferPassed = false, isHoverPassed = false, onHover = () => undefined, withGap, trackColor, }) => {
115
+ const positionPercent = getPositionPercent(maxTime, startTime);
116
+ const timeDiff = endTime - startTime;
117
+ const widthPercent = (timeDiff / maxTime) * 100;
118
+ const mainClassName = `main${withGap ? " with-gap" : ""}`;
119
+ const currentTimeScale = getTimeScale(currentTime, startTime, endTime, isTimePassed);
120
+ const seekHoverTimeScale = getTimeScale(seekHoverTime, startTime, endTime, isHoverPassed);
121
+ const bufferTimeScale = getTimeScale(bufferTime, startTime, endTime, isBufferPassed);
122
+ const handleMouseMove = () => onHover(label);
123
+ return (React__default.createElement("div", { className: mainClassName, onMouseMove: handleMouseMove, style: {
124
+ width: `${widthPercent}%`,
125
+ left: `${positionPercent}%`,
126
+ } },
127
+ React__default.createElement("div", { className: "inner-seek-block buffered", "data-test-id": "test-buffered", style: { transform: `scaleX(${bufferTimeScale})` } }),
128
+ React__default.createElement("div", { className: "inner-seek-block seek-hover", "data-test-id": "test-seek-hover", style: { transform: `scaleX(${seekHoverTimeScale})` } }),
129
+ React__default.createElement("div", { className: "inner-seek-block connect", style: {
130
+ transform: `scaleX(${currentTimeScale})`,
131
+ backgroundColor: trackColor || "#ff0000",
132
+ } })));
133
+ });
134
+
135
+ function positionToMs(max, position, trackWidth) {
136
+ const percent = (position * 100) / trackWidth;
137
+ return Math.floor(+(percent * (max / 100)));
138
+ }
139
+
140
+ const getEndTimeByIndex = (timeCodes, index, max) => (index + 1 < timeCodes.length ? timeCodes[index + 1].fromMs : max);
141
+
142
+ const TimeCodes = ({ max = 1000, currentTime = 0, bufferTime = 0, seekHoverPosition = 0, timeCodes, trackWidth, mobileSeeking, label, setLabel, trackColor, }) => {
143
+ const hoverTimeValue = positionToMs(max, seekHoverPosition, trackWidth);
144
+ const handleLabelChange = useCallback((currentLabel) => {
145
+ if (label !== currentLabel) {
146
+ setLabel(currentLabel);
147
+ }
148
+ }, [label]);
149
+ useEffect(() => {
150
+ if (!mobileSeeking) {
151
+ return;
152
+ }
153
+ const currentCode = timeCodes?.find(({ fromMs }, index) => {
154
+ const endTime = getEndTimeByIndex(timeCodes, index, max);
155
+ return isInRange(currentTime, fromMs, endTime);
156
+ });
157
+ if (currentCode?.description !== label) {
158
+ setLabel(currentCode?.description || "");
159
+ }
160
+ }, [currentTime, label, max, timeCodes]);
161
+ return (React__default.createElement(React__default.Fragment, null, timeCodes?.map(({ fromMs, description }, index) => {
162
+ const endTime = getEndTimeByIndex(timeCodes, index, max);
163
+ const isTimePassed = endTime <= currentTime;
164
+ const isBufferPassed = endTime <= bufferTime;
165
+ const isHoverPassed = endTime <= hoverTimeValue;
166
+ let inRange = isInRange(currentTime, fromMs, endTime);
167
+ const newCurrentTime = isTimePassed || !inRange ? 0 : currentTime;
168
+ inRange = isInRange(bufferTime, fromMs, endTime);
169
+ const newBufferTime = isBufferPassed || !inRange ? 0 : bufferTime;
170
+ inRange = isInRange(hoverTimeValue, fromMs, endTime);
171
+ const newHoverTime = isHoverPassed || !inRange ? 0 : hoverTimeValue;
172
+ return (React__default.createElement(TimeCodeItem, { key: fromMs, label: description, maxTime: max, startTime: fromMs, endTime: endTime, isTimePassed: isTimePassed, isBufferPassed: isBufferPassed, isHoverPassed: isHoverPassed, currentTime: newCurrentTime, bufferTime: newBufferTime, seekHoverTime: newHoverTime, onHover: handleLabelChange, withGap: true, trackColor: trackColor }));
173
+ })));
174
+ };
175
+
176
+ function getHoverTimePosition(seekHoverPosition, hoverTimeElement, trackWidth, limitTimeTooltipBySides) {
177
+ let position = 0;
178
+ if (hoverTimeElement) {
179
+ position = seekHoverPosition - hoverTimeElement.offsetWidth / 2;
180
+ if (limitTimeTooltipBySides) {
181
+ if (position < 0) {
182
+ position = 0;
183
+ }
184
+ else if (position + hoverTimeElement.offsetWidth > trackWidth) {
185
+ position = trackWidth - hoverTimeElement.offsetWidth;
186
+ }
187
+ }
188
+ }
189
+ return { transform: `translateX(${position}px)` };
190
+ }
191
+
192
+ function millisecondsToTime(ms, offset = 0) {
193
+ const roundedSeconds = Math.round(ms / 1000 + offset);
194
+ const hours = Math.floor(roundedSeconds / 3600);
195
+ const divirsForMinutes = roundedSeconds % 3600;
196
+ const minutes = Math.floor(divirsForMinutes / 60);
197
+ const sec = Math.ceil(divirsForMinutes % 60);
198
+ return {
199
+ hh: hours.toString(),
200
+ mm: minutes < 10 ? `0${minutes}` : minutes.toString(),
201
+ ss: sec < 10 ? `0${sec}` : sec.toString(),
202
+ };
203
+ }
204
+
205
+ function timeToTimeString(max, seekHoverTime, offset = 0, minutesPrefix = "", secondsPrefix = "") {
206
+ const times = millisecondsToTime(seekHoverTime, offset);
207
+ if (max + offset < 60 * 1000) {
208
+ return secondsPrefix + times.ss;
209
+ }
210
+ if (max + offset < 3600 * 1000) {
211
+ return `${minutesPrefix + times.mm}:${times.ss}`;
212
+ }
213
+ return `${times.hh}:${times.mm}:${times.ss}`;
214
+ }
215
+
216
+ const HoverTimeWithPreview = ({ max, hoverTimeValue, offset, trackWidth, seekHoverPosition, isThumbActive, limitTimeTooltipBySides, label, minutesPrefix, secondsPrefix, getPreviewScreenUrl, }) => {
217
+ const hoverTimeElement = useRef(null);
218
+ const hoverTimeClassName = isThumbActive ? "hover-time active" : "hover-time";
219
+ const hoverTimePosition = getHoverTimePosition(seekHoverPosition, hoverTimeElement?.current, trackWidth, limitTimeTooltipBySides);
220
+ const hoverTimeString = timeToTimeString(max, hoverTimeValue, offset, minutesPrefix, secondsPrefix);
221
+ return (React__default.createElement("div", { className: hoverTimeClassName, style: hoverTimePosition, ref: hoverTimeElement, "data-testid": "hover-time" },
222
+ isThumbActive && getPreviewScreenUrl && (React__default.createElement("div", { className: "preview-screen", style: {
223
+ backgroundImage: `url(${getPreviewScreenUrl(hoverTimeValue)})`,
224
+ } })),
225
+ label && React__default.createElement("div", null, label),
226
+ hoverTimeString));
227
+ };
228
+
229
+ const Thumb = ({ max, currentTime, isThumbActive, trackColor, }) => {
230
+ const getThumbHandlerPosition = () => {
231
+ const thumbConstantOffset = -6;
232
+ const leftPosition = (currentTime / max) * 100;
233
+ return {
234
+ left: `calc(${leftPosition}% + ${thumbConstantOffset}px)`,
235
+ };
236
+ };
237
+ return (React__default.createElement("div", { className: isThumbActive ? "thumb active" : "thumb active", "data-testid": "testThumb", style: getThumbHandlerPosition() },
238
+ React__default.createElement("div", { className: "handler", style: {
239
+ backgroundColor: trackColor || "#ff0000",
240
+ } })));
241
+ };
242
+
243
+ const VideoSeekSlider = ({ max = 1000, currentTime = 0, bufferTime = 0, hideThumbTooltip = false, offset = 0, secondsPrefix = "", minutesPrefix = "", limitTimeTooltipBySides = true, timeCodes, onChange = () => undefined, getPreviewScreenUrl, trackColor, }) => {
244
+ const [seekHoverPosition, setSeekHoverPosition] = useState(0);
245
+ const [label, setLabel] = useState("");
246
+ const seeking = useRef(false);
247
+ const mobileSeeking = useRef(false);
248
+ const trackElement = useRef(null);
249
+ const trackWidth = trackElement.current?.offsetWidth || 0;
250
+ const isThumbActive = seekHoverPosition > 0 || seeking.current;
251
+ const hoverTimeValue = positionToMs(max, seekHoverPosition, trackWidth);
252
+ const changeCurrentTimePosition = (pageX) => {
253
+ const clientRect = trackElement.current?.getBoundingClientRect();
254
+ const left = clientRect?.left || 0;
255
+ const width = clientRect?.width || 0;
256
+ let position = pageX - left;
257
+ position = position < 0 ? 0 : position;
258
+ position = position > width ? width : position;
259
+ const percent = (position * 100) / width;
260
+ const time = +(percent * (max / 100)).toFixed(0);
261
+ setSeekHoverPosition(position);
262
+ onChange(time, time + offset);
263
+ };
264
+ const handleTouchSeeking = (event) => {
265
+ event.preventDefault();
266
+ event.stopPropagation();
267
+ if (!mobileSeeking.current) {
268
+ return;
269
+ }
270
+ const { changedTouches } = event;
271
+ let pageX = changedTouches?.[changedTouches.length - 1]?.pageX || 0;
272
+ pageX = pageX < 0 ? 0 : pageX;
273
+ changeCurrentTimePosition(pageX);
274
+ };
275
+ const handleSeeking = (event) => {
276
+ if (seeking.current) {
277
+ changeCurrentTimePosition(event.pageX);
278
+ }
279
+ };
280
+ const handleTrackHover = (clear, event) => {
281
+ const left = trackElement.current?.getBoundingClientRect().left || 0;
282
+ const position = clear ? 0 : event.pageX - left;
283
+ setSeekHoverPosition(position);
284
+ };
285
+ const setMobileSeeking = (state = true) => {
286
+ mobileSeeking.current = state;
287
+ setSeekHoverPosition(state ? seekHoverPosition : 0);
288
+ };
289
+ const setSeeking = (state, event) => {
290
+ event.preventDefault();
291
+ handleSeeking(event);
292
+ seeking.current = state;
293
+ setSeekHoverPosition(state ? seekHoverPosition : 0);
294
+ };
295
+ const mouseSeekingHandler = (event) => {
296
+ setSeeking(false, event);
297
+ };
298
+ const mobileTouchSeekingHandler = () => {
299
+ setMobileSeeking(false);
300
+ };
301
+ useEffect(() => {
302
+ if (!mobileSeeking.current) {
303
+ return;
304
+ }
305
+ const currentCode = timeCodes?.find(({ fromMs }, index) => {
306
+ const endTime = getEndTimeByIndex(timeCodes, index, max);
307
+ return isInRange(currentTime, fromMs, endTime);
308
+ });
309
+ if (currentCode?.description !== label) {
310
+ setLabel(currentCode?.description || "");
311
+ }
312
+ }, [currentTime, label, max, timeCodes]);
313
+ useEffect(() => {
314
+ window.addEventListener("mousemove", handleSeeking);
315
+ window.addEventListener("mouseup", mouseSeekingHandler);
316
+ window.addEventListener("touchmove", handleTouchSeeking);
317
+ window.addEventListener("touchend", mobileTouchSeekingHandler);
318
+ return () => {
319
+ window.removeEventListener("mousemove", handleSeeking);
320
+ window.removeEventListener("mouseup", mouseSeekingHandler);
321
+ window.removeEventListener("touchmove", handleTouchSeeking);
322
+ window.removeEventListener("touchend", mobileTouchSeekingHandler);
323
+ };
324
+ }, [max, offset, trackWidth]);
325
+ return (React__default.createElement("div", { className: "ui-video-seek-slider" },
326
+ React__default.createElement("div", { className: isThumbActive ? "track active" : "track", ref: trackElement, onMouseMove: (event) => handleTrackHover(false, event), onMouseLeave: (event) => handleTrackHover(true, event), onMouseDown: (event) => setSeeking(true, event), onTouchStart: () => setMobileSeeking(true), "data-testid": "main-track" },
327
+ Boolean(timeCodes?.length) && (React__default.createElement(TimeCodes, { currentTime: currentTime, max: max, bufferTime: bufferTime, seekHoverPosition: seekHoverPosition, timeCodes: timeCodes, mobileSeeking: mobileSeeking.current, trackWidth: trackWidth, label: label, setLabel: setLabel, trackColor: trackColor })),
328
+ !timeCodes && (React__default.createElement(TimeCodeItem, { maxTime: max, startTime: 0, endTime: max, currentTime: currentTime, bufferTime: bufferTime, seekHoverTime: hoverTimeValue, trackColor: trackColor }))),
329
+ !hideThumbTooltip && (React__default.createElement(HoverTimeWithPreview, { max: max, hoverTimeValue: hoverTimeValue, isThumbActive: isThumbActive, label: label, limitTimeTooltipBySides: limitTimeTooltipBySides, offset: offset, seekHoverPosition: seekHoverPosition, trackWidth: trackWidth, getPreviewScreenUrl: getPreviewScreenUrl, minutesPrefix: minutesPrefix, secondsPrefix: secondsPrefix })),
330
+ React__default.createElement(Thumb, { max: max, currentTime: currentTime, isThumbActive: isThumbActive, trackColor: trackColor })));
331
+ };
332
+
333
+ var css_248z = ".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, .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 }";
334
+ styleInject(css_248z,{"insertAt":"top"});
335
+
336
+ const BottomControls = ({ config }) => {
337
+ const { videoRef, currentTime, isFullscreen } = useVideoStore();
338
+ const duration = videoRef?.duration;
339
+ return (React__default.createElement("div", { className: "px-10 text-white" },
340
+ React__default.createElement(VideoSeekSlider, { max: secondsToMilliseconds(duration || 0), currentTime: secondsToMilliseconds(currentTime || 0), bufferTime: secondsToMilliseconds(0), onChange: (currentTime) => {
341
+ if (videoRef) {
342
+ videoRef.currentTime = currentTime / 1000;
343
+ }
344
+ }, secondsPrefix: "00:00:", minutesPrefix: "00:", getPreviewScreenUrl: config?.seekBarConfig?.getPreviewScreenUrl, timeCodes: config?.seekBarConfig?.timeCodes, trackColor: config?.seekBarConfig?.trackColor }),
345
+ React__default.createElement("div", { className: `pt-6 ${isFullscreen ? "pb-10" : "pb-16"} lg:pb-10 flex gap-2 items-center` },
346
+ React__default.createElement("p", { className: "lg:text-xl font-semibold" }, timeFormat(currentTime || 0)),
347
+ " ",
348
+ React__default.createElement("p", { className: "lg:text-2xl" }, "/"),
349
+ " ",
350
+ React__default.createElement("p", { className: "lg:text-xl font-semibold" }, timeFormat(duration || 0)))));
351
+ };
352
+
353
+ const Tooltip = ({ children, title, position = "top", }) => {
354
+ const [visible, setVisible] = useState(false);
355
+ const positionStyles = {
356
+ top: "bottom-full left-1/2 transform -translate-x-1/2 mb-2",
357
+ bottom: "top-full left-1/2 transform -translate-x-1/2 mt-2",
358
+ left: "right-full top-1/2 transform -translate-y-1/2 mr-2",
359
+ right: "left-full top-1/2 transform -translate-y-1/2 ml-2",
360
+ };
361
+ return (React__default.createElement("div", { className: "relative inline-block cursor-pointer", onMouseEnter: () => setVisible(true), onMouseLeave: () => setVisible(false) },
362
+ children,
363
+ 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))));
364
+ };
365
+
366
+ const Popover = ({ button, children, }) => {
367
+ const [isOpen, setIsOpen] = useState(false);
368
+ const popoverRef = useRef(null);
369
+ // Close popover when clicking outside
370
+ useEffect(() => {
371
+ const handleClickOutside = (event) => {
372
+ if (popoverRef.current &&
373
+ !popoverRef.current.contains(event.target)) {
374
+ setIsOpen(false);
375
+ }
376
+ };
377
+ document.addEventListener("mousedown", handleClickOutside);
378
+ return () => document.removeEventListener("mousedown", handleClickOutside);
379
+ }, []);
380
+ return (React__default.createElement("div", { className: "relative inline-block", ref: popoverRef },
381
+ React__default.createElement("div", { onClick: () => setIsOpen(!isOpen) }, button),
382
+ isOpen && (React__default.createElement("div", { className: "absolute left-0 mt-2 w-fit bg-white shadow-lg rounded-lg border border-gray-200 z-50 p-4" }, children))));
383
+ };
384
+
385
+ const ControlsHeader = ({ config }) => {
386
+ const className = "w-5 h-5 lg:w-8 lg:h-8";
387
+ const { videoWrapperRef, videoRef, qualityLevels, hlsInstance, setActiveQuality, activeQuality, } = useVideoStore();
388
+ const handleFullscreen = () => {
389
+ if (document.fullscreenElement) {
390
+ document.exitFullscreen();
391
+ }
392
+ else {
393
+ videoWrapperRef?.requestFullscreen();
394
+ }
395
+ };
396
+ const isFullscreen = document.fullscreenElement !== null;
397
+ const handleMute = () => {
398
+ if (videoRef?.muted) {
399
+ if (videoRef) {
400
+ videoRef.muted = false;
401
+ }
402
+ }
403
+ else {
404
+ if (videoRef) {
405
+ videoRef.muted = true;
406
+ }
407
+ }
408
+ };
409
+ return (React.createElement("div", { className: "flex items-center justify-between p-10 bg-gradient-to-b from-black" },
410
+ React.createElement("div", null,
411
+ config?.title && (React.createElement("h1", { className: "text-white lg:text-2xl font-bold" }, config.title)),
412
+ config?.isTrailer && (React.createElement("h1", { className: "text-white lg:text-lg" }, "Trailer"))),
413
+ React.createElement("div", { className: "flex items-center gap-7 text-white" },
414
+ React.createElement("div", null,
415
+ React.createElement(Tooltip, { title: "Settings" },
416
+ React.createElement(Popover, { button: React.createElement(Settings, { className: className }) },
417
+ React.createElement("div", { className: "text-black" },
418
+ React.createElement("div", null,
419
+ React.createElement("p", { className: "p-2 font-bold" }, "Quality"),
420
+ React.createElement("p", { onClick: () => {
421
+ if (hlsInstance) {
422
+ hlsInstance.currentLevel = -1;
423
+ setActiveQuality("auto");
424
+ }
425
+ }, className: "p-2 cursor-pointer flex items-center gap-1.5" },
426
+ activeQuality === "auto" && React.createElement(Check, null),
427
+ " Auto"),
428
+ qualityLevels
429
+ ?.map((level, index) => (React.createElement("p", { key: index, onClick: () => {
430
+ if (hlsInstance) {
431
+ hlsInstance.currentLevel = index;
432
+ setActiveQuality(String(level.height));
433
+ }
434
+ }, className: "p-2 cursor-pointer flex items-center gap-1.5" },
435
+ activeQuality === String(level.height) && React.createElement(Check, null),
436
+ " ",
437
+ level.height,
438
+ "p")))
439
+ .reverse()))))),
440
+ React.createElement("div", { onClick: handleMute }, videoRef?.muted ? (React.createElement(Tooltip, { title: "Unmute" },
441
+ React.createElement(VolumeOff, { className: className }))) : (React.createElement(Tooltip, { title: "Mute" },
442
+ React.createElement(Volume2, { className: className })))),
443
+ React.createElement("div", { onClick: handleFullscreen }, isFullscreen ? (React.createElement(Tooltip, { title: "Exit" },
444
+ React.createElement(Shrink, { className: className }))) : (React.createElement(Tooltip, { title: "Fullscreen" },
445
+ React.createElement(Maximize, { className: className })))),
446
+ config?.onClose && (React.createElement(React.Fragment, null,
447
+ React.createElement("div", { className: "w-[2px] h-10 bg-white mx-1" }),
448
+ React.createElement("div", { onClick: config.onClose },
449
+ React.createElement(Tooltip, { title: "Close" },
450
+ React.createElement(X, { className: className }))))))));
451
+ };
452
+
453
+ const MiddleControls = () => {
454
+ const { videoRef, isPlaying, setIsPlaying } = useVideoStore();
455
+ const handlePlayPause = () => {
456
+ if (!videoRef)
457
+ return;
458
+ if (videoRef.paused) {
459
+ videoRef
460
+ .play()
461
+ .catch((error) => console.error("Error playing video:", error));
462
+ setIsPlaying(true);
463
+ }
464
+ else {
465
+ videoRef.pause();
466
+ setIsPlaying(false);
467
+ }
468
+ };
469
+ const handleBackword = () => {
470
+ if (!videoRef)
471
+ return;
472
+ videoRef.currentTime -= 10;
473
+ };
474
+ const handleForword = () => {
475
+ if (!videoRef)
476
+ return;
477
+ videoRef.currentTime += 10;
478
+ };
479
+ const nextAndPrevIconSize = "size-10 lg:size-12";
480
+ const playPauseIconSize = "size-10 lg:size-15";
481
+ return (React__default.createElement("div", { className: "flex justify-center items-center" },
482
+ React__default.createElement("div", { onClick: handleBackword, className: "w-[15vw] flex justify-center items-center h-full cursor-pointer" },
483
+ React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: nextAndPrevIconSize, fill: "none", viewBox: "0 0 67 67" },
484
+ React__default.createElement("path", { fill: "#fff", 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.04zm.48 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" }))),
485
+ React__default.createElement("div", { className: "w-[10vw] flex justify-center items-center h-full cursor-pointer", onClick: handlePlayPause }, isPlaying ? (React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: playPauseIconSize, fill: "none", viewBox: "0 0 67 67" },
486
+ React__default.createElement("path", { fill: "#fff", 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: playPauseIconSize, width: "67", height: "67", fill: "none", viewBox: "0 0 67 67" },
487
+ React__default.createElement("path", { fill: "#fff", 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" })))),
488
+ React__default.createElement("div", { className: "w-[15vw] flex justify-center items-center h-full cursor-pointer", onClick: handleForword },
489
+ React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: nextAndPrevIconSize, fill: "none", viewBox: "0 0 67 67" },
490
+ React__default.createElement("path", { fill: "#fff", 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-.04zm.48 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" })))));
491
+ };
492
+
493
+ const VideoPlayerControls = ({ config, height, width, }) => {
494
+ return (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` },
495
+ React.createElement(ControlsHeader, { config: config?.headerConfig?.config }),
496
+ React.createElement(MiddleControls, null),
497
+ React.createElement(BottomControls, { config: config?.bottomConfig?.config })));
498
+ };
499
+
500
+ const Overlay = ({ config }) => {
501
+ const controlsTimerRef = useRef(null);
502
+ const { setControls, controls } = useVideoStore();
503
+ const handleMouseEnter = useCallback(() => {
504
+ const videoPlayerControls = document?.getElementById("videoPlayerControls");
505
+ if (videoPlayerControls) {
506
+ videoPlayerControls.classList.remove("noCursor");
507
+ }
508
+ setControls(true);
509
+ if (controlsTimerRef.current) {
510
+ clearTimeout(controlsTimerRef.current);
511
+ }
512
+ controlsTimerRef.current = setTimeout(() => {
513
+ setControls(false);
514
+ if (videoPlayerControls) {
515
+ videoPlayerControls.classList.add("noCursor");
516
+ }
517
+ }, 3000); // 3 seconds
518
+ }, [setControls]);
519
+ return (React.createElement("div", { id: "videoPlayerControls", className: "absolute inset-0", onMouseMove: handleMouseEnter }, controls && React.createElement(VideoPlayerControls, { config: config })));
520
+ };
521
+
522
+ const VideoPlayer = ({ trackSrc, trackTitle, trackPoster, isTrailer, className, type, height, width, timeCodes, getPreviewScreenUrl, tracking, }) => {
523
+ const { setVideoRef, setCurrentTime, setVideoWrapperRef, videoRef, setQualityLevels, setHlsInstance, setDuration, setIsPlaying, } = useVideoStore();
524
+ const onRightClick = (e) => {
525
+ e.preventDefault();
526
+ };
527
+ useEffect(() => {
528
+ if (!videoRef) {
529
+ return;
530
+ }
531
+ const getVideoExtension = getExtensionFromUrl(trackSrc);
532
+ const contentType = type || getVideoExtension;
533
+ if (contentType === "mp4") {
534
+ videoRef.src = trackSrc;
535
+ setQualityLevels([]);
536
+ }
537
+ else if (contentType === "hls") {
538
+ if (videoRef?.canPlayType("application/vnd.apple.mpegurl")) {
539
+ // Native HLS support (Safari)
540
+ videoRef.src = trackSrc;
541
+ }
542
+ else if (Hls.isSupported()) {
543
+ // Use hls.js for other browsers
544
+ const hls = new Hls();
545
+ hls.loadSource(trackSrc);
546
+ hls.attachMedia(videoRef);
547
+ setHlsInstance(hls);
548
+ // Get quality levels when HLS loads
549
+ hls.on(Hls.Events.MANIFEST_PARSED, () => {
550
+ setQualityLevels(hls.levels);
551
+ });
552
+ return () => {
553
+ hls.destroy(); // Cleanup on unmount
554
+ };
555
+ }
556
+ }
557
+ else {
558
+ videoRef.src = trackSrc;
559
+ setQualityLevels([]);
560
+ }
561
+ }, [trackSrc, videoRef]);
562
+ // Analytics Start
563
+ const startTime = useRef(null);
564
+ const isViewCounted = useRef(false);
565
+ useEffect(() => {
566
+ if (videoRef) {
567
+ videoRef.addEventListener("play", () => {
568
+ if (!isViewCounted.current) {
569
+ isViewCounted.current = true;
570
+ if (tracking?.onViewed) {
571
+ tracking.onViewed();
572
+ }
573
+ }
574
+ startTime.current = Date.now();
575
+ setIsPlaying(true);
576
+ });
577
+ videoRef.addEventListener("pause", () => {
578
+ if (startTime.current) {
579
+ const elapsedTime = (Date.now() - startTime.current) / 1000;
580
+ const getCurrentTime = localStorage.getItem("current_time");
581
+ localStorage.setItem("current_time", (Number(getCurrentTime || 0) + elapsedTime).toString());
582
+ startTime.current = null;
583
+ }
584
+ setIsPlaying(false);
585
+ });
586
+ return () => {
587
+ videoRef.removeEventListener("play", () => { });
588
+ videoRef.removeEventListener("pause", () => { });
589
+ };
590
+ }
591
+ }, [videoRef]);
592
+ const handleUnload = (e) => {
593
+ e.preventDefault();
594
+ if (startTime.current) {
595
+ const elapsedTime = (Date.now() - startTime.current) / 1000;
596
+ const getCurrentTime = localStorage.getItem("current_time");
597
+ localStorage.setItem("current_time", (Number(getCurrentTime || 0) + elapsedTime).toString());
598
+ }
599
+ const totalTimeWatched = Number(localStorage.getItem("current_time") || 0);
600
+ if (totalTimeWatched >= 30) {
601
+ if (tracking?.onWatchTimeUpdated) {
602
+ tracking.onWatchTimeUpdated({
603
+ watchTime: totalTimeWatched,
604
+ });
605
+ }
606
+ }
607
+ localStorage.setItem("current_time", "0");
608
+ };
609
+ useEffect(() => {
610
+ window.addEventListener("beforeunload", handleUnload);
611
+ window.addEventListener("unload", handleUnload);
612
+ return () => {
613
+ window.removeEventListener("beforeunload", handleUnload);
614
+ window.removeEventListener("unload", handleUnload);
615
+ };
616
+ }, [startTime]);
617
+ return (React__default.createElement("div", { ref: setVideoWrapperRef, className: `${height || "h-full"} ${width || "w-full"} mx-auto relative` },
618
+ React__default.createElement("video", { ref: setVideoRef, className: `w-full h-full ${className}`, poster: trackPoster, onContextMenu: onRightClick, onTimeUpdate: (e) => {
619
+ if (e?.currentTarget?.currentTime) {
620
+ setCurrentTime(e?.currentTarget?.currentTime);
621
+ }
622
+ }, onLoad: (e) => {
623
+ if (e?.currentTarget?.duration) {
624
+ localStorage.setItem("current_time", "0");
625
+ setDuration(e?.currentTarget?.duration);
626
+ }
627
+ } },
628
+ React__default.createElement("track", { kind: "captions", srcLang: "en", label: "English", default: true, src: "https://res.cloudinary.com/dm4uaqlio/raw/upload/v1742096015/sintel-captions-en_ehel5s.vtt" })),
629
+ React__default.createElement(Overlay, { height: height, width: width, config: {
630
+ headerConfig: {
631
+ config: {
632
+ isTrailer: isTrailer,
633
+ title: trackTitle,
634
+ },
635
+ },
636
+ bottomConfig: {
637
+ config: {
638
+ seekBarConfig: {
639
+ timeCodes: timeCodes,
640
+ trackColor: "white",
641
+ getPreviewScreenUrl,
642
+ },
643
+ },
644
+ },
645
+ } })));
646
+ };
647
+
648
+ export { VideoPlayer, useVideoStore };
@@ -0,0 +1,25 @@
1
+ import Hls from "hls.js";
2
+ interface VideoState {
3
+ videoRef: HTMLVideoElement | null;
4
+ setVideoRef: (ref: HTMLVideoElement) => void;
5
+ videoWrapperRef: HTMLDivElement | null;
6
+ setVideoWrapperRef: (ref: HTMLDivElement) => void;
7
+ isPlaying: boolean;
8
+ setIsPlaying: (isPlaying: boolean) => void;
9
+ controls: boolean;
10
+ setControls: (controls: boolean) => void;
11
+ currentTime: number;
12
+ setCurrentTime: (currentTime: number) => void;
13
+ hlsInstance?: Hls;
14
+ setHlsInstance: (hlsInstance: Hls) => void;
15
+ qualityLevels?: Hls["levels"];
16
+ setQualityLevels: (qualityLevels: Hls["levels"]) => void;
17
+ activeQuality?: string;
18
+ setActiveQuality: (activeQuality: string) => void;
19
+ isFullscreen?: boolean;
20
+ setIsFullscreen: (isFullscreen: boolean) => void;
21
+ duration?: number;
22
+ setDuration: (duration: number) => void;
23
+ }
24
+ export declare const useVideoStore: import("zustand").UseBoundStore<import("zustand").StoreApi<VideoState>>;
25
+ export {};
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@zezosoft/react-player",
3
+ "version": "0.0.1",
4
+ "main": "dist/index.js",
5
+ "type": "module",
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
+ "files": [
13
+ "dist"
14
+ ],
15
+ "keywords": [
16
+ "video",
17
+ "player",
18
+ "react",
19
+ "react-player",
20
+ "react-video-player",
21
+ "react-video",
22
+ "react-video-player",
23
+ "react-video-component",
24
+ "react-video-component",
25
+ "react-video-component"
26
+ ],
27
+ "author": "Pukhraj Dhamu",
28
+ "license": "MIT",
29
+ "description": "A lightweight and customizable video player by Zezosoft, built for seamless streaming with advanced controls, adaptive playback, and modern UI. Perfect for web and React applications.",
30
+ "devDependencies": {
31
+ "@rollup/plugin-typescript": "^12.1.2",
32
+ "@tailwindcss/postcss": "^4.0.14",
33
+ "@types/react": "^19.0.10",
34
+ "hls.js": "^1.5.20",
35
+ "lucide-react": "^0.481.0",
36
+ "postcss": "^8.5.3",
37
+ "react": "^19.0.0",
38
+ "react-dom": "^19.0.0",
39
+ "rollup": "^4.35.0",
40
+ "rollup-plugin-postcss": "^4.0.2",
41
+ "tailwindcss": "^4.0.14",
42
+ "tslib": "^2.8.1",
43
+ "zustand": "^5.0.3"
44
+ },
45
+ "peerDependencies": {
46
+ "hls.js": "^1.5.20",
47
+ "lucide-react": "^0.481.0",
48
+ "react": "^19.0.0",
49
+ "react-dom": "^19.0.0",
50
+ "zustand": "^5.0.3"
51
+ }
52
+ }