fix preview
This commit is contained in:
@@ -1,32 +1,92 @@
|
||||
use std::fs;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::env;
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[derive(serde::Serialize, Clone)]
|
||||
struct ImageItem {
|
||||
path: String,
|
||||
name: String,
|
||||
thumbnail: String,
|
||||
}
|
||||
|
||||
fn get_cache_dir() -> std::path::PathBuf {
|
||||
let mut path = env::temp_dir();
|
||||
path.push("watermark-wizard-thumbs");
|
||||
if !path.exists() {
|
||||
let _ = fs::create_dir_all(&path);
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
fn generate_thumbnail(original_path: &Path) -> Option<String> {
|
||||
let cache_dir = get_cache_dir();
|
||||
|
||||
// Generate simple hash for filename
|
||||
let mut hasher = DefaultHasher::new();
|
||||
original_path.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
let file_name = format!("{}.jpg", hash);
|
||||
let thumb_path = cache_dir.join(file_name);
|
||||
|
||||
// Return if exists
|
||||
if thumb_path.exists() {
|
||||
return Some(thumb_path.to_string_lossy().to_string());
|
||||
}
|
||||
|
||||
// Generate
|
||||
// Use image reader to be faster? open is fine.
|
||||
if let Ok(img) = image::open(original_path) {
|
||||
// Resize to height 200, preserve ratio
|
||||
let thumb = img.thumbnail(u32::MAX, 200);
|
||||
// Save as jpeg with quality 80
|
||||
let mut file = fs::File::create(&thumb_path).ok()?;
|
||||
// thumb.write_to(&mut file, image::ImageOutputFormat::Jpeg(80)).ok()?;
|
||||
// write_to might be slow due to encoding, but parallel execution helps.
|
||||
// save directly
|
||||
thumb.save_with_format(&thumb_path, image::ImageFormat::Jpeg).ok()?;
|
||||
|
||||
return Some(thumb_path.to_string_lossy().to_string());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn scan_dir(path: String) -> Result<Vec<ImageItem>, String> {
|
||||
let mut images = Vec::new();
|
||||
let dir = fs::read_dir(&path).map_err(|e| e.to_string())?;
|
||||
|
||||
for entry in dir {
|
||||
async fn scan_dir(path: String) -> Result<Vec<ImageItem>, String> {
|
||||
let entries = fs::read_dir(&path).map_err(|e| e.to_string())?;
|
||||
|
||||
// Collect valid paths first to avoid holding fs locks or iterators during parallel proc
|
||||
let mut valid_paths = Vec::new();
|
||||
for entry in entries {
|
||||
if let Ok(entry) = entry {
|
||||
let path = entry.path();
|
||||
if path.is_file() {
|
||||
if let Some(ext) = path.extension() {
|
||||
let p = entry.path();
|
||||
if p.is_file() {
|
||||
if let Some(ext) = p.extension() {
|
||||
let ext_str = ext.to_string_lossy().to_lowercase();
|
||||
if ["png", "jpg", "jpeg", "webp"].contains(&ext_str.as_str()) {
|
||||
images.push(ImageItem {
|
||||
path: path.to_string_lossy().to_string(),
|
||||
name: path.file_name().unwrap_or_default().to_string_lossy().to_string(),
|
||||
});
|
||||
valid_paths.push(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process in parallel
|
||||
let mut images: Vec<ImageItem> = valid_paths.par_iter().filter_map(|path| {
|
||||
let name = path.file_name()?.to_string_lossy().to_string();
|
||||
let path_str = path.to_string_lossy().to_string();
|
||||
|
||||
// Generate thumbnail
|
||||
let thumb = generate_thumbnail(path).unwrap_or_else(|| path_str.clone());
|
||||
|
||||
Some(ImageItem {
|
||||
path: path_str,
|
||||
name,
|
||||
thumbnail: thumb,
|
||||
})
|
||||
}).collect();
|
||||
|
||||
// Sort by name
|
||||
images.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
Ok(images)
|
||||
|
||||
Reference in New Issue
Block a user