diff --git a/client/src/pages/Recorder.tsx b/client/src/pages/Recorder.tsx index c96585f..d9a401e 100644 --- a/client/src/pages/Recorder.tsx +++ b/client/src/pages/Recorder.tsx @@ -123,6 +123,25 @@ function getArchiveProgress(session: MediaSession | null) { } } +function getArchivePhaseLabel(mode: RecorderMode, session: MediaSession | null, taskProgress?: number | null) { + if (mode === "finalizing" && !taskProgress) { + return "正在提交归档任务"; + } + if (session?.archiveStatus === "completed") { + return "回放已生成"; + } + if (session?.archiveStatus === "failed") { + return "归档失败"; + } + if (session?.archiveStatus === "processing") { + return "正在生成回放"; + } + if (session?.archiveStatus === "queued") { + return "正在合并分段"; + } + return "等待归档"; +} + function formatFileSize(bytes: number) { if (!bytes) return "0 MB"; return `${(bytes / 1024 / 1024).toFixed(bytes > 20 * 1024 * 1024 ? 1 : 2)} MB`; @@ -174,6 +193,7 @@ export default function Recorder() { const [isOnline, setIsOnline] = useState(() => navigator.onLine); const [reconnectAttempts, setReconnectAttempts] = useState(0); const [queuedSegments, setQueuedSegments] = useState(0); + const [queuedBytes, setQueuedBytes] = useState(0); const [uploadedSegments, setUploadedSegments] = useState(0); const [uploadBytes, setUploadBytes] = useState(0); const [cameraError, setCameraError] = useState(""); @@ -187,8 +207,10 @@ export default function Recorder() { const mobile = useMemo(() => isMobileDevice(), []); const mimeType = useMemo(() => pickRecorderMimeType(), []); const currentPlaybackUrl = mediaSession?.playback.mp4Url || mediaSession?.playback.webmUrl || ""; - const archiveProgress = getArchiveProgress(mediaSession); const archiveTaskQuery = useBackgroundTask(archiveTaskId); + const archiveProgress = archiveTaskQuery.data?.progress ?? getArchiveProgress(mediaSession); + const archivePhaseLabel = getArchivePhaseLabel(mode, mediaSession, archiveTaskQuery.data?.progress); + const totalUploadBytes = uploadBytes + queuedBytes; const syncSessionState = useCallback((session: MediaSession | null) => { currentSessionRef.current = session; @@ -198,6 +220,11 @@ export default function Recorder() { setUploadBytes(session?.uploadedBytes ?? 0); }, []); + const syncQueuedUploadState = useCallback(() => { + setQueuedSegments(pendingUploadsRef.current.length); + setQueuedBytes(pendingUploadsRef.current.reduce((total, item) => total + item.blob.size, 0)); + }, []); + useEffect(() => { modeRef.current = mode; }, [mode]); @@ -328,7 +355,7 @@ export default function Recorder() { const processUploadQueue = useCallback(async () => { if (uploadInFlightRef.current || pendingUploadsRef.current.length === 0 || !currentSessionRef.current?.id || !navigator.onLine) { - setQueuedSegments(pendingUploadsRef.current.length); + syncQueuedUploadState(); return; } @@ -343,7 +370,7 @@ export default function Recorder() { nextSegment.blob ); pendingUploadsRef.current.shift(); - setQueuedSegments(pendingUploadsRef.current.length); + syncQueuedUploadState(); syncSessionState(response.session); } } catch (error: any) { @@ -351,7 +378,7 @@ export default function Recorder() { } finally { uploadInFlightRef.current = false; } - }, [syncSessionState]); + }, [syncQueuedUploadState, syncSessionState]); const enqueueSegment = useCallback(async (blob: Blob, durationForSegmentMs: number) => { if (!blob.size) return; @@ -360,9 +387,9 @@ export default function Recorder() { durationMs: Math.max(1, durationForSegmentMs), blob, }); - setQueuedSegments(pendingUploadsRef.current.length); + syncQueuedUploadState(); await processUploadQueue(); - }, [processUploadQueue]); + }, [processUploadQueue, syncQueuedUploadState]); const flushPendingSegments = useCallback(async () => { while (pendingUploadsRef.current.length > 0 || uploadInFlightRef.current) { @@ -588,6 +615,7 @@ export default function Recorder() { setUploadedSegments(0); setUploadBytes(0); setQueuedSegments(0); + setQueuedBytes(0); setReconnectAttempts(0); setArchiveTaskId(null); segmentSequenceRef.current = 0; @@ -664,6 +692,7 @@ export default function Recorder() { setMarkers([]); setDurationMs(0); setQueuedSegments(0); + setQueuedBytes(0); setUploadedSegments(0); setUploadBytes(0); setReconnectAttempts(0); @@ -976,8 +1005,8 @@ export default function Recorder() {
- {mediaSession?.archiveStatus === "completed" + {archiveTaskQuery.data?.message + ? `${archiveTaskQuery.data.message},当前已上传 ${formatFileSize(uploadBytes)}。` + : mediaSession?.archiveStatus === "completed" ? "归档完成,已生成可回放文件并同步到视频库。" : mediaSession?.archiveStatus === "failed" ? mediaSession.lastError || "归档失败,请检查媒体服务日志。" - : "Worker 正在合并分段并生成归档文件。"} + : `Worker 正在合并分段并生成归档文件,当前已上传 ${formatFileSize(uploadBytes)}。`}