232 lines
6.4 KiB
Rust
232 lines
6.4 KiB
Rust
use anyhow::Result;
|
|
use reqwest::Client;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::path::PathBuf;
|
|
|
|
const DEFAULT_API_URL: &str = "http://localhost:3002";
|
|
|
|
const DEV_API_URL: &str = "http://localhost:3003";
|
|
|
|
fn get_api_url() -> String {
|
|
std::env::var("MOMENTRY_API_URL").unwrap_or_else(|_| {
|
|
std::env::var("MOMENTRY_SERVER_PORT")
|
|
.ok()
|
|
.map(|port| format!("http://localhost:{}", port))
|
|
.unwrap_or_else(|| DEFAULT_API_URL.to_string())
|
|
})
|
|
}
|
|
|
|
fn get_api_key() -> Option<String> {
|
|
std::env::var("MOMENTRY_API_KEY").ok()
|
|
}
|
|
|
|
#[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 = get_api_url();
|
|
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 mut request_builder = self.client.post(&url).json(&request);
|
|
if let Some(key) = get_api_key() {
|
|
request_builder = request_builder.header("X-API-Key", key);
|
|
}
|
|
let response = request_builder.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 mut request_builder = self.client.post(&url).json(&request);
|
|
if let Some(key) = get_api_key() {
|
|
request_builder = request_builder.header("X-API-Key", key);
|
|
}
|
|
let response = request_builder.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 mut request = self.client.get(&url);
|
|
if let Some(key) = get_api_key() {
|
|
request = request.header("X-API-Key", key);
|
|
}
|
|
let response = request.send().await?;
|
|
let status = response.status();
|
|
if status == 200 {
|
|
let result = response.json::<LookupResponse>().await?;
|
|
if result.uuid.is_empty() {
|
|
anyhow::bail!("影片不存在: {}", uuid);
|
|
}
|
|
Ok(result)
|
|
} else {
|
|
anyhow::bail!("API request failed with status: {}", status);
|
|
}
|
|
}
|
|
|
|
pub async fn list_videos(&self) -> Result<Vec<VideoInfo>> {
|
|
let url = format!("{}/api/v1/videos", self.base_url);
|
|
let mut request = self.client.get(&url);
|
|
if let Some(key) = get_api_key() {
|
|
request = request.header("X-API-Key", key);
|
|
}
|
|
let response = request.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
|
|
}
|