feat: update core API, database layer, and worker modules
- Remove unused imports (n8n_search, universal_search, Client, Arc, etc.) - Update API endpoints for identity, face recognition, search - Fix postgres_db.rs search_videos parent_uuid column - Add snapshot API and identity agent API - Clean up backup files (.bak, .bak2)
This commit is contained in:
296
src/processing/decision.rs
Normal file
296
src/processing/decision.rs
Normal file
@@ -0,0 +1,296 @@
|
||||
//! Processing decision logic and system resource management
|
||||
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
/// Decision on how to process a video file
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ProcessingDecision {
|
||||
Process,
|
||||
SkipComplete,
|
||||
ResumePartial,
|
||||
ForceReprocess,
|
||||
}
|
||||
|
||||
impl ProcessingDecision {
|
||||
/// Check if processing should proceed
|
||||
pub fn should_process(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
ProcessingDecision::Process
|
||||
| ProcessingDecision::ResumePartial
|
||||
| ProcessingDecision::ForceReprocess
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if processing should resume from checkpoint
|
||||
pub fn should_resume(&self) -> bool {
|
||||
matches!(self, ProcessingDecision::ResumePartial)
|
||||
}
|
||||
}
|
||||
|
||||
/// System resource information
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SystemResources {
|
||||
pub cpu_idle_percent: f64,
|
||||
pub memory_available_mb: u64,
|
||||
pub memory_total_mb: u64,
|
||||
pub memory_used_percent: f64,
|
||||
pub gpu_available: bool,
|
||||
pub gpu_type: GpuType,
|
||||
pub gpu_utilization: Option<f64>,
|
||||
}
|
||||
|
||||
/// GPU type enumeration
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GpuType {
|
||||
Nvidia,
|
||||
AppleMps,
|
||||
}
|
||||
|
||||
impl SystemResources {
|
||||
/// Check current system resources
|
||||
pub fn check() -> Self {
|
||||
let cpu_idle = Self::get_cpu_idle();
|
||||
let (mem_available, mem_total) = Self::get_memory_info();
|
||||
let mem_used_pct = if mem_total > 0 && mem_available <= mem_total {
|
||||
((mem_total - mem_available) as f64 / mem_total as f64) * 100.0
|
||||
} else if mem_total > 0 {
|
||||
100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let (gpu_available, gpu_type, gpu_util) = Self::get_gpu_info();
|
||||
|
||||
Self {
|
||||
cpu_idle_percent: cpu_idle,
|
||||
memory_available_mb: mem_available,
|
||||
memory_total_mb: mem_total,
|
||||
memory_used_percent: mem_used_pct,
|
||||
gpu_available,
|
||||
gpu_type,
|
||||
gpu_utilization: gpu_util,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if parallel processing is possible
|
||||
pub fn can_parallel(&self, required_memory_mb: u64) -> bool {
|
||||
const MIN_CPU_IDLE: f64 = 30.0;
|
||||
const MIN_MEMORY_MB: u64 = 4096;
|
||||
|
||||
self.cpu_idle_percent >= MIN_CPU_IDLE
|
||||
&& self.memory_available_mb >= required_memory_mb
|
||||
&& self.memory_available_mb >= MIN_MEMORY_MB
|
||||
}
|
||||
|
||||
/// Recommend which modules can be processed in parallel
|
||||
pub fn recommend_parallel_modules(&self) -> Vec<&'static str> {
|
||||
let mut recommended = Vec::new();
|
||||
|
||||
if self.gpu_available {
|
||||
recommended.push("yolo");
|
||||
}
|
||||
|
||||
if self.memory_available_mb >= 8192 {
|
||||
recommended.push("ocr");
|
||||
recommended.push("face");
|
||||
recommended.push("pose");
|
||||
}
|
||||
|
||||
recommended
|
||||
}
|
||||
|
||||
/// Get CPU idle percentage
|
||||
fn get_cpu_idle() -> f64 {
|
||||
let output = Command::new("top").args(["-l", "1", "-n", "1"]).output();
|
||||
match output {
|
||||
Ok(o) => {
|
||||
let s = String::from_utf8_lossy(&o.stdout);
|
||||
if let Some(line) = s.lines().find(|l| l.contains("idle")) {
|
||||
if let Some(pct) = line
|
||||
.split_whitespace()
|
||||
.find_map(|s| s.strip_suffix("%idle"))
|
||||
{
|
||||
pct.trim().parse().ok().unwrap_or(50.0)
|
||||
} else {
|
||||
50.0
|
||||
}
|
||||
} else {
|
||||
50.0
|
||||
}
|
||||
}
|
||||
Err(_) => 50.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get memory information (available and total in MB)
|
||||
fn get_memory_info() -> (u64, u64) {
|
||||
let output = Command::new("sysctl").args(["hw.memsize"]).output();
|
||||
match output {
|
||||
Ok(o) => {
|
||||
let s = String::from_utf8_lossy(&o.stdout);
|
||||
let total = s
|
||||
.split_whitespace()
|
||||
.nth(1)
|
||||
.and_then(|v| v.parse::<u64>().ok())
|
||||
.unwrap_or(0)
|
||||
/ 1024
|
||||
/ 1024;
|
||||
|
||||
let vm_stat = Command::new("vm_stat").output();
|
||||
let available = match vm_stat {
|
||||
Ok(v) => {
|
||||
let vs = String::from_utf8_lossy(&v.stdout);
|
||||
let mut free_pages: u64 = 0;
|
||||
let mut inactive_pages: u64 = 0;
|
||||
|
||||
for line in vs.lines() {
|
||||
if line.contains("Pages free:") {
|
||||
free_pages = line
|
||||
.split_whitespace()
|
||||
.last()
|
||||
.and_then(|v| v.trim_end_matches('.').parse().ok())
|
||||
.unwrap_or(0);
|
||||
} else if line.contains("Pages inactive:") {
|
||||
inactive_pages = line
|
||||
.split_whitespace()
|
||||
.last()
|
||||
.and_then(|v| v.trim_end_matches('.').parse().ok())
|
||||
.unwrap_or(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Pages * 4096 bytes / 1024 / 1024 = MB
|
||||
(free_pages + inactive_pages) * 4096 / 1024 / 1024
|
||||
}
|
||||
Err(_) => total / 4,
|
||||
};
|
||||
|
||||
(available, total)
|
||||
}
|
||||
Err(_) => (0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get GPU information
|
||||
fn get_gpu_info() -> (bool, GpuType, Option<f64>) {
|
||||
// Check NVIDIA GPU
|
||||
let nvidia_output = Command::new("nvidia-smi")
|
||||
.args([
|
||||
"--query-gpu=utilization.gpu",
|
||||
"--format=csv,noheader,nounits",
|
||||
])
|
||||
.output();
|
||||
|
||||
if let Ok(o) = nvidia_output {
|
||||
if o.status.success() {
|
||||
let s = String::from_utf8_lossy(&o.stdout);
|
||||
let util = s.trim().parse::<f64>().ok();
|
||||
return (true, GpuType::Nvidia, util);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Apple MPS (Metal Performance Shaders)
|
||||
let mps_output = Command::new("system_profiler")
|
||||
.args(["SPDisplaysDataType", "-detailLevel", "mini"])
|
||||
.output();
|
||||
|
||||
if let Ok(o) = mps_output {
|
||||
let s = String::from_utf8_lossy(&o.stdout);
|
||||
if s.contains("Metal") || s.contains("Apple") {
|
||||
return (true, GpuType::AppleMps, Some(0.0));
|
||||
}
|
||||
}
|
||||
|
||||
(false, GpuType::Nvidia, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SystemResources {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"CPU: {:.1}% idle, Memory: {:.1}GB/{:.1}GB ({:.0}% used), GPU: {}",
|
||||
self.cpu_idle_percent,
|
||||
self.memory_available_mb as f64 / 1024.0,
|
||||
self.memory_total_mb as f64 / 1024.0,
|
||||
self.memory_used_percent,
|
||||
if self.gpu_available {
|
||||
format!("{:.0}% utilized", self.gpu_utilization.unwrap_or(0.0))
|
||||
} else {
|
||||
"N/A".to_string()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// JSON file completeness status
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum JsonCompleteness {
|
||||
Complete,
|
||||
Partial { current: u32, total: u32 },
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Decide processing strategy based on JSON file state
|
||||
pub fn decide_processing(json_path: &Path, force: bool, resume: bool) -> ProcessingDecision {
|
||||
if !json_path.exists() {
|
||||
return ProcessingDecision::Process;
|
||||
}
|
||||
|
||||
if force {
|
||||
return ProcessingDecision::ForceReprocess;
|
||||
}
|
||||
|
||||
if resume {
|
||||
return ProcessingDecision::ResumePartial;
|
||||
}
|
||||
|
||||
match check_json_completeness(json_path) {
|
||||
JsonCompleteness::Complete => ProcessingDecision::SkipComplete,
|
||||
JsonCompleteness::Partial { current, total } => {
|
||||
eprintln!("\n⚠️ Found incomplete JSON file: {}", json_path.display());
|
||||
eprintln!(
|
||||
" Progress: {}/{} ({:.1}%)",
|
||||
current,
|
||||
total,
|
||||
(current as f64 / total as f64) * 100.0
|
||||
);
|
||||
eprintln!(" Use --resume to continue from checkpoint");
|
||||
eprintln!(" Use --force to reprocess from scratch");
|
||||
ProcessingDecision::SkipComplete
|
||||
}
|
||||
JsonCompleteness::Empty => ProcessingDecision::Process,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check JSON file completeness
|
||||
pub fn check_json_completeness(json_path: &Path) -> JsonCompleteness {
|
||||
let content = match std::fs::read_to_string(json_path) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return JsonCompleteness::Empty,
|
||||
};
|
||||
|
||||
if content.trim().is_empty() {
|
||||
return JsonCompleteness::Empty;
|
||||
}
|
||||
|
||||
let json: serde_json::Value = match serde_json::from_str(&content) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return JsonCompleteness::Empty,
|
||||
};
|
||||
|
||||
match json.get("segments") {
|
||||
Some(serde_json::Value::Array(arr)) if !arr.is_empty() => JsonCompleteness::Complete,
|
||||
Some(serde_json::Value::Object(obj)) => {
|
||||
let current = obj.get("current").and_then(|v| v.as_u64()).unwrap_or(0) as u32;
|
||||
let total = obj.get("total").and_then(|v| v.as_u64()).unwrap_or(0) as u32;
|
||||
if total > 0 && current < total {
|
||||
JsonCompleteness::Partial { current, total }
|
||||
} else {
|
||||
JsonCompleteness::Complete
|
||||
}
|
||||
}
|
||||
_ => JsonCompleteness::Complete,
|
||||
}
|
||||
}
|
||||
421
src/processing/handlers.rs
Normal file
421
src/processing/handlers.rs
Normal file
@@ -0,0 +1,421 @@
|
||||
//! Command handlers for Momentry Core CLI
|
||||
//!
|
||||
//! This module contains the actual implementation of CLI command handlers.
|
||||
|
||||
use anyhow::Result;
|
||||
use std::path::Path;
|
||||
|
||||
/// Handle video registration command
|
||||
pub async fn handle_register(path: &str) -> Result<()> {
|
||||
println!("Registering video: {}", path);
|
||||
|
||||
// TODO: Implement proper video registration
|
||||
println!("(Video registration would happen here)");
|
||||
|
||||
// Simple UUID simulation
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
let mut hasher = DefaultHasher::new();
|
||||
path.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
let uuid = format!("{:016x}", hash);
|
||||
println!("UUID that would be generated: {}", uuid);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle video processing command
|
||||
pub async fn handle_process(
|
||||
target: &str,
|
||||
modules: Option<Vec<String>>,
|
||||
cloud: bool,
|
||||
force: bool,
|
||||
resume: bool,
|
||||
) -> Result<()> {
|
||||
println!("Processing target: {}", target);
|
||||
println!("Modules: {:?}", modules);
|
||||
println!("Cloud processing: {}", cloud);
|
||||
println!("Force reprocess: {}", force);
|
||||
println!("Resume: {}", resume);
|
||||
|
||||
// Determine if target is a UUID or a path
|
||||
let is_uuid = target.len() == 16 && target.chars().all(|c| c.is_ascii_hexdigit());
|
||||
|
||||
if is_uuid {
|
||||
println!("Target is a UUID, would process video: {}", target);
|
||||
// TODO: Implement UUID-based processing
|
||||
} else {
|
||||
let path = Path::new(target);
|
||||
if !path.exists() {
|
||||
anyhow::bail!("Path does not exist: {}", target);
|
||||
}
|
||||
|
||||
// For now, just show decision
|
||||
if force {
|
||||
println!("Force reprocessing: {}", target);
|
||||
} else if resume {
|
||||
println!("Resume processing: {}", target);
|
||||
} else {
|
||||
println!("Normal processing: {}", target);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle video chunking command
|
||||
pub async fn handle_chunk(uuid: &str) -> Result<()> {
|
||||
println!("Chunking video with UUID: {}", uuid);
|
||||
|
||||
// TODO: Connect to database and fetch video
|
||||
println!(
|
||||
"Would connect to database and fetch video with UUID: {}",
|
||||
uuid
|
||||
);
|
||||
|
||||
println!("(Would show video details)");
|
||||
|
||||
// TODO: Implement chunking logic
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle story generation command
|
||||
pub async fn handle_story(uuid: &str) -> Result<()> {
|
||||
println!("Generating story for video with UUID: {}", uuid);
|
||||
|
||||
// TODO: Connect to database and fetch video
|
||||
println!(
|
||||
"Would connect to database and fetch video with UUID: {}",
|
||||
uuid
|
||||
);
|
||||
|
||||
println!("(Would show video details)");
|
||||
println!("Generating story...");
|
||||
|
||||
// TODO: Implement story generation logic
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle vectorization command
|
||||
pub async fn handle_vectorize(uuid: &str) -> Result<()> {
|
||||
println!("Vectorizing video with UUID: {}", uuid);
|
||||
|
||||
// TODO: Connect to database and fetch video
|
||||
println!(
|
||||
"Would connect to database and fetch video with UUID: {}",
|
||||
uuid
|
||||
);
|
||||
|
||||
println!("(Would show video details)");
|
||||
println!("Starting vectorization...");
|
||||
|
||||
// TODO: Implement vectorization logic
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle video playback command
|
||||
pub async fn handle_play(target: &str) -> Result<()> {
|
||||
println!("Playing target: {}", target);
|
||||
|
||||
// TODO: Implement video playback logic
|
||||
|
||||
println!("(Video playback would start here)");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle file system watching command
|
||||
pub async fn handle_watch(directories: Vec<String>) -> Result<()> {
|
||||
println!("Watching directories for new videos:");
|
||||
for dir in &directories {
|
||||
println!(" - {}", dir);
|
||||
}
|
||||
|
||||
// TODO: Implement directory watching logic
|
||||
|
||||
println!("(Directory watcher would start here)");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle system information command
|
||||
pub async fn handle_system(gpu: bool) -> Result<()> {
|
||||
println!("System Information:");
|
||||
|
||||
// TODO: Implement system information gathering
|
||||
|
||||
if gpu {
|
||||
println!("GPU information requested");
|
||||
// TODO: Add GPU-specific info
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle server startup command
|
||||
pub async fn handle_server(host: &str, port: u16) -> Result<()> {
|
||||
println!("Starting API server on {}:{}", host, port);
|
||||
|
||||
// This is handled by the server binary directly
|
||||
println!("(Server is started through binary entry point)");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle worker startup command
|
||||
pub async fn handle_worker(
|
||||
max_concurrent: Option<u32>,
|
||||
poll_interval: Option<u64>,
|
||||
batch_size: Option<u32>,
|
||||
) -> Result<()> {
|
||||
println!("Starting worker with configuration:");
|
||||
println!(" Max concurrent jobs: {}", max_concurrent.unwrap_or(2));
|
||||
println!(" Poll interval: {} seconds", poll_interval.unwrap_or(5));
|
||||
println!(" Batch size: {}", batch_size.unwrap_or(10));
|
||||
|
||||
// TODO: Implement worker startup logic
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle search query command
|
||||
pub async fn handle_query(query: &str) -> Result<()> {
|
||||
println!("Searching for: {}", query);
|
||||
|
||||
// TODO: Connect to database and search
|
||||
println!("Would search for: {}", query);
|
||||
let results: Vec<String> = vec![]; // Placeholder
|
||||
|
||||
println!("(Search would return results here)");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle file lookup command
|
||||
pub async fn handle_lookup(path: &str) -> Result<()> {
|
||||
println!("Looking up file: {}", path);
|
||||
|
||||
// TODO: Connect to database and lookup file
|
||||
println!("Looking up file in database: {}", path);
|
||||
let video: Option<String> = None; // Placeholder
|
||||
|
||||
if let Some(_) = video {
|
||||
println!("File found in database: {}", path);
|
||||
// In real implementation, show video details
|
||||
} else {
|
||||
println!("File not found in database: {}", path);
|
||||
println!("(UUID would be generated for new files)");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle UUID resolution command
|
||||
pub async fn handle_resolve(uuid: &str) -> Result<()> {
|
||||
println!("Resolving UUID: {}", uuid);
|
||||
|
||||
// TODO: Connect to database and fetch video
|
||||
println!(
|
||||
"Would connect to database and fetch video with UUID: {}",
|
||||
uuid
|
||||
);
|
||||
|
||||
println!("Resolved to:");
|
||||
println!(" File name: (would show from database)");
|
||||
println!(" File path: (would show from database)");
|
||||
println!(" Duration: (would show from database)");
|
||||
println!(" Resolution: (would show from database)");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle thumbnail extraction command
|
||||
pub async fn handle_thumbnails(uuid: Option<String>, count: Option<u32>) -> Result<()> {
|
||||
let count = count.unwrap_or(5);
|
||||
|
||||
if let Some(uuid) = uuid {
|
||||
println!("Extracting {} thumbnails for video UUID: {}", count, uuid);
|
||||
|
||||
// TODO: Connect to database and fetch video
|
||||
println!("Would fetch video with UUID: {}", uuid);
|
||||
|
||||
println!("(Would show video details)");
|
||||
|
||||
// TODO: Implement thumbnail extraction
|
||||
} else {
|
||||
println!("Extracting {} thumbnails from all pending videos", count);
|
||||
|
||||
// TODO: Implement bulk thumbnail extraction
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle video status check command
|
||||
pub async fn handle_status(uuid: Option<String>) -> Result<()> {
|
||||
// TODO: Connect to database
|
||||
println!("Would connect to database to check status");
|
||||
|
||||
if let Some(uuid) = uuid {
|
||||
println!("Checking status for video UUID: {}", uuid);
|
||||
|
||||
// Would fetch video from database
|
||||
println!("Would fetch video with UUID: {}", uuid);
|
||||
|
||||
println!("Video Status:");
|
||||
println!(" UUID: {}", uuid);
|
||||
println!(" File name: (would show from database)");
|
||||
println!(" Status: (would show from database)");
|
||||
println!(" Processing status: (would show from database)");
|
||||
println!(" Created at: (would show from database)");
|
||||
println!(" Registration time: (would show from database)");
|
||||
|
||||
// TODO: Add more detailed processing status
|
||||
} else {
|
||||
println!("Checking status of all videos");
|
||||
|
||||
println!("(Would list videos from database)");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle backup operations command
|
||||
pub async fn handle_backup(action: &str, days: Option<u32>) -> Result<()> {
|
||||
match action {
|
||||
"create" => {
|
||||
println!("Creating backup...");
|
||||
// TODO: Implement backup creation
|
||||
}
|
||||
"list" => {
|
||||
println!("Listing backups...");
|
||||
// TODO: Implement backup listing
|
||||
}
|
||||
"restore" => {
|
||||
println!("Restoring from backup...");
|
||||
// TODO: Implement backup restoration
|
||||
}
|
||||
"clean" | "cleanup" => {
|
||||
let days = days.unwrap_or(30);
|
||||
println!("Cleaning backups older than {} days...", days);
|
||||
// TODO: Implement backup cleanup
|
||||
}
|
||||
_ => {
|
||||
println!("Unknown backup action: {}", action);
|
||||
println!("Available actions: create, list, restore, clean(up)");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle API key management command
|
||||
pub async fn handle_api_key(
|
||||
action: &crate::cli::args::ApiKeyAction,
|
||||
name: Option<String>,
|
||||
key_type: Option<String>,
|
||||
ttl: Option<u64>,
|
||||
key: Option<String>,
|
||||
key_id: Option<String>,
|
||||
) -> Result<()> {
|
||||
println!("API Key Management:");
|
||||
println!(" Action: {:?}", action);
|
||||
|
||||
// TODO: Implement proper API key management
|
||||
match action {
|
||||
crate::cli::args::ApiKeyAction::Create => {
|
||||
let name = name.unwrap_or_else(|| "default".to_string());
|
||||
println!("Would create API key with name: {}", name);
|
||||
println!("(API key creation would be implemented here)");
|
||||
}
|
||||
crate::cli::args::ApiKeyAction::List => {
|
||||
println!("Would list API keys");
|
||||
println!("(API key listing would be implemented here)");
|
||||
}
|
||||
crate::cli::args::ApiKeyAction::Validate => {
|
||||
let key = key.expect("API key required for validation");
|
||||
println!("Would validate API key: {}...", &key[..8]);
|
||||
println!("(API key validation would be implemented here)");
|
||||
}
|
||||
crate::cli::args::ApiKeyAction::Revoke => {
|
||||
let key_id = key_id.expect("Key ID required for revocation");
|
||||
println!("Would revoke API key with ID: {}", key_id);
|
||||
println!("(API key revocation would be implemented here)");
|
||||
}
|
||||
crate::cli::args::ApiKeyAction::Rotate => {
|
||||
let key_id = key_id.expect("Key ID required for rotation");
|
||||
println!("Would rotate API key with ID: {}", key_id);
|
||||
println!("(API key rotation would be implemented here)");
|
||||
}
|
||||
crate::cli::args::ApiKeyAction::Stats => {
|
||||
println!("Would show API key statistics");
|
||||
println!("(API key statistics would be implemented here)");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle Gitea integration command
|
||||
pub async fn handle_gitea(
|
||||
action: &crate::cli::args::GiteaAction,
|
||||
name: Option<String>,
|
||||
url: Option<String>,
|
||||
token: Option<String>,
|
||||
repo_id: Option<String>,
|
||||
) -> Result<()> {
|
||||
match action {
|
||||
crate::cli::args::GiteaAction::Create => {
|
||||
println!("Creating Gitea integration...");
|
||||
// TODO: Implement Gitea integration creation
|
||||
}
|
||||
crate::cli::args::GiteaAction::List => {
|
||||
println!("Listing Gitea integrations...");
|
||||
// TODO: Implement Gitea integration listing
|
||||
}
|
||||
crate::cli::args::GiteaAction::Delete => {
|
||||
let repo_id = repo_id.expect("Repository ID required for deletion");
|
||||
println!("Deleting Gitea integration: {}", repo_id);
|
||||
// TODO: Implement Gitea integration deletion
|
||||
}
|
||||
crate::cli::args::GiteaAction::Verify => {
|
||||
println!("Verifying Gitea integration...");
|
||||
// TODO: Implement Gitea integration verification
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle n8n workflow command
|
||||
pub async fn handle_n8n(
|
||||
action: &crate::cli::args::N8nAction,
|
||||
name: Option<String>,
|
||||
url: Option<String>,
|
||||
workflow_id: Option<String>,
|
||||
) -> Result<()> {
|
||||
match action {
|
||||
crate::cli::args::N8nAction::Create => {
|
||||
println!("Creating n8n workflow...");
|
||||
// TODO: Implement n8n workflow creation
|
||||
}
|
||||
crate::cli::args::N8nAction::List => {
|
||||
println!("Listing n8n workflows...");
|
||||
// TODO: Implement n8n workflow listing
|
||||
}
|
||||
crate::cli::args::N8nAction::Delete => {
|
||||
let workflow_id = workflow_id.expect("Workflow ID required for deletion");
|
||||
println!("Deleting n8n workflow: {}", workflow_id);
|
||||
// TODO: Implement n8n workflow deletion
|
||||
}
|
||||
crate::cli::args::N8nAction::Verify => {
|
||||
println!("Verifying n8n workflow...");
|
||||
// TODO: Implement n8n workflow verification
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
8
src/processing/mod.rs
Normal file
8
src/processing/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
//! Video processing modules and logic
|
||||
|
||||
pub mod decision;
|
||||
pub mod handlers;
|
||||
pub mod modules;
|
||||
|
||||
pub use decision::*;
|
||||
pub use handlers::*;
|
||||
49
src/processing/modules/asr.rs
Normal file
49
src/processing/modules/asr.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
//! ASR (Automatic Speech Recognition) processing module
|
||||
|
||||
use anyhow::Result;
|
||||
use momentry_core::ui::progress::{ProcessorType, ProgressState, ProgressUi};
|
||||
use momentry_core::OutputDir;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Process ASR module
|
||||
pub async fn process_asr_module(
|
||||
asr_path: &Path,
|
||||
video_path: &str,
|
||||
uuid: &str,
|
||||
progress_state: &Arc<Mutex<ProgressState>>,
|
||||
ui: &Arc<Mutex<Option<ProgressUi>>>,
|
||||
) -> Result<()> {
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state.get_processor(ProcessorType::Asr).start(1);
|
||||
}
|
||||
|
||||
let asr_result = momentry_core::core::processor::process_asr(
|
||||
video_path,
|
||||
asr_path.to_str().unwrap(),
|
||||
Some(uuid),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let asr_json = serde_json::to_string_pretty(&asr_result)?;
|
||||
std::fs::write(asr_path, &asr_json)?;
|
||||
|
||||
let output_dir = OutputDir::new();
|
||||
let _ = output_dir.backup_file(uuid, "asr.json");
|
||||
|
||||
println!(" ✓ ASR saved: {} segments", asr_result.segments.len());
|
||||
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state
|
||||
.get_processor(ProcessorType::Asr)
|
||||
.complete(&format!("{} segments", asr_result.segments.len()));
|
||||
}
|
||||
|
||||
if let Some(ref mut ui) = *ui.lock().unwrap() {
|
||||
let _ = ui.render();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
49
src/processing/modules/asrx.rs
Normal file
49
src/processing/modules/asrx.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
//! ASRX (Extended ASR) processing module
|
||||
|
||||
use anyhow::Result;
|
||||
use momentry_core::ui::progress::{ProcessorType, ProgressState, ProgressUi};
|
||||
use momentry_core::OutputDir;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Process ASRX module
|
||||
pub async fn process_asrx_module(
|
||||
asrx_path: &Path,
|
||||
video_path: &str,
|
||||
uuid: &str,
|
||||
progress_state: &Arc<Mutex<ProgressState>>,
|
||||
ui: &Arc<Mutex<Option<ProgressUi>>>,
|
||||
) -> Result<()> {
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state.get_processor(ProcessorType::Asrx).start(1);
|
||||
}
|
||||
|
||||
let asrx_result = momentry_core::core::processor::process_asrx(
|
||||
video_path,
|
||||
asrx_path.to_str().unwrap(),
|
||||
Some(uuid),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let asrx_json = serde_json::to_string_pretty(&asrx_result)?;
|
||||
std::fs::write(asrx_path, &asrx_json)?;
|
||||
|
||||
let output_dir = OutputDir::new();
|
||||
let _ = output_dir.backup_file(uuid, "asrx.json");
|
||||
|
||||
println!(" ✓ ASRX saved: {} segments", asrx_result.segments.len());
|
||||
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state
|
||||
.get_processor(ProcessorType::Asrx)
|
||||
.complete(&format!("{} segments", asrx_result.segments.len()));
|
||||
}
|
||||
|
||||
if let Some(ref mut ui) = *ui.lock().unwrap() {
|
||||
let _ = ui.render();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
49
src/processing/modules/caption.rs
Normal file
49
src/processing/modules/caption.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
//! Caption generation processing module
|
||||
|
||||
use anyhow::Result;
|
||||
use momentry_core::ui::progress::{ProcessorType, ProgressState, ProgressUi};
|
||||
use momentry_core::OutputDir;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Process Caption module
|
||||
pub async fn process_caption_module(
|
||||
caption_path: &Path,
|
||||
video_path: &str,
|
||||
uuid: &str,
|
||||
progress_state: &Arc<Mutex<ProgressState>>,
|
||||
ui: &Arc<Mutex<Option<ProgressUi>>>,
|
||||
) -> Result<()> {
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state.get_processor(ProcessorType::Caption).start(1);
|
||||
}
|
||||
|
||||
let caption_result = momentry_core::core::processor::process_caption(
|
||||
video_path,
|
||||
caption_path.to_str().unwrap(),
|
||||
Some(uuid),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let caption_json = serde_json::to_string_pretty(&caption_result)?;
|
||||
std::fs::write(caption_path, &caption_json)?;
|
||||
|
||||
let output_dir = OutputDir::new();
|
||||
let _ = output_dir.backup_file(uuid, "caption.json");
|
||||
|
||||
println!(" ✓ Caption saved: {} frames", caption_result.total_frames);
|
||||
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state
|
||||
.get_processor(ProcessorType::Caption)
|
||||
.complete(&format!("{} frames", caption_result.total_frames));
|
||||
}
|
||||
|
||||
if let Some(ref mut ui) = *ui.lock().unwrap() {
|
||||
let _ = ui.render();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
49
src/processing/modules/cut.rs
Normal file
49
src/processing/modules/cut.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
//! CUT (Scene Cut Detection) processing module
|
||||
|
||||
use anyhow::Result;
|
||||
use momentry_core::ui::progress::{ProcessorType, ProgressState, ProgressUi};
|
||||
use momentry_core::OutputDir;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Process CUT module
|
||||
pub async fn process_cut_module(
|
||||
cut_path: &Path,
|
||||
video_path: &str,
|
||||
uuid: &str,
|
||||
progress_state: &Arc<Mutex<ProgressState>>,
|
||||
ui: &Arc<Mutex<Option<ProgressUi>>>,
|
||||
) -> Result<()> {
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state.get_processor(ProcessorType::Cut).start(1);
|
||||
}
|
||||
|
||||
let cut_result = momentry_core::core::processor::process_cut(
|
||||
video_path,
|
||||
cut_path.to_str().unwrap(),
|
||||
Some(uuid),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let cut_json = serde_json::to_string_pretty(&cut_result)?;
|
||||
std::fs::write(cut_path, &cut_json)?;
|
||||
|
||||
let output_dir = OutputDir::new();
|
||||
let _ = output_dir.backup_file(uuid, "cut.json");
|
||||
|
||||
println!(" ✓ CUT saved: {} scenes", cut_result.scenes.len());
|
||||
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state
|
||||
.get_processor(ProcessorType::Cut)
|
||||
.complete(&format!("{} scenes", cut_result.scenes.len()));
|
||||
}
|
||||
|
||||
if let Some(ref mut ui) = *ui.lock().unwrap() {
|
||||
let _ = ui.render();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
49
src/processing/modules/face.rs
Normal file
49
src/processing/modules/face.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
//! Face detection and recognition processing module
|
||||
|
||||
use anyhow::Result;
|
||||
use momentry_core::ui::progress::{ProcessorType, ProgressState, ProgressUi};
|
||||
use momentry_core::OutputDir;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Process Face module
|
||||
pub async fn process_face_module(
|
||||
face_path: &Path,
|
||||
video_path: &str,
|
||||
uuid: &str,
|
||||
progress_state: &Arc<Mutex<ProgressState>>,
|
||||
ui: &Arc<Mutex<Option<ProgressUi>>>,
|
||||
) -> Result<()> {
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state.get_processor(ProcessorType::Face).start(1);
|
||||
}
|
||||
|
||||
let face_result = momentry_core::core::processor::process_face(
|
||||
video_path,
|
||||
face_path.to_str().unwrap(),
|
||||
Some(uuid),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let face_json = serde_json::to_string_pretty(&face_result)?;
|
||||
std::fs::write(face_path, &face_json)?;
|
||||
|
||||
let output_dir = OutputDir::new();
|
||||
let _ = output_dir.backup_file(uuid, "face.json");
|
||||
|
||||
println!(" ✓ Face saved: {} frames", face_result.frames.len());
|
||||
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state
|
||||
.get_processor(ProcessorType::Face)
|
||||
.complete(&format!("{} frames", face_result.frames.len()));
|
||||
}
|
||||
|
||||
if let Some(ref mut ui) = *ui.lock().unwrap() {
|
||||
let _ = ui.render();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
21
src/processing/modules/mod.rs
Normal file
21
src/processing/modules/mod.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
//! Video processing modules
|
||||
|
||||
pub mod asr;
|
||||
pub mod asrx;
|
||||
pub mod caption;
|
||||
pub mod cut;
|
||||
pub mod face;
|
||||
pub mod ocr;
|
||||
pub mod pose;
|
||||
pub mod story;
|
||||
pub mod yolo;
|
||||
|
||||
pub use asr::*;
|
||||
pub use asrx::*;
|
||||
pub use caption::*;
|
||||
pub use cut::*;
|
||||
pub use face::*;
|
||||
pub use ocr::*;
|
||||
pub use pose::*;
|
||||
pub use story::*;
|
||||
pub use yolo::*;
|
||||
52
src/processing/modules/ocr.rs
Normal file
52
src/processing/modules/ocr.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
//! OCR (Optical Character Recognition) processing module
|
||||
|
||||
use anyhow::Result;
|
||||
use momentry_core::ui::progress::{ProcessorType, ProgressState, ProgressUi};
|
||||
use momentry_core::OutputDir;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Process OCR module
|
||||
pub async fn process_ocr_module(
|
||||
ocr_path: &Path,
|
||||
video_path: &str,
|
||||
uuid: &str,
|
||||
progress_state: &Arc<Mutex<ProgressState>>,
|
||||
ui: &Arc<Mutex<Option<ProgressUi>>>,
|
||||
) -> Result<()> {
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state.get_processor(ProcessorType::Ocr).start(1);
|
||||
}
|
||||
|
||||
let ocr_result = momentry_core::core::processor::process_ocr(
|
||||
video_path,
|
||||
ocr_path.to_str().unwrap(),
|
||||
Some(uuid),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let ocr_json = serde_json::to_string_pretty(&ocr_result)?;
|
||||
std::fs::write(ocr_path, &ocr_json)?;
|
||||
|
||||
let output_dir = OutputDir::new();
|
||||
let _ = output_dir.backup_file(uuid, "ocr.json");
|
||||
|
||||
println!(
|
||||
" ✓ OCR saved: {} frames with text",
|
||||
ocr_result.frames.len()
|
||||
);
|
||||
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state
|
||||
.get_processor(ProcessorType::Ocr)
|
||||
.complete(&format!("{} frames", ocr_result.frames.len()));
|
||||
}
|
||||
|
||||
if let Some(ref mut ui) = *ui.lock().unwrap() {
|
||||
let _ = ui.render();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
50
src/processing/modules/pose.rs
Normal file
50
src/processing/modules/pose.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
//! Pose estimation processing module
|
||||
|
||||
use anyhow::Result;
|
||||
use momentry_core::ui::progress::{ProcessorType, ProgressState, ProgressUi};
|
||||
use momentry_core::OutputDir;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Process Pose module
|
||||
pub async fn process_pose_module(
|
||||
pose_path: &Path,
|
||||
video_path: &str,
|
||||
uuid: &str,
|
||||
progress_state: &Arc<Mutex<ProgressState>>,
|
||||
ui: &Arc<Mutex<Option<ProgressUi>>>,
|
||||
) -> Result<()> {
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state.get_processor(ProcessorType::Pose).start(1);
|
||||
}
|
||||
|
||||
let pose_result = momentry_core::core::processor::process_pose(
|
||||
video_path,
|
||||
pose_path.to_str().unwrap(),
|
||||
Some(uuid),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let pose_json = serde_json::to_string_pretty(&pose_result)?;
|
||||
std::fs::write(pose_path, &pose_json)?;
|
||||
|
||||
let output_dir = OutputDir::new();
|
||||
let _ = output_dir.backup_file(uuid, "pose.json");
|
||||
|
||||
println!(" ✓ Pose saved: {} frames", pose_result.frames.len());
|
||||
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state
|
||||
.get_processor(ProcessorType::Pose)
|
||||
.complete(&format!("{} frames", pose_result.frames.len()));
|
||||
state.stop();
|
||||
}
|
||||
|
||||
if let Some(ref mut ui) = *ui.lock().unwrap() {
|
||||
let _ = ui.render();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
53
src/processing/modules/story.rs
Normal file
53
src/processing/modules/story.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
//! Story generation processing module
|
||||
|
||||
use anyhow::Result;
|
||||
use momentry_core::ui::progress::{ProcessorType, ProgressState, ProgressUi};
|
||||
use momentry_core::OutputDir;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Process Story module
|
||||
pub async fn process_story_module(
|
||||
story_path: &Path,
|
||||
video_path: &str,
|
||||
uuid: &str,
|
||||
progress_state: &Arc<Mutex<ProgressState>>,
|
||||
ui: &Arc<Mutex<Option<ProgressUi>>>,
|
||||
) -> Result<()> {
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state.get_processor(ProcessorType::Story).start(1);
|
||||
}
|
||||
|
||||
let story_result = momentry_core::core::processor::process_story(
|
||||
video_path,
|
||||
story_path.to_str().unwrap(),
|
||||
Some(uuid),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let story_json = serde_json::to_string_pretty(&story_result)?;
|
||||
std::fs::write(story_path, &story_json)?;
|
||||
|
||||
let output_dir = OutputDir::new();
|
||||
let _ = output_dir.backup_file(uuid, "story.json");
|
||||
|
||||
println!(
|
||||
" ✓ Story saved: {} parent chunks, {} child chunks",
|
||||
story_result.stats.total_parent_chunks, story_result.stats.total_child_chunks
|
||||
);
|
||||
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state.get_processor(ProcessorType::Story).complete(&format!(
|
||||
"{} parents, {} children",
|
||||
story_result.stats.total_parent_chunks, story_result.stats.total_child_chunks
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(ref mut ui) = *ui.lock().unwrap() {
|
||||
let _ = ui.render();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
49
src/processing/modules/yolo.rs
Normal file
49
src/processing/modules/yolo.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
//! YOLO (Object Detection) processing module
|
||||
|
||||
use anyhow::Result;
|
||||
use momentry_core::ui::progress::{ProcessorType, ProgressState, ProgressUi};
|
||||
use momentry_core::OutputDir;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Process YOLO module
|
||||
pub async fn process_yolo_module(
|
||||
yolo_path: &Path,
|
||||
video_path: &str,
|
||||
uuid: &str,
|
||||
progress_state: &Arc<Mutex<ProgressState>>,
|
||||
ui: &Arc<Mutex<Option<ProgressUi>>>,
|
||||
) -> Result<()> {
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state.get_processor(ProcessorType::Yolo).start(1);
|
||||
}
|
||||
|
||||
let yolo_result = momentry_core::core::processor::process_yolo(
|
||||
video_path,
|
||||
yolo_path.to_str().unwrap(),
|
||||
Some(uuid),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let yolo_json = serde_json::to_string_pretty(&yolo_result)?;
|
||||
std::fs::write(yolo_path, &yolo_json)?;
|
||||
|
||||
let output_dir = OutputDir::new();
|
||||
let _ = output_dir.backup_file(uuid, "yolo.json");
|
||||
|
||||
println!(" ✓ YOLO saved: {} frames", yolo_result.frame_count);
|
||||
|
||||
{
|
||||
let mut state = progress_state.lock().unwrap();
|
||||
state
|
||||
.get_processor(ProcessorType::Yolo)
|
||||
.complete(&format!("{} frames", yolo_result.frame_count));
|
||||
}
|
||||
|
||||
if let Some(ref mut ui) = *ui.lock().unwrap() {
|
||||
let _ = ui.render();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user