feat: Initial v0.9 release with API Key authentication

## v0.9.20260325_144654

### Features
- API Key Authentication System
- Job Worker System
- V2 Backup Versioning

### Bug Fixes
- get_processor_results_by_job column mapping

Co-authored-by: OpenCode
This commit is contained in:
accusys
2026-03-25 14:52:51 +08:00
parent 47e86b696f
commit 383201cacd
193 changed files with 40268 additions and 422 deletions

196
src/player/api_client.rs Normal file
View File

@@ -0,0 +1,196 @@
use anyhow::Result;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
const DEFAULT_API_URL: &str = "http://localhost:3002";
#[derive(Debug, Clone)]
pub struct ApiClient {
client: Client,
base_url: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct RegisterRequest {
pub path: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct RegisterResponse {
pub uuid: String,
pub video_id: i64,
pub job_id: i64,
pub file_name: String,
pub duration: f64,
pub width: u32,
pub height: u32,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct SearchRequest {
pub query: String,
pub limit: Option<usize>,
pub uuid: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct SearchResult {
pub uuid: String,
pub chunk_id: String,
pub chunk_type: String,
pub start_time: f64,
pub end_time: f64,
pub text: String,
pub score: f32,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct SearchResponse {
pub results: Vec<SearchResult>,
pub query: String,
}
#[derive(Debug, Deserialize, Serialize)]
#[allow(dead_code)]
pub struct LookupQuery {
pub path: Option<String>,
pub uuid: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct LookupResponse {
pub uuid: String,
pub file_path: Option<String>,
pub file_name: Option<String>,
pub duration: Option<f64>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct VideoInfo {
pub uuid: String,
pub file_path: String,
pub file_name: String,
pub duration: f64,
pub width: u32,
pub height: u32,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct VideosResponse {
pub videos: Vec<VideoInfo>,
}
impl ApiClient {
pub fn new() -> Self {
let url = std::env::var("MOMENTRY_API_URL").unwrap_or_else(|_| DEFAULT_API_URL.to_string());
Self {
client: Client::new(),
base_url: url,
}
}
#[allow(dead_code)]
pub fn with_url(url: &str) -> Self {
Self {
client: Client::new(),
base_url: url.to_string(),
}
}
pub async fn register_video(&self, path: &str) -> Result<RegisterResponse> {
let url = format!("{}/api/v1/register", self.base_url);
let request = RegisterRequest {
path: path.to_string(),
};
let response = self.client.post(&url).json(&request).send().await?;
let status = response.status();
let result = response.json::<RegisterResponse>().await?;
if !status.is_success() {
anyhow::bail!("API request failed with status: {}", status);
}
Ok(result)
}
pub async fn search_chunks(
&self,
query: &str,
uuid: Option<&str>,
limit: Option<usize>,
) -> Result<SearchResponse> {
let url = format!("{}/api/v1/search", self.base_url);
let request = SearchRequest {
query: query.to_string(),
limit,
uuid: uuid.map(|s| s.to_string()),
};
let response = self.client.post(&url).json(&request).send().await?;
let status = response.status();
let result = response.json::<SearchResponse>().await?;
if !status.is_success() {
anyhow::bail!("API request failed with status: {}", status);
}
Ok(result)
}
pub async fn lookup_video(&self, uuid: &str) -> Result<LookupResponse> {
let url = format!("{}/api/v1/lookup?uuid={}", self.base_url, uuid);
let response = self.client.get(&url).send().await?;
let status = response.status();
let result = response.json::<LookupResponse>().await?;
if !status.is_success() {
anyhow::bail!("API request failed with status: {}", status);
}
Ok(result)
}
pub async fn list_videos(&self) -> Result<Vec<VideoInfo>> {
let url = format!("{}/api/v1/videos", self.base_url);
let response = self.client.get(&url).send().await?;
let status = response.status();
let result = response.json::<VideosResponse>().await?;
if !status.is_success() {
anyhow::bail!("API request failed with status: {}", status);
}
Ok(result.videos)
}
pub fn base_url(&self) -> &str {
&self.base_url
}
}
impl Default for ApiClient {
fn default() -> Self {
Self::new()
}
}
pub fn find_video_path() -> Option<String> {
let test_dirs = vec![
PathBuf::from("/Users/accusys/Movies"),
PathBuf::from("/Users/accusys/Downloads"),
PathBuf::from("/Users/accusys/momentry_core_project/test_video"),
PathBuf::from("."),
];
for dir in test_dirs {
if dir.exists() {
if let Ok(entries) = std::fs::read_dir(&dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
let ext_str = ext.to_string_lossy().to_lowercase();
if matches!(
ext_str.as_str(),
"mp4" | "mov" | "m4v" | "avi" | "mkv" | "webm"
) {
return Some(path.to_string_lossy().to_string());
}
}
}
}
}
}
None
}