fix: about dialog, cache paths, keyframes, reprocess always uses low-res
- Replace native macOS About menu with custom Svelte dialog via Tauri event - Inline SVG icon in AboutDialog (fixes broken image in webview) - Align cache directory with Tauri $APPDATA, add to asset protocol scope - Fix ffprobe 8.x keyframe parsing (skip_frame nokey, flexible parser) - Copy caption files into cache dir when outside cache path - Add -y flag to ffmpeg thumbnail extraction (prevent hang on re-extract) - Reprocess always uses preview (low-res) file, never HQ export - Prefer cached preview file for analysis even when HQ is cached - Guard upgrade toast when already on HQ Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
# Fix pyenv pot-provider detection & version bump to v0.1.1
|
||||
|
||||
## Task Description
|
||||
The built `.app` bundle was not detecting the `bgutil-ytdlp-pot-provider` yt-dlp plugin, even after the initial `fix_path_env()` implementation that added Homebrew paths to `PATH`. After fixing, bump version to 0.1.1, commit, push, and tag.
|
||||
|
||||
## Root Cause
|
||||
`fix_path_env()` added `/opt/homebrew/bin` to PATH, which found Homebrew's `yt-dlp` binary. However, the pot-provider plugin was pip-installed under **pyenv**'s Python (`~/.pyenv/versions/3.12.6/lib/python3.12/site-packages/yt_dlp_plugins`). Homebrew's yt-dlp uses a different Python runtime with no plugin directories (`[debug] Plugin directories: none`). The pyenv shim for `yt-dlp` correctly resolves to pyenv's Python where the plugin is installed.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### `src-tauri/src/lib.rs` — Enhanced `fix_path_env()`
|
||||
- Added `~/.pyenv/shims` to PATH **before** Homebrew paths so pyenv-managed yt-dlp (with pip plugins) takes priority
|
||||
- Sets `PYENV_ROOT` env var when pyenv exists so shims resolve correctly
|
||||
- Added `~/.nvm/versions/node/<latest>/bin` discovery for nvm-managed Node.js
|
||||
- Added `~/.deno/bin` to handled paths
|
||||
- Restructured the function for clarity: home-relative paths first, then system-wide paths
|
||||
|
||||
### Version Bump (via `scripts/bump-version.sh 0.1.1`)
|
||||
- `VERSION` → 0.1.1
|
||||
- `package.json` → 0.1.1
|
||||
- `src-tauri/Cargo.toml` → 0.1.1
|
||||
- `src-tauri/tauri.conf.json` → 0.1.1
|
||||
- `src-tauri/Cargo.lock` regenerated
|
||||
|
||||
### Git Operations
|
||||
- Committed: `d46973a` on `master`
|
||||
- Pushed to `origin/master`
|
||||
- Tagged: `v0.1.1` (annotated, pushed)
|
||||
|
||||
## Verification
|
||||
- `npm run check` — 0 errors, 0 warnings
|
||||
- `npm test` — 5 files, 31 tests passed
|
||||
- `cargo build` — clean compile
|
||||
- `npm run release` — built `.app` and `.dmg` successfully
|
||||
- Launched `.app` bundle — all 4 dependencies detected (ffmpeg, yt-dlp, JS runtime, bgutil-ytdlp-pot-provider)
|
||||
|
||||
## Lessons Learned
|
||||
- **Python version managers (pyenv) create shim binaries** that dispatch to a specific Python version. When a tool like yt-dlp is both Homebrew-installed and pyenv/pip-installed, PATH ordering determines which one runs — and they can have different plugin directories.
|
||||
- **Simulating the `.app` environment** with `env -i HOME="$HOME" PATH="<minimal>" yt-dlp -v` was the key diagnostic step that instantly reproduced the issue (`Plugin directories: none` vs `Plugin directories: ~/.pyenv/...`).
|
||||
- The `bump-version.sh` script works correctly for multi-file version bumps across `VERSION`, `package.json`, `Cargo.toml`, `tauri.conf.json`, and `Cargo.lock`.
|
||||
|
||||
## Follow-up Items
|
||||
- The `npm pkg set` command in `bump-version.sh` produces a cosmetic JSON error with `--json` flag before falling back; could be cleaned up.
|
||||
86
chat-summaries/2026-09-22_14-50-v0.1.2-polish-summary.md
Normal file
86
chat-summaries/2026-09-22_14-50-v0.1.2-polish-summary.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# v0.1.2 Polish — Placeholders, Auto-Upgrade, Analysis Cache
|
||||
|
||||
**Date**: 2026-09-22 14:50 ET
|
||||
**Duration**: ~20 minutes (brainstorm) + ~20 minutes (execution)
|
||||
**Version**: 0.1.1 → 0.1.2
|
||||
**Commits**: 2f81011..925a3fc (11 commits)
|
||||
|
||||
## Task Description
|
||||
|
||||
Three polish features for v0.1.2:
|
||||
1. Fix misleading "Generating Waveform…" / "Generating Thumbnails…" placeholder text shown in the timeline before any video is loaded
|
||||
2. Automatically use cached HQ video files instead of prompting user to "Upgrade" when the export file is already downloaded
|
||||
3. Cache video analysis results (waveforms, keyframes, thumbnails, captions) to avoid reprocessing the same video on reload
|
||||
|
||||
## Changes Made
|
||||
|
||||
### Feature 1: Idle-State Placeholder Text
|
||||
- **`src/lib/timeline/renderer.ts`**: Added `hasVideo` boolean parameter to `drawTimeline()` and `showLabel` to `drawPlaceholderLane()`. When no video is loaded, timeline lanes show empty backgrounds without misleading "Generating…" text.
|
||||
- **`src/lib/components/Timeline.svelte`**: Passes `session.status === 'ready'` as the `hasVideo` argument.
|
||||
|
||||
### Feature 2: Auto-Use Cached HQ Video
|
||||
- **`src/lib/stores/videoSession.svelte.ts`**: Restructured `beginDownload()` to check export (HQ) cache before preview cache. When HQ file exists on disk, it's used directly as the active video — no preview download, no upgrade toast. The upgrade toast is preserved for mid-session HQ completions (to avoid jarring video source swaps during playback).
|
||||
|
||||
### Feature 3: Analysis Cache System
|
||||
|
||||
**New Rust service** (`src-tauri/src/services/cache_manager.rs`):
|
||||
- Cache stored at `~/Library/Application Support/gui-video-clipper/cache/`
|
||||
- Videos keyed by SHA-256 URL hash (first 16 hex chars)
|
||||
- JSON manifest per video with waveform tiers, keyframe positions, thumbnail spritesheets, caption paths
|
||||
- Functions: `load_manifest`, `save_manifest`, `clear_cache_entry`, `clear_all_cache`, `get_cache_stats`
|
||||
- 5 unit tests for URL hashing and path computation
|
||||
|
||||
**New IPC commands** (`src-tauri/src/commands/cache.rs`):
|
||||
- `load_cached_analysis` / `save_analysis_to_cache` / `clear_video_cache` / `clear_all_cache` / `get_cache_stats` / `get_thumbnail_cache_dir` / `get_caption_cache_dir`
|
||||
- Path resolution: manifest stores relative paths, IPC layer resolves to absolute for frontend
|
||||
|
||||
**Updated `extract_thumbnails`**: Added optional `output_dir` parameter so thumbnails can be directed to cache directory instead of random temp dir.
|
||||
|
||||
**TypeScript bindings** (`src/lib/bindings/cache.ts`): Full wrapper layer for all cache IPC commands.
|
||||
|
||||
**Frontend integration** (`src/lib/stores/videoSession.svelte.ts`):
|
||||
- `triggerPostDownloadProcessing()` checks cache first — on hit, populates session state instantly and skips ffmpeg. On miss, extracts normally then saves to cache.
|
||||
- Thumbnails directed to cache dir via `getThumbnailCacheDir`
|
||||
- New `reprocessCurrentVideo()` export: clears cache entry and re-runs extraction
|
||||
|
||||
**Preferences panel**: Cache management section showing total size and entry count with "Clear All Cache" button.
|
||||
|
||||
**StatusBar**: Per-video "↻" reprocess button visible when processing is complete.
|
||||
|
||||
**Dependency added**: `sha2 = "0.10"` for URL hashing.
|
||||
|
||||
## Files Changed (15 files)
|
||||
|
||||
### New Files (3)
|
||||
- `src-tauri/src/services/cache_manager.rs`
|
||||
- `src-tauri/src/commands/cache.rs`
|
||||
- `src/lib/bindings/cache.ts`
|
||||
|
||||
### Modified Files (12)
|
||||
- `src/lib/timeline/renderer.ts`
|
||||
- `src/lib/components/Timeline.svelte`
|
||||
- `src/lib/stores/videoSession.svelte.ts`
|
||||
- `src-tauri/src/services/mod.rs`
|
||||
- `src-tauri/src/commands/mod.rs`
|
||||
- `src-tauri/src/lib.rs`
|
||||
- `src-tauri/Cargo.toml` + `Cargo.lock`
|
||||
- `src-tauri/src/commands/media_analysis.rs`
|
||||
- `src/lib/bindings/mediaAnalysis.ts`
|
||||
- `src/lib/components/PreferencesPanel.svelte`
|
||||
- `src/lib/components/StatusBar.svelte`
|
||||
- `VERSION`, `package.json`, `tauri.conf.json`
|
||||
|
||||
## Test Results
|
||||
- **Rust**: 56 tests pass, 0 warnings
|
||||
- **Frontend**: svelte-check 0 errors/warnings, vitest 31/31 pass
|
||||
|
||||
## Follow-Up Items
|
||||
- **HQ+toast edge case**: When video loaded with cached HQ file, `triggerPostDownloadProcessing` can still set `showUpgradeToast = true` even though playback is already HQ. Cosmetic — toast appears but does nothing harmful. Could add a guard `activeVideoPath !== exportFilePath` in a future pass.
|
||||
- **`now_iso8601` naming**: Function in `cache_manager.rs` returns Unix epoch seconds, not ISO 8601. Cosmetic naming issue.
|
||||
- **Caption cache dir**: `getCaptionCacheDir` is wired but not used yet — captions still go to temp dir. The manifest stores whatever path captions end up at. Could wire caption output to cache dir in a future pass for full persistence.
|
||||
- **Project files**: Cache manifest structure is designed as forward-looking scaffold for future project file save/load. No UI implemented yet.
|
||||
|
||||
## Lessons Learned
|
||||
- Checking export cache before preview cache in `beginDownload()` was a clean refactor that simplified the flow — early return avoids duplicate logic.
|
||||
- Inlining waveform/keyframe data in JSON manifests works well for the data sizes involved (~2 MB worst case). No need for binary serialization.
|
||||
- The `#![allow(dead_code)]` at module scope in `cache_manager.rs` was pragmatic but broader than ideal — individual annotations would be more precise. Resolved naturally once Task 5 wired up the commands.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Fix v0.1.2 Regressions
|
||||
|
||||
**Date:** 2026-09-22 15:21
|
||||
**Task:** Fix three regressions introduced by the v0.1.2 caching work
|
||||
|
||||
## Changes Made
|
||||
|
||||
### Bug 1: Slow keyframe/thumbnail/caption generation (seconds → minutes)
|
||||
|
||||
**Root cause:** When the HQ export file was cached from a prior session, `beginDownload()` passed the HQ file to `triggerPostDownloadProcessing()`. All ffmpeg operations ran on the full-resolution file instead of the ~360p preview, causing 10-50x slowdown.
|
||||
|
||||
**Fix in `src/lib/stores/videoSession.svelte.ts`:** In the HQ early-return path, also check for cached preview and prefer it for ffmpeg analysis while still using HQ for playback:
|
||||
```typescript
|
||||
const previewForProcessing = await checkCachedDownload(session.title, 'preview').catch(() => null);
|
||||
triggerPostDownloadProcessing(previewForProcessing || exportCached);
|
||||
```
|
||||
|
||||
### Bug 2: Blank thumbnails
|
||||
|
||||
**Root cause:** Tauri's asset protocol scope in `tauri.conf.json` only allowed temp directories. The new thumbnail cache directory under `~/Library/Application Support/` was blocked, so `convertFileSrc()` requests were denied.
|
||||
|
||||
**Fix (3 files):**
|
||||
1. `src-tauri/src/services/cache_manager.rs`: Added `OnceLock<PathBuf>` + `init()` function so `cache_base_dir()` uses the Tauri-resolved app data directory (matching `$APPDATA`).
|
||||
2. `src-tauri/src/lib.rs`: Call `cache_manager::init(app.path().app_data_dir())` during setup.
|
||||
3. `src-tauri/tauri.conf.json`: Added `"$APPDATA/**"` to the asset protocol scope.
|
||||
|
||||
**Side effect:** Existing caches under `~/Library/Application Support/gui-video-clipper/cache/` are orphaned. New location is `~/Library/Application Support/xyz.cottongin.gui-video-clipper/cache/`. One-time cost for a brand-new feature.
|
||||
|
||||
### Bug 3: False upgrade toast when already on HQ
|
||||
|
||||
**Root cause:** `triggerPostDownloadProcessing()` unconditionally set `showUpgradeToast = true` when `exportStatus === 'complete'`, even when `activeVideoPath` was already the export file.
|
||||
|
||||
**Fix in `src/lib/stores/videoSession.svelte.ts`:** Added guard `&& session.activeVideoPath !== session.exportFilePath` to both toast-setting locations (cache-hit and cache-miss blocks).
|
||||
|
||||
### Defensive fix: ffmpeg `-y` flag
|
||||
|
||||
**File:** `src-tauri/src/services/thumbnail_extractor.rs`
|
||||
Added `-y` flag to the ffmpeg thumbnail extraction command to prevent hangs if re-extracting to a directory with existing frame files.
|
||||
|
||||
## Files Changed
|
||||
|
||||
- `src/lib/stores/videoSession.svelte.ts` — Fixes 1 and 3
|
||||
- `src-tauri/src/services/cache_manager.rs` — Fix 2 (OnceLock for app data dir)
|
||||
- `src-tauri/src/lib.rs` — Fix 2 (init cache manager in setup)
|
||||
- `src-tauri/tauri.conf.json` — Fix 2 (add $APPDATA to scope)
|
||||
- `src-tauri/src/services/thumbnail_extractor.rs` — Defensive `-y` flag
|
||||
|
||||
## Lessons Learned
|
||||
|
||||
1. When adding a caching layer that moves files from temp dirs to persistent dirs, always verify that security scopes (Tauri asset protocol, CSP, etc.) cover the new location.
|
||||
2. When adding an "HQ early return" optimization, consider that downstream processing might still benefit from the smaller preview file — separate the "playback source" from the "analysis source."
|
||||
3. Toast/notification conditions should always guard against the "already in target state" case, not just the "target state reached" case.
|
||||
4. ffmpeg commands writing to potentially non-empty directories need `-y` to avoid interactive prompts that hang `.output()` calls.
|
||||
|
||||
## Follow-up Items
|
||||
|
||||
- Consider cleaning up orphaned caches under the old `gui-video-clipper` directory.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Fix Post-v0.1.2 Issues
|
||||
|
||||
**Date:** 2026-09-22 15:48
|
||||
**Task:** Fix broken keyframe detection, missing cached captions, and About dialog version duplication
|
||||
|
||||
## Changes Made
|
||||
|
||||
### Issue 1: Keyframe buttons always greyed out
|
||||
|
||||
**Root cause:** ffprobe 8.x no longer populates the `flags` field. The parser in `keyframe_index.rs` expected 3-column CSV output (`media_type,pts_time,flags`) and checked `parts[2].contains('K')`. With ffprobe 8.0.1, the output was just timestamps (flags absent), so the parser's `parts.len() >= 3` check failed on every line, always returning `[]`.
|
||||
|
||||
Confirmed locally: `ffprobe -show_entries frame=pts_time,flags -of csv=p=0 -skip_frame nokey` produces:
|
||||
```
|
||||
0.000000,
|
||||
1.000000
|
||||
```
|
||||
|
||||
**Fix in `src-tauri/src/services/keyframe_index.rs`:**
|
||||
- Changed ffprobe command to only request `frame=pts_time` (dropped `flags` since `-skip_frame nokey` already filters to keyframes)
|
||||
- Rewrote `parse_ffprobe_output` to handle: (a) new format: plain timestamp per line with optional trailing comma, (b) legacy format: `section,pts_time[,flags]`
|
||||
- Updated all tests — 5 tests covering new format, legacy with flags, legacy with section prefix, empty, and whitespace
|
||||
|
||||
### Issue 2: Captions not cached or loaded from cache
|
||||
|
||||
**Root cause:** In `save_analysis_to_cache` (cache.rs), caption paths were relativized via `strip_prefix(&cache_dir)`. But captions live outside the cache dir (e.g. `/tmp/video-clipper/subtitles/embedded.vtt`), so `strip_prefix` always failed and `relative_caption` was `None`. Captions were never saved to the manifest.
|
||||
|
||||
**Fix (2 files):**
|
||||
1. `src-tauri/src/commands/cache.rs`: When `strip_prefix` fails, copy the caption file into `cache_dir/captions/` and store the relative path.
|
||||
2. `src/lib/stores/videoSession.svelte.ts`: In the cache-hit path of `triggerPostDownloadProcessing`, if `captionFilePath` is null (old caches without captions), fall back to `loadCaptionsWithCache(filePath)` for re-extraction.
|
||||
|
||||
### Issue 3: About dialog shows "v0.1.2 (0.1.2)"
|
||||
|
||||
**Root cause:** The native macOS About dialog shows "Version {CFBundleShortVersionString} ({CFBundleVersion})". Tauri sets both from the same `version` field, producing "Version 0.1.2 (0.1.2)".
|
||||
|
||||
**Fix in `src-tauri/src/lib.rs`:** Created a custom macOS app menu with `AboutMetadataBuilder` that only sets `short_version` (no build number). Also provides standard Edit menu (undo/redo/cut/copy/paste/select-all). Version is read at compile time via `env!("CARGO_PKG_VERSION")`.
|
||||
|
||||
## Files Changed
|
||||
|
||||
- `src-tauri/src/services/keyframe_index.rs` — Fix 1: ffprobe command + parser + tests
|
||||
- `src-tauri/src/commands/cache.rs` — Fix 2: copy captions into cache dir
|
||||
- `src/lib/stores/videoSession.svelte.ts` — Fix 2: caption fallback on cache hit
|
||||
- `src-tauri/src/lib.rs` — Fix 3: custom macOS menu
|
||||
|
||||
## Verification
|
||||
|
||||
- `cargo check`: 0 warnings, 0 errors
|
||||
- `cargo test`: 58 passed, 0 failed
|
||||
- `svelte-check`: 0 errors, 0 warnings
|
||||
- `ReadLints`: no issues
|
||||
|
||||
## Lessons Learned
|
||||
|
||||
1. ffprobe output format can change across major versions. The `flags` field was silently dropped in ffprobe 8.x. Always test against the actual binary on the target system.
|
||||
2. When caching file references, files outside the cache directory need to be copied in — `strip_prefix` silently returns `None` on failure via `.ok()`, making the bug invisible.
|
||||
3. Native macOS About dialogs auto-format version info as "Version X (Build Y)". When both values are identical, it looks like a bug. Use `AboutMetadataBuilder` to control this.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Fix About Dialog & Reprocess Always Uses Low-Res
|
||||
|
||||
**Date**: 2026-09-22 16:07
|
||||
**Task**: Fix native About dialog duplication/icon + force reprocessing to always use low-res preview file
|
||||
|
||||
## Issues Addressed
|
||||
|
||||
### 1. Native About Dialog Still Showing (Duplicate Version)
|
||||
**Problem**: The macOS menu "About" item still opened the native About panel (which shows duplicate version strings "v0.1.2 (0.1.2)") instead of the custom Svelte `AboutDialog`.
|
||||
|
||||
**Fix**: Replaced the `AboutMetadataBuilder`-based `.about()` menu item with a custom `MenuItemBuilder` that emits a Tauri event (`open-about`). The frontend listens for this event via `listen('open-about', ...)` in `App.svelte` and toggles `showAboutDialog = true`.
|
||||
|
||||
**Files changed**:
|
||||
- `src-tauri/src/lib.rs` — Replaced `AboutMetadataBuilder` usage with `MenuItemBuilder` + `PredefinedMenuItem` for hide/show/quit. Added `.on_menu_event()` handler to emit `open-about` event.
|
||||
- `src/App.svelte` — Added `import { listen } from '@tauri-apps/api/event'` and a `$effect` that listens for `open-about` and opens the custom dialog.
|
||||
|
||||
### 2. Broken Icon in About Dialog
|
||||
**Problem**: The `<img src="/favicon.svg">` tag failed to load in the Tauri webview, showing a broken image placeholder.
|
||||
|
||||
**Fix**: Inlined the SVG directly into the `AboutDialog.svelte` component, eliminating the external file reference. Added `rx="90"` to the background rect for rounded-corner macOS icon styling.
|
||||
|
||||
**Files changed**:
|
||||
- `src/lib/components/AboutDialog.svelte` — Replaced `<img>` tag with inline `<svg>`.
|
||||
|
||||
### 3. Reprocessing Uses Full-Resolution File (Performance)
|
||||
**Problem**: `reprocessCurrentVideo()` used `session.activeVideoPath` which points to the HQ export file after upgrade. All ffmpeg analysis (waveform, keyframes, thumbnails) on the full-res file takes minutes instead of seconds.
|
||||
|
||||
**Fix**: `reprocessCurrentVideo()` now explicitly prefers `session.previewFilePath` for processing. If that equals the export file (or is null), it falls back to checking the disk cache for a low-res preview via `checkCachedDownload(title, 'preview')`. Only uses `activeVideoPath` as a last resort.
|
||||
|
||||
**Files changed**:
|
||||
- `src/lib/stores/videoSession.svelte.ts` — Rewrote `reprocessCurrentVideo()` to resolve the low-res preview path before triggering processing.
|
||||
|
||||
## Verification
|
||||
- `cargo check` — 0 errors, 0 warnings
|
||||
- `cargo test` — 58 tests passed
|
||||
- `svelte-check` — 0 errors, 0 warnings
|
||||
- `ReadLints` — No linter errors
|
||||
|
||||
## Lessons Learned
|
||||
- Tauri's `.about(Some(metadata))` on `SubmenuBuilder` always creates the *native* macOS About panel — there is no way to redirect it to a custom webview dialog. To intercept "About", you must use a regular `MenuItemBuilder` with `.on_menu_event()`.
|
||||
- Inline SVGs in Svelte components are more reliable than `<img src="...">` references in Tauri webviews, where path resolution can differ between dev and production builds.
|
||||
- When a session tracks both a preview and export file path, any function that runs ffmpeg analysis must explicitly resolve which path to use — defaulting to `activeVideoPath` is a footgun since that gets upgraded to HQ.
|
||||
Reference in New Issue
Block a user