@livepeer-frameworks/player-react 0.1.0 → 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 (187) hide show
  1. package/README.md +7 -9
  2. package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js +359 -0
  3. package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
  4. package/dist/cjs/assets/logomark.svg.js +8 -0
  5. package/dist/cjs/assets/logomark.svg.js.map +1 -0
  6. package/dist/cjs/components/DevModePanel.js +826 -0
  7. package/dist/cjs/components/DevModePanel.js.map +1 -0
  8. package/dist/cjs/components/DvdLogo.js +200 -0
  9. package/dist/cjs/components/DvdLogo.js.map +1 -0
  10. package/dist/cjs/components/Icons.js +439 -0
  11. package/dist/cjs/components/Icons.js.map +1 -0
  12. package/dist/cjs/components/IdleScreen.js +587 -0
  13. package/dist/cjs/components/IdleScreen.js.map +1 -0
  14. package/dist/cjs/components/LoadingScreen.js +523 -0
  15. package/dist/cjs/components/LoadingScreen.js.map +1 -0
  16. package/dist/cjs/components/Player.js +420 -0
  17. package/dist/cjs/components/Player.js.map +1 -0
  18. package/dist/cjs/components/PlayerControls.js +798 -0
  19. package/dist/cjs/components/PlayerControls.js.map +1 -0
  20. package/dist/cjs/components/PlayerErrorBoundary.js +80 -0
  21. package/dist/cjs/components/PlayerErrorBoundary.js.map +1 -0
  22. package/dist/cjs/components/SeekBar.js +253 -0
  23. package/dist/cjs/components/SeekBar.js.map +1 -0
  24. package/dist/cjs/components/SkipIndicator.js +92 -0
  25. package/dist/cjs/components/SkipIndicator.js.map +1 -0
  26. package/dist/cjs/components/SpeedIndicator.js +43 -0
  27. package/dist/cjs/components/SpeedIndicator.js.map +1 -0
  28. package/dist/cjs/components/StatsPanel.js +202 -0
  29. package/dist/cjs/components/StatsPanel.js.map +1 -0
  30. package/dist/cjs/components/StreamStateOverlay.js +229 -0
  31. package/dist/cjs/components/StreamStateOverlay.js.map +1 -0
  32. package/dist/cjs/components/ThumbnailOverlay.js +86 -0
  33. package/dist/cjs/components/ThumbnailOverlay.js.map +1 -0
  34. package/dist/cjs/components/TitleOverlay.js +32 -0
  35. package/dist/cjs/components/TitleOverlay.js.map +1 -0
  36. package/dist/cjs/context/PlayerContext.js +46 -0
  37. package/dist/cjs/context/PlayerContext.js.map +1 -0
  38. package/dist/cjs/hooks/useMetaTrack.js +165 -0
  39. package/dist/cjs/hooks/useMetaTrack.js.map +1 -0
  40. package/dist/cjs/hooks/usePlaybackQuality.js +131 -0
  41. package/dist/cjs/hooks/usePlaybackQuality.js.map +1 -0
  42. package/dist/cjs/hooks/usePlayerController.js +518 -0
  43. package/dist/cjs/hooks/usePlayerController.js.map +1 -0
  44. package/dist/cjs/hooks/usePlayerSelection.js +90 -0
  45. package/dist/cjs/hooks/usePlayerSelection.js.map +1 -0
  46. package/dist/cjs/hooks/useStreamState.js +360 -0
  47. package/dist/cjs/hooks/useStreamState.js.map +1 -0
  48. package/dist/cjs/hooks/useTelemetry.js +120 -0
  49. package/dist/cjs/hooks/useTelemetry.js.map +1 -0
  50. package/dist/cjs/hooks/useViewerEndpoints.js +222 -0
  51. package/dist/cjs/hooks/useViewerEndpoints.js.map +1 -0
  52. package/dist/cjs/index.js +97 -1
  53. package/dist/cjs/index.js.map +1 -1
  54. package/dist/cjs/ui/badge.js +34 -0
  55. package/dist/cjs/ui/badge.js.map +1 -0
  56. package/dist/cjs/ui/button.js +74 -0
  57. package/dist/cjs/ui/button.js.map +1 -0
  58. package/dist/cjs/ui/context-menu.js +163 -0
  59. package/dist/cjs/ui/context-menu.js.map +1 -0
  60. package/dist/cjs/ui/slider.js +60 -0
  61. package/dist/cjs/ui/slider.js.map +1 -0
  62. package/dist/esm/_virtual/_rollupPluginBabelHelpers.js +329 -0
  63. package/dist/esm/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
  64. package/dist/esm/assets/logomark.svg.js +4 -0
  65. package/dist/esm/assets/logomark.svg.js.map +1 -0
  66. package/dist/esm/components/DevModePanel.js +822 -0
  67. package/dist/esm/components/DevModePanel.js.map +1 -0
  68. package/dist/esm/components/DvdLogo.js +196 -0
  69. package/dist/esm/components/DvdLogo.js.map +1 -0
  70. package/dist/esm/components/Icons.js +421 -0
  71. package/dist/esm/components/Icons.js.map +1 -0
  72. package/dist/esm/components/IdleScreen.js +582 -0
  73. package/dist/esm/components/IdleScreen.js.map +1 -0
  74. package/dist/esm/components/LoadingScreen.js +519 -0
  75. package/dist/esm/components/LoadingScreen.js.map +1 -0
  76. package/dist/esm/components/Player.js +416 -0
  77. package/dist/esm/components/Player.js.map +1 -0
  78. package/dist/esm/components/PlayerControls.js +794 -0
  79. package/dist/esm/components/PlayerControls.js.map +1 -0
  80. package/dist/esm/components/PlayerErrorBoundary.js +76 -0
  81. package/dist/esm/components/PlayerErrorBoundary.js.map +1 -0
  82. package/dist/esm/components/SeekBar.js +249 -0
  83. package/dist/esm/components/SeekBar.js.map +1 -0
  84. package/dist/esm/components/SkipIndicator.js +88 -0
  85. package/dist/esm/components/SkipIndicator.js.map +1 -0
  86. package/dist/esm/components/SpeedIndicator.js +39 -0
  87. package/dist/esm/components/SpeedIndicator.js.map +1 -0
  88. package/dist/esm/components/StatsPanel.js +198 -0
  89. package/dist/esm/components/StatsPanel.js.map +1 -0
  90. package/dist/esm/components/StreamStateOverlay.js +224 -0
  91. package/dist/esm/components/StreamStateOverlay.js.map +1 -0
  92. package/dist/esm/components/ThumbnailOverlay.js +82 -0
  93. package/dist/esm/components/ThumbnailOverlay.js.map +1 -0
  94. package/dist/esm/components/TitleOverlay.js +28 -0
  95. package/dist/esm/components/TitleOverlay.js.map +1 -0
  96. package/dist/esm/context/PlayerContext.js +41 -0
  97. package/dist/esm/context/PlayerContext.js.map +1 -0
  98. package/dist/esm/hooks/useMetaTrack.js +163 -0
  99. package/dist/esm/hooks/useMetaTrack.js.map +1 -0
  100. package/dist/esm/hooks/usePlaybackQuality.js +129 -0
  101. package/dist/esm/hooks/usePlaybackQuality.js.map +1 -0
  102. package/dist/esm/hooks/usePlayerController.js +516 -0
  103. package/dist/esm/hooks/usePlayerController.js.map +1 -0
  104. package/dist/esm/hooks/usePlayerSelection.js +88 -0
  105. package/dist/esm/hooks/usePlayerSelection.js.map +1 -0
  106. package/dist/esm/hooks/useStreamState.js +358 -0
  107. package/dist/esm/hooks/useStreamState.js.map +1 -0
  108. package/dist/esm/hooks/useTelemetry.js +118 -0
  109. package/dist/esm/hooks/useTelemetry.js.map +1 -0
  110. package/dist/esm/hooks/useViewerEndpoints.js +220 -0
  111. package/dist/esm/hooks/useViewerEndpoints.js.map +1 -0
  112. package/dist/esm/index.js +23 -1
  113. package/dist/esm/index.js.map +1 -1
  114. package/dist/esm/ui/badge.js +31 -0
  115. package/dist/esm/ui/badge.js.map +1 -0
  116. package/dist/esm/ui/button.js +52 -0
  117. package/dist/esm/ui/button.js.map +1 -0
  118. package/dist/esm/ui/context-menu.js +132 -0
  119. package/dist/esm/ui/context-menu.js.map +1 -0
  120. package/dist/esm/ui/slider.js +38 -0
  121. package/dist/esm/ui/slider.js.map +1 -0
  122. package/dist/types/components/DvdLogo.d.ts +1 -1
  123. package/dist/types/components/Icons.d.ts +1 -1
  124. package/dist/types/components/Player.d.ts +1 -1
  125. package/dist/types/components/PlayerErrorBoundary.d.ts +2 -1
  126. package/dist/types/components/StreamStateOverlay.d.ts +2 -2
  127. package/dist/types/components/SubtitleRenderer.d.ts +2 -2
  128. package/dist/types/context/PlayerContext.d.ts +2 -2
  129. package/dist/types/context/index.d.ts +2 -2
  130. package/dist/types/hooks/useMetaTrack.d.ts +3 -3
  131. package/dist/types/hooks/usePlaybackQuality.d.ts +2 -2
  132. package/dist/types/hooks/usePlayerController.d.ts +26 -3
  133. package/dist/types/hooks/usePlayerSelection.d.ts +1 -1
  134. package/dist/types/hooks/useStreamState.d.ts +1 -1
  135. package/dist/types/hooks/useTelemetry.d.ts +1 -1
  136. package/dist/types/hooks/useViewerEndpoints.d.ts +3 -3
  137. package/dist/types/index.d.ts +28 -28
  138. package/dist/types/types.d.ts +3 -3
  139. package/dist/types/ui/select.d.ts +1 -1
  140. package/package.json +22 -14
  141. package/src/components/DevModePanel.tsx +244 -143
  142. package/src/components/DvdLogo.tsx +1 -1
  143. package/src/components/Icons.tsx +105 -25
  144. package/src/components/IdleScreen.tsx +262 -128
  145. package/src/components/LoadingScreen.tsx +169 -151
  146. package/src/components/LogoOverlay.tsx +3 -6
  147. package/src/components/Player.tsx +126 -59
  148. package/src/components/PlayerControls.tsx +384 -272
  149. package/src/components/PlayerErrorBoundary.tsx +7 -13
  150. package/src/components/SeekBar.tsx +96 -88
  151. package/src/components/SkipIndicator.tsx +2 -12
  152. package/src/components/SpeedIndicator.tsx +2 -11
  153. package/src/components/StatsPanel.tsx +31 -22
  154. package/src/components/StreamStateOverlay.tsx +105 -49
  155. package/src/components/SubtitleRenderer.tsx +29 -29
  156. package/src/components/ThumbnailOverlay.tsx +5 -6
  157. package/src/components/TitleOverlay.tsx +2 -8
  158. package/src/context/PlayerContext.tsx +4 -8
  159. package/src/context/index.ts +3 -3
  160. package/src/hooks/useMetaTrack.ts +27 -27
  161. package/src/hooks/usePlaybackQuality.ts +3 -3
  162. package/src/hooks/usePlayerController.ts +246 -138
  163. package/src/hooks/usePlayerSelection.ts +6 -6
  164. package/src/hooks/useStreamState.ts +51 -56
  165. package/src/hooks/useTelemetry.ts +18 -3
  166. package/src/hooks/useViewerEndpoints.ts +34 -23
  167. package/src/index.tsx +36 -28
  168. package/src/types.ts +8 -8
  169. package/src/ui/badge.tsx +6 -5
  170. package/src/ui/button.tsx +9 -8
  171. package/src/ui/context-menu.tsx +42 -61
  172. package/src/ui/select.tsx +13 -7
  173. package/src/ui/slider.tsx +18 -29
  174. package/dist/types/components/players/DashJsPlayer.d.ts +0 -18
  175. package/dist/types/components/players/HlsJsPlayer.d.ts +0 -18
  176. package/dist/types/components/players/MewsWsPlayer/index.d.ts +0 -18
  177. package/dist/types/components/players/MistPlayer.d.ts +0 -20
  178. package/dist/types/components/players/MistWebRTCPlayer/index.d.ts +0 -20
  179. package/dist/types/components/players/NativePlayer.d.ts +0 -19
  180. package/dist/types/components/players/VideoJsPlayer.d.ts +0 -18
  181. package/src/components/players/DashJsPlayer.tsx +0 -56
  182. package/src/components/players/HlsJsPlayer.tsx +0 -56
  183. package/src/components/players/MewsWsPlayer/index.tsx +0 -56
  184. package/src/components/players/MistPlayer.tsx +0 -60
  185. package/src/components/players/MistWebRTCPlayer/index.tsx +0 -59
  186. package/src/components/players/NativePlayer.tsx +0 -58
  187. package/src/components/players/VideoJsPlayer.tsx +0 -56
@@ -0,0 +1,822 @@
1
+ import { slicedToArray as _slicedToArray, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../_virtual/_rollupPluginBabelHelpers.js';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
4
+ import { QualityMonitor, globalPlayerManager, cn } from '@livepeer-frameworks/player-core';
5
+
6
+ /** Short labels for source types */
7
+ var SOURCE_TYPE_LABELS = {
8
+ "html5/application/vnd.apple.mpegurl": "HLS",
9
+ "dash/video/mp4": "DASH",
10
+ "html5/video/mp4": "MP4",
11
+ "html5/video/webm": "WebM",
12
+ whep: "WHEP",
13
+ "mist/html": "Mist",
14
+ "mist/legacy": "Auto",
15
+ "ws/video/mp4": "MEWS"
16
+ };
17
+ /**
18
+ * DevModePanel - Advanced Settings overlay for testing player configurations
19
+ * Similar to MistPlayer's skin: "dev" mode
20
+ */
21
+ var DevModePanel = function DevModePanel(_ref) {
22
+ var _playerStats$levels, _mistStreamInfo$meta, _mistStreamInfo$meta2;
23
+ var onSettingsChange = _ref.onSettingsChange,
24
+ _ref$playbackMode = _ref.playbackMode,
25
+ playbackMode = _ref$playbackMode === void 0 ? "auto" : _ref$playbackMode,
26
+ onModeChange = _ref.onModeChange,
27
+ onReload = _ref.onReload,
28
+ streamInfo = _ref.streamInfo,
29
+ mistStreamInfo = _ref.mistStreamInfo,
30
+ currentPlayer = _ref.currentPlayer,
31
+ currentSource = _ref.currentSource,
32
+ videoElement = _ref.videoElement,
33
+ protocol = _ref.protocol,
34
+ nodeId = _ref.nodeId;
35
+ _ref.isVisible;
36
+ var controlledIsOpen = _ref.isOpen,
37
+ onOpenChange = _ref.onOpenChange;
38
+ // Support both controlled and uncontrolled modes
39
+ var _useState = useState(false),
40
+ _useState2 = _slicedToArray(_useState, 2),
41
+ internalIsOpen = _useState2[0],
42
+ setInternalIsOpen = _useState2[1];
43
+ var isOpen = controlledIsOpen !== undefined ? controlledIsOpen : internalIsOpen;
44
+ var setIsOpen = useCallback(function (value) {
45
+ if (onOpenChange) {
46
+ onOpenChange(value);
47
+ } else {
48
+ setInternalIsOpen(value);
49
+ }
50
+ }, [onOpenChange]);
51
+ var _useState3 = useState("config"),
52
+ _useState4 = _slicedToArray(_useState3, 2),
53
+ activeTab = _useState4[0],
54
+ setActiveTab = _useState4[1];
55
+ var _useState5 = useState(0),
56
+ _useState6 = _slicedToArray(_useState5, 2),
57
+ setCurrentComboIndex = _useState6[1];
58
+ var _useState7 = useState(null),
59
+ _useState8 = _slicedToArray(_useState7, 2),
60
+ hoveredComboIndex = _useState8[0],
61
+ setHoveredComboIndex = _useState8[1];
62
+ var _useState9 = useState(false),
63
+ _useState0 = _slicedToArray(_useState9, 2),
64
+ tooltipAbove = _useState0[0],
65
+ setTooltipAbove = _useState0[1];
66
+ var _useState1 = useState(false),
67
+ _useState10 = _slicedToArray(_useState1, 2),
68
+ showDisabledPlayers = _useState10[0],
69
+ setShowDisabledPlayers = _useState10[1];
70
+ var comboListRef = useRef(null);
71
+ // Quality monitoring for playback score
72
+ var qualityMonitorRef = useRef(null);
73
+ var _useState11 = useState(1.0),
74
+ _useState12 = _slicedToArray(_useState11, 2),
75
+ playbackScore = _useState12[0],
76
+ setPlaybackScore = _useState12[1];
77
+ var _useState13 = useState(100),
78
+ _useState14 = _slicedToArray(_useState13, 2),
79
+ qualityScore = _useState14[0],
80
+ setQualityScore = _useState14[1];
81
+ var _useState15 = useState(0),
82
+ _useState16 = _slicedToArray(_useState15, 2),
83
+ stallCount = _useState16[0],
84
+ setStallCount = _useState16[1];
85
+ var _useState17 = useState(0),
86
+ _useState18 = _slicedToArray(_useState17, 2),
87
+ frameDropRate = _useState18[0],
88
+ setFrameDropRate = _useState18[1];
89
+ // Player-specific stats (from getStats())
90
+ var _useState19 = useState(null),
91
+ _useState20 = _slicedToArray(_useState19, 2),
92
+ playerStats = _useState20[0],
93
+ setPlayerStats = _useState20[1];
94
+ var statsIntervalRef = useRef(null);
95
+ // Start/stop quality monitoring based on video element
96
+ useEffect(function () {
97
+ if (videoElement && isOpen) {
98
+ if (!qualityMonitorRef.current) {
99
+ qualityMonitorRef.current = new QualityMonitor({
100
+ sampleInterval: 500,
101
+ onSample: function onSample(quality) {
102
+ setQualityScore(quality.score);
103
+ setStallCount(quality.stallCount);
104
+ setFrameDropRate(quality.frameDropRate);
105
+ // Get playback score from monitor
106
+ if (qualityMonitorRef.current) {
107
+ setPlaybackScore(qualityMonitorRef.current.getPlaybackScore());
108
+ }
109
+ }
110
+ });
111
+ }
112
+ qualityMonitorRef.current.start(videoElement);
113
+ }
114
+ return function () {
115
+ if (qualityMonitorRef.current) {
116
+ qualityMonitorRef.current.stop();
117
+ }
118
+ };
119
+ }, [videoElement, isOpen]);
120
+ // Poll player-specific stats when stats tab is open
121
+ useEffect(function () {
122
+ if (isOpen && activeTab === "stats") {
123
+ var pollStats = /*#__PURE__*/function () {
124
+ var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
125
+ var player, _stats;
126
+ return _regenerator().w(function (_context) {
127
+ while (1) switch (_context.p = _context.n) {
128
+ case 0:
129
+ _context.p = 0;
130
+ player = globalPlayerManager.getCurrentPlayer();
131
+ if (!(player && player.getStats)) {
132
+ _context.n = 2;
133
+ break;
134
+ }
135
+ _context.n = 1;
136
+ return player.getStats();
137
+ case 1:
138
+ _stats = _context.v;
139
+ if (_stats) {
140
+ setPlayerStats(_stats);
141
+ }
142
+ case 2:
143
+ _context.n = 4;
144
+ break;
145
+ case 3:
146
+ _context.p = 3;
147
+ _context.v;
148
+ case 4:
149
+ return _context.a(2);
150
+ }
151
+ }, _callee, null, [[0, 3]]);
152
+ }));
153
+ return function pollStats() {
154
+ return _ref2.apply(this, arguments);
155
+ };
156
+ }();
157
+ // Poll immediately and then every 500ms
158
+ pollStats();
159
+ statsIntervalRef.current = setInterval(pollStats, 500);
160
+ return function () {
161
+ if (statsIntervalRef.current) {
162
+ clearInterval(statsIntervalRef.current);
163
+ statsIntervalRef.current = null;
164
+ }
165
+ };
166
+ }
167
+ }, [isOpen, activeTab]);
168
+ // Get all player-source combinations with scores (including incompatible)
169
+ // Uses cached results from PlayerManager - won't recompute if data unchanged
170
+ var allCombinations = useMemo(function () {
171
+ if (!streamInfo) return [];
172
+ try {
173
+ // getAllCombinations now includes all combos (compatible + incompatible)
174
+ // and uses content-based caching - won't spam on every render
175
+ return globalPlayerManager.getAllCombinations(streamInfo, playbackMode);
176
+ } catch (_unused2) {
177
+ return [];
178
+ }
179
+ }, [streamInfo, playbackMode]);
180
+ // For backward compatibility (Next Option only cycles compatible)
181
+ var combinations = useMemo(function () {
182
+ return allCombinations.filter(function (c) {
183
+ return c.compatible;
184
+ });
185
+ }, [allCombinations]);
186
+ // Find current active combo index based on current player/source (in allCombinations)
187
+ var activeComboIndex = useMemo(function () {
188
+ if (!currentPlayer || !currentSource || allCombinations.length === 0) return -1;
189
+ return allCombinations.findIndex(function (c) {
190
+ return c.player === currentPlayer.shortname && c.sourceType === currentSource.type;
191
+ });
192
+ }, [currentPlayer, currentSource, allCombinations]);
193
+ // Find index in compatible-only list for Next Option cycling
194
+ var activeCompatibleIndex = useMemo(function () {
195
+ if (!currentPlayer || !currentSource || combinations.length === 0) return -1;
196
+ return combinations.findIndex(function (c) {
197
+ return c.player === currentPlayer.shortname && c.sourceType === currentSource.type;
198
+ });
199
+ }, [currentPlayer, currentSource, combinations]);
200
+ var handleReload = useCallback(function () {
201
+ // Just trigger reload - controller manages the state
202
+ onReload === null || onReload === void 0 || onReload();
203
+ }, [onReload]);
204
+ var handleNextCombo = useCallback(function () {
205
+ if (combinations.length === 0) return;
206
+ // Start from active combo or 0, then move to next (only cycles compatible)
207
+ var startIdx = activeCompatibleIndex >= 0 ? activeCompatibleIndex : -1;
208
+ var nextIdx = (startIdx + 1) % combinations.length;
209
+ var combo = combinations[nextIdx];
210
+ setCurrentComboIndex(nextIdx);
211
+ onSettingsChange({
212
+ forcePlayer: combo.player,
213
+ forceType: combo.sourceType,
214
+ forceSource: combo.sourceIndex
215
+ });
216
+ }, [combinations, activeCompatibleIndex, onSettingsChange]);
217
+ var handleSelectCombo = useCallback(function (index) {
218
+ var combo = allCombinations[index];
219
+ if (!combo) return;
220
+ // Allow selecting even incompatible combos in dev mode (for testing)
221
+ setCurrentComboIndex(index);
222
+ onSettingsChange({
223
+ forcePlayer: combo.player,
224
+ forceType: combo.sourceType,
225
+ forceSource: combo.sourceIndex
226
+ });
227
+ }, [allCombinations, onSettingsChange]);
228
+ // Video stats - poll periodically when stats tab is open
229
+ var _useState21 = useState(null),
230
+ _useState22 = _slicedToArray(_useState21, 2),
231
+ stats = _useState22[0],
232
+ setStats = _useState22[1];
233
+ useEffect(function () {
234
+ if (!isOpen || activeTab !== "stats") return;
235
+ var updateStats = function updateStats() {
236
+ // Get fresh video element from player manager
237
+ var player = globalPlayerManager.getCurrentPlayer();
238
+ var v = (player === null || player === void 0 ? void 0 : player.getVideoElement()) || videoElement;
239
+ if (!v) {
240
+ setStats(null);
241
+ return;
242
+ }
243
+ setStats({
244
+ resolution: "".concat(v.videoWidth, "x").concat(v.videoHeight),
245
+ buffered: v.buffered.length > 0 ? (v.buffered.end(v.buffered.length - 1) - v.currentTime).toFixed(1) : "0",
246
+ playbackRate: v.playbackRate.toFixed(2),
247
+ currentTime: v.currentTime.toFixed(1),
248
+ duration: isFinite(v.duration) ? v.duration.toFixed(1) : "live",
249
+ readyState: v.readyState,
250
+ networkState: v.networkState
251
+ });
252
+ };
253
+ updateStats();
254
+ var interval = setInterval(updateStats, 500);
255
+ return function () {
256
+ return clearInterval(interval);
257
+ };
258
+ }, [isOpen, activeTab, videoElement]);
259
+ // Panel is only rendered when open (no floating toggle button)
260
+ if (!isOpen) {
261
+ return null;
262
+ }
263
+ return jsxs("div", {
264
+ className: "fw-dev-panel",
265
+ children: [jsxs("div", {
266
+ className: "fw-dev-header",
267
+ children: [jsx("button", {
268
+ type: "button",
269
+ onClick: function onClick() {
270
+ return setActiveTab("config");
271
+ },
272
+ className: cn("fw-dev-tab", activeTab === "config" && "fw-dev-tab--active"),
273
+ children: "Config"
274
+ }), jsx("button", {
275
+ type: "button",
276
+ onClick: function onClick() {
277
+ return setActiveTab("stats");
278
+ },
279
+ className: cn("fw-dev-tab", activeTab === "stats" && "fw-dev-tab--active"),
280
+ children: "Stats"
281
+ }), jsx("div", {
282
+ className: "fw-dev-spacer"
283
+ }), jsx("button", {
284
+ type: "button",
285
+ onClick: function onClick() {
286
+ return setIsOpen(false);
287
+ },
288
+ className: "fw-dev-close",
289
+ "aria-label": "Close dev mode panel",
290
+ children: jsx("svg", {
291
+ width: "12",
292
+ height: "12",
293
+ viewBox: "0 0 12 12",
294
+ fill: "none",
295
+ stroke: "currentColor",
296
+ strokeWidth: "1.5",
297
+ children: jsx("path", {
298
+ d: "M2 2l8 8M10 2l-8 8"
299
+ })
300
+ })
301
+ })]
302
+ }), activeTab === "config" && jsxs("div", {
303
+ ref: comboListRef,
304
+ className: "fw-dev-body",
305
+ children: [jsxs("div", {
306
+ className: "fw-dev-section",
307
+ children: [jsx("div", {
308
+ className: "fw-dev-label",
309
+ children: "Active"
310
+ }), jsxs("div", {
311
+ className: "fw-dev-value",
312
+ children: [(currentPlayer === null || currentPlayer === void 0 ? void 0 : currentPlayer.name) || "None", " ", jsx("span", {
313
+ className: "fw-dev-value-arrow",
314
+ children: "\u2192"
315
+ }), " ", SOURCE_TYPE_LABELS[(currentSource === null || currentSource === void 0 ? void 0 : currentSource.type) || ""] || (currentSource === null || currentSource === void 0 ? void 0 : currentSource.type) || "—"]
316
+ }), nodeId && jsxs("div", {
317
+ className: "fw-dev-value-muted",
318
+ children: ["Node: ", nodeId]
319
+ })]
320
+ }), jsxs("div", {
321
+ className: "fw-dev-section",
322
+ children: [jsx("div", {
323
+ className: "fw-dev-label",
324
+ children: "Playback Mode"
325
+ }), jsx("div", {
326
+ className: "fw-dev-mode-group",
327
+ children: ["auto", "low-latency", "quality"].map(function (mode) {
328
+ return jsx("button", {
329
+ type: "button",
330
+ onClick: function onClick() {
331
+ return onModeChange === null || onModeChange === void 0 ? void 0 : onModeChange(mode);
332
+ },
333
+ className: cn("fw-dev-mode-btn", playbackMode === mode && "fw-dev-mode-btn--active"),
334
+ children: mode === "low-latency" ? "Low Lat" : mode.charAt(0).toUpperCase() + mode.slice(1)
335
+ }, mode);
336
+ })
337
+ }), jsxs("div", {
338
+ className: "fw-dev-mode-desc",
339
+ children: [playbackMode === "auto" && "Balanced: MP4/WS → WHEP → HLS", playbackMode === "low-latency" && "WHEP/WebRTC first (<1s delay)", playbackMode === "quality" && "MP4/WS first, HLS fallback"]
340
+ })]
341
+ }), jsxs("div", {
342
+ className: "fw-dev-actions",
343
+ children: [jsx("button", {
344
+ type: "button",
345
+ onClick: handleReload,
346
+ className: "fw-dev-action-btn",
347
+ children: "Reload"
348
+ }), jsx("button", {
349
+ type: "button",
350
+ onClick: handleNextCombo,
351
+ className: "fw-dev-action-btn",
352
+ children: "Next Option"
353
+ })]
354
+ }), jsxs("div", {
355
+ className: "fw-dev-section",
356
+ style: {
357
+ padding: 0,
358
+ borderBottom: 0
359
+ },
360
+ children: [jsxs("div", {
361
+ className: "fw-dev-list-header",
362
+ children: [jsxs("span", {
363
+ className: "fw-dev-list-title",
364
+ children: ["Player Options (", combinations.length, ")"]
365
+ }), allCombinations.length > combinations.length && jsxs("button", {
366
+ type: "button",
367
+ onClick: function onClick() {
368
+ return setShowDisabledPlayers(!showDisabledPlayers);
369
+ },
370
+ className: "fw-dev-list-toggle",
371
+ children: [jsx("svg", {
372
+ width: "10",
373
+ height: "10",
374
+ viewBox: "0 0 24 24",
375
+ fill: "none",
376
+ stroke: "currentColor",
377
+ strokeWidth: "2",
378
+ className: cn("fw-dev-chevron", showDisabledPlayers && "fw-dev-chevron--open"),
379
+ children: jsx("path", {
380
+ d: "M6 9l6 6 6-6"
381
+ })
382
+ }), showDisabledPlayers ? "Hide" : "Show", " disabled (", allCombinations.length - combinations.length, ")"]
383
+ })]
384
+ }), allCombinations.length === 0 ? jsx("div", {
385
+ className: "fw-dev-list-empty",
386
+ children: "No stream info available"
387
+ }) : jsx("div", {
388
+ children: allCombinations.map(function (combo, index) {
389
+ var _combo$scoreBreakdown, _combo$scoreBreakdown2, _combo$scoreBreakdown3, _combo$scoreBreakdown4;
390
+ // Codec-incompatible items always show (with warning), MIME-incompatible hide in "disabled"
391
+ var isCodecIncompat = combo.codecIncompatible === true;
392
+ if (!combo.compatible && !isCodecIncompat && !showDisabledPlayers) return null;
393
+ var isActive = activeComboIndex === index;
394
+ var typeLabel = SOURCE_TYPE_LABELS[combo.sourceType] || combo.sourceType.split("/").pop();
395
+ // Determine score class
396
+ var getScoreClass = function getScoreClass() {
397
+ if (!combo.compatible && !isCodecIncompat) return "fw-dev-combo-score--disabled";
398
+ if (isCodecIncompat) return "fw-dev-combo-score--low";
399
+ if (combo.score >= 2) return "fw-dev-combo-score--high";
400
+ if (combo.score >= 1.5) return "fw-dev-combo-score--mid";
401
+ return "fw-dev-combo-score--low";
402
+ };
403
+ // Determine rank class
404
+ var getRankClass = function getRankClass() {
405
+ if (isActive) return "fw-dev-combo-rank--active";
406
+ if (!combo.compatible && !isCodecIncompat) return "fw-dev-combo-rank--disabled";
407
+ if (isCodecIncompat) return "fw-dev-combo-rank--warn";
408
+ return "";
409
+ };
410
+ // Determine type class
411
+ var getTypeClass = function getTypeClass() {
412
+ if (!combo.compatible && !isCodecIncompat) return "fw-dev-combo-type--disabled";
413
+ if (isCodecIncompat) return "fw-dev-combo-type--warn";
414
+ return "";
415
+ };
416
+ return jsxs("div", {
417
+ onMouseEnter: function onMouseEnter(e) {
418
+ setHoveredComboIndex(index);
419
+ if (comboListRef.current) {
420
+ var container = comboListRef.current;
421
+ var row = e.currentTarget;
422
+ var containerRect = container.getBoundingClientRect();
423
+ var rowRect = row.getBoundingClientRect();
424
+ var relativePosition = (rowRect.top - containerRect.top) / containerRect.height;
425
+ setTooltipAbove(relativePosition > 0.6);
426
+ }
427
+ },
428
+ onMouseLeave: function onMouseLeave() {
429
+ return setHoveredComboIndex(null);
430
+ },
431
+ className: "fw-dev-combo",
432
+ children: [jsxs("button", {
433
+ type: "button",
434
+ onClick: function onClick() {
435
+ return handleSelectCombo(index);
436
+ },
437
+ className: cn("fw-dev-combo-btn", isActive && "fw-dev-combo-btn--active", !combo.compatible && !isCodecIncompat && "fw-dev-combo-btn--disabled", isCodecIncompat && "fw-dev-combo-btn--codec-warn"),
438
+ children: [jsx("span", {
439
+ className: cn("fw-dev-combo-rank", getRankClass()),
440
+ children: combo.compatible ? index + 1 : isCodecIncompat ? "⚠" : "—"
441
+ }), jsxs("span", {
442
+ className: "fw-dev-combo-name",
443
+ children: [combo.playerName, " ", jsx("span", {
444
+ className: "fw-dev-combo-arrow",
445
+ children: "\u2192"
446
+ }), " ", jsx("span", {
447
+ className: cn("fw-dev-combo-type", getTypeClass()),
448
+ children: typeLabel
449
+ })]
450
+ }), jsx("span", {
451
+ className: cn("fw-dev-combo-score", getScoreClass()),
452
+ children: combo.score.toFixed(2)
453
+ })]
454
+ }), hoveredComboIndex === index && jsxs("div", {
455
+ className: cn("fw-dev-tooltip", tooltipAbove ? "fw-dev-tooltip--above" : "fw-dev-tooltip--below"),
456
+ children: [jsxs("div", {
457
+ className: "fw-dev-tooltip-header",
458
+ children: [jsx("div", {
459
+ className: "fw-dev-tooltip-title",
460
+ children: combo.playerName
461
+ }), jsx("div", {
462
+ className: "fw-dev-tooltip-subtitle",
463
+ children: combo.sourceType
464
+ }), ((_combo$scoreBreakdown = combo.scoreBreakdown) === null || _combo$scoreBreakdown === void 0 ? void 0 : _combo$scoreBreakdown.trackTypes) && combo.scoreBreakdown.trackTypes.length > 0 && jsxs("div", {
465
+ className: "fw-dev-tooltip-tracks",
466
+ children: ["Tracks:", " ", jsx("span", {
467
+ className: "fw-dev-tooltip-value",
468
+ children: combo.scoreBreakdown.trackTypes.join(", ")
469
+ })]
470
+ })]
471
+ }), combo.compatible && combo.scoreBreakdown ? jsxs(Fragment, {
472
+ children: [jsxs("div", {
473
+ className: "fw-dev-tooltip-score",
474
+ children: ["Score: ", combo.score.toFixed(2)]
475
+ }), jsxs("div", {
476
+ className: "fw-dev-tooltip-row",
477
+ children: ["Tracks [", combo.scoreBreakdown.trackTypes.join(", "), "]:", " ", jsx("span", {
478
+ className: "fw-dev-tooltip-value",
479
+ children: combo.scoreBreakdown.trackScore.toFixed(2)
480
+ }), " ", jsxs("span", {
481
+ className: "fw-dev-tooltip-weight",
482
+ children: ["x", combo.scoreBreakdown.weights.tracks]
483
+ })]
484
+ }), jsxs("div", {
485
+ className: "fw-dev-tooltip-row",
486
+ children: ["Priority:", " ", jsx("span", {
487
+ className: "fw-dev-tooltip-value",
488
+ children: combo.scoreBreakdown.priorityScore.toFixed(2)
489
+ }), " ", jsxs("span", {
490
+ className: "fw-dev-tooltip-weight",
491
+ children: ["x", combo.scoreBreakdown.weights.priority]
492
+ })]
493
+ }), jsxs("div", {
494
+ className: "fw-dev-tooltip-row",
495
+ children: ["Source:", " ", jsx("span", {
496
+ className: "fw-dev-tooltip-value",
497
+ children: combo.scoreBreakdown.sourceScore.toFixed(2)
498
+ }), " ", jsxs("span", {
499
+ className: "fw-dev-tooltip-weight",
500
+ children: ["x", combo.scoreBreakdown.weights.source]
501
+ })]
502
+ }), combo.scoreBreakdown.reliabilityScore !== undefined && jsxs("div", {
503
+ className: "fw-dev-tooltip-row",
504
+ children: ["Reliability:", " ", jsx("span", {
505
+ className: "fw-dev-tooltip-value",
506
+ children: combo.scoreBreakdown.reliabilityScore.toFixed(2)
507
+ }), " ", jsxs("span", {
508
+ className: "fw-dev-tooltip-weight",
509
+ children: ["x", (_combo$scoreBreakdown2 = combo.scoreBreakdown.weights.reliability) !== null && _combo$scoreBreakdown2 !== void 0 ? _combo$scoreBreakdown2 : 0]
510
+ })]
511
+ }), combo.scoreBreakdown.modeBonus !== undefined && combo.scoreBreakdown.modeBonus !== 0 && jsxs("div", {
512
+ className: "fw-dev-tooltip-row",
513
+ children: ["Mode (", playbackMode, "):", " ", jsxs("span", {
514
+ className: "fw-dev-tooltip-bonus",
515
+ children: ["+", combo.scoreBreakdown.modeBonus.toFixed(2)]
516
+ }), " ", jsxs("span", {
517
+ className: "fw-dev-tooltip-weight",
518
+ children: ["x", (_combo$scoreBreakdown3 = combo.scoreBreakdown.weights.mode) !== null && _combo$scoreBreakdown3 !== void 0 ? _combo$scoreBreakdown3 : 0]
519
+ })]
520
+ }), combo.scoreBreakdown.routingBonus !== undefined && combo.scoreBreakdown.routingBonus !== 0 && jsxs("div", {
521
+ className: "fw-dev-tooltip-row",
522
+ children: ["Routing:", " ", jsxs("span", {
523
+ className: combo.scoreBreakdown.routingBonus > 0 ? "fw-dev-tooltip-bonus" : "fw-dev-tooltip-penalty",
524
+ children: [combo.scoreBreakdown.routingBonus > 0 ? "+" : "", combo.scoreBreakdown.routingBonus.toFixed(2)]
525
+ }), " ", jsxs("span", {
526
+ className: "fw-dev-tooltip-weight",
527
+ children: ["x", (_combo$scoreBreakdown4 = combo.scoreBreakdown.weights.routing) !== null && _combo$scoreBreakdown4 !== void 0 ? _combo$scoreBreakdown4 : 0]
528
+ })]
529
+ })]
530
+ }) : jsx("div", {
531
+ className: "fw-dev-tooltip-error",
532
+ children: combo.incompatibleReason || "Incompatible"
533
+ })]
534
+ })]
535
+ }, "".concat(combo.player, "-").concat(combo.sourceType));
536
+ })
537
+ })]
538
+ })]
539
+ }), activeTab === "stats" && jsxs("div", {
540
+ className: "fw-dev-body",
541
+ children: [jsxs("div", {
542
+ className: "fw-dev-section",
543
+ children: [jsx("div", {
544
+ className: "fw-dev-label",
545
+ children: "Playback Rate"
546
+ }), jsxs("div", {
547
+ className: "fw-dev-rate",
548
+ children: [jsxs("div", {
549
+ className: cn("fw-dev-rate-value", playbackScore >= 0.95 && playbackScore <= 1.05 ? "fw-dev-stat-value--good" : playbackScore > 1.05 ? "fw-dev-stat-value--accent" : playbackScore >= 0.75 ? "fw-dev-stat-value--warn" : "fw-dev-stat-value--bad"),
550
+ children: [playbackScore.toFixed(2), "\xD7"]
551
+ }), jsx("div", {
552
+ className: "fw-dev-rate-status",
553
+ children: playbackScore >= 0.95 && playbackScore <= 1.05 ? "realtime" : playbackScore > 1.05 ? "catching up" : playbackScore >= 0.75 ? "slightly slow" : "stalling"
554
+ })]
555
+ }), jsxs("div", {
556
+ className: "fw-dev-rate-stats",
557
+ children: [jsxs("span", {
558
+ className: qualityScore >= 75 ? "fw-dev-stat-value--good" : "fw-dev-stat-value--bad",
559
+ children: ["Quality: ", qualityScore, "/100"]
560
+ }), jsxs("span", {
561
+ className: stallCount === 0 ? "fw-dev-stat-value--good" : "fw-dev-stat-value--warn",
562
+ children: ["Stalls: ", stallCount]
563
+ }), jsxs("span", {
564
+ className: frameDropRate < 1 ? "fw-dev-stat-value--good" : "fw-dev-stat-value--bad",
565
+ children: ["Drops: ", frameDropRate.toFixed(1), "%"]
566
+ })]
567
+ })]
568
+ }), stats ? jsxs("div", {
569
+ children: [jsxs("div", {
570
+ className: "fw-dev-stat",
571
+ children: [jsx("span", {
572
+ className: "fw-dev-stat-label",
573
+ children: "Resolution"
574
+ }), jsx("span", {
575
+ className: "fw-dev-stat-value",
576
+ children: stats.resolution
577
+ })]
578
+ }), jsxs("div", {
579
+ className: "fw-dev-stat",
580
+ children: [jsx("span", {
581
+ className: "fw-dev-stat-label",
582
+ children: "Buffer"
583
+ }), jsxs("span", {
584
+ className: "fw-dev-stat-value",
585
+ children: [stats.buffered, "s"]
586
+ })]
587
+ }), jsxs("div", {
588
+ className: "fw-dev-stat",
589
+ children: [jsx("span", {
590
+ className: "fw-dev-stat-label",
591
+ children: "Playback Rate"
592
+ }), jsxs("span", {
593
+ className: "fw-dev-stat-value",
594
+ children: [stats.playbackRate, "x"]
595
+ })]
596
+ }), jsxs("div", {
597
+ className: "fw-dev-stat",
598
+ children: [jsx("span", {
599
+ className: "fw-dev-stat-label",
600
+ children: "Time"
601
+ }), jsxs("span", {
602
+ className: "fw-dev-stat-value",
603
+ children: [stats.currentTime, " / ", stats.duration]
604
+ })]
605
+ }), jsxs("div", {
606
+ className: "fw-dev-stat",
607
+ children: [jsx("span", {
608
+ className: "fw-dev-stat-label",
609
+ children: "Ready State"
610
+ }), jsx("span", {
611
+ className: "fw-dev-stat-value",
612
+ children: stats.readyState
613
+ })]
614
+ }), jsxs("div", {
615
+ className: "fw-dev-stat",
616
+ children: [jsx("span", {
617
+ className: "fw-dev-stat-label",
618
+ children: "Network State"
619
+ }), jsx("span", {
620
+ className: "fw-dev-stat-value",
621
+ children: stats.networkState
622
+ })]
623
+ }), protocol && jsxs("div", {
624
+ className: "fw-dev-stat",
625
+ children: [jsx("span", {
626
+ className: "fw-dev-stat-label",
627
+ children: "Protocol"
628
+ }), jsx("span", {
629
+ className: "fw-dev-stat-value",
630
+ children: protocol
631
+ })]
632
+ }), nodeId && jsxs("div", {
633
+ className: "fw-dev-stat",
634
+ children: [jsx("span", {
635
+ className: "fw-dev-stat-label",
636
+ children: "Node ID"
637
+ }), jsx("span", {
638
+ className: "fw-dev-stat-value truncate",
639
+ style: {
640
+ maxWidth: "150px"
641
+ },
642
+ children: nodeId
643
+ })]
644
+ })]
645
+ }) : jsx("div", {
646
+ className: "fw-dev-list-empty",
647
+ children: "No video element available"
648
+ }), playerStats && jsxs("div", {
649
+ children: [jsx("div", {
650
+ className: "fw-dev-list-header fw-dev-section-header",
651
+ children: jsx("span", {
652
+ className: "fw-dev-list-title",
653
+ children: playerStats.type === "hls" ? "HLS.js Stats" : playerStats.type === "webrtc" ? "WebRTC Stats" : "Player Stats"
654
+ })
655
+ }), playerStats.type === "hls" && jsxs(Fragment, {
656
+ children: [jsxs("div", {
657
+ className: "fw-dev-stat",
658
+ children: [jsx("span", {
659
+ className: "fw-dev-stat-label",
660
+ children: "Bitrate"
661
+ }), jsx("span", {
662
+ className: "fw-dev-stat-value--accent",
663
+ children: playerStats.currentBitrate > 0 ? "".concat(Math.round(playerStats.currentBitrate / 1000), " kbps") : "N/A"
664
+ })]
665
+ }), jsxs("div", {
666
+ className: "fw-dev-stat",
667
+ children: [jsx("span", {
668
+ className: "fw-dev-stat-label",
669
+ children: "Bandwidth Est."
670
+ }), jsx("span", {
671
+ className: "fw-dev-stat-value",
672
+ children: playerStats.bandwidthEstimate > 0 ? "".concat(Math.round(playerStats.bandwidthEstimate / 1000), " kbps") : "N/A"
673
+ })]
674
+ }), jsxs("div", {
675
+ className: "fw-dev-stat",
676
+ children: [jsx("span", {
677
+ className: "fw-dev-stat-label",
678
+ children: "Level"
679
+ }), jsxs("span", {
680
+ className: "fw-dev-stat-value",
681
+ children: [playerStats.currentLevel >= 0 ? playerStats.currentLevel : "Auto", " /", " ", ((_playerStats$levels = playerStats.levels) === null || _playerStats$levels === void 0 ? void 0 : _playerStats$levels.length) || 0]
682
+ })]
683
+ }), playerStats.latency !== undefined && jsxs("div", {
684
+ className: "fw-dev-stat",
685
+ children: [jsx("span", {
686
+ className: "fw-dev-stat-label",
687
+ children: "Latency"
688
+ }), jsxs("span", {
689
+ className: playerStats.latency > 5000 ? "fw-dev-stat-value--warn" : "fw-dev-stat-value",
690
+ children: [Math.round(playerStats.latency), " ms"]
691
+ })]
692
+ })]
693
+ }), playerStats.type === "webrtc" && jsxs(Fragment, {
694
+ children: [playerStats.video && jsxs(Fragment, {
695
+ children: [jsxs("div", {
696
+ className: "fw-dev-stat",
697
+ children: [jsx("span", {
698
+ className: "fw-dev-stat-label",
699
+ children: "Video Bitrate"
700
+ }), jsx("span", {
701
+ className: "fw-dev-stat-value--accent",
702
+ children: playerStats.video.bitrate > 0 ? "".concat(Math.round(playerStats.video.bitrate / 1000), " kbps") : "N/A"
703
+ })]
704
+ }), jsxs("div", {
705
+ className: "fw-dev-stat",
706
+ children: [jsx("span", {
707
+ className: "fw-dev-stat-label",
708
+ children: "FPS"
709
+ }), jsx("span", {
710
+ className: "fw-dev-stat-value",
711
+ children: Math.round(playerStats.video.framesPerSecond || 0)
712
+ })]
713
+ }), jsxs("div", {
714
+ className: "fw-dev-stat",
715
+ children: [jsx("span", {
716
+ className: "fw-dev-stat-label",
717
+ children: "Frames"
718
+ }), jsxs("span", {
719
+ className: "fw-dev-stat-value",
720
+ children: [playerStats.video.framesDecoded, " decoded,", " ", jsxs("span", {
721
+ className: playerStats.video.frameDropRate > 1 ? "fw-dev-stat-value--bad" : "fw-dev-stat-value--good",
722
+ children: [playerStats.video.framesDropped, " dropped"]
723
+ })]
724
+ })]
725
+ }), jsxs("div", {
726
+ className: "fw-dev-stat",
727
+ children: [jsx("span", {
728
+ className: "fw-dev-stat-label",
729
+ children: "Packet Loss"
730
+ }), jsxs("span", {
731
+ className: playerStats.video.packetLossRate > 1 ? "fw-dev-stat-value--bad" : "fw-dev-stat-value--good",
732
+ children: [playerStats.video.packetLossRate.toFixed(2), "%"]
733
+ })]
734
+ }), jsxs("div", {
735
+ className: "fw-dev-stat",
736
+ children: [jsx("span", {
737
+ className: "fw-dev-stat-label",
738
+ children: "Jitter"
739
+ }), jsxs("span", {
740
+ className: playerStats.video.jitter > 30 ? "fw-dev-stat-value--warn" : "fw-dev-stat-value",
741
+ children: [playerStats.video.jitter.toFixed(1), " ms"]
742
+ })]
743
+ }), jsxs("div", {
744
+ className: "fw-dev-stat",
745
+ children: [jsx("span", {
746
+ className: "fw-dev-stat-label",
747
+ children: "Jitter Buffer"
748
+ }), jsxs("span", {
749
+ className: "fw-dev-stat-value",
750
+ children: [playerStats.video.jitterBufferDelay.toFixed(1), " ms"]
751
+ })]
752
+ })]
753
+ }), playerStats.network && jsxs("div", {
754
+ className: "fw-dev-stat",
755
+ children: [jsx("span", {
756
+ className: "fw-dev-stat-label",
757
+ children: "RTT"
758
+ }), jsxs("span", {
759
+ className: playerStats.network.rtt > 200 ? "fw-dev-stat-value--warn" : "fw-dev-stat-value",
760
+ children: [Math.round(playerStats.network.rtt), " ms"]
761
+ })]
762
+ })]
763
+ })]
764
+ }), (mistStreamInfo === null || mistStreamInfo === void 0 || (_mistStreamInfo$meta = mistStreamInfo.meta) === null || _mistStreamInfo$meta === void 0 ? void 0 : _mistStreamInfo$meta.tracks) && Object.keys(mistStreamInfo.meta.tracks).length > 0 && jsxs("div", {
765
+ children: [jsx("div", {
766
+ className: "fw-dev-list-header fw-dev-section-header",
767
+ children: jsxs("span", {
768
+ className: "fw-dev-list-title",
769
+ children: ["Tracks (", Object.keys(mistStreamInfo.meta.tracks).length, ")"]
770
+ })
771
+ }), Object.entries(mistStreamInfo.meta.tracks).map(function (_ref3) {
772
+ var _ref4 = _slicedToArray(_ref3, 2),
773
+ id = _ref4[0],
774
+ track = _ref4[1];
775
+ return jsxs("div", {
776
+ className: "fw-dev-track",
777
+ children: [jsxs("div", {
778
+ className: "fw-dev-track-header",
779
+ children: [jsx("span", {
780
+ className: cn("fw-dev-track-badge", track.type === "video" ? "fw-dev-track-badge--video" : track.type === "audio" ? "fw-dev-track-badge--audio" : "fw-dev-track-badge--other"),
781
+ children: track.type
782
+ }), jsx("span", {
783
+ className: "fw-dev-track-codec",
784
+ children: track.codec
785
+ }), jsxs("span", {
786
+ className: "fw-dev-track-id",
787
+ children: ["#", id]
788
+ })]
789
+ }), jsxs("div", {
790
+ className: "fw-dev-track-meta",
791
+ children: [track.type === "video" && track.width && track.height && jsxs("span", {
792
+ children: [track.width, "\xD7", track.height]
793
+ }), track.bps && jsxs("span", {
794
+ children: [Math.round(track.bps / 1000), " kbps"]
795
+ }), track.fpks && jsxs("span", {
796
+ children: [Math.round(track.fpks / 1000), " fps"]
797
+ }), track.type === "audio" && track.channels && jsxs("span", {
798
+ children: [track.channels, "ch"]
799
+ }), track.type === "audio" && track.rate && jsxs("span", {
800
+ children: [track.rate, " Hz"]
801
+ }), track.lang && jsx("span", {
802
+ children: track.lang
803
+ })]
804
+ })]
805
+ }, id);
806
+ })]
807
+ }), mistStreamInfo && (!((_mistStreamInfo$meta2 = mistStreamInfo.meta) !== null && _mistStreamInfo$meta2 !== void 0 && _mistStreamInfo$meta2.tracks) || Object.keys(mistStreamInfo.meta.tracks).length === 0) && jsx("div", {
808
+ className: "fw-dev-no-tracks",
809
+ children: jsxs("span", {
810
+ className: "fw-dev-no-tracks-text",
811
+ children: ["No track data available", mistStreamInfo.type && jsxs("span", {
812
+ className: "fw-dev-no-tracks-type",
813
+ children: ["(", mistStreamInfo.type, ")"]
814
+ })]
815
+ })
816
+ })]
817
+ })]
818
+ });
819
+ };
820
+
821
+ export { DevModePanel as default };
822
+ //# sourceMappingURL=DevModePanel.js.map