fix: file/identities — replace NULL first/last_appearance with actual start_frame/end_frame + start_time/end_time + fps

This commit is contained in:
Accusys
2026-05-15 10:07:35 +08:00
parent d7a133e1e4
commit fdcec82274
2 changed files with 31 additions and 18 deletions

View File

@@ -170,6 +170,7 @@ async fn get_file_detail(
pub struct FileIdentitiesResponse { pub struct FileIdentitiesResponse {
pub success: bool, pub success: bool,
pub file_uuid: String, pub file_uuid: String,
pub fps: f64,
pub total: i64, pub total: i64,
pub page: usize, pub page: usize,
pub page_size: usize, pub page_size: usize,
@@ -183,8 +184,10 @@ pub struct FileIdentityItem {
pub metadata: serde_json::Value, pub metadata: serde_json::Value,
pub face_count: Option<i32>, pub face_count: Option<i32>,
pub speaker_count: Option<i32>, pub speaker_count: Option<i32>,
pub first_appearance: Option<f64>, pub start_frame: Option<i32>,
pub last_appearance: Option<f64>, pub end_frame: Option<i32>,
pub start_time: Option<f64>,
pub end_time: Option<f64>,
pub confidence: Option<f64>, pub confidence: Option<f64>,
} }
@@ -203,6 +206,7 @@ async fn get_file_identities(
.await .await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; .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<FileIdentityItem> = records let data: Vec<FileIdentityItem> = records
.into_iter() .into_iter()
.map(|r| FileIdentityItem { .map(|r| FileIdentityItem {
@@ -211,8 +215,10 @@ async fn get_file_identities(
metadata: r.metadata, metadata: r.metadata,
face_count: r.face_count, face_count: r.face_count,
speaker_count: r.speaker_count, speaker_count: r.speaker_count,
first_appearance: r.first_appearance, start_frame: r.start_frame,
last_appearance: r.last_appearance, 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, confidence: r.confidence,
}) })
.collect(); .collect();
@@ -220,6 +226,7 @@ async fn get_file_identities(
Ok(Json(FileIdentitiesResponse { Ok(Json(FileIdentitiesResponse {
success: true, success: true,
file_uuid: file_uuid, file_uuid: file_uuid,
fps,
total: data.len() as i64, total: data.len() as i64,
page, page,
page_size, page_size,

View File

@@ -57,16 +57,15 @@ pub struct CandidateRecord {
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct FileIdentityRecord { pub struct FileIdentityRecord {
pub id: i32,
pub file_uuid: String,
pub identity_id: i32, pub identity_id: i32,
pub name: String, pub name: String,
pub metadata: serde_json::Value, pub metadata: serde_json::Value,
pub face_count: Option<i32>, pub face_count: Option<i32>,
pub speaker_count: Option<i32>, pub speaker_count: Option<i32>,
pub first_appearance: Option<f64>, pub start_frame: Option<i32>,
pub last_appearance: Option<f64>, pub end_frame: Option<i32>,
pub confidence: Option<f64>, pub confidence: Option<f64>,
pub fps: f64,
} }
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
@@ -2402,22 +2401,29 @@ impl PostgresDb {
limit: i32, limit: i32,
offset: i64, offset: i64,
) -> Result<Vec<FileIdentityRecord>> { ) -> Result<Vec<FileIdentityRecord>> {
let query = r#" let table = schema::table_name("face_detections");
SELECT 0 as id, fd.file_uuid, fd.identity_id::int4, i.name, i.metadata, 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, COUNT(*)::int4 as face_count,
0::int4 as speaker_count, 0::int4 as speaker_count,
NULL::float8 as first_appearance, MIN(fd.frame_number) as start_frame,
NULL::float8 as last_appearance, MAX(fd.frame_number) as end_frame,
AVG(fd.confidence)::float8 as confidence AVG(fd.confidence)::float8 as confidence,
FROM face_detections fd (SELECT COALESCE(fps, 24.0) FROM {} WHERE file_uuid = $1)::float8 as fps
JOIN identities i ON fd.identity_id = i.id FROM {} fd
JOIN {} i ON fd.identity_id = i.id
WHERE fd.file_uuid = $1 AND fd.identity_id IS NOT NULL 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 ORDER BY confidence DESC
LIMIT $2 OFFSET $3 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(file_uuid)
.bind(limit) .bind(limit)
.bind(offset) .bind(offset)