feat: wrapup env vars + render concurrency semaphore
Some checks failed
CI / Check / Test (push) Failing after 43s
Some checks failed
CI / Check / Test (push) Failing after 43s
This commit is contained in:
@@ -3,6 +3,15 @@ pub struct AppConfig {
|
||||
pub allow_registration: bool,
|
||||
pub base_url: String,
|
||||
pub rate_limit: u64,
|
||||
pub wrapup: WrapUpConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WrapUpConfig {
|
||||
pub font_path: Option<String>,
|
||||
pub logo_path: Option<String>,
|
||||
pub ffmpeg_path: String,
|
||||
pub max_concurrent_renders: usize,
|
||||
}
|
||||
|
||||
impl AppConfig {
|
||||
@@ -20,6 +29,21 @@ impl AppConfig {
|
||||
allow_registration,
|
||||
base_url,
|
||||
rate_limit,
|
||||
wrapup: WrapUpConfig::from_env(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WrapUpConfig {
|
||||
pub fn from_env() -> Self {
|
||||
Self {
|
||||
font_path: std::env::var("WRAPUP_FONT_PATH").ok(),
|
||||
logo_path: std::env::var("WRAPUP_LOGO_PATH").ok(),
|
||||
ffmpeg_path: std::env::var("FFMPEG_PATH").unwrap_or_else(|_| "ffmpeg".to_string()),
|
||||
max_concurrent_renders: std::env::var("WRAPUP_MAX_CONCURRENT")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,12 @@ impl TestContextBuilder {
|
||||
allow_registration: true,
|
||||
base_url: "http://localhost:3000".into(),
|
||||
rate_limit: 20,
|
||||
wrapup: crate::config::WrapUpConfig {
|
||||
font_path: None,
|
||||
logo_path: None,
|
||||
ffmpeg_path: "ffmpeg".into(),
|
||||
max_concurrent_renders: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use domain::errors::DomainError;
|
||||
use domain::events::DomainEvent;
|
||||
use domain::ports::EventHandler;
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use crate::context::AppContext;
|
||||
|
||||
pub struct WrapUpEventHandler {
|
||||
ctx: AppContext,
|
||||
semaphore: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
impl WrapUpEventHandler {
|
||||
pub fn new(ctx: AppContext) -> Self {
|
||||
Self { ctx }
|
||||
let max = ctx.config.wrapup.max_concurrent_renders;
|
||||
Self {
|
||||
ctx,
|
||||
semaphore: Arc::new(Semaphore::new(max)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +33,9 @@ impl EventHandler for WrapUpEventHandler {
|
||||
start_date,
|
||||
end_date,
|
||||
} => {
|
||||
let _permit = self.semaphore.acquire().await.map_err(|_| {
|
||||
DomainError::InfrastructureError("render semaphore closed".into())
|
||||
})?;
|
||||
super::handle_requested::execute(
|
||||
&self.ctx,
|
||||
wrapup_id.clone(),
|
||||
|
||||
@@ -39,13 +39,14 @@ pub async fn execute(
|
||||
// Optionally render video (non-fatal)
|
||||
if let Some(ref renderer) = ctx.services.video_renderer {
|
||||
let poster_images = resolve_poster_images(ctx, &report).await;
|
||||
let wc = &ctx.config.wrapup;
|
||||
let config = VideoRenderConfig {
|
||||
slide_duration_secs: 4,
|
||||
transition_duration_secs: 0.8,
|
||||
resolution: (1080, 1920),
|
||||
ffmpeg_path: "ffmpeg".to_string(),
|
||||
font_path: None,
|
||||
logo_path: None,
|
||||
ffmpeg_path: wc.ffmpeg_path.clone(),
|
||||
font_path: wc.font_path.clone(),
|
||||
logo_path: wc.logo_path.clone(),
|
||||
};
|
||||
match renderer.render(&report, poster_images, &config).await {
|
||||
Ok(video_bytes) => {
|
||||
|
||||
Reference in New Issue
Block a user