Files
gui-video-clipper/chat-summaries/2026-09-21_21-36-fix-caption-export-bugs-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

3.2 KiB

Fix Caption Export Bugs (Round 2)

Task

Three bugs in the caption export implementation needed fixing after the initial burn-in/mux feature was added.

Bug 1: Burn-in filter syntax error

Symptom: ffmpeg error No option name near '/var/.../sanitized.vtt' Root cause: format!("subtitles='{}'", path) passed literal single quotes to ffmpeg via Command::args(). Since there's no shell to strip them, ffmpeg tried to open 'sanitized.vtt' (with quotes in the filename). Fix: Changed to format!("subtitles={}", path) — no quotes needed when passing args directly.

Bug 2: Wrong output duration (1:43 instead of 32s)

Symptom: Exported clip was 103 seconds instead of 32 seconds. Root cause: Both subtitle functions used -to end_time with output seeking (-ss/-to after -i). After -ss discards initial frames and the muxer resets PTS to 0, -to 103 means "output 103 seconds" rather than "stop at input timestamp 103". Fix: Replaced -to end_time with -t duration (clip.end_time - clip.start_time) in both build_ffmpeg_args_mux_subs and build_ffmpeg_args_burnin_subs. -t is unambiguous.

Bug 3: Muxed subtitles invisible in VLC

Symptom: Subtitle track exists in the file but selecting it in VLC shows nothing. Root cause: With output seeking, video PTS was reset to 0 by the muxer, but subtitle cues retained their original absolute timestamps (e.g., 71s-103s). The player sees subtitle cues at 71s but the video is only 32s long. Fix: Two-part strategy change:

  1. Added trim_and_sanitize_vtt() function that extracts cues within the clip's time range and shifts all timestamps to start from 0.
  2. Switched build_ffmpeg_args_mux_subs to use input seeking (-ss/-t before -i) for speed. The pre-trimmed VTT timestamps already start from 0, matching the video output.
  3. Burn-in mode (build_ffmpeg_args_burnin_subs) retains output seeking since the subtitles filter needs to see the original absolute timestamps from the VTT.

Changes Made

src-tauri/src/services/clip_exporter.rs

  • Removed single quotes from subtitles= filter value
  • Changed -to end to -t duration in both subtitle builder functions
  • Added trim_and_sanitize_vtt() with VTT timestamp parsing/shifting
  • Added helper functions: parse_vtt_timestamp_line(), parse_vtt_ts(), format_vtt_timestamp()
  • Switched build_ffmpeg_args_mux_subs to input seeking with pre-trimmed VTT
  • Updated export_single_clip_with_subs to use trimmed VTT for mux, sanitized VTT for burn-in
  • Updated all related tests (43 tests pass)

Lessons Learned

  • Command::args() in Rust bypasses the shell entirely — shell-style quoting (single quotes) becomes literal characters in the argument. Never quote paths for ffmpeg filters when using Command::args().
  • ffmpeg's -to is ambiguous with output seeking — it may refer to post-PTS-reset time rather than input time. -t duration is always safe.
  • When using input seeking on video (-ss before -i), subtitle PTS from a separate input must match the reset PTS (~0). Pre-trimming the VTT with shifted timestamps is the reliable approach.

Follow-up

None — all three user-reported bugs are addressed.