feat: add cache IPC commands for analysis caching
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
153
src-tauri/src/commands/cache.rs
Normal file
153
src-tauri/src/commands/cache.rs
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
use crate::models::{ThumbnailSpritesheet, WaveformTiers};
|
||||||
|
use crate::services::cache_manager::{self, CacheManifest, CacheStats};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CachedAnalysis {
|
||||||
|
pub waveform_tiers: Option<WaveformTiers>,
|
||||||
|
pub keyframe_positions: Vec<f64>,
|
||||||
|
pub thumbnail_spritesheets: Vec<ThumbnailSpritesheet>,
|
||||||
|
pub caption_file_path: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn load_cached_analysis(url: String) -> Option<CachedAnalysis> {
|
||||||
|
let url_clone = url.clone();
|
||||||
|
let manifest = tokio::task::spawn_blocking(move || cache_manager::load_manifest(&url_clone))
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.flatten()?;
|
||||||
|
|
||||||
|
let cache_dir = cache_manager::cache_dir_for_url(&url);
|
||||||
|
|
||||||
|
// Resolve relative paths in thumbnail spritesheets to absolute paths
|
||||||
|
let spritesheets: Vec<ThumbnailSpritesheet> = manifest
|
||||||
|
.thumbnail_spritesheets
|
||||||
|
.into_iter()
|
||||||
|
.map(|mut sheet| {
|
||||||
|
let abs_path = cache_dir.join(&sheet.file_path);
|
||||||
|
sheet.file_path = abs_path.to_string_lossy().to_string();
|
||||||
|
sheet
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Verify thumbnail files actually exist on disk
|
||||||
|
let thumbs_valid = if spritesheets.is_empty() {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
let first_dir = std::path::Path::new(&spritesheets[0].file_path);
|
||||||
|
first_dir.is_dir()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Resolve caption path to absolute
|
||||||
|
let caption_path = manifest
|
||||||
|
.caption_file_path
|
||||||
|
.map(|rel| cache_dir.join(&rel).to_string_lossy().to_string())
|
||||||
|
.filter(|p| std::path::Path::new(p).is_file());
|
||||||
|
|
||||||
|
Some(CachedAnalysis {
|
||||||
|
waveform_tiers: manifest.waveform_tiers,
|
||||||
|
keyframe_positions: manifest.keyframe_positions,
|
||||||
|
thumbnail_spritesheets: if thumbs_valid {
|
||||||
|
spritesheets
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
|
caption_file_path: caption_path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn save_analysis_to_cache(
|
||||||
|
url: String,
|
||||||
|
title: String,
|
||||||
|
duration: f64,
|
||||||
|
fps: f64,
|
||||||
|
waveform_tiers: Option<WaveformTiers>,
|
||||||
|
keyframe_positions: Vec<f64>,
|
||||||
|
thumbnail_spritesheets: Vec<ThumbnailSpritesheet>,
|
||||||
|
caption_file_path: Option<String>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let cache_dir = cache_manager::cache_dir_for_url(&url);
|
||||||
|
|
||||||
|
// Convert absolute spritesheet paths to relative
|
||||||
|
let relative_sheets: Vec<ThumbnailSpritesheet> = thumbnail_spritesheets
|
||||||
|
.into_iter()
|
||||||
|
.map(|mut sheet| {
|
||||||
|
if let Ok(rel) = std::path::Path::new(&sheet.file_path).strip_prefix(&cache_dir) {
|
||||||
|
sheet.file_path = rel.to_string_lossy().to_string();
|
||||||
|
}
|
||||||
|
sheet
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Convert absolute caption path to relative
|
||||||
|
let relative_caption = caption_file_path.and_then(|abs| {
|
||||||
|
std::path::Path::new(&abs)
|
||||||
|
.strip_prefix(&cache_dir)
|
||||||
|
.ok()
|
||||||
|
.map(|rel| rel.to_string_lossy().to_string())
|
||||||
|
});
|
||||||
|
|
||||||
|
let now = format!(
|
||||||
|
"{}",
|
||||||
|
std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_secs()
|
||||||
|
);
|
||||||
|
|
||||||
|
let manifest = CacheManifest {
|
||||||
|
version: 1,
|
||||||
|
source_url: url.clone(),
|
||||||
|
title,
|
||||||
|
duration,
|
||||||
|
fps,
|
||||||
|
created_at: now.clone(),
|
||||||
|
last_accessed_at: now,
|
||||||
|
waveform_tiers,
|
||||||
|
keyframe_positions,
|
||||||
|
thumbnail_spritesheets: relative_sheets,
|
||||||
|
caption_file_path: relative_caption,
|
||||||
|
};
|
||||||
|
|
||||||
|
tokio::task::spawn_blocking(move || cache_manager::save_manifest(&url, &manifest))
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Task failed: {e}"))?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn clear_video_cache(url: String) -> Result<(), String> {
|
||||||
|
tokio::task::spawn_blocking(move || cache_manager::clear_cache_entry(&url))
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Task failed: {e}"))?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn clear_all_cache() -> Result<(), String> {
|
||||||
|
tokio::task::spawn_blocking(cache_manager::clear_all_cache)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Task failed: {e}"))?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_cache_stats() -> Result<CacheStats, String> {
|
||||||
|
tokio::task::spawn_blocking(cache_manager::get_cache_stats)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Task failed: {e}"))?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_thumbnail_cache_dir(url: String) -> String {
|
||||||
|
let dir = cache_manager::thumbnail_cache_dir(&url);
|
||||||
|
std::fs::create_dir_all(&dir).ok();
|
||||||
|
dir.to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_caption_cache_dir(url: String) -> String {
|
||||||
|
let dir = cache_manager::caption_cache_dir(&url);
|
||||||
|
std::fs::create_dir_all(&dir).ok();
|
||||||
|
dir.to_string_lossy().to_string()
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pub mod cache;
|
||||||
pub mod dependencies;
|
pub mod dependencies;
|
||||||
pub mod export;
|
pub mod export;
|
||||||
pub mod media_analysis;
|
pub mod media_analysis;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ mod services;
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use commands::{dependencies, export, media_analysis, video};
|
use commands::{cache, dependencies, export, media_analysis, video};
|
||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
|
|
||||||
struct MediaServerPort(u16);
|
struct MediaServerPort(u16);
|
||||||
@@ -123,6 +123,13 @@ pub fn run() {
|
|||||||
media_analysis::check_embedded_subtitles,
|
media_analysis::check_embedded_subtitles,
|
||||||
media_analysis::list_system_fonts,
|
media_analysis::list_system_fonts,
|
||||||
export::export_clips,
|
export::export_clips,
|
||||||
|
cache::load_cached_analysis,
|
||||||
|
cache::save_analysis_to_cache,
|
||||||
|
cache::clear_video_cache,
|
||||||
|
cache::clear_all_cache,
|
||||||
|
cache::get_cache_stats,
|
||||||
|
cache::get_thumbnail_cache_dir,
|
||||||
|
cache::get_caption_cache_dir,
|
||||||
get_media_server_port,
|
get_media_server_port,
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
|
|||||||
Reference in New Issue
Block a user