# Styled Burn-In and Granular Export Progress **Date:** 2026-09-21 23:00 **Task:** Apply user's caption style settings to burned-in subtitles via ffmpeg's ASS force_style, and add granular per-clip progress reporting during export. ## Changes Made ### 1. CaptionStyle Model (Rust + TS) - **`src-tauri/src/models.rs`** — Added `CaptionStyle` struct with `font_size`, `text_color`, `background_opacity`, `text_outline`, `position`. Added `caption_style: Option` to `ExportConfig`. - **`src/lib/bindings/export.ts`** — Added matching `CaptionStyle` interface and `captionStyle: CaptionStyle | null` to `ExportConfig`. Added `clipProgress` event variant to the discriminated union. Added optional `onClipProgress` callback parameter to `exportClips()`. ### 2. ASS force_style in clip_exporter.rs - **`src-tauri/src/services/clip_exporter.rs`**: - `hex_to_ass_color()` — Converts CSS hex `#RRGGBB` to ASS `&H00BBGGRR` format. - `opacity_to_ass_back_colour()` — Converts 0-1 opacity to ASS alpha-prefixed BackColour. - `caption_style_to_force_style()` — Builds the full ASS force_style string from CaptionStyle (Fontsize, PrimaryColour, BackColour, Outline, Shadow, Alignment, MarginV, BorderStyle). - `build_ffmpeg_args_burnin_subs()` now accepts `Option<&CaptionStyle>` and appends `:force_style='...'` to the subtitles filter when provided. - `parse_ffmpeg_time()` — Extracts `time=HH:MM:SS.mm` from ffmpeg stderr progress output. - `run_ffmpeg_with_progress()` — Runs ffmpeg with piped stderr, parses progress lines, and invokes a callback with 0.0-1.0 percent values. ### 3. Progress Callbacks Throughout Export Pipeline All four export functions (`export_single_clip`, `export_single_clip_with_subs`, `export_merged`, `export_merged_with_subs`) now accept progress callbacks. They use `run_ffmpeg_with_progress()` instead of `.output()` for the encode step, streaming real-time progress. ### 4. export.rs Command Handler - **`src-tauri/src/commands/export.rs`** — Added `ClipProgress` event variant with `current`, `total`, `label`, `percent`. Wired progress callbacks for both Individual and Merged export paths. Passes `caption_style` through to the exporter. ### 5. ExportDialog Frontend - **`src/lib/components/ExportDialog.svelte`**: - Populates `captionStyle` in config from `preferences.captionSettings` when burn-in is enabled. - Shows a progress bar with percentage during each clip export. - Displays note that karaoke word highlighting is preview-only for burn-in. ## Files Modified | File | Change | |------|--------| | `src-tauri/src/models.rs` | Added `CaptionStyle` struct, added field to `ExportConfig` | | `src-tauri/src/services/clip_exporter.rs` | ASS helpers, force_style, ffmpeg progress streaming, progress callbacks | | `src-tauri/src/commands/export.rs` | `ClipProgress` event, caption_style passthrough, progress wiring | | `src/lib/bindings/export.ts` | `CaptionStyle` interface, `clipProgress` event, `onClipProgress` callback | | `src/lib/components/ExportDialog.svelte` | captionStyle in config, progress bar UI, karaoke note | ## Build Verification - `cargo check` — passes (only pre-existing warnings) - `cargo test` — all 43 tests pass - `svelte-check` — passes (only pre-existing `vite.config.ts` errors) ## Lessons Learned - ASS color format uses BGR byte order with alpha prefix (`&HAA_BB_GG_RR`), where alpha `00` = opaque and `FF` = transparent (inverted from CSS). - `BorderStyle=4` in ASS gives an opaque background box behind text (like CSS background), vs `BorderStyle=1` which uses outline+shadow only. - ffmpeg progress is on stderr, not stdout. The `time=` field is the key metric for computing encode progress percentage. - For merged exports, progress is reported per-segment during encoding, plus a concat step at the end. ## Follow-Up Items - Smoke test burn-in export to verify styled subtitles render correctly. - Karaoke word-by-word animation in burn-in would require converting VTT word timestamps to ASS `\k` override tags — complex and fragile, noted as preview-only limitation.