From cab53abcbeb3249bda1900e50aaf64f8774ed9f9 Mon Sep 17 00:00:00 2001 From: Priler Date: Mon, 5 Jan 2026 03:38:04 +0500 Subject: [PATCH] vosk model selection in GUI --- crates/jarvis-app/Cargo.toml | 2 +- crates/jarvis-core/src/config.rs | 11 +- crates/jarvis-core/src/db/structs.rs | 4 + crates/jarvis-core/src/lib.rs | 12 +- crates/jarvis-core/src/stt.rs | 4 + crates/jarvis-core/src/stt/vosk.rs | 48 +++++++- crates/jarvis-core/src/vosk_models.rs | 113 ++++++++++++++++++ crates/jarvis-gui/src/main.rs | 3 + crates/jarvis-gui/src/tauri_commands.rs | 4 + crates/jarvis-gui/src/tauri_commands/db.rs | 4 + crates/jarvis-gui/src/tauri_commands/stt.rs | 21 ++++ frontend/src/routes/settings/index.svelte | 37 +++++- ....timestamp-1767564602601-0b6e8552efaa9.mjs | 40 +++++++ post_build.py | 60 ++++++---- 14 files changed, 323 insertions(+), 40 deletions(-) create mode 100644 crates/jarvis-core/src/vosk_models.rs create mode 100644 crates/jarvis-gui/src/tauri_commands/stt.rs create mode 100644 frontend/vite.config.ts.timestamp-1767564602601-0b6e8552efaa9.mjs diff --git a/crates/jarvis-app/Cargo.toml b/crates/jarvis-app/Cargo.toml index fd23a70..78353b5 100644 --- a/crates/jarvis-app/Cargo.toml +++ b/crates/jarvis-app/Cargo.toml @@ -7,7 +7,7 @@ repository.workspace = true edition.workspace = true [dependencies] -jarvis-core = { path = "../jarvis-core" } +jarvis-core = { path = "../jarvis-core", features = ["intent"] } once_cell.workspace = true log.workspace = true simple-log = "2.4" diff --git a/crates/jarvis-core/src/config.rs b/crates/jarvis-core/src/config.rs index c39c5c2..f031711 100644 --- a/crates/jarvis-core/src/config.rs +++ b/crates/jarvis-core/src/config.rs @@ -64,7 +64,7 @@ pub fn init_dirs() -> Result<(), String> { */ pub const DEFAULT_AUDIO_TYPE: AudioType = AudioType::Kira; pub const DEFAULT_RECORDER_TYPE: RecorderType = RecorderType::PvRecorder; -pub const DEFAULT_WAKE_WORD_ENGINE: WakeWordEngine = WakeWordEngine::Rustpotter; +pub const DEFAULT_WAKE_WORD_ENGINE: WakeWordEngine = WakeWordEngine::Vosk; pub const DEFAULT_INTENT_RECOGNITION_ENGINE: IntentRecognitionEngine = IntentRecognitionEngine::IntentClassifier; pub const DEFAULT_SPEECH_TO_TEXT_ENGINE: SpeechToTextEngine = SpeechToTextEngine::Vosk; @@ -121,19 +121,20 @@ pub const RUSTPOTTER_DEFAULT_CONFIG: Lazy = Lazy::new(|| { }); // PICOVOICE -pub const COMMANDS_PATH: &str = "commands/"; -pub const KEYWORDS_PATH: &str = "picovoice/keywords/"; +pub const COMMANDS_PATH: &str = "resources/commands/"; +pub const KEYWORDS_PATH: &str = "resources/picovoice/keywords/"; pub const DEFAULT_KEYWORD: &str = "jarvis_windows.ppn"; pub const DEFAULT_SENSITIVITY: f32 = 1.0; // VOSK // pub const VOSK_MODEL_PATH: &str = const_concat!(PUBLIC_PATH, "/vosk/model_small"); +pub const VOSK_MODELS_PATH: &str = "resources/vosk"; +pub const VOSK_MODEL_PATH: &str = "resources/vosk/model_small"; pub const VOSK_FETCH_PHRASE: &str = "джарвис"; -pub const VOSK_MODEL_PATH: &str = "vosk/model_small"; pub const VOSK_MIN_RATIO: f64 = 70.0; // IRE (intents recognition) -pub const INTENT_CLASSIFIER_MIN_CONFIDENCE: f64 = 0.5; +pub const INTENT_CLASSIFIER_MIN_CONFIDENCE: f64 = 0.75; // ETC pub const CMD_RATIO_THRESHOLD: f64 = 65f64; diff --git a/crates/jarvis-core/src/db/structs.rs b/crates/jarvis-core/src/db/structs.rs index 8a5f47c..74b980a 100644 --- a/crates/jarvis-core/src/db/structs.rs +++ b/crates/jarvis-core/src/db/structs.rs @@ -14,6 +14,8 @@ pub struct Settings { pub intent_recognition_engine: IntentRecognitionEngine, pub speech_to_text_engine: SpeechToTextEngine, + pub vosk_model: String, + pub api_keys: ApiKeys, } @@ -27,6 +29,8 @@ impl Default for Settings { intent_recognition_engine: config::DEFAULT_INTENT_RECOGNITION_ENGINE, speech_to_text_engine: config::DEFAULT_SPEECH_TO_TEXT_ENGINE, + vosk_model: String::from(""), // auto detect first available + api_keys: ApiKeys { picovoice: String::from(""), openai: String::from(""), diff --git a/crates/jarvis-core/src/lib.rs b/crates/jarvis-core/src/lib.rs index a72a1db..bc76285 100644 --- a/crates/jarvis-core/src/lib.rs +++ b/crates/jarvis-core/src/lib.rs @@ -23,9 +23,17 @@ pub mod stt; #[cfg(feature = "intent")] pub mod intent; +pub mod vosk_models; + // shared statics -pub static APP_DIR: Lazy = Lazy::new(|| std::env::current_dir().unwrap()); -pub static SOUND_DIR: Lazy = Lazy::new(|| APP_DIR.clone().join("sound")); +// pub static APP_DIR: Lazy = Lazy::new(|| std::env::current_dir().unwrap()); +pub static APP_DIR: Lazy = Lazy::new(|| { + std::env::current_exe() + .ok() + .and_then(|p| p.parent().map(|p| p.to_path_buf())) + .unwrap_or_else(|| std::env::current_dir().unwrap()) +}); +pub static SOUND_DIR: Lazy = Lazy::new(|| APP_DIR.clone().join("resources/sound")); pub static APP_DIRS: OnceCell = OnceCell::new(); pub static APP_CONFIG_DIR: OnceCell = OnceCell::new(); pub static APP_LOG_DIR: OnceCell = OnceCell::new(); diff --git a/crates/jarvis-core/src/stt.rs b/crates/jarvis-core/src/stt.rs index 3fdd10c..3e91af5 100644 --- a/crates/jarvis-core/src/stt.rs +++ b/crates/jarvis-core/src/stt.rs @@ -6,6 +6,9 @@ use once_cell::sync::OnceCell; use crate::config::structs::SpeechToTextEngine; +use crate::vosk_models; +// use vosk_models::{scan_vosk_models, get_model_path, VoskModelInfo}; + static STT_TYPE: OnceCell = OnceCell::new(); pub fn init() -> Result<(), ()> { @@ -30,6 +33,7 @@ pub fn init() -> Result<(), ()> { Ok(()) } + pub fn recognize(data: &[i16], partial: bool) -> Option { match STT_TYPE.get().unwrap() { SpeechToTextEngine::Vosk => vosk::recognize(data, partial), diff --git a/crates/jarvis-core/src/stt/vosk.rs b/crates/jarvis-core/src/stt/vosk.rs index c716e03..4ee3e90 100644 --- a/crates/jarvis-core/src/stt/vosk.rs +++ b/crates/jarvis-core/src/stt/vosk.rs @@ -3,18 +3,26 @@ use vosk::{DecodingState, Model, Recognizer}; use std::sync::Mutex; -use crate::config::VOSK_MODEL_PATH; +// use crate::config::VOSK_MODEL_PATH; +use crate::config; +use crate::stt::vosk_models; +use crate::DB; static MODEL: OnceCell = OnceCell::new(); static RECOGNIZER: OnceCell> = OnceCell::new(); -pub fn init_vosk() { +pub fn init_vosk() -> Result<(), String> { if RECOGNIZER.get().is_some() { - return; + return Ok(()); } // already initialized - let model = Model::new(VOSK_MODEL_PATH).unwrap(); - let mut recognizer = Recognizer::new(&model, 16000.0).unwrap(); + let model_path = get_configured_model_path()?; + + let model = Model::new(model_path.to_str().unwrap()) + .ok_or_else(|| format!("Failed to load Vosk model from: {}", model_path.display()))?; + + let mut recognizer = Recognizer::new(&model, 16000.0) + .ok_or("Failed to create Vosk recognizer")?; recognizer.set_max_alternatives(10); recognizer.set_words(true); @@ -22,6 +30,8 @@ pub fn init_vosk() { MODEL.set(model); RECOGNIZER.set(Mutex::new(recognizer)); + + Ok(()) } pub fn recognize(data: &[i16], include_partial: bool) -> Option { @@ -73,6 +83,34 @@ pub fn recognize(data: &[i16], include_partial: bool) -> Option { } } +fn get_configured_model_path() -> Result { + // try to get from settings + if let Some(db) = DB.get() { + let settings = db.read(); + if !settings.vosk_model.is_empty() { + if let Some(path) = vosk_models::get_model_path(&settings.vosk_model) { + return Ok(path); + } + warn!("Configured Vosk model '{}' not found, falling back to auto-detect", settings.vosk_model); + } + } + + // auto-detect: use first available model + let available = vosk_models::scan_vosk_models(); + if let Some(first) = available.first() { + info!("Auto-detected Vosk model: {}", first.name); + return Ok(first.path.clone()); + } + + // fallback to legacy path + let legacy_path = std::path::Path::new(config::VOSK_MODEL_PATH); + if legacy_path.exists() { + return Ok(legacy_path.to_path_buf()); + } + + Err("No Vosk models found".into()) +} + // pub fn stereo_to_mono(input_data: &[i16]) -> Vec { // let mut result = Vec::with_capacity(input_data.len() / 2); // result.extend( diff --git a/crates/jarvis-core/src/vosk_models.rs b/crates/jarvis-core/src/vosk_models.rs new file mode 100644 index 0000000..e80408f --- /dev/null +++ b/crates/jarvis-core/src/vosk_models.rs @@ -0,0 +1,113 @@ +use std::fs; +use std::path::{Path, PathBuf}; + +use crate::{APP_DIR, config}; + +#[derive(Debug, Clone)] +pub struct VoskModelInfo { + pub name: String, // folder name: "vosk-model-small-ru-0.22" + pub path: PathBuf, // full path + pub language: String, // extracted from name: "ru" + pub size: String, // "small", "large", etc. +} + +// Scan for available Vosk models +pub fn scan_vosk_models() -> Vec { + let models_dir = { + APP_DIR.join(config::VOSK_MODELS_PATH) + }; + let mut models = Vec::new(); + + info!("TESTTTTTTTTTTTTT: {}", models_dir.display()); + + if !models_dir.exists() { + warn!("Vosk models directory not found: {}", models_dir.display()); + return models; + } + + let entries = match fs::read_dir(models_dir) { + Ok(e) => e, + Err(e) => { + warn!("Failed to read vosk models directory: {}", e); + return models; + } + }; + + for entry in entries { + let entry = match entry { + Ok(e) => e, + Err(_) => continue, + }; + + let path = entry.path(); + + // must be a directory + if !path.is_dir() { + continue; + } + + // check if it looks like a vosk model (has am/conf/graph folders or similar) + if !is_vosk_model(&path) { + continue; + } + + let name = path.file_name() + .and_then(|n| n.to_str()) + .unwrap_or("") + .to_string(); + + let (language, size) = parse_model_name(&name); + + models.push(VoskModelInfo { + name, + path, + language, + size, + }); + } + + models.sort_by(|a, b| a.name.cmp(&b.name)); + models +} + +// Check if directory looks like a Vosk model +fn is_vosk_model(path: &Path) -> bool { + // vosk models typically have these subdirectories + path.join("am").exists() || + path.join("conf").exists() || + path.join("graph").exists() || + path.join("ivector").exists() +} + +// Extract language and size from model name +// e.g., "vosk-model-small-ru-0.22" -> ("ru", "small") +fn parse_model_name(name: &str) -> (String, String) { + let parts: Vec<&str> = name.split('-').collect(); + + let mut language = String::from("unknown"); + let mut size = String::from("unknown"); + + // look for common size indicators + for part in &parts { + match *part { + "small" | "big" | "large" | "lgraph" => size = part.to_string(), + // language codes are usually 2 letters + s if s.len() == 2 && s.chars().all(|c| c.is_alphabetic()) => { + language = s.to_string(); + } + _ => {} + } + } + + (language, size) +} + +// Get model path by name +pub fn get_model_path(model_name: &str) -> Option { + let path = Path::new(config::VOSK_MODELS_PATH).join(model_name); + if path.exists() && path.is_dir() { + Some(path) + } else { + None + } +} \ No newline at end of file diff --git a/crates/jarvis-gui/src/main.rs b/crates/jarvis-gui/src/main.rs index 2a566e9..a4d4df4 100644 --- a/crates/jarvis-gui/src/main.rs +++ b/crates/jarvis-gui/src/main.rs @@ -61,6 +61,9 @@ fn main() { tauri_commands::get_peak_ram_usage, tauri_commands::get_cpu_temp, tauri_commands::get_cpu_usage, + + // vosk + tauri_commands::list_vosk_models, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/crates/jarvis-gui/src/tauri_commands.rs b/crates/jarvis-gui/src/tauri_commands.rs index 8c06b2e..8afc8f8 100644 --- a/crates/jarvis-gui/src/tauri_commands.rs +++ b/crates/jarvis-gui/src/tauri_commands.rs @@ -22,3 +22,7 @@ pub use fs::*; // import SYS commands mod sys; pub use sys::*; + +// import STT commands +mod stt; +pub use stt::*; \ No newline at end of file diff --git a/crates/jarvis-gui/src/tauri_commands/db.rs b/crates/jarvis-gui/src/tauri_commands/db.rs index a3d2c2b..80aa9f0 100644 --- a/crates/jarvis-gui/src/tauri_commands/db.rs +++ b/crates/jarvis-gui/src/tauri_commands/db.rs @@ -10,6 +10,7 @@ pub fn db_read(state: tauri::State<'_, AppState>, key: &str) -> String { "assistant_voice" => settings.voice.clone(), "selected_wake_word_engine" => format!("{:?}", settings.wake_word_engine), "selected_intent_recognition_engine" => format!("{:?}", settings.intent_recognition_engine), + "selected_vosk_model" => settings.vosk_model.clone(), "speech_to_text_engine" => format!("{:?}", settings.speech_to_text_engine), "api_key__picovoice" => settings.api_keys.picovoice.clone(), "api_key__openai" => settings.api_keys.openai.clone(), @@ -49,6 +50,9 @@ pub fn db_write(state: tauri::State<'_, AppState>, key: &str, val: &str) -> bool _ => return false, } } + "selected_vosk_model" => { + settings.vosk_model = val.to_string(); + } "api_key__picovoice" => { settings.api_keys.picovoice = val.to_string(); } diff --git a/crates/jarvis-gui/src/tauri_commands/stt.rs b/crates/jarvis-gui/src/tauri_commands/stt.rs new file mode 100644 index 0000000..f0ae9b5 --- /dev/null +++ b/crates/jarvis-gui/src/tauri_commands/stt.rs @@ -0,0 +1,21 @@ +use jarvis_core::{vosk_models, DB}; +use serde::Serialize; + +#[derive(Serialize)] +pub struct VoskModel { + pub name: String, + pub language: String, + pub size: String, +} + +#[tauri::command] +pub fn list_vosk_models() -> Vec { + vosk_models::scan_vosk_models() + .into_iter() + .map(|m| VoskModel { + name: m.name, + language: m.language, + size: m.size, + }) + .collect() +} \ No newline at end of file diff --git a/frontend/src/routes/settings/index.svelte b/frontend/src/routes/settings/index.svelte index 0240fae..faf44f5 100644 --- a/frontend/src/routes/settings/index.svelte +++ b/frontend/src/routes/settings/index.svelte @@ -39,6 +39,7 @@ } let availableMicrophones: MicrophoneOption[] = [] + let availableVoskModels: { label: string; value: string }[] = [] let settingsSaved = false let saveButtonDisabled = false @@ -47,6 +48,7 @@ let selectedMicrophone = "" let selectedWakeWordEngine = "" let selectedIntentRecognitionEngine = "" + let selectedVoskModel = "" let apiKeyPicovoice = "" let apiKeyOpenai = "" @@ -73,6 +75,7 @@ invoke("db_write", { key: "selected_microphone", val: selectedMicrophone }), invoke("db_write", { key: "selected_wake_word_engine", val: selectedWakeWordEngine }), invoke("db_write", { key: "selected_intent_recognition_engine", val: selectedIntentRecognitionEngine }), + invoke("db_write", { key: "selected_vosk_model", val: selectedVoskModel }), invoke("db_write", { key: "api_key__picovoice", val: apiKeyPicovoice }), invoke("db_write", { key: "api_key__openai", val: apiKeyOpenai }) ]) @@ -107,11 +110,19 @@ value: String(idx) })) + // load vosk models + const voskModels = await invoke<{ name: string; language: string; size: string }[]>("list_vosk_models") + availableVoskModels = voskModels.map(m => ({ + label: `${m.name} (${m.language}, ${m.size})`, + value: m.name + })) + // load settings from db - const [mic, wakeWord, intentReco, pico, openai] = await Promise.all([ + const [mic, wakeWord, intentReco, voskModel, pico, openai] = await Promise.all([ invoke("db_read", { key: "selected_microphone" }), invoke("db_read", { key: "selected_wake_word_engine" }), invoke("db_read", { key: "selected_intent_recognition_engine" }), + invoke("db_read", { key: "selected_vosk_model" }), invoke("db_read", { key: "api_key__picovoice" }), invoke("db_read", { key: "api_key__openai" }) ]) @@ -119,6 +130,7 @@ selectedMicrophone = mic selectedWakeWordEngine = wakeWord selectedIntentRecognitionEngine = intentReco + selectedVoskModel = voskModel apiKeyPicovoice = pico apiKeyOpenai = openai } catch (err) { @@ -232,6 +244,29 @@ {/if} + + {#key availableVoskModels} + + {/key} + + {#if availableVoskModels.length === 0} + + + + Поместите модели Vosk в папку resources/vosk + + + {/if} + { + const { code, frame } = warning; + if (code === "css-unused-selector") + return; + handler(warning); + } + }), + routify(), + tsconfigPaths() + ], + clearScreen: false, + server: { + port: 1420, + strictPort: true + }, + envPrefix: ["VITE_", "TAURI_"], + build: { + target: process.env.TAURI_PLATFORM == "windows" ? "chrome105" : "safari13", + minify: !process.env.TAURI_DEBUG ? "esbuild" : false, + sourcemap: !!process.env.TAURI_DEBUG + } +}); +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFxSdXN0XFxcXGphcnZpcy1hcHBcXFxcZnJvbnRlbmRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkQ6XFxcXFJ1c3RcXFxcamFydmlzLWFwcFxcXFxmcm9udGVuZFxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRDovUnVzdC9qYXJ2aXMtYXBwL2Zyb250ZW5kL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSBcInZpdGVcIjtcclxuaW1wb3J0IHsgc3ZlbHRlIH0gZnJvbSBcIkBzdmVsdGVqcy92aXRlLXBsdWdpbi1zdmVsdGVcIjtcclxuaW1wb3J0IHN2ZWx0ZVByZXByb2Nlc3MgZnJvbSBcInN2ZWx0ZS1wcmVwcm9jZXNzXCI7XHJcbmltcG9ydCB0c2NvbmZpZ1BhdGhzIGZyb20gJ3ZpdGUtdHNjb25maWctcGF0aHMnXHJcbmltcG9ydCByb3V0aWZ5IGZyb20gJ0Byb3hpL3JvdXRpZnkvdml0ZS1wbHVnaW4nXHJcblxyXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xyXG4gIHBsdWdpbnM6IFtcclxuICAgIHN2ZWx0ZSh7XHJcbiAgICAgIHByZXByb2Nlc3M6IFtcclxuICAgICAgICBzdmVsdGVQcmVwcm9jZXNzKHtcclxuICAgICAgICAgIHR5cGVzY3JpcHQ6IHRydWUsXHJcbiAgICAgICAgfSksXHJcbiAgICAgIF0sXHJcbiAgICAgIG9ud2FybjogKHdhcm5pbmcsIGhhbmRsZXIpID0+IHtcclxuICAgICAgICBjb25zdCB7IGNvZGUsIGZyYW1lIH0gPSB3YXJuaW5nO1xyXG4gICAgICAgIGlmIChjb2RlID09PSBcImNzcy11bnVzZWQtc2VsZWN0b3JcIilcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuICAgICAgICBoYW5kbGVyKHdhcm5pbmcpO1xyXG4gICAgICB9LFxyXG4gICAgfSksXHJcbiAgICByb3V0aWZ5KCksXHJcbiAgICB0c2NvbmZpZ1BhdGhzKClcclxuICBdLFxyXG5cclxuICBjbGVhclNjcmVlbjogZmFsc2UsXHJcbiAgc2VydmVyOiB7XHJcbiAgICBwb3J0OiAxNDIwLFxyXG4gICAgc3RyaWN0UG9ydDogdHJ1ZSxcclxuICB9LFxyXG4gIGVudlByZWZpeDogW1wiVklURV9cIiwgXCJUQVVSSV9cIl0sXHJcbiAgYnVpbGQ6IHtcclxuICAgIHRhcmdldDogcHJvY2Vzcy5lbnYuVEFVUklfUExBVEZPUk0gPT0gXCJ3aW5kb3dzXCIgPyBcImNocm9tZTEwNVwiIDogXCJzYWZhcmkxM1wiLFxyXG4gICAgbWluaWZ5OiAhcHJvY2Vzcy5lbnYuVEFVUklfREVCVUcgPyBcImVzYnVpbGRcIiA6IGZhbHNlLFxyXG4gICAgc291cmNlbWFwOiAhIXByb2Nlc3MuZW52LlRBVVJJX0RFQlVHLFxyXG4gIH0sXHJcbn0pOyJdLAogICJtYXBwaW5ncyI6ICI7QUFBMlEsU0FBUyxvQkFBb0I7QUFDeFMsU0FBUyxjQUFjO0FBQ3ZCLE9BQU8sc0JBQXNCO0FBQzdCLE9BQU8sbUJBQW1CO0FBQzFCLE9BQU8sYUFBYTtBQUVwQixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixTQUFTO0FBQUEsSUFDUCxPQUFPO0FBQUEsTUFDTCxZQUFZO0FBQUEsUUFDVixpQkFBaUI7QUFBQSxVQUNmLFlBQVk7QUFBQSxRQUNkLENBQUM7QUFBQSxNQUNIO0FBQUEsTUFDQSxRQUFRLENBQUMsU0FBUyxZQUFZO0FBQzVCLGNBQU0sRUFBRSxNQUFNLE1BQU0sSUFBSTtBQUN4QixZQUFJLFNBQVM7QUFDVDtBQUVKLGdCQUFRLE9BQU87QUFBQSxNQUNqQjtBQUFBLElBQ0YsQ0FBQztBQUFBLElBQ0QsUUFBUTtBQUFBLElBQ1IsY0FBYztBQUFBLEVBQ2hCO0FBQUEsRUFFQSxhQUFhO0FBQUEsRUFDYixRQUFRO0FBQUEsSUFDTixNQUFNO0FBQUEsSUFDTixZQUFZO0FBQUEsRUFDZDtBQUFBLEVBQ0EsV0FBVyxDQUFDLFNBQVMsUUFBUTtBQUFBLEVBQzdCLE9BQU87QUFBQSxJQUNMLFFBQVEsUUFBUSxJQUFJLGtCQUFrQixZQUFZLGNBQWM7QUFBQSxJQUNoRSxRQUFRLENBQUMsUUFBUSxJQUFJLGNBQWMsWUFBWTtBQUFBLElBQy9DLFdBQVcsQ0FBQyxDQUFDLFFBQVEsSUFBSTtBQUFBLEVBQzNCO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K diff --git a/post_build.py b/post_build.py index b45a8a8..8d65dff 100644 --- a/post_build.py +++ b/post_build.py @@ -1,27 +1,26 @@ # Simple python script used to # copy some libraries to the "target" directory # after Rust build - # Note that Rust build should be run via "cargo make " command # in order to automate all the compile process - import os from pathlib import Path import shutil # some config vars +# format: (source, destination_name) SOURCE = ( - "resources/commands/", - "resources/vosk/", - "resources/lib/", - "resources/keywords/", - "resources/rustpotter/", - "resources/sound/", - "lib/windows/amd64/libgcc_s_seh-1.dll", - "lib/windows/amd64/libstdc++-6.dll", - "lib/windows/amd64/libvosk.dll", - "lib/windows/amd64/libvosk.lib", - "lib/windows/amd64/libwinpthread-1.dll" + ("resources/commands/", "resources/commands/"), + ("resources/vosk/", "resources/vosk/"), + ("resources/lib/", "lib/"), + ("resources/keywords/", "resources/keywords/"), + ("resources/rustpotter/", "resources/rustpotter/"), + ("resources/sound/", "resources/sound/"), + ("lib/windows/amd64/libgcc_s_seh-1.dll", None), + ("lib/windows/amd64/libstdc++-6.dll", None), + ("lib/windows/amd64/libvosk.dll", None), + ("lib/windows/amd64/libvosk.lib", None), + ("lib/windows/amd64/libwinpthread-1.dll", None) ) TARGET_DIRS = ( @@ -39,27 +38,36 @@ for tdir in TARGET_DIRS: continue # copy lib files - for src in SOURCE: - if os.path.isdir(ABS_PATH + src): + for entry in SOURCE: + if isinstance(entry, tuple): + src, dest_name = entry + else: + src, dest_name = entry, None + + src_path = ABS_PATH + src + + if os.path.isdir(src_path): # copy the whole directory - full_target_dir_path = os.path.join(tdir, src) - + target_name = dest_name if dest_name else os.path.basename(src.rstrip('/')) + full_target_dir_path = os.path.join(tdir, target_name) + if os.path.isdir(full_target_dir_path): - print("[-] Directory already exists, skipping: ", src) + print("[-] Directory already exists, skipping: ", src, "->", target_name) else: - shutil.copytree(ABS_PATH + src, os.path.join(tdir, src)) + shutil.copytree(src_path, full_target_dir_path) + print("[+] Directory copied: ", src, "->", target_name) - print("[+] Directory copied: ", src) - elif os.path.isfile(ABS_PATH + src): + elif os.path.isfile(src_path): # copy file - full_target_file_path = os.path.join(tdir, src) + target_name = dest_name if dest_name else os.path.basename(src) + full_target_file_path = os.path.join(tdir, target_name) + if os.path.isfile(full_target_file_path): - print("[-] File already exists, skipping: ", src) + print("[-] File already exists, skipping: ", src, "->", target_name) else: - shutil.copy(ABS_PATH + src, tdir) - print("[+] File copied: ", src) + shutil.copy(src_path, full_target_file_path) + print("[+] File copied: ", src, "->", target_name) else: print("[?] Unknown entity to copy: ", src) - print("Post compile build done.") \ No newline at end of file