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>
3.2 KiB
Fix: Video Codec Compatibility + Timeline Pan/Scroll + Minimap
Date: 2026-09-21 12:56
Task: Fix video playback (black screen despite play state), add timeline panning when zoomed, add minimap overview.
Root Causes & Fixes
1. Video playback: WKWebView doesn't support VP9/WebM
The downloaded file was VP9+Opus in WebM container (yt-dlp format 399+251). Apple's WKWebView on macOS does NOT support VP9 codec. The <video> element accepted .play() but had no decodable frames.
Fix: Added -f "bv*[vcodec^=avc1]+ba[acodec^=mp4a]/bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]/b" to the yt-dlp download command in download_manager.rs. This selects H.264 (avc1) video + AAC (mp4a) audio, producing an MP4 file playable by WKWebView. Deleted the cached WebM file.
2. Timeline: no scroll/pan when zoomed
Mouse wheel only triggered zoom. No way to scroll horizontally when zoomed in.
Fix in Timeline.svelte:
- Horizontal scroll → pan:
deltaXfrom trackpad/shift+wheel now callspanBy()to shift the visible window - Vertical scroll → zoom (unchanged)
New panBy() in interactions.ts: Computes new visibleStart/visibleEnd from pixel delta, clamped to [0, duration].
3. Minimap overview bar
Added a minimap canvas that appears when zoomed in:
- Shows the full waveform at a glance
- Highlights the current viewport with a blue border
- Dims regions outside the viewport
- Click to center viewport, drag to pan
- Playhead indicator
4. Other fixes applied this session (cumulative)
- stdout/stderr swap: yt-dlp sends status messages to stdout, not stderr
[Merger]path parsing: Correctly captures final merged file path- "Already downloaded" parsing: Handles
has already been downloadedmessage - Cache check:
checkCachedDownload(title)checks temp dir before invoking yt-dlp - Parallel processing: Waveform/thumbnails/keyframes results applied individually as they complete
- Asset protocol scope: Added
/private/var/**and/var/**for macOS symlink paths
Files Changed
src-tauri/src/services/download_manager.rs— H.264 format selectionsrc/lib/components/Timeline.svelte— Minimap + horizontal pan supportsrc/lib/timeline/interactions.ts— AddedpanBy()functionsrc-tauri/src/commands/video.rs— Cache check, diagnostic loggingsrc-tauri/src/commands/media_analysis.rs— Diagnostic loggingsrc-tauri/src/lib.rs— Registeredcheck_cached_downloadcommandsrc-tauri/tauri.conf.json— Expanded asset protocol scopesrc/lib/stores/videoSession.svelte.ts— Cache check before download, parallel resultssrc/lib/bindings/video.ts—checkCachedDownloadbindingsrc/lib/components/VideoPlayer.svelte— Local file playback with progress UI
Lessons Learned
- WKWebView codec support is limited. VP9/AV1/WebM don't work. Must use H.264+AAC in MP4.
- macOS
/varis a symlink to/private/var. Asset protocol scope must cover both paths. Promise.allSettledblocks all results until the slowest completes. Use individual.then()chains when you want progressive rendering.- yt-dlp sends status to stdout, warnings to stderr. Not the other way around.