review page, first
This commit is contained in:
@@ -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<String, Vec<String>>);
|
||||
|
||||
// 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<AppConfig, String> {
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
// --- History Commands ---
|
||||
|
||||
fn get_history_path(app_handle: &tauri::AppHandle) -> Result<std::path::PathBuf, String> {
|
||||
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<HistoryMap, String> {
|
||||
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<HistoryMap, String> {
|
||||
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<HistoryMap, String> {
|
||||
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<String>, prefix: String, base_name: String) -> Result<String, String> {
|
||||
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<std::path::PathBuf, Vec<std::path::PathBuf>> = 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::<u32>() {
|
||||
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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user