feat: Add thumbnail management for albums and people, implement face embedding functionality

This commit is contained in:
2025-11-15 22:50:53 +01:00
parent 98f56e4f1e
commit 0f3e098d6d
28 changed files with 560 additions and 26 deletions

View File

@@ -15,3 +15,10 @@ pub struct BoundingBox {
pub trait FaceDetector: Send + Sync {
async fn detect_faces(&self, image_bytes: &[u8]) -> CoreResult<Vec<BoundingBox>>;
}
#[async_trait]
pub trait FaceEmbedder: Send + Sync {
/// Generates a feature vector for a cropped face image.
/// The image bytes should be a pre-cropped face.
async fn generate_embedding(&self, image_bytes: &[u8]) -> CoreResult<Vec<f32>>;
}

View File

@@ -39,10 +39,20 @@ pub enum FaceDetectorRuntime {
RemoteNats { subject: String },
}
#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "lowercase")]
pub enum FaceEmbedderRuntime {
Tract,
Onnx,
RemoteNats { subject: String },
}
#[derive(Deserialize, Clone, Debug)]
pub struct AiConfig {
pub face_detector_runtime: FaceDetectorRuntime,
pub face_detector_model_path: Option<String>,
pub face_embedder_runtime: FaceEmbedderRuntime,
pub face_embedder_model_path: Option<String>,
}
#[derive(Deserialize, Clone, Debug)]

View File

@@ -194,3 +194,11 @@ pub struct PersonShare {
pub user_id: uuid::Uuid,
pub permission: PersonPermission,
}
#[derive(Debug, Clone)]
pub struct FaceEmbedding {
pub id: uuid::Uuid,
pub face_region_id: uuid::Uuid,
pub model_id: i16,
pub embedding: Vec<u8>,
}

View File

@@ -7,8 +7,8 @@ use crate::{
error::CoreResult,
models::Media,
repositories::{
AlbumRepository, FaceRegionRepository, MediaMetadataRepository, MediaRepository,
PersonRepository, TagRepository, UserRepository,
AlbumRepository, FaceEmbeddingRepository, FaceRegionRepository, MediaMetadataRepository,
MediaRepository, PersonRepository, TagRepository, UserRepository,
},
};
@@ -24,6 +24,7 @@ pub struct PluginContext {
pub tag_repo: Arc<dyn TagRepository>,
pub person_repo: Arc<dyn PersonRepository>,
pub face_region_repo: Arc<dyn FaceRegionRepository>,
pub face_embedding_repo: Arc<dyn FaceEmbeddingRepository>,
pub media_library_path: String,
pub config: Arc<AppConfig>,
}

View File

@@ -4,8 +4,8 @@ use uuid::Uuid;
use crate::{
error::CoreResult,
models::{
Album, AlbumPermission, FaceRegion, Media, MediaMetadata, Person, PersonPermission, Tag,
User,
Album, AlbumPermission, FaceEmbedding, FaceRegion, Media, MediaMetadata, Person,
PersonPermission, Tag, User,
},
schema::{ListMediaOptions, MediaImportBundle},
};
@@ -43,6 +43,7 @@ pub trait AlbumRepository: Send + Sync {
async fn delete(&self, id: Uuid) -> CoreResult<()>;
async fn list_media_by_album_id(&self, album_id: Uuid) -> CoreResult<Vec<Media>>;
async fn is_media_in_public_album(&self, media_id: Uuid) -> CoreResult<bool>;
async fn set_thumbnail_media_id(&self, album_id: Uuid, media_id: Uuid) -> CoreResult<()>;
}
#[async_trait]
@@ -90,6 +91,7 @@ pub trait PersonRepository: Send + Sync {
async fn list_by_user(&self, user_id: Uuid) -> CoreResult<Vec<Person>>;
async fn update(&self, person: Person) -> CoreResult<()>;
async fn delete(&self, id: Uuid) -> CoreResult<()>;
async fn set_thumbnail_media_id(&self, person_id: Uuid, media_id: Uuid) -> CoreResult<()>;
}
#[async_trait]
@@ -129,3 +131,12 @@ pub trait PersonShareRepository: Send + Sync {
pub trait MediaImportRepository: Send + Sync {
async fn create_media_bundle(&self, bundle: MediaImportBundle) -> CoreResult<()>;
}
#[async_trait]
pub trait FaceEmbeddingRepository: Send + Sync {
async fn create(&self, embedding: &FaceEmbedding) -> CoreResult<()>;
async fn find_by_face_region_id(
&self,
face_region_id: Uuid,
) -> CoreResult<Option<FaceEmbedding>>;
}

View File

@@ -52,6 +52,12 @@ pub trait AlbumService: Send + Sync {
) -> CoreResult<Album>;
async fn delete_album(&self, album_id: Uuid, user_id: Uuid) -> CoreResult<()>;
async fn get_public_album_bundle(&self, album_id: Uuid) -> CoreResult<PublicAlbumBundle>;
async fn set_album_thumbnail(
&self,
album_id: Uuid,
media_id: Uuid,
user_id: Uuid,
) -> CoreResult<()>;
}
#[async_trait]
@@ -114,6 +120,13 @@ pub trait PersonService: Send + Sync {
source_person_id: Uuid,
user_id: Uuid,
) -> CoreResult<()>;
async fn set_person_thumbnail(
&self,
person_id: Uuid,
face_region_id: Uuid,
user_id: Uuid,
) -> CoreResult<()>;
}
#[async_trait]