refactor: extract storage key conventions into WrapUpStorage
Some checks failed
CI / Check / Test (push) Failing after 43s

This commit is contained in:
2026-06-03 01:20:51 +02:00
parent 3cec726e3d
commit e4b8ba550e
4 changed files with 73 additions and 33 deletions

View File

@@ -2,18 +2,17 @@ use domain::errors::DomainError;
use domain::value_objects::WrapUpId;
use crate::context::AppContext;
use crate::wrapup::storage::WrapUpStorage;
pub async fn execute(ctx: &AppContext, id: WrapUpId) -> Result<(), DomainError> {
let record = ctx
.repos
ctx.repos
.wrapup_repo
.get_by_id(&id)
.await?
.ok_or_else(|| DomainError::NotFound("wrap-up not found".into()))?;
let wrapup_key = format!("wrapups/{}", id.value());
let video_key = format!("{wrapup_key}/video.mp4");
let _ = ctx.services.image_storage.delete(&video_key).await;
let storage = WrapUpStorage::new(ctx.services.image_storage.clone());
let _ = storage.delete_video(&id).await;
ctx.repos.wrapup_repo.delete(&record.id).await
ctx.repos.wrapup_repo.delete(&id).await
}

View File

@@ -1,5 +1,5 @@
use crate::context::AppContext;
use crate::wrapup::{compute, queries::ComputeWrapUpQuery};
use crate::wrapup::{compute, queries::ComputeWrapUpQuery, storage::WrapUpStorage};
use domain::errors::DomainError;
use domain::events::DomainEvent;
use domain::models::wrapup::{DateRange, WrapUpScope, WrapUpStatus};
@@ -48,26 +48,18 @@ pub async fn execute(
.await?;
if let Some(ref renderer) = ctx.services.video_renderer {
let poster_images = resolve_images(ctx, &report.poster_paths, "poster").await;
let cast_keys: Vec<String> = report
.top_cast_profile_paths
.iter()
.map(|p| format!("cast{p}"))
.collect();
let cast_images = resolve_images(ctx, &cast_keys, "cast").await;
let asset_storage = WrapUpStorage::new(ctx.services.image_storage.clone());
let poster_images = asset_storage.resolve_poster_images(&report.poster_paths).await;
let cast_images = asset_storage
.resolve_cast_images(&report.top_cast_profile_paths)
.await;
let assets = VideoRenderAssets {
poster_images,
cast_images,
};
match renderer.render(&report, assets).await {
Ok(video_bytes) => {
let video_key = format!("wrapups/{}/video.mp4", wrapup_id.value());
if let Err(e) = ctx
.services
.image_storage
.store(&video_key, &video_bytes)
.await
{
if let Err(e) = asset_storage.store_video(&wrapup_id, &video_bytes).await {
tracing::warn!("failed to store wrapup video: {e}");
}
}
@@ -92,15 +84,3 @@ pub async fn execute(
}
}
}
async fn resolve_images(ctx: &AppContext, paths: &[String], label: &str) -> Vec<(String, Vec<u8>)> {
let mut images = Vec::new();
for path in paths.iter().take(20) {
match ctx.services.image_storage.get(path).await {
Ok(bytes) => images.push((path.clone(), bytes)),
Err(e) => tracing::debug!("{label} fetch skipped for {path}: {e}"),
}
}
tracing::info!("resolved {}/{} {label} images", images.len(), paths.len());
images
}

View File

@@ -7,3 +7,4 @@ pub mod get_wrapup;
pub mod handle_requested;
pub mod list_wrapups;
pub mod queries;
pub mod storage;

View File

@@ -0,0 +1,60 @@
use domain::errors::DomainError;
use domain::ports::ImageStorage;
use domain::value_objects::WrapUpId;
use std::sync::Arc;
pub struct WrapUpStorage {
inner: Arc<dyn ImageStorage>,
}
impl WrapUpStorage {
pub fn new(storage: Arc<dyn ImageStorage>) -> Self {
Self { inner: storage }
}
pub async fn store_video(&self, id: &WrapUpId, bytes: &[u8]) -> Result<(), DomainError> {
let key = format!("wrapups/{}/video.mp4", id.value());
self.inner.store(&key, bytes).await?;
Ok(())
}
pub async fn delete_video(&self, id: &WrapUpId) -> Result<(), DomainError> {
let key = format!("wrapups/{}/video.mp4", id.value());
self.inner.delete(&key).await
}
pub fn cast_image_key(profile_path: &str) -> String {
format!("cast{profile_path}")
}
pub async fn resolve_cast_images(
&self,
profile_paths: &[String],
) -> Vec<(String, Vec<u8>)> {
let mut images = Vec::new();
for path in profile_paths.iter().take(20) {
let key = Self::cast_image_key(path);
match self.inner.get(&key).await {
Ok(bytes) => images.push((key, bytes)),
Err(e) => tracing::debug!("cast fetch skipped for {key}: {e}"),
}
}
tracing::info!("resolved {}/{} cast images", images.len(), profile_paths.len());
images
}
pub async fn resolve_poster_images(
&self,
paths: &[String],
) -> Vec<(String, Vec<u8>)> {
let mut images = Vec::new();
for path in paths.iter().take(20) {
match self.inner.get(path).await {
Ok(bytes) => images.push((path.clone(), bytes)),
Err(e) => tracing::debug!("poster fetch skipped for {path}: {e}"),
}
}
tracing::info!("resolved {}/{} poster images", images.len(), paths.len());
images
}
}