diff --git a/pmoparadise/src/client.rs b/pmoparadise/src/client.rs index 3ba5ca94..48b87295 100644 --- a/pmoparadise/src/client.rs +++ b/pmoparadise/src/client.rs @@ -9,8 +9,8 @@ use url::Url; /// Default Radio Paradise API base URL pub const DEFAULT_API_BASE: &str = "https://api.radioparadise.com/api"; -/// Default block base URL pattern -pub const DEFAULT_BLOCK_BASE: &str = "https://apps.radioparadise.com/blocks/chan/0"; +/// Default block base URL (channel is appended) +pub const DEFAULT_BLOCK_BASE: &str = "https://apps.radioparadise.com/blocks/chan"; /// Default image base URL pub const DEFAULT_IMAGE_BASE: &str = "https://img.radioparadise.com/"; @@ -24,6 +24,9 @@ pub const DEFAULT_BLOCK_TIMEOUT_SECS: u64 = 180; /// Default User-Agent pub const DEFAULT_USER_AGENT: &str = "pmoparadise/0.1.0"; +/// Default channel (0 = main mix) +pub const DEFAULT_CHANNEL: u8 = 0; + /// Radio Paradise HTTP client /// /// This client provides access to Radio Paradise's streaming API, @@ -48,7 +51,6 @@ pub const DEFAULT_USER_AGENT: &str = "pmoparadise/0.1.0"; pub struct RadioParadiseClient { pub(crate) client: Client, api_base: String, - block_base: String, channel: u8, pub(crate) request_timeout: Duration, pub(crate) block_timeout: Duration, @@ -71,12 +73,14 @@ impl RadioParadiseClient { /// Create a client with a custom reqwest::Client /// /// Useful for sharing HTTP connection pools or custom proxy settings + /// + /// Note: Uses default settings (channel 0, default timeouts). + /// For more control, use `ClientBuilder::default().client(client).build()`. pub fn with_client(client: Client) -> Self { Self { client, api_base: DEFAULT_API_BASE.to_string(), - block_base: DEFAULT_BLOCK_BASE.to_string(), - channel: 0, + channel: DEFAULT_CHANNEL, request_timeout: Duration::from_secs(DEFAULT_REQUEST_TIMEOUT_SECS), block_timeout: Duration::from_secs(DEFAULT_BLOCK_TIMEOUT_SECS), next_block_url: None, @@ -88,15 +92,15 @@ impl RadioParadiseClient { self.channel } - fn block_base_for_channel(channel: u8) -> String { - format!("https://apps.radioparadise.com/blocks/chan/{}", channel) + /// Get the block base URL for this client's channel + pub fn block_base(&self) -> String { + format!("{}/{}", DEFAULT_BLOCK_BASE, self.channel) } /// Clone the client with a different channel while preserving other settings. pub fn clone_with_channel(&self, channel: u8) -> Self { let mut cloned = self.clone(); cloned.channel = channel; - cloned.block_base = Self::block_base_for_channel(channel); cloned.next_block_url = None; cloned } @@ -234,7 +238,6 @@ impl RadioParadiseClient { pub struct ClientBuilder { client: Option, api_base: String, - block_base: String, channel: u8, request_timeout: Duration, block_timeout: Duration, @@ -247,8 +250,7 @@ impl Default for ClientBuilder { Self { client: None, api_base: DEFAULT_API_BASE.to_string(), - block_base: DEFAULT_BLOCK_BASE.to_string(), - channel: 0, + channel: DEFAULT_CHANNEL, request_timeout: Duration::from_secs(DEFAULT_REQUEST_TIMEOUT_SECS), block_timeout: Duration::from_secs(DEFAULT_BLOCK_TIMEOUT_SECS), user_agent: DEFAULT_USER_AGENT.to_string(), @@ -275,12 +277,6 @@ impl ClientBuilder { self } - /// Set the block base URL - pub fn block_base(mut self, url: impl Into) -> Self { - self.block_base = url.into(); - self - } - /// Set the channel (0 = main mix, 1 = mellow, 2 = rock, 3 = world/etc) pub fn channel(mut self, channel: u8) -> Self { self.channel = channel; @@ -329,16 +325,9 @@ impl ClientBuilder { builder.build()? }; - let block_base = if self.block_base == DEFAULT_BLOCK_BASE { - RadioParadiseClient::block_base_for_channel(self.channel) - } else { - self.block_base.clone() - }; - Ok(RadioParadiseClient { client, api_base: self.api_base, - block_base, channel: self.channel, request_timeout: self.request_timeout, block_timeout: self.block_timeout, @@ -355,6 +344,6 @@ mod tests { fn test_builder_defaults() { let builder = ClientBuilder::default(); assert_eq!(builder.api_base, DEFAULT_API_BASE); - assert_eq!(builder.channel, 0); + assert_eq!(builder.channel, DEFAULT_CHANNEL); } } diff --git a/pmoparadise/src/config_ext.rs b/pmoparadise/src/config_ext.rs index c2303595..49becbb5 100644 --- a/pmoparadise/src/config_ext.rs +++ b/pmoparadise/src/config_ext.rs @@ -21,6 +21,7 @@ //! } //! ``` +use crate::{channels::ParadiseChannelKind, client::DEFAULT_CHANNEL}; use anyhow::Result; use pmoconfig::Config; use serde_yaml::Value; @@ -81,6 +82,60 @@ pub trait RadioParadiseConfigExt { /// config.set_paradise_enabled(false)?; /// ``` fn set_paradise_enabled(&self, enabled: bool) -> Result<()>; + + /// Récupère le channel par défaut + /// + /// # Returns + /// + /// Le channel par défaut (0 = Main Mix par défaut). + /// + /// Si la valeur n'existe pas dans la configuration, elle est automatiquement + /// définie à "main" et persistée. + /// + /// # Channels disponibles + /// + /// Peut être configuré comme chaîne de caractères ou nombre : + /// - "main" ou 0 = Main Mix (eclectic, diverse mix) + /// - "mellow" ou 1 = Mellow Mix (smooth, chilled music) + /// - "rock" ou 2 = Rock Mix (classic & modern rock) + /// - "eclectic" ou 3 = Eclectic Mix (global sounds) + /// + /// # Exemple de configuration YAML + /// + /// ```yaml + /// sources: + /// radio_paradise: + /// default_channel: mellow # or 1 + /// ``` + /// + /// # Exemple d'utilisation + /// + /// ```rust,ignore + /// let channel = config.get_paradise_default_channel()?; + /// let client = RadioParadiseClient::builder().channel(channel).build().await?; + /// ``` + fn get_paradise_default_channel(&self) -> Result; + + /// Définit le channel par défaut + /// + /// # Arguments + /// + /// * `channel` - Le channel (0-3) + /// + /// La valeur est stockée sous forme de nom convivial ("main", "mellow", etc.) + /// dans le fichier de configuration. + /// + /// # Exemple + /// + /// ```rust,ignore + /// use pmoparadise::channels::ParadiseChannelKind; + /// + /// // Use Mellow Mix by default + /// config.set_paradise_default_channel(ParadiseChannelKind::Mellow.id())?; + /// // Or simply: + /// config.set_paradise_default_channel(1)?; + /// ``` + fn set_paradise_default_channel(&self, channel: u8) -> Result<()>; } impl RadioParadiseConfigExt for Config { @@ -101,6 +156,62 @@ impl RadioParadiseConfigExt for Config { Value::Bool(enabled), ) } + + fn get_paradise_default_channel(&self) -> Result { + match self.get_value(&["sources", "radio_paradise", "default_channel"]) { + Ok(Value::String(s)) => { + // Try to parse as channel name (e.g., "main", "mellow", etc.) + match s.parse::() { + Ok(kind) => Ok(kind.id()), + Err(_) => { + // Invalid channel name, use default + self.set_paradise_default_channel(DEFAULT_CHANNEL)?; + Ok(DEFAULT_CHANNEL) + } + } + } + Ok(Value::Number(n)) => { + // Accept numeric channel ID (0-3) + if let Some(ch) = n.as_u64() { + if ch <= 3 { + Ok(ch as u8) + } else { + // Invalid channel number, use default + self.set_paradise_default_channel(DEFAULT_CHANNEL)?; + Ok(DEFAULT_CHANNEL) + } + } else { + // Not a valid number, use default + self.set_paradise_default_channel(DEFAULT_CHANNEL)?; + Ok(DEFAULT_CHANNEL) + } + } + _ => { + // Use default and persist it as "main" (user-friendly) + self.set_value( + &["sources", "radio_paradise", "default_channel"], + Value::String("main".to_string()), + )?; + Ok(DEFAULT_CHANNEL) + } + } + } + + fn set_paradise_default_channel(&self, channel: u8) -> Result<()> { + // Convert channel ID to user-friendly string name + let channel_name = match channel { + 0 => "main", + 1 => "mellow", + 2 => "rock", + 3 => "eclectic", + _ => return Err(anyhow::anyhow!("Invalid channel ID: {}", channel)), + }; + + self.set_value( + &["sources", "radio_paradise", "default_channel"], + Value::String(channel_name.to_string()), + ) + } } #[cfg(test)]