@nikelroot/video-player 0.1.0

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/index.js ADDED
@@ -0,0 +1,1340 @@
1
+ // src/VideoPlayer.tsx
2
+ import {
3
+ forwardRef,
4
+ memo,
5
+ useCallback,
6
+ useEffect,
7
+ useImperativeHandle,
8
+ useMemo as useMemo2,
9
+ useRef,
10
+ useState
11
+ } from "react";
12
+
13
+ // src/controls.tsx
14
+ import { Flex, Button, Slider, Typography } from "antd";
15
+ import {
16
+ BackwardFilled,
17
+ FastForwardFilled,
18
+ FullscreenOutlined,
19
+ MutedFilled,
20
+ PauseOutlined,
21
+ PlaySquareFilled,
22
+ SoundFilled
23
+ } from "@ant-design/icons";
24
+
25
+ // src/styles.ts
26
+ import styled, { css } from "styled-components";
27
+ var VideoPlayerControlStyles = styled.div`
28
+ position: absolute;
29
+ left: 0;
30
+ right: 0;
31
+ bottom: 0;
32
+ padding: 5px;
33
+ color: #fff;
34
+ z-index: 1000;
35
+
36
+ ${(props) => props.$showControl && css`
37
+ background: rgba(0, 0, 0, 0.5);
38
+ `};
39
+
40
+ .full {
41
+ flex: 1 auto;
42
+ }
43
+
44
+ .video-player-control-title {
45
+ color: #fff;
46
+ max-width: 40%;
47
+ }
48
+
49
+ ${(props) => !props.$showControl && css`
50
+ .ant-flex {
51
+ > * {
52
+ opacity: 0;
53
+ display: none;
54
+ }
55
+ > .seek-slider {
56
+ opacity: 1;
57
+ display: block;
58
+ top: 2px;
59
+ }
60
+ }
61
+ `};
62
+
63
+ ${(props) => props.$tiny && css`
64
+ padding: 0;
65
+ `};
66
+ `;
67
+ var VideoPlayerStyles = styled.div`
68
+ position: relative;
69
+ aspect-ratio: 16 / 9;
70
+ max-height: 100%;
71
+ max-width: 100vw;
72
+ background: #000;
73
+
74
+ ${(props) => props.$color && css`
75
+ box-shadow: 0 0 5px 1px ${props.$color};
76
+ `};
77
+
78
+ &,
79
+ * {
80
+ user-select: none;
81
+ }
82
+
83
+ ${(props) => props.$isMobile && css`
84
+ max-width: 100vw;
85
+ min-height: auto !important;
86
+ `};
87
+
88
+ video {
89
+ display: block;
90
+ max-width: 100%;
91
+ width: 100%;
92
+ max-height: 100%;
93
+ }
94
+
95
+ .video-wrapper {
96
+ position: relative;
97
+ z-index: 10;
98
+ transition: all 0.25s ease-in-out;
99
+ max-height: 100%;
100
+ overflow: hidden;
101
+ display: flex;
102
+ justify-content: center;
103
+ align-items: center;
104
+ }
105
+
106
+ .video-error-overlay {
107
+ position: absolute;
108
+ inset: 0;
109
+ z-index: 13;
110
+ display: flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ padding: 16px;
114
+ background: rgba(0, 0, 0, 0.72);
115
+ }
116
+
117
+ .video-error-content {
118
+ display: flex;
119
+ flex-direction: column;
120
+ align-items: center;
121
+ gap: 12px;
122
+ max-width: 320px;
123
+ text-align: center;
124
+ color: #fff;
125
+ }
126
+
127
+ .video-error-title {
128
+ font-size: 14px;
129
+ line-height: 1.4;
130
+ }
131
+
132
+ .video-error-details {
133
+ display: flex;
134
+ flex-direction: column;
135
+ gap: 6px;
136
+ width: 100%;
137
+ padding: 10px 12px;
138
+ border-radius: 10px;
139
+ background: rgba(255, 255, 255, 0.08);
140
+ font-size: 12px;
141
+ line-height: 1.45;
142
+ text-align: left;
143
+ word-break: break-word;
144
+ }
145
+
146
+ .video-error-detail {
147
+ opacity: 0.92;
148
+ }
149
+
150
+ .video-error-retry {
151
+ border: 0;
152
+ border-radius: 999px;
153
+ padding: 8px 14px;
154
+ background: #fff;
155
+ color: #000;
156
+ font-size: 13px;
157
+ cursor: pointer;
158
+ }
159
+
160
+ ${(props) => props.type === "torrent" && css`
161
+ width: 100%;
162
+ video {
163
+ width: 100%;
164
+ }
165
+ `};
166
+
167
+ .video-top-bar {
168
+ padding: 5px;
169
+ position: absolute;
170
+ left: 0;
171
+ right: 0;
172
+ top: 0;
173
+ z-index: 11;
174
+ }
175
+
176
+ .video-right-bar {
177
+ position: absolute;
178
+ right: 0;
179
+ top: 0;
180
+ bottom: 0;
181
+ z-index: 12;
182
+ display: flex;
183
+ align-items: stretch;
184
+ pointer-events: auto;
185
+ margin-top: 32px;
186
+ overflow-y: auto;
187
+ background: rgba(0, 0, 0, 0.01);
188
+ max-width: 380px;
189
+ color: #fff;
190
+ transition: all ease-in-out 0.25s;
191
+ opacity: 0.01;
192
+ border-radius: 3px;
193
+
194
+ &:hover {
195
+ background: rgba(0, 0, 0, 0.1);
196
+ opacity: 1;
197
+ }
198
+
199
+ .video-right-bar-toggle {
200
+ flex-direction: column;
201
+ justify-content: center;
202
+ align-items: center;
203
+ color: #fff;
204
+ font-size: 12px;
205
+ width: 20px;
206
+ cursor: pointer;
207
+ display: none !important;
208
+ }
209
+
210
+ .video-right-bar-content {
211
+ display: block;
212
+ }
213
+ }
214
+
215
+ ${(props) => (props.$showControl || props.$alwaysShowSegments) && css`
216
+ .video-right-bar {
217
+ background: rgba(0, 0, 0, 0.1);
218
+ opacity: 1;
219
+ }
220
+ `};
221
+ `;
222
+ var SeekSliderStyled = styled.div`
223
+ position: absolute;
224
+ left: 0;
225
+ right: 0;
226
+ top: -12px;
227
+
228
+ .ant-slider-rail {
229
+ height: 20px;
230
+ top: auto;
231
+ bottom: 0;
232
+ background: transparent !important;
233
+ }
234
+
235
+ .ant-slider {
236
+ &:hover {
237
+ .ant-slider-rail {
238
+ background: transparent !important;
239
+ }
240
+ }
241
+ }
242
+
243
+ .ant-slider-track {
244
+ top: 6px;
245
+ }
246
+
247
+ .ant-slider-handle {
248
+ top: 3px;
249
+ }
250
+
251
+ .ant-slider-step {
252
+ height: 8px;
253
+ background: rgba(255, 255, 255, 0.1);
254
+ }
255
+
256
+ .ant-slider {
257
+ margin: 0;
258
+ }
259
+ `;
260
+
261
+ // src/utils.ts
262
+ import { useMemo, useSyncExternalStore } from "react";
263
+ var formatTime = (seconds) => {
264
+ const totalSeconds = Number.parseInt(String(seconds), 10);
265
+ const safeSeconds = Number.isFinite(totalSeconds) ? totalSeconds : 0;
266
+ const hours = Math.floor(safeSeconds / 3600);
267
+ const minutes = Math.floor(safeSeconds % 3600 / 60);
268
+ const secs = safeSeconds % 60;
269
+ return [
270
+ hours.toString().padStart(2, "0"),
271
+ minutes.toString().padStart(2, "0"),
272
+ secs.toString().padStart(2, "0")
273
+ ].join(":");
274
+ };
275
+ var widthSnapshot = 0;
276
+ var resizeRafId = null;
277
+ var isListening = false;
278
+ var listeners = /* @__PURE__ */ new Set();
279
+ var notifyAll = () => {
280
+ listeners.forEach((listener) => listener());
281
+ };
282
+ var resizeHandler = () => {
283
+ if (typeof window === "undefined") return;
284
+ const nextWidth = window.innerWidth;
285
+ if (resizeRafId != null) cancelAnimationFrame(resizeRafId);
286
+ resizeRafId = requestAnimationFrame(() => {
287
+ if (widthSnapshot === nextWidth) return;
288
+ widthSnapshot = nextWidth;
289
+ notifyAll();
290
+ });
291
+ };
292
+ var startListening = () => {
293
+ if (typeof window === "undefined" || isListening) return;
294
+ isListening = true;
295
+ widthSnapshot = window.innerWidth;
296
+ window.addEventListener("resize", resizeHandler, { passive: true });
297
+ };
298
+ var stopListening = () => {
299
+ if (typeof window === "undefined" || !isListening || listeners.size > 0) return;
300
+ window.removeEventListener("resize", resizeHandler);
301
+ if (resizeRafId != null) {
302
+ cancelAnimationFrame(resizeRafId);
303
+ resizeRafId = null;
304
+ }
305
+ isListening = false;
306
+ };
307
+ var subscribe = (listener) => {
308
+ listeners.add(listener);
309
+ startListening();
310
+ return () => {
311
+ listeners.delete(listener);
312
+ stopListening();
313
+ };
314
+ };
315
+ var getSnapshot = () => {
316
+ if (typeof window !== "undefined" && !isListening) {
317
+ widthSnapshot = window.innerWidth;
318
+ }
319
+ return widthSnapshot;
320
+ };
321
+ var getServerSnapshot = () => 0;
322
+ function useWindowWidth(mobileLimit = 768) {
323
+ const width = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
324
+ const isMobile = width <= mobileLimit;
325
+ const mobileType = useMemo(() => {
326
+ if (width <= mobileLimit) return "mobile";
327
+ if (width <= 1024) return "laptop";
328
+ return "default";
329
+ }, [width, mobileLimit]);
330
+ return [width, isMobile, mobileType];
331
+ }
332
+ var createThrottledNumberFn = (fn, waitMs) => {
333
+ let timer = null;
334
+ let lastCall = 0;
335
+ let pendingValue = null;
336
+ const throttled = ((value) => {
337
+ const now = Date.now();
338
+ const elapsed = now - lastCall;
339
+ if (elapsed >= waitMs) {
340
+ lastCall = now;
341
+ fn(value);
342
+ return;
343
+ }
344
+ pendingValue = value;
345
+ if (timer) return;
346
+ timer = setTimeout(() => {
347
+ timer = null;
348
+ lastCall = Date.now();
349
+ if (pendingValue != null) {
350
+ fn(pendingValue);
351
+ pendingValue = null;
352
+ }
353
+ }, waitMs - elapsed);
354
+ });
355
+ throttled.cancel = () => {
356
+ if (timer) clearTimeout(timer);
357
+ timer = null;
358
+ pendingValue = null;
359
+ };
360
+ return throttled;
361
+ };
362
+
363
+ // src/controls.tsx
364
+ import { jsx, jsxs } from "react/jsx-runtime";
365
+ var SeekSlider = ({ playAction, duration, currentTime, seekSetTime }) => {
366
+ const formatter = (value) => {
367
+ return formatTime(Number(value != null ? value : 0));
368
+ };
369
+ const clickHandler = (e) => {
370
+ e.preventDefault();
371
+ e.stopPropagation();
372
+ };
373
+ const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
374
+ const safeCurrentTime = Math.max(0, Math.min(Number.isFinite(currentTime) ? currentTime : 0, safeDuration || Infinity));
375
+ return /* @__PURE__ */ jsx(SeekSliderStyled, { onClick: clickHandler, className: "seek-slider", children: /* @__PURE__ */ jsx(
376
+ Slider,
377
+ {
378
+ onChange: seekSetTime,
379
+ onChangeComplete: () => {
380
+ void playAction();
381
+ },
382
+ value: safeCurrentTime,
383
+ tooltip: { formatter },
384
+ max: safeDuration,
385
+ keyboard: false
386
+ }
387
+ ) });
388
+ };
389
+ var TimeBlock = ({ duration, currentTime }) => {
390
+ return /* @__PURE__ */ jsxs(Typography.Text, { style: { color: "#fff", whiteSpace: "nowrap" }, children: [
391
+ formatTime(currentTime),
392
+ " / ",
393
+ formatTime(duration)
394
+ ] });
395
+ };
396
+ var stopControlClick = (e) => {
397
+ e.preventDefault();
398
+ e.stopPropagation();
399
+ };
400
+ var VideoPlayerControls = (props) => {
401
+ const {
402
+ variant,
403
+ fullScreenAction,
404
+ showControl,
405
+ videoRef,
406
+ playAction,
407
+ pauseAction,
408
+ seekSetTime,
409
+ seekPrevAction,
410
+ seekNextAction,
411
+ playing,
412
+ muted,
413
+ onMutedChange,
414
+ duration,
415
+ currentTime,
416
+ title
417
+ } = props;
418
+ const [, isMobile] = useWindowWidth();
419
+ if (variant === "none") return null;
420
+ if (!videoRef.current) return null;
421
+ if (variant === "tiny") {
422
+ return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $tiny: true, $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs(Flex, { gap: "small", align: "center", justify: "space-between", children: [
423
+ /* @__PURE__ */ jsx("div", { className: "full" }),
424
+ /* @__PURE__ */ jsx(SeekSlider, { playAction, duration, currentTime, seekSetTime })
425
+ ] }) });
426
+ }
427
+ if (variant === "fullscreen-only") {
428
+ return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs(Flex, { gap: "small", align: "center", justify: "space-between", children: [
429
+ /* @__PURE__ */ jsx("div", { className: "full" }),
430
+ /* @__PURE__ */ jsx(Button, { size: "small", onClick: () => onMutedChange(!muted), icon: muted ? /* @__PURE__ */ jsx(MutedFilled, {}) : /* @__PURE__ */ jsx(SoundFilled, {}) }),
431
+ /* @__PURE__ */ jsx(Button, { size: "small", onClick: fullScreenAction, icon: /* @__PURE__ */ jsx(FullscreenOutlined, {}) })
432
+ ] }) });
433
+ }
434
+ return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs(Flex, { gap: "small", align: "center", justify: "space-between", children: [
435
+ playing ? /* @__PURE__ */ jsx(Button, { size: "small", onClick: () => pauseAction(), icon: /* @__PURE__ */ jsx(PauseOutlined, {}) }) : /* @__PURE__ */ jsx(Button, { size: "small", onClick: () => playAction(), icon: /* @__PURE__ */ jsx(PlaySquareFilled, {}) }),
436
+ /* @__PURE__ */ jsx(Button, { size: "small", onClick: () => seekPrevAction(), icon: /* @__PURE__ */ jsx(BackwardFilled, {}) }),
437
+ /* @__PURE__ */ jsx(Button, { size: "small", onClick: () => seekNextAction(), icon: /* @__PURE__ */ jsx(FastForwardFilled, {}) }),
438
+ /* @__PURE__ */ jsx(Typography.Text, { ellipsis: true, className: "video-player-control-title", children: title }),
439
+ /* @__PURE__ */ jsx("div", { className: "full" }),
440
+ /* @__PURE__ */ jsx(SeekSlider, { playAction, duration, currentTime, seekSetTime }),
441
+ /* @__PURE__ */ jsx(TimeBlock, { duration, currentTime }),
442
+ /* @__PURE__ */ jsx(Button, { size: "small", onClick: () => onMutedChange(!muted), icon: muted ? /* @__PURE__ */ jsx(MutedFilled, {}) : /* @__PURE__ */ jsx(SoundFilled, {}) }),
443
+ /* @__PURE__ */ jsx(Button, { size: "small", onClick: fullScreenAction, icon: /* @__PURE__ */ jsx(FullscreenOutlined, {}) })
444
+ ] }) });
445
+ };
446
+
447
+ // src/configs.ts
448
+ var confVod = {
449
+ autoStartLoad: true,
450
+ enableWorker: true,
451
+ capLevelToPlayerSize: true,
452
+ appendErrorMaxRetry: 5,
453
+ maxBufferLength: 45,
454
+ maxMaxBufferLength: 90,
455
+ maxBufferHole: 0.3,
456
+ backBufferLength: 20,
457
+ fragLoadPolicy: {
458
+ default: {
459
+ maxTimeToFirstByteMs: 12e3,
460
+ maxLoadTimeMs: 2e4,
461
+ timeoutRetry: { maxNumRetry: 2, retryDelayMs: 500, maxRetryDelayMs: 2e3 },
462
+ errorRetry: { maxNumRetry: 2, retryDelayMs: 500, maxRetryDelayMs: 2e3 }
463
+ }
464
+ }
465
+ };
466
+ var confLiveStability = {
467
+ autoStartLoad: true,
468
+ enableWorker: true,
469
+ capLevelToPlayerSize: true,
470
+ lowLatencyMode: false,
471
+ liveSyncDuration: 7,
472
+ liveMaxLatencyDuration: 14,
473
+ maxBufferLength: 30,
474
+ backBufferLength: 20,
475
+ fragLoadPolicy: {
476
+ default: {
477
+ maxTimeToFirstByteMs: 15e3,
478
+ maxLoadTimeMs: 25e3,
479
+ timeoutRetry: { maxNumRetry: 3, retryDelayMs: 500, maxRetryDelayMs: 2500 },
480
+ errorRetry: { maxNumRetry: 3, retryDelayMs: 500, maxRetryDelayMs: 2500 }
481
+ }
482
+ },
483
+ debug: false
484
+ };
485
+ var withCredentialsConfig = (config, credentials) => {
486
+ if (!credentials || credentials === "same-origin") return config;
487
+ return {
488
+ ...config,
489
+ xhrSetup: (xhr, url) => {
490
+ var _a;
491
+ xhr.withCredentials = credentials === "include";
492
+ (_a = config.xhrSetup) == null ? void 0 : _a.call(config, xhr, url);
493
+ },
494
+ fetchSetup: (context, initParams) => {
495
+ var _a;
496
+ const request = new Request(context.url, {
497
+ ...initParams,
498
+ credentials
499
+ });
500
+ return ((_a = config.fetchSetup) == null ? void 0 : _a.call(config, context, initParams)) || request;
501
+ }
502
+ };
503
+ };
504
+
505
+ // src/errorDetails.ts
506
+ var isRecord = (value) => typeof value === "object" && value !== null;
507
+ var asText = (value) => {
508
+ if (typeof value === "string") {
509
+ const trimmed = value.trim();
510
+ return trimmed ? trimmed : null;
511
+ }
512
+ if (typeof value === "number" || typeof value === "boolean") {
513
+ return String(value);
514
+ }
515
+ return null;
516
+ };
517
+ var getMediaErrorCodeLabel = (code) => {
518
+ switch (code) {
519
+ case 1:
520
+ return "MEDIA_ERR_ABORTED";
521
+ case 2:
522
+ return "MEDIA_ERR_NETWORK";
523
+ case 3:
524
+ return "MEDIA_ERR_DECODE";
525
+ case 4:
526
+ return "MEDIA_ERR_SRC_NOT_SUPPORTED";
527
+ default:
528
+ return "MEDIA_ERR_UNKNOWN";
529
+ }
530
+ };
531
+ var getPlaybackErrorCodeText = (error) => {
532
+ if (!isRecord(error) || typeof error.code !== "number") return null;
533
+ return `${getMediaErrorCodeLabel(error.code)} (${error.code})`;
534
+ };
535
+ var getResponseSummary = (response) => {
536
+ if (!isRecord(response)) return null;
537
+ const code = asText(response.code);
538
+ const text = asText(response.text);
539
+ if (code && text) return `${code} ${text}`;
540
+ return code || text;
541
+ };
542
+ var getSource = (data) => {
543
+ if (!isRecord(data)) return null;
544
+ const directSource = asText(data.src) || asText(data.url);
545
+ if (directSource) return directSource;
546
+ const contextUrl = isRecord(data.context) ? asText(data.context.url) : null;
547
+ if (contextUrl) return contextUrl;
548
+ const fragUrl = isRecord(data.frag) ? asText(data.frag.url) : null;
549
+ if (fragUrl) return fragUrl;
550
+ const responseUrl = isRecord(data.response) ? asText(data.response.url) : null;
551
+ return responseUrl;
552
+ };
553
+ var addDetail = (details, label, value) => {
554
+ const text = asText(value);
555
+ if (!text) return;
556
+ const line = `${label}: ${text}`;
557
+ if (!details.includes(line)) {
558
+ details.push(line);
559
+ }
560
+ };
561
+ var formatPlaybackErrorDetails = (error, data) => {
562
+ const details = [];
563
+ addDetail(details, "\u041A\u043E\u0434", getPlaybackErrorCodeText(error));
564
+ if (error instanceof Error) {
565
+ addDetail(details, "\u041E\u0448\u0438\u0431\u043A\u0430", error.message || error.name);
566
+ } else if (typeof error === "string") {
567
+ addDetail(details, "\u041E\u0448\u0438\u0431\u043A\u0430", error);
568
+ }
569
+ if (isRecord(data)) {
570
+ addDetail(details, "\u0422\u0438\u043F", data.type);
571
+ addDetail(details, "\u0414\u0435\u0442\u0430\u043B\u0438", data.details);
572
+ addDetail(details, "\u041F\u0440\u0438\u0447\u0438\u043D\u0430", data.reason);
573
+ addDetail(details, "HTTP", getResponseSummary(data.response));
574
+ addDetail(details, "\u0418\u0441\u0442\u043E\u0447\u043D\u0438\u043A", getSource(data));
575
+ }
576
+ return details;
577
+ };
578
+
579
+ // src/VideoPlayer.tsx
580
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
581
+ var defaultMessages = {
582
+ defaultError: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0432\u0438\u0434\u0435\u043E",
583
+ aborted: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0432\u0438\u0434\u0435\u043E \u0431\u044B\u043B\u0430 \u043F\u0440\u0435\u0440\u0432\u0430\u043D\u0430",
584
+ network: "\u041E\u0448\u0438\u0431\u043A\u0430 \u0441\u0435\u0442\u0438 \u043F\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0435 \u0432\u0438\u0434\u0435\u043E",
585
+ decode: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0434\u0435\u043A\u043E\u0434\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0432\u0438\u0434\u0435\u043E",
586
+ unsupported: "\u0424\u043E\u0440\u043C\u0430\u0442 \u0432\u0438\u0434\u0435\u043E \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F",
587
+ playFailed: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043D\u0430\u0447\u0430\u0442\u044C \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0435\u0434\u0435\u043D\u0438\u0435",
588
+ streamLoadFailed: "\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0432\u0438\u0434\u0435\u043E\u043F\u043E\u0442\u043E\u043A\u0430",
589
+ streamDecodeFailed: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0434\u0435\u043A\u043E\u0434\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0432\u0438\u0434\u0435\u043E\u043F\u043E\u0442\u043E\u043A",
590
+ retry: "\u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C"
591
+ };
592
+ var MAX_NET_RETRIES = 3;
593
+ var MAX_FATAL_RELOADS = 3;
594
+ var LIVE_STALL_WINDOW_MS = 2e4;
595
+ var LIVE_STALLS_BEFORE_STABILITY = 3;
596
+ var hlsCtorPromise = null;
597
+ var activeExclusiveVideo = null;
598
+ var loadHlsCtor = () => {
599
+ if (!hlsCtorPromise) {
600
+ hlsCtorPromise = import("hls.js").then((mod) => mod.default);
601
+ }
602
+ return hlsCtorPromise;
603
+ };
604
+ var isInterruptedPlayRequestError = (error) => {
605
+ if (!(error instanceof Error)) return false;
606
+ return error.name === "AbortError" || error.message.includes("The play() request was interrupted by a new load request") || error.message.includes("The play() request was interrupted");
607
+ };
608
+ var canAttemptPlayback = (video) => {
609
+ return !!video && video.readyState >= HTMLMediaElement.HAVE_METADATA;
610
+ };
611
+ function hasNativeHlsSupport() {
612
+ if (typeof navigator === "undefined" || typeof document === "undefined") return false;
613
+ const isSafari = /^((?!chrome|android|firefox|opera|edg).)*safari/i.test(navigator.userAgent);
614
+ if (!isSafari) return false;
615
+ const video = document.createElement("video");
616
+ const mimes = ["application/vnd.apple.mpegurl", "application/x-mpegURL"];
617
+ return mimes.some((t) => {
618
+ const r = video.canPlayType(t);
619
+ return r === "probably" || r === "maybe";
620
+ });
621
+ }
622
+ var getNativeVideoErrorMessage = (error, messages) => {
623
+ if (!error) return messages.defaultError;
624
+ switch (error.code) {
625
+ case MediaError.MEDIA_ERR_ABORTED:
626
+ return messages.aborted;
627
+ case MediaError.MEDIA_ERR_NETWORK:
628
+ return messages.network;
629
+ case MediaError.MEDIA_ERR_DECODE:
630
+ return messages.decode;
631
+ case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
632
+ return messages.unsupported;
633
+ default:
634
+ return messages.defaultError;
635
+ }
636
+ };
637
+ var clampTime = (time, duration) => {
638
+ const safeTime = Number.isFinite(time) ? time : 0;
639
+ const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
640
+ return safeDuration > 0 ? Math.max(0, Math.min(safeTime, safeDuration)) : Math.max(0, safeTime);
641
+ };
642
+ var VideoPlayerBase = (props, ref) => {
643
+ const {
644
+ controlsVariant = "none",
645
+ autoPlay = false,
646
+ preload = "metadata",
647
+ loop = false,
648
+ type,
649
+ sourceType = "auto",
650
+ onVideoClick,
651
+ onVideoDoubleClick,
652
+ playOnClick = true,
653
+ live = false,
654
+ videoTopBar: VideoTopBar,
655
+ videoRightBar: VideoRightBar,
656
+ defaultPlaying = false,
657
+ playing: playingProp,
658
+ poster,
659
+ alwaysShowSegments = false,
660
+ initialTime = 0,
661
+ reloadKey,
662
+ onVideoRefChange,
663
+ handleClick = true,
664
+ onPointerDown,
665
+ onPointerMove,
666
+ onPointerUp,
667
+ onPointerCancel,
668
+ exclusivePlayback = true,
669
+ onMutedChange,
670
+ onPlayingChange,
671
+ onTimeChange,
672
+ onDurationChange,
673
+ onActiveChange,
674
+ onPlaybackError,
675
+ title,
676
+ scrollTo = true,
677
+ muted: mutedProp,
678
+ defaultMuted = false,
679
+ currentTime: currentTimeProp,
680
+ duration: durationProp,
681
+ hlsConfig,
682
+ vodHlsConfig,
683
+ liveHlsConfig,
684
+ hlsCredentials = "include",
685
+ crossOrigin = "use-credentials",
686
+ messages: messagesProp,
687
+ accentColor,
688
+ ...videoProps
689
+ } = props;
690
+ let { videoSrc = "" } = props;
691
+ videoSrc = videoSrc.replaceAll(" ", "%20").replaceAll("#", "%23");
692
+ const messages = useMemo2(() => ({ ...defaultMessages, ...messagesProp }), [messagesProp]);
693
+ const [, isMobile, mobileType] = useWindowWidth();
694
+ const videoRef = useRef(null);
695
+ const hlsRef = useRef(null);
696
+ const containerRef = useRef(null);
697
+ const netRetryCount = useRef(0);
698
+ const mediaRecoverCount = useRef(0);
699
+ const fatalReloadCount = useRef(0);
700
+ const reloadTimer = useRef(null);
701
+ const stalledNudgeTimer = useRef(null);
702
+ const liveStabilityMode = useRef(false);
703
+ const liveStallEvents = useRef([]);
704
+ const controlTime = useRef(null);
705
+ const [playingState, setPlayingState] = useState(!!defaultPlaying);
706
+ const [mutedState, setMutedState] = useState(!!defaultMuted);
707
+ const [durationState, setDurationState] = useState(0);
708
+ const [currentTimeState, setCurrentTimeState] = useState(0);
709
+ const [isFullscreen, setIsFullscreen] = useState(false);
710
+ const [showControl, setControlVisible] = useState(true);
711
+ const [errorState, setErrorState] = useState(null);
712
+ const [reloadToken, setReloadToken] = useState(0);
713
+ const playing = playingProp != null ? playingProp : playingState;
714
+ const muted = mutedProp != null ? mutedProp : mutedState;
715
+ const duration = durationProp != null ? durationProp : durationState;
716
+ const currentTime = currentTimeProp != null ? currentTimeProp : currentTimeState;
717
+ const setTimeD = useRef(
718
+ createThrottledNumberFn((value) => {
719
+ const v = videoRef.current;
720
+ if (!v) return;
721
+ v.currentTime = value;
722
+ }, 30)
723
+ );
724
+ const clearTimers = useCallback(() => {
725
+ if (reloadTimer.current) {
726
+ clearTimeout(reloadTimer.current);
727
+ reloadTimer.current = null;
728
+ }
729
+ if (stalledNudgeTimer.current) {
730
+ clearTimeout(stalledNudgeTimer.current);
731
+ stalledNudgeTimer.current = null;
732
+ }
733
+ if (controlTime.current) {
734
+ clearTimeout(controlTime.current);
735
+ controlTime.current = null;
736
+ }
737
+ }, []);
738
+ const setNextPlaying = useCallback(
739
+ (value) => {
740
+ if (playingProp === void 0) setPlayingState(value);
741
+ onPlayingChange == null ? void 0 : onPlayingChange(value);
742
+ },
743
+ [onPlayingChange, playingProp]
744
+ );
745
+ const setNextMuted = useCallback(
746
+ (value) => {
747
+ if (mutedProp === void 0) setMutedState(value);
748
+ const el = videoRef.current;
749
+ if (el) el.muted = value;
750
+ onMutedChange == null ? void 0 : onMutedChange(value);
751
+ },
752
+ [mutedProp, onMutedChange]
753
+ );
754
+ const clearErrorState = useCallback(() => {
755
+ setErrorState((prev) => prev ? null : prev);
756
+ }, []);
757
+ const reportError = useCallback(
758
+ (error, data, message = messages.defaultError) => {
759
+ const details = formatPlaybackErrorDetails(error, data);
760
+ const code = getPlaybackErrorCodeText(error);
761
+ setNextPlaying(false);
762
+ setErrorState({ message, error, data, details });
763
+ console.error("[VideoPlayer] playback error", {
764
+ message,
765
+ code,
766
+ details,
767
+ error,
768
+ data
769
+ });
770
+ onPlaybackError == null ? void 0 : onPlaybackError({ error, data, message, details });
771
+ },
772
+ [messages.defaultError, onPlaybackError, setNextPlaying]
773
+ );
774
+ const notifyActiveChange = useCallback(
775
+ (video, reason) => {
776
+ onActiveChange == null ? void 0 : onActiveChange({ video, reason });
777
+ },
778
+ [onActiveChange]
779
+ );
780
+ const onTime = useCallback(() => {
781
+ const el = videoRef.current;
782
+ if (!el) return;
783
+ const nextTime = Number.isFinite(el.currentTime) ? el.currentTime : 0;
784
+ if (currentTimeProp === void 0) setCurrentTimeState(nextTime);
785
+ onTimeChange == null ? void 0 : onTimeChange(nextTime, el);
786
+ }, [currentTimeProp, onTimeChange]);
787
+ const onDur = useCallback(() => {
788
+ const el = videoRef.current;
789
+ if (!el) return;
790
+ const nextDuration = Number.isFinite(el.duration) ? el.duration : 0;
791
+ if (durationProp === void 0) setDurationState(nextDuration);
792
+ onDurationChange == null ? void 0 : onDurationChange(nextDuration, el);
793
+ }, [durationProp, onDurationChange]);
794
+ const setVideoEl = useCallback(
795
+ (el) => {
796
+ const prev = videoRef.current;
797
+ if (prev === el) return;
798
+ if (prev) {
799
+ prev.removeEventListener("timeupdate", onTime);
800
+ prev.removeEventListener("durationchange", onDur);
801
+ }
802
+ videoRef.current = el;
803
+ onVideoRefChange == null ? void 0 : onVideoRefChange(el);
804
+ notifyActiveChange(el, "ref");
805
+ if (!el) return;
806
+ el.muted = muted;
807
+ el.addEventListener("timeupdate", onTime);
808
+ el.addEventListener("durationchange", onDur);
809
+ },
810
+ [muted, notifyActiveChange, onDur, onTime, onVideoRefChange]
811
+ );
812
+ const pauseOtherVideos = useCallback(
813
+ (el) => {
814
+ if (!exclusivePlayback) return;
815
+ if (typeof exclusivePlayback === "object") {
816
+ exclusivePlayback.pauseOthers(el);
817
+ return;
818
+ }
819
+ if (activeExclusiveVideo && activeExclusiveVideo !== el && !activeExclusiveVideo.paused) {
820
+ activeExclusiveVideo.pause();
821
+ }
822
+ activeExclusiveVideo = el;
823
+ },
824
+ [exclusivePlayback]
825
+ );
826
+ const playAction = useCallback(async () => {
827
+ const el = videoRef.current;
828
+ if (!el) return;
829
+ pauseOtherVideos(el);
830
+ try {
831
+ await el.play();
832
+ clearErrorState();
833
+ setNextPlaying(true);
834
+ notifyActiveChange(el, "play");
835
+ } catch (e) {
836
+ if (isInterruptedPlayRequestError(e)) return;
837
+ console.debug(e);
838
+ reportError(e, { type: "play" }, messages.playFailed);
839
+ }
840
+ if (scrollTo === true) {
841
+ const rect = el.getBoundingClientRect();
842
+ const inViewport = rect.top >= 0 && rect.left >= 0 && rect.bottom <= window.innerHeight && rect.right <= window.innerWidth;
843
+ if (!inViewport) {
844
+ el.scrollIntoView({
845
+ behavior: "smooth",
846
+ block: "center",
847
+ inline: "nearest"
848
+ });
849
+ }
850
+ }
851
+ }, [clearErrorState, messages.playFailed, notifyActiveChange, pauseOtherVideos, reportError, scrollTo, setNextPlaying]);
852
+ const pauseAction = useCallback(() => {
853
+ const el = videoRef.current;
854
+ if (!el) return;
855
+ el.pause();
856
+ setNextPlaying(false);
857
+ notifyActiveChange(el, "pause");
858
+ }, [notifyActiveChange, setNextPlaying]);
859
+ const seekSetTime = useCallback(
860
+ (value) => {
861
+ const el = videoRef.current;
862
+ if (!el) return;
863
+ const next = clampTime(value, duration);
864
+ setTimeD.current(next);
865
+ if (currentTimeProp === void 0) setCurrentTimeState(next);
866
+ notifyActiveChange(el, "seek");
867
+ },
868
+ [currentTimeProp, duration, notifyActiveChange]
869
+ );
870
+ const seekPrevAction = useCallback(
871
+ (sec = 15) => {
872
+ const el = videoRef.current;
873
+ if (!el) return;
874
+ seekSetTime(el.currentTime - sec);
875
+ },
876
+ [seekSetTime]
877
+ );
878
+ const seekNextAction = useCallback(
879
+ (sec = 15) => {
880
+ const el = videoRef.current;
881
+ if (!el) return;
882
+ seekSetTime(el.currentTime + sec);
883
+ },
884
+ [seekSetTime]
885
+ );
886
+ const toggleAction = useCallback(
887
+ (force) => {
888
+ const el = videoRef.current;
889
+ if (!el) return;
890
+ if (force || el.paused) {
891
+ void playAction();
892
+ } else {
893
+ pauseAction();
894
+ }
895
+ },
896
+ [pauseAction, playAction]
897
+ );
898
+ const nudgeOnStall = useCallback(() => {
899
+ const v = videoRef.current;
900
+ if (!v) return;
901
+ v.currentTime = Math.max(0, v.currentTime + 0.05);
902
+ }, []);
903
+ const shouldSwitchLiveToStability = useCallback(() => {
904
+ if (!live || liveStabilityMode.current) return false;
905
+ const now = Date.now();
906
+ liveStallEvents.current = [...liveStallEvents.current.filter((ts) => now - ts <= LIVE_STALL_WINDOW_MS), now];
907
+ if (liveStallEvents.current.length < LIVE_STALLS_BEFORE_STABILITY) return false;
908
+ liveStabilityMode.current = true;
909
+ liveStallEvents.current = [];
910
+ return true;
911
+ }, [live]);
912
+ const resolvedHlsConfig = useCallback(() => {
913
+ const modeConfig = live ? liveHlsConfig : vodHlsConfig;
914
+ const base = live ? confLiveStability : confVod;
915
+ return withCredentialsConfig(
916
+ {
917
+ ...base,
918
+ ...hlsConfig,
919
+ ...modeConfig
920
+ },
921
+ hlsCredentials
922
+ );
923
+ }, [hlsConfig, hlsCredentials, live, liveHlsConfig, vodHlsConfig]);
924
+ const loadVideo = useCallback(
925
+ async (startAt = 0) => {
926
+ const startVideoRef = videoRef.current;
927
+ if (!startVideoRef) return;
928
+ const HlsCtor = await loadHlsCtor();
929
+ if (!videoRef.current || videoRef.current !== startVideoRef) return;
930
+ const video = videoRef.current;
931
+ if (!video) return;
932
+ const h = new HlsCtor(resolvedHlsConfig());
933
+ hlsRef.current = h;
934
+ h.on(HlsCtor.Events.ERROR, (_evt, data) => {
935
+ var _a, _b;
936
+ const { type: errorType, details, fatal } = data;
937
+ if (details === HlsCtor.ErrorDetails.BUFFER_APPEND_ERROR || details === "bufferAppendingError") return;
938
+ if (!fatal) {
939
+ switch (details) {
940
+ case HlsCtor.ErrorDetails.BUFFER_STALLED_ERROR:
941
+ case HlsCtor.ErrorDetails.BUFFER_NUDGE_ON_STALL:
942
+ if (!stalledNudgeTimer.current) {
943
+ stalledNudgeTimer.current = setTimeout(() => {
944
+ stalledNudgeTimer.current = null;
945
+ nudgeOnStall();
946
+ }, 200);
947
+ }
948
+ if (shouldSwitchLiveToStability()) {
949
+ const resumeTime2 = ((_a = videoRef.current) == null ? void 0 : _a.currentTime) || 0;
950
+ clearTimers();
951
+ reloadTimer.current = setTimeout(() => {
952
+ if (hlsRef.current) {
953
+ hlsRef.current.destroy();
954
+ hlsRef.current = null;
955
+ }
956
+ void loadVideo(resumeTime2);
957
+ }, 250);
958
+ }
959
+ return;
960
+ default:
961
+ return;
962
+ }
963
+ }
964
+ const resumeTime = ((_b = videoRef.current) == null ? void 0 : _b.currentTime) || 0;
965
+ if (errorType === HlsCtor.ErrorTypes.NETWORK_ERROR) {
966
+ if (netRetryCount.current < MAX_NET_RETRIES) {
967
+ netRetryCount.current += 1;
968
+ const backoffMs = 400 * Math.pow(2, netRetryCount.current - 1);
969
+ clearErrorState();
970
+ clearTimers();
971
+ reloadTimer.current = setTimeout(() => {
972
+ if (hlsRef.current) {
973
+ hlsRef.current.destroy();
974
+ hlsRef.current = null;
975
+ }
976
+ void loadVideo(resumeTime);
977
+ }, backoffMs);
978
+ return;
979
+ }
980
+ h.destroy();
981
+ if (hlsRef.current === h) {
982
+ hlsRef.current = null;
983
+ }
984
+ reportError(details, data, messages.streamLoadFailed);
985
+ return;
986
+ }
987
+ if (errorType === HlsCtor.ErrorTypes.MEDIA_ERROR) {
988
+ if (mediaRecoverCount.current === 0) {
989
+ mediaRecoverCount.current = 1;
990
+ clearErrorState();
991
+ h.recoverMediaError();
992
+ return;
993
+ }
994
+ if (mediaRecoverCount.current === 1) {
995
+ mediaRecoverCount.current = 2;
996
+ clearErrorState();
997
+ h.swapAudioCodec();
998
+ h.recoverMediaError();
999
+ return;
1000
+ }
1001
+ }
1002
+ if (fatalReloadCount.current >= MAX_FATAL_RELOADS) {
1003
+ if (hlsRef.current) {
1004
+ hlsRef.current.destroy();
1005
+ hlsRef.current = null;
1006
+ }
1007
+ reportError(
1008
+ details,
1009
+ data,
1010
+ errorType === HlsCtor.ErrorTypes.MEDIA_ERROR ? messages.streamDecodeFailed : messages.streamLoadFailed
1011
+ );
1012
+ return;
1013
+ }
1014
+ fatalReloadCount.current += 1;
1015
+ clearErrorState();
1016
+ if (hlsRef.current) {
1017
+ hlsRef.current.destroy();
1018
+ hlsRef.current = null;
1019
+ }
1020
+ void loadVideo(resumeTime);
1021
+ });
1022
+ h.attachMedia(video);
1023
+ h.on(HlsCtor.Events.MEDIA_ATTACHED, () => {
1024
+ h.loadSource(videoSrc);
1025
+ });
1026
+ h.on(HlsCtor.Events.MANIFEST_PARSED, () => {
1027
+ fatalReloadCount.current = 0;
1028
+ netRetryCount.current = 0;
1029
+ mediaRecoverCount.current = 0;
1030
+ if (!live && startAt > 0) {
1031
+ h.startLoad(startAt);
1032
+ video.currentTime = startAt;
1033
+ } else {
1034
+ h.startLoad();
1035
+ }
1036
+ if (autoPlay) void playAction();
1037
+ });
1038
+ },
1039
+ [
1040
+ autoPlay,
1041
+ clearErrorState,
1042
+ clearTimers,
1043
+ live,
1044
+ messages.streamDecodeFailed,
1045
+ messages.streamLoadFailed,
1046
+ nudgeOnStall,
1047
+ playAction,
1048
+ reportError,
1049
+ resolvedHlsConfig,
1050
+ shouldSwitchLiveToStability,
1051
+ videoSrc
1052
+ ]
1053
+ );
1054
+ const loadVideoNative = useCallback(() => {
1055
+ const video = videoRef.current;
1056
+ if (!video) return;
1057
+ video.src = videoSrc;
1058
+ video.currentTime = initialTime;
1059
+ if (autoPlay) void playAction();
1060
+ }, [autoPlay, initialTime, playAction, videoSrc]);
1061
+ const loadVideoHls = useCallback(async () => {
1062
+ const video = videoRef.current;
1063
+ const canNativeHls = !!(video == null ? void 0 : video.canPlayType) && video.canPlayType("application/vnd.apple.mpegurl") !== "";
1064
+ const HlsCtor = await loadHlsCtor();
1065
+ if (!videoRef.current || videoRef.current !== video) return;
1066
+ if (HlsCtor.isSupported()) {
1067
+ void loadVideo();
1068
+ return;
1069
+ }
1070
+ if (canNativeHls) {
1071
+ loadVideoNative();
1072
+ return;
1073
+ }
1074
+ void loadVideo();
1075
+ }, [loadVideo, loadVideoNative]);
1076
+ const destroy = useCallback(() => {
1077
+ clearTimers();
1078
+ if (hlsRef.current) {
1079
+ hlsRef.current.destroy();
1080
+ hlsRef.current = null;
1081
+ }
1082
+ const v = videoRef.current;
1083
+ if (v) {
1084
+ v.removeEventListener("timeupdate", onTime);
1085
+ v.removeEventListener("durationchange", onDur);
1086
+ v.pause();
1087
+ v.removeAttribute("src");
1088
+ v.load();
1089
+ if (activeExclusiveVideo === v) activeExclusiveVideo = null;
1090
+ }
1091
+ videoRef.current = null;
1092
+ onVideoRefChange == null ? void 0 : onVideoRefChange(null);
1093
+ notifyActiveChange(null, "destroy");
1094
+ setNextPlaying(false);
1095
+ if (durationProp === void 0) setDurationState(0);
1096
+ if (currentTimeProp === void 0) setCurrentTimeState(0);
1097
+ }, [
1098
+ clearTimers,
1099
+ currentTimeProp,
1100
+ durationProp,
1101
+ notifyActiveChange,
1102
+ onDur,
1103
+ onTime,
1104
+ onVideoRefChange,
1105
+ setNextPlaying
1106
+ ]);
1107
+ const videoClickHandler = useCallback(() => {
1108
+ if (!handleClick) return;
1109
+ if (playOnClick) toggleAction();
1110
+ onVideoClick == null ? void 0 : onVideoClick();
1111
+ }, [handleClick, onVideoClick, playOnClick, toggleAction]);
1112
+ const fullScreenAction = useCallback(() => {
1113
+ if (typeof document === "undefined") return;
1114
+ const doc = document;
1115
+ const videoEl = videoRef.current;
1116
+ const containerEl = containerRef.current;
1117
+ const isFs = !!document.fullscreenElement || !!doc.webkitFullscreenElement || !!doc.mozFullScreenElement || !!doc.msFullscreenElement;
1118
+ if (isFs) {
1119
+ if (document.exitFullscreen) void document.exitFullscreen();
1120
+ else if (doc.webkitExitFullscreen) void doc.webkitExitFullscreen();
1121
+ else if (doc.msExitFullscreen) void doc.msExitFullscreen();
1122
+ return;
1123
+ }
1124
+ if (containerEl) {
1125
+ if (containerEl.requestFullscreen) void containerEl.requestFullscreen();
1126
+ else if (containerEl.webkitRequestFullscreen) void containerEl.webkitRequestFullscreen();
1127
+ else if (containerEl.msRequestFullscreen) void containerEl.msRequestFullscreen();
1128
+ else if (videoEl && videoEl.webkitEnterFullscreen) void videoEl.webkitEnterFullscreen();
1129
+ return;
1130
+ }
1131
+ if (videoEl) {
1132
+ if (videoEl.requestFullscreen) void videoEl.requestFullscreen();
1133
+ else if (videoEl.webkitEnterFullscreen) void videoEl.webkitEnterFullscreen();
1134
+ }
1135
+ }, []);
1136
+ const videoDbClickHandler = useCallback(() => {
1137
+ if (!handleClick) return;
1138
+ fullScreenAction();
1139
+ onVideoDoubleClick == null ? void 0 : onVideoDoubleClick();
1140
+ }, [fullScreenAction, handleClick, onVideoDoubleClick]);
1141
+ const showClickHandler = useCallback(() => {
1142
+ setControlVisible((st) => !st);
1143
+ }, []);
1144
+ const retryLoadHandler = useCallback(() => {
1145
+ clearErrorState();
1146
+ setReloadToken((value) => value + 1);
1147
+ }, [clearErrorState]);
1148
+ const videoErrorHandler = useCallback(() => {
1149
+ const video = videoRef.current;
1150
+ const error = video == null ? void 0 : video.error;
1151
+ const src = (video == null ? void 0 : video.currentSrc) || videoSrc;
1152
+ if (hlsRef.current && src.startsWith("blob:")) return;
1153
+ reportError(error, { type: "native", src }, getNativeVideoErrorMessage(error, messages));
1154
+ }, [messages, reportError, videoSrc]);
1155
+ const videoLoadedHandler = useCallback(() => {
1156
+ clearErrorState();
1157
+ }, [clearErrorState]);
1158
+ const mouseMoveHandler = useCallback(() => {
1159
+ if (controlTime.current) clearTimeout(controlTime.current);
1160
+ setControlVisible(true);
1161
+ controlTime.current = setTimeout(() => {
1162
+ setControlVisible(false);
1163
+ }, 1e3 * 10);
1164
+ }, []);
1165
+ useImperativeHandle(
1166
+ ref,
1167
+ () => ({
1168
+ play: playAction,
1169
+ pause: pauseAction,
1170
+ seek: seekSetTime,
1171
+ fullscreen: fullScreenAction,
1172
+ getVideoElement: () => videoRef.current
1173
+ }),
1174
+ [fullScreenAction, pauseAction, playAction, seekSetTime]
1175
+ );
1176
+ useEffect(() => {
1177
+ const el = videoRef.current;
1178
+ if (!el) return;
1179
+ el.muted = muted;
1180
+ }, [muted]);
1181
+ useEffect(() => {
1182
+ if (playingProp === void 0) return;
1183
+ if (!canAttemptPlayback(videoRef.current)) return;
1184
+ if (playingProp) void playAction();
1185
+ else pauseAction();
1186
+ }, [pauseAction, playAction, playingProp]);
1187
+ useEffect(() => {
1188
+ const el = videoRef.current;
1189
+ if (!el || currentTimeProp === void 0) return;
1190
+ if (Math.abs(el.currentTime - currentTimeProp) < 0.25) return;
1191
+ el.currentTime = clampTime(currentTimeProp, duration);
1192
+ }, [currentTimeProp, duration]);
1193
+ useEffect(() => {
1194
+ if (!videoRef.current) return;
1195
+ clearErrorState();
1196
+ fatalReloadCount.current = 0;
1197
+ netRetryCount.current = 0;
1198
+ mediaRecoverCount.current = 0;
1199
+ liveStabilityMode.current = false;
1200
+ liveStallEvents.current = [];
1201
+ if (hlsRef.current) {
1202
+ hlsRef.current.destroy();
1203
+ hlsRef.current = null;
1204
+ }
1205
+ if (sourceType === "native" || type === "video") loadVideoNative();
1206
+ else if (sourceType === "hls") void loadVideo();
1207
+ else void loadVideoHls();
1208
+ return () => {
1209
+ clearTimers();
1210
+ if (hlsRef.current) {
1211
+ hlsRef.current.destroy();
1212
+ hlsRef.current = null;
1213
+ }
1214
+ };
1215
+ }, [
1216
+ clearErrorState,
1217
+ clearTimers,
1218
+ loadVideo,
1219
+ loadVideoHls,
1220
+ loadVideoNative,
1221
+ reloadKey,
1222
+ reloadToken,
1223
+ sourceType,
1224
+ type,
1225
+ videoSrc
1226
+ ]);
1227
+ useEffect(() => {
1228
+ if (!autoPlay) return;
1229
+ if (!canAttemptPlayback(videoRef.current)) return;
1230
+ void playAction();
1231
+ }, [autoPlay, playAction]);
1232
+ useEffect(() => {
1233
+ if (typeof document === "undefined") return;
1234
+ const doc = document;
1235
+ const handleFsChange = () => {
1236
+ const fs = !!document.fullscreenElement || !!doc.webkitFullscreenElement || !!doc.mozFullScreenElement || !!doc.msFullscreenElement;
1237
+ setIsFullscreen(fs);
1238
+ };
1239
+ document.addEventListener("fullscreenchange", handleFsChange);
1240
+ document.addEventListener("webkitfullscreenchange", handleFsChange);
1241
+ document.addEventListener("mozfullscreenchange", handleFsChange);
1242
+ document.addEventListener("MSFullscreenChange", handleFsChange);
1243
+ return () => {
1244
+ document.removeEventListener("fullscreenchange", handleFsChange);
1245
+ document.removeEventListener("webkitfullscreenchange", handleFsChange);
1246
+ document.removeEventListener("mozfullscreenchange", handleFsChange);
1247
+ document.removeEventListener("MSFullscreenChange", handleFsChange);
1248
+ };
1249
+ }, []);
1250
+ useEffect(() => {
1251
+ return () => {
1252
+ setTimeD.current.cancel();
1253
+ destroy();
1254
+ };
1255
+ }, [destroy]);
1256
+ return /* @__PURE__ */ jsxs2(
1257
+ VideoPlayerStyles,
1258
+ {
1259
+ ref: containerRef,
1260
+ onMouseMove: mouseMoveHandler,
1261
+ className: "VideoPlayer",
1262
+ type,
1263
+ $isMobile: isMobile,
1264
+ $mobileType: mobileType,
1265
+ $showControl: showControl,
1266
+ $alwaysShowSegments: alwaysShowSegments,
1267
+ onPointerDown,
1268
+ onPointerMove,
1269
+ onPointerUp,
1270
+ onPointerCancel: onPointerCancel || onPointerUp,
1271
+ $color: accentColor,
1272
+ children: [
1273
+ VideoTopBar && /* @__PURE__ */ jsx2("div", { className: "video-top-bar", children: VideoTopBar }),
1274
+ isFullscreen && VideoRightBar && !isMobile && /* @__PURE__ */ jsxs2("div", { className: "video-right-bar", children: [
1275
+ /* @__PURE__ */ jsx2("div", { onClick: showClickHandler, className: "video-right-bar-toggle", children: "<" }),
1276
+ /* @__PURE__ */ jsx2("div", { className: "video-right-bar-content", children: VideoRightBar })
1277
+ ] }),
1278
+ !isFullscreen && VideoRightBar && /* @__PURE__ */ jsxs2("div", { className: "video-right-bar", children: [
1279
+ /* @__PURE__ */ jsx2("div", { onClick: showClickHandler, className: "video-right-bar-toggle", children: "<" }),
1280
+ /* @__PURE__ */ jsx2("div", { className: "video-right-bar-content", children: VideoRightBar })
1281
+ ] }),
1282
+ /* @__PURE__ */ jsxs2("div", { className: "video-wrapper", children: [
1283
+ /* @__PURE__ */ jsx2(
1284
+ "video",
1285
+ {
1286
+ ...videoProps,
1287
+ ref: setVideoEl,
1288
+ crossOrigin,
1289
+ playsInline: true,
1290
+ muted,
1291
+ preload,
1292
+ loop,
1293
+ onPlay: () => setNextPlaying(true),
1294
+ onPause: () => setNextPlaying(false),
1295
+ onEnded: () => setNextPlaying(false),
1296
+ onLoadedData: videoLoadedHandler,
1297
+ onCanPlay: videoLoadedHandler,
1298
+ onError: videoErrorHandler,
1299
+ onClick: videoClickHandler,
1300
+ onDoubleClick: videoDbClickHandler,
1301
+ poster
1302
+ }
1303
+ ),
1304
+ errorState && /* @__PURE__ */ jsx2("div", { className: "video-error-overlay", children: /* @__PURE__ */ jsxs2("div", { className: "video-error-content", children: [
1305
+ /* @__PURE__ */ jsx2("div", { className: "video-error-title", children: errorState.message }),
1306
+ errorState.details.length > 0 && /* @__PURE__ */ jsx2("div", { className: "video-error-details", children: errorState.details.map((detail) => /* @__PURE__ */ jsx2("div", { className: "video-error-detail", children: detail }, detail)) }),
1307
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: retryLoadHandler, className: "video-error-retry", children: messages.retry })
1308
+ ] }) })
1309
+ ] }),
1310
+ /* @__PURE__ */ jsx2(
1311
+ VideoPlayerControls,
1312
+ {
1313
+ variant: controlsVariant,
1314
+ fullScreenAction,
1315
+ showControl,
1316
+ videoRef,
1317
+ playAction,
1318
+ pauseAction,
1319
+ seekSetTime,
1320
+ seekPrevAction,
1321
+ seekNextAction,
1322
+ playing,
1323
+ muted,
1324
+ onMutedChange: setNextMuted,
1325
+ duration,
1326
+ currentTime,
1327
+ title
1328
+ }
1329
+ )
1330
+ ]
1331
+ }
1332
+ );
1333
+ };
1334
+ var VideoPlayer = memo(forwardRef(VideoPlayerBase));
1335
+ var VideoPlayer_default = VideoPlayer;
1336
+ export {
1337
+ VideoPlayer,
1338
+ VideoPlayer_default as default,
1339
+ hasNativeHlsSupport
1340
+ };