Files
gui-video-clipper/chat-summaries/2026-09-21_12-21-fix-playback-waveform-thumbnails-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.6 KiB

Fix: Video Playback, Waveform, and Thumbnails Not Working

Date: 2026-09-21 12:21
Task: Diagnose and fix three user-reported issues after downloading a video: no playback, waveform/thumbnails stuck at "Generating...", scrubbing doesn't update the player frame.

Root Causes

1. [Merger] line path not parsed (Primary Bug)

When yt-dlp downloads video+audio separately and merges them, it outputs:

[download] Destination: /path/to/file.f399.mp4    ← intermediate
[download] Destination: /path/to/file.f251.webm   ← intermediate
[Merger] Merging formats into "/path/to/file.webm" ← final merged file

The old parsing used line.split(": ").nth(1) for all three cases. This works for [download] Destination: lines (which have : delimiter) but fails for [Merger] lines (which use into "path" format — no : ). As a result, last_file_path pointed to a deleted intermediate file. Both triggerPostDownloadProcessing(path) and session.localFilePath received the wrong path, causing:

  • All ffmpeg post-processing (waveform, thumbnails, keyframes) to fail with file-not-found
  • The video player to reference a non-existent file

2. Stream URL doesn't work in Tauri webview

The yt-dlp -g stream URL (googlevideo.com) has anti-hotlinking protections (referer/IP checks) that block playback in a Tauri/WKWebView context. The <video> element was present but silently failed to load the source.

3. "Already downloaded" case not handled

When yt-dlp finds an existing file, it outputs [download] /path/file.webm has already been downloaded — a format that matches neither Destination: nor Merger patterns. So re-loading the same URL would result in localFilePath = "".

Changes Made

src-tauri/src/commands/video.rs

  • Fixed [Merger] parsing: Replaced the broken split(": ") approach with trim_start_matches("[Merger] Merging formats into ").trim().trim_matches('"').
  • Added "already downloaded" parsing: New branch for has already been downloaded using strip_prefix/strip_suffix to extract the path.

src/lib/components/VideoPlayer.svelte

  • Switched to local file for playback: Uses convertFileSrc(session.localFilePath) via Tauri's asset protocol once download is complete, instead of the unreliable stream URL.
  • Added download progress UI: Shows a progress bar with percentage while downloading, instead of a broken black video area.
  • Added video error handling: onerror/onloadeddata handlers with an overlay error message.
  • Import: Added convertFileSrc from @tauri-apps/api/core.

Lessons Learned

  1. yt-dlp stderr output formats are inconsistent. [download] Destination: uses : delimiter, [Merger] uses into "path", and "already downloaded" embeds the path mid-sentence. Each needs its own parser.
  2. googlevideo.com stream URLs don't work in embedded webviews due to anti-hotlinking. The design's "hybrid streaming preview" approach needs a local proxy or must fall back to showing download progress until the file is available locally.
  3. Always test with cached files. The "already downloaded" edge case only surfaces on the second load of the same URL.

Follow-up Items

  • Add progress indicators for post-download processing (waveform/thumbnail/keyframe extraction can take minutes on long videos)
  • Consider pre-download preview via local proxy or lower-quality quick download
  • The waveform extraction uses aresample=8000 (sample rate) which produces ~26M samples for a 54-min video — consider optimizing to reduce to target count directly
  • Add error surfacing for failed post-download processing (currently silent console.error)