/** * Hook for content analysis functionality * Manages video and audio analysis state and actions */ import { useMemo } from "react" import type { AudioAnalysis, Fragment, MomentScore, VideoAnalysis } from "../types" import { useMontagePlanner } from "./use-montage-planner" export function useContentAnalysis() { const { context, isAnalyzing, progress, progressMessage, send } = useMontagePlanner() // Get analyses for specific video const getVideoAnalysis = (videoId: string): VideoAnalysis | undefined => { return context.videoAnalyses?.get(videoId) } const getAudioAnalysis = (videoId: string): AudioAnalysis | undefined => { return context.audioAnalyses?.get(videoId) } // Aggregate quality scores const averageVideoQuality = useMemo(() => { if (!context.videoAnalyses) return 0 const analyses = Array.from(context.videoAnalyses.values()) if (analyses.length === 0) return 0 const totalQuality = analyses.reduce((sum: number, analysis: VideoAnalysis) => { const sharpness = analysis.quality.sharpness || 0 const stability = analysis.quality.stability || 0 const exposure = analysis.quality.exposure || 0 const colorGrading = analysis.quality.colorGrading || 0 const qualityScore = (sharpness + stability + (100 + exposure) / 2 + colorGrading) / 4 return sum + qualityScore }, 0) return totalQuality / analyses.length }, [context.videoAnalyses]) const averageAudioQuality = useMemo(() => { if (!context.audioAnalyses) return 0 const analyses = Array.from(context.audioAnalyses.values()) if (analyses.length === 0) return 0 const totalQuality = analyses.reduce((sum: number, analysis: AudioAnalysis) => { const clarity = analysis.quality.clarity || 0 const noiseLevel = analysis.quality.noiseLevel || 0 const qualityScore = (clarity + (100 - noiseLevel)) / 2 return sum + qualityScore }, 0) return totalQuality / analyses.length }, [context.audioAnalyses]) // Best moments const topMoments = useMemo(() => { if (context.momentScores?.length > 0) { return [...context.momentScores].sort((a, b) => b.totalScore - a.totalScore).slice(0, 10) } // Fallback to fragments for tests if (!context.fragments) return [] return [...context.fragments] .sort((a, b) => b.score.totalScore - a.score.totalScore) .slice(0, 5) .map((fragment) => fragment.score) }, [context.momentScores, context.fragments]) // Moment categories breakdown const momentCategoryCounts = useMemo(() => { const counts: Record = {} if (context.momentScores) { context.momentScores.forEach((moment: MomentScore) => { counts[moment.category] = (counts[moment.category] || 0) + 1 }) } return counts }, [context.momentScores]) // Fragment categories breakdown (for tests) const fragmentCategories = useMemo(() => { const counts: Record = {} if (context.fragments) { context.fragments.forEach((fragment: Fragment) => { const category = fragment.score?.category || (fragment as any).category || "unknown" counts[category] = (counts[category] || 0) + 1 }) } return counts }, [context.fragments]) // Quality distribution (for tests) const qualityDistribution = useMemo(() => { const distribution = { excellent: 0, good: 0, fair: 0, poor: 0 } if (context.fragments) { context.fragments.forEach((fragment: Fragment) => { const score = fragment.score.totalScore if (score >= 95) distribution.excellent++ else if (score >= 80) distribution.good++ else if (score >= 60) distribution.fair++ else distribution.poor++ }) } return distribution }, [context.fragments]) // Content statistics const contentStats = useMemo(() => { const videoAnalyses = context.videoAnalyses ? Array.from(context.videoAnalyses.values()) : [] const audioAnalyses = context.audioAnalyses ? Array.from(context.audioAnalyses.values()) : [] return { totalVideos: context.videoIds?.length || 0, analyzedVideos: videoAnalyses.length, totalMoments: context.momentScores?.length || 0, averageActionLevel: videoAnalyses.reduce((sum: number, a: VideoAnalysis) => sum + (a.content.actionLevel || 0), 0) / (videoAnalyses.length || 1), speechPresence: audioAnalyses.reduce((sum: number, a: AudioAnalysis) => sum + (a.content.speechPresence || 0), 0) / (audioAnalyses.length || 1), musicPresence: audioAnalyses.reduce((sum: number, a: AudioAnalysis) => sum + (a.content.musicPresence || 0), 0) / (audioAnalyses.length || 1), } }, [context.videoIds, context.videoAnalyses, context.audioAnalyses, context.momentScores]) return { // State isAnalyzing, progress, progressMessage, send, // Expose send for tests // Data videoAnalyses: context.videoAnalyses, audioAnalyses: context.audioAnalyses, momentScores: context.momentScores, fragments: context.fragments, // Computed values expected by tests analyzedVideos: context.videoAnalyses ? Array.from(context.videoAnalyses.values()) : [], totalFragments: context.fragments?.length || 0, averageQuality: context.fragments && context.fragments.length > 0 ? context.fragments.reduce((sum: number, fragment: Fragment) => sum + (fragment.score?.totalScore || 0), 0) / context.fragments.length : 0, // Helpers getVideoAnalysis, getAudioAnalysis, // Computed averageVideoQuality, averageAudioQuality, topMoments, momentCategoryCounts, fragmentCategories, qualityDistribution, contentStats, // Analysis options analysisOptions: context.analysisOptions, } }