feat: wire person enrichment handler, update API DTOs

This commit is contained in:
2026-06-11 13:40:16 +02:00
parent a68e19aad7
commit 9b932cde8e
3 changed files with 59 additions and 7 deletions

View File

@@ -70,6 +70,21 @@ pub struct PersonDto {
pub name: String, pub name: String,
pub known_for_department: Option<String>, pub known_for_department: Option<String>,
pub profile_path: Option<String>, pub profile_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub biography: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub birthday: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deathday: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub place_of_birth: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub also_known_as: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub homepage: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub imdb_url: Option<String>,
pub enriched: bool,
} }
#[derive(Debug, Serialize, ToSchema)] #[derive(Debug, Serialize, ToSchema)]

View File

@@ -108,6 +108,14 @@ pub async fn get_person_handler(
name: person.name().to_string(), name: person.name().to_string(),
known_for_department: person.known_for_department().map(str::to_string), known_for_department: person.known_for_department().map(str::to_string),
profile_path: person.profile_path().map(str::to_string), profile_path: person.profile_path().map(str::to_string),
biography: person.biography().map(str::to_string),
birthday: person.birthday().map(|d| d.to_string()),
deathday: person.deathday().map(|d| d.to_string()),
place_of_birth: person.place_of_birth().map(str::to_string),
also_known_as: person.also_known_as().to_vec(),
homepage: person.homepage().map(str::to_string),
imdb_url: person.imdb_id().map(|id| format!("https://www.imdb.com/name/{id}")),
enriched: person.enriched_at().is_some(),
}) })
.into_response(), .into_response(),
Ok(None) => StatusCode::NOT_FOUND.into_response(), Ok(None) => StatusCode::NOT_FOUND.into_response(),
@@ -136,6 +144,14 @@ pub async fn get_person_credits_handler(
name: credits.person.name().to_string(), name: credits.person.name().to_string(),
known_for_department: credits.person.known_for_department().map(str::to_string), known_for_department: credits.person.known_for_department().map(str::to_string),
profile_path: credits.person.profile_path().map(str::to_string), profile_path: credits.person.profile_path().map(str::to_string),
biography: credits.person.biography().map(str::to_string),
birthday: credits.person.birthday().map(|d| d.to_string()),
deathday: credits.person.deathday().map(|d| d.to_string()),
place_of_birth: credits.person.place_of_birth().map(str::to_string),
also_known_as: credits.person.also_known_as().to_vec(),
homepage: credits.person.homepage().map(str::to_string),
imdb_url: credits.person.imdb_id().map(|id| format!("https://www.imdb.com/name/{id}")),
enriched: credits.person.enriched_at().is_some(),
}, },
cast: credits cast: credits
.cast .cast

View File

@@ -15,7 +15,10 @@ use export::ExportAdapter;
use importer::ImporterDocumentParser; use importer::ImporterDocumentParser;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use domain::ports::{DiaryExporter, DocumentParser, EventHandler, PeriodicJob}; use domain::ports::{
DiaryExporter, DocumentParser, EventHandler, MovieEnrichmentClient, PeriodicJob,
PersonEnrichmentClient,
};
#[cfg(not(any(feature = "sqlite", feature = "postgres")))] #[cfg(not(any(feature = "sqlite", feature = "postgres")))]
compile_error!( compile_error!(
@@ -74,7 +77,7 @@ async fn main() -> anyhow::Result<()> {
Arc::clone(&event_publisher_arc), Arc::clone(&event_publisher_arc),
)); ));
let ctx = AppContext { let mut ctx = AppContext {
repos: Repositories { repos: Repositories {
movie: db.movie, movie: db.movie,
review: db.review, review: db.review,
@@ -125,26 +128,38 @@ async fn main() -> anyhow::Result<()> {
// Both the event handler and the staleness job are gated on TMDB_API_KEY. // Both the event handler and the staleness job are gated on TMDB_API_KEY.
// Without a key, no MovieEnrichmentRequested events are produced or handled. // Without a key, no MovieEnrichmentRequested events are produced or handled.
type OptionalPair = (Option<Arc<dyn EventHandler>>, Option<Arc<dyn PeriodicJob>>); type EnrichmentParts = (
let (enrichment_handler, enrichment_job): OptionalPair = Option<Arc<dyn EventHandler>>,
Option<Arc<dyn EventHandler>>,
Option<Arc<dyn PeriodicJob>>,
);
let (enrichment_handler, person_enrichment_handler, enrichment_job): EnrichmentParts =
match tmdb_enrichment::TmdbEnrichmentClient::from_env() { match tmdb_enrichment::TmdbEnrichmentClient::from_env() {
Ok(client) => { Ok(client) => {
tracing::info!("TMDb enrichment enabled"); tracing::info!("TMDb enrichment enabled");
let client = Arc::new(client);
let handler = Arc::new(tmdb_enrichment::EnrichmentHandler::new( let handler = Arc::new(tmdb_enrichment::EnrichmentHandler::new(
Arc::new(client), Arc::clone(&client) as Arc<dyn MovieEnrichmentClient>,
Arc::clone(&ctx.repos.movie), Arc::clone(&ctx.repos.movie),
Arc::clone(&ctx.repos.movie_profile), Arc::clone(&ctx.repos.movie_profile),
Arc::clone(&ctx.repos.person_command), Arc::clone(&ctx.repos.person_command),
Arc::clone(&ctx.repos.search_command), Arc::clone(&ctx.repos.search_command),
Arc::clone(&ctx.services.object_storage), Arc::clone(&ctx.services.object_storage),
)) as Arc<dyn EventHandler>; )) as Arc<dyn EventHandler>;
let person_handler = Arc::new(tmdb_enrichment::PersonEnrichmentHandler::new(
Arc::clone(&client) as Arc<dyn PersonEnrichmentClient>,
Arc::clone(&ctx.repos.person_query),
Arc::clone(&ctx.repos.person_command),
)) as Arc<dyn EventHandler>;
ctx.services.person_enrichment =
Some(Arc::clone(&client) as Arc<dyn PersonEnrichmentClient>);
let job = Arc::new(application::jobs::EnrichmentStalenessJob::new(ctx.clone())) let job = Arc::new(application::jobs::EnrichmentStalenessJob::new(ctx.clone()))
as Arc<dyn PeriodicJob>; as Arc<dyn PeriodicJob>;
(Some(handler), Some(job)) (Some(handler), Some(person_handler), Some(job))
} }
Err(e) => { Err(e) => {
tracing::warn!("TMDb enrichment disabled: {e}"); tracing::warn!("TMDb enrichment disabled: {e}");
(None, None) (None, None, None)
} }
}; };
@@ -226,6 +241,9 @@ async fn main() -> anyhow::Result<()> {
if let Some(e) = enrichment_handler { if let Some(e) = enrichment_handler {
h.push(e); h.push(e);
} }
if let Some(e) = person_enrichment_handler {
h.push(e);
}
if let Some((ref conv_handler, _)) = conversion { if let Some((ref conv_handler, _)) = conversion {
h.push(Arc::clone(conv_handler)); h.push(Arc::clone(conv_handler));
} }
@@ -282,6 +300,9 @@ async fn main() -> anyhow::Result<()> {
if let Some(e) = enrichment_handler { if let Some(e) = enrichment_handler {
h.push(e); h.push(e);
} }
if let Some(e) = person_enrichment_handler {
h.push(e);
}
if let Some((ref conv_handler, _)) = conversion { if let Some((ref conv_handler, _)) = conversion {
h.push(Arc::clone(conv_handler)); h.push(Arc::clone(conv_handler));
} }