2025-12-08 22:32:42 +01:00
|
|
|
//! Extension pour intégrer la configuration Qobuz dans pmoconfig
|
|
|
|
|
//!
|
|
|
|
|
//! Ce module fournit le trait `QobuzConfigExt` qui permet d'ajouter facilement
|
|
|
|
|
//! des méthodes de gestion des credentials Qobuz à pmoconfig::Config.
|
|
|
|
|
|
|
|
|
|
use anyhow::{anyhow, Result};
|
|
|
|
|
use pmoconfig::Config;
|
|
|
|
|
use serde_yaml::Value;
|
|
|
|
|
|
|
|
|
|
/// Trait d'extension pour gérer la configuration Qobuz dans pmoconfig
|
|
|
|
|
///
|
|
|
|
|
/// Ce trait étend `pmoconfig::Config` avec des méthodes spécifiques
|
|
|
|
|
/// aux credentials et paramètres Qobuz.
|
|
|
|
|
///
|
|
|
|
|
/// # Exemple
|
|
|
|
|
///
|
|
|
|
|
/// ```rust,ignore
|
|
|
|
|
/// use pmoconfig::get_config;
|
|
|
|
|
/// use pmoqobuz::QobuzConfigExt;
|
|
|
|
|
///
|
|
|
|
|
/// let config = get_config();
|
|
|
|
|
/// let (username, password) = config.get_qobuz_credentials()?;
|
|
|
|
|
/// println!("Qobuz user: {}", username);
|
|
|
|
|
/// ```
|
|
|
|
|
pub trait QobuzConfigExt {
|
|
|
|
|
/// Récupère le nom d'utilisateur Qobuz depuis la configuration
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// Le nom d'utilisateur (email) configuré pour Qobuz
|
|
|
|
|
///
|
|
|
|
|
/// # Errors
|
|
|
|
|
///
|
|
|
|
|
/// Retourne une erreur si le nom d'utilisateur n'est pas configuré
|
|
|
|
|
fn get_qobuz_username(&self) -> Result<String>;
|
|
|
|
|
|
|
|
|
|
/// Définit le nom d'utilisateur Qobuz dans la configuration
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `username` - Le nom d'utilisateur (email) Qobuz
|
|
|
|
|
fn set_qobuz_username(&self, username: &str) -> Result<()>;
|
|
|
|
|
|
|
|
|
|
/// Récupère le mot de passe Qobuz depuis la configuration
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// Le mot de passe configuré pour Qobuz
|
|
|
|
|
///
|
|
|
|
|
/// # Errors
|
|
|
|
|
///
|
|
|
|
|
/// Retourne une erreur si le mot de passe n'est pas configuré
|
|
|
|
|
fn get_qobuz_password(&self) -> Result<String>;
|
|
|
|
|
|
|
|
|
|
/// Définit le mot de passe Qobuz dans la configuration
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `password` - Le mot de passe Qobuz
|
|
|
|
|
fn set_qobuz_password(&self, password: &str) -> Result<()>;
|
|
|
|
|
|
|
|
|
|
/// Récupère les credentials Qobuz (username et password)
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// Un tuple (username, password) contenant les credentials Qobuz
|
|
|
|
|
///
|
|
|
|
|
/// # Errors
|
|
|
|
|
///
|
|
|
|
|
/// Retourne une erreur si l'un des credentials n'est pas configuré
|
|
|
|
|
///
|
|
|
|
|
/// # Exemple
|
|
|
|
|
///
|
|
|
|
|
/// ```rust,ignore
|
|
|
|
|
/// use pmoconfig::get_config;
|
|
|
|
|
/// use pmoqobuz::QobuzConfigExt;
|
|
|
|
|
///
|
|
|
|
|
/// let config = get_config();
|
|
|
|
|
/// match config.get_qobuz_credentials() {
|
|
|
|
|
/// Ok((username, password)) => {
|
|
|
|
|
/// println!("Credentials configured for: {}", username);
|
|
|
|
|
/// }
|
|
|
|
|
/// Err(e) => {
|
|
|
|
|
/// eprintln!("Qobuz credentials not configured: {}", e);
|
|
|
|
|
/// }
|
|
|
|
|
/// }
|
|
|
|
|
/// ```
|
|
|
|
|
fn get_qobuz_credentials(&self) -> Result<(String, String)>;
|
|
|
|
|
|
|
|
|
|
/// Récupère l'App ID Qobuz depuis la configuration
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// L'App ID configuré pour Qobuz, ou None si non configuré
|
|
|
|
|
///
|
|
|
|
|
/// # Note
|
|
|
|
|
///
|
|
|
|
|
/// Si aucun App ID n'est configuré, le client utilisera soit le Spoofer
|
|
|
|
|
/// pour en obtenir un dynamiquement, soit un App ID par défaut.
|
|
|
|
|
fn get_qobuz_appid(&self) -> Result<Option<String>>;
|
|
|
|
|
|
|
|
|
|
/// Définit l'App ID Qobuz dans la configuration
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `appid` - L'App ID Qobuz (ex: "1401488693436528")
|
|
|
|
|
fn set_qobuz_appid(&self, appid: &str) -> Result<()>;
|
|
|
|
|
|
|
|
|
|
/// Récupère le secret Qobuz depuis la configuration
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// Le secret encodé en base64, ou None si non configuré
|
|
|
|
|
///
|
|
|
|
|
/// # Note
|
|
|
|
|
///
|
|
|
|
|
/// Le secret est la valeur `configvalue` du code Python.
|
|
|
|
|
/// Il est décodé et XORé avec l'App ID pour obtenir le secret `s4`
|
|
|
|
|
/// utilisé pour signer les requêtes sensibles.
|
|
|
|
|
///
|
|
|
|
|
/// Si aucun secret n'est configuré, le client utilisera le Spoofer
|
|
|
|
|
/// pour en obtenir un dynamiquement.
|
|
|
|
|
fn get_qobuz_secret(&self) -> Result<Option<String>>;
|
|
|
|
|
|
|
|
|
|
/// Définit le secret Qobuz dans la configuration
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `secret` - Le secret encodé en base64 (configvalue)
|
|
|
|
|
fn set_qobuz_secret(&self, secret: &str) -> Result<()>;
|
|
|
|
|
|
|
|
|
|
/// Récupère le token d'authentification depuis la configuration
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// Le token d'authentification, ou None si non configuré ou expiré
|
|
|
|
|
fn get_qobuz_auth_token(&self) -> Result<Option<String>>;
|
|
|
|
|
|
|
|
|
|
/// Récupère l'ID utilisateur depuis la configuration
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// L'ID utilisateur, ou None si non configuré
|
|
|
|
|
fn get_qobuz_user_id(&self) -> Result<Option<String>>;
|
|
|
|
|
|
|
|
|
|
/// Récupère le timestamp d'expiration du token
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// Le timestamp d'expiration (Unix timestamp), ou None si non configuré
|
|
|
|
|
fn get_qobuz_token_expires_at(&self) -> Result<Option<u64>>;
|
|
|
|
|
|
|
|
|
|
/// Récupère le label de l'abonnement depuis la configuration
|
|
|
|
|
fn get_qobuz_subscription_label(&self) -> Result<Option<String>>;
|
|
|
|
|
|
|
|
|
|
/// Sauvegarde les informations d'authentification dans la configuration
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `token` - Le token d'authentification
|
|
|
|
|
/// * `user_id` - L'ID utilisateur
|
|
|
|
|
/// * `subscription_label` - Le label de l'abonnement (optionnel)
|
|
|
|
|
/// * `expires_at` - Timestamp d'expiration (Unix timestamp)
|
|
|
|
|
fn set_qobuz_auth_info(
|
|
|
|
|
&self,
|
|
|
|
|
token: &str,
|
|
|
|
|
user_id: &str,
|
|
|
|
|
subscription_label: Option<&str>,
|
|
|
|
|
expires_at: u64,
|
|
|
|
|
) -> Result<()>;
|
|
|
|
|
|
|
|
|
|
/// Supprime les informations d'authentification de la configuration
|
|
|
|
|
fn clear_qobuz_auth_info(&self) -> Result<()>;
|
|
|
|
|
|
|
|
|
|
/// Vérifie si le token d'authentification est encore valide
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// true si un token existe et n'est pas expiré, false sinon
|
|
|
|
|
fn is_qobuz_auth_valid(&self) -> bool;
|
|
|
|
|
|
|
|
|
|
/// Récupère le répertoire de cache Qobuz
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// Le chemin absolu du répertoire de cache, créé s'il n'existe pas
|
|
|
|
|
fn get_qobuz_cache_dir(&self) -> Result<String>;
|
|
|
|
|
|
|
|
|
|
/// Définit le répertoire de cache Qobuz
|
|
|
|
|
fn set_qobuz_cache_dir(&self, directory: String) -> Result<()>;
|
2025-12-12 21:42:04 +00:00
|
|
|
|
|
|
|
|
/// Récupère le nombre maximum de requêtes concurrentes
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// Le nombre maximum de requêtes concurrentes, ou None si non configuré (défaut: 2)
|
|
|
|
|
fn get_qobuz_rate_limit_max_concurrent(&self) -> Result<Option<usize>>;
|
|
|
|
|
|
|
|
|
|
/// Définit le nombre maximum de requêtes concurrentes
|
|
|
|
|
fn set_qobuz_rate_limit_max_concurrent(&self, max: usize) -> Result<()>;
|
|
|
|
|
|
|
|
|
|
/// Récupère le délai minimum entre requêtes en millisecondes
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// Le délai minimum en ms, ou None si non configuré (défaut: 400ms)
|
|
|
|
|
fn get_qobuz_rate_limit_min_delay_ms(&self) -> Result<Option<u64>>;
|
|
|
|
|
|
|
|
|
|
/// Définit le délai minimum entre requêtes
|
|
|
|
|
fn set_qobuz_rate_limit_min_delay_ms(&self, delay_ms: u64) -> Result<()>;
|
|
|
|
|
|
|
|
|
|
/// Vérifie si le rate limiting est activé
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// true si activé (défaut), false sinon
|
|
|
|
|
fn is_qobuz_rate_limiting_enabled(&self) -> bool;
|
|
|
|
|
|
|
|
|
|
/// Active ou désactive le rate limiting
|
|
|
|
|
fn set_qobuz_rate_limiting_enabled(&self, enabled: bool) -> Result<()>;
|
2025-12-08 22:32:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl QobuzConfigExt for Config {
|
|
|
|
|
fn get_qobuz_username(&self) -> Result<String> {
|
|
|
|
|
match self.get_value(&["accounts", "qobuz", "username"])? {
|
|
|
|
|
Value::String(s) => Ok(s),
|
|
|
|
|
_ => Err(anyhow!("Qobuz username not configured")),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_qobuz_username(&self, username: &str) -> Result<()> {
|
|
|
|
|
self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "username"],
|
|
|
|
|
Value::String(username.to_string()),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_qobuz_password(&self) -> Result<String> {
|
|
|
|
|
match self.get_value(&["accounts", "qobuz", "password"])? {
|
|
|
|
|
Value::String(s) => {
|
|
|
|
|
// Déchiffrement automatique si le mot de passe est chiffré
|
|
|
|
|
pmoconfig::encryption::get_password(&s)
|
|
|
|
|
.map_err(|e| anyhow!("Failed to decrypt password: {}", e))
|
|
|
|
|
}
|
|
|
|
|
_ => Err(anyhow!("Qobuz password not configured")),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_qobuz_password(&self, password: &str) -> Result<()> {
|
|
|
|
|
self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "password"],
|
|
|
|
|
Value::String(password.to_string()),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_qobuz_credentials(&self) -> Result<(String, String)> {
|
|
|
|
|
let username = self.get_qobuz_username()?;
|
|
|
|
|
let password = self.get_qobuz_password()?;
|
|
|
|
|
Ok((username, password))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_qobuz_appid(&self) -> Result<Option<String>> {
|
|
|
|
|
match self.get_value(&["accounts", "qobuz", "appid"]) {
|
|
|
|
|
Ok(Value::String(s)) if !s.is_empty() => Ok(Some(s)),
|
|
|
|
|
Ok(Value::String(_)) => Ok(None), // Empty string
|
2025-12-13 13:36:37 +01:00
|
|
|
Ok(_) => Ok(None), // Wrong type
|
|
|
|
|
Err(_) => Ok(None), // Not configured
|
2025-12-08 22:32:42 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_qobuz_appid(&self, appid: &str) -> Result<()> {
|
|
|
|
|
self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "appid"],
|
|
|
|
|
Value::String(appid.to_string()),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_qobuz_secret(&self) -> Result<Option<String>> {
|
|
|
|
|
match self.get_value(&["accounts", "qobuz", "secret"]) {
|
|
|
|
|
Ok(Value::String(s)) if !s.is_empty() => Ok(Some(s)),
|
|
|
|
|
Ok(Value::String(_)) => Ok(None), // Empty string
|
2025-12-13 13:36:37 +01:00
|
|
|
Ok(_) => Ok(None), // Wrong type
|
|
|
|
|
Err(_) => Ok(None), // Not configured
|
2025-12-08 22:32:42 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_qobuz_secret(&self, secret: &str) -> Result<()> {
|
|
|
|
|
self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "secret"],
|
|
|
|
|
Value::String(secret.to_string()),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_qobuz_auth_token(&self) -> Result<Option<String>> {
|
|
|
|
|
match self.get_value(&["accounts", "qobuz", "auth_token"]) {
|
|
|
|
|
Ok(Value::String(s)) if !s.is_empty() => Ok(Some(s)),
|
|
|
|
|
Ok(Value::String(_)) => Ok(None), // Empty string
|
2025-12-13 13:36:37 +01:00
|
|
|
Ok(_) => Ok(None), // Wrong type
|
|
|
|
|
Err(_) => Ok(None), // Not configured
|
2025-12-08 22:32:42 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_qobuz_user_id(&self) -> Result<Option<String>> {
|
|
|
|
|
match self.get_value(&["accounts", "qobuz", "user_id"]) {
|
|
|
|
|
Ok(Value::String(s)) if !s.is_empty() => Ok(Some(s)),
|
|
|
|
|
Ok(Value::String(_)) => Ok(None), // Empty string
|
2025-12-13 13:36:37 +01:00
|
|
|
Ok(_) => Ok(None), // Wrong type
|
|
|
|
|
Err(_) => Ok(None), // Not configured
|
2025-12-08 22:32:42 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_qobuz_token_expires_at(&self) -> Result<Option<u64>> {
|
|
|
|
|
match self.get_value(&["accounts", "qobuz", "token_expires_at"]) {
|
|
|
|
|
Ok(Value::Number(n)) if n.is_u64() => Ok(Some(n.as_u64().unwrap())),
|
|
|
|
|
Ok(Value::Number(n)) if n.is_i64() => Ok(Some(n.as_i64().unwrap() as u64)),
|
|
|
|
|
Ok(_) => Ok(None), // Wrong type
|
|
|
|
|
Err(_) => Ok(None), // Not configured
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_qobuz_subscription_label(&self) -> Result<Option<String>> {
|
|
|
|
|
match self.get_value(&["accounts", "qobuz", "subscription_label"]) {
|
|
|
|
|
Ok(Value::String(s)) if !s.is_empty() => Ok(Some(s)),
|
|
|
|
|
Ok(Value::String(_)) => Ok(None), // Empty string
|
2025-12-13 13:36:37 +01:00
|
|
|
Ok(_) => Ok(None), // Wrong type
|
|
|
|
|
Err(_) => Ok(None), // Not configured
|
2025-12-08 22:32:42 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_qobuz_auth_info(
|
|
|
|
|
&self,
|
|
|
|
|
token: &str,
|
|
|
|
|
user_id: &str,
|
|
|
|
|
subscription_label: Option<&str>,
|
|
|
|
|
expires_at: u64,
|
|
|
|
|
) -> Result<()> {
|
|
|
|
|
self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "auth_token"],
|
|
|
|
|
Value::String(token.to_string()),
|
|
|
|
|
)?;
|
|
|
|
|
self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "user_id"],
|
|
|
|
|
Value::String(user_id.to_string()),
|
|
|
|
|
)?;
|
|
|
|
|
self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "token_expires_at"],
|
|
|
|
|
Value::Number(serde_yaml::Number::from(expires_at)),
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
if let Some(label) = subscription_label {
|
|
|
|
|
self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "subscription_label"],
|
|
|
|
|
Value::String(label.to_string()),
|
|
|
|
|
)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn clear_qobuz_auth_info(&self) -> Result<()> {
|
|
|
|
|
// On ne propage pas les erreurs car les valeurs peuvent ne pas exister
|
2025-12-13 13:36:37 +01:00
|
|
|
let _ = self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "auth_token"],
|
|
|
|
|
Value::String(String::new()),
|
|
|
|
|
);
|
|
|
|
|
let _ = self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "user_id"],
|
|
|
|
|
Value::String(String::new()),
|
|
|
|
|
);
|
2025-12-08 22:32:42 +01:00
|
|
|
let _ = self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "token_expires_at"],
|
|
|
|
|
Value::Number(serde_yaml::Number::from(0)),
|
|
|
|
|
);
|
|
|
|
|
let _ = self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "subscription_label"],
|
|
|
|
|
Value::String(String::new()),
|
|
|
|
|
);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_qobuz_auth_valid(&self) -> bool {
|
2025-12-12 21:42:04 +00:00
|
|
|
let token_present = self
|
|
|
|
|
.get_qobuz_auth_token()
|
|
|
|
|
.ok()
|
|
|
|
|
.flatten()
|
|
|
|
|
.map(|token| !token.is_empty())
|
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
|
|
|
|
let user_present = self
|
|
|
|
|
.get_qobuz_user_id()
|
|
|
|
|
.ok()
|
|
|
|
|
.flatten()
|
|
|
|
|
.map(|user_id| !user_id.is_empty())
|
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
|
|
|
|
token_present && user_present
|
2025-12-08 22:32:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_qobuz_cache_dir(&self) -> Result<String> {
|
|
|
|
|
self.get_managed_dir(&["host", "qobuz_cache", "directory"], "cache_qobuz")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_qobuz_cache_dir(&self, directory: String) -> Result<()> {
|
|
|
|
|
self.set_managed_dir(&["host", "qobuz_cache", "directory"], directory)
|
|
|
|
|
}
|
2025-12-12 21:42:04 +00:00
|
|
|
|
|
|
|
|
fn get_qobuz_rate_limit_max_concurrent(&self) -> Result<Option<usize>> {
|
|
|
|
|
match self.get_value(&["accounts", "qobuz", "rate_limit", "max_concurrent"]) {
|
|
|
|
|
Ok(Value::Number(n)) if n.is_u64() => Ok(Some(n.as_u64().unwrap() as usize)),
|
|
|
|
|
Ok(_) => Ok(None),
|
|
|
|
|
Err(_) => Ok(Some(2)), // Default: 2 concurrent requests
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_qobuz_rate_limit_max_concurrent(&self, max: usize) -> Result<()> {
|
|
|
|
|
self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "rate_limit", "max_concurrent"],
|
|
|
|
|
Value::Number(serde_yaml::Number::from(max)),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_qobuz_rate_limit_min_delay_ms(&self) -> Result<Option<u64>> {
|
|
|
|
|
match self.get_value(&["accounts", "qobuz", "rate_limit", "min_delay_ms"]) {
|
|
|
|
|
Ok(Value::Number(n)) if n.is_u64() => Ok(Some(n.as_u64().unwrap())),
|
|
|
|
|
Ok(_) => Ok(None),
|
|
|
|
|
Err(_) => Ok(Some(400)), // Default: 400ms
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_qobuz_rate_limit_min_delay_ms(&self, delay_ms: u64) -> Result<()> {
|
|
|
|
|
self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "rate_limit", "min_delay_ms"],
|
|
|
|
|
Value::Number(serde_yaml::Number::from(delay_ms)),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_qobuz_rate_limiting_enabled(&self) -> bool {
|
|
|
|
|
match self.get_value(&["accounts", "qobuz", "rate_limit", "enabled"]) {
|
|
|
|
|
Ok(Value::Bool(b)) => b,
|
|
|
|
|
_ => true, // Default: enabled
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_qobuz_rate_limiting_enabled(&self, enabled: bool) -> Result<()> {
|
|
|
|
|
self.set_value(
|
|
|
|
|
&["accounts", "qobuz", "rate_limit", "enabled"],
|
|
|
|
|
Value::Bool(enabled),
|
|
|
|
|
)
|
|
|
|
|
}
|
2025-12-08 22:32:42 +01:00
|
|
|
}
|