From 02cca7bedac85b01f021c2fcbd9c46d812bc862d Mon Sep 17 00:00:00 2001 From: Accusys Date: Mon, 18 May 2026 02:52:27 +0800 Subject: [PATCH] fix: search frames SQL alias bug, visual search serde default, identity JSON hyphen lookup --- src/api/identity_api.rs | 32 +++++++++++++++++++++++--------- src/api/universal_search.rs | 8 ++++++-- src/api/visual_chunk_search.rs | 2 ++ 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/api/identity_api.rs b/src/api/identity_api.rs index 1408dcb..2093632 100644 --- a/src/api/identity_api.rs +++ b/src/api/identity_api.rs @@ -475,6 +475,9 @@ pub struct IdentityChunkItem { pub file_uuid: String, pub chunk_id: String, pub chunk_type: String, + pub start_frame: i64, + pub end_frame: i64, + pub fps: f64, pub start_time: Option, pub end_time: Option, pub text_content: Option, @@ -504,6 +507,9 @@ async fn get_identity_chunks( file_uuid: r.file_uuid, chunk_id: r.chunk_id, chunk_type: r.chunk_type, + start_frame: r.start_frame, + end_frame: r.end_frame, + fps: r.fps, start_time: r.start_time, end_time: r.end_time, text_content: r.text_content, @@ -788,16 +794,24 @@ async fn get_profile_image( async fn get_identity_json( Path(identity_uuid): Path, ) -> Result<(StatusCode, [(String, String); 1], Vec), StatusCode> { - let path = crate::core::identity::storage::identity_file_path(&identity_uuid); - if !path.exists() { - return Err(StatusCode::NOT_FOUND); + let clean = identity_uuid.replace('-', ""); + let with_hyphens = if clean.len() == 32 { + format!("{}-{}-{}-{}-{}", &clean[0..8], &clean[8..12], &clean[12..16], &clean[16..20], &clean[20..32]) + } else { + identity_uuid.clone() + }; + for u in [&clean, &identity_uuid, &with_hyphens] { + let p = crate::core::identity::storage::identity_file_path(u); + if p.exists() { + let data = std::fs::read(&p).map_err(|_| StatusCode::NOT_FOUND)?; + return Ok(( + StatusCode::OK, + [("content-type".to_string(), "application/json".to_string())], + data, + )); + } } - let data = std::fs::read(&path).map_err(|_| StatusCode::NOT_FOUND)?; - Ok(( - StatusCode::OK, - [("content-type".to_string(), "application/json".to_string())], - data, - )) + Err(StatusCode::NOT_FOUND) } // ── Experiment: Identity Text Search ────────────────────────── diff --git a/src/api/universal_search.rs b/src/api/universal_search.rs index 5cacf48..7869f08 100644 --- a/src/api/universal_search.rs +++ b/src/api/universal_search.rs @@ -65,6 +65,7 @@ pub enum SearchResult { // Primary: frame-accurate position start_frame: i64, end_frame: i64, + fps: f64, // Reference: time derived from frames (subject to FPS variation) start_time: f64, end_time: f64, @@ -340,7 +341,7 @@ async fn search_chunks( let chunk_table = schema::table_name("chunk"); let mut sql = format!( - "SELECT chunk_id, chunk_type, start_time, end_time, start_frame, end_frame, text_content, content FROM {} WHERE file_uuid = '{}'", + "SELECT chunk_id, chunk_type, start_time, end_time, (start_time * fps)::bigint as start_frame, (end_time * fps)::bigint as end_frame, fps, text_content, content FROM {} WHERE file_uuid = '{}'", chunk_table, uuid ); if let Some(tr) = &req.time_range { @@ -432,6 +433,7 @@ async fn search_chunks( f64, i64, i64, + f64, Option, Option, )> = sqlx::query_as(&sql).fetch_all(db.pool()).await?; @@ -446,6 +448,7 @@ async fn search_chunks( end_time, start_frame, end_frame, + fps, text_content, content, )| { @@ -476,6 +479,7 @@ async fn search_chunks( end_time, start_frame, end_frame, + fps, score, text, speaker_id, @@ -666,7 +670,7 @@ async fn search_frames_internal_v2( ); if let Some(uuid) = &req.file_uuid { - sql.push_str(&format!(" AND fd.file_uuid = '{}'", uuid)); + sql.push_str(&format!(" AND v.file_uuid = '{}'", uuid)); } if let Some(tr) = &req.time_range { sql.push_str(&format!( diff --git a/src/api/visual_chunk_search.rs b/src/api/visual_chunk_search.rs index 4e0ff7c..448a381 100644 --- a/src/api/visual_chunk_search.rs +++ b/src/api/visual_chunk_search.rs @@ -23,8 +23,10 @@ pub struct VisualChunkSearchCriteria { /// Minimum number of unique object classes pub min_unique_classes: Option, /// Specific object classes to include (empty means all) + #[serde(default)] pub required_classes: Vec, /// Object class counts to filter by + #[serde(default)] pub class_counts: HashMap, /// Time range (optional) pub time_range: Option<(f64, f64)>,