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:
2026-09-22 16:16:00 -04:00
parent 925a3fc054
commit 9499d776fd
15 changed files with 471 additions and 30 deletions

View File

@@ -31,7 +31,43 @@
onclick={(e) => e.stopPropagation()}
onkeydown={(e) => e.stopPropagation()}
>
<img class="app-icon" src="/favicon.svg" alt="GUI Video Clipper icon" />
<svg class="app-icon" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="blade-grad" gradientUnits="userSpaceOnUse" x1="256" y1="130" x2="256" y2="290">
<stop offset="0%" stop-color="#fb923c"/>
<stop offset="100%" stop-color="#ea580c"/>
</linearGradient>
</defs>
<rect width="512" height="512" rx="90" fill="#18181b"/>
<g transform="translate(-3, 3)">
<polygon points="72,220 265,220 239,292 72,292" fill="#3f3f46"/>
<rect x="92" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="130" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="168" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="92" y="273" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="130" y="273" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="168" y="273" width="11" height="13" rx="3" fill="#27272a"/>
</g>
<g transform="translate(3, -3)">
<polygon points="273,220 440,220 440,292 247,292" fill="#3f3f46"/>
<rect x="298" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="345" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="392" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="278" y="273" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="330" y="273" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="382" y="273" width="11" height="13" rx="3" fill="#27272a"/>
</g>
<line x1="269" y1="216" x2="243" y2="296" stroke="#f97316" stroke-width="2" opacity="0.6"/>
<g transform="rotate(20, 256, 256)">
<polygon points="264,250 172,155 248,262" fill="url(#blade-grad)"/>
<polygon points="248,250 340,155 264,262" fill="url(#blade-grad)"/>
<polygon points="248,250 194,330 202,336 264,262" fill="#52525b"/>
<polygon points="264,250 318,330 310,336 248,262" fill="#52525b"/>
<circle cx="192" cy="348" r="20" stroke="#a1a1aa" stroke-width="4" fill="#18181b"/>
<circle cx="320" cy="348" r="20" stroke="#a1a1aa" stroke-width="4" fill="#18181b"/>
<circle cx="256" cy="256" r="7" fill="#a1a1aa"/>
</g>
</svg>
<h2>GUI Video Clipper</h2>
{#if version}
<span class="version">v{version}</span>

View File

@@ -209,9 +209,13 @@ async function triggerPostDownloadProcessing(filePath: string) {
}
if (cached.captionFilePath) {
session.captionFilePath = cached.captionFilePath;
} else {
// Captions weren't cached — re-extract
await loadCaptionsWithCache(filePath);
}
session.processingStep = 'done';
if (session.exportStatus === 'complete' && session.exportFilePath) {
if (session.exportStatus === 'complete' && session.exportFilePath
&& session.activeVideoPath !== session.exportFilePath) {
session.showUpgradeToast = true;
}
return;
@@ -272,7 +276,8 @@ async function triggerPostDownloadProcessing(filePath: string) {
console.error('Failed to save analysis to cache:', err);
}
if (session.exportStatus === 'complete' && session.exportFilePath) {
if (session.exportStatus === 'complete' && session.exportFilePath
&& session.activeVideoPath !== session.exportFilePath) {
session.showUpgradeToast = true;
}
}
@@ -319,8 +324,9 @@ export async function beginDownload() {
session.previewProgress = 1.0;
session.previewStatus = 'complete';
session.activeVideoPath = exportCached;
// No upgrade toast — we're already at best quality
triggerPostDownloadProcessing(exportCached);
// Prefer preview file for analysis (much faster ffmpeg operations)
const previewForProcessing = await checkCachedDownload(session.title, 'preview').catch(() => null);
triggerPostDownloadProcessing(previewForProcessing || exportCached);
return;
}
@@ -403,7 +409,15 @@ export async function beginDownload() {
/** Clear cache for the current video and re-extract all analysis data. */
export async function reprocessCurrentVideo() {
if (!session.url || !session.activeVideoPath) return;
const filePath = session.activeVideoPath;
// Always use the low-res preview file for processing (never the HQ export)
let processingPath: string | null = session.previewFilePath;
if (!processingPath || processingPath === session.exportFilePath) {
// Try to find a cached preview file on disk
processingPath = await checkCachedDownload(session.title, 'preview').catch(() => null);
}
// Last resort: fall back to whatever is active (should rarely happen)
const filePath = processingPath || session.activeVideoPath;
// Clear cache
try {