[refactor] rename and simplify ProgressManager to FileProgress, enhance caching logic, update Hugging Face API integration, and clean up unused comments
Some checks failed
CI / build (push) Has been cancelled
Some checks failed
CI / build (push) Has been cancelled
This commit is contained in:
@@ -1,101 +1,104 @@
|
||||
use crate::prelude::*;
|
||||
use directories::ProjectDirs;
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs, path::PathBuf};
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const ENV_PREFIX: &str = "POLYSCRIBE";
|
||||
|
||||
/// Configuration for the Polyscribe application
|
||||
///
|
||||
/// Contains paths to models and plugins directories that can be customized
|
||||
/// through configuration files or environment variables.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct Config {
|
||||
/// Directory path where ML models are stored
|
||||
pub models_dir: Option<PathBuf>,
|
||||
/// Directory path where plugins are stored
|
||||
pub plugins_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
// Default is derived
|
||||
|
||||
/// Service for managing Polyscribe configuration
|
||||
///
|
||||
/// Provides functionality to load, save, and access configuration settings
|
||||
/// from disk or environment variables.
|
||||
pub struct ConfigService;
|
||||
|
||||
impl ConfigService {
|
||||
/// Loads configuration from disk or returns default values if not found
|
||||
///
|
||||
/// This function attempts to read the configuration file from disk. If the file
|
||||
/// doesn't exist or can't be parsed, it falls back to default values.
|
||||
/// Environment variable overrides are then applied to the configuration.
|
||||
pub fn load_or_default() -> Result<Config> {
|
||||
let mut cfg = Self::read_disk().unwrap_or_default();
|
||||
Self::apply_env_overrides(&mut cfg)?;
|
||||
Ok(cfg)
|
||||
pub const ENV_NO_CACHE_MANIFEST: &'static str = "POLYSCRIBE_NO_CACHE_MANIFEST";
|
||||
pub const ENV_MANIFEST_TTL_SECONDS: &'static str = "POLYSCRIBE_MANIFEST_TTL_SECONDS";
|
||||
pub const ENV_MODELS_DIR: &'static str = "POLYSCRIBE_MODELS_DIR";
|
||||
pub const ENV_USER_AGENT: &'static str = "POLYSCRIBE_USER_AGENT";
|
||||
pub const ENV_HTTP_TIMEOUT_SECS: &'static str = "POLYSCRIBE_HTTP_TIMEOUT_SECS";
|
||||
pub const ENV_HF_REPO: &'static str = "POLYSCRIBE_HF_REPO";
|
||||
pub const ENV_CACHE_FILENAME: &'static str = "POLYSCRIBE_MANIFEST_CACHE_FILENAME";
|
||||
|
||||
pub const DEFAULT_USER_AGENT: &'static str = "polyscribe/0.1";
|
||||
pub const DEFAULT_DOWNLOADER_UA: &'static str = "polyscribe-model-downloader/1";
|
||||
pub const DEFAULT_HF_REPO: &'static str = "ggerganov/whisper.cpp";
|
||||
pub const DEFAULT_CACHE_FILENAME: &'static str = "hf_manifest_whisper_cpp.json";
|
||||
pub const DEFAULT_HTTP_TIMEOUT_SECS: u64 = 8;
|
||||
pub const DEFAULT_MANIFEST_CACHE_TTL_SECONDS: u64 = 24 * 60 * 60;
|
||||
|
||||
pub fn project_dirs() -> Option<directories::ProjectDirs> {
|
||||
directories::ProjectDirs::from("dev", "polyscribe", "polyscribe")
|
||||
}
|
||||
|
||||
/// Saves the configuration to disk
|
||||
///
|
||||
/// This function serializes the configuration to TOML format and writes it
|
||||
/// to the standard configuration directory for the application.
|
||||
/// Returns an error if writing fails or if project directories cannot be determined.
|
||||
pub fn save(cfg: &Config) -> Result<()> {
|
||||
let Some(dirs) = Self::dirs() else {
|
||||
return Err(Error::Other("unable to get project dirs".into()));
|
||||
};
|
||||
let cfg_dir = dirs.config_dir();
|
||||
fs::create_dir_all(cfg_dir)?;
|
||||
let path = cfg_dir.join("config.toml");
|
||||
let s = toml::to_string_pretty(cfg)?;
|
||||
fs::write(path, s)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_disk() -> Option<Config> {
|
||||
let dirs = Self::dirs()?;
|
||||
let path = dirs.config_dir().join("config.toml");
|
||||
let s = fs::read_to_string(path).ok()?;
|
||||
toml::from_str(&s).ok()
|
||||
}
|
||||
|
||||
fn apply_env_overrides(cfg: &mut Config) -> Result<()> {
|
||||
// POLYSCRIBE__SECTION__KEY format reserved for future nested config.
|
||||
if let Ok(v) = std::env::var(format!("{ENV_PREFIX}_MODELS_DIR")) {
|
||||
cfg.models_dir = Some(PathBuf::from(v));
|
||||
}
|
||||
if let Ok(v) = std::env::var(format!("{ENV_PREFIX}_PLUGINS_DIR")) {
|
||||
cfg.plugins_dir = Some(PathBuf::from(v));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the standard project directories for the application
|
||||
///
|
||||
/// This function creates a ProjectDirs instance with the appropriate
|
||||
/// organization and application names for Polyscribe.
|
||||
/// Returns None if the project directories cannot be determined.
|
||||
pub fn dirs() -> Option<ProjectDirs> {
|
||||
ProjectDirs::from("dev", "polyscribe", "polyscribe")
|
||||
}
|
||||
|
||||
/// Returns the default directory path for storing ML models
|
||||
///
|
||||
/// This function determines the standard data directory for the application
|
||||
/// and appends a 'models' subdirectory to it.
|
||||
/// Returns None if the project directories cannot be determined.
|
||||
pub fn default_models_dir() -> Option<PathBuf> {
|
||||
Self::dirs().map(|d| d.data_dir().join("models"))
|
||||
Self::project_dirs().map(|d| d.data_dir().join("models"))
|
||||
}
|
||||
|
||||
/// Returns the default directory path for storing plugins
|
||||
///
|
||||
/// This function determines the standard data directory for the application
|
||||
/// and appends a 'plugins' subdirectory to it.
|
||||
/// Returns None if the project directories cannot be determined.
|
||||
pub fn default_plugins_dir() -> Option<PathBuf> {
|
||||
Self::dirs().map(|d| d.data_dir().join("plugins"))
|
||||
Self::project_dirs().map(|d| d.data_dir().join("plugins"))
|
||||
}
|
||||
|
||||
pub fn manifest_cache_dir() -> Option<PathBuf> {
|
||||
Self::project_dirs().map(|d| d.cache_dir().join("manifest"))
|
||||
}
|
||||
|
||||
pub fn bypass_manifest_cache() -> bool {
|
||||
env::var(Self::ENV_NO_CACHE_MANIFEST).is_ok()
|
||||
}
|
||||
|
||||
pub fn manifest_cache_ttl_seconds() -> u64 {
|
||||
env::var(Self::ENV_MANIFEST_TTL_SECONDS)
|
||||
.ok()
|
||||
.and_then(|s| s.parse::<u64>().ok())
|
||||
.unwrap_or(Self::DEFAULT_MANIFEST_CACHE_TTL_SECONDS)
|
||||
}
|
||||
|
||||
pub fn manifest_cache_filename() -> String {
|
||||
env::var(Self::ENV_CACHE_FILENAME)
|
||||
.unwrap_or_else(|_| Self::DEFAULT_CACHE_FILENAME.to_string())
|
||||
}
|
||||
|
||||
pub fn models_dir(cfg: Option<&Config>) -> Option<PathBuf> {
|
||||
if let Ok(env_dir) = env::var(Self::ENV_MODELS_DIR) {
|
||||
if !env_dir.is_empty() {
|
||||
return Some(PathBuf::from(env_dir));
|
||||
}
|
||||
}
|
||||
if let Some(c) = cfg {
|
||||
if let Some(dir) = c.models_dir.clone() {
|
||||
return Some(dir);
|
||||
}
|
||||
}
|
||||
Self::default_models_dir()
|
||||
}
|
||||
|
||||
pub fn user_agent() -> String {
|
||||
env::var(Self::ENV_USER_AGENT).unwrap_or_else(|_| Self::DEFAULT_USER_AGENT.to_string())
|
||||
}
|
||||
|
||||
pub fn downloader_user_agent() -> String {
|
||||
env::var(Self::ENV_USER_AGENT).unwrap_or_else(|_| Self::DEFAULT_DOWNLOADER_UA.to_string())
|
||||
}
|
||||
|
||||
pub fn http_timeout_secs() -> u64 {
|
||||
env::var(Self::ENV_HTTP_TIMEOUT_SECS)
|
||||
.ok()
|
||||
.and_then(|s| s.parse::<u64>().ok())
|
||||
.unwrap_or(Self::DEFAULT_HTTP_TIMEOUT_SECS)
|
||||
}
|
||||
|
||||
pub fn hf_repo() -> String {
|
||||
env::var(Self::ENV_HF_REPO).unwrap_or_else(|_| Self::DEFAULT_HF_REPO.to_string())
|
||||
}
|
||||
|
||||
pub fn hf_api_base_for(repo: &str) -> String {
|
||||
format!("https://huggingface.co/api/models/{}", repo)
|
||||
}
|
||||
|
||||
pub fn manifest_cache_path() -> Option<PathBuf> {
|
||||
let dir = Self::manifest_cache_dir()?;
|
||||
Some(dir.join(Self::manifest_cache_filename()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct Config {
|
||||
pub models_dir: Option<PathBuf>,
|
||||
pub plugins_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user