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:
Warren
2026-04-30 15:07:02 +08:00
parent 8f2208dd63
commit 2b23d1cfbd
148 changed files with 8553 additions and 48637 deletions

296
src/processing/decision.rs Normal file
View 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
View 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
View 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::*;

View 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(())
}

View 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(())
}

View 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(())
}

View 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(())
}

View 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(())
}

View 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::*;

View 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(())
}

View 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(())
}

View 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(())
}

View 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(())
}