Files
gui-video-clipper/chat-summaries/2026-09-21_23-11-karaoke-burnin-export-summary.md
cottongin 8ad2f1c800 chore: stage all pending work — caption styling, media server, processing modal, docs, summaries
Includes:
- Extended caption styling (font, shadow, dimmed color, bg toggle)
- Media server, subtitle downloader, VTT parser, processing modal
- Waveform tiers, thumbnail/timeline improvements, transport controls
- Hybrid download model, dependency management, clip export enhancements
- 21 chat summaries, 2 implementation plans, 2 design specs

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 10:48:16 -04:00

61 lines
3.1 KiB
Markdown

# 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<centiseconds>` 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 `<timestamp><c> word</c>` 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<N>` 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.