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

View File

@@ -1,11 +1,7 @@
use anyhow::Result;
use std::path::Path;
use std::sync::Arc;
use tokio::time;
use tracing::{error, info, warn};
use crate::core::db::{Database, PostgresDb};
use crate::core::ingestion::IngestionService;
use tracing::{info, warn};
pub struct WatcherConfig {
pub directories: Vec<String>,
@@ -26,7 +22,7 @@ impl Default for WatcherConfig {
}
/// Starts the file watcher in the background.
/// Scans directories for video files and registers them if not already present.
/// Scans directories for video files (auto-registration disabled).
pub async fn run_watcher() -> Result<()> {
let config = WatcherConfig::default();
let dirs = config.directories.clone();
@@ -36,35 +32,26 @@ pub async fn run_watcher() -> Result<()> {
return Err(anyhow::anyhow!("No watch directories"));
}
info!("Initializing Database for Watcher...");
// Use Database::init() which handles config and pool creation
let db = PostgresDb::init().await?;
let service = Arc::new(IngestionService::new(db));
info!("Starting Video File Watcher (auto-registration disabled)...");
info!("Watch directories: {:?}", dirs);
info!("Starting Ingestion Poller for: {:?}", dirs);
// Spawn background task
// Spawn background task for monitoring only (no auto-registration)
tokio::spawn(async move {
let mut interval = time::interval(time::Duration::from_millis(config.poll_interval_ms));
// Run once immediately on startup to catch existing files
scan_and_ingest(&dirs, &service).await;
loop {
interval.tick().await;
scan_and_ingest(&dirs, &service).await;
scan_videos(&dirs).await;
}
});
Ok(())
}
async fn scan_and_ingest(directories: &[String], service: &Arc<IngestionService>) {
async fn scan_videos(directories: &[String]) {
// Allowed extensions list
let allowed_extensions = vec!["mp4", "mov", "mkv"];
info!("Scanning directories for new videos...");
for dir in directories {
let path = Path::new(dir);
if !path.exists() {
@@ -73,34 +60,33 @@ async fn scan_and_ingest(directories: &[String], service: &Arc<IngestionService>
}
if let Ok(entries) = std::fs::read_dir(path) {
for entry in entries.flatten() {
let file_path = entry.path();
if file_path.is_file() {
// Check extension
let is_video = if let Some(ext) = file_path.extension().and_then(|e| e.to_str())
{
allowed_extensions.contains(&ext.to_lowercase().as_str())
} else {
false
};
if is_video {
if let Some(p_str) = file_path.to_str() {
// Try to ingest. The service checks if it already exists.
match service.ingest(p_str).await {
Ok(Some(uuid)) => {
info!("Auto-registered: {} -> {}", file_path.display(), uuid);
}
Ok(None) => {
// Already registered
}
Err(e) => {
error!("Failed to ingest {}: {}", file_path.display(), e);
}
let video_count = entries
.flatten()
.filter(|entry| {
let file_path = entry.path();
if file_path.is_dir() {
if let Some(name) = file_path.file_name().and_then(|n| n.to_str()) {
if name.starts_with('.') {
return false;
}
}
}
}
if !file_path.is_file() {
return false;
}
if let Some(ext) = file_path.extension().and_then(|e| e.to_str()) {
allowed_extensions.contains(&ext.to_lowercase().as_str())
} else {
false
}
})
.count();
if video_count > 0 {
info!(
"Found {} video files in {} (use API to register)",
video_count, dir
);
}
}
}