Initial commit: Momentry Core v0.1
- Rust-based digital asset management system - Video analysis: ASR, OCR, YOLO, Face, Pose - RAG capabilities with Qdrant vector database - Multi-database support: PostgreSQL, Redis, MongoDB - Monitoring system with launchd plists - n8n workflow automation integration
This commit is contained in:
3
src/core/probe/mod.rs
Normal file
3
src/core/probe/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod probe;
|
||||
|
||||
pub use probe::{probe_video, ProbeResult};
|
||||
84
src/core/probe/probe.rs
Normal file
84
src/core/probe/probe.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use anyhow::{Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::process::Command;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ProbeResult {
|
||||
pub streams: Vec<StreamInfo>,
|
||||
pub format: FormatInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct StreamInfo {
|
||||
pub index: u32,
|
||||
pub codec_name: Option<String>,
|
||||
pub codec_type: Option<String>,
|
||||
pub width: Option<u32>,
|
||||
pub height: Option<u32>,
|
||||
pub r_frame_rate: Option<String>,
|
||||
pub duration: Option<String>,
|
||||
pub sample_rate: Option<String>,
|
||||
pub channels: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct FormatInfo {
|
||||
pub filename: Option<String>,
|
||||
pub format_name: Option<String>,
|
||||
pub duration: Option<String>,
|
||||
pub size: Option<String>,
|
||||
pub bit_rate: Option<String>,
|
||||
}
|
||||
|
||||
pub fn probe_video(video_path: &str) -> Result<ProbeResult> {
|
||||
let output = Command::new("ffprobe")
|
||||
.args([
|
||||
"-v",
|
||||
"quiet",
|
||||
"-print_format",
|
||||
"json",
|
||||
"-show_format",
|
||||
"-show_streams",
|
||||
video_path,
|
||||
])
|
||||
.output()
|
||||
.context("Failed to run ffprobe")?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
anyhow::bail!("ffprobe failed: {}", stderr);
|
||||
}
|
||||
|
||||
let json_str = String::from_utf8_lossy(&output.stdout);
|
||||
let json: serde_json::Value =
|
||||
serde_json::from_str(&json_str).context("Failed to parse ffprobe output")?;
|
||||
|
||||
let streams: Vec<StreamInfo> = json["streams"]
|
||||
.as_array()
|
||||
.map(|arr| {
|
||||
arr.iter()
|
||||
.map(|s| StreamInfo {
|
||||
index: s["index"].as_u64().unwrap_or(0) as u32,
|
||||
codec_name: s["codec_name"].as_str().map(String::from),
|
||||
codec_type: s["codec_type"].as_str().map(String::from),
|
||||
width: s["width"].as_u64().map(|v| v as u32),
|
||||
height: s["height"].as_u64().map(|v| v as u32),
|
||||
r_frame_rate: s["r_frame_rate"].as_str().map(String::from),
|
||||
duration: s["duration"].as_str().map(String::from),
|
||||
sample_rate: s["sample_rate"].as_str().map(String::from),
|
||||
channels: s["channels"].as_u64().map(|v| v as u32),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let format = FormatInfo {
|
||||
filename: json["format"]["filename"].as_str().map(String::from),
|
||||
format_name: json["format"]["format_name"].as_str().map(String::from),
|
||||
duration: json["format"]["duration"].as_str().map(String::from),
|
||||
size: json["format"]["size"].as_str().map(String::from),
|
||||
bit_rate: json["format"]["bit_rate"].as_str().map(String::from),
|
||||
};
|
||||
|
||||
Ok(ProbeResult { streams, format })
|
||||
}
|
||||
Reference in New Issue
Block a user