178 lines
5.9 KiB
Rust
178 lines
5.9 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
|
|
fn default_true() -> bool { true }
|
|
fn default_replay_duration() -> u32 { 60 }
|
|
fn default_replay_bitrate() -> u32 { 18000 }
|
|
fn default_record_bitrate() -> u32 { 20000 }
|
|
fn default_fps() -> u32 { 60 }
|
|
fn default_video_codec() -> String { "h264".to_string() }
|
|
fn default_audio_device() -> String { "default".to_string() }
|
|
fn default_audio_mode() -> String { "system".to_string() }
|
|
fn default_lang() -> String { "en".to_string() }
|
|
fn default_theme() -> String { "dark".to_string() }
|
|
fn default_save_hotkey() -> String { "Ctrl+Shift+R".to_string() }
|
|
fn default_menu_hotkey() -> String { "Alt+Z".to_string() }
|
|
fn default_record_hotkey() -> String { "Ctrl+Shift+F9".to_string() }
|
|
fn default_cursor_hotkey() -> String { "Ctrl+Shift+F10".to_string() }
|
|
fn default_mic_volume() -> f32 { 0.60 }
|
|
fn default_system_volume() -> f32 { 1.00 }
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct VrecConfig {
|
|
#[serde(default = "default_true")]
|
|
pub replay_enabled: bool,
|
|
#[serde(default = "default_replay_duration")]
|
|
pub replay_duration_sec: u32,
|
|
#[serde(default = "default_replay_bitrate")]
|
|
pub replay_bitrate_kbps: u32,
|
|
|
|
#[serde(default)]
|
|
pub record_enabled: bool,
|
|
#[serde(default = "default_record_bitrate")]
|
|
pub record_bitrate_kbps: u32,
|
|
#[serde(default = "default_fps")]
|
|
pub fps: u32,
|
|
#[serde(default = "default_video_codec")]
|
|
pub video_codec: String,
|
|
#[serde(default = "VrecConfig::default_output_directory")]
|
|
pub output_directory: String,
|
|
#[serde(default = "default_true")]
|
|
pub show_cursor: bool,
|
|
#[serde(default = "default_audio_device")]
|
|
pub audio_device: String,
|
|
#[serde(default = "default_audio_mode")]
|
|
pub audio_mode: String,
|
|
#[serde(default = "default_mic_volume")]
|
|
pub mic_volume: f32,
|
|
#[serde(default = "default_system_volume")]
|
|
pub system_volume: f32,
|
|
|
|
#[serde(default)]
|
|
pub autostart: bool,
|
|
#[serde(default = "default_lang")]
|
|
pub language: String,
|
|
#[serde(default = "default_theme")]
|
|
pub ui_color_theme: String,
|
|
#[serde(default = "default_save_hotkey")]
|
|
pub save_hotkey: String,
|
|
#[serde(default = "default_menu_hotkey")]
|
|
pub menu_hotkey: String,
|
|
#[serde(default = "default_record_hotkey")]
|
|
pub record_hotkey: String,
|
|
#[serde(default = "default_cursor_hotkey")]
|
|
pub cursor_hotkey: String,
|
|
}
|
|
|
|
impl Default for VrecConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
replay_enabled: true,
|
|
replay_duration_sec: 60,
|
|
replay_bitrate_kbps: 18000,
|
|
|
|
record_enabled: false,
|
|
record_bitrate_kbps: 20000,
|
|
fps: 60,
|
|
video_codec: "h264".to_string(),
|
|
output_directory: Self::default_output_directory(),
|
|
show_cursor: true,
|
|
audio_device: "default".to_string(),
|
|
audio_mode: "system".to_string(),
|
|
mic_volume: default_mic_volume(),
|
|
system_volume: default_system_volume(),
|
|
|
|
autostart: false,
|
|
language: "en".to_string(),
|
|
ui_color_theme: "dark".to_string(),
|
|
save_hotkey: "Ctrl+Shift+R".to_string(),
|
|
menu_hotkey: "Alt+Z".to_string(),
|
|
record_hotkey: "Ctrl+Shift+F9".to_string(),
|
|
cursor_hotkey: "Ctrl+Shift+F10".to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl VrecConfig {
|
|
pub fn default_output_directory() -> String {
|
|
let mut p = dirs::video_dir().unwrap_or_else(|| PathBuf::from("."));
|
|
p.push("vrec");
|
|
p.to_string_lossy().to_string()
|
|
}
|
|
|
|
pub fn config_path() -> PathBuf {
|
|
let mut path = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
|
|
path.push("vrec");
|
|
path.push("config.json");
|
|
path
|
|
}
|
|
|
|
pub fn expand_tilde(path: &str) -> PathBuf {
|
|
let trimmed = path.trim();
|
|
if let Some(sub) = trimmed.strip_prefix("~/") {
|
|
if let Some(home) = dirs::home_dir() {
|
|
return home.join(sub);
|
|
}
|
|
} else if trimmed == "~"
|
|
&& let Some(home) = dirs::home_dir() {
|
|
return home;
|
|
}
|
|
PathBuf::from(trimmed)
|
|
}
|
|
|
|
pub fn resolve_save_path(&self, filename: &str) -> String {
|
|
let mut p = Self::expand_tilde(&self.output_directory);
|
|
let _ = fs::create_dir_all(&p);
|
|
p.push(filename);
|
|
p.to_string_lossy().to_string()
|
|
}
|
|
|
|
pub fn load() -> Self {
|
|
let path = Self::config_path();
|
|
if let Ok(content) = fs::read_to_string(&path)
|
|
&& let Ok(cfg) = serde_json::from_str(&content) {
|
|
return cfg;
|
|
}
|
|
let default_cfg = Self::default();
|
|
let _ = default_cfg.save();
|
|
default_cfg
|
|
}
|
|
|
|
pub fn save(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
let path = Self::config_path();
|
|
if let Some(parent) = path.parent() {
|
|
fs::create_dir_all(parent)?;
|
|
}
|
|
let json = serde_json::to_string_pretty(self)?;
|
|
fs::write(path, json)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn notify_daemon_reload() {
|
|
let _ = crate::ipc::send_command(crate::ipc::Command::ReloadConfig);
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_config_defaults_and_json() {
|
|
let cfg = VrecConfig::default();
|
|
assert_eq!(cfg.replay_duration_sec, 60);
|
|
assert_eq!(cfg.replay_bitrate_kbps, 18000);
|
|
assert_eq!(cfg.fps, 60);
|
|
assert_eq!(cfg.video_codec, "h264");
|
|
assert_eq!(cfg.audio_mode, "system");
|
|
assert!(cfg.show_cursor);
|
|
let json = serde_json::to_string(&cfg).unwrap();
|
|
let parsed: VrecConfig = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(parsed.replay_duration_sec, 60);
|
|
assert_eq!(parsed.audio_mode, "system");
|
|
assert!(parsed.show_cursor);
|
|
assert!((parsed.mic_volume - 0.60).abs() < 1e-4);
|
|
assert!((parsed.system_volume - 1.00).abs() < 1e-4);
|
|
}
|
|
}
|