polishing

This commit is contained in:
2026-05-06 10:53:27 -06:00
parent 09ebcbc9ec
commit fd6697b48e
18 changed files with 889 additions and 145 deletions

View File

@ -11,6 +11,7 @@ mod ai_provider;
mod caption_generator;
mod background_removal;
mod licensing;
mod models;
#[tauri::command]
fn get_projects_directory() -> Result<String, String> {
@ -207,6 +208,22 @@ async fn save_captions(content: String, output_path: String) -> Result<String, S
.map_err(|e| format!("Task error: {:?}", e))?
}
/// List downloaded models (Whisper + LLM) with sizes.
#[tauri::command]
fn list_models(app_handle: tauri::AppHandle) -> Result<Vec<models::ModelInfo>, String> {
let data_dir = app_handle
.path()
.app_data_dir()
.map_err(|e| format!("No app data directory: {e}"))?;
Ok(models::list_models(&data_dir))
}
/// Delete a downloaded model by path.
#[tauri::command]
fn delete_model(path: String) -> Result<(), String> {
models::delete_model(&path)
}
/// Get the combined app status: licensed, trial, or expired.
#[tauri::command]
fn get_app_status(app_handle: tauri::AppHandle) -> Result<licensing::AppStatus, String> {
@ -358,6 +375,8 @@ pub fn run() {
deactivate_license,
start_trial,
has_license_feature,
list_models,
delete_model,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");

141
src-tauri/src/models.rs Normal file
View File

@ -0,0 +1,141 @@
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Serialize, Deserialize)]
pub struct ModelInfo {
pub name: String,
pub path: String,
pub size_bytes: u64,
pub kind: String,
}
fn huggingface_cache_dir() -> PathBuf {
// Follows huggingface_hub default cache location
if let Ok(custom) = std::env::var("HF_HOME") {
return PathBuf::from(custom).join("hub");
}
if let Ok(custom) = std::env::var("XDG_CACHE_HOME") {
return PathBuf::from(custom).join("huggingface").join("hub");
}
dirs::home_dir()
.unwrap_or_default()
.join(".cache")
.join("huggingface")
.join("hub")
}
fn scan_whisper_models() -> Vec<ModelInfo> {
let cache_dir = huggingface_cache_dir();
if !cache_dir.exists() {
return vec![];
}
let mut models = vec![];
let pattern = "models--Systran--faster-whisper-";
let Ok(entries) = std::fs::read_dir(&cache_dir) else {
return vec![];
};
for entry in entries.flatten() {
let name = entry.file_name();
let name_str = name.to_string_lossy();
if !name_str.starts_with(pattern) {
continue;
}
let model_name = name_str
.strip_prefix(pattern)
.unwrap_or(&name_str)
.to_string();
// The actual model files are in snapshots/ subdirectory
let snapshots_dir = entry.path().join("snapshots");
let mut total_size = 0u64;
if let Ok(snap_entries) = std::fs::read_dir(&snapshots_dir) {
for snap in snap_entries.flatten() {
total_size += dir_size(&snap.path());
}
}
// If no snapshots dir, try blobs/
if total_size == 0 {
let blobs_dir = entry.path().join("blobs");
if blobs_dir.exists() {
total_size = dir_size(&blobs_dir);
}
}
models.push(ModelInfo {
name: model_name,
path: entry.path().to_string_lossy().to_string(),
size_bytes: total_size,
kind: "whisper".to_string(),
});
}
models
}
fn scan_llm_models(app_data_dir: &PathBuf) -> Vec<ModelInfo> {
let models_dir = app_data_dir.join("models");
if !models_dir.exists() {
return vec![];
}
let mut models = vec![];
let Ok(entries) = std::fs::read_dir(&models_dir) else {
return vec![];
};
for entry in entries.flatten() {
let path = entry.path();
if path.extension().map(|e| e == "gguf").unwrap_or(false) {
let meta = std::fs::metadata(&path).ok();
models.push(ModelInfo {
name: entry.file_name().to_string_lossy().to_string(),
path: path.to_string_lossy().to_string(),
size_bytes: meta.map(|m| m.len()).unwrap_or(0),
kind: "llm".to_string(),
});
}
}
models
}
fn dir_size(path: &std::path::Path) -> u64 {
let mut total = 0u64;
if let Ok(entries) = std::fs::read_dir(path) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
total += dir_size(&path);
} else if let Ok(meta) = std::fs::metadata(&path) {
total += meta.len();
}
}
}
total
}
pub fn list_models(app_data_dir: &PathBuf) -> Vec<ModelInfo> {
let mut models = scan_whisper_models();
models.extend(scan_llm_models(app_data_dir));
models
}
pub fn delete_model(path: &str) -> Result<(), String> {
let path = std::path::Path::new(path);
if !path.exists() {
return Err("Model path not found".to_string());
}
if path.is_dir() {
std::fs::remove_dir_all(path)
.map_err(|e| format!("Failed to delete model: {e}"))?;
} else {
std::fs::remove_file(path)
.map_err(|e| format!("Failed to delete model: {e}"))?;
}
Ok(())
}