@dialtribe/react-sdk 0.1.0-alpha.8 → 0.1.4
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/LICENSE +20 -20
- package/README.md +535 -535
- package/dist/{broadcast-player.d.mts → dialtribe-player-CNriUtNi.d.mts} +97 -85
- package/dist/{broadcast-player.d.ts → dialtribe-player-CNriUtNi.d.ts} +97 -85
- package/dist/dialtribe-player.d.mts +3 -0
- package/dist/dialtribe-player.d.ts +3 -0
- package/dist/{broadcast-player.js → dialtribe-player.js} +1103 -548
- package/dist/dialtribe-player.js.map +1 -0
- package/dist/{broadcast-player.mjs → dialtribe-player.mjs} +1099 -544
- package/dist/dialtribe-player.mjs.map +1 -0
- package/dist/dialtribe-streamer-C16mRqha.d.ts +567 -0
- package/dist/dialtribe-streamer-FjMRCQfA.d.mts +567 -0
- package/dist/dialtribe-streamer.d.mts +4 -0
- package/dist/dialtribe-streamer.d.ts +4 -0
- package/dist/dialtribe-streamer.js +4836 -0
- package/dist/dialtribe-streamer.js.map +1 -0
- package/dist/dialtribe-streamer.mjs +4799 -0
- package/dist/dialtribe-streamer.mjs.map +1 -0
- package/dist/hello-world.js.map +1 -1
- package/dist/hello-world.mjs.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3306 -609
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3222 -541
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +3 -3
- package/package.json +91 -86
- package/dist/broadcast-player.js.map +0 -1
- package/dist/broadcast-player.mjs.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { createContext, useState, useCallback, useEffect, useContext, useRef, Component } from 'react';
|
|
1
|
+
import { createContext, useState, useCallback, useEffect, useContext, useRef, useMemo, Component } from 'react';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import ReactPlayer from 'react-player';
|
|
4
4
|
|
|
5
|
-
// src/context/
|
|
6
|
-
var
|
|
7
|
-
function
|
|
5
|
+
// src/context/DialtribeProvider.tsx
|
|
6
|
+
var DialtribeContext = createContext(null);
|
|
7
|
+
function DialtribeProvider({
|
|
8
8
|
sessionToken: initialToken,
|
|
9
9
|
onTokenRefresh,
|
|
10
10
|
onTokenExpired,
|
|
@@ -40,19 +40,19 @@ function DialTribeProvider({
|
|
|
40
40
|
markExpired,
|
|
41
41
|
apiBaseUrl
|
|
42
42
|
};
|
|
43
|
-
return /* @__PURE__ */ jsx(
|
|
43
|
+
return /* @__PURE__ */ jsx(DialtribeContext.Provider, { value, children });
|
|
44
44
|
}
|
|
45
|
-
function
|
|
46
|
-
const context = useContext(
|
|
45
|
+
function useDialtribe() {
|
|
46
|
+
const context = useContext(DialtribeContext);
|
|
47
47
|
if (!context) {
|
|
48
48
|
throw new Error(
|
|
49
|
-
'
|
|
49
|
+
'useDialtribe must be used within a DialtribeProvider. Wrap your app with <DialtribeProvider sessionToken="sess_xxx">...</DialtribeProvider>'
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
52
|
return context;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
// src/client/
|
|
55
|
+
// src/client/DialtribeClient.ts
|
|
56
56
|
function getDefaultApiBaseUrl() {
|
|
57
57
|
if (typeof process !== "undefined" && process.env?.NEXT_PUBLIC_DIALTRIBE_API_URL) {
|
|
58
58
|
return process.env.NEXT_PUBLIC_DIALTRIBE_API_URL;
|
|
@@ -66,18 +66,19 @@ function getEndpoints(baseUrl = DIALTRIBE_API_BASE) {
|
|
|
66
66
|
broadcast: (id) => `${baseUrl}/broadcasts/${id}`,
|
|
67
67
|
contentPlay: `${baseUrl}/content/play`,
|
|
68
68
|
presignedUrl: `${baseUrl}/media/presigned-url`,
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
audienceStart: `${baseUrl}/audiences/start`,
|
|
70
|
+
audiencePing: `${baseUrl}/audiences/ping`,
|
|
71
|
+
sessionPing: `${baseUrl}/sessions/ping`
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
74
|
var ENDPOINTS = getEndpoints();
|
|
74
|
-
var
|
|
75
|
+
var DialtribeClient = class {
|
|
75
76
|
constructor(config) {
|
|
76
77
|
this.config = config;
|
|
77
78
|
this.endpoints = config.apiBaseUrl ? getEndpoints(config.apiBaseUrl) : ENDPOINTS;
|
|
78
79
|
}
|
|
79
80
|
/**
|
|
80
|
-
* Make an authenticated request to
|
|
81
|
+
* Make an authenticated request to Dialtribe API
|
|
81
82
|
*
|
|
82
83
|
* Automatically:
|
|
83
84
|
* - Adds Authorization header with session token
|
|
@@ -191,7 +192,7 @@ var DialTribeClient = class {
|
|
|
191
192
|
* @returns audienceId and optional resumePosition
|
|
192
193
|
*/
|
|
193
194
|
async startSession(params) {
|
|
194
|
-
const response = await this.fetch(this.endpoints.
|
|
195
|
+
const response = await this.fetch(this.endpoints.audienceStart, {
|
|
195
196
|
method: "POST",
|
|
196
197
|
body: JSON.stringify(params)
|
|
197
198
|
});
|
|
@@ -210,7 +211,7 @@ var DialTribeClient = class {
|
|
|
210
211
|
* - 3: UNMOUNT
|
|
211
212
|
*/
|
|
212
213
|
async sendSessionPing(params) {
|
|
213
|
-
const response = await this.fetch(this.endpoints.
|
|
214
|
+
const response = await this.fetch(this.endpoints.audiencePing, {
|
|
214
215
|
method: "POST",
|
|
215
216
|
body: JSON.stringify(params)
|
|
216
217
|
});
|
|
@@ -742,7 +743,7 @@ function getErrorMessage(error) {
|
|
|
742
743
|
}
|
|
743
744
|
return "Unable to play media. Please try refreshing the page or contact support if the problem persists.";
|
|
744
745
|
}
|
|
745
|
-
function
|
|
746
|
+
function DialtribePlayer({
|
|
746
747
|
broadcast,
|
|
747
748
|
appId,
|
|
748
749
|
contentId,
|
|
@@ -753,18 +754,18 @@ function BroadcastPlayer({
|
|
|
753
754
|
className = "",
|
|
754
755
|
enableKeyboardShortcuts = false
|
|
755
756
|
}) {
|
|
756
|
-
const { sessionToken, setSessionToken, markExpired, apiBaseUrl } =
|
|
757
|
+
const { sessionToken, setSessionToken, markExpired, apiBaseUrl } = useDialtribe();
|
|
757
758
|
const clientRef = useRef(null);
|
|
758
759
|
if (!clientRef.current && sessionToken) {
|
|
759
|
-
clientRef.current = new
|
|
760
|
+
clientRef.current = new DialtribeClient({
|
|
760
761
|
sessionToken,
|
|
761
762
|
apiBaseUrl,
|
|
762
763
|
onTokenRefresh: (newToken, expiresAt) => {
|
|
763
|
-
debug.log(`[
|
|
764
|
+
debug.log(`[DialtribeClient] Token refreshed, expires at ${expiresAt}`);
|
|
764
765
|
setSessionToken(newToken, expiresAt);
|
|
765
766
|
},
|
|
766
767
|
onTokenExpired: () => {
|
|
767
|
-
debug.error("[
|
|
768
|
+
debug.error("[DialtribeClient] Token expired");
|
|
768
769
|
markExpired();
|
|
769
770
|
}
|
|
770
771
|
});
|
|
@@ -775,7 +776,9 @@ function BroadcastPlayer({
|
|
|
775
776
|
const playerRef = useRef(null);
|
|
776
777
|
const transcriptContainerRef = useRef(null);
|
|
777
778
|
const activeWordRef = useRef(null);
|
|
778
|
-
const [audioElement, setAudioElement] = useState(
|
|
779
|
+
const [audioElement, setAudioElement] = useState(
|
|
780
|
+
null
|
|
781
|
+
);
|
|
779
782
|
const [playing, setPlaying] = useState(false);
|
|
780
783
|
const [played, setPlayed] = useState(0);
|
|
781
784
|
const [duration, setDuration] = useState(0);
|
|
@@ -787,7 +790,9 @@ function BroadcastPlayer({
|
|
|
787
790
|
const [hasEnded, setHasEnded] = useState(false);
|
|
788
791
|
const [hasStreamEnded, setHasStreamEnded] = useState(false);
|
|
789
792
|
const [showTranscript, setShowTranscript] = useState(false);
|
|
790
|
-
const [transcriptData, setTranscriptData] = useState(
|
|
793
|
+
const [transcriptData, setTranscriptData] = useState(
|
|
794
|
+
null
|
|
795
|
+
);
|
|
791
796
|
const [currentTime, setCurrentTime] = useState(0);
|
|
792
797
|
const [isLoadingTranscript, setIsLoadingTranscript] = useState(false);
|
|
793
798
|
const [isLoadingVideo, setIsLoadingVideo] = useState(true);
|
|
@@ -815,7 +820,9 @@ function BroadcastPlayer({
|
|
|
815
820
|
const refreshPresignedUrl = useCallback(
|
|
816
821
|
async (fileType) => {
|
|
817
822
|
if (!broadcast.hash || isRefreshingUrl.current || !client) {
|
|
818
|
-
debug.log(
|
|
823
|
+
debug.log(
|
|
824
|
+
"[URL Refresh] Skipping refresh - no hash, already refreshing, or no client"
|
|
825
|
+
);
|
|
819
826
|
return false;
|
|
820
827
|
}
|
|
821
828
|
if (fileType === "hls") {
|
|
@@ -823,14 +830,18 @@ function BroadcastPlayer({
|
|
|
823
830
|
return false;
|
|
824
831
|
}
|
|
825
832
|
isRefreshingUrl.current = true;
|
|
826
|
-
debug.log(
|
|
833
|
+
debug.log(
|
|
834
|
+
`[URL Refresh] Refreshing ${fileType} URL for broadcast ${broadcast.id}`
|
|
835
|
+
);
|
|
827
836
|
try {
|
|
828
837
|
const data = await client.refreshPresignedUrl({
|
|
829
838
|
broadcastId: broadcast.id,
|
|
830
839
|
hash: broadcast.hash,
|
|
831
840
|
fileType
|
|
832
841
|
});
|
|
833
|
-
debug.log(
|
|
842
|
+
debug.log(
|
|
843
|
+
`[URL Refresh] Successfully refreshed URL, expires at ${data.expiresAt}`
|
|
844
|
+
);
|
|
834
845
|
setCurrentPlaybackInfo({ url: data.url, type: fileType });
|
|
835
846
|
setUrlExpiresAt(new Date(data.expiresAt));
|
|
836
847
|
if (errorMessage.includes("URL") || errorMessage.includes("session") || errorMessage.includes("refresh")) {
|
|
@@ -845,7 +856,9 @@ function BroadcastPlayer({
|
|
|
845
856
|
}
|
|
846
857
|
debug.error("[URL Refresh] Failed to refresh presigned URL:", error);
|
|
847
858
|
setHasError(true);
|
|
848
|
-
setErrorMessage(
|
|
859
|
+
setErrorMessage(
|
|
860
|
+
"Unable to refresh media URL. The session may have expired."
|
|
861
|
+
);
|
|
849
862
|
if (onError && error instanceof Error) {
|
|
850
863
|
onError(error);
|
|
851
864
|
}
|
|
@@ -865,7 +878,8 @@ function BroadcastPlayer({
|
|
|
865
878
|
};
|
|
866
879
|
const initializeTrackingSession = useCallback(async () => {
|
|
867
880
|
if (!contentId || !appId || !client) return;
|
|
868
|
-
if (currentPlaybackInfo?.type === "hls" && broadcast.broadcastStatus === 1)
|
|
881
|
+
if (currentPlaybackInfo?.type === "hls" && broadcast.broadcastStatus === 1)
|
|
882
|
+
return;
|
|
869
883
|
if (hasInitializedSession.current) return;
|
|
870
884
|
hasInitializedSession.current = true;
|
|
871
885
|
try {
|
|
@@ -888,7 +902,9 @@ function BroadcastPlayer({
|
|
|
888
902
|
setAudienceId(data.audienceId);
|
|
889
903
|
if (data.resumePosition && data.resumePosition > 0 && audioElement) {
|
|
890
904
|
audioElement.currentTime = data.resumePosition;
|
|
891
|
-
debug.log(
|
|
905
|
+
debug.log(
|
|
906
|
+
`[Audience Tracking] Resumed playback at ${data.resumePosition}s`
|
|
907
|
+
);
|
|
892
908
|
}
|
|
893
909
|
debug.log("[Audience Tracking] Session initialized:", data.audienceId);
|
|
894
910
|
} catch (error) {
|
|
@@ -897,7 +913,19 @@ function BroadcastPlayer({
|
|
|
897
913
|
onError(error);
|
|
898
914
|
}
|
|
899
915
|
}
|
|
900
|
-
}, [
|
|
916
|
+
}, [
|
|
917
|
+
contentId,
|
|
918
|
+
appId,
|
|
919
|
+
broadcast.id,
|
|
920
|
+
broadcast.broadcastStatus,
|
|
921
|
+
foreignId,
|
|
922
|
+
foreignTier,
|
|
923
|
+
sessionId,
|
|
924
|
+
currentPlaybackInfo?.type,
|
|
925
|
+
audioElement,
|
|
926
|
+
client,
|
|
927
|
+
onError
|
|
928
|
+
]);
|
|
901
929
|
const sendTrackingPing = useCallback(
|
|
902
930
|
async (eventType) => {
|
|
903
931
|
if (!audienceId || !sessionId || !client) return;
|
|
@@ -921,15 +949,25 @@ function BroadcastPlayer({
|
|
|
921
949
|
return { url: broadcast.hlsPlaylistUrl, type: "hls" };
|
|
922
950
|
}
|
|
923
951
|
if (broadcast.hash && broadcast.app?.s3Hash) {
|
|
924
|
-
const hlsUrl = buildBroadcastCdnUrl(
|
|
952
|
+
const hlsUrl = buildBroadcastCdnUrl(
|
|
953
|
+
broadcast.app.s3Hash,
|
|
954
|
+
broadcast.hash,
|
|
955
|
+
"index.m3u8"
|
|
956
|
+
);
|
|
925
957
|
return { url: hlsUrl, type: "hls" };
|
|
926
958
|
}
|
|
927
959
|
}
|
|
928
960
|
if (broadcast.recordingMp4Url && broadcast.isVideo && broadcast.hash) {
|
|
929
|
-
return {
|
|
961
|
+
return {
|
|
962
|
+
url: buildPlaybackUrl(broadcast.id, broadcast.hash),
|
|
963
|
+
type: "mp4"
|
|
964
|
+
};
|
|
930
965
|
}
|
|
931
966
|
if (broadcast.recordingMp3Url && broadcast.hash) {
|
|
932
|
-
return {
|
|
967
|
+
return {
|
|
968
|
+
url: buildPlaybackUrl(broadcast.id, broadcast.hash),
|
|
969
|
+
type: "mp3"
|
|
970
|
+
};
|
|
933
971
|
}
|
|
934
972
|
if (broadcast.hlsPlaylistUrl) {
|
|
935
973
|
return { url: broadcast.hlsPlaylistUrl, type: "hls" };
|
|
@@ -944,7 +982,9 @@ function BroadcastPlayer({
|
|
|
944
982
|
if (info && (info.type === "mp4" || info.type === "mp3")) {
|
|
945
983
|
const expiresAt = new Date(Date.now() + URL_EXPIRATION_MS);
|
|
946
984
|
setUrlExpiresAt(expiresAt);
|
|
947
|
-
debug.log(
|
|
985
|
+
debug.log(
|
|
986
|
+
`[URL Refresh] Initial ${info.type} URL expires at ${expiresAt.toISOString()}`
|
|
987
|
+
);
|
|
948
988
|
}
|
|
949
989
|
if (info) {
|
|
950
990
|
setPlaying(true);
|
|
@@ -983,12 +1023,34 @@ function BroadcastPlayer({
|
|
|
983
1023
|
setAudioElement(null);
|
|
984
1024
|
setPlaying(true);
|
|
985
1025
|
}
|
|
986
|
-
}, [
|
|
1026
|
+
}, [
|
|
1027
|
+
broadcast.broadcastStatus,
|
|
1028
|
+
broadcast.recordingMp3Url,
|
|
1029
|
+
broadcast.mp3Size,
|
|
1030
|
+
broadcast.hash,
|
|
1031
|
+
broadcast.id,
|
|
1032
|
+
currentPlaybackInfo
|
|
1033
|
+
]);
|
|
987
1034
|
const playbackUrl = currentPlaybackInfo?.url || null;
|
|
988
1035
|
const playbackType = currentPlaybackInfo?.type || null;
|
|
989
1036
|
const isAudioOnly = playbackType === "mp3" || !broadcast.isVideo && playbackType !== "mp4";
|
|
990
1037
|
const isLiveStream = broadcast.broadcastStatus === 1 && playbackType === "hls" && !hasStreamEnded;
|
|
991
1038
|
const wasLiveStream = initialPlaybackTypeRef.current === "hls";
|
|
1039
|
+
const playerConfig = useMemo(
|
|
1040
|
+
() => ({
|
|
1041
|
+
file: {
|
|
1042
|
+
forceHLS: playbackType === "hls",
|
|
1043
|
+
hlsOptions: isLiveStream ? {
|
|
1044
|
+
maxLoadingDelay: 10,
|
|
1045
|
+
minAutoBitrate: 0,
|
|
1046
|
+
lowLatencyMode: true,
|
|
1047
|
+
enableWorker: true
|
|
1048
|
+
} : {}
|
|
1049
|
+
}
|
|
1050
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1051
|
+
}),
|
|
1052
|
+
[playbackType, isLiveStream]
|
|
1053
|
+
);
|
|
992
1054
|
const formatTimestamp = (seconds) => {
|
|
993
1055
|
if (!seconds || isNaN(seconds) || !isFinite(seconds)) return "00:00:00";
|
|
994
1056
|
const hrs = Math.floor(seconds / 3600);
|
|
@@ -1059,7 +1121,7 @@ function BroadcastPlayer({
|
|
|
1059
1121
|
duration: Math.floor(duration || 0)
|
|
1060
1122
|
};
|
|
1061
1123
|
const headers = {
|
|
1062
|
-
|
|
1124
|
+
Authorization: `Bearer ${sessionToken}`,
|
|
1063
1125
|
"Content-Type": "application/json"
|
|
1064
1126
|
};
|
|
1065
1127
|
fetch(ENDPOINTS.sessionPing, {
|
|
@@ -1077,7 +1139,9 @@ function BroadcastPlayer({
|
|
|
1077
1139
|
setIsLoadingTranscript(true);
|
|
1078
1140
|
fetch(broadcast.transcriptUrl).then((res) => {
|
|
1079
1141
|
if (!res.ok) {
|
|
1080
|
-
throw new Error(
|
|
1142
|
+
throw new Error(
|
|
1143
|
+
`Failed to fetch transcript: ${res.status} ${res.statusText}`
|
|
1144
|
+
);
|
|
1081
1145
|
}
|
|
1082
1146
|
return res.json();
|
|
1083
1147
|
}).then((data) => {
|
|
@@ -1209,7 +1273,7 @@ function BroadcastPlayer({
|
|
|
1209
1273
|
setAudioElement(internalPlayer);
|
|
1210
1274
|
}
|
|
1211
1275
|
} catch (error) {
|
|
1212
|
-
debug.error("[
|
|
1276
|
+
debug.error("[DialtribePlayer] Error getting internal player:", error);
|
|
1213
1277
|
}
|
|
1214
1278
|
};
|
|
1215
1279
|
useEffect(() => {
|
|
@@ -1223,7 +1287,17 @@ function BroadcastPlayer({
|
|
|
1223
1287
|
return false;
|
|
1224
1288
|
};
|
|
1225
1289
|
if (!findAudioElement()) {
|
|
1226
|
-
const retryIntervals = [
|
|
1290
|
+
const retryIntervals = [
|
|
1291
|
+
100,
|
|
1292
|
+
300,
|
|
1293
|
+
500,
|
|
1294
|
+
1e3,
|
|
1295
|
+
1500,
|
|
1296
|
+
2e3,
|
|
1297
|
+
3e3,
|
|
1298
|
+
4e3,
|
|
1299
|
+
5e3
|
|
1300
|
+
];
|
|
1227
1301
|
const timeouts = retryIntervals.map(
|
|
1228
1302
|
(delay) => setTimeout(() => {
|
|
1229
1303
|
findAudioElement();
|
|
@@ -1281,16 +1355,23 @@ function BroadcastPlayer({
|
|
|
1281
1355
|
debug.error("Media playback error:", error);
|
|
1282
1356
|
const isPotentialExpiration = error?.code === HTTP_STATUS.FORBIDDEN || error?.status === HTTP_STATUS.FORBIDDEN || error?.statusCode === HTTP_STATUS.FORBIDDEN || error?.code === HTTP_STATUS.NOT_FOUND || error?.status === HTTP_STATUS.NOT_FOUND || error?.statusCode === HTTP_STATUS.NOT_FOUND || error?.message?.includes("403") || error?.message?.includes("404") || error?.message?.includes("Forbidden") || error?.message?.toLowerCase().includes("network") || error?.type === "network" || error?.message?.includes("MEDIA_ERR_SRC_NOT_SUPPORTED");
|
|
1283
1357
|
if (isPotentialExpiration && currentPlaybackInfo?.type && !isRefreshingUrl.current) {
|
|
1284
|
-
debug.log(
|
|
1358
|
+
debug.log(
|
|
1359
|
+
"[Player Error] Detected potential URL expiration, attempting refresh..."
|
|
1360
|
+
);
|
|
1285
1361
|
const currentPosition = audioElement?.currentTime || 0;
|
|
1286
1362
|
const wasPlaying = playing;
|
|
1287
1363
|
const fileType = currentPlaybackInfo.type;
|
|
1288
1364
|
if (fileType !== "mp4" && fileType !== "mp3" && fileType !== "hls") {
|
|
1289
|
-
debug.error(
|
|
1365
|
+
debug.error(
|
|
1366
|
+
"[Player Error] Invalid file type, cannot refresh:",
|
|
1367
|
+
fileType
|
|
1368
|
+
);
|
|
1290
1369
|
} else {
|
|
1291
1370
|
const refreshed = await refreshPresignedUrl(fileType);
|
|
1292
1371
|
if (refreshed) {
|
|
1293
|
-
debug.log(
|
|
1372
|
+
debug.log(
|
|
1373
|
+
"[Player Error] URL refreshed successfully, resuming playback"
|
|
1374
|
+
);
|
|
1294
1375
|
setTimeout(() => {
|
|
1295
1376
|
if (audioElement && currentPosition > 0) {
|
|
1296
1377
|
audioElement.currentTime = currentPosition;
|
|
@@ -1338,7 +1419,10 @@ function BroadcastPlayer({
|
|
|
1338
1419
|
if (!enableKeyboardShortcuts) return;
|
|
1339
1420
|
const seekBy = (seconds) => {
|
|
1340
1421
|
if (!audioElement || duration <= 0) return;
|
|
1341
|
-
const newTime = Math.max(
|
|
1422
|
+
const newTime = Math.max(
|
|
1423
|
+
0,
|
|
1424
|
+
Math.min(duration, audioElement.currentTime + seconds)
|
|
1425
|
+
);
|
|
1342
1426
|
audioElement.currentTime = newTime;
|
|
1343
1427
|
setPlayed(newTime / duration);
|
|
1344
1428
|
};
|
|
@@ -1406,7 +1490,17 @@ function BroadcastPlayer({
|
|
|
1406
1490
|
};
|
|
1407
1491
|
window.addEventListener("keydown", handleKeyDown);
|
|
1408
1492
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1409
|
-
}, [
|
|
1493
|
+
}, [
|
|
1494
|
+
enableKeyboardShortcuts,
|
|
1495
|
+
audioElement,
|
|
1496
|
+
duration,
|
|
1497
|
+
playing,
|
|
1498
|
+
muted,
|
|
1499
|
+
isAudioOnly,
|
|
1500
|
+
handlePlayPause,
|
|
1501
|
+
toggleMute,
|
|
1502
|
+
toggleFullscreen
|
|
1503
|
+
]);
|
|
1410
1504
|
if (currentPlaybackInfo !== null && !playbackUrl) {
|
|
1411
1505
|
return /* @__PURE__ */ jsxs("div", { className: "bg-white dark:bg-zinc-900 rounded-lg p-6 max-w-md w-full mx-4 border border-gray-200 dark:border-zinc-800", children: [
|
|
1412
1506
|
/* @__PURE__ */ jsx("h3", { className: "text-lg font-bold text-black dark:text-white mb-4", children: "Broadcast Unavailable" }),
|
|
@@ -1417,452 +1511,914 @@ function BroadcastPlayer({
|
|
|
1417
1511
|
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-8", children: /* @__PURE__ */ jsx(LoadingSpinner, { variant: "white", text: "Loading..." }) });
|
|
1418
1512
|
}
|
|
1419
1513
|
const hasTranscript = broadcast.transcriptStatus === 2 && transcriptData && (transcriptData.segments && transcriptData.segments.some((s) => s.words && s.words.length > 0) || transcriptData.words && transcriptData.words.length > 0);
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
onClick: () => setShowClipCreator(true),
|
|
1435
|
-
className: "px-3 md:px-4 py-1.5 md:py-2 bg-blue-600 hover:bg-blue-700 text-white text-xs md:text-sm font-medium rounded-lg transition-colors flex items-center gap-1 md:gap-2",
|
|
1436
|
-
title: "Create a clip from this broadcast",
|
|
1437
|
-
"aria-label": "Create clip from broadcast",
|
|
1438
|
-
children: [
|
|
1439
|
-
/* @__PURE__ */ jsxs("svg", { className: "w-3 h-3 md:w-4 md:h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [
|
|
1440
|
-
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" }),
|
|
1441
|
-
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 12a9 9 0 11-18 0 9 9 0 0118 0z" })
|
|
1442
|
-
] }),
|
|
1443
|
-
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Create Clip" }),
|
|
1444
|
-
/* @__PURE__ */ jsx("span", { className: "sm:hidden", children: "Clip" })
|
|
1445
|
-
]
|
|
1446
|
-
}
|
|
1447
|
-
) })
|
|
1448
|
-
] }),
|
|
1449
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row flex-1 min-h-0 overflow-hidden", children: [
|
|
1450
|
-
/* @__PURE__ */ jsxs("div", { className: "shrink-0 md:shrink md:flex-1 flex flex-col overflow-hidden", children: [
|
|
1451
|
-
/* @__PURE__ */ jsxs("div", { className: `relative ${isAudioOnly ? "bg-linear-to-br from-zinc-900 via-zinc-800 to-zinc-900 flex items-stretch" : "bg-black"}`, children: [
|
|
1452
|
-
isAudioOnly ? /* @__PURE__ */ jsx("div", { className: "relative cursor-pointer w-full flex flex-col", onClick: handleVideoClick, children: !hasError ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "w-full h-full relative", children: [
|
|
1453
|
-
/* @__PURE__ */ jsx(AudioWaveform, { audioElement, isPlaying: isLiveStream ? true : playing, isLive: isLiveStream }),
|
|
1454
|
-
isLoadingVideo && !hasError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/90 flex items-center justify-center z-20", children: /* @__PURE__ */ jsx(LoadingSpinner, { variant: "white" }) }),
|
|
1455
|
-
hasEnded && !wasLiveStream && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 flex items-center justify-center z-20 pointer-events-auto", children: /* @__PURE__ */ jsxs(
|
|
1456
|
-
"button",
|
|
1457
|
-
{
|
|
1458
|
-
onClick: (e) => {
|
|
1459
|
-
e.stopPropagation();
|
|
1460
|
-
handleRestart();
|
|
1461
|
-
},
|
|
1462
|
-
className: "bg-white hover:bg-blue-500 text-black hover:text-white font-semibold py-4 px-8 rounded-full transition-all transform hover:scale-105 flex items-center gap-3",
|
|
1463
|
-
"aria-label": "Restart playback from beginning",
|
|
1464
|
-
children: [
|
|
1465
|
-
/* @__PURE__ */ jsx("svg", { className: "w-6 h-6", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z", clipRule: "evenodd" }) }),
|
|
1466
|
-
"Restart"
|
|
1467
|
-
]
|
|
1468
|
-
}
|
|
1469
|
-
) })
|
|
1470
|
-
] }) }) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center max-w-md px-4", children: [
|
|
1471
|
-
/* @__PURE__ */ jsx("div", { className: "text-6xl mb-4", children: "\u26A0\uFE0F" }),
|
|
1472
|
-
/* @__PURE__ */ jsx("h3", { className: "text-xl font-semibold text-white mb-3", children: "Playback Error" }),
|
|
1473
|
-
/* @__PURE__ */ jsx("p", { className: "text-gray-300 text-sm mb-6", children: errorMessage }),
|
|
1474
|
-
/* @__PURE__ */ jsxs(
|
|
1475
|
-
"button",
|
|
1476
|
-
{
|
|
1477
|
-
onClick: handleRetry,
|
|
1478
|
-
className: "px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors inline-flex items-center gap-2",
|
|
1479
|
-
"aria-label": "Retry playback",
|
|
1480
|
-
children: [
|
|
1481
|
-
/* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) }),
|
|
1482
|
-
"Retry"
|
|
1483
|
-
]
|
|
1484
|
-
}
|
|
1485
|
-
)
|
|
1486
|
-
] }) }) }) : /* @__PURE__ */ jsxs("div", { className: "aspect-video relative", children: [
|
|
1487
|
-
/* @__PURE__ */ jsx("div", { onClick: handleVideoClick, className: "cursor-pointer", children: /* @__PURE__ */ jsx(
|
|
1488
|
-
ReactPlayer,
|
|
1489
|
-
{
|
|
1490
|
-
ref: playerRef,
|
|
1491
|
-
src: playbackUrl || void 0,
|
|
1492
|
-
playing,
|
|
1493
|
-
volume,
|
|
1494
|
-
muted,
|
|
1495
|
-
width: "100%",
|
|
1496
|
-
height: "100%",
|
|
1497
|
-
crossOrigin: "anonymous",
|
|
1498
|
-
onReady: handlePlayerReady,
|
|
1499
|
-
onTimeUpdate: handleTimeUpdate,
|
|
1500
|
-
onLoadedMetadata: handleLoadedMetadata,
|
|
1501
|
-
onPlay: handlePlay,
|
|
1502
|
-
onPause: handlePause,
|
|
1503
|
-
onEnded: handleEnded,
|
|
1504
|
-
onError: handleError,
|
|
1505
|
-
style: { backgroundColor: "#000" }
|
|
1506
|
-
},
|
|
1507
|
-
playbackUrl || "no-url"
|
|
1508
|
-
) }),
|
|
1509
|
-
isLoadingVideo && !hasError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/90 flex items-center justify-center", children: /* @__PURE__ */ jsx(LoadingSpinner, { variant: "white" }) }),
|
|
1510
|
-
hasEnded && !hasError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 flex items-center justify-center", children: /* @__PURE__ */ jsxs(
|
|
1511
|
-
"button",
|
|
1512
|
-
{
|
|
1513
|
-
onClick: (e) => {
|
|
1514
|
-
e.stopPropagation();
|
|
1515
|
-
handleRestart();
|
|
1516
|
-
},
|
|
1517
|
-
className: "bg-white hover:bg-blue-500 text-black hover:text-white font-semibold py-4 px-8 rounded-full transition-all transform hover:scale-105 flex items-center gap-3",
|
|
1518
|
-
children: [
|
|
1519
|
-
/* @__PURE__ */ jsx("svg", { className: "w-6 h-6", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z", clipRule: "evenodd" }) }),
|
|
1520
|
-
"Restart"
|
|
1521
|
-
]
|
|
1522
|
-
}
|
|
1523
|
-
) }),
|
|
1524
|
-
hasError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/90 flex items-center justify-center p-8", children: /* @__PURE__ */ jsxs("div", { className: "text-center max-w-md", children: [
|
|
1525
|
-
/* @__PURE__ */ jsx("div", { className: "text-6xl mb-4", children: "\u26A0\uFE0F" }),
|
|
1526
|
-
/* @__PURE__ */ jsx("h3", { className: "text-xl font-semibold text-white mb-3", children: "Playback Error" }),
|
|
1527
|
-
/* @__PURE__ */ jsx("p", { className: "text-gray-300 text-sm mb-6", children: errorMessage }),
|
|
1528
|
-
/* @__PURE__ */ jsxs(
|
|
1529
|
-
"button",
|
|
1530
|
-
{
|
|
1531
|
-
onClick: handleRetry,
|
|
1532
|
-
className: "px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors inline-flex items-center gap-2",
|
|
1533
|
-
"aria-label": "Retry playback",
|
|
1534
|
-
children: [
|
|
1535
|
-
/* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) }),
|
|
1536
|
-
"Retry"
|
|
1537
|
-
]
|
|
1538
|
-
}
|
|
1539
|
-
)
|
|
1540
|
-
] }) })
|
|
1514
|
+
const playerContent = /* @__PURE__ */ jsxs(
|
|
1515
|
+
"div",
|
|
1516
|
+
{
|
|
1517
|
+
className: `bg-black rounded-lg shadow-2xl w-full max-h-full flex flex-col overflow-hidden ${className}`,
|
|
1518
|
+
children: [
|
|
1519
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-zinc-900/50 backdrop-blur-sm border-b border-zinc-800 px-3 sm:px-4 md:px-6 py-2 sm:py-3 md:py-4 flex justify-between items-center rounded-t-lg shrink-0", children: [
|
|
1520
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1521
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-white", children: broadcast.streamKeyRecord?.foreignName || "Broadcast" }),
|
|
1522
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-400", children: [
|
|
1523
|
+
broadcast.isVideo ? "Video" : "Audio",
|
|
1524
|
+
" \u2022",
|
|
1525
|
+
" ",
|
|
1526
|
+
broadcast.broadcastStatus === 1 ? /* @__PURE__ */ jsx("span", { className: "text-red-500 font-semibold", children: "\u{1F534} LIVE" }) : playbackType === "hls" ? /* @__PURE__ */ jsx("span", { className: "text-gray-500 font-semibold", children: "OFFLINE" }) : formatTime(duration)
|
|
1527
|
+
] })
|
|
1541
1528
|
] }),
|
|
1542
|
-
|
|
1543
|
-
|
|
1529
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: renderClipCreator && playbackType !== "hls" && appId && contentId && duration > 0 && /* @__PURE__ */ jsxs(
|
|
1530
|
+
"button",
|
|
1544
1531
|
{
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1532
|
+
onClick: () => setShowClipCreator(true),
|
|
1533
|
+
className: "px-3 md:px-4 py-1.5 md:py-2 bg-blue-600 hover:bg-blue-700 text-white text-xs md:text-sm font-medium rounded-lg transition-colors flex items-center gap-1 md:gap-2",
|
|
1534
|
+
title: "Create a clip from this broadcast",
|
|
1535
|
+
"aria-label": "Create clip from broadcast",
|
|
1536
|
+
children: [
|
|
1537
|
+
/* @__PURE__ */ jsxs(
|
|
1538
|
+
"svg",
|
|
1539
|
+
{
|
|
1540
|
+
className: "w-3 h-3 md:w-4 md:h-4",
|
|
1541
|
+
fill: "none",
|
|
1542
|
+
stroke: "currentColor",
|
|
1543
|
+
viewBox: "0 0 24 24",
|
|
1544
|
+
children: [
|
|
1545
|
+
/* @__PURE__ */ jsx(
|
|
1546
|
+
"path",
|
|
1547
|
+
{
|
|
1548
|
+
strokeLinecap: "round",
|
|
1549
|
+
strokeLinejoin: "round",
|
|
1550
|
+
strokeWidth: 2,
|
|
1551
|
+
d: "M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"
|
|
1552
|
+
}
|
|
1553
|
+
),
|
|
1554
|
+
/* @__PURE__ */ jsx(
|
|
1555
|
+
"path",
|
|
1556
|
+
{
|
|
1557
|
+
strokeLinecap: "round",
|
|
1558
|
+
strokeLinejoin: "round",
|
|
1559
|
+
strokeWidth: 2,
|
|
1560
|
+
d: "M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
1561
|
+
}
|
|
1562
|
+
)
|
|
1563
|
+
]
|
|
1564
|
+
}
|
|
1565
|
+
),
|
|
1566
|
+
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Create Clip" }),
|
|
1567
|
+
/* @__PURE__ */ jsx("span", { className: "sm:hidden", children: "Clip" })
|
|
1568
|
+
]
|
|
1569
|
+
}
|
|
1562
1570
|
) })
|
|
1563
1571
|
] }),
|
|
1564
|
-
|
|
1565
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
1566
|
-
/* @__PURE__ */
|
|
1567
|
-
"
|
|
1572
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row flex-1 min-h-0 overflow-hidden", children: [
|
|
1573
|
+
/* @__PURE__ */ jsxs("div", { className: "shrink-0 md:shrink md:flex-1 flex flex-col overflow-hidden", children: [
|
|
1574
|
+
/* @__PURE__ */ jsxs(
|
|
1575
|
+
"div",
|
|
1568
1576
|
{
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1577
|
+
className: `relative ${isAudioOnly ? "bg-linear-to-br from-zinc-900 via-zinc-800 to-zinc-900 flex items-stretch" : "bg-black"}`,
|
|
1578
|
+
children: [
|
|
1579
|
+
isAudioOnly ? /* @__PURE__ */ jsx(
|
|
1580
|
+
"div",
|
|
1581
|
+
{
|
|
1582
|
+
className: "relative cursor-pointer w-full flex flex-col",
|
|
1583
|
+
onClick: handleVideoClick,
|
|
1584
|
+
children: !hasError ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "w-full h-full relative", children: [
|
|
1585
|
+
/* @__PURE__ */ jsx(
|
|
1586
|
+
AudioWaveform,
|
|
1587
|
+
{
|
|
1588
|
+
audioElement,
|
|
1589
|
+
isPlaying: isLiveStream ? true : playing,
|
|
1590
|
+
isLive: isLiveStream
|
|
1591
|
+
}
|
|
1592
|
+
),
|
|
1593
|
+
isLoadingVideo && !hasError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/90 flex items-center justify-center z-20", children: /* @__PURE__ */ jsx(LoadingSpinner, { variant: "white" }) }),
|
|
1594
|
+
hasEnded && !wasLiveStream && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 flex items-center justify-center z-20 pointer-events-auto", children: /* @__PURE__ */ jsxs(
|
|
1595
|
+
"button",
|
|
1596
|
+
{
|
|
1597
|
+
onClick: (e) => {
|
|
1598
|
+
e.stopPropagation();
|
|
1599
|
+
handleRestart();
|
|
1600
|
+
},
|
|
1601
|
+
className: "bg-white hover:bg-blue-500 text-black hover:text-white font-semibold py-4 px-8 rounded-full transition-all transform hover:scale-105 flex items-center gap-3",
|
|
1602
|
+
"aria-label": "Restart playback from beginning",
|
|
1603
|
+
children: [
|
|
1604
|
+
/* @__PURE__ */ jsx(
|
|
1605
|
+
"svg",
|
|
1606
|
+
{
|
|
1607
|
+
className: "w-6 h-6",
|
|
1608
|
+
fill: "currentColor",
|
|
1609
|
+
viewBox: "0 0 20 20",
|
|
1610
|
+
children: /* @__PURE__ */ jsx(
|
|
1611
|
+
"path",
|
|
1612
|
+
{
|
|
1613
|
+
fillRule: "evenodd",
|
|
1614
|
+
d: "M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z",
|
|
1615
|
+
clipRule: "evenodd"
|
|
1616
|
+
}
|
|
1617
|
+
)
|
|
1618
|
+
}
|
|
1619
|
+
),
|
|
1620
|
+
"Restart"
|
|
1621
|
+
]
|
|
1622
|
+
}
|
|
1623
|
+
) })
|
|
1624
|
+
] }) }) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center max-w-md px-4", children: [
|
|
1625
|
+
/* @__PURE__ */ jsx("div", { className: "text-6xl mb-4", children: "\u26A0\uFE0F" }),
|
|
1626
|
+
/* @__PURE__ */ jsx("h3", { className: "text-xl font-semibold text-white mb-3", children: "Playback Error" }),
|
|
1627
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-300 text-sm mb-6", children: errorMessage }),
|
|
1628
|
+
/* @__PURE__ */ jsxs(
|
|
1629
|
+
"button",
|
|
1630
|
+
{
|
|
1631
|
+
onClick: handleRetry,
|
|
1632
|
+
className: "px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors inline-flex items-center gap-2",
|
|
1633
|
+
"aria-label": "Retry playback",
|
|
1634
|
+
children: [
|
|
1635
|
+
/* @__PURE__ */ jsx(
|
|
1636
|
+
"svg",
|
|
1637
|
+
{
|
|
1638
|
+
className: "w-5 h-5",
|
|
1639
|
+
fill: "none",
|
|
1640
|
+
stroke: "currentColor",
|
|
1641
|
+
viewBox: "0 0 24 24",
|
|
1642
|
+
children: /* @__PURE__ */ jsx(
|
|
1643
|
+
"path",
|
|
1644
|
+
{
|
|
1645
|
+
strokeLinecap: "round",
|
|
1646
|
+
strokeLinejoin: "round",
|
|
1647
|
+
strokeWidth: 2,
|
|
1648
|
+
d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
|
1649
|
+
}
|
|
1650
|
+
)
|
|
1651
|
+
}
|
|
1652
|
+
),
|
|
1653
|
+
"Retry"
|
|
1654
|
+
]
|
|
1655
|
+
}
|
|
1656
|
+
)
|
|
1657
|
+
] }) })
|
|
1658
|
+
}
|
|
1659
|
+
) : /* @__PURE__ */ jsxs("div", { className: "aspect-video relative", children: [
|
|
1660
|
+
/* @__PURE__ */ jsx("div", { onClick: handleVideoClick, className: "cursor-pointer", children: /* @__PURE__ */ jsx(
|
|
1661
|
+
ReactPlayer,
|
|
1662
|
+
{
|
|
1663
|
+
ref: playerRef,
|
|
1664
|
+
src: playbackUrl || void 0,
|
|
1665
|
+
playing,
|
|
1666
|
+
volume,
|
|
1667
|
+
muted,
|
|
1668
|
+
width: "100%",
|
|
1669
|
+
height: "100%",
|
|
1670
|
+
crossOrigin: "anonymous",
|
|
1671
|
+
config: playerConfig,
|
|
1672
|
+
onReady: handlePlayerReady,
|
|
1673
|
+
onTimeUpdate: handleTimeUpdate,
|
|
1674
|
+
onLoadedMetadata: handleLoadedMetadata,
|
|
1675
|
+
onPlay: handlePlay,
|
|
1676
|
+
onPause: handlePause,
|
|
1677
|
+
onEnded: handleEnded,
|
|
1678
|
+
onError: handleError,
|
|
1679
|
+
style: { backgroundColor: "#000" }
|
|
1680
|
+
},
|
|
1681
|
+
playbackUrl || "no-url"
|
|
1682
|
+
) }),
|
|
1683
|
+
isLoadingVideo && !hasError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/90 flex items-center justify-center", children: /* @__PURE__ */ jsx(LoadingSpinner, { variant: "white" }) }),
|
|
1684
|
+
hasEnded && !hasError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 flex items-center justify-center", children: /* @__PURE__ */ jsxs(
|
|
1685
|
+
"button",
|
|
1686
|
+
{
|
|
1687
|
+
onClick: (e) => {
|
|
1688
|
+
e.stopPropagation();
|
|
1689
|
+
handleRestart();
|
|
1690
|
+
},
|
|
1691
|
+
className: "bg-white hover:bg-blue-500 text-black hover:text-white font-semibold py-4 px-8 rounded-full transition-all transform hover:scale-105 flex items-center gap-3",
|
|
1692
|
+
"aria-label": "Restart playback from beginning",
|
|
1693
|
+
children: [
|
|
1694
|
+
/* @__PURE__ */ jsx(
|
|
1695
|
+
"svg",
|
|
1696
|
+
{
|
|
1697
|
+
className: "w-6 h-6",
|
|
1698
|
+
fill: "currentColor",
|
|
1699
|
+
viewBox: "0 0 20 20",
|
|
1700
|
+
"aria-hidden": "true",
|
|
1701
|
+
children: /* @__PURE__ */ jsx(
|
|
1702
|
+
"path",
|
|
1703
|
+
{
|
|
1704
|
+
fillRule: "evenodd",
|
|
1705
|
+
d: "M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z",
|
|
1706
|
+
clipRule: "evenodd"
|
|
1707
|
+
}
|
|
1708
|
+
)
|
|
1709
|
+
}
|
|
1710
|
+
),
|
|
1711
|
+
"Restart"
|
|
1712
|
+
]
|
|
1713
|
+
}
|
|
1714
|
+
) }),
|
|
1715
|
+
hasError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/90 flex items-center justify-center p-8", children: /* @__PURE__ */ jsxs("div", { className: "text-center max-w-md", children: [
|
|
1716
|
+
/* @__PURE__ */ jsx("div", { className: "text-6xl mb-4", children: "\u26A0\uFE0F" }),
|
|
1717
|
+
/* @__PURE__ */ jsx("h3", { className: "text-xl font-semibold text-white mb-3", children: "Playback Error" }),
|
|
1718
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-300 text-sm mb-6", children: errorMessage }),
|
|
1719
|
+
/* @__PURE__ */ jsxs(
|
|
1720
|
+
"button",
|
|
1721
|
+
{
|
|
1722
|
+
onClick: handleRetry,
|
|
1723
|
+
className: "px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors inline-flex items-center gap-2",
|
|
1724
|
+
"aria-label": "Retry playback",
|
|
1725
|
+
children: [
|
|
1726
|
+
/* @__PURE__ */ jsx(
|
|
1727
|
+
"svg",
|
|
1728
|
+
{
|
|
1729
|
+
className: "w-5 h-5",
|
|
1730
|
+
fill: "none",
|
|
1731
|
+
stroke: "currentColor",
|
|
1732
|
+
viewBox: "0 0 24 24",
|
|
1733
|
+
children: /* @__PURE__ */ jsx(
|
|
1734
|
+
"path",
|
|
1735
|
+
{
|
|
1736
|
+
strokeLinecap: "round",
|
|
1737
|
+
strokeLinejoin: "round",
|
|
1738
|
+
strokeWidth: 2,
|
|
1739
|
+
d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
|
1740
|
+
}
|
|
1741
|
+
)
|
|
1742
|
+
}
|
|
1743
|
+
),
|
|
1744
|
+
"Retry"
|
|
1745
|
+
]
|
|
1746
|
+
}
|
|
1747
|
+
)
|
|
1748
|
+
] }) })
|
|
1749
|
+
] }),
|
|
1750
|
+
isAudioOnly && /* @__PURE__ */ jsx("div", { className: "hidden", children: /* @__PURE__ */ jsx(
|
|
1751
|
+
ReactPlayer,
|
|
1752
|
+
{
|
|
1753
|
+
ref: playerRef,
|
|
1754
|
+
src: playbackUrl || void 0,
|
|
1755
|
+
playing,
|
|
1756
|
+
volume,
|
|
1757
|
+
muted,
|
|
1758
|
+
width: "0",
|
|
1759
|
+
height: "0",
|
|
1760
|
+
crossOrigin: "anonymous",
|
|
1761
|
+
config: playerConfig,
|
|
1762
|
+
onReady: handlePlayerReady,
|
|
1763
|
+
onTimeUpdate: handleTimeUpdate,
|
|
1764
|
+
onLoadedMetadata: handleLoadedMetadata,
|
|
1765
|
+
onPlay: handlePlay,
|
|
1766
|
+
onPause: handlePause,
|
|
1767
|
+
onEnded: handleEnded,
|
|
1768
|
+
onError: handleError
|
|
1769
|
+
},
|
|
1770
|
+
playbackUrl || "no-url"
|
|
1771
|
+
) })
|
|
1772
|
+
]
|
|
1586
1773
|
}
|
|
1587
1774
|
),
|
|
1588
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
1589
|
-
/* @__PURE__ */
|
|
1590
|
-
/* @__PURE__ */ jsx("span", { children: formatTime(duration) })
|
|
1591
|
-
] })
|
|
1592
|
-
] }),
|
|
1593
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1594
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
1595
|
-
/* @__PURE__ */ jsx(
|
|
1596
|
-
"button",
|
|
1597
|
-
{
|
|
1598
|
-
onClick: handlePlayPause,
|
|
1599
|
-
className: "text-white hover:text-blue-400 transition-colors",
|
|
1600
|
-
title: playing ? "Pause" : "Play",
|
|
1601
|
-
"aria-label": playing ? "Pause" : "Play",
|
|
1602
|
-
"aria-pressed": playing,
|
|
1603
|
-
children: playing ? /* @__PURE__ */ jsx("svg", { className: "w-8 h-8", fill: "currentColor", viewBox: "0 0 20 20", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z", clipRule: "evenodd" }) }) : /* @__PURE__ */ jsx("svg", { className: "w-8 h-8", fill: "currentColor", viewBox: "0 0 20 20", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z", clipRule: "evenodd" }) })
|
|
1604
|
-
}
|
|
1605
|
-
),
|
|
1606
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1775
|
+
!hasError && !isLiveStream && (wasLiveStream ? parseInt(broadcast.mp3Size || "0") > 0 || parseInt(broadcast.mp4Size || "0") > 0 ? /* @__PURE__ */ jsxs("div", { className: "bg-zinc-900/50 backdrop-blur-sm px-4 md:px-6 py-3 md:py-4 rounded-b-lg", children: [
|
|
1776
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
|
|
1607
1777
|
/* @__PURE__ */ jsx(
|
|
1608
|
-
"
|
|
1778
|
+
"input",
|
|
1609
1779
|
{
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1780
|
+
type: "range",
|
|
1781
|
+
min: 0,
|
|
1782
|
+
max: 0.999999,
|
|
1783
|
+
step: "any",
|
|
1784
|
+
value: played || 0,
|
|
1785
|
+
onMouseDown: handleSeekMouseDown,
|
|
1786
|
+
onMouseUp: handleSeekMouseUp,
|
|
1787
|
+
onTouchStart: handleSeekTouchStart,
|
|
1788
|
+
onTouchEnd: handleSeekTouchEnd,
|
|
1789
|
+
onChange: handleSeekChange,
|
|
1790
|
+
className: "w-full h-1 bg-zinc-700 rounded-lg appearance-none cursor-pointer slider",
|
|
1791
|
+
"aria-label": "Seek position",
|
|
1792
|
+
"aria-valuemin": 0,
|
|
1793
|
+
"aria-valuemax": duration,
|
|
1794
|
+
"aria-valuenow": played * duration,
|
|
1795
|
+
"aria-valuetext": `${formatTime(
|
|
1796
|
+
played * duration
|
|
1797
|
+
)} of ${formatTime(duration)}`,
|
|
1798
|
+
role: "slider"
|
|
1616
1799
|
}
|
|
1617
1800
|
),
|
|
1801
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-xs text-gray-400 mt-1", children: [
|
|
1802
|
+
/* @__PURE__ */ jsx("span", { children: formatTime((played || 0) * duration) }),
|
|
1803
|
+
/* @__PURE__ */ jsx("span", { children: formatTime(duration) })
|
|
1804
|
+
] })
|
|
1805
|
+
] }),
|
|
1806
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1807
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
1808
|
+
/* @__PURE__ */ jsx(
|
|
1809
|
+
"button",
|
|
1810
|
+
{
|
|
1811
|
+
onClick: handlePlayPause,
|
|
1812
|
+
className: "text-white hover:text-blue-400 transition-colors",
|
|
1813
|
+
title: playing ? "Pause" : "Play",
|
|
1814
|
+
"aria-label": playing ? "Pause" : "Play",
|
|
1815
|
+
"aria-pressed": playing,
|
|
1816
|
+
children: playing ? /* @__PURE__ */ jsx(
|
|
1817
|
+
"svg",
|
|
1818
|
+
{
|
|
1819
|
+
className: "w-8 h-8",
|
|
1820
|
+
fill: "currentColor",
|
|
1821
|
+
viewBox: "0 0 20 20",
|
|
1822
|
+
"aria-hidden": "true",
|
|
1823
|
+
children: /* @__PURE__ */ jsx(
|
|
1824
|
+
"path",
|
|
1825
|
+
{
|
|
1826
|
+
fillRule: "evenodd",
|
|
1827
|
+
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z",
|
|
1828
|
+
clipRule: "evenodd"
|
|
1829
|
+
}
|
|
1830
|
+
)
|
|
1831
|
+
}
|
|
1832
|
+
) : /* @__PURE__ */ jsx(
|
|
1833
|
+
"svg",
|
|
1834
|
+
{
|
|
1835
|
+
className: "w-8 h-8",
|
|
1836
|
+
fill: "currentColor",
|
|
1837
|
+
viewBox: "0 0 20 20",
|
|
1838
|
+
"aria-hidden": "true",
|
|
1839
|
+
children: /* @__PURE__ */ jsx(
|
|
1840
|
+
"path",
|
|
1841
|
+
{
|
|
1842
|
+
fillRule: "evenodd",
|
|
1843
|
+
d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z",
|
|
1844
|
+
clipRule: "evenodd"
|
|
1845
|
+
}
|
|
1846
|
+
)
|
|
1847
|
+
}
|
|
1848
|
+
)
|
|
1849
|
+
}
|
|
1850
|
+
),
|
|
1851
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1852
|
+
/* @__PURE__ */ jsx(
|
|
1853
|
+
"button",
|
|
1854
|
+
{
|
|
1855
|
+
onClick: toggleMute,
|
|
1856
|
+
className: "text-white hover:text-blue-400 transition-colors",
|
|
1857
|
+
title: muted ? "Unmute" : "Mute",
|
|
1858
|
+
"aria-label": muted ? "Unmute" : "Mute",
|
|
1859
|
+
"aria-pressed": muted,
|
|
1860
|
+
children: muted || volume === 0 ? /* @__PURE__ */ jsx(
|
|
1861
|
+
"svg",
|
|
1862
|
+
{
|
|
1863
|
+
className: "w-5 h-5",
|
|
1864
|
+
fill: "currentColor",
|
|
1865
|
+
viewBox: "0 0 20 20",
|
|
1866
|
+
children: /* @__PURE__ */ jsx(
|
|
1867
|
+
"path",
|
|
1868
|
+
{
|
|
1869
|
+
fillRule: "evenodd",
|
|
1870
|
+
d: "M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM12.293 7.293a1 1 0 011.414 0L15 8.586l1.293-1.293a1 1 0 111.414 1.414L16.414 10l1.293 1.293a1 1 0 01-1.414 1.414L15 11.414l-1.293 1.293a1 1 0 01-1.414-1.414L13.586 10l-1.293-1.293a1 1 0 010-1.414z",
|
|
1871
|
+
clipRule: "evenodd"
|
|
1872
|
+
}
|
|
1873
|
+
)
|
|
1874
|
+
}
|
|
1875
|
+
) : /* @__PURE__ */ jsx(
|
|
1876
|
+
"svg",
|
|
1877
|
+
{
|
|
1878
|
+
className: "w-5 h-5",
|
|
1879
|
+
fill: "currentColor",
|
|
1880
|
+
viewBox: "0 0 20 20",
|
|
1881
|
+
children: /* @__PURE__ */ jsx(
|
|
1882
|
+
"path",
|
|
1883
|
+
{
|
|
1884
|
+
fillRule: "evenodd",
|
|
1885
|
+
d: "M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z",
|
|
1886
|
+
clipRule: "evenodd"
|
|
1887
|
+
}
|
|
1888
|
+
)
|
|
1889
|
+
}
|
|
1890
|
+
)
|
|
1891
|
+
}
|
|
1892
|
+
),
|
|
1893
|
+
/* @__PURE__ */ jsx(
|
|
1894
|
+
"input",
|
|
1895
|
+
{
|
|
1896
|
+
type: "range",
|
|
1897
|
+
min: 0,
|
|
1898
|
+
max: 1,
|
|
1899
|
+
step: 0.01,
|
|
1900
|
+
value: muted ? 0 : volume || 1,
|
|
1901
|
+
onChange: handleVolumeChange,
|
|
1902
|
+
className: "w-20 h-1 bg-zinc-700 rounded-lg appearance-none cursor-pointer slider",
|
|
1903
|
+
"aria-label": "Volume",
|
|
1904
|
+
"aria-valuemin": 0,
|
|
1905
|
+
"aria-valuemax": 100,
|
|
1906
|
+
"aria-valuenow": muted ? 0 : Math.round(volume * 100),
|
|
1907
|
+
"aria-valuetext": muted ? "Muted" : `${Math.round(volume * 100)}%`,
|
|
1908
|
+
role: "slider"
|
|
1909
|
+
}
|
|
1910
|
+
)
|
|
1911
|
+
] })
|
|
1912
|
+
] }),
|
|
1913
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1914
|
+
!isLiveStream && broadcast.hash && (broadcast.recordingMp4Url || broadcast.recordingMp3Url) && /* @__PURE__ */ jsx(
|
|
1915
|
+
"button",
|
|
1916
|
+
{
|
|
1917
|
+
onClick: () => {
|
|
1918
|
+
const downloadUrl = buildPlaybackUrl(
|
|
1919
|
+
broadcast.id,
|
|
1920
|
+
broadcast.hash,
|
|
1921
|
+
"download"
|
|
1922
|
+
);
|
|
1923
|
+
window.open(downloadUrl, "_blank");
|
|
1924
|
+
},
|
|
1925
|
+
className: "text-white hover:text-blue-400 transition-colors",
|
|
1926
|
+
title: "Download Recording",
|
|
1927
|
+
"aria-label": "Download recording",
|
|
1928
|
+
children: /* @__PURE__ */ jsx(
|
|
1929
|
+
"svg",
|
|
1930
|
+
{
|
|
1931
|
+
className: "w-5 h-5",
|
|
1932
|
+
fill: "none",
|
|
1933
|
+
stroke: "currentColor",
|
|
1934
|
+
viewBox: "0 0 24 24",
|
|
1935
|
+
children: /* @__PURE__ */ jsx(
|
|
1936
|
+
"path",
|
|
1937
|
+
{
|
|
1938
|
+
strokeLinecap: "round",
|
|
1939
|
+
strokeLinejoin: "round",
|
|
1940
|
+
strokeWidth: 2,
|
|
1941
|
+
d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
|
1942
|
+
}
|
|
1943
|
+
)
|
|
1944
|
+
}
|
|
1945
|
+
)
|
|
1946
|
+
}
|
|
1947
|
+
),
|
|
1948
|
+
!isAudioOnly && /* @__PURE__ */ jsx(
|
|
1949
|
+
"button",
|
|
1950
|
+
{
|
|
1951
|
+
onClick: toggleFullscreen,
|
|
1952
|
+
className: "text-white hover:text-blue-400 transition-colors",
|
|
1953
|
+
title: "Fullscreen",
|
|
1954
|
+
"aria-label": "Toggle fullscreen",
|
|
1955
|
+
children: /* @__PURE__ */ jsx(
|
|
1956
|
+
"svg",
|
|
1957
|
+
{
|
|
1958
|
+
className: "w-5 h-5",
|
|
1959
|
+
fill: "currentColor",
|
|
1960
|
+
viewBox: "0 0 20 20",
|
|
1961
|
+
children: /* @__PURE__ */ jsx(
|
|
1962
|
+
"path",
|
|
1963
|
+
{
|
|
1964
|
+
fillRule: "evenodd",
|
|
1965
|
+
d: "M3 4a1 1 0 011-1h4a1 1 0 010 2H6.414l2.293 2.293a1 1 0 11-1.414 1.414L5 6.414V8a1 1 0 01-2 0V4zm9 1a1 1 0 010-2h4a1 1 0 011 1v4a1 1 0 01-2 0V6.414l-2.293 2.293a1 1 0 11-1.414-1.414L13.586 5H12zm-9 7a1 1 0 012 0v1.586l2.293-2.293a1 1 0 111.414 1.414L6.414 15H8a1 1 0 010 2H4a1 1 0 01-1-1v-4zm13-1a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 010-2h1.586l-2.293-2.293a1 1 0 111.414-1.414L15 13.586V12a1 1 0 011-1z",
|
|
1966
|
+
clipRule: "evenodd"
|
|
1967
|
+
}
|
|
1968
|
+
)
|
|
1969
|
+
}
|
|
1970
|
+
)
|
|
1971
|
+
}
|
|
1972
|
+
),
|
|
1973
|
+
hasTranscript && /* @__PURE__ */ jsx(
|
|
1974
|
+
"button",
|
|
1975
|
+
{
|
|
1976
|
+
onClick: () => setShowTranscript(!showTranscript),
|
|
1977
|
+
className: `transition-colors ${showTranscript ? "text-blue-400" : "text-white hover:text-blue-400"}`,
|
|
1978
|
+
title: showTranscript ? "Hide Transcript" : "Show Transcript",
|
|
1979
|
+
"aria-label": showTranscript ? "Hide transcript" : "Show transcript",
|
|
1980
|
+
"aria-pressed": showTranscript,
|
|
1981
|
+
children: /* @__PURE__ */ jsx(
|
|
1982
|
+
"svg",
|
|
1983
|
+
{
|
|
1984
|
+
className: "w-5 h-5",
|
|
1985
|
+
fill: "none",
|
|
1986
|
+
stroke: "currentColor",
|
|
1987
|
+
viewBox: "0 0 24 24",
|
|
1988
|
+
children: /* @__PURE__ */ jsx(
|
|
1989
|
+
"path",
|
|
1990
|
+
{
|
|
1991
|
+
strokeLinecap: "round",
|
|
1992
|
+
strokeLinejoin: "round",
|
|
1993
|
+
strokeWidth: 2,
|
|
1994
|
+
d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
1995
|
+
}
|
|
1996
|
+
)
|
|
1997
|
+
}
|
|
1998
|
+
)
|
|
1999
|
+
}
|
|
2000
|
+
)
|
|
2001
|
+
] })
|
|
2002
|
+
] })
|
|
2003
|
+
] }) : null : /* @__PURE__ */ jsxs("div", { className: "bg-zinc-900/50 backdrop-blur-sm px-4 md:px-6 py-3 md:py-4 rounded-b-lg", children: [
|
|
2004
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
|
|
1618
2005
|
/* @__PURE__ */ jsx(
|
|
1619
2006
|
"input",
|
|
1620
2007
|
{
|
|
1621
2008
|
type: "range",
|
|
1622
2009
|
min: 0,
|
|
1623
|
-
max:
|
|
1624
|
-
step:
|
|
1625
|
-
value:
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
2010
|
+
max: 0.999999,
|
|
2011
|
+
step: "any",
|
|
2012
|
+
value: played || 0,
|
|
2013
|
+
onMouseDown: handleSeekMouseDown,
|
|
2014
|
+
onMouseUp: handleSeekMouseUp,
|
|
2015
|
+
onTouchStart: handleSeekTouchStart,
|
|
2016
|
+
onTouchEnd: handleSeekTouchEnd,
|
|
2017
|
+
onChange: handleSeekChange,
|
|
2018
|
+
className: "w-full h-1 bg-zinc-700 rounded-lg appearance-none cursor-pointer slider",
|
|
2019
|
+
"aria-label": "Seek position",
|
|
1629
2020
|
"aria-valuemin": 0,
|
|
1630
|
-
"aria-valuemax":
|
|
1631
|
-
"aria-valuenow":
|
|
1632
|
-
"aria-valuetext":
|
|
2021
|
+
"aria-valuemax": duration,
|
|
2022
|
+
"aria-valuenow": played * duration,
|
|
2023
|
+
"aria-valuetext": `${formatTime(
|
|
2024
|
+
played * duration
|
|
2025
|
+
)} of ${formatTime(duration)}`,
|
|
1633
2026
|
role: "slider"
|
|
1634
2027
|
}
|
|
1635
|
-
)
|
|
2028
|
+
),
|
|
2029
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-xs text-gray-400 mt-1", children: [
|
|
2030
|
+
/* @__PURE__ */ jsx("span", { children: formatTime((played || 0) * duration) }),
|
|
2031
|
+
/* @__PURE__ */ jsx("span", { children: formatTime(duration) })
|
|
2032
|
+
] })
|
|
2033
|
+
] }),
|
|
2034
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2035
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
2036
|
+
/* @__PURE__ */ jsx(
|
|
2037
|
+
"button",
|
|
2038
|
+
{
|
|
2039
|
+
onClick: handlePlayPause,
|
|
2040
|
+
className: "text-white hover:text-blue-400 transition-colors",
|
|
2041
|
+
title: playing ? "Pause" : "Play",
|
|
2042
|
+
"aria-label": playing ? "Pause" : "Play",
|
|
2043
|
+
"aria-pressed": playing,
|
|
2044
|
+
children: playing ? /* @__PURE__ */ jsx(
|
|
2045
|
+
"svg",
|
|
2046
|
+
{
|
|
2047
|
+
className: "w-8 h-8",
|
|
2048
|
+
fill: "currentColor",
|
|
2049
|
+
viewBox: "0 0 20 20",
|
|
2050
|
+
"aria-hidden": "true",
|
|
2051
|
+
children: /* @__PURE__ */ jsx(
|
|
2052
|
+
"path",
|
|
2053
|
+
{
|
|
2054
|
+
fillRule: "evenodd",
|
|
2055
|
+
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z",
|
|
2056
|
+
clipRule: "evenodd"
|
|
2057
|
+
}
|
|
2058
|
+
)
|
|
2059
|
+
}
|
|
2060
|
+
) : /* @__PURE__ */ jsx(
|
|
2061
|
+
"svg",
|
|
2062
|
+
{
|
|
2063
|
+
className: "w-8 h-8",
|
|
2064
|
+
fill: "currentColor",
|
|
2065
|
+
viewBox: "0 0 20 20",
|
|
2066
|
+
"aria-hidden": "true",
|
|
2067
|
+
children: /* @__PURE__ */ jsx(
|
|
2068
|
+
"path",
|
|
2069
|
+
{
|
|
2070
|
+
fillRule: "evenodd",
|
|
2071
|
+
d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z",
|
|
2072
|
+
clipRule: "evenodd"
|
|
2073
|
+
}
|
|
2074
|
+
)
|
|
2075
|
+
}
|
|
2076
|
+
)
|
|
2077
|
+
}
|
|
2078
|
+
),
|
|
2079
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2080
|
+
/* @__PURE__ */ jsx(
|
|
2081
|
+
"button",
|
|
2082
|
+
{
|
|
2083
|
+
onClick: toggleMute,
|
|
2084
|
+
className: "text-white hover:text-blue-400 transition-colors",
|
|
2085
|
+
title: muted ? "Unmute" : "Mute",
|
|
2086
|
+
"aria-label": muted ? "Unmute" : "Mute",
|
|
2087
|
+
"aria-pressed": muted,
|
|
2088
|
+
children: muted || volume === 0 ? /* @__PURE__ */ jsx(
|
|
2089
|
+
"svg",
|
|
2090
|
+
{
|
|
2091
|
+
className: "w-5 h-5",
|
|
2092
|
+
fill: "currentColor",
|
|
2093
|
+
viewBox: "0 0 20 20",
|
|
2094
|
+
"aria-hidden": "true",
|
|
2095
|
+
children: /* @__PURE__ */ jsx(
|
|
2096
|
+
"path",
|
|
2097
|
+
{
|
|
2098
|
+
fillRule: "evenodd",
|
|
2099
|
+
d: "M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM12.293 7.293a1 1 0 011.414 0L15 8.586l1.293-1.293a1 1 0 111.414 1.414L16.414 10l1.293 1.293a1 1 0 01-1.414 1.414L15 11.414l-1.293 1.293a1 1 0 01-1.414-1.414L13.586 10l-1.293-1.293a1 1 0 010-1.414z",
|
|
2100
|
+
clipRule: "evenodd"
|
|
2101
|
+
}
|
|
2102
|
+
)
|
|
2103
|
+
}
|
|
2104
|
+
) : /* @__PURE__ */ jsx(
|
|
2105
|
+
"svg",
|
|
2106
|
+
{
|
|
2107
|
+
className: "w-5 h-5",
|
|
2108
|
+
fill: "currentColor",
|
|
2109
|
+
viewBox: "0 0 20 20",
|
|
2110
|
+
"aria-hidden": "true",
|
|
2111
|
+
children: /* @__PURE__ */ jsx(
|
|
2112
|
+
"path",
|
|
2113
|
+
{
|
|
2114
|
+
fillRule: "evenodd",
|
|
2115
|
+
d: "M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z",
|
|
2116
|
+
clipRule: "evenodd"
|
|
2117
|
+
}
|
|
2118
|
+
)
|
|
2119
|
+
}
|
|
2120
|
+
)
|
|
2121
|
+
}
|
|
2122
|
+
),
|
|
2123
|
+
/* @__PURE__ */ jsx(
|
|
2124
|
+
"input",
|
|
2125
|
+
{
|
|
2126
|
+
type: "range",
|
|
2127
|
+
min: 0,
|
|
2128
|
+
max: 1,
|
|
2129
|
+
step: 0.01,
|
|
2130
|
+
value: muted ? 0 : volume || 1,
|
|
2131
|
+
onChange: handleVolumeChange,
|
|
2132
|
+
className: "w-20 h-1 bg-zinc-700 rounded-lg appearance-none cursor-pointer slider",
|
|
2133
|
+
"aria-label": "Volume",
|
|
2134
|
+
"aria-valuemin": 0,
|
|
2135
|
+
"aria-valuemax": 100,
|
|
2136
|
+
"aria-valuenow": muted ? 0 : Math.round(volume * 100),
|
|
2137
|
+
"aria-valuetext": muted ? "Muted" : `${Math.round(volume * 100)}%`,
|
|
2138
|
+
role: "slider"
|
|
2139
|
+
}
|
|
2140
|
+
)
|
|
2141
|
+
] })
|
|
2142
|
+
] }),
|
|
2143
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2144
|
+
!isAudioOnly && /* @__PURE__ */ jsx(
|
|
2145
|
+
"button",
|
|
2146
|
+
{
|
|
2147
|
+
onClick: toggleFullscreen,
|
|
2148
|
+
className: "text-white hover:text-blue-400 transition-colors",
|
|
2149
|
+
title: "Toggle fullscreen",
|
|
2150
|
+
"aria-label": "Toggle fullscreen",
|
|
2151
|
+
children: /* @__PURE__ */ jsx(
|
|
2152
|
+
"svg",
|
|
2153
|
+
{
|
|
2154
|
+
className: "w-5 h-5",
|
|
2155
|
+
fill: "currentColor",
|
|
2156
|
+
viewBox: "0 0 20 20",
|
|
2157
|
+
"aria-hidden": "true",
|
|
2158
|
+
children: /* @__PURE__ */ jsx(
|
|
2159
|
+
"path",
|
|
2160
|
+
{
|
|
2161
|
+
fillRule: "evenodd",
|
|
2162
|
+
d: "M3 4a1 1 0 011-1h4a1 1 0 010 2H6.414l2.293 2.293a1 1 0 11-1.414 1.414L5 6.414V8a1 1 0 01-2 0V4zm9 1a1 1 0 010-2h4a1 1 0 011 1v4a1 1 0 01-2 0V6.414l-2.293 2.293a1 1 0 11-1.414-1.414L13.586 5H12zm-9 7a1 1 0 012 0v1.586l2.293-2.293a1 1 0 111.414 1.414L6.414 15H8a1 1 0 010 2H4a1 1 0 01-1-1v-4zm13-1a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 010-2h1.586l-2.293-2.293a1 1 0 111.414-1.414L15 13.586V12a1 1 0 011-1z",
|
|
2163
|
+
clipRule: "evenodd"
|
|
2164
|
+
}
|
|
2165
|
+
)
|
|
2166
|
+
}
|
|
2167
|
+
)
|
|
2168
|
+
}
|
|
2169
|
+
),
|
|
2170
|
+
hasTranscript && /* @__PURE__ */ jsx(
|
|
2171
|
+
"button",
|
|
2172
|
+
{
|
|
2173
|
+
onClick: () => setShowTranscript(!showTranscript),
|
|
2174
|
+
className: `transition-colors ${showTranscript ? "text-blue-400" : "text-white hover:text-blue-400"}`,
|
|
2175
|
+
title: showTranscript ? "Hide transcript" : "Show transcript",
|
|
2176
|
+
"aria-label": showTranscript ? "Hide transcript" : "Show transcript",
|
|
2177
|
+
"aria-pressed": showTranscript,
|
|
2178
|
+
children: /* @__PURE__ */ jsx(
|
|
2179
|
+
"svg",
|
|
2180
|
+
{
|
|
2181
|
+
className: "w-5 h-5",
|
|
2182
|
+
fill: "none",
|
|
2183
|
+
stroke: "currentColor",
|
|
2184
|
+
viewBox: "0 0 24 24",
|
|
2185
|
+
"aria-hidden": "true",
|
|
2186
|
+
children: /* @__PURE__ */ jsx(
|
|
2187
|
+
"path",
|
|
2188
|
+
{
|
|
2189
|
+
strokeLinecap: "round",
|
|
2190
|
+
strokeLinejoin: "round",
|
|
2191
|
+
strokeWidth: 2,
|
|
2192
|
+
d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
2193
|
+
}
|
|
2194
|
+
)
|
|
2195
|
+
}
|
|
2196
|
+
)
|
|
2197
|
+
}
|
|
2198
|
+
)
|
|
2199
|
+
] })
|
|
1636
2200
|
] })
|
|
1637
|
-
] })
|
|
1638
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1639
|
-
!isLiveStream && broadcast.hash && (broadcast.recordingMp4Url || broadcast.recordingMp3Url) && /* @__PURE__ */ jsx(
|
|
1640
|
-
"button",
|
|
1641
|
-
{
|
|
1642
|
-
onClick: () => {
|
|
1643
|
-
const downloadUrl = buildPlaybackUrl(broadcast.id, broadcast.hash, "download");
|
|
1644
|
-
window.open(downloadUrl, "_blank");
|
|
1645
|
-
},
|
|
1646
|
-
className: "text-white hover:text-blue-400 transition-colors",
|
|
1647
|
-
title: "Download Recording",
|
|
1648
|
-
"aria-label": "Download recording",
|
|
1649
|
-
children: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" }) })
|
|
1650
|
-
}
|
|
1651
|
-
),
|
|
1652
|
-
!isAudioOnly && /* @__PURE__ */ jsx(
|
|
1653
|
-
"button",
|
|
1654
|
-
{
|
|
1655
|
-
onClick: toggleFullscreen,
|
|
1656
|
-
className: "text-white hover:text-blue-400 transition-colors",
|
|
1657
|
-
title: "Fullscreen",
|
|
1658
|
-
"aria-label": "Toggle fullscreen",
|
|
1659
|
-
children: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M3 4a1 1 0 011-1h4a1 1 0 010 2H6.414l2.293 2.293a1 1 0 11-1.414 1.414L5 6.414V8a1 1 0 01-2 0V4zm9 1a1 1 0 010-2h4a1 1 0 011 1v4a1 1 0 01-2 0V6.414l-2.293 2.293a1 1 0 11-1.414-1.414L13.586 5H12zm-9 7a1 1 0 012 0v1.586l2.293-2.293a1 1 0 111.414 1.414L6.414 15H8a1 1 0 010 2H4a1 1 0 01-1-1v-4zm13-1a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 010-2h1.586l-2.293-2.293a1 1 0 111.414-1.414L15 13.586V12a1 1 0 011-1z", clipRule: "evenodd" }) })
|
|
1660
|
-
}
|
|
1661
|
-
),
|
|
1662
|
-
hasTranscript && /* @__PURE__ */ jsx(
|
|
1663
|
-
"button",
|
|
1664
|
-
{
|
|
1665
|
-
onClick: () => setShowTranscript(!showTranscript),
|
|
1666
|
-
className: `transition-colors ${showTranscript ? "text-blue-400" : "text-white hover:text-blue-400"}`,
|
|
1667
|
-
title: showTranscript ? "Hide Transcript" : "Show Transcript",
|
|
1668
|
-
"aria-label": showTranscript ? "Hide transcript" : "Show transcript",
|
|
1669
|
-
"aria-pressed": showTranscript,
|
|
1670
|
-
children: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) })
|
|
1671
|
-
}
|
|
1672
|
-
)
|
|
1673
|
-
] })
|
|
1674
|
-
] })
|
|
1675
|
-
] }) : null : /* @__PURE__ */ jsxs("div", { className: "bg-zinc-900/50 backdrop-blur-sm px-4 md:px-6 py-3 md:py-4 rounded-b-lg", children: [
|
|
1676
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
|
|
1677
|
-
/* @__PURE__ */ jsx(
|
|
1678
|
-
"input",
|
|
1679
|
-
{
|
|
1680
|
-
type: "range",
|
|
1681
|
-
min: 0,
|
|
1682
|
-
max: 0.999999,
|
|
1683
|
-
step: "any",
|
|
1684
|
-
value: played || 0,
|
|
1685
|
-
onMouseDown: handleSeekMouseDown,
|
|
1686
|
-
onMouseUp: handleSeekMouseUp,
|
|
1687
|
-
onTouchStart: handleSeekTouchStart,
|
|
1688
|
-
onTouchEnd: handleSeekTouchEnd,
|
|
1689
|
-
onChange: handleSeekChange,
|
|
1690
|
-
className: "w-full h-1 bg-zinc-700 rounded-lg appearance-none cursor-pointer slider"
|
|
1691
|
-
}
|
|
1692
|
-
),
|
|
1693
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-xs text-gray-400 mt-1", children: [
|
|
1694
|
-
/* @__PURE__ */ jsx("span", { children: formatTime((played || 0) * duration) }),
|
|
1695
|
-
/* @__PURE__ */ jsx("span", { children: formatTime(duration) })
|
|
1696
|
-
] })
|
|
2201
|
+
] }))
|
|
1697
2202
|
] }),
|
|
1698
|
-
/* @__PURE__ */ jsxs("div", { className: "flex
|
|
1699
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center
|
|
1700
|
-
/* @__PURE__ */ jsx("button", { onClick: handlePlayPause, className: "text-white hover:text-blue-400 transition-colors", title: playing ? "Pause" : "Play", children: playing ? /* @__PURE__ */ jsx("svg", { className: "w-8 h-8", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z", clipRule: "evenodd" }) }) : /* @__PURE__ */ jsx("svg", { className: "w-8 h-8", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z", clipRule: "evenodd" }) }) }),
|
|
2203
|
+
showTranscript && hasTranscript && /* @__PURE__ */ jsxs("div", { className: "flex-1 md:flex-none min-h-0 w-full md:w-96 bg-zinc-900 border-t md:border-t-0 border-l border-zinc-800 flex flex-col overflow-hidden", children: [
|
|
2204
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-b border-zinc-800 bg-zinc-900/50 shrink-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1701
2205
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1702
|
-
/* @__PURE__ */ jsx("button", { onClick: toggleMute, className: "text-white hover:text-blue-400 transition-colors", title: muted ? "Unmute" : "Mute", children: muted || volume === 0 ? /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM12.293 7.293a1 1 0 011.414 0L15 8.586l1.293-1.293a1 1 0 111.414 1.414L16.414 10l1.293 1.293a1 1 0 01-1.414 1.414L15 11.414l-1.293 1.293a1 1 0 01-1.414-1.414L13.586 10l-1.293-1.293a1 1 0 010-1.414z", clipRule: "evenodd" }) }) : /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z", clipRule: "evenodd" }) }) }),
|
|
1703
2206
|
/* @__PURE__ */ jsx(
|
|
1704
|
-
"
|
|
2207
|
+
"svg",
|
|
1705
2208
|
{
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
2209
|
+
className: "w-5 h-5 text-green-400",
|
|
2210
|
+
fill: "none",
|
|
2211
|
+
stroke: "currentColor",
|
|
2212
|
+
viewBox: "0 0 24 24",
|
|
2213
|
+
children: /* @__PURE__ */ jsx(
|
|
2214
|
+
"path",
|
|
2215
|
+
{
|
|
2216
|
+
strokeLinecap: "round",
|
|
2217
|
+
strokeLinejoin: "round",
|
|
2218
|
+
strokeWidth: 2,
|
|
2219
|
+
d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
2220
|
+
}
|
|
2221
|
+
)
|
|
1713
2222
|
}
|
|
1714
|
-
)
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
"button",
|
|
2223
|
+
),
|
|
2224
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-white", children: "Transcript" }),
|
|
2225
|
+
broadcast.transcriptLanguage && /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400 px-2 py-0.5 bg-zinc-800 rounded", children: broadcast.transcriptLanguage.toUpperCase() })
|
|
2226
|
+
] }),
|
|
2227
|
+
broadcast.transcriptUrl && /* @__PURE__ */ jsx(
|
|
2228
|
+
"a",
|
|
1721
2229
|
{
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
2230
|
+
href: broadcast.transcriptUrl,
|
|
2231
|
+
download: `${broadcast.hash || broadcast.id}-transcript.json`,
|
|
2232
|
+
className: "text-gray-400 hover:text-white transition-colors",
|
|
2233
|
+
title: "Download transcript",
|
|
2234
|
+
"aria-label": "Download transcript as JSON file",
|
|
2235
|
+
children: /* @__PURE__ */ jsx(
|
|
2236
|
+
"svg",
|
|
2237
|
+
{
|
|
2238
|
+
className: "w-5 h-5",
|
|
2239
|
+
fill: "none",
|
|
2240
|
+
stroke: "currentColor",
|
|
2241
|
+
viewBox: "0 0 24 24",
|
|
2242
|
+
"aria-hidden": "true",
|
|
2243
|
+
children: /* @__PURE__ */ jsx(
|
|
2244
|
+
"path",
|
|
2245
|
+
{
|
|
2246
|
+
strokeLinecap: "round",
|
|
2247
|
+
strokeLinejoin: "round",
|
|
2248
|
+
strokeWidth: 2,
|
|
2249
|
+
d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
|
2250
|
+
}
|
|
2251
|
+
)
|
|
2252
|
+
}
|
|
2253
|
+
)
|
|
1726
2254
|
}
|
|
1727
2255
|
)
|
|
1728
|
-
] })
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
"Resume Auto-Scroll"
|
|
1758
|
-
]
|
|
1759
|
-
}
|
|
1760
|
-
) }),
|
|
1761
|
-
/* @__PURE__ */ jsx("div", { ref: transcriptContainerRef, className: "flex-1 min-h-0 overflow-y-auto px-4 py-4 text-gray-300 leading-relaxed", children: isLoadingTranscript ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx("div", { className: "h-6 w-6 border-2 border-gray-600 border-t-blue-500 rounded-full animate-spin" }) }) : transcriptData?.segments && transcriptData.segments.length > 0 ? /* @__PURE__ */ jsx("div", { className: "space-y-4", children: (() => {
|
|
1762
|
-
const filteredSegments = transcriptData.segments.filter((s) => s.words && s.words.length > 0);
|
|
1763
|
-
let globalWordIndex = 0;
|
|
1764
|
-
const wordMap = /* @__PURE__ */ new Map();
|
|
1765
|
-
filteredSegments.forEach((segment) => {
|
|
1766
|
-
segment.words.forEach((_word, wordIndex) => {
|
|
1767
|
-
wordMap.set(`${segment.id}-${wordIndex}`, globalWordIndex++);
|
|
1768
|
-
});
|
|
1769
|
-
});
|
|
1770
|
-
let currentWordIndex = -1;
|
|
1771
|
-
filteredSegments.forEach((segment) => {
|
|
1772
|
-
segment.words.forEach((word, wordIndex) => {
|
|
1773
|
-
const globalIdx = wordMap.get(`${segment.id}-${wordIndex}`) || -1;
|
|
1774
|
-
if (currentTime >= word.start && globalIdx > currentWordIndex) {
|
|
1775
|
-
currentWordIndex = globalIdx;
|
|
2256
|
+
] }) }),
|
|
2257
|
+
!autoScrollEnabled && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 bg-zinc-800/50 border-b border-zinc-700 shrink-0", children: /* @__PURE__ */ jsxs(
|
|
2258
|
+
"button",
|
|
2259
|
+
{
|
|
2260
|
+
onClick: () => setAutoScrollEnabled(true),
|
|
2261
|
+
className: "w-full px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded transition-colors flex items-center justify-center gap-2 text-sm font-medium",
|
|
2262
|
+
"aria-label": "Resume automatic scrolling of transcript",
|
|
2263
|
+
children: [
|
|
2264
|
+
/* @__PURE__ */ jsx(
|
|
2265
|
+
"svg",
|
|
2266
|
+
{
|
|
2267
|
+
className: "w-4 h-4",
|
|
2268
|
+
fill: "none",
|
|
2269
|
+
stroke: "currentColor",
|
|
2270
|
+
viewBox: "0 0 24 24",
|
|
2271
|
+
"aria-hidden": "true",
|
|
2272
|
+
children: /* @__PURE__ */ jsx(
|
|
2273
|
+
"path",
|
|
2274
|
+
{
|
|
2275
|
+
strokeLinecap: "round",
|
|
2276
|
+
strokeLinejoin: "round",
|
|
2277
|
+
strokeWidth: 2,
|
|
2278
|
+
d: "M19 14l-7 7m0 0l-7-7m7 7V3"
|
|
2279
|
+
}
|
|
2280
|
+
)
|
|
2281
|
+
}
|
|
2282
|
+
),
|
|
2283
|
+
"Resume Auto-Scroll"
|
|
2284
|
+
]
|
|
1776
2285
|
}
|
|
1777
|
-
})
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
2286
|
+
) }),
|
|
2287
|
+
/* @__PURE__ */ jsx(
|
|
2288
|
+
"div",
|
|
2289
|
+
{
|
|
2290
|
+
ref: transcriptContainerRef,
|
|
2291
|
+
className: "flex-1 min-h-0 overflow-y-auto px-4 py-4 text-gray-300 leading-relaxed",
|
|
2292
|
+
children: isLoadingTranscript ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx("div", { className: "h-6 w-6 border-2 border-gray-600 border-t-blue-500 rounded-full animate-spin" }) }) : transcriptData?.segments && transcriptData.segments.length > 0 ? /* @__PURE__ */ jsx("div", { className: "space-y-4", children: (() => {
|
|
2293
|
+
const filteredSegments = transcriptData.segments.filter(
|
|
2294
|
+
(s) => s.words && s.words.length > 0
|
|
2295
|
+
);
|
|
2296
|
+
let globalWordIndex = 0;
|
|
2297
|
+
const wordMap = /* @__PURE__ */ new Map();
|
|
2298
|
+
filteredSegments.forEach((segment) => {
|
|
2299
|
+
segment.words.forEach((_word, wordIndex) => {
|
|
2300
|
+
wordMap.set(
|
|
2301
|
+
`${segment.id}-${wordIndex}`,
|
|
2302
|
+
globalWordIndex++
|
|
2303
|
+
);
|
|
2304
|
+
});
|
|
2305
|
+
});
|
|
2306
|
+
let currentWordIndex = -1;
|
|
2307
|
+
filteredSegments.forEach((segment) => {
|
|
2308
|
+
segment.words.forEach((word, wordIndex) => {
|
|
2309
|
+
const globalIdx = wordMap.get(`${segment.id}-${wordIndex}`) || -1;
|
|
2310
|
+
if (currentTime >= word.start && globalIdx > currentWordIndex) {
|
|
2311
|
+
currentWordIndex = globalIdx;
|
|
2312
|
+
}
|
|
2313
|
+
});
|
|
2314
|
+
});
|
|
2315
|
+
const previousWordIndex = lastActiveWordIndex.current;
|
|
2316
|
+
let minHighlightIndex = -1;
|
|
2317
|
+
let maxHighlightIndex = -1;
|
|
2318
|
+
if (currentWordIndex >= 0) {
|
|
2319
|
+
minHighlightIndex = Math.max(
|
|
2320
|
+
0,
|
|
2321
|
+
currentWordIndex - TRAILING_WORDS
|
|
2322
|
+
);
|
|
2323
|
+
maxHighlightIndex = currentWordIndex;
|
|
2324
|
+
if (currentWordIndex <= TRAILING_WORDS) {
|
|
2325
|
+
minHighlightIndex = 0;
|
|
2326
|
+
}
|
|
2327
|
+
lastActiveWordIndex.current = currentWordIndex;
|
|
2328
|
+
} else if (currentWordIndex === -1) {
|
|
2329
|
+
minHighlightIndex = 0;
|
|
2330
|
+
maxHighlightIndex = 0;
|
|
2331
|
+
} else if (previousWordIndex >= 0) {
|
|
2332
|
+
minHighlightIndex = Math.max(
|
|
2333
|
+
0,
|
|
2334
|
+
previousWordIndex - TRAILING_WORDS
|
|
2335
|
+
);
|
|
2336
|
+
maxHighlightIndex = previousWordIndex;
|
|
2337
|
+
}
|
|
2338
|
+
return filteredSegments.map((segment, _segmentIndex) => {
|
|
2339
|
+
const isSegmentActive = currentTime >= segment.start && currentTime < segment.end;
|
|
2340
|
+
return /* @__PURE__ */ jsxs(
|
|
2341
|
+
"div",
|
|
2342
|
+
{
|
|
2343
|
+
ref: isSegmentActive ? transcriptContainerRef : null,
|
|
2344
|
+
className: "flex gap-3 items-start leading-relaxed",
|
|
2345
|
+
children: [
|
|
2346
|
+
/* @__PURE__ */ jsx(
|
|
2347
|
+
"button",
|
|
2348
|
+
{
|
|
2349
|
+
onClick: () => handleWordClick(segment.start),
|
|
2350
|
+
className: "text-xs text-gray-500 hover:text-gray-300 transition-colors shrink-0 pt-0.5 font-mono",
|
|
2351
|
+
title: `Jump to ${formatTimestamp(segment.start)}`,
|
|
2352
|
+
children: formatTimestamp(segment.start)
|
|
2353
|
+
}
|
|
2354
|
+
),
|
|
2355
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: segment.words.map((word, wordIndex) => {
|
|
2356
|
+
const thisGlobalIndex = wordMap.get(`${segment.id}-${wordIndex}`) ?? -1;
|
|
2357
|
+
const isTimestampActive = currentTime >= word.start && currentTime < word.end;
|
|
2358
|
+
const isInGapFill = minHighlightIndex >= 0 && thisGlobalIndex >= minHighlightIndex && thisGlobalIndex <= maxHighlightIndex;
|
|
2359
|
+
const isWordActive = isInGapFill;
|
|
2360
|
+
return /* @__PURE__ */ jsxs(
|
|
2361
|
+
"span",
|
|
2362
|
+
{
|
|
2363
|
+
ref: isTimestampActive ? activeWordRef : null,
|
|
2364
|
+
onClick: () => handleWordClick(word.start),
|
|
2365
|
+
className: `cursor-pointer ${isWordActive ? "text-blue-400 font-medium active-word" : isSegmentActive ? "text-gray-200 segment-word" : "text-gray-400 hover:text-gray-200 inactive-word"}`,
|
|
2366
|
+
title: `${formatTime(
|
|
2367
|
+
word.start
|
|
2368
|
+
)} - ${formatTime(word.end)}`,
|
|
2369
|
+
children: [
|
|
2370
|
+
word.word,
|
|
2371
|
+
" "
|
|
2372
|
+
]
|
|
2373
|
+
},
|
|
2374
|
+
`${segment.id}-${wordIndex}`
|
|
2375
|
+
);
|
|
2376
|
+
}) })
|
|
2377
|
+
]
|
|
2378
|
+
},
|
|
2379
|
+
segment.id
|
|
2380
|
+
);
|
|
2381
|
+
});
|
|
2382
|
+
})() }) : transcriptData?.words && transcriptData.words.length > 0 ? /* @__PURE__ */ jsx("div", { className: "space-y-1", children: transcriptData.words.map((word, index) => {
|
|
2383
|
+
const isActive = currentTime >= word.start && currentTime < word.end;
|
|
2384
|
+
return /* @__PURE__ */ jsxs(
|
|
2385
|
+
"span",
|
|
2386
|
+
{
|
|
2387
|
+
ref: isActive ? activeWordRef : null,
|
|
2388
|
+
onClick: () => handleWordClick(word.start),
|
|
2389
|
+
className: `inline-block cursor-pointer transition-all ${isActive ? "text-blue-400 underline decoration-blue-400 decoration-2 font-medium" : "text-gray-400 hover:text-gray-200"}`,
|
|
2390
|
+
title: `${formatTime(word.start)} - ${formatTime(
|
|
2391
|
+
word.end
|
|
2392
|
+
)}`,
|
|
2393
|
+
children: [
|
|
2394
|
+
word.word,
|
|
2395
|
+
" "
|
|
2396
|
+
]
|
|
2397
|
+
},
|
|
2398
|
+
index
|
|
2399
|
+
);
|
|
2400
|
+
}) }) : /* @__PURE__ */ jsxs("div", { className: "text-center text-gray-500 py-8", children: [
|
|
2401
|
+
/* @__PURE__ */ jsx("p", { className: "mb-2", children: "Transcript data not available" }),
|
|
2402
|
+
transcriptData && /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-600", children: [
|
|
2403
|
+
"Debug: segments=",
|
|
2404
|
+
transcriptData.segments ? transcriptData.segments.length : 0,
|
|
2405
|
+
", words=",
|
|
2406
|
+
transcriptData.words ? transcriptData.words.length : 0
|
|
2407
|
+
] })
|
|
2408
|
+
] })
|
|
2409
|
+
}
|
|
2410
|
+
)
|
|
1853
2411
|
] })
|
|
1854
|
-
] })
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
}),
|
|
1865
|
-
/* @__PURE__ */ jsx("style", { children: `
|
|
2412
|
+
] }),
|
|
2413
|
+
renderClipCreator && renderClipCreator({
|
|
2414
|
+
isOpen: showClipCreator,
|
|
2415
|
+
onClose: () => setShowClipCreator(false),
|
|
2416
|
+
sourceVideoUrl: playbackType === "mp4" ? playbackUrl || void 0 : void 0,
|
|
2417
|
+
sourceAudioUrl: playbackType === "mp3" ? playbackUrl || void 0 : void 0,
|
|
2418
|
+
sourceDuration: duration,
|
|
2419
|
+
onPauseParent: () => setPlaying(false)
|
|
2420
|
+
}),
|
|
2421
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
1866
2422
|
.slider::-webkit-slider-thumb {
|
|
1867
2423
|
appearance: none;
|
|
1868
2424
|
width: 14px;
|
|
@@ -1902,91 +2458,12 @@ function BroadcastPlayer({
|
|
|
1902
2458
|
transition: color 0.15s ease-in;
|
|
1903
2459
|
}
|
|
1904
2460
|
` })
|
|
1905
|
-
|
|
1906
|
-
}
|
|
1907
|
-
function BroadcastPlayerModal({
|
|
1908
|
-
broadcast,
|
|
1909
|
-
isOpen,
|
|
1910
|
-
onClose,
|
|
1911
|
-
appId,
|
|
1912
|
-
contentId,
|
|
1913
|
-
foreignId,
|
|
1914
|
-
foreignTier,
|
|
1915
|
-
renderClipCreator,
|
|
1916
|
-
className,
|
|
1917
|
-
enableKeyboardShortcuts = false
|
|
1918
|
-
}) {
|
|
1919
|
-
const closeButtonRef = useRef(null);
|
|
1920
|
-
const previousActiveElement = useRef(null);
|
|
1921
|
-
useEffect(() => {
|
|
1922
|
-
if (!isOpen) return;
|
|
1923
|
-
previousActiveElement.current = document.activeElement;
|
|
1924
|
-
setTimeout(() => {
|
|
1925
|
-
closeButtonRef.current?.focus();
|
|
1926
|
-
}, 100);
|
|
1927
|
-
return () => {
|
|
1928
|
-
if (previousActiveElement.current) {
|
|
1929
|
-
previousActiveElement.current.focus();
|
|
1930
|
-
}
|
|
1931
|
-
};
|
|
1932
|
-
}, [isOpen]);
|
|
1933
|
-
useEffect(() => {
|
|
1934
|
-
if (!isOpen) return;
|
|
1935
|
-
const handleKeyDown = (e) => {
|
|
1936
|
-
if (e.key === "Escape") {
|
|
1937
|
-
onClose();
|
|
1938
|
-
}
|
|
1939
|
-
};
|
|
1940
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
1941
|
-
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1942
|
-
}, [isOpen, onClose]);
|
|
1943
|
-
if (!isOpen) return null;
|
|
1944
|
-
const handleBackdropClick = (e) => {
|
|
1945
|
-
if (e.target === e.currentTarget) {
|
|
1946
|
-
onClose();
|
|
1947
|
-
}
|
|
1948
|
-
};
|
|
1949
|
-
return /* @__PURE__ */ jsx(
|
|
1950
|
-
"div",
|
|
1951
|
-
{
|
|
1952
|
-
className: "fixed inset-0 bg-black/70 backdrop-blur-xl flex items-center justify-center z-50 p-2 sm:p-4",
|
|
1953
|
-
onClick: handleBackdropClick,
|
|
1954
|
-
role: "dialog",
|
|
1955
|
-
"aria-modal": "true",
|
|
1956
|
-
"aria-label": "Broadcast player",
|
|
1957
|
-
children: /* @__PURE__ */ jsxs("div", { className: "relative w-full max-w-7xl max-h-[95vh] sm:max-h-[90vh] overflow-hidden", children: [
|
|
1958
|
-
/* @__PURE__ */ jsx(
|
|
1959
|
-
"button",
|
|
1960
|
-
{
|
|
1961
|
-
ref: closeButtonRef,
|
|
1962
|
-
onClick: onClose,
|
|
1963
|
-
className: "absolute top-2 right-2 sm:top-4 sm:right-4 z-10 text-gray-400 hover:text-white text-2xl leading-none transition-colors w-8 h-8 flex items-center justify-center bg-black/50 rounded-full",
|
|
1964
|
-
title: "Close (ESC)",
|
|
1965
|
-
"aria-label": "Close player",
|
|
1966
|
-
children: "\xD7"
|
|
1967
|
-
}
|
|
1968
|
-
),
|
|
1969
|
-
/* @__PURE__ */ jsx(
|
|
1970
|
-
BroadcastPlayer,
|
|
1971
|
-
{
|
|
1972
|
-
broadcast,
|
|
1973
|
-
appId,
|
|
1974
|
-
contentId,
|
|
1975
|
-
foreignId,
|
|
1976
|
-
foreignTier,
|
|
1977
|
-
renderClipCreator,
|
|
1978
|
-
className,
|
|
1979
|
-
enableKeyboardShortcuts,
|
|
1980
|
-
onError: (error) => {
|
|
1981
|
-
debug.error("[BroadcastPlayerModal] Player error:", error);
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
)
|
|
1985
|
-
] })
|
|
2461
|
+
]
|
|
1986
2462
|
}
|
|
1987
2463
|
);
|
|
2464
|
+
return playerContent;
|
|
1988
2465
|
}
|
|
1989
|
-
var
|
|
2466
|
+
var DialtribePlayerErrorBoundary = class extends Component {
|
|
1990
2467
|
constructor(props) {
|
|
1991
2468
|
super(props);
|
|
1992
2469
|
this.handleReset = () => {
|
|
@@ -2112,7 +2589,85 @@ var BroadcastPlayerErrorBoundary = class extends Component {
|
|
|
2112
2589
|
return this.props.children;
|
|
2113
2590
|
}
|
|
2114
2591
|
};
|
|
2592
|
+
var overlayStyles = {
|
|
2593
|
+
modal: {
|
|
2594
|
+
backdrop: "bg-black/70 backdrop-blur-xl p-2 sm:p-4",
|
|
2595
|
+
container: "max-w-7xl max-h-[95vh] sm:max-h-[90vh]"
|
|
2596
|
+
},
|
|
2597
|
+
fullscreen: {
|
|
2598
|
+
backdrop: "bg-black",
|
|
2599
|
+
container: "h-full"
|
|
2600
|
+
}
|
|
2601
|
+
};
|
|
2602
|
+
function DialtribeOverlay({
|
|
2603
|
+
isOpen,
|
|
2604
|
+
onClose,
|
|
2605
|
+
mode = "modal",
|
|
2606
|
+
children,
|
|
2607
|
+
ariaLabel = "Dialog",
|
|
2608
|
+
showCloseButton = true,
|
|
2609
|
+
closeOnBackdropClick = true,
|
|
2610
|
+
closeOnEsc = true
|
|
2611
|
+
}) {
|
|
2612
|
+
const closeButtonRef = useRef(null);
|
|
2613
|
+
const previousActiveElement = useRef(null);
|
|
2614
|
+
useEffect(() => {
|
|
2615
|
+
if (!isOpen) return;
|
|
2616
|
+
previousActiveElement.current = document.activeElement;
|
|
2617
|
+
setTimeout(() => {
|
|
2618
|
+
closeButtonRef.current?.focus();
|
|
2619
|
+
}, 100);
|
|
2620
|
+
return () => {
|
|
2621
|
+
if (previousActiveElement.current) {
|
|
2622
|
+
previousActiveElement.current.focus();
|
|
2623
|
+
}
|
|
2624
|
+
};
|
|
2625
|
+
}, [isOpen]);
|
|
2626
|
+
useEffect(() => {
|
|
2627
|
+
if (!isOpen || !closeOnEsc) return;
|
|
2628
|
+
const handleKeyDown = (e) => {
|
|
2629
|
+
if (e.key === "Escape") {
|
|
2630
|
+
onClose();
|
|
2631
|
+
}
|
|
2632
|
+
};
|
|
2633
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
2634
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
2635
|
+
}, [isOpen, onClose, closeOnEsc]);
|
|
2636
|
+
if (!isOpen) {
|
|
2637
|
+
return null;
|
|
2638
|
+
}
|
|
2639
|
+
const handleBackdropClick = (e) => {
|
|
2640
|
+
if (closeOnBackdropClick && e.target === e.currentTarget) {
|
|
2641
|
+
onClose();
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
const styles = overlayStyles[mode];
|
|
2645
|
+
return /* @__PURE__ */ jsx(
|
|
2646
|
+
"div",
|
|
2647
|
+
{
|
|
2648
|
+
className: `fixed inset-0 flex items-center justify-center z-50 ${styles.backdrop}`,
|
|
2649
|
+
onClick: handleBackdropClick,
|
|
2650
|
+
role: "dialog",
|
|
2651
|
+
"aria-modal": "true",
|
|
2652
|
+
"aria-label": ariaLabel,
|
|
2653
|
+
children: /* @__PURE__ */ jsxs("div", { className: `relative w-full overflow-hidden ${styles.container}`, children: [
|
|
2654
|
+
showCloseButton && /* @__PURE__ */ jsx(
|
|
2655
|
+
"button",
|
|
2656
|
+
{
|
|
2657
|
+
ref: closeButtonRef,
|
|
2658
|
+
onClick: onClose,
|
|
2659
|
+
className: "absolute top-2 right-2 sm:top-4 sm:right-4 z-10 text-gray-400 hover:text-white text-2xl leading-none transition-colors w-8 h-8 flex items-center justify-center bg-black/50 rounded-full",
|
|
2660
|
+
title: "Close (ESC)",
|
|
2661
|
+
"aria-label": "Close",
|
|
2662
|
+
children: "\xD7"
|
|
2663
|
+
}
|
|
2664
|
+
),
|
|
2665
|
+
children
|
|
2666
|
+
] })
|
|
2667
|
+
}
|
|
2668
|
+
);
|
|
2669
|
+
}
|
|
2115
2670
|
|
|
2116
|
-
export { AudioWaveform,
|
|
2117
|
-
//# sourceMappingURL=
|
|
2118
|
-
//# sourceMappingURL=
|
|
2671
|
+
export { AudioWaveform, CDN_DOMAIN, DIALTRIBE_API_BASE, DialtribeClient, DialtribeOverlay, DialtribePlayer, DialtribePlayerErrorBoundary, DialtribeProvider, ENDPOINTS, HTTP_STATUS, LoadingSpinner, buildBroadcastCdnUrl, buildBroadcastS3KeyPrefix, formatTime, useDialtribe };
|
|
2672
|
+
//# sourceMappingURL=dialtribe-player.mjs.map
|
|
2673
|
+
//# sourceMappingURL=dialtribe-player.mjs.map
|