/** * Quality meter component for Smart Montage Planner * Displays real-time quality metrics for video fragments and overall plan */ import { AlertTriangle, CheckCircle, Minus, TrendingDown, TrendingUp } from "lucide-react" import { useTranslation } from "react-i18next" import { Badge } from "@/components/ui/badge" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Progress } from "@/components/ui/progress" import { cn } from "@/lib/utils" import type { AudioAnalysis, MomentScore, VideoAnalysis } from "../../types" interface QualityMeterProps { videoAnalysis?: VideoAnalysis audioAnalysis?: AudioAnalysis momentScore?: MomentScore className?: string } export function QualityMeter({ videoAnalysis, audioAnalysis, momentScore, className }: QualityMeterProps) { const { t } = useTranslation() const getQualityColor = (score: number) => { if (score >= 80) return "text-green-600" if (score >= 60) return "text-yellow-600" return "text-red-600" } const getQualityBadge = (score: number) => { if (score >= 90) return { label: t("montage-planner.quality.excellent"), variant: "default" as const } if (score >= 75) return { label: t("montage-planner.quality.good"), variant: "secondary" as const } if (score >= 50) return { label: t("montage-planner.quality.fair"), variant: "outline" as const } return { label: t("montage-planner.quality.poor"), variant: "destructive" as const } } const getTrendIcon = (current: number, previous?: number) => { if (!previous) return if (current > previous + 5) return if (current < previous - 5) return return } const overallScore = momentScore?.totalScore || 0 const qualityBadge = getQualityBadge(overallScore) return ( {t("montage-planner.analysis.quality")} {t("common.qualityAssessment")} {/* Overall Quality */}
{t("common.overallQuality")}
{qualityBadge.label} {overallScore.toFixed(0)}%
{/* Video Metrics */} {videoAnalysis && (

{t("montage-planner.analysis.title")} {(videoAnalysis.quality?.sharpness || 0) >= 70 ? ( ) : ( )}

{t("common.resolution")} {videoAnalysis.quality?.resolution?.width || 0}x{videoAnalysis.quality?.resolution?.height || 0}
{t("common.frameRate")} {videoAnalysis.quality?.frameRate || 0} fps
{t("common.bitrate")} {((videoAnalysis.quality?.bitrate || 0) / 1000000).toFixed(1)} Mbps
{t("montage-planner.quality.sharpness")}
{(videoAnalysis.quality?.sharpness || 0).toFixed(0)}% {getTrendIcon(videoAnalysis.quality?.sharpness || 0)}
{t("montage-planner.quality.stability")}
{(videoAnalysis.quality?.stability || 0).toFixed(0)}% {getTrendIcon(videoAnalysis.quality?.stability || 0)}
{t("montage-planner.quality.exposure")}
{(videoAnalysis.quality?.exposure || 0).toFixed(0)}% {getTrendIcon(videoAnalysis.quality?.exposure || 0)}
{/* Composition */}
{t("montage-planner.quality.composition")}
{t("common.ruleOfThirds")}
{t("common.balance")}
{t("common.leadingLines")}
)} {/* Audio Metrics */} {audioAnalysis && (

{t("montage-planner.analysis.audio")} {(audioAnalysis.quality?.clarity || 0) >= 70 ? ( ) : ( )}

{t("common.sampleRate")} {audioAnalysis.quality?.sampleRate || 0} Hz
{t("common.bitDepth")} {audioAnalysis.quality?.bitDepth || 0} bit
{t("common.noiseLevel")} {(audioAnalysis.quality?.noiseLevel || 0).toFixed(0)}%
{t("common.speechClarity")}
{(audioAnalysis.quality?.clarity || 0).toFixed(0)}% {getTrendIcon(audioAnalysis.quality?.clarity || 0)}
{t("common.musicEnergy")}
{(audioAnalysis.music?.energy || 0).toFixed(0)}% {getTrendIcon(audioAnalysis.music?.energy || 0)}
{t("common.silenceRatio")} {( 100 - (audioAnalysis.content?.speechPresence || 0) - (audioAnalysis.content?.musicPresence || 0) ).toFixed(0)} %
)} {/* Moment Score Breakdown */} {momentScore && (

{t("montage-planner.analysis.moments")}

{t("common.visualImpact")}
{t("common.technicalQuality")}
{t("common.emotionalValue")}
{t("common.relevance")}
)}
) }