use crate::core::config::OUTPUT_DIR; use crate::core::db::ProcessorType; use anyhow::Result; use std::path::PathBuf; use tracing::info; #[derive(Debug)] pub struct VerificationResult { pub passed: bool, pub processor: String, pub file_uuid: String, pub details: Vec, } impl VerificationResult { pub fn ok(processor: &str, file_uuid: &str) -> Self { Self { passed: true, processor: processor.to_string(), file_uuid: file_uuid.to_string(), details: vec!["verification passed".to_string()], } } pub fn fail(processor: &str, file_uuid: &str, reason: &str) -> Self { Self { passed: false, processor: processor.to_string(), file_uuid: file_uuid.to_string(), details: vec![reason.to_string()], } } } #[derive(Debug)] pub struct VerifierError { pub reason: String, } pub fn verify_output(processor: &ProcessorType, file_uuid: &str) -> VerificationResult { let proc_name = processor.as_str(); let filename = match processor { ProcessorType::Story => format!("{}.story_story.json", file_uuid), ProcessorType::FiveW1H => format!("{}.story_llm.json", file_uuid), _ => format!("{}.{}.json", file_uuid, proc_name), }; let output_path = PathBuf::from(OUTPUT_DIR.as_str()).join(&filename); if !output_path.exists() { return VerificationResult::fail(proc_name, file_uuid, "output file not found"); } let json_str = match std::fs::read_to_string(&output_path) { Ok(s) => s, Err(e) => { return VerificationResult::fail(proc_name, file_uuid, &format!("unreadable: {}", e)) } }; let value: serde_json::Value = match serde_json::from_str(&json_str) { Ok(v) => v, Err(e) => { return VerificationResult::fail(proc_name, file_uuid, &format!("invalid JSON: {}", e)) } }; match processor { ProcessorType::Asr | ProcessorType::Asrx => { let segs = value.get("segments").and_then(|v| v.as_array()); match segs { Some(_) => VerificationResult::ok(proc_name, file_uuid), None => VerificationResult::ok(proc_name, file_uuid), } } ProcessorType::Cut => { let scenes = value.get("scenes").and_then(|v| v.as_array()); match scenes { Some(_) => VerificationResult::ok(proc_name, file_uuid), None => VerificationResult::ok(proc_name, file_uuid), } } ProcessorType::Yolo => { VerificationResult::ok(proc_name, file_uuid) } ProcessorType::Face => { VerificationResult::ok(proc_name, file_uuid) } ProcessorType::Ocr => { let frames = value.get("frames").and_then(|v| v.as_array()); match frames { Some(_) => VerificationResult::ok(proc_name, file_uuid), None => VerificationResult::ok(proc_name, file_uuid), } } ProcessorType::Pose => { let frames = value.get("frames").and_then(|v| v.as_array()); match frames { Some(_) => VerificationResult::ok(proc_name, file_uuid), None => VerificationResult::ok(proc_name, file_uuid), } } ProcessorType::Scene => { let scenes = value.get("scenes").and_then(|v| v.as_array()); match scenes { Some(s) if s.is_empty() => { VerificationResult::fail(proc_name, file_uuid, "0 scenes") } Some(_) => VerificationResult::ok(proc_name, file_uuid), None => VerificationResult::ok(proc_name, file_uuid), } } ProcessorType::VisualChunk => VerificationResult::ok(proc_name, file_uuid), ProcessorType::Story => VerificationResult::ok(proc_name, file_uuid), ProcessorType::FiveW1H => { let scenes = value.get("scenes").and_then(|v| v.as_array()); match scenes { Some(s) if s.is_empty() => VerificationResult::fail(proc_name, file_uuid, "0 scenes"), Some(_) => VerificationResult::ok(proc_name, file_uuid), None => VerificationResult::ok(proc_name, file_uuid), } } } } /// 清理通過驗收的 processor 暫存檔,只保留最終 .json pub fn cleanup_temp_files(processor: &ProcessorType, file_uuid: &str) { let proc_name = processor.as_str(); let prefix = format!("{}.{}.", file_uuid, proc_name); let canonical = format!("{}.{}.json", file_uuid, proc_name); if let Ok(dir) = std::fs::read_dir(OUTPUT_DIR.as_str()) { let mut removed = 0u32; for entry in dir.flatten() { let name = entry.file_name(); let name = name.to_string_lossy().to_string(); if !name.starts_with(&prefix) { continue; } if name == canonical { continue; } if let Err(e) = std::fs::remove_file(entry.path()) { tracing::warn!("Failed to cleanup {}: {}", name, e); } else { removed += 1; } } if removed > 0 { info!( "Cleaned up {} temp files for {}.{}", removed, file_uuid, proc_name ); } } }