review page, first

This commit is contained in:
Julian Freeman
2025-12-07 19:41:25 -04:00
parent b9a41342a5
commit 4fab0b813f
3 changed files with 489 additions and 33 deletions

View File

@@ -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(&current_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");
}
}