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:
@@ -14,6 +14,7 @@
|
||||
import { getSelectedClipId, removeClip } from '$lib/stores/clips.svelte';
|
||||
import { adjustShuttle, resetShuttleRate } from '$lib/transport/playback';
|
||||
import { session } from '$lib/stores/videoSession.svelte';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import type { TransportKeyAction } from '$lib/transport/playback';
|
||||
|
||||
let showSetupWizard = $state(true);
|
||||
@@ -35,6 +36,14 @@
|
||||
loadPreferences();
|
||||
});
|
||||
|
||||
// Listen for native macOS menu "About" action
|
||||
$effect(() => {
|
||||
const unlisten = listen('open-about', () => {
|
||||
showAboutDialog = true;
|
||||
});
|
||||
return () => { unlisten.then((fn) => fn()); };
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (session.status === 'resolving') {
|
||||
shuttleRate = 1;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user