# Karaoke Burn-In for Exported Subtitles **Date:** 2026-09-21 23:11 **Task:** Convert word-timed YouTube VTT captions to ASS format with karaoke `\kf` tags so burned-in subtitles have the same word-by-word highlight effect as the preview. ## Changes Made ### 1. VTT-to-ASS Converter with Karaoke Tags **`src-tauri/src/services/clip_exporter.rs`** — Added several new functions: - `vtt_to_ass_with_karaoke()` — Main converter. Reads raw VTT, generates a complete ASS file with: - `[Script Info]` header (1920x1080 play resolution) - `[V4+ Styles]` section with the user's CaptionStyle baked in (PrimaryColour, SecondaryColour at 60% alpha for the "dim/upcoming" look, BackColour, Outline, Shadow, Alignment, BorderStyle=4) - `[Events]` section where each cue is a Dialogue line with `\kf` tags for word-level karaoke fill animation - Graceful fallback: cues without word-level timestamps render as plain text (no `\kf`) - `parse_vtt_word_timings()` — Rust equivalent of the TypeScript `parseWordTimings()`. Extracts `(word, start_time)` pairs from YouTube's ` word` format. - `format_ass_timestamp()` — Formats seconds as ASS timestamp `H:MM:SS.cc` (centiseconds). - `hex_to_ass_color_with_alpha()` — Like `hex_to_ass_color()` but with a specific alpha byte. - `find_timestamp_tag_pos()` / `find_next_timestamp()` — Helpers for parsing VTT timestamp tags without regex. ### 2. Updated Burn-In Export Path In `export_single_clip_with_subs()`, the burn-in path now uses: ``` VTT → vtt_to_ass_with_karaoke() → .ass file → subtitles= filter ``` Instead of the previous: ``` VTT → sanitize_vtt_for_ffmpeg() → plain VTT → subtitles= filter ``` Since the style is now embedded in the ASS file header, `caption_style` is passed as `None` to `build_ffmpeg_args_burnin_subs()` (no `force_style` needed — avoids potential conflicts between the ASS header and force_style overrides). ### 3. Updated UI Note **`src/lib/components/ExportDialog.svelte`** — Changed the burn-in note from "word-by-word highlighting is preview-only" to "Captions will be burned into the video with your style settings". ## Files Modified | File | Change | |------|--------| | `src-tauri/src/services/clip_exporter.rs` | Added VTT-to-ASS converter, word timing parser, ASS timestamp formatter, updated burn-in path | | `src/lib/components/ExportDialog.svelte` | Updated burn-in note text | ## Build Verification - `cargo check` — passes (only pre-existing warnings) - `cargo test` — all 43 tests pass ## Technical Notes - ASS karaoke `\kf` means "fill this word over N centiseconds", transitioning from SecondaryColour to PrimaryColour. This matches the preview behavior where upcoming words are dimmed and spoken words are bright. - The ASS SecondaryColour is set to the same text color with 0x99 alpha (~60% transparent), matching the preview's `opacity: 0.4` for upcoming words. - No regex crate was needed — the VTT word timing pattern is simple enough to parse with manual string operations. - The `subtitles` ffmpeg filter handles ASS files natively (same libass backend), so no filter change was needed.