diff --git a/crates/adapters/tmdb-enrichment/src/lib.rs b/crates/adapters/tmdb-enrichment/src/lib.rs index 4bc3caa..42195a1 100644 --- a/crates/adapters/tmdb-enrichment/src/lib.rs +++ b/crates/adapters/tmdb-enrichment/src/lib.rs @@ -8,8 +8,8 @@ use domain::{ events::DomainEvent, models::{CastMember, CrewMember, Genre, Keyword, MovieProfile}, ports::{ - EventHandler, MovieEnrichmentClient, MovieProfileRepository, MovieRepository, - PersonCommand, SearchCommand, + EventHandler, ImageStorage, MovieEnrichmentClient, MovieProfileRepository, + MovieRepository, PersonCommand, SearchCommand, }, value_objects::MovieId, }; @@ -229,6 +229,52 @@ pub struct EnrichmentHandler { pub profile_repo: Arc, pub person_command: Arc, pub search_command: Arc, + pub image_storage: Arc, + http: reqwest::Client, +} + +impl EnrichmentHandler { + pub fn new( + enrichment_client: Arc, + movie_repository: Arc, + profile_repo: Arc, + person_command: Arc, + search_command: Arc, + image_storage: Arc, + ) -> Self { + Self { + enrichment_client, + movie_repository, + profile_repo, + person_command, + search_command, + image_storage, + http: reqwest::Client::new(), + } + } + + async fn download_cast_photos(&self, profile: &MovieProfile) { + for member in profile.cast.iter().take(5) { + let Some(ref path) = member.profile_path else { + continue; + }; + let key = format!("cast{path}"); + if self.image_storage.get(&key).await.is_ok() { + continue; + } + let url = format!("https://image.tmdb.org/t/p/w185{path}"); + match self.http.get(&url).send().await { + Ok(resp) if resp.status().is_success() => { + if let Ok(bytes) = resp.bytes().await { + if let Err(e) = self.image_storage.store(&key, &bytes).await { + tracing::debug!("cast photo store failed for {path}: {e}"); + } + } + } + _ => tracing::debug!("cast photo download failed for {path}"), + } + } + } } #[async_trait] @@ -263,6 +309,7 @@ impl EventHandler for EnrichmentHandler { .await { Ok(profile) => { + self.download_cast_photos(&profile).await; enrich_movie::execute( &self.movie_repository, &self.profile_repo, diff --git a/crates/application/src/wrapup/handle_requested.rs b/crates/application/src/wrapup/handle_requested.rs index 5adf7dc..4d026f3 100644 --- a/crates/application/src/wrapup/handle_requested.rs +++ b/crates/application/src/wrapup/handle_requested.rs @@ -2,7 +2,7 @@ use crate::context::AppContext; use crate::wrapup::{compute, queries::ComputeWrapUpQuery}; use domain::errors::DomainError; use domain::events::DomainEvent; -use domain::models::wrapup::{DateRange, WrapUpReport, WrapUpScope, WrapUpStatus}; +use domain::models::wrapup::{DateRange, WrapUpScope, WrapUpStatus}; use domain::ports::{VideoRenderAssets, VideoRenderConfig}; use domain::value_objects::WrapUpId; @@ -41,8 +41,12 @@ pub async fn execute( if let Some(ref renderer) = ctx.services.video_renderer { let poster_images = resolve_images(ctx, &report.poster_paths, "poster").await; - let cast_images = - resolve_images(ctx, &report.top_cast_profile_paths, "cast").await; + let cast_keys: Vec = report + .top_cast_profile_paths + .iter() + .map(|p| format!("cast{p}")) + .collect(); + let cast_images = resolve_images(ctx, &cast_keys, "cast").await; let wc = &ctx.config.wrapup; let config = VideoRenderConfig { slide_duration_secs: 4, diff --git a/crates/worker/src/main.rs b/crates/worker/src/main.rs index 0f707f1..93bf05c 100644 --- a/crates/worker/src/main.rs +++ b/crates/worker/src/main.rs @@ -127,13 +127,14 @@ async fn main() -> anyhow::Result<()> { match tmdb_enrichment::TmdbEnrichmentClient::from_env() { Ok(client) => { tracing::info!("TMDb enrichment enabled"); - let handler = Arc::new(tmdb_enrichment::EnrichmentHandler { - enrichment_client: Arc::new(client), - movie_repository: Arc::clone(&ctx.repos.movie), - profile_repo: Arc::clone(&ctx.repos.movie_profile), - person_command: Arc::clone(&ctx.repos.person_command), - search_command: Arc::clone(&ctx.repos.search_command), - }) as Arc; + let handler = Arc::new(tmdb_enrichment::EnrichmentHandler::new( + Arc::new(client), + Arc::clone(&ctx.repos.movie), + Arc::clone(&ctx.repos.movie_profile), + Arc::clone(&ctx.repos.person_command), + Arc::clone(&ctx.repos.search_command), + Arc::clone(&ctx.services.image_storage), + )) as Arc; let job = Arc::new(application::jobs::EnrichmentStalenessJob::new(ctx.clone())) as Arc; (Some(handler), Some(job))