fix: lazy-load wrapup backgrounds, cap sqlite pool to 4 connections
Some checks failed
CI / Check / Test (push) Failing after 6m32s
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:
@@ -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")?;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user