This commit is contained in:
2026-06-11 13:51:33 +02:00
parent 0fdc79af23
commit c05087a6c7
9 changed files with 76 additions and 36 deletions

View File

@@ -158,16 +158,47 @@ impl MovieEnrichmentClient for TmdbEnrichmentClient {
vote_count: d.vote_count, vote_count: d.vote_count,
original_language: d.original_language, original_language: d.original_language,
collection_name: d.belongs_to_collection.map(|c| c.name), collection_name: d.belongs_to_collection.map(|c| c.name),
genres: d.genres.into_iter().map(|g| Genre { tmdb_id: g.id, name: g.name }).collect(), genres: d
keywords: d.keywords.keywords.into_iter().map(|k| Keyword { tmdb_id: k.id, name: k.name }).collect(), .genres
cast: d.credits.cast.into_iter().map(|c| CastMember { .into_iter()
tmdb_person_id: c.id, name: c.name, character: c.character, .map(|g| Genre {
billing_order: c.order, profile_path: c.profile_path, tmdb_id: g.id,
}).collect(), name: g.name,
crew: d.credits.crew.into_iter().map(|c| CrewMember { })
tmdb_person_id: c.id, name: c.name, job: c.job, .collect(),
department: c.department, profile_path: c.profile_path, keywords: d
}).collect(), .keywords
.keywords
.into_iter()
.map(|k| Keyword {
tmdb_id: k.id,
name: k.name,
})
.collect(),
cast: d
.credits
.cast
.into_iter()
.map(|c| CastMember {
tmdb_person_id: c.id,
name: c.name,
character: c.character,
billing_order: c.order,
profile_path: c.profile_path,
})
.collect(),
crew: d
.credits
.crew
.into_iter()
.map(|c| CrewMember {
tmdb_person_id: c.id,
name: c.name,
job: c.job,
department: c.department,
profile_path: c.profile_path,
})
.collect(),
enriched_at: Utc::now(), enriched_at: Utc::now(),
}) })
} }
@@ -201,8 +232,12 @@ impl PersonEnrichmentClient for TmdbEnrichmentClient {
Ok(PersonEnrichmentData { Ok(PersonEnrichmentData {
biography: d.biography.filter(|s| !s.is_empty()), biography: d.biography.filter(|s| !s.is_empty()),
birthday: d.birthday.and_then(|s| chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d").ok()), birthday: d
deathday: d.deathday.and_then(|s| chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d").ok()), .birthday
.and_then(|s| chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d").ok()),
deathday: d
.deathday
.and_then(|s| chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d").ok()),
place_of_birth: d.place_of_birth.filter(|s| !s.is_empty()), place_of_birth: d.place_of_birth.filter(|s| !s.is_empty()),
also_known_as: d.also_known_as.unwrap_or_default(), also_known_as: d.also_known_as.unwrap_or_default(),
homepage: d.homepage.filter(|s| !s.is_empty()), homepage: d.homepage.filter(|s| !s.is_empty()),

View File

@@ -9,5 +9,8 @@ pub async fn execute(
person_id: PersonId, person_id: PersonId,
data: PersonEnrichmentData, data: PersonEnrichmentData,
) -> Result<(), DomainError> { ) -> Result<(), DomainError> {
ctx.repos.person_command.update_enrichment(&person_id, &data).await ctx.repos
.person_command
.update_enrichment(&person_id, &data)
.await
} }

View File

@@ -11,12 +11,14 @@ const ENRICHMENT_TTL_DAYS: i64 = 90;
pub async fn execute(ctx: &AppContext, id: PersonId) -> Result<PersonCredits, DomainError> { pub async fn execute(ctx: &AppContext, id: PersonId) -> Result<PersonCredits, DomainError> {
let credits = ctx.repos.person_query.get_credits(&id).await?; let credits = ctx.repos.person_query.get_credits(&id).await?;
if should_enrich(&credits.person) { if should_enrich(&credits.person) {
let _ = ctx.services.event_publisher.publish( let _ = ctx
&DomainEvent::PersonEnrichmentRequested { .services
.event_publisher
.publish(&DomainEvent::PersonEnrichmentRequested {
person_id: id, person_id: id,
external_person_id: credits.person.external_id().value().to_string(), external_person_id: credits.person.external_id().value().to_string(),
}, })
).await; .await;
} }
Ok(credits) Ok(credits)
} }

View File

@@ -294,10 +294,7 @@ pub trait MovieEnrichmentClient: Send + Sync {
#[async_trait] #[async_trait]
pub trait PersonEnrichmentClient: Send + Sync { pub trait PersonEnrichmentClient: Send + Sync {
async fn fetch_details( async fn fetch_details(&self, external_id: &str) -> Result<PersonEnrichmentData, DomainError>;
&self,
external_id: &str,
) -> Result<PersonEnrichmentData, DomainError>;
} }
#[async_trait] #[async_trait]

View File

@@ -6,8 +6,8 @@ use crate::{
AnnotatedRow, DiaryEntry, DiaryFilter, EntityType, ExportFormat, ExternalPersonId, AnnotatedRow, DiaryEntry, DiaryFilter, EntityType, ExportFormat, ExternalPersonId,
FeedEntry, FieldMapping, FileFormat, ImportError, ImportProfile, ImportSession, FeedEntry, FieldMapping, FileFormat, ImportError, ImportProfile, ImportSession,
IndexableDocument, MovieProfile, MovieStats, ParsedFile, Person, PersonCredits, IndexableDocument, MovieProfile, MovieStats, ParsedFile, Person, PersonCredits,
PersonEnrichmentData, PersonId, PersonEnrichmentData, PersonId, ReviewHistory, SearchQuery, SearchResults, UserStats,
ReviewHistory, SearchQuery, SearchResults, UserStats, UserTrends, UserTrends,
collections::{PageParams, Paginated}, collections::{PageParams, Paginated},
}, },
ports::{ ports::{

View File

@@ -13,8 +13,7 @@ use std::collections::HashMap;
use crate::render::render_page; use crate::render::render_page;
use application::import::{ use application::import::{
apply_mapping as apply_import_mapping, apply_mapping as apply_import_mapping, apply_profile as apply_import_profile,
apply_profile as apply_import_profile,
commands::{ commands::{
ApplyImportMappingCommand, ApplyImportProfileCommand, CreateImportSessionCommand, ApplyImportMappingCommand, ApplyImportProfileCommand, CreateImportSessionCommand,
DeleteImportProfileCommand, ExecuteImportCommand, SaveImportProfileCommand, DeleteImportProfileCommand, ExecuteImportCommand, SaveImportProfileCommand,
@@ -911,17 +910,18 @@ pub async fn api_apply_profile(
} else { } else {
StatusCode::UNPROCESSABLE_ENTITY StatusCode::UNPROCESSABLE_ENTITY
}; };
return (status, axum::Json(serde_json::json!({"error": e.to_string()}))).into_response(); return (
status,
axum::Json(serde_json::json!({"error": e.to_string()})),
)
.into_response();
} }
let session = match state let session = match state
.app_ctx .app_ctx
.repos .repos
.import_session .import_session
.get( .get(&ImportSessionId::from_uuid(session_id), &user_id)
&ImportSessionId::from_uuid(session_id),
&user_id,
)
.await .await
{ {
Ok(Some(s)) => s, Ok(Some(s)) => s,

View File

@@ -114,7 +114,9 @@ pub async fn get_person_handler(
place_of_birth: person.place_of_birth().map(str::to_string), place_of_birth: person.place_of_birth().map(str::to_string),
also_known_as: person.also_known_as().to_vec(), also_known_as: person.also_known_as().to_vec(),
homepage: person.homepage().map(str::to_string), homepage: person.homepage().map(str::to_string),
imdb_url: person.imdb_id().map(|id| format!("https://www.imdb.com/name/{id}")), imdb_url: person
.imdb_id()
.map(|id| format!("https://www.imdb.com/name/{id}")),
enriched: person.enriched_at().is_some(), enriched: person.enriched_at().is_some(),
}) })
.into_response(), .into_response(),
@@ -150,7 +152,10 @@ pub async fn get_person_credits_handler(
place_of_birth: credits.person.place_of_birth().map(str::to_string), place_of_birth: credits.person.place_of_birth().map(str::to_string),
also_known_as: credits.person.also_known_as().to_vec(), also_known_as: credits.person.also_known_as().to_vec(),
homepage: credits.person.homepage().map(str::to_string), homepage: credits.person.homepage().map(str::to_string),
imdb_url: credits.person.imdb_id().map(|id| format!("https://www.imdb.com/name/{id}")), imdb_url: credits
.person
.imdb_id()
.map(|id| format!("https://www.imdb.com/name/{id}")),
enriched: credits.person.enriched_at().is_some(), enriched: credits.person.enriched_at().is_some(),
}, },
cast: credits cast: credits

View File

@@ -15,8 +15,7 @@ use domain::{
models::{ models::{
DiaryEntry, DiaryFilter, EntityType, FeedEntry, IndexableDocument, Movie, Person, DiaryEntry, DiaryFilter, EntityType, FeedEntry, IndexableDocument, Movie, Person,
PersonCredits, PersonEnrichmentData, PersonId, Review, ReviewHistory, SearchQuery, PersonCredits, PersonEnrichmentData, PersonId, Review, ReviewHistory, SearchQuery,
SearchResults, UserStats, SearchResults, UserStats, UserTrends,
UserTrends,
collections::{PageParams, Paginated}, collections::{PageParams, Paginated},
}, },
ports::{ ports::{

View File

@@ -15,8 +15,7 @@ use domain::{
events::DomainEvent, events::DomainEvent,
models::{ models::{
EntityType, ExternalPersonId, IndexableDocument, Movie, Person, PersonCredits, EntityType, ExternalPersonId, IndexableDocument, Movie, Person, PersonCredits,
PersonEnrichmentData, PersonId, PersonEnrichmentData, PersonId, SearchQuery, SearchResults, User,
SearchQuery, SearchResults, User,
}, },
ports::{ ports::{
AuthService, EventPublisher, GeneratedToken, MetadataClient, MetadataSearchCriteria, AuthService, EventPublisher, GeneratedToken, MetadataClient, MetadataSearchCriteria,