/** * Диалог синхронизации по аудио с визуализацией процесса */ import { CheckCircle2, Loader2, Music, XCircle } from "lucide-react" import { useCallback, useEffect, useState } from "react" import { Alert, AlertDescription } from "@/components/ui/alert" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { Progress } from "@/components/ui/progress" import { cn } from "@/lib/utils" import type { AudioSyncProgress, SyncResult } from "../services/audio-sync" interface AudioSyncDialogProps { isOpen: boolean onClose: () => void onSync: () => Promise angleCount: number } interface SyncResultItem { angleName: string result: SyncResult } export function AudioSyncDialog({ isOpen, onClose, onSync, angleCount }: AudioSyncDialogProps) { const [isProcessing, setIsProcessing] = useState(false) const [progress, setProgress] = useState(null) const [results, setResults] = useState(null) const [error, setError] = useState(null) // Сброс состояния при открытии/закрытии useEffect(() => { if (!isOpen) { setProgress(null) setResults(null) setError(null) setIsProcessing(false) } }, [isOpen]) // Запуск синхронизации const handleStartSync = useCallback(async () => { setIsProcessing(true) setError(null) setResults(null) try { // Имитация прогресса для демонстрации const progressSteps: AudioSyncProgress[] = [ { current: 0, total: angleCount - 1, phase: "loading", message: "Загрузка аудио..." }, { current: 1, total: angleCount - 1, phase: "analyzing", message: "Анализ аудиодорожек..." }, { current: 2, total: angleCount - 1, phase: "correlating", message: "Поиск совпадений..." }, ] // Показываем прогресс for (const step of progressSteps) { setProgress(step) await new Promise((resolve) => setTimeout(resolve, 800)) } // Выполняем синхронизацию const syncResults = await onSync() // Форматируем результаты для отображения const formattedResults: SyncResultItem[] = syncResults.map((result, index) => ({ angleName: `Камера ${index + 2}`, // +2 так как первая камера - базовая result, })) setResults(formattedResults) setProgress({ current: angleCount - 1, total: angleCount - 1, phase: "complete", message: "Синхронизация завершена!", }) } catch (err) { console.error("[AudioSyncDialog] Sync error:", err) setError("Произошла ошибка при синхронизации. Попробуйте еще раз.") } finally { setIsProcessing(false) } }, [angleCount, onSync]) // Получение иконки для фазы const getPhaseIcon = (phase: AudioSyncProgress["phase"]) => { switch (phase) { case "loading": return case "analyzing": return case "correlating": return case "complete": return default: return } } // Получение цвета для уверенности const getConfidenceColor = (confidence: number) => { if (confidence >= 0.8) return "text-green-500" if (confidence >= 0.6) return "text-yellow-500" return "text-red-500" } // Вычисление прогресса в процентах const progressPercent = progress ? Math.round((progress.current / progress.total) * 100) : 0 return ( !open && !isProcessing && onClose()}> Синхронизация по аудио Автоматический анализ аудиодорожек для синхронизации камер
{/* Информация о количестве камер */} {!isProcessing && !results && ( Будет проанализировано {angleCount} камер. Процесс может занять несколько секунд. )} {/* Прогресс синхронизации */} {isProcessing && progress && (
{getPhaseIcon(progress.phase)} {progress.message}
Обработано: {progress.current} из {progress.total} {progressPercent}%
)} {/* Результаты синхронизации */} {results && (
Результаты синхронизации
{results.map((item, index) => (
0.7 ? "bg-green-50 dark:bg-green-950/20" : "bg-yellow-50 dark:bg-yellow-950/20", )} >
{item.angleName} {item.result.method === "audio" && ( Аудио )}
{item.result.offset > 0 ? "+" : ""} {item.result.offset.toFixed(3)}s {Math.round(item.result.confidence * 100)}%
))}
{/* Общая статистика */} {results.length > 0 && (
Средняя уверенность: {Math.round((results.reduce((sum, r) => sum + r.result.confidence, 0) / results.length) * 100)}%
)}
)} {/* Ошибка */} {error && ( {error} )} {/* Кнопки действий */}
{!isProcessing && !results && ( <> )} {results && }
) }