@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.
- package/README.md +7 -9
- package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js +359 -0
- package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
- package/dist/cjs/assets/logomark.svg.js +8 -0
- package/dist/cjs/assets/logomark.svg.js.map +1 -0
- package/dist/cjs/components/DevModePanel.js +826 -0
- package/dist/cjs/components/DevModePanel.js.map +1 -0
- package/dist/cjs/components/DvdLogo.js +200 -0
- package/dist/cjs/components/DvdLogo.js.map +1 -0
- package/dist/cjs/components/Icons.js +439 -0
- package/dist/cjs/components/Icons.js.map +1 -0
- package/dist/cjs/components/IdleScreen.js +587 -0
- package/dist/cjs/components/IdleScreen.js.map +1 -0
- package/dist/cjs/components/LoadingScreen.js +523 -0
- package/dist/cjs/components/LoadingScreen.js.map +1 -0
- package/dist/cjs/components/Player.js +420 -0
- package/dist/cjs/components/Player.js.map +1 -0
- package/dist/cjs/components/PlayerControls.js +798 -0
- package/dist/cjs/components/PlayerControls.js.map +1 -0
- package/dist/cjs/components/PlayerErrorBoundary.js +80 -0
- package/dist/cjs/components/PlayerErrorBoundary.js.map +1 -0
- package/dist/cjs/components/SeekBar.js +253 -0
- package/dist/cjs/components/SeekBar.js.map +1 -0
- package/dist/cjs/components/SkipIndicator.js +92 -0
- package/dist/cjs/components/SkipIndicator.js.map +1 -0
- package/dist/cjs/components/SpeedIndicator.js +43 -0
- package/dist/cjs/components/SpeedIndicator.js.map +1 -0
- package/dist/cjs/components/StatsPanel.js +202 -0
- package/dist/cjs/components/StatsPanel.js.map +1 -0
- package/dist/cjs/components/StreamStateOverlay.js +229 -0
- package/dist/cjs/components/StreamStateOverlay.js.map +1 -0
- package/dist/cjs/components/ThumbnailOverlay.js +86 -0
- package/dist/cjs/components/ThumbnailOverlay.js.map +1 -0
- package/dist/cjs/components/TitleOverlay.js +32 -0
- package/dist/cjs/components/TitleOverlay.js.map +1 -0
- package/dist/cjs/context/PlayerContext.js +46 -0
- package/dist/cjs/context/PlayerContext.js.map +1 -0
- package/dist/cjs/hooks/useMetaTrack.js +165 -0
- package/dist/cjs/hooks/useMetaTrack.js.map +1 -0
- package/dist/cjs/hooks/usePlaybackQuality.js +131 -0
- package/dist/cjs/hooks/usePlaybackQuality.js.map +1 -0
- package/dist/cjs/hooks/usePlayerController.js +518 -0
- package/dist/cjs/hooks/usePlayerController.js.map +1 -0
- package/dist/cjs/hooks/usePlayerSelection.js +90 -0
- package/dist/cjs/hooks/usePlayerSelection.js.map +1 -0
- package/dist/cjs/hooks/useStreamState.js +360 -0
- package/dist/cjs/hooks/useStreamState.js.map +1 -0
- package/dist/cjs/hooks/useTelemetry.js +120 -0
- package/dist/cjs/hooks/useTelemetry.js.map +1 -0
- package/dist/cjs/hooks/useViewerEndpoints.js +222 -0
- package/dist/cjs/hooks/useViewerEndpoints.js.map +1 -0
- package/dist/cjs/index.js +97 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/ui/badge.js +34 -0
- package/dist/cjs/ui/badge.js.map +1 -0
- package/dist/cjs/ui/button.js +74 -0
- package/dist/cjs/ui/button.js.map +1 -0
- package/dist/cjs/ui/context-menu.js +163 -0
- package/dist/cjs/ui/context-menu.js.map +1 -0
- package/dist/cjs/ui/slider.js +60 -0
- package/dist/cjs/ui/slider.js.map +1 -0
- package/dist/esm/_virtual/_rollupPluginBabelHelpers.js +329 -0
- package/dist/esm/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
- package/dist/esm/assets/logomark.svg.js +4 -0
- package/dist/esm/assets/logomark.svg.js.map +1 -0
- package/dist/esm/components/DevModePanel.js +822 -0
- package/dist/esm/components/DevModePanel.js.map +1 -0
- package/dist/esm/components/DvdLogo.js +196 -0
- package/dist/esm/components/DvdLogo.js.map +1 -0
- package/dist/esm/components/Icons.js +421 -0
- package/dist/esm/components/Icons.js.map +1 -0
- package/dist/esm/components/IdleScreen.js +582 -0
- package/dist/esm/components/IdleScreen.js.map +1 -0
- package/dist/esm/components/LoadingScreen.js +519 -0
- package/dist/esm/components/LoadingScreen.js.map +1 -0
- package/dist/esm/components/Player.js +416 -0
- package/dist/esm/components/Player.js.map +1 -0
- package/dist/esm/components/PlayerControls.js +794 -0
- package/dist/esm/components/PlayerControls.js.map +1 -0
- package/dist/esm/components/PlayerErrorBoundary.js +76 -0
- package/dist/esm/components/PlayerErrorBoundary.js.map +1 -0
- package/dist/esm/components/SeekBar.js +249 -0
- package/dist/esm/components/SeekBar.js.map +1 -0
- package/dist/esm/components/SkipIndicator.js +88 -0
- package/dist/esm/components/SkipIndicator.js.map +1 -0
- package/dist/esm/components/SpeedIndicator.js +39 -0
- package/dist/esm/components/SpeedIndicator.js.map +1 -0
- package/dist/esm/components/StatsPanel.js +198 -0
- package/dist/esm/components/StatsPanel.js.map +1 -0
- package/dist/esm/components/StreamStateOverlay.js +224 -0
- package/dist/esm/components/StreamStateOverlay.js.map +1 -0
- package/dist/esm/components/ThumbnailOverlay.js +82 -0
- package/dist/esm/components/ThumbnailOverlay.js.map +1 -0
- package/dist/esm/components/TitleOverlay.js +28 -0
- package/dist/esm/components/TitleOverlay.js.map +1 -0
- package/dist/esm/context/PlayerContext.js +41 -0
- package/dist/esm/context/PlayerContext.js.map +1 -0
- package/dist/esm/hooks/useMetaTrack.js +163 -0
- package/dist/esm/hooks/useMetaTrack.js.map +1 -0
- package/dist/esm/hooks/usePlaybackQuality.js +129 -0
- package/dist/esm/hooks/usePlaybackQuality.js.map +1 -0
- package/dist/esm/hooks/usePlayerController.js +516 -0
- package/dist/esm/hooks/usePlayerController.js.map +1 -0
- package/dist/esm/hooks/usePlayerSelection.js +88 -0
- package/dist/esm/hooks/usePlayerSelection.js.map +1 -0
- package/dist/esm/hooks/useStreamState.js +358 -0
- package/dist/esm/hooks/useStreamState.js.map +1 -0
- package/dist/esm/hooks/useTelemetry.js +118 -0
- package/dist/esm/hooks/useTelemetry.js.map +1 -0
- package/dist/esm/hooks/useViewerEndpoints.js +220 -0
- package/dist/esm/hooks/useViewerEndpoints.js.map +1 -0
- package/dist/esm/index.js +23 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/ui/badge.js +31 -0
- package/dist/esm/ui/badge.js.map +1 -0
- package/dist/esm/ui/button.js +52 -0
- package/dist/esm/ui/button.js.map +1 -0
- package/dist/esm/ui/context-menu.js +132 -0
- package/dist/esm/ui/context-menu.js.map +1 -0
- package/dist/esm/ui/slider.js +38 -0
- package/dist/esm/ui/slider.js.map +1 -0
- package/dist/types/components/DvdLogo.d.ts +1 -1
- package/dist/types/components/Icons.d.ts +1 -1
- package/dist/types/components/Player.d.ts +1 -1
- package/dist/types/components/PlayerErrorBoundary.d.ts +2 -1
- package/dist/types/components/StreamStateOverlay.d.ts +2 -2
- package/dist/types/components/SubtitleRenderer.d.ts +2 -2
- package/dist/types/context/PlayerContext.d.ts +2 -2
- package/dist/types/context/index.d.ts +2 -2
- package/dist/types/hooks/useMetaTrack.d.ts +3 -3
- package/dist/types/hooks/usePlaybackQuality.d.ts +2 -2
- package/dist/types/hooks/usePlayerController.d.ts +26 -3
- package/dist/types/hooks/usePlayerSelection.d.ts +1 -1
- package/dist/types/hooks/useStreamState.d.ts +1 -1
- package/dist/types/hooks/useTelemetry.d.ts +1 -1
- package/dist/types/hooks/useViewerEndpoints.d.ts +3 -3
- package/dist/types/index.d.ts +28 -28
- package/dist/types/types.d.ts +3 -3
- package/dist/types/ui/select.d.ts +1 -1
- package/package.json +22 -14
- package/src/components/DevModePanel.tsx +244 -143
- package/src/components/DvdLogo.tsx +1 -1
- package/src/components/Icons.tsx +105 -25
- package/src/components/IdleScreen.tsx +262 -128
- package/src/components/LoadingScreen.tsx +169 -151
- package/src/components/LogoOverlay.tsx +3 -6
- package/src/components/Player.tsx +126 -59
- package/src/components/PlayerControls.tsx +384 -272
- package/src/components/PlayerErrorBoundary.tsx +7 -13
- package/src/components/SeekBar.tsx +96 -88
- package/src/components/SkipIndicator.tsx +2 -12
- package/src/components/SpeedIndicator.tsx +2 -11
- package/src/components/StatsPanel.tsx +31 -22
- package/src/components/StreamStateOverlay.tsx +105 -49
- package/src/components/SubtitleRenderer.tsx +29 -29
- package/src/components/ThumbnailOverlay.tsx +5 -6
- package/src/components/TitleOverlay.tsx +2 -8
- package/src/context/PlayerContext.tsx +4 -8
- package/src/context/index.ts +3 -3
- package/src/hooks/useMetaTrack.ts +27 -27
- package/src/hooks/usePlaybackQuality.ts +3 -3
- package/src/hooks/usePlayerController.ts +246 -138
- package/src/hooks/usePlayerSelection.ts +6 -6
- package/src/hooks/useStreamState.ts +51 -56
- package/src/hooks/useTelemetry.ts +18 -3
- package/src/hooks/useViewerEndpoints.ts +34 -23
- package/src/index.tsx +36 -28
- package/src/types.ts +8 -8
- package/src/ui/badge.tsx +6 -5
- package/src/ui/button.tsx +9 -8
- package/src/ui/context-menu.tsx +42 -61
- package/src/ui/select.tsx +13 -7
- package/src/ui/slider.tsx +18 -29
- package/dist/types/components/players/DashJsPlayer.d.ts +0 -18
- package/dist/types/components/players/HlsJsPlayer.d.ts +0 -18
- package/dist/types/components/players/MewsWsPlayer/index.d.ts +0 -18
- package/dist/types/components/players/MistPlayer.d.ts +0 -20
- package/dist/types/components/players/MistWebRTCPlayer/index.d.ts +0 -20
- package/dist/types/components/players/NativePlayer.d.ts +0 -19
- package/dist/types/components/players/VideoJsPlayer.d.ts +0 -18
- package/src/components/players/DashJsPlayer.tsx +0 -56
- package/src/components/players/HlsJsPlayer.tsx +0 -56
- package/src/components/players/MewsWsPlayer/index.tsx +0 -56
- package/src/components/players/MistPlayer.tsx +0 -60
- package/src/components/players/MistWebRTCPlayer/index.tsx +0 -59
- package/src/components/players/NativePlayer.tsx +0 -58
- package/src/components/players/VideoJsPlayer.tsx +0 -56
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import type { StreamStatus } from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { StreamStatus } from "../types";
|
|
3
3
|
|
|
4
4
|
export interface StreamStateOverlayProps {
|
|
5
5
|
/** Current stream status */
|
|
@@ -23,43 +23,93 @@ function StatusIcon({ status }: { status: StreamStatus }) {
|
|
|
23
23
|
const iconClass = "w-5 h-5";
|
|
24
24
|
|
|
25
25
|
switch (status) {
|
|
26
|
-
case
|
|
26
|
+
case "ONLINE":
|
|
27
27
|
return (
|
|
28
|
-
<svg
|
|
28
|
+
<svg
|
|
29
|
+
className={`${iconClass} fw-status-online`}
|
|
30
|
+
fill="none"
|
|
31
|
+
viewBox="0 0 24 24"
|
|
32
|
+
stroke="currentColor"
|
|
33
|
+
>
|
|
29
34
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
30
35
|
</svg>
|
|
31
36
|
);
|
|
32
37
|
|
|
33
|
-
case
|
|
38
|
+
case "OFFLINE":
|
|
34
39
|
return (
|
|
35
|
-
<svg
|
|
36
|
-
|
|
40
|
+
<svg
|
|
41
|
+
className={`${iconClass} fw-status-offline`}
|
|
42
|
+
fill="none"
|
|
43
|
+
viewBox="0 0 24 24"
|
|
44
|
+
stroke="currentColor"
|
|
45
|
+
>
|
|
46
|
+
<path
|
|
47
|
+
strokeLinecap="round"
|
|
48
|
+
strokeLinejoin="round"
|
|
49
|
+
strokeWidth={2}
|
|
50
|
+
d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a4.978 4.978 0 01-1.414-2.83m-1.414 5.658a9 9 0 01-2.167-9.238m7.824 2.167a1 1 0 111.414 1.414m-1.414-1.414L3 3m8.293 8.293l1.414 1.414"
|
|
51
|
+
/>
|
|
37
52
|
</svg>
|
|
38
53
|
);
|
|
39
54
|
|
|
40
|
-
case
|
|
41
|
-
case
|
|
42
|
-
case
|
|
55
|
+
case "INITIALIZING":
|
|
56
|
+
case "BOOTING":
|
|
57
|
+
case "WAITING_FOR_DATA":
|
|
43
58
|
return (
|
|
44
|
-
<svg
|
|
45
|
-
|
|
46
|
-
|
|
59
|
+
<svg
|
|
60
|
+
className={`${iconClass} fw-status-warning animate-spin`}
|
|
61
|
+
fill="none"
|
|
62
|
+
viewBox="0 0 24 24"
|
|
63
|
+
>
|
|
64
|
+
<circle
|
|
65
|
+
className="opacity-25"
|
|
66
|
+
cx="12"
|
|
67
|
+
cy="12"
|
|
68
|
+
r="10"
|
|
69
|
+
stroke="currentColor"
|
|
70
|
+
strokeWidth="4"
|
|
71
|
+
/>
|
|
72
|
+
<path
|
|
73
|
+
className="opacity-75"
|
|
74
|
+
fill="currentColor"
|
|
75
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
76
|
+
/>
|
|
47
77
|
</svg>
|
|
48
78
|
);
|
|
49
79
|
|
|
50
|
-
case
|
|
80
|
+
case "SHUTTING_DOWN":
|
|
51
81
|
return (
|
|
52
|
-
<svg
|
|
53
|
-
|
|
82
|
+
<svg
|
|
83
|
+
className={`${iconClass} fw-status-warning`}
|
|
84
|
+
fill="none"
|
|
85
|
+
viewBox="0 0 24 24"
|
|
86
|
+
stroke="currentColor"
|
|
87
|
+
>
|
|
88
|
+
<path
|
|
89
|
+
strokeLinecap="round"
|
|
90
|
+
strokeLinejoin="round"
|
|
91
|
+
strokeWidth={2}
|
|
92
|
+
d="M13 10V3L4 14h7v7l9-11h-7z"
|
|
93
|
+
/>
|
|
54
94
|
</svg>
|
|
55
95
|
);
|
|
56
96
|
|
|
57
|
-
case
|
|
58
|
-
case
|
|
97
|
+
case "ERROR":
|
|
98
|
+
case "INVALID":
|
|
59
99
|
default:
|
|
60
100
|
return (
|
|
61
|
-
<svg
|
|
62
|
-
|
|
101
|
+
<svg
|
|
102
|
+
className={`${iconClass} fw-status-offline`}
|
|
103
|
+
fill="none"
|
|
104
|
+
viewBox="0 0 24 24"
|
|
105
|
+
stroke="currentColor"
|
|
106
|
+
>
|
|
107
|
+
<path
|
|
108
|
+
strokeLinecap="round"
|
|
109
|
+
strokeLinejoin="round"
|
|
110
|
+
strokeWidth={2}
|
|
111
|
+
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
|
112
|
+
/>
|
|
63
113
|
</svg>
|
|
64
114
|
);
|
|
65
115
|
}
|
|
@@ -70,15 +120,24 @@ function StatusIcon({ status }: { status: StreamStatus }) {
|
|
|
70
120
|
*/
|
|
71
121
|
function getStatusLabel(status: StreamStatus): string {
|
|
72
122
|
switch (status) {
|
|
73
|
-
case
|
|
74
|
-
|
|
75
|
-
case
|
|
76
|
-
|
|
77
|
-
case
|
|
78
|
-
|
|
79
|
-
case
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
case "ONLINE":
|
|
124
|
+
return "ONLINE";
|
|
125
|
+
case "OFFLINE":
|
|
126
|
+
return "OFFLINE";
|
|
127
|
+
case "INITIALIZING":
|
|
128
|
+
return "INITIALIZING";
|
|
129
|
+
case "BOOTING":
|
|
130
|
+
return "STARTING";
|
|
131
|
+
case "WAITING_FOR_DATA":
|
|
132
|
+
return "WAITING";
|
|
133
|
+
case "SHUTTING_DOWN":
|
|
134
|
+
return "ENDING";
|
|
135
|
+
case "ERROR":
|
|
136
|
+
return "ERROR";
|
|
137
|
+
case "INVALID":
|
|
138
|
+
return "INVALID";
|
|
139
|
+
default:
|
|
140
|
+
return "STATUS";
|
|
82
141
|
}
|
|
83
142
|
}
|
|
84
143
|
|
|
@@ -94,26 +153,26 @@ export const StreamStateOverlay: React.FC<StreamStateOverlayProps> = ({
|
|
|
94
153
|
percentage,
|
|
95
154
|
onRetry,
|
|
96
155
|
visible = true,
|
|
97
|
-
className =
|
|
156
|
+
className = "",
|
|
98
157
|
}) => {
|
|
99
|
-
if (!visible || status ===
|
|
158
|
+
if (!visible || status === "ONLINE") {
|
|
100
159
|
return null;
|
|
101
160
|
}
|
|
102
161
|
|
|
103
|
-
const showRetry = status ===
|
|
104
|
-
const showProgress = status ===
|
|
162
|
+
const showRetry = status === "ERROR" || status === "INVALID" || status === "OFFLINE";
|
|
163
|
+
const showProgress = status === "INITIALIZING" && percentage !== undefined;
|
|
105
164
|
|
|
106
165
|
return (
|
|
107
166
|
<div
|
|
108
167
|
className={`absolute inset-0 z-20 flex items-center justify-center ${className}`}
|
|
109
|
-
style={{ backgroundColor:
|
|
168
|
+
style={{ backgroundColor: "hsl(var(--tn-bg-dark) / 0.8)", backdropFilter: "blur(4px)" }}
|
|
110
169
|
role="status"
|
|
111
170
|
aria-live="polite"
|
|
112
171
|
>
|
|
113
172
|
{/* Slab container - no rounded corners, seam borders */}
|
|
114
173
|
<div
|
|
115
174
|
className="fw-slab w-[280px] max-w-[90%]"
|
|
116
|
-
style={{ backgroundColor:
|
|
175
|
+
style={{ backgroundColor: "hsl(var(--tn-bg) / 0.95)" }}
|
|
117
176
|
>
|
|
118
177
|
{/* Slab header - status label with icon */}
|
|
119
178
|
<div className="fw-slab-header flex items-center gap-2">
|
|
@@ -123,7 +182,7 @@ export const StreamStateOverlay: React.FC<StreamStateOverlayProps> = ({
|
|
|
123
182
|
|
|
124
183
|
{/* Slab body - message and progress */}
|
|
125
184
|
<div className="fw-slab-body">
|
|
126
|
-
<p className="text-sm" style={{ color:
|
|
185
|
+
<p className="text-sm" style={{ color: "hsl(var(--tn-fg))" }}>
|
|
127
186
|
{message}
|
|
128
187
|
</p>
|
|
129
188
|
|
|
@@ -132,33 +191,30 @@ export const StreamStateOverlay: React.FC<StreamStateOverlayProps> = ({
|
|
|
132
191
|
{/* Progress bar - no rounded corners */}
|
|
133
192
|
<div
|
|
134
193
|
className="h-1.5 w-full overflow-hidden"
|
|
135
|
-
style={{ backgroundColor:
|
|
194
|
+
style={{ backgroundColor: "hsl(var(--tn-bg-visual))" }}
|
|
136
195
|
>
|
|
137
196
|
<div
|
|
138
197
|
className="h-full transition-all duration-300"
|
|
139
198
|
style={{
|
|
140
199
|
width: `${Math.min(100, percentage)}%`,
|
|
141
|
-
backgroundColor:
|
|
200
|
+
backgroundColor: "hsl(var(--tn-yellow))",
|
|
142
201
|
}}
|
|
143
202
|
/>
|
|
144
203
|
</div>
|
|
145
|
-
<p
|
|
146
|
-
className="mt-1.5 text-xs font-mono"
|
|
147
|
-
style={{ color: 'hsl(var(--tn-fg-dark))' }}
|
|
148
|
-
>
|
|
204
|
+
<p className="mt-1.5 text-xs font-mono" style={{ color: "hsl(var(--tn-fg-dark))" }}>
|
|
149
205
|
{Math.round(percentage)}%
|
|
150
206
|
</p>
|
|
151
207
|
</div>
|
|
152
208
|
)}
|
|
153
209
|
|
|
154
|
-
{status ===
|
|
155
|
-
<p className="mt-2 text-xs" style={{ color:
|
|
210
|
+
{status === "OFFLINE" && (
|
|
211
|
+
<p className="mt-2 text-xs" style={{ color: "hsl(var(--tn-fg-dark))" }}>
|
|
156
212
|
The stream will start when the broadcaster goes live
|
|
157
213
|
</p>
|
|
158
214
|
)}
|
|
159
215
|
|
|
160
|
-
{(status ===
|
|
161
|
-
<p className="mt-2 text-xs" style={{ color:
|
|
216
|
+
{(status === "BOOTING" || status === "WAITING_FOR_DATA") && (
|
|
217
|
+
<p className="mt-2 text-xs" style={{ color: "hsl(var(--tn-fg-dark))" }}>
|
|
162
218
|
Please wait while the stream prepares...
|
|
163
219
|
</p>
|
|
164
220
|
)}
|
|
@@ -167,11 +223,11 @@ export const StreamStateOverlay: React.FC<StreamStateOverlayProps> = ({
|
|
|
167
223
|
{!showRetry && (
|
|
168
224
|
<div
|
|
169
225
|
className="mt-3 flex items-center gap-2 text-xs"
|
|
170
|
-
style={{ color:
|
|
226
|
+
style={{ color: "hsl(var(--tn-fg-dark))" }}
|
|
171
227
|
>
|
|
172
228
|
<span
|
|
173
229
|
className="h-1.5 w-1.5 animate-pulse"
|
|
174
|
-
style={{ backgroundColor:
|
|
230
|
+
style={{ backgroundColor: "hsl(var(--tn-cyan))" }}
|
|
175
231
|
/>
|
|
176
232
|
<span>Checking stream status...</span>
|
|
177
233
|
</div>
|
|
@@ -185,7 +241,7 @@ export const StreamStateOverlay: React.FC<StreamStateOverlayProps> = ({
|
|
|
185
241
|
type="button"
|
|
186
242
|
onClick={onRetry}
|
|
187
243
|
className="fw-btn-flush py-2.5 text-xs font-medium uppercase tracking-wide"
|
|
188
|
-
style={{ color:
|
|
244
|
+
style={{ color: "hsl(var(--tn-blue))" }}
|
|
189
245
|
aria-label="Retry connection"
|
|
190
246
|
>
|
|
191
247
|
Retry Connection
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { useEffect, useState, useRef } from
|
|
2
|
-
import type { SubtitleCue, MetaTrackEvent } from
|
|
1
|
+
import React, { useEffect, useState, useRef } from "react";
|
|
2
|
+
import type { SubtitleCue, MetaTrackEvent } from "../types";
|
|
3
3
|
|
|
4
4
|
export interface SubtitleRendererProps {
|
|
5
5
|
/** Current video playback time in seconds */
|
|
@@ -40,48 +40,48 @@ export interface SubtitleStyle {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const DEFAULT_STYLE: SubtitleStyle = {
|
|
43
|
-
fontSize:
|
|
44
|
-
fontFamily:
|
|
45
|
-
color:
|
|
46
|
-
backgroundColor:
|
|
47
|
-
textShadow:
|
|
48
|
-
bottom:
|
|
49
|
-
maxWidth:
|
|
50
|
-
padding:
|
|
51
|
-
borderRadius:
|
|
43
|
+
fontSize: "1.5rem",
|
|
44
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
45
|
+
color: "white",
|
|
46
|
+
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
|
47
|
+
textShadow: "2px 2px 4px rgba(0, 0, 0, 0.5)",
|
|
48
|
+
bottom: "5%",
|
|
49
|
+
maxWidth: "90%",
|
|
50
|
+
padding: "0.5em 1em",
|
|
51
|
+
borderRadius: "4px",
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Parse subtitle cue from meta track event data
|
|
56
56
|
*/
|
|
57
57
|
function parseSubtitleCue(data: unknown): SubtitleCue | null {
|
|
58
|
-
if (typeof data !==
|
|
58
|
+
if (typeof data !== "object" || data === null) return null;
|
|
59
59
|
|
|
60
60
|
const obj = data as Record<string, unknown>;
|
|
61
61
|
|
|
62
62
|
// Extract text
|
|
63
|
-
const text = typeof obj.text ===
|
|
63
|
+
const text = typeof obj.text === "string" ? obj.text : String(obj.text ?? "");
|
|
64
64
|
if (!text) return null;
|
|
65
65
|
|
|
66
66
|
// Extract timing
|
|
67
67
|
let startTime = 0;
|
|
68
68
|
let endTime = Infinity;
|
|
69
69
|
|
|
70
|
-
if (
|
|
71
|
-
else if (
|
|
70
|
+
if ("startTime" in obj) startTime = Number(obj.startTime);
|
|
71
|
+
else if ("start" in obj) startTime = Number(obj.start);
|
|
72
72
|
|
|
73
|
-
if (
|
|
74
|
-
else if (
|
|
73
|
+
if ("endTime" in obj) endTime = Number(obj.endTime);
|
|
74
|
+
else if ("end" in obj) endTime = Number(obj.end);
|
|
75
75
|
|
|
76
76
|
// Extract ID
|
|
77
|
-
const id = typeof obj.id ===
|
|
77
|
+
const id = typeof obj.id === "string" ? obj.id : String(Date.now());
|
|
78
78
|
|
|
79
79
|
return {
|
|
80
80
|
id,
|
|
81
81
|
text,
|
|
82
82
|
startTime,
|
|
83
83
|
endTime,
|
|
84
|
-
lang: typeof obj.lang ===
|
|
84
|
+
lang: typeof obj.lang === "string" ? obj.lang : undefined,
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -121,10 +121,10 @@ export const SubtitleRenderer: React.FC<SubtitleRendererProps> = ({
|
|
|
121
121
|
subscribeToMetaTrack,
|
|
122
122
|
metaTrackId,
|
|
123
123
|
style: customStyle,
|
|
124
|
-
className =
|
|
124
|
+
className = "",
|
|
125
125
|
}) => {
|
|
126
126
|
const [liveCues, setLiveCues] = useState<SubtitleCue[]>([]);
|
|
127
|
-
const [displayedText, setDisplayedText] = useState<string>(
|
|
127
|
+
const [displayedText, setDisplayedText] = useState<string>("");
|
|
128
128
|
const lastCueIdRef = useRef<string | null>(null);
|
|
129
129
|
|
|
130
130
|
const style = { ...DEFAULT_STYLE, ...customStyle };
|
|
@@ -139,12 +139,12 @@ export const SubtitleRenderer: React.FC<SubtitleRendererProps> = ({
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
const handleMetaEvent = (event: MetaTrackEvent) => {
|
|
142
|
-
if (event.type ===
|
|
142
|
+
if (event.type === "subtitle") {
|
|
143
143
|
const cue = parseSubtitleCue(event.data);
|
|
144
144
|
if (cue) {
|
|
145
|
-
setLiveCues(prev => {
|
|
145
|
+
setLiveCues((prev) => {
|
|
146
146
|
// Deduplicate by ID
|
|
147
|
-
const existing = prev.find(c => c.id === cue.id);
|
|
147
|
+
const existing = prev.find((c) => c.id === cue.id);
|
|
148
148
|
if (existing) return prev;
|
|
149
149
|
|
|
150
150
|
// Keep last 50 cues max
|
|
@@ -165,13 +165,13 @@ export const SubtitleRenderer: React.FC<SubtitleRendererProps> = ({
|
|
|
165
165
|
// Find active cue based on current time
|
|
166
166
|
useEffect(() => {
|
|
167
167
|
if (!enabled) {
|
|
168
|
-
setDisplayedText(
|
|
168
|
+
setDisplayedText("");
|
|
169
169
|
return;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
// Find cue that matches current time
|
|
173
173
|
const currentTimeMs = currentTime * 1000; // Convert to ms if needed
|
|
174
|
-
const activeCue = allCues.find(cue => {
|
|
174
|
+
const activeCue = allCues.find((cue) => {
|
|
175
175
|
const start = cue.startTime;
|
|
176
176
|
const end = cue.endTime;
|
|
177
177
|
return currentTimeMs >= start && currentTimeMs < end;
|
|
@@ -181,7 +181,7 @@ export const SubtitleRenderer: React.FC<SubtitleRendererProps> = ({
|
|
|
181
181
|
setDisplayedText(activeCue.text);
|
|
182
182
|
lastCueIdRef.current = activeCue.id;
|
|
183
183
|
} else {
|
|
184
|
-
setDisplayedText(
|
|
184
|
+
setDisplayedText("");
|
|
185
185
|
lastCueIdRef.current = null;
|
|
186
186
|
}
|
|
187
187
|
}, [enabled, currentTime, allCues]);
|
|
@@ -190,9 +190,9 @@ export const SubtitleRenderer: React.FC<SubtitleRendererProps> = ({
|
|
|
190
190
|
useEffect(() => {
|
|
191
191
|
const currentTimeMs = currentTime * 1000;
|
|
192
192
|
|
|
193
|
-
setLiveCues(prev => {
|
|
193
|
+
setLiveCues((prev) => {
|
|
194
194
|
// Remove cues that are more than 30 seconds old
|
|
195
|
-
return prev.filter(cue => {
|
|
195
|
+
return prev.filter((cue) => {
|
|
196
196
|
const endTime = cue.endTime === Infinity ? cue.startTime + 10000 : cue.endTime;
|
|
197
197
|
return endTime >= currentTimeMs - 30000;
|
|
198
198
|
});
|
|
@@ -9,7 +9,7 @@ const ThumbnailOverlay: React.FC<ThumbnailOverlayProps> = ({
|
|
|
9
9
|
message,
|
|
10
10
|
showUnmuteMessage = false,
|
|
11
11
|
style,
|
|
12
|
-
className
|
|
12
|
+
className,
|
|
13
13
|
}) => {
|
|
14
14
|
const handleClick = (e: React.MouseEvent | React.KeyboardEvent) => {
|
|
15
15
|
e.stopPropagation();
|
|
@@ -29,8 +29,9 @@ const ThumbnailOverlay: React.FC<ThumbnailOverlayProps> = ({
|
|
|
29
29
|
}}
|
|
30
30
|
style={style}
|
|
31
31
|
className={cn(
|
|
32
|
-
"fw-player-thumbnail relative flex h-full min-h-[280px] w-full cursor-pointer items-center justify-center overflow-hidden rounded-xl bg-slate-950 text-foreground outline-none transition focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
33
|
-
|
|
32
|
+
"fw-player-thumbnail relative flex h-full min-h-[280px] w-full cursor-pointer items-center justify-center overflow-hidden rounded-xl bg-slate-950 text-foreground outline-none transition focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
34
35
|
>
|
|
35
36
|
{thumbnailUrl && (
|
|
36
37
|
<div
|
|
@@ -73,9 +74,7 @@ const ThumbnailOverlay: React.FC<ThumbnailOverlayProps> = ({
|
|
|
73
74
|
</svg>
|
|
74
75
|
</Button>
|
|
75
76
|
<div className="w-full rounded-lg border border-white/10 bg-black/70 p-5 text-white shadow-inner backdrop-blur">
|
|
76
|
-
<p className="text-base font-semibold text-primary">
|
|
77
|
-
{message ?? "Click to play"}
|
|
78
|
-
</p>
|
|
77
|
+
<p className="text-base font-semibold text-primary">{message ?? "Click to play"}</p>
|
|
79
78
|
<p className="mt-1 text-xs text-white/70">
|
|
80
79
|
{message ? "Start streaming instantly" : "Jump into the live feed"}
|
|
81
80
|
</p>
|
|
@@ -31,15 +31,9 @@ const TitleOverlay: React.FC<TitleOverlayProps> = ({
|
|
|
31
31
|
className
|
|
32
32
|
)}
|
|
33
33
|
>
|
|
34
|
-
{title &&
|
|
35
|
-
<h2 className="text-white text-sm font-medium truncate max-w-[80%]">
|
|
36
|
-
{title}
|
|
37
|
-
</h2>
|
|
38
|
-
)}
|
|
34
|
+
{title && <h2 className="text-white text-sm font-medium truncate max-w-[80%]">{title}</h2>}
|
|
39
35
|
{description && (
|
|
40
|
-
<p className="text-white/70 text-xs mt-0.5 line-clamp-2 max-w-[70%]">
|
|
41
|
-
{description}
|
|
42
|
-
</p>
|
|
36
|
+
<p className="text-white/70 text-xs mt-0.5 line-clamp-2 max-w-[70%]">{description}</p>
|
|
43
37
|
)}
|
|
44
38
|
</div>
|
|
45
39
|
);
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
* ```
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import React, { createContext, useContext, type ReactNode } from
|
|
15
|
+
import React, { createContext, useContext, type ReactNode } from "react";
|
|
16
16
|
import {
|
|
17
17
|
usePlayerController,
|
|
18
18
|
type UsePlayerControllerConfig,
|
|
19
19
|
type UsePlayerControllerReturn,
|
|
20
|
-
} from
|
|
20
|
+
} from "../hooks/usePlayerController";
|
|
21
21
|
|
|
22
22
|
// Context holds the full hook return value
|
|
23
23
|
const PlayerContext = createContext<UsePlayerControllerReturn | null>(null);
|
|
@@ -35,11 +35,7 @@ export interface PlayerProviderProps {
|
|
|
35
35
|
export function PlayerProvider({ children, config }: PlayerProviderProps) {
|
|
36
36
|
const playerController = usePlayerController(config);
|
|
37
37
|
|
|
38
|
-
return
|
|
39
|
-
<PlayerContext.Provider value={playerController}>
|
|
40
|
-
{children}
|
|
41
|
-
</PlayerContext.Provider>
|
|
42
|
-
);
|
|
38
|
+
return <PlayerContext.Provider value={playerController}>{children}</PlayerContext.Provider>;
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
/**
|
|
@@ -49,7 +45,7 @@ export function PlayerProvider({ children, config }: PlayerProviderProps) {
|
|
|
49
45
|
export function usePlayerContext(): UsePlayerControllerReturn {
|
|
50
46
|
const context = useContext(PlayerContext);
|
|
51
47
|
if (!context) {
|
|
52
|
-
throw new Error(
|
|
48
|
+
throw new Error("usePlayerContext must be used within a PlayerProvider");
|
|
53
49
|
}
|
|
54
50
|
return context;
|
|
55
51
|
}
|
package/src/context/index.ts
CHANGED
|
@@ -6,6 +6,6 @@ export {
|
|
|
6
6
|
PlayerProvider,
|
|
7
7
|
usePlayerContext,
|
|
8
8
|
usePlayerContextOptional,
|
|
9
|
-
PlayerContext
|
|
10
|
-
} from
|
|
11
|
-
export type { PlayerContextValue, UsePlayerControllerConfig } from
|
|
9
|
+
PlayerContext,
|
|
10
|
+
} from "./PlayerContext";
|
|
11
|
+
export type { PlayerContextValue, UsePlayerControllerConfig } from "./PlayerContext";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { useEffect, useState, useRef, useCallback } from
|
|
2
|
-
import { MetaTrackManager, type MetaTrackEvent } from
|
|
3
|
-
import type { UseMetaTrackOptions } from
|
|
1
|
+
import { useEffect, useState, useRef, useCallback } from "react";
|
|
2
|
+
import { MetaTrackManager, type MetaTrackEvent } from "@livepeer-frameworks/player-core";
|
|
3
|
+
import type { UseMetaTrackOptions } from "../types";
|
|
4
4
|
|
|
5
5
|
export interface UseMetaTrackReturn {
|
|
6
6
|
/** Whether connected to MistServer WebSocket */
|
|
7
7
|
isConnected: boolean;
|
|
8
8
|
/** Connection state */
|
|
9
|
-
connectionState:
|
|
9
|
+
connectionState: "disconnected" | "connecting" | "connected" | "reconnecting";
|
|
10
10
|
/** List of subscribed track IDs */
|
|
11
11
|
subscribedTracks: string[];
|
|
12
12
|
/** Subscribe to a meta track */
|
|
@@ -54,15 +54,12 @@ export interface UseMetaTrackReturn {
|
|
|
54
54
|
* ```
|
|
55
55
|
*/
|
|
56
56
|
export function useMetaTrack(options: UseMetaTrackOptions): UseMetaTrackReturn {
|
|
57
|
-
const {
|
|
58
|
-
mistBaseUrl,
|
|
59
|
-
streamName,
|
|
60
|
-
subscriptions: initialSubscriptions,
|
|
61
|
-
enabled = true,
|
|
62
|
-
} = options;
|
|
57
|
+
const { mistBaseUrl, streamName, subscriptions: initialSubscriptions, enabled = true } = options;
|
|
63
58
|
|
|
64
59
|
const [isConnected, setIsConnected] = useState(false);
|
|
65
|
-
const [connectionState, setConnectionState] = useState<
|
|
60
|
+
const [connectionState, setConnectionState] = useState<
|
|
61
|
+
"disconnected" | "connecting" | "connected" | "reconnecting"
|
|
62
|
+
>("disconnected");
|
|
66
63
|
const [subscribedTracks, setSubscribedTracks] = useState<string[]>([]);
|
|
67
64
|
const managerRef = useRef<MetaTrackManager | null>(null);
|
|
68
65
|
|
|
@@ -74,7 +71,7 @@ export function useMetaTrack(options: UseMetaTrackOptions): UseMetaTrackReturn {
|
|
|
74
71
|
managerRef.current = null;
|
|
75
72
|
}
|
|
76
73
|
setIsConnected(false);
|
|
77
|
-
setConnectionState(
|
|
74
|
+
setConnectionState("disconnected");
|
|
78
75
|
return;
|
|
79
76
|
}
|
|
80
77
|
|
|
@@ -90,7 +87,7 @@ export function useMetaTrack(options: UseMetaTrackOptions): UseMetaTrackReturn {
|
|
|
90
87
|
if (managerRef.current) {
|
|
91
88
|
const state = managerRef.current.getState();
|
|
92
89
|
setConnectionState(state);
|
|
93
|
-
setIsConnected(state ===
|
|
90
|
+
setIsConnected(state === "connected");
|
|
94
91
|
setSubscribedTracks(managerRef.current.getSubscribedTracks());
|
|
95
92
|
}
|
|
96
93
|
};
|
|
@@ -108,28 +105,31 @@ export function useMetaTrack(options: UseMetaTrackOptions): UseMetaTrackReturn {
|
|
|
108
105
|
managerRef.current = null;
|
|
109
106
|
}
|
|
110
107
|
setIsConnected(false);
|
|
111
|
-
setConnectionState(
|
|
108
|
+
setConnectionState("disconnected");
|
|
112
109
|
};
|
|
113
110
|
}, [enabled, mistBaseUrl, streamName, initialSubscriptions]);
|
|
114
111
|
|
|
115
112
|
/**
|
|
116
113
|
* Subscribe to a meta track
|
|
117
114
|
*/
|
|
118
|
-
const subscribe = useCallback(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
const subscribe = useCallback(
|
|
116
|
+
(trackId: string, callback: (event: MetaTrackEvent) => void): (() => void) => {
|
|
117
|
+
if (!managerRef.current) {
|
|
118
|
+
return () => {};
|
|
119
|
+
}
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
const unsubscribe = managerRef.current.subscribe(trackId, callback);
|
|
122
|
+
setSubscribedTracks(managerRef.current.getSubscribedTracks());
|
|
125
123
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
124
|
+
return () => {
|
|
125
|
+
unsubscribe();
|
|
126
|
+
if (managerRef.current) {
|
|
127
|
+
setSubscribedTracks(managerRef.current.getSubscribedTracks());
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
[]
|
|
132
|
+
);
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
135
|
* Unsubscribe from a meta track
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useEffect, useState, useRef, useCallback } from
|
|
2
|
-
import { QualityMonitor, type PlaybackQuality } from
|
|
3
|
-
import type { UsePlaybackQualityOptions } from
|
|
1
|
+
import { useEffect, useState, useRef, useCallback } from "react";
|
|
2
|
+
import { QualityMonitor, type PlaybackQuality } from "@livepeer-frameworks/player-core";
|
|
3
|
+
import type { UsePlaybackQualityOptions } from "../types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Hook to monitor video playback quality
|