feat: integrate analysis cache into video session processing
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,6 +10,12 @@ import {
|
||||
} from '$lib/bindings/mediaAnalysis';
|
||||
import { clearThumbnailCache } from '$lib/timeline/thumbnailRenderer';
|
||||
import { clearAll as clearAllClips } from '$lib/stores/clips.svelte';
|
||||
import {
|
||||
loadCachedAnalysis,
|
||||
saveAnalysisToCache,
|
||||
getThumbnailCacheDir,
|
||||
clearVideoCache,
|
||||
} from '$lib/bindings/cache';
|
||||
|
||||
export type SessionStatus = 'idle' | 'resolving' | 'ready' | 'error';
|
||||
export type DownloadStatus = 'idle' | 'downloading' | 'complete' | 'failed';
|
||||
@@ -159,7 +165,7 @@ export function dismissUpgradeToast() {
|
||||
session.showUpgradeToast = false;
|
||||
}
|
||||
|
||||
async function loadCaptions(filePath: string) {
|
||||
async function loadCaptionsWithCache(filePath: string) {
|
||||
session.processingStep = 'captions';
|
||||
|
||||
// Priority 1: Check for embedded subtitle streams in the file
|
||||
@@ -187,10 +193,34 @@ async function loadCaptions(filePath: string) {
|
||||
async function triggerPostDownloadProcessing(filePath: string) {
|
||||
const duration = session.duration;
|
||||
|
||||
// Run processing SEQUENTIALLY to avoid overloading the CPU with
|
||||
// concurrent ffmpeg subprocesses. Each one decodes the full file.
|
||||
// Order: waveform first (most useful for editing), then keyframes, then thumbnails.
|
||||
// Check cache first
|
||||
try {
|
||||
const cached = await loadCachedAnalysis(session.url);
|
||||
if (cached) {
|
||||
console.log('[triggerPostDownloadProcessing] cache hit for', session.url);
|
||||
if (cached.waveformTiers) {
|
||||
session.waveformTiers = cached.waveformTiers;
|
||||
}
|
||||
if (cached.keyframePositions.length > 0) {
|
||||
session.keyframePositions = cached.keyframePositions;
|
||||
}
|
||||
if (cached.thumbnailSpritesheets.length > 0) {
|
||||
session.thumbnailSpritesheets = cached.thumbnailSpritesheets;
|
||||
}
|
||||
if (cached.captionFilePath) {
|
||||
session.captionFilePath = cached.captionFilePath;
|
||||
}
|
||||
session.processingStep = 'done';
|
||||
if (session.exportStatus === 'complete' && session.exportFilePath) {
|
||||
session.showUpgradeToast = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Cache load failed, proceeding with extraction:', err);
|
||||
}
|
||||
|
||||
// Cache miss — run extraction sequentially
|
||||
session.processingStep = 'waveform';
|
||||
session.processingProgress = 0;
|
||||
try {
|
||||
@@ -214,18 +244,34 @@ async function triggerPostDownloadProcessing(filePath: string) {
|
||||
session.processingStep = 'thumbnails';
|
||||
session.processingProgress = 0;
|
||||
try {
|
||||
const sheets = await extractThumbnails(filePath, duration);
|
||||
const thumbDir = await getThumbnailCacheDir(session.url);
|
||||
const sheets = await extractThumbnails(filePath, duration, thumbDir);
|
||||
session.thumbnailSpritesheets = sheets;
|
||||
} catch (err) {
|
||||
console.error('Thumbnail extraction failed:', err);
|
||||
}
|
||||
|
||||
// Captions can run after analysis since they use yt-dlp, not ffmpeg
|
||||
await loadCaptions(filePath);
|
||||
// Captions — use cache dir for output
|
||||
await loadCaptionsWithCache(filePath);
|
||||
|
||||
session.processingStep = 'done';
|
||||
|
||||
// If export download already completed while we were processing, show upgrade toast
|
||||
// Save to cache
|
||||
try {
|
||||
await saveAnalysisToCache(
|
||||
session.url,
|
||||
session.title,
|
||||
session.duration,
|
||||
session.fps,
|
||||
session.waveformTiers,
|
||||
session.keyframePositions,
|
||||
session.thumbnailSpritesheets,
|
||||
session.captionFilePath
|
||||
);
|
||||
} catch (err) {
|
||||
console.error('Failed to save analysis to cache:', err);
|
||||
}
|
||||
|
||||
if (session.exportStatus === 'complete' && session.exportFilePath) {
|
||||
session.showUpgradeToast = true;
|
||||
}
|
||||
@@ -353,3 +399,26 @@ export async function beginDownload() {
|
||||
console.error('Export download failed:', e);
|
||||
});
|
||||
}
|
||||
|
||||
/** 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;
|
||||
|
||||
// Clear cache
|
||||
try {
|
||||
await clearVideoCache(session.url);
|
||||
} catch (err) {
|
||||
console.error('Failed to clear video cache:', err);
|
||||
}
|
||||
|
||||
// Reset analysis state
|
||||
clearThumbnailCache();
|
||||
session.keyframePositions = [];
|
||||
session.waveformTiers = null;
|
||||
session.thumbnailSpritesheets = [];
|
||||
session.captionFilePath = null;
|
||||
|
||||
// Re-run processing (will miss cache and extract fresh)
|
||||
await triggerPostDownloadProcessing(filePath);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user