@livepeer-frameworks/player-react 0.1.1 → 0.1.2

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 (158) hide show
  1. package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js +359 -0
  2. package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
  3. package/dist/cjs/assets/logomark.svg.js +8 -0
  4. package/dist/cjs/assets/logomark.svg.js.map +1 -0
  5. package/dist/cjs/components/DevModePanel.js +826 -0
  6. package/dist/cjs/components/DevModePanel.js.map +1 -0
  7. package/dist/cjs/components/DvdLogo.js +200 -0
  8. package/dist/cjs/components/DvdLogo.js.map +1 -0
  9. package/dist/cjs/components/Icons.js +439 -0
  10. package/dist/cjs/components/Icons.js.map +1 -0
  11. package/dist/cjs/components/IdleScreen.js +587 -0
  12. package/dist/cjs/components/IdleScreen.js.map +1 -0
  13. package/dist/cjs/components/LoadingScreen.js +523 -0
  14. package/dist/cjs/components/LoadingScreen.js.map +1 -0
  15. package/dist/cjs/components/Player.js +420 -0
  16. package/dist/cjs/components/Player.js.map +1 -0
  17. package/dist/cjs/components/PlayerControls.js +798 -0
  18. package/dist/cjs/components/PlayerControls.js.map +1 -0
  19. package/dist/cjs/components/PlayerErrorBoundary.js +80 -0
  20. package/dist/cjs/components/PlayerErrorBoundary.js.map +1 -0
  21. package/dist/cjs/components/SeekBar.js +253 -0
  22. package/dist/cjs/components/SeekBar.js.map +1 -0
  23. package/dist/cjs/components/SkipIndicator.js +92 -0
  24. package/dist/cjs/components/SkipIndicator.js.map +1 -0
  25. package/dist/cjs/components/SpeedIndicator.js +43 -0
  26. package/dist/cjs/components/SpeedIndicator.js.map +1 -0
  27. package/dist/cjs/components/StatsPanel.js +202 -0
  28. package/dist/cjs/components/StatsPanel.js.map +1 -0
  29. package/dist/cjs/components/StreamStateOverlay.js +229 -0
  30. package/dist/cjs/components/StreamStateOverlay.js.map +1 -0
  31. package/dist/cjs/components/ThumbnailOverlay.js +86 -0
  32. package/dist/cjs/components/ThumbnailOverlay.js.map +1 -0
  33. package/dist/cjs/components/TitleOverlay.js +32 -0
  34. package/dist/cjs/components/TitleOverlay.js.map +1 -0
  35. package/dist/cjs/context/PlayerContext.js +46 -0
  36. package/dist/cjs/context/PlayerContext.js.map +1 -0
  37. package/dist/cjs/hooks/useMetaTrack.js +165 -0
  38. package/dist/cjs/hooks/useMetaTrack.js.map +1 -0
  39. package/dist/cjs/hooks/usePlaybackQuality.js +131 -0
  40. package/dist/cjs/hooks/usePlaybackQuality.js.map +1 -0
  41. package/dist/cjs/hooks/usePlayerController.js +518 -0
  42. package/dist/cjs/hooks/usePlayerController.js.map +1 -0
  43. package/dist/cjs/hooks/usePlayerSelection.js +90 -0
  44. package/dist/cjs/hooks/usePlayerSelection.js.map +1 -0
  45. package/dist/cjs/hooks/useStreamState.js +360 -0
  46. package/dist/cjs/hooks/useStreamState.js.map +1 -0
  47. package/dist/cjs/hooks/useTelemetry.js +120 -0
  48. package/dist/cjs/hooks/useTelemetry.js.map +1 -0
  49. package/dist/cjs/hooks/useViewerEndpoints.js +222 -0
  50. package/dist/cjs/hooks/useViewerEndpoints.js.map +1 -0
  51. package/dist/cjs/index.js +97 -1
  52. package/dist/cjs/index.js.map +1 -1
  53. package/dist/cjs/ui/badge.js +34 -0
  54. package/dist/cjs/ui/badge.js.map +1 -0
  55. package/dist/cjs/ui/button.js +74 -0
  56. package/dist/cjs/ui/button.js.map +1 -0
  57. package/dist/cjs/ui/context-menu.js +163 -0
  58. package/dist/cjs/ui/context-menu.js.map +1 -0
  59. package/dist/cjs/ui/slider.js +60 -0
  60. package/dist/cjs/ui/slider.js.map +1 -0
  61. package/dist/esm/_virtual/_rollupPluginBabelHelpers.js +329 -0
  62. package/dist/esm/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
  63. package/dist/esm/assets/logomark.svg.js +4 -0
  64. package/dist/esm/assets/logomark.svg.js.map +1 -0
  65. package/dist/esm/components/DevModePanel.js +822 -0
  66. package/dist/esm/components/DevModePanel.js.map +1 -0
  67. package/dist/esm/components/DvdLogo.js +196 -0
  68. package/dist/esm/components/DvdLogo.js.map +1 -0
  69. package/dist/esm/components/Icons.js +421 -0
  70. package/dist/esm/components/Icons.js.map +1 -0
  71. package/dist/esm/components/IdleScreen.js +582 -0
  72. package/dist/esm/components/IdleScreen.js.map +1 -0
  73. package/dist/esm/components/LoadingScreen.js +519 -0
  74. package/dist/esm/components/LoadingScreen.js.map +1 -0
  75. package/dist/esm/components/Player.js +416 -0
  76. package/dist/esm/components/Player.js.map +1 -0
  77. package/dist/esm/components/PlayerControls.js +794 -0
  78. package/dist/esm/components/PlayerControls.js.map +1 -0
  79. package/dist/esm/components/PlayerErrorBoundary.js +76 -0
  80. package/dist/esm/components/PlayerErrorBoundary.js.map +1 -0
  81. package/dist/esm/components/SeekBar.js +249 -0
  82. package/dist/esm/components/SeekBar.js.map +1 -0
  83. package/dist/esm/components/SkipIndicator.js +88 -0
  84. package/dist/esm/components/SkipIndicator.js.map +1 -0
  85. package/dist/esm/components/SpeedIndicator.js +39 -0
  86. package/dist/esm/components/SpeedIndicator.js.map +1 -0
  87. package/dist/esm/components/StatsPanel.js +198 -0
  88. package/dist/esm/components/StatsPanel.js.map +1 -0
  89. package/dist/esm/components/StreamStateOverlay.js +224 -0
  90. package/dist/esm/components/StreamStateOverlay.js.map +1 -0
  91. package/dist/esm/components/ThumbnailOverlay.js +82 -0
  92. package/dist/esm/components/ThumbnailOverlay.js.map +1 -0
  93. package/dist/esm/components/TitleOverlay.js +28 -0
  94. package/dist/esm/components/TitleOverlay.js.map +1 -0
  95. package/dist/esm/context/PlayerContext.js +41 -0
  96. package/dist/esm/context/PlayerContext.js.map +1 -0
  97. package/dist/esm/hooks/useMetaTrack.js +163 -0
  98. package/dist/esm/hooks/useMetaTrack.js.map +1 -0
  99. package/dist/esm/hooks/usePlaybackQuality.js +129 -0
  100. package/dist/esm/hooks/usePlaybackQuality.js.map +1 -0
  101. package/dist/esm/hooks/usePlayerController.js +516 -0
  102. package/dist/esm/hooks/usePlayerController.js.map +1 -0
  103. package/dist/esm/hooks/usePlayerSelection.js +88 -0
  104. package/dist/esm/hooks/usePlayerSelection.js.map +1 -0
  105. package/dist/esm/hooks/useStreamState.js +358 -0
  106. package/dist/esm/hooks/useStreamState.js.map +1 -0
  107. package/dist/esm/hooks/useTelemetry.js +118 -0
  108. package/dist/esm/hooks/useTelemetry.js.map +1 -0
  109. package/dist/esm/hooks/useViewerEndpoints.js +220 -0
  110. package/dist/esm/hooks/useViewerEndpoints.js.map +1 -0
  111. package/dist/esm/index.js +23 -1
  112. package/dist/esm/index.js.map +1 -1
  113. package/dist/esm/ui/badge.js +31 -0
  114. package/dist/esm/ui/badge.js.map +1 -0
  115. package/dist/esm/ui/button.js +52 -0
  116. package/dist/esm/ui/button.js.map +1 -0
  117. package/dist/esm/ui/context-menu.js +132 -0
  118. package/dist/esm/ui/context-menu.js.map +1 -0
  119. package/dist/esm/ui/slider.js +38 -0
  120. package/dist/esm/ui/slider.js.map +1 -0
  121. package/dist/types/components/DvdLogo.d.ts +1 -1
  122. package/dist/types/components/Icons.d.ts +1 -1
  123. package/dist/types/components/Player.d.ts +1 -1
  124. package/dist/types/components/PlayerErrorBoundary.d.ts +2 -1
  125. package/dist/types/components/StreamStateOverlay.d.ts +2 -2
  126. package/dist/types/components/SubtitleRenderer.d.ts +2 -2
  127. package/dist/types/context/PlayerContext.d.ts +2 -2
  128. package/dist/types/context/index.d.ts +2 -2
  129. package/dist/types/hooks/useMetaTrack.d.ts +3 -3
  130. package/dist/types/hooks/usePlaybackQuality.d.ts +2 -2
  131. package/dist/types/hooks/usePlayerController.d.ts +26 -3
  132. package/dist/types/hooks/usePlayerSelection.d.ts +1 -1
  133. package/dist/types/hooks/useStreamState.d.ts +1 -1
  134. package/dist/types/hooks/useTelemetry.d.ts +1 -1
  135. package/dist/types/hooks/useViewerEndpoints.d.ts +3 -3
  136. package/dist/types/index.d.ts +28 -28
  137. package/dist/types/types.d.ts +3 -3
  138. package/dist/types/ui/select.d.ts +1 -1
  139. package/package.json +22 -14
  140. package/src/components/DvdLogo.tsx +1 -1
  141. package/src/components/Player.tsx +42 -3
  142. package/src/components/PlayerControls.tsx +36 -17
  143. package/src/components/PlayerErrorBoundary.tsx +2 -1
  144. package/src/hooks/usePlayerController.ts +61 -1
  145. package/dist/types/components/players/DashJsPlayer.d.ts +0 -18
  146. package/dist/types/components/players/HlsJsPlayer.d.ts +0 -18
  147. package/dist/types/components/players/MewsWsPlayer/index.d.ts +0 -18
  148. package/dist/types/components/players/MistPlayer.d.ts +0 -20
  149. package/dist/types/components/players/MistWebRTCPlayer/index.d.ts +0 -20
  150. package/dist/types/components/players/NativePlayer.d.ts +0 -19
  151. package/dist/types/components/players/VideoJsPlayer.d.ts +0 -18
  152. package/src/components/players/DashJsPlayer.tsx +0 -58
  153. package/src/components/players/HlsJsPlayer.tsx +0 -58
  154. package/src/components/players/MewsWsPlayer/index.tsx +0 -58
  155. package/src/components/players/MistPlayer.tsx +0 -62
  156. package/src/components/players/MistWebRTCPlayer/index.tsx +0 -68
  157. package/src/components/players/NativePlayer.tsx +0 -56
  158. package/src/components/players/VideoJsPlayer.tsx +0 -58
@@ -0,0 +1,794 @@
1
+ import { slicedToArray as _slicedToArray, toConsumableArray as _toConsumableArray } from '../_virtual/_rollupPluginBabelHelpers.js';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
4
+ import { usePlayerContextOptional } from '../context/PlayerContext.js';
5
+ import { isLiveContent, isMediaStreamSource, supportsPlaybackRate, calculateSeekableRange, calculateLiveThresholds, canSeekStream, calculateIsNearLive, formatTimeDisplay, cn, SPEED_PRESETS } from '@livepeer-frameworks/player-core';
6
+ import { Slider } from '../ui/slider.js';
7
+ import SeekBar from './SeekBar.js';
8
+ import { PlayPauseIcon, SkipBackIcon, SkipForwardIcon, VolumeIcon, SeekToLiveIcon, SettingsIcon, FullscreenToggleIcon } from './Icons.js';
9
+
10
+ var PlayerControls = function PlayerControls(_ref) {
11
+ var _ctx$state, _mistStreamInfo$meta, _player$getTextTracks, _player$getTextTracks2, _mistStreamInfo$meta$, _mistStreamInfo$meta2, _mistStreamInfo$meta3, _player$getSeekableSt, _player$getLiveEdge;
12
+ var currentTime = _ref.currentTime,
13
+ duration = _ref.duration,
14
+ _ref$isVisible = _ref.isVisible,
15
+ isVisible = _ref$isVisible === void 0 ? true : _ref$isVisible;
16
+ _ref.className;
17
+ var _onSeek = _ref.onSeek,
18
+ mistStreamInfo = _ref.mistStreamInfo,
19
+ _ref$disabled = _ref.disabled,
20
+ disabled = _ref$disabled === void 0 ? false : _ref$disabled,
21
+ _ref$playbackMode = _ref.playbackMode,
22
+ playbackMode = _ref$playbackMode === void 0 ? "auto" : _ref$playbackMode,
23
+ onModeChange = _ref.onModeChange,
24
+ sourceType = _ref.sourceType,
25
+ isContentLive = _ref.isContentLive,
26
+ propVideoElement = _ref.videoElement,
27
+ _ref$qualities = _ref.qualities,
28
+ propQualities = _ref$qualities === void 0 ? [] : _ref$qualities,
29
+ onSelectQuality = _ref.onSelectQuality,
30
+ propIsMuted = _ref.isMuted,
31
+ propVolume = _ref.volume,
32
+ onVolumeChange = _ref.onVolumeChange,
33
+ onToggleMute = _ref.onToggleMute,
34
+ propIsPlaying = _ref.isPlaying,
35
+ onTogglePlay = _ref.onTogglePlay,
36
+ onToggleFullscreen = _ref.onToggleFullscreen,
37
+ propIsFullscreen = _ref.isFullscreen;
38
+ _ref.isLoopEnabled;
39
+ _ref.onToggleLoop;
40
+ var onJumpToLive = _ref.onJumpToLive;
41
+ // Context fallback - prefer props passed from parent over context
42
+ // Context provides UsePlayerControllerReturn which has state.videoElement and controller
43
+ var ctx = usePlayerContextOptional();
44
+ var contextVideo = ctx === null || ctx === void 0 || (_ctx$state = ctx.state) === null || _ctx$state === void 0 ? void 0 : _ctx$state.videoElement;
45
+ var player = ctx === null || ctx === void 0 ? void 0 : ctx.controller;
46
+ // Robust video element detection - prefer prop, then context, then DOM query
47
+ var _useState = useState(null),
48
+ _useState2 = _slicedToArray(_useState, 2),
49
+ video = _useState2[0],
50
+ setVideo = _useState2[1];
51
+ var videoCheckIntervalRef = useRef(null);
52
+ var findVideoElement = useCallback(function () {
53
+ var _player$getVideoEleme, _ref2, _document$querySelect;
54
+ if (propVideoElement) return propVideoElement;
55
+ if (contextVideo) return contextVideo;
56
+ if (player !== null && player !== void 0 && (_player$getVideoEleme = player.getVideoElement) !== null && _player$getVideoEleme !== void 0 && _player$getVideoEleme.call(player)) return player.getVideoElement();
57
+ var domVideo = (_ref2 = (_document$querySelect = document.querySelector(".fw-player-video")) !== null && _document$querySelect !== void 0 ? _document$querySelect : document.querySelector('[data-player-container="true"] video')) !== null && _ref2 !== void 0 ? _ref2 : document.querySelector(".fw-player-container video");
58
+ return domVideo;
59
+ }, [propVideoElement, contextVideo, player]);
60
+ useEffect(function () {
61
+ var updateVideo = function updateVideo() {
62
+ var v = findVideoElement();
63
+ if (v && v !== video) {
64
+ setVideo(v);
65
+ }
66
+ };
67
+ updateVideo();
68
+ if (!video) {
69
+ videoCheckIntervalRef.current = setInterval(function () {
70
+ var v = findVideoElement();
71
+ if (v) {
72
+ setVideo(v);
73
+ if (videoCheckIntervalRef.current) {
74
+ clearInterval(videoCheckIntervalRef.current);
75
+ videoCheckIntervalRef.current = null;
76
+ }
77
+ }
78
+ }, 100);
79
+ setTimeout(function () {
80
+ if (videoCheckIntervalRef.current) {
81
+ clearInterval(videoCheckIntervalRef.current);
82
+ videoCheckIntervalRef.current = null;
83
+ }
84
+ }, 5000);
85
+ }
86
+ return function () {
87
+ if (videoCheckIntervalRef.current) {
88
+ clearInterval(videoCheckIntervalRef.current);
89
+ videoCheckIntervalRef.current = null;
90
+ }
91
+ };
92
+ }, [contextVideo, player, findVideoElement, video]);
93
+ var mistTracks = mistStreamInfo === null || mistStreamInfo === void 0 || (_mistStreamInfo$meta = mistStreamInfo.meta) === null || _mistStreamInfo$meta === void 0 ? void 0 : _mistStreamInfo$meta.tracks;
94
+ // Quality selection priority:
95
+ // 1. Player-provided qualities (HLS.js/DASH.js levels with correct numeric indices)
96
+ // 2. Mist track metadata (for players that don't provide quality API)
97
+ // This fixes a critical bug where Mist track IDs (e.g., "a1", "v0") were passed to
98
+ // HLS/DASH players which expect numeric indices (e.g., "0", "1", "2")
99
+ // Quality levels - prefer props, then player API, then Mist tracks
100
+ var qualities = useMemo(function () {
101
+ var _player$getQualities;
102
+ // Priority 1: Props from parent (usePlayerController hook)
103
+ if (propQualities && propQualities.length > 0) {
104
+ return propQualities;
105
+ }
106
+ // Priority 2: Player's quality API
107
+ var playerQualities = player === null || player === void 0 || (_player$getQualities = player.getQualities) === null || _player$getQualities === void 0 ? void 0 : _player$getQualities.call(player);
108
+ if (playerQualities && playerQualities.length > 0) {
109
+ return playerQualities;
110
+ }
111
+ // Fallback to Mist track metadata for players without quality API
112
+ if (mistTracks) {
113
+ return Object.entries(mistTracks).filter(function (_ref3) {
114
+ var _ref4 = _slicedToArray(_ref3, 2),
115
+ t = _ref4[1];
116
+ return t.type === "video";
117
+ }).map(function (_ref5) {
118
+ var _ref6 = _slicedToArray(_ref5, 2),
119
+ id = _ref6[0],
120
+ t = _ref6[1];
121
+ return {
122
+ id: id,
123
+ label: t.height ? "".concat(t.height, "p") : t.codec,
124
+ width: t.width,
125
+ height: t.height,
126
+ bitrate: t.bps
127
+ };
128
+ }).sort(function (a, b) {
129
+ return (b.height || 0) - (a.height || 0);
130
+ });
131
+ }
132
+ return [];
133
+ }, [propQualities, player, mistTracks]);
134
+ var textTracks = (_player$getTextTracks = player === null || player === void 0 || (_player$getTextTracks2 = player.getTextTracks) === null || _player$getTextTracks2 === void 0 ? void 0 : _player$getTextTracks2.call(player)) !== null && _player$getTextTracks !== void 0 ? _player$getTextTracks : [];
135
+ // Internal state - used as fallback when props not provided
136
+ var _useState3 = useState(false),
137
+ _useState4 = _slicedToArray(_useState3, 2),
138
+ internalIsPlaying = _useState4[0],
139
+ setInternalIsPlaying = _useState4[1];
140
+ var _useState5 = useState(false),
141
+ _useState6 = _slicedToArray(_useState5, 2),
142
+ internalIsMuted = _useState6[0],
143
+ setInternalIsMuted = _useState6[1];
144
+ var _useState7 = useState(false),
145
+ _useState8 = _slicedToArray(_useState7, 2),
146
+ internalIsFullscreen = _useState8[0],
147
+ setInternalIsFullscreen = _useState8[1];
148
+ var _useState9 = useState(true),
149
+ _useState0 = _slicedToArray(_useState9, 2),
150
+ hasAudio = _useState0[0],
151
+ setHasAudio = _useState0[1];
152
+ var _useState1 = useState(undefined),
153
+ _useState10 = _slicedToArray(_useState1, 2),
154
+ buffered = _useState10[0],
155
+ setBuffered = _useState10[1];
156
+ var _useState11 = useState(function () {
157
+ if (!video) return 100;
158
+ return Math.round(video.volume * 100);
159
+ }),
160
+ _useState12 = _slicedToArray(_useState11, 2),
161
+ internalVolume = _useState12[0],
162
+ setInternalVolume = _useState12[1];
163
+ var _useState13 = useState(function () {
164
+ var _video$playbackRate;
165
+ return (_video$playbackRate = video === null || video === void 0 ? void 0 : video.playbackRate) !== null && _video$playbackRate !== void 0 ? _video$playbackRate : 1;
166
+ }),
167
+ _useState14 = _slicedToArray(_useState13, 2),
168
+ playbackRate = _useState14[0],
169
+ setPlaybackRate = _useState14[1];
170
+ // Derived state - prefer props over internal state
171
+ var isPlaying = propIsPlaying !== null && propIsPlaying !== void 0 ? propIsPlaying : internalIsPlaying;
172
+ var isMuted = propIsMuted !== null && propIsMuted !== void 0 ? propIsMuted : internalIsMuted;
173
+ var isFullscreen = propIsFullscreen !== null && propIsFullscreen !== void 0 ? propIsFullscreen : internalIsFullscreen;
174
+ var actualVolume = propVolume !== undefined ? Math.round(propVolume * 100) : internalVolume;
175
+ // Show 0 when muted, actual volume otherwise
176
+ var volumeValue = isMuted ? 0 : actualVolume;
177
+ var _useState15 = useState("auto"),
178
+ _useState16 = _slicedToArray(_useState15, 2),
179
+ qualityValue = _useState16[0],
180
+ setQualityValue = _useState16[1];
181
+ var _useState17 = useState("none"),
182
+ _useState18 = _slicedToArray(_useState17, 2),
183
+ captionValue = _useState18[0],
184
+ setCaptionValue = _useState18[1];
185
+ var _useState19 = useState(false),
186
+ _useState20 = _slicedToArray(_useState19, 2),
187
+ isSettingsOpen = _useState20[0],
188
+ setIsSettingsOpen = _useState20[1];
189
+ // Hysteresis state for Live badge - prevents flip-flopping
190
+ var _useState21 = useState(true),
191
+ _useState22 = _slicedToArray(_useState21, 2),
192
+ isNearLiveState = _useState22[0],
193
+ setIsNearLiveState = _useState22[1];
194
+ // Close settings menu when clicking outside
195
+ useEffect(function () {
196
+ if (!isSettingsOpen) return;
197
+ var handleWindowClick = function handleWindowClick(event) {
198
+ var target = event.target;
199
+ if (target && !target.closest(".fw-settings-menu")) {
200
+ setIsSettingsOpen(false);
201
+ }
202
+ };
203
+ // Use setTimeout to avoid immediate close from the same click that opened it
204
+ var timeoutId = setTimeout(function () {
205
+ window.addEventListener("click", handleWindowClick);
206
+ }, 0);
207
+ return function () {
208
+ clearTimeout(timeoutId);
209
+ window.removeEventListener("click", handleWindowClick);
210
+ };
211
+ }, [isSettingsOpen]);
212
+ // Core utility-based calculations
213
+ var deriveBufferWindowMs = useCallback(function (tracks) {
214
+ if (!tracks) return undefined;
215
+ var list = Object.values(tracks);
216
+ if (list.length === 0) return undefined;
217
+ var firstmsValues = list.map(function (t) {
218
+ return t.firstms;
219
+ }).filter(function (v) {
220
+ return v !== undefined;
221
+ });
222
+ var lastmsValues = list.map(function (t) {
223
+ return t.lastms;
224
+ }).filter(function (v) {
225
+ return v !== undefined;
226
+ });
227
+ if (firstmsValues.length === 0 || lastmsValues.length === 0) return undefined;
228
+ var firstms = Math.max.apply(Math, _toConsumableArray(firstmsValues));
229
+ var lastms = Math.min.apply(Math, _toConsumableArray(lastmsValues));
230
+ var window = lastms - firstms;
231
+ if (!Number.isFinite(window) || window <= 0) return undefined;
232
+ return window;
233
+ }, []);
234
+ var bufferWindowMs = (_mistStreamInfo$meta$ = mistStreamInfo === null || mistStreamInfo === void 0 || (_mistStreamInfo$meta2 = mistStreamInfo.meta) === null || _mistStreamInfo$meta2 === void 0 ? void 0 : _mistStreamInfo$meta2.buffer_window) !== null && _mistStreamInfo$meta$ !== void 0 ? _mistStreamInfo$meta$ : deriveBufferWindowMs(mistStreamInfo === null || mistStreamInfo === void 0 || (_mistStreamInfo$meta3 = mistStreamInfo.meta) === null || _mistStreamInfo$meta3 === void 0 ? void 0 : _mistStreamInfo$meta3.tracks);
235
+ var isLive = useMemo(function () {
236
+ return isLiveContent(isContentLive, mistStreamInfo, duration);
237
+ }, [isContentLive, mistStreamInfo, duration]);
238
+ var isWebRTC = useMemo(function () {
239
+ return isMediaStreamSource(video);
240
+ }, [video]);
241
+ var supportsPlaybackRate$1 = useMemo(function () {
242
+ return supportsPlaybackRate(video);
243
+ }, [video]);
244
+ // Seekable range using core calculation (allow controller override)
245
+ var allowMediaStreamDvr = isMediaStreamSource(video) && bufferWindowMs !== undefined && bufferWindowMs > 0 && sourceType !== "whep" && sourceType !== "webrtc";
246
+ var _useMemo = useMemo(function () {
247
+ return calculateSeekableRange({
248
+ isLive: isLive,
249
+ video: video,
250
+ mistStreamInfo: mistStreamInfo,
251
+ currentTime: currentTime,
252
+ duration: duration,
253
+ allowMediaStreamDvr: allowMediaStreamDvr
254
+ });
255
+ }, [isLive, video, mistStreamInfo, currentTime, duration, allowMediaStreamDvr]),
256
+ calcSeekableStart = _useMemo.seekableStart,
257
+ calcLiveEdge = _useMemo.liveEdge;
258
+ var controllerSeekableStart = player === null || player === void 0 || (_player$getSeekableSt = player.getSeekableStart) === null || _player$getSeekableSt === void 0 ? void 0 : _player$getSeekableSt.call(player);
259
+ var controllerLiveEdge = player === null || player === void 0 || (_player$getLiveEdge = player.getLiveEdge) === null || _player$getLiveEdge === void 0 ? void 0 : _player$getLiveEdge.call(player);
260
+ var useControllerRange = Number.isFinite(controllerSeekableStart) && Number.isFinite(controllerLiveEdge) && controllerLiveEdge >= controllerSeekableStart && (controllerLiveEdge > 0 || controllerSeekableStart > 0);
261
+ var seekableStart = useControllerRange ? controllerSeekableStart : calcSeekableStart;
262
+ var liveEdge = useControllerRange ? controllerLiveEdge : calcLiveEdge;
263
+ var hasDvrWindow = isLive && Number.isFinite(liveEdge) && Number.isFinite(seekableStart) && liveEdge > seekableStart;
264
+ var commitOnRelease = isLive;
265
+ // Live thresholds with buffer window scaling
266
+ var liveThresholds = useMemo(function () {
267
+ return calculateLiveThresholds(sourceType, isWebRTC, bufferWindowMs);
268
+ }, [sourceType, isWebRTC, bufferWindowMs]);
269
+ // Can seek - prefer PlayerController's computed value (includes player-specific canSeek)
270
+ // Fall back to utility function when controller not available
271
+ var baseCanSeek = useMemo(function () {
272
+ // PlayerController already computes canSeek with player-specific logic
273
+ if (player && typeof player.canSeekStream === "function") {
274
+ return player.canSeekStream();
275
+ }
276
+ // Fallback when no controller
277
+ return canSeekStream({
278
+ video: video,
279
+ isLive: isLive,
280
+ duration: duration,
281
+ bufferWindowMs: bufferWindowMs
282
+ });
283
+ }, [video, isLive, duration, bufferWindowMs, player]);
284
+ var canSeek = baseCanSeek && (!isLive || hasDvrWindow);
285
+ // Hysteresis for live badge - using core calculation
286
+ useEffect(function () {
287
+ if (!isLive) {
288
+ setIsNearLiveState(true);
289
+ return;
290
+ }
291
+ var newState = calculateIsNearLive(currentTime, liveEdge, liveThresholds, isNearLiveState);
292
+ if (newState !== isNearLiveState) {
293
+ setIsNearLiveState(newState);
294
+ }
295
+ }, [isLive, liveEdge, currentTime, liveThresholds, isNearLiveState]);
296
+ // Track if we've already seeked to live on initial playback
297
+ var hasSeekToLiveRef = useRef(false);
298
+ // Sync internal state from video element (only when props not provided)
299
+ useEffect(function () {
300
+ if (!video) return;
301
+ var updatePlayingState = function updatePlayingState() {
302
+ return setInternalIsPlaying(!video.paused);
303
+ };
304
+ var updateMutedState = function updateMutedState() {
305
+ var muted = video.muted || video.volume === 0;
306
+ setInternalIsMuted(muted);
307
+ setInternalVolume(Math.round(video.volume * 100));
308
+ };
309
+ var updateFullscreenState = function updateFullscreenState() {
310
+ if (typeof document !== "undefined") setInternalIsFullscreen(!!document.fullscreenElement);
311
+ };
312
+ var updatePlaybackRate = function updatePlaybackRate() {
313
+ return setPlaybackRate(video.playbackRate);
314
+ };
315
+ updatePlayingState();
316
+ updateMutedState();
317
+ updateFullscreenState();
318
+ updatePlaybackRate();
319
+ video.addEventListener("play", updatePlayingState);
320
+ video.addEventListener("pause", updatePlayingState);
321
+ video.addEventListener("playing", updatePlayingState);
322
+ video.addEventListener("volumechange", updateMutedState);
323
+ video.addEventListener("ratechange", updatePlaybackRate);
324
+ if (typeof document !== "undefined") document.addEventListener("fullscreenchange", updateFullscreenState);
325
+ return function () {
326
+ video.removeEventListener("play", updatePlayingState);
327
+ video.removeEventListener("pause", updatePlayingState);
328
+ video.removeEventListener("playing", updatePlayingState);
329
+ video.removeEventListener("volumechange", updateMutedState);
330
+ video.removeEventListener("ratechange", updatePlaybackRate);
331
+ if (typeof document !== "undefined") document.removeEventListener("fullscreenchange", updateFullscreenState);
332
+ };
333
+ }, [video, isLive]);
334
+ // Reset the seek-to-live flag when video element changes (new stream)
335
+ useEffect(function () {
336
+ hasSeekToLiveRef.current = false;
337
+ }, [video]);
338
+ useEffect(function () {
339
+ var activeTrack = textTracks.find(function (track) {
340
+ return track.active;
341
+ });
342
+ setCaptionValue(activeTrack ? activeTrack.id : "none");
343
+ }, [textTracks]);
344
+ // Track buffered ranges for SeekBar
345
+ useEffect(function () {
346
+ if (!video) return;
347
+ var updateBuffered = function updateBuffered() {
348
+ var _player$getBufferedRa, _player$getBufferedRa2;
349
+ var next = (_player$getBufferedRa = player === null || player === void 0 || (_player$getBufferedRa2 = player.getBufferedRanges) === null || _player$getBufferedRa2 === void 0 ? void 0 : _player$getBufferedRa2.call(player)) !== null && _player$getBufferedRa !== void 0 ? _player$getBufferedRa : video.buffered;
350
+ setBuffered(next);
351
+ };
352
+ updateBuffered();
353
+ video.addEventListener("progress", updateBuffered);
354
+ video.addEventListener("loadeddata", updateBuffered);
355
+ return function () {
356
+ video.removeEventListener("progress", updateBuffered);
357
+ video.removeEventListener("loadeddata", updateBuffered);
358
+ };
359
+ }, [video]);
360
+ useEffect(function () {
361
+ if (!video) {
362
+ setHasAudio(true);
363
+ return;
364
+ }
365
+ var checkAudio = function checkAudio() {
366
+ if (video.srcObject instanceof MediaStream) {
367
+ var audioTracks = video.srcObject.getAudioTracks();
368
+ setHasAudio(audioTracks.length > 0);
369
+ return;
370
+ }
371
+ var videoAny = video;
372
+ if (videoAny.audioTracks && videoAny.audioTracks.length !== undefined) {
373
+ setHasAudio(videoAny.audioTracks.length > 0);
374
+ return;
375
+ }
376
+ setHasAudio(true);
377
+ };
378
+ checkAudio();
379
+ video.addEventListener("loadedmetadata", checkAudio);
380
+ return function () {
381
+ return video.removeEventListener("loadedmetadata", checkAudio);
382
+ };
383
+ }, [video]);
384
+ var handlePlayPause = function handlePlayPause() {
385
+ var _ref7, _player$isPaused, _player$isPaused2;
386
+ if (disabled) return;
387
+ // Prefer prop callback from usePlayerController
388
+ if (onTogglePlay) {
389
+ onTogglePlay();
390
+ return;
391
+ }
392
+ // Fallback: direct video/player manipulation
393
+ if (!video && !player) return;
394
+ var isPaused = (_ref7 = (_player$isPaused = player === null || player === void 0 || (_player$isPaused2 = player.isPaused) === null || _player$isPaused2 === void 0 ? void 0 : _player$isPaused2.call(player)) !== null && _player$isPaused !== void 0 ? _player$isPaused : video === null || video === void 0 ? void 0 : video.paused) !== null && _ref7 !== void 0 ? _ref7 : true;
395
+ if (isPaused) {
396
+ if (player !== null && player !== void 0 && player.play) player.play()["catch"](function () {});else if (video) video.play()["catch"](function () {});
397
+ } else {
398
+ if (player !== null && player !== void 0 && player.pause) player.pause();else if (video) video.pause();
399
+ }
400
+ };
401
+ var handleSkipBack = function handleSkipBack() {
402
+ var newTime = Math.max(0, currentTime - 10);
403
+ if (_onSeek) {
404
+ _onSeek(newTime);
405
+ return;
406
+ }
407
+ var v = findVideoElement();
408
+ if (v) v.currentTime = newTime;
409
+ };
410
+ var handleSkipForward = function handleSkipForward() {
411
+ var maxTime = Number.isFinite(duration) ? duration : currentTime + 10;
412
+ var newTime = Math.min(maxTime, currentTime + 10);
413
+ if (_onSeek) {
414
+ _onSeek(newTime);
415
+ return;
416
+ }
417
+ var v = findVideoElement();
418
+ if (v) v.currentTime = newTime;
419
+ };
420
+ var handleMute = function handleMute() {
421
+ var _player$isMuted, _player$isMuted2, _player$setMuted;
422
+ if (disabled) return;
423
+ // Prefer prop callback from usePlayerController
424
+ if (onToggleMute) {
425
+ onToggleMute();
426
+ return;
427
+ }
428
+ // Fallback: direct video/player manipulation
429
+ var v = video !== null && video !== void 0 ? video : document.querySelector(".fw-player-video");
430
+ if (!v) return;
431
+ var nextMuted = !((_player$isMuted = player === null || player === void 0 || (_player$isMuted2 = player.isMuted) === null || _player$isMuted2 === void 0 ? void 0 : _player$isMuted2.call(player)) !== null && _player$isMuted !== void 0 ? _player$isMuted : v.muted);
432
+ player === null || player === void 0 || (_player$setMuted = player.setMuted) === null || _player$setMuted === void 0 || _player$setMuted.call(player, nextMuted);
433
+ v.muted = nextMuted;
434
+ setInternalIsMuted(nextMuted);
435
+ if (nextMuted) setInternalVolume(0);else setInternalVolume(Math.round(v.volume * 100));
436
+ };
437
+ var handleVolumeChange = useCallback(function (value) {
438
+ var _value$;
439
+ if (disabled) return;
440
+ var next = Math.max(0, Math.min(100, (_value$ = value[0]) !== null && _value$ !== void 0 ? _value$ : 0));
441
+ // Prefer prop callback from usePlayerController
442
+ if (onVolumeChange) {
443
+ onVolumeChange(next / 100);
444
+ return;
445
+ }
446
+ // Fallback: direct video manipulation
447
+ var v = video !== null && video !== void 0 ? video : document.querySelector(".fw-player-video");
448
+ if (!v) return;
449
+ v.volume = next / 100;
450
+ v.muted = next === 0;
451
+ setInternalVolume(next);
452
+ setInternalIsMuted(next === 0);
453
+ }, [disabled, onVolumeChange, video]);
454
+ var handleFullscreen = function handleFullscreen() {
455
+ if (disabled) return;
456
+ // Prefer prop callback from usePlayerController
457
+ if (onToggleFullscreen) {
458
+ onToggleFullscreen();
459
+ return;
460
+ }
461
+ // Fallback: direct DOM manipulation
462
+ if (typeof document === "undefined") return;
463
+ var container = document.querySelector('[data-player-container="true"]');
464
+ if (!container) return;
465
+ if (document.fullscreenElement) document.exitFullscreen()["catch"](function () {});else container.requestFullscreen()["catch"](function () {});
466
+ };
467
+ var handleGoLive = function handleGoLive() {
468
+ var _player$jumpToLive;
469
+ if (disabled) return;
470
+ if (onJumpToLive) {
471
+ onJumpToLive();
472
+ return;
473
+ }
474
+ player === null || player === void 0 || (_player$jumpToLive = player.jumpToLive) === null || _player$jumpToLive === void 0 || _player$jumpToLive.call(player);
475
+ };
476
+ var handleSpeedChange = function handleSpeedChange(value) {
477
+ if (disabled) return;
478
+ var rate = Number(value);
479
+ setPlaybackRate(rate);
480
+ // Use player API if available, fall back to direct video element
481
+ if (player !== null && player !== void 0 && player.setPlaybackRate) {
482
+ player.setPlaybackRate(rate);
483
+ } else {
484
+ var v = findVideoElement();
485
+ if (v) v.playbackRate = rate;
486
+ }
487
+ };
488
+ var handleQualityChange = function handleQualityChange(value) {
489
+ var _player$selectQuality;
490
+ if (disabled) return;
491
+ setQualityValue(value);
492
+ // Prefer prop callback from usePlayerController
493
+ if (onSelectQuality) {
494
+ onSelectQuality(value);
495
+ return;
496
+ }
497
+ // Fallback: direct player manipulation
498
+ player === null || player === void 0 || (_player$selectQuality = player.selectQuality) === null || _player$selectQuality === void 0 || _player$selectQuality.call(player, value);
499
+ };
500
+ var handleCaptionChange = function handleCaptionChange(value) {
501
+ var _player$selectTextTra, _player$selectTextTra2;
502
+ if (disabled) return;
503
+ setCaptionValue(value);
504
+ if (value === "none") player === null || player === void 0 || (_player$selectTextTra = player.selectTextTrack) === null || _player$selectTextTra === void 0 || _player$selectTextTra.call(player, null);else player === null || player === void 0 || (_player$selectTextTra2 = player.selectTextTrack) === null || _player$selectTextTra2 === void 0 || _player$selectTextTra2.call(player, value);
505
+ };
506
+ // Time display - using core formatTimeDisplay
507
+ var timeDisplay = useMemo(function () {
508
+ return formatTimeDisplay({
509
+ isLive: isLive,
510
+ currentTime: currentTime,
511
+ duration: duration,
512
+ liveEdge: liveEdge,
513
+ seekableStart: seekableStart,
514
+ unixoffset: mistStreamInfo === null || mistStreamInfo === void 0 ? void 0 : mistStreamInfo.unixoffset
515
+ });
516
+ }, [isLive, currentTime, duration, liveEdge, seekableStart, mistStreamInfo === null || mistStreamInfo === void 0 ? void 0 : mistStreamInfo.unixoffset]);
517
+ var _useState23 = useState(false),
518
+ _useState24 = _slicedToArray(_useState23, 2),
519
+ isVolumeHovered = _useState24[0],
520
+ setIsVolumeHovered = _useState24[1];
521
+ var _useState25 = useState(false),
522
+ _useState26 = _slicedToArray(_useState25, 2),
523
+ isVolumeFocused = _useState26[0],
524
+ setIsVolumeFocused = _useState26[1];
525
+ var isVolumeExpanded = isVolumeHovered || isVolumeFocused;
526
+ var volumeGroupRef = useRef(null);
527
+ // Non-passive wheel listener for volume control
528
+ useEffect(function () {
529
+ var el = volumeGroupRef.current;
530
+ if (!el) return;
531
+ var handler = function handler(e) {
532
+ if (disabled || !hasAudio) return;
533
+ e.preventDefault();
534
+ var delta = e.deltaY < 0 ? 5 : -5;
535
+ handleVolumeChange([actualVolume + delta]);
536
+ };
537
+ el.addEventListener("wheel", handler, {
538
+ passive: false
539
+ });
540
+ return function () {
541
+ return el.removeEventListener("wheel", handler);
542
+ };
543
+ }, [disabled, hasAudio, actualVolume, handleVolumeChange]);
544
+ return jsx("div", {
545
+ className: cn("fw-player-surface fw-controls-wrapper", isVisible ? "fw-controls-wrapper--visible" : "fw-controls-wrapper--hidden"),
546
+ children: jsxs("div", {
547
+ className: "fw-control-bar pointer-events-auto",
548
+ onClick: function onClick(e) {
549
+ return e.stopPropagation();
550
+ },
551
+ children: [canSeek && jsx("div", {
552
+ className: "fw-seek-wrapper",
553
+ children: jsx(SeekBar, {
554
+ currentTime: currentTime,
555
+ duration: duration,
556
+ buffered: buffered,
557
+ disabled: disabled,
558
+ isLive: isLive,
559
+ seekableStart: seekableStart,
560
+ liveEdge: liveEdge,
561
+ commitOnRelease: commitOnRelease,
562
+ onSeek: function onSeek(time) {
563
+ if (_onSeek) {
564
+ _onSeek(time);
565
+ } else if (video) {
566
+ video.currentTime = time;
567
+ }
568
+ }
569
+ })
570
+ }), jsxs("div", {
571
+ className: "fw-controls-row",
572
+ children: [jsxs("div", {
573
+ className: "fw-controls-left",
574
+ children: [jsxs("div", {
575
+ className: "fw-control-group",
576
+ children: [jsx("button", {
577
+ type: "button",
578
+ className: "fw-btn-flush",
579
+ "aria-label": isPlaying ? "Pause" : "Play",
580
+ onClick: handlePlayPause,
581
+ children: jsx(PlayPauseIcon, {
582
+ isPlaying: isPlaying,
583
+ size: 18
584
+ })
585
+ }), canSeek && jsxs(Fragment, {
586
+ children: [jsx("button", {
587
+ type: "button",
588
+ className: "fw-btn-flush hidden sm:flex",
589
+ "aria-label": "Skip back 10 seconds",
590
+ onClick: handleSkipBack,
591
+ children: jsx(SkipBackIcon, {
592
+ size: 16
593
+ })
594
+ }), jsx("button", {
595
+ type: "button",
596
+ className: "fw-btn-flush hidden sm:flex",
597
+ "aria-label": "Skip forward 10 seconds",
598
+ onClick: handleSkipForward,
599
+ children: jsx(SkipForwardIcon, {
600
+ size: 16
601
+ })
602
+ })]
603
+ })]
604
+ }), jsxs("div", {
605
+ ref: volumeGroupRef,
606
+ className: cn("fw-volume-group", isVolumeExpanded && "fw-volume-group--expanded", !hasAudio && "fw-volume-group--disabled"),
607
+ onMouseEnter: function onMouseEnter() {
608
+ return hasAudio && setIsVolumeHovered(true);
609
+ },
610
+ onMouseLeave: function onMouseLeave() {
611
+ setIsVolumeHovered(false);
612
+ setIsVolumeFocused(false);
613
+ },
614
+ onFocusCapture: function onFocusCapture() {
615
+ return hasAudio && setIsVolumeFocused(true);
616
+ },
617
+ onBlurCapture: function onBlurCapture(e) {
618
+ if (!e.currentTarget.contains(e.relatedTarget)) setIsVolumeFocused(false);
619
+ },
620
+ onClick: function onClick(e) {
621
+ // Click on the pill (not slider) toggles mute
622
+ if (hasAudio && e.target === e.currentTarget) {
623
+ handleMute();
624
+ }
625
+ },
626
+ children: [jsx("button", {
627
+ type: "button",
628
+ className: "fw-volume-btn",
629
+ "aria-label": !hasAudio ? "No audio" : isMuted ? "Unmute" : "Mute",
630
+ onClick: hasAudio ? handleMute : undefined,
631
+ disabled: !hasAudio,
632
+ children: jsx(VolumeIcon, {
633
+ isMuted: isMuted || !hasAudio,
634
+ size: 16
635
+ })
636
+ }), jsx("div", {
637
+ className: cn("fw-volume-slider-wrapper", isVolumeExpanded ? "fw-volume-slider-wrapper--expanded" : "fw-volume-slider-wrapper--collapsed"),
638
+ children: jsx(Slider, {
639
+ orientation: "horizontal",
640
+ "aria-label": "Volume",
641
+ max: 100,
642
+ step: 1,
643
+ value: [volumeValue],
644
+ onValueChange: handleVolumeChange,
645
+ className: "w-full",
646
+ disabled: !hasAudio
647
+ })
648
+ })]
649
+ }), jsx("div", {
650
+ className: "fw-control-group",
651
+ children: jsx("span", {
652
+ className: "fw-time-display",
653
+ children: timeDisplay
654
+ })
655
+ }), isLive && jsx("div", {
656
+ className: "fw-control-group",
657
+ children: jsxs("button", {
658
+ type: "button",
659
+ onClick: handleGoLive,
660
+ disabled: !hasDvrWindow || isNearLiveState,
661
+ className: cn("fw-live-badge", !hasDvrWindow || isNearLiveState ? "fw-live-badge--active" : "fw-live-badge--behind"),
662
+ title: !hasDvrWindow ? "Live only" : isNearLiveState ? "At live edge" : "Jump to live",
663
+ children: ["LIVE", !isNearLiveState && hasDvrWindow && jsx(SeekToLiveIcon, {
664
+ size: 10
665
+ })]
666
+ })
667
+ })]
668
+ }), jsxs("div", {
669
+ className: "fw-controls-right",
670
+ children: [jsxs("div", {
671
+ className: "fw-control-group relative",
672
+ children: [jsx("button", {
673
+ type: "button",
674
+ className: cn("fw-btn-flush group", isSettingsOpen && "fw-btn-flush--active"),
675
+ "aria-label": "Settings",
676
+ title: "Settings",
677
+ onClick: function onClick() {
678
+ return setIsSettingsOpen(!isSettingsOpen);
679
+ },
680
+ children: jsx(SettingsIcon, {
681
+ size: 16,
682
+ className: "transition-transform group-hover:rotate-90"
683
+ })
684
+ }), isSettingsOpen && jsxs("div", {
685
+ className: "fw-player-surface fw-settings-menu",
686
+ children: [onModeChange && isContentLive !== false && jsxs("div", {
687
+ className: "fw-settings-section",
688
+ children: [jsx("div", {
689
+ className: "fw-settings-label",
690
+ children: "Mode"
691
+ }), jsx("div", {
692
+ className: "fw-settings-options",
693
+ children: ["auto", "low-latency", "quality"].map(function (mode) {
694
+ return jsx("button", {
695
+ className: cn("fw-settings-btn", playbackMode === mode && "fw-settings-btn--active"),
696
+ onClick: function onClick() {
697
+ onModeChange(mode);
698
+ setIsSettingsOpen(false);
699
+ },
700
+ children: mode === "low-latency" ? "Fast" : mode === "quality" ? "Stable" : "Auto"
701
+ }, mode);
702
+ })
703
+ })]
704
+ }), supportsPlaybackRate$1 && jsxs("div", {
705
+ className: "fw-settings-section",
706
+ children: [jsx("div", {
707
+ className: "fw-settings-label",
708
+ children: "Speed"
709
+ }), jsx("div", {
710
+ className: "fw-settings-options fw-settings-options--wrap",
711
+ children: SPEED_PRESETS.map(function (rate) {
712
+ return jsxs("button", {
713
+ className: cn("fw-settings-btn", playbackRate === rate && "fw-settings-btn--active"),
714
+ onClick: function onClick() {
715
+ handleSpeedChange(String(rate));
716
+ setIsSettingsOpen(false);
717
+ },
718
+ children: [rate, "x"]
719
+ }, rate);
720
+ })
721
+ })]
722
+ }), qualities.length > 0 && jsxs("div", {
723
+ className: "fw-settings-section",
724
+ children: [jsx("div", {
725
+ className: "fw-settings-label",
726
+ children: "Quality"
727
+ }), jsxs("div", {
728
+ className: "fw-settings-list",
729
+ children: [jsx("button", {
730
+ className: cn("fw-settings-list-item", qualityValue === "auto" && "fw-settings-list-item--active"),
731
+ onClick: function onClick() {
732
+ handleQualityChange("auto");
733
+ setIsSettingsOpen(false);
734
+ },
735
+ children: "Auto"
736
+ }), qualities.map(function (q) {
737
+ return jsx("button", {
738
+ className: cn("fw-settings-list-item", qualityValue === q.id && "fw-settings-list-item--active"),
739
+ onClick: function onClick() {
740
+ handleQualityChange(q.id);
741
+ setIsSettingsOpen(false);
742
+ },
743
+ children: q.label
744
+ }, q.id);
745
+ })]
746
+ })]
747
+ }), textTracks.length > 0 && jsxs("div", {
748
+ className: "fw-settings-section",
749
+ children: [jsx("div", {
750
+ className: "fw-settings-label",
751
+ children: "Captions"
752
+ }), jsxs("div", {
753
+ className: "fw-settings-list",
754
+ children: [jsx("button", {
755
+ className: cn("fw-settings-list-item", captionValue === "none" && "fw-settings-list-item--active"),
756
+ onClick: function onClick() {
757
+ handleCaptionChange("none");
758
+ setIsSettingsOpen(false);
759
+ },
760
+ children: "Off"
761
+ }), textTracks.map(function (t) {
762
+ return jsx("button", {
763
+ className: cn("fw-settings-list-item", captionValue === t.id && "fw-settings-list-item--active"),
764
+ onClick: function onClick() {
765
+ handleCaptionChange(t.id);
766
+ setIsSettingsOpen(false);
767
+ },
768
+ children: t.label || t.id
769
+ }, t.id);
770
+ })]
771
+ })]
772
+ })]
773
+ })]
774
+ }), jsx("div", {
775
+ className: "fw-control-group",
776
+ children: jsx("button", {
777
+ type: "button",
778
+ className: "fw-btn-flush",
779
+ "aria-label": "Toggle fullscreen",
780
+ onClick: handleFullscreen,
781
+ children: jsx(FullscreenToggleIcon, {
782
+ isFullscreen: isFullscreen,
783
+ size: 16
784
+ })
785
+ })
786
+ })]
787
+ })]
788
+ })]
789
+ })
790
+ });
791
+ };
792
+
793
+ export { PlayerControls as default };
794
+ //# sourceMappingURL=PlayerControls.js.map