9.4 KiB
v0.1.2 Polish: Placeholders, Auto-Upgrade, and Analysis Cache
Date: 2026-09-22 Version: 0.1.2 Status: Design
Overview
Three polish features for v0.1.2:
- Fix misleading placeholder text shown before any video is loaded
- Automatically use cached HQ video files instead of prompting to upgrade
- Cache video analysis results (waveforms, keyframes, thumbnails, captions) to avoid reprocessing
1. Idle-State Placeholder Text
Problem
When the app first loads with no video, the timeline shows "Generating Waveform..." and "Generating Thumbnails..." in the placeholder lanes. No work is happening — this is misleading.
Solution
Make drawTimeline context-aware by accepting a hasVideo boolean (derived from session.status === 'ready').
- No video loaded (
hasVideo === false): Draw empty lane backgrounds with no text. - Video loaded, data pending (
hasVideo === true, data missing): Show "Generating Waveform..." / "Generating Thumbnails..." as today.
Files Changed
src/lib/timeline/renderer.ts: AddhasVideoparameter todrawTimeline(). UpdatedrawPlaceholderLane()to accept a boolean controlling whether to show text. WhenhasVideois false, render the background rectangle but skip the text label.src/lib/components/Timeline.svelte: Passsession.status === 'ready'as thehasVideoargument.
2. Auto-Use Cached HQ Video
Problem
When a video's HQ (export) file is already cached on disk from a prior session, the app still downloads a low-res preview, then shows an "Upgrade" toast to switch to HQ. The user should get the HQ version immediately.
Solution
At the start of beginDownload(), check the export cache first. If the HQ file exists:
- Set
activeVideoPathto the HQ file immediately - Set
exportFilePath,exportStatus = 'complete',exportProgress = 1.0 - Mark preview as complete too (skip the preview download entirely)
- Run
triggerPostDownloadProcessing()against the HQ file - No upgrade toast shown
When no cached HQ exists, the existing two-track download flow (preview first, background HQ) continues unchanged. The upgrade toast still appears when HQ completes mid-session — silently swapping the video source during active playback would be jarring.
Behavior Matrix
| HQ cached? | Preview cached? | Behavior |
|---|---|---|
| Yes | (irrelevant) | Use HQ directly. No preview download. No toast. |
| No | Yes | Use cached preview. Start background HQ download. Toast on HQ completion. |
| No | No | Download preview, start background HQ. Toast on HQ completion. |
Files Changed
src/lib/stores/videoSession.svelte.ts: RestructurebeginDownload()to check export cache before preview cache. Add early-return path that sets HQ as active video.
3. Analysis Cache System
Problem
Every time a video is loaded, waveforms, keyframes, and thumbnails are re-extracted from scratch via ffmpeg. For a 5-minute video this takes 10-30 seconds. Re-opening the same video should be instant.
Architecture
Cache Directory
~/Library/Application Support/gui-video-clipper/cache/
<url-hash>/
manifest.json
thumbnails/
frame_000001.jpg
frame_000002.jpg
...
captions/
en.vtt
The URL hash is the first 16 hex characters of the SHA-256 of the source URL. This provides sufficient collision resistance while keeping paths short.
Manifest Schema
{
"version": 1,
"sourceUrl": "https://youtube.com/watch?v=abc123",
"title": "Example Video",
"duration": 312.5,
"fps": 30.0,
"createdAt": "2026-09-22T18:00:00Z",
"lastAccessedAt": "2026-09-22T18:05:00Z",
"waveformTiers": {
"tier0": [0.1, 0.5, ...],
"tier1": [0.1, 0.3, 0.5, ...],
"tier2": [0.05, 0.1, 0.15, ...]
},
"keyframePositions": [0.0, 2.5, 5.0, 7.5],
"thumbnailSpritesheets": [
{
"filePath": "thumbnails",
"startIndex": 0,
"count": 100,
"thumbWidth": 160,
"thumbHeight": 90,
"columns": 10,
"intervalSeconds": 1.0
}
],
"captionFilePath": "captions/en.vtt"
}
Data sizing: Waveform tier2 is at most 200K floats (~1.6 MB as JSON). Tier0 and tier1 add ~100 KB combined. Keyframes are a few hundred entries. This all fits comfortably inline in JSON. Thumbnails are JPEG files on disk, referenced by relative path.
All paths in the manifest are relative to the cache entry directory.
New Rust Service: cache_manager.rs
// Public API
pub fn cache_dir_for_url(url: &str) -> PathBuf
pub fn load_manifest(url: &str) -> Option<CacheManifest>
pub fn save_manifest(url: &str, manifest: &CacheManifest) -> Result<(), String>
pub fn clear_cache_entry(url: &str) -> Result<(), String>
pub fn clear_all_cache() -> Result<(), String>
pub fn get_cache_stats() -> Result<CacheStats, String>
pub fn thumbnail_cache_dir(url: &str) -> PathBuf
pub fn caption_cache_dir(url: &str) -> PathBuf
CacheManifest struct mirrors the JSON schema above. CacheStats contains entry_count: usize and total_bytes: u64.
New IPC Commands
Added to src-tauri/src/commands/ (new file cache.rs or added to existing):
| Command | Signature | Purpose |
|---|---|---|
load_cached_analysis |
(url: String) -> Option<CachedAnalysis> |
Load all cached data for a URL |
save_analysis_to_cache |
(url, waveform_tiers, keyframe_positions, thumbnail_spritesheets, caption_file_path) -> () |
Persist analysis results |
clear_video_cache |
(url: String) -> () |
Delete one video's cache |
clear_all_cache |
() -> () |
Delete the entire cache dir |
get_cache_stats |
() -> CacheStats |
Get entry count and total size |
get_thumbnail_cache_dir |
(url: String) -> String |
Get the cache directory path for thumbnail output |
get_caption_cache_dir |
(url: String) -> String |
Get the cache directory path for caption output |
CachedAnalysis contains:
waveformTiers: WaveformTierskeyframePositions: Vec<f64>thumbnailSpritesheets: Vec<ThumbnailSpritesheet>(with absolute paths resolved from the cache dir)captionFilePath: Option<String>(absolute path)
save_analysis_to_cache accepts the individual analysis fields rather than a wrapper struct, keeping the IPC boundary simple. The Rust side constructs the CacheManifest from these fields plus metadata (source URL, title, timestamps) looked up from the manifest or computed fresh.
Frontend Integration
videoSession.svelte.ts — triggerPostDownloadProcessing():
- Call
load_cached_analysis(session.url)before running ffmpeg. - On cache hit: populate session state directly, jump to
processingStep = 'done'. Skip all ffmpeg extraction. - On cache miss: run extraction as today. After all steps complete (waveform, keyframes, thumbnails, captions), call
save_analysis_to_cache(...)to persist.
New TypeScript bindings: src/lib/bindings/cache.ts with wrapper functions for the new IPC commands.
Thumbnail Path Changes
Currently, extract_thumbnails (Rust command) generates a UUID-based temp dir for thumbnails. With caching:
- Add an optional
output_dir: Option<String>parameter to theextract_thumbnailsTauri command. When provided, thumbnails are written there. WhenNone, the existing UUID-based temp dir behavior is preserved as a fallback. - The frontend calls
get_thumbnail_cache_dir(session.url)to get the cache-managed output path, then passes it toextract_thumbnails. - Similarly,
get_caption_cache_dir(session.url)provides the path for caption file output.
Cache Invalidation UX
Preferences Panel (PreferencesPanel.svelte):
- New "Cache" section at the bottom
- Display total cache size (e.g., "Cache: 450 MB across 12 videos")
- "Clear All Cache" button with confirmation
Per-Video Reprocess (in StatusBar.svelte or near the transport controls):
- A small "↻" (reprocess) button visible when a video is loaded and processing is complete (
processingStep === 'done') - Calls
clear_video_cache(session.url)then re-runstriggerPostDownloadProcessing()on the current video file - This re-extracts analysis data only — it does not re-download the video
Future: Project Files
The cache manifest structure is designed to be referenced by future project files. A project file would store:
- The source URL
- Clip regions (start/end times, labels, colors)
- Caption settings
- A reference to the cache entry (by URL hash)
This design means a project file doesn't duplicate analysis data — it points to the cache. If the cache is cleared, the project can re-derive everything from the source URL.
No project file save/load UI is implemented in this version. The manifest structure is the forward-looking scaffold.
Dead Code Annotations
Any types or fields scaffolded for future project file support (e.g., fields in CacheManifest not yet read by the frontend) will be annotated with #[allow(dead_code)] and a // Future: used by project file save/load comment. This satisfies the requirement to handle dead code warnings proactively (item 3b).
Constraints
- Zero new warnings/errors: All existing warnings and errors were recently resolved. Any new warnings introduced during implementation must be addressed immediately.
- macOS-only paths:
~/Library/Application Support/is macOS-specific, which matches the project's macOS-only target. - Backward compatibility: Existing temp-dir downloads (preview/export) continue to work as before. The cache is additive — it doesn't replace the download mechanism.