Files
SR-Label-SamTool/sam-tool-tauri/src-tauri/src/settings.rs
2026-05-28 14:18:10 +05:30

107 lines
3.3 KiB
Rust

use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use tauri::{AppHandle, Manager};
#[derive(Serialize, Deserialize, Clone)]
pub struct Settings {
#[serde(default = "default_sam_url")]
pub sam_url: String,
#[serde(default)]
pub sam_enabled: bool,
/// Model id chosen from the backend's /models list (e.g.
/// "sam2.1_hiera_small"). `None` lets the backend pick its default.
#[serde(default)]
pub sam_model: Option<String>,
#[serde(default = "default_cache_capacity")]
pub cache_capacity: usize,
/// ffmpeg `-hwaccel` argument. Empty string = software decode (default).
/// Recognized: "auto", "vaapi", "cuda", "vdpau". Applied on next
/// `open_video` or worker respawn.
#[serde(default)]
pub hwaccel: String,
/// Decimation factor used by playback (advance by N frames per tick) and
/// bulk extract (take every Nth frame in range). 1 = no decimation.
#[serde(default = "default_frame_skip")]
pub frame_skip: u64,
/// Maximum height (in pixels) of the canvas backing store during preview.
/// 0 = no cap beyond native resolution. Common presets: 2160 (4K),
/// 1440 (2K), 1080, 720, 480. Smaller → less draw cost.
#[serde(default)]
pub preview_max_height: u32,
}
fn default_sam_url() -> String {
"http://localhost:9090".into()
}
fn default_cache_capacity() -> usize {
crate::decoder::DEFAULT_CACHE_CAPACITY
}
fn default_frame_skip() -> u64 {
1
}
fn normalize(mut s: Settings) -> Settings {
s.cache_capacity = s.cache_capacity.clamp(
crate::decoder::MIN_CACHE_CAPACITY,
crate::decoder::MAX_CACHE_CAPACITY,
);
if s.frame_skip < 1 {
s.frame_skip = 1;
}
s
}
impl Default for Settings {
fn default() -> Self {
Self {
sam_url: default_sam_url(),
sam_enabled: false,
sam_model: None,
cache_capacity: default_cache_capacity(),
hwaccel: String::new(),
frame_skip: default_frame_skip(),
preview_max_height: 0,
}
}
}
fn settings_path(app: &AppHandle) -> Result<PathBuf, String> {
let dir = app
.path()
.app_local_data_dir()
.map_err(|e| format!("app data dir: {e}"))?;
fs::create_dir_all(&dir).map_err(|e| format!("mkdir {}: {e}", dir.display()))?;
Ok(dir.join("settings.json"))
}
pub fn load(app: &AppHandle) -> Result<Settings, String> {
let path = settings_path(app)?;
if !path.exists() {
return Ok(Settings::default());
}
let text = fs::read_to_string(&path).map_err(|e| format!("read {}: {e}", path.display()))?;
serde_json::from_str(&text)
.map(normalize)
.map_err(|e| format!("parse settings: {e}"))
}
pub fn save(app: &AppHandle, s: &Settings) -> Result<(), String> {
let path = settings_path(app)?;
let normalized = normalize(s.clone());
let text = serde_json::to_string_pretty(&normalized).map_err(|e| format!("serialize: {e}"))?;
fs::write(&path, text).map_err(|e| format!("write {}: {e}", path.display()))
}
#[tauri::command]
pub fn get_settings(app: AppHandle) -> Result<Settings, String> {
load(&app)
}
#[tauri::command]
pub fn save_settings(app: AppHandle, settings: Settings) -> Result<(), String> {
save(&app, &settings)
}