fix: file/identities — replace NULL first/last_appearance with actual start_frame/end_frame + start_time/end_time + fps
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user