Reels: portrait canvas with blurred fill, fade transitions, warmer TTS
Fixes the "image is tiny" problem: a 1920x1080 landscape reel letterboxes to a ~25%-height band on a portrait phone. Switch to a portrait 1080x1920 canvas and fill it per photo with a blurred, zoomed copy of the image behind the sharp fitted photo — so the frame is always full regardless of the photo's orientation, with no black bars and no cropping of the subject. Add a quick 0.35s fade in/out baked into each segment so concatenated photos dip smoothly instead of hard-cutting (fade-out lands in the narration's silent tail, so speech isn't clipped). Drop the unused Ken Burns branch — motion can return deliberately later. Warm up the narration a touch: thread Chatterbox's `exaggeration` through synthesize_serialized and default reels to 0.7 (tunable via REEL_TTS_EXAGGERATION). Bump RENDER_VERSION so existing landscape reels re-render. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+27
-11
@@ -180,7 +180,18 @@ fn finish_job(
|
||||
|
||||
/// Render version: bump to invalidate every cached reel after a rendering /
|
||||
/// scripting change that should produce a fresh result.
|
||||
const RENDER_VERSION: u32 = 1;
|
||||
const RENDER_VERSION: u32 = 2;
|
||||
|
||||
/// Narration expressiveness — Chatterbox's `exaggeration` knob. A modest bump
|
||||
/// over the ~0.5 default warms up otherwise-flat narration; tune via
|
||||
/// `REEL_TTS_EXAGGERATION` (0.25–2.0).
|
||||
fn reel_tts_exaggeration() -> f32 {
|
||||
std::env::var("REEL_TTS_EXAGGERATION")
|
||||
.ok()
|
||||
.and_then(|s| s.trim().parse::<f32>().ok())
|
||||
.filter(|x| x.is_finite())
|
||||
.unwrap_or(0.7)
|
||||
}
|
||||
|
||||
/// Cache key over everything that determines *which* media and *how* it's
|
||||
/// voiced — but not the (non-deterministic) narration text. Same inputs → same
|
||||
@@ -470,16 +481,21 @@ async fn run_reel_job(
|
||||
}
|
||||
};
|
||||
|
||||
let audio_bytes =
|
||||
match crate::ai::tts::synthesize_serialized(&client, line, voice.as_deref(), "wav")
|
||||
.await
|
||||
{
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
log::warn!("reel {job_id}: skipping segment {i}, TTS failed: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let audio_bytes = match crate::ai::tts::synthesize_serialized(
|
||||
&client,
|
||||
line,
|
||||
voice.as_deref(),
|
||||
"wav",
|
||||
Some(reel_tts_exaggeration()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
log::warn!("reel {job_id}: skipping segment {i}, TTS failed: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let audio_path = work.path().join(format!("narration_{i:03}.wav"));
|
||||
if let Err(e) = tokio::fs::write(&audio_path, &audio_bytes).await {
|
||||
log::warn!("reel {job_id}: skipping segment {i}, writing audio failed: {e}");
|
||||
|
||||
Reference in New Issue
Block a user