Files
momentry_core/src/core/thumbnail/mod.rs
accusys 75edf0aa71 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
2026-03-25 14:53:41 +08:00

109 lines
3.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::process::Command;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThumbnailResult {
pub uuid: String,
pub count: usize,
pub files: Vec<String>,
}
pub struct ThumbnailExtractor {
output_dir: PathBuf,
count: u32,
}
impl ThumbnailExtractor {
pub fn new(output_dir: PathBuf, count: u32) -> Self {
Self { output_dir, count }
}
pub fn extract(&self, video_path: &str, uuid: &str) -> Result<ThumbnailResult> {
let script_path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("scripts")
.join("thumbnail_extractor.py");
// 使用 venv 中的 Python確保版本正確且隔離依賴
let venv_python = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("venv")
.join("bin")
.join("python");
let output = Command::new(venv_python)
.arg(script_path)
.arg(video_path)
.arg(uuid)
.arg("-o")
.arg(&self.output_dir)
.arg("-c")
.arg(self.count.to_string())
.output()
.context("Failed to run thumbnail extractor")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("Thumbnail extraction failed: {}", stderr);
}
let json_str = String::from_utf8_lossy(&output.stdout);
let result: ThumbnailResult =
serde_json::from_str(&json_str).context("Failed to parse thumbnail result")?;
Ok(result)
}
pub fn get_or_create(&self, video_path: &str, uuid: &str) -> Result<ThumbnailResult> {
let thumb_dir = self.output_dir.join(uuid);
// Check if thumbnails already exist
if thumb_dir.exists() {
let files: Vec<String> = (0..self.count)
.map(|i| thumb_dir.join(format!("thumb_{:03}.jpg", i)))
.filter(|p| p.exists())
.map(|p| p.to_string_lossy().to_string())
.collect();
if files.len() as u32 == self.count {
return Ok(ThumbnailResult {
uuid: uuid.to_string(),
count: files.len(),
files,
});
}
}
// Extract new thumbnails
self.extract(video_path, uuid)
}
pub fn get_thumbnails(&self, uuid: &str) -> Option<Vec<String>> {
let thumb_dir = self.output_dir.join(uuid);
if !thumb_dir.exists() {
return None;
}
let files: Vec<String> = (0..10)
.map(|i| thumb_dir.join(format!("thumb_{:03}.jpg", i)))
.filter(|p| p.exists())
.map(|p| p.to_string_lossy().to_string())
.collect();
if files.is_empty() {
None
} else {
Some(files)
}
}
pub fn cleanup(&self, uuid: &str) -> Result<()> {
let thumb_dir = self.output_dir.join(uuid);
if thumb_dir.exists() {
std::fs::remove_dir_all(&thumb_dir).context("Failed to remove thumbnail directory")?;
}
Ok(())
}
}