diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 57d3784..0e9b175 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -3,6 +3,9 @@ use fs_extra::dir::CopyOptions; use tauri::Manager; use serde::{Deserialize, Serialize}; use std::fs; +use std::collections::HashMap; +use std::collections::HashSet; +use std::io; // Define the configuration structure #[derive(Serialize, Deserialize, Default, Debug)] @@ -11,6 +14,10 @@ pub struct AppConfig { pub template_dir: String, } +// History Structure +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +pub struct HistoryMap(HashMap>); + // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ #[tauri::command] fn greet(name: &str) -> String { @@ -74,13 +81,178 @@ fn load_config(app_handle: tauri::AppHandle) -> Result { Ok(config) } +// --- History Commands --- + +fn get_history_path(app_handle: &tauri::AppHandle) -> Result { + let config_dir = app_handle.path().app_config_dir().map_err(|e| e.to_string())?; + if !config_dir.exists() { + fs::create_dir_all(&config_dir).map_err(|e| e.to_string())?; + } + Ok(config_dir.join("history.json")) +} + +#[tauri::command] +fn load_history(app_handle: tauri::AppHandle) -> Result { + let path = get_history_path(&app_handle)?; + if !path.exists() { + return Ok(HistoryMap::default()); + } + let json = fs::read_to_string(path).map_err(|e| e.to_string())?; + let history: HistoryMap = serde_json::from_str(&json).map_err(|e| e.to_string())?; + Ok(history) +} + +#[tauri::command] +fn save_history_item(app_handle: tauri::AppHandle, key: String, value: String) -> Result { + let path = get_history_path(&app_handle)?; + + let mut history = if path.exists() { + let json = fs::read_to_string(&path).map_err(|e| e.to_string())?; + serde_json::from_str(&json).unwrap_or_default() + } else { + HistoryMap::default() + }; + + let list = history.0.entry(key).or_insert_with(Vec::new); + if !list.contains(&value) { + list.push(value); + } + + let json = serde_json::to_string_pretty(&history).map_err(|e| e.to_string())?; + fs::write(path, json).map_err(|e| e.to_string())?; + + Ok(history) +} + +#[tauri::command] +fn remove_history_item(app_handle: tauri::AppHandle, key: String, value: String) -> Result { + let path = get_history_path(&app_handle)?; + + if !path.exists() { + return Ok(HistoryMap::default()); + } + + let json = fs::read_to_string(&path).map_err(|e| e.to_string())?; + let mut history: HistoryMap = serde_json::from_str(&json).map_err(|e| e.to_string())?; + + if let Some(list) = history.0.get_mut(&key) { + list.retain(|x| x != &value); + if list.is_empty() { + history.0.remove(&key); + } + } + + let json = serde_json::to_string_pretty(&history).map_err(|e| e.to_string())?; + fs::write(path, json).map_err(|e| e.to_string())?; + + Ok(history) +} + +#[tauri::command] +fn check_dir_exists(path: String) -> bool { + Path::new(&path).exists() +} + +// --- Renaming Logic --- + +#[tauri::command] +fn rename_videos(files: Vec, prefix: String, base_name: String) -> Result { + if files.is_empty() { + return Err("No files provided".to_string()); + } + + // 1. Group files by parent directory to ensure index uniqueness per directory + let mut files_by_dir: HashMap> = HashMap::new(); + + for file_str in files { + let path = std::path::PathBuf::from(file_str); + if path.exists() && path.is_file() { + if let Some(parent) = path.parent() { + files_by_dir.entry(parent.to_path_buf()).or_default().push(path); + } + } + } + + let mut renamed_count = 0; + + // 2. Process each directory + for (dir, file_list) in files_by_dir { + // Find existing indices + let mut occupied_indices = HashSet::new(); + + let read_dir = fs::read_dir(&dir).map_err(|e| e.to_string())?; + for entry in read_dir { + let entry = entry.map_err(|e| e.to_string())?; + let path = entry.path(); + if path.is_file() { + if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) { + // Check if file matches Pattern: Prefix + base_name + XX + .ext + // e.g., "AI-20231207-" + "Text" + "01" + ".mp4" + let name_without_ext = Path::new(file_name).file_stem().and_then(|s| s.to_str()).unwrap_or(""); + + let prefix_base = format!("{}{}", prefix, base_name); + if name_without_ext.starts_with(&prefix_base) { + let suffix = &name_without_ext[prefix_base.len()..]; + // suffix should be digits (e.g., "01", "02") + if let Ok(index) = suffix.parse::() { + occupied_indices.insert(index); + } + } + } + } + } + + // Rename files in this directory + let mut current_index = 1; + for file_path in file_list { + // Find next available index + while occupied_indices.contains(¤t_index) { + current_index += 1; + } + + let ext = file_path.extension().and_then(|e| e.to_str()).unwrap_or(""); + let new_name = if ext.is_empty() { + format!("{}{}{:02}", prefix, base_name, current_index) + } else { + format!("{}{}{:02}.{}", prefix, base_name, current_index, ext) + }; + + let new_path = dir.join(new_name); + + // Should not happen due to index check, but safety first + if !new_path.exists() { + fs::rename(&file_path, &new_path).map_err(|e| e.to_string())?; + occupied_indices.insert(current_index); + renamed_count += 1; + } else { + // If it exists, skip to next index (rare race condition or manually named weirdly) + current_index += 1; + // Retry logic could go here, but for now let's just try next loop or fail this file + // For robustness, let's just skip this file to avoid overwrite + } + } + } + + Ok(format!("Successfully renamed {} files.", renamed_count)) +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_dialog::init()) - .invoke_handler(tauri::generate_handler![greet, copy_directory, save_config, load_config]) + .invoke_handler(tauri::generate_handler![ + greet, + copy_directory, + save_config, + load_config, + load_history, + save_history_item, + remove_history_item, + check_dir_exists, + rename_videos + ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); -} +} \ No newline at end of file diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e125557..21d3eb2 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -13,7 +13,7 @@ "windows": [ { "title": "review-videos", - "width": 1050, + "width": 1200, "height": 840 } ], diff --git a/src/App.vue b/src/App.vue index 14cad8f..339a8a6 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,9 +1,10 @@