From fdcec82274896ad22e428ebd1bbdffd50f807232 Mon Sep 17 00:00:00 2001 From: Accusys Date: Fri, 15 May 2026 10:07:35 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20file/identities=20=E2=80=94=20replace=20?= =?UTF-8?q?NULL=20first/last=5Fappearance=20with=20actual=20start=5Fframe/?= =?UTF-8?q?end=5Fframe=20+=20start=5Ftime/end=5Ftime=20+=20fps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/identity_api.rs | 15 +++++++++++---- src/core/db/postgres_db.rs | 34 ++++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/api/identity_api.rs b/src/api/identity_api.rs index 01e2fad..e653a22 100644 --- a/src/api/identity_api.rs +++ b/src/api/identity_api.rs @@ -170,6 +170,7 @@ async fn get_file_detail( pub struct FileIdentitiesResponse { pub success: bool, pub file_uuid: String, + pub fps: f64, pub total: i64, pub page: usize, pub page_size: usize, @@ -183,8 +184,10 @@ pub struct FileIdentityItem { pub metadata: serde_json::Value, pub face_count: Option, pub speaker_count: Option, - pub first_appearance: Option, - pub last_appearance: Option, + pub start_frame: Option, + pub end_frame: Option, + pub start_time: Option, + pub end_time: Option, pub confidence: Option, } @@ -203,6 +206,7 @@ async fn get_file_identities( .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; + let fps = records.first().map(|r| r.fps).unwrap_or(25.0); let data: Vec = records .into_iter() .map(|r| FileIdentityItem { @@ -211,8 +215,10 @@ async fn get_file_identities( metadata: r.metadata, face_count: r.face_count, speaker_count: r.speaker_count, - first_appearance: r.first_appearance, - last_appearance: r.last_appearance, + start_frame: r.start_frame, + end_frame: r.end_frame, + start_time: r.start_frame.map(|sf| sf as f64 / r.fps), + end_time: r.end_frame.map(|ef| ef as f64 / r.fps), confidence: r.confidence, }) .collect(); @@ -220,6 +226,7 @@ async fn get_file_identities( Ok(Json(FileIdentitiesResponse { success: true, file_uuid: file_uuid, + fps, total: data.len() as i64, page, page_size, diff --git a/src/core/db/postgres_db.rs b/src/core/db/postgres_db.rs index 7d038b9..f9a875f 100644 --- a/src/core/db/postgres_db.rs +++ b/src/core/db/postgres_db.rs @@ -57,16 +57,15 @@ pub struct CandidateRecord { #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct FileIdentityRecord { - pub id: i32, - pub file_uuid: String, pub identity_id: i32, pub name: String, pub metadata: serde_json::Value, pub face_count: Option, pub speaker_count: Option, - pub first_appearance: Option, - pub last_appearance: Option, + pub start_frame: Option, + pub end_frame: Option, pub confidence: Option, + pub fps: f64, } #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] @@ -2402,22 +2401,29 @@ impl PostgresDb { limit: i32, offset: i64, ) -> Result> { - let query = r#" - SELECT 0 as id, fd.file_uuid, fd.identity_id::int4, i.name, i.metadata, + let table = schema::table_name("face_detections"); + let ident_table = schema::table_name("identities"); + let videos_table = schema::table_name("videos"); + let query = format!( + r#" + SELECT fd.identity_id::int4, i.name, i.metadata, COUNT(*)::int4 as face_count, 0::int4 as speaker_count, - NULL::float8 as first_appearance, - NULL::float8 as last_appearance, - AVG(fd.confidence)::float8 as confidence - FROM face_detections fd - JOIN identities i ON fd.identity_id = i.id + MIN(fd.frame_number) as start_frame, + MAX(fd.frame_number) as end_frame, + AVG(fd.confidence)::float8 as confidence, + (SELECT COALESCE(fps, 24.0) FROM {} WHERE file_uuid = $1)::float8 as fps + FROM {} fd + JOIN {} i ON fd.identity_id = i.id WHERE fd.file_uuid = $1 AND fd.identity_id IS NOT NULL - GROUP BY fd.file_uuid, fd.identity_id, i.name, i.metadata + GROUP BY fd.identity_id, i.name, i.metadata ORDER BY confidence DESC LIMIT $2 OFFSET $3 - "#; + "#, + videos_table, table, ident_table + ); - let rows = sqlx::query_as(query) + let rows = sqlx::query_as(&query) .bind(file_uuid) .bind(limit) .bind(offset)