fix: lazy-load wrapup backgrounds, cap sqlite pool to 4 connections
Some checks failed
CI / Check / Test (push) Failing after 6m32s

backgrounds were decoded to RGBA at startup (~173MB for 9 images).
now only store paths, decode on demand during video generation.
This commit is contained in:
2026-06-04 14:57:47 +02:00
parent 01c1082290
commit dacc057af6
2 changed files with 21 additions and 13 deletions

View File

@@ -990,7 +990,9 @@ pub async fn wire(database_url: &str) -> anyhow::Result<SqliteWireOutput> {
.create_if_missing(true) .create_if_missing(true)
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal) .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.busy_timeout(std::time::Duration::from_secs(5)); .busy_timeout(std::time::Duration::from_secs(5));
let pool = sqlx::SqlitePool::connect_with(opts) let pool = sqlx::pool::PoolOptions::new()
.max_connections(4)
.connect_with(opts)
.await .await
.context("Failed to connect to SQLite database")?; .context("Failed to connect to SQLite database")?;

View File

@@ -52,7 +52,7 @@ const GLASS_PADDING: u32 = 30;
pub struct SlideRenderer { pub struct SlideRenderer {
font: FontArc, font: FontArc,
logo: Option<RgbaImage>, logo: Option<RgbaImage>,
backgrounds: Vec<RgbaImage>, bg_paths: Vec<std::path::PathBuf>,
} }
impl SlideRenderer { impl SlideRenderer {
@@ -78,7 +78,7 @@ impl SlideRenderer {
None None
}; };
let mut backgrounds = Vec::new(); let mut bg_paths = Vec::new();
if let Some(dir) = bg_dir if let Some(dir) = bg_dir
&& let Ok(entries) = std::fs::read_dir(dir) && let Ok(entries) = std::fs::read_dir(dir)
{ {
@@ -90,28 +90,34 @@ impl SlideRenderer {
.unwrap_or("") .unwrap_or("")
.to_lowercase(); .to_lowercase();
if matches!(ext.as_str(), "jpg" | "jpeg" | "png" | "webp") { if matches!(ext.as_str(), "jpg" | "jpeg" | "png" | "webp") {
match image::open(&path) { bg_paths.push(path);
Ok(img) => backgrounds.push(img.to_rgba8()),
Err(e) => tracing::warn!("bg load {}: {e}", path.display()),
}
} }
} }
bg_paths.sort();
} }
Ok(Self { Ok(Self {
font, font,
logo, logo,
backgrounds, bg_paths,
}) })
} }
fn load_background(&self, index: usize) -> Option<RgbaImage> {
let path = self.bg_paths.get(index % self.bg_paths.len())?;
match image::open(path) {
Ok(img) => Some(img.to_rgba8()),
Err(e) => {
tracing::warn!("bg load {}: {e}", path.display());
None
}
}
}
/// Pick a background for slide at `index`, resized to `w x h` with dark gradient overlay. /// Pick a background for slide at `index`, resized to `w x h` with dark gradient overlay.
fn pick_background(&self, index: usize, w: u32, h: u32) -> Option<RgbaImage> { fn pick_background(&self, index: usize, w: u32, h: u32) -> Option<RgbaImage> {
if self.backgrounds.is_empty() { let bg = self.load_background(index)?;
return None; let mut out = resize_cover(&bg, w, h);
}
let bg = &self.backgrounds[index % self.backgrounds.len()];
let mut out = resize_cover(bg, w, h);
// darken top 40% and bottom 40% with gradient to ~70% black // darken top 40% and bottom 40% with gradient to ~70% black
let top_cutoff = (h as f32 * 0.4) as u32; let top_cutoff = (h as f32 * 0.4) as u32;
let bot_start = h - top_cutoff; let bot_start = h - top_cutoff;