diff --git a/pmomediaserver/src/sources.rs b/pmomediaserver/src/sources.rs index 6c6b2ff0..bd7714fe 100644 --- a/pmomediaserver/src/sources.rs +++ b/pmomediaserver/src/sources.rs @@ -127,13 +127,16 @@ impl SourcesExt for Server { tracing::info!("Initializing Qobuz source..."); + // Obtenir l'URL de base du serveur + let base_url = self.base_url(); + // Créer le client depuis la config let client = QobuzClient::from_config() .await .map_err(|e| SourceInitError::QobuzError(format!("Failed to create client: {}", e)))?; // Créer la source depuis le registry - let source = QobuzSource::from_registry(client) + let source = QobuzSource::from_registry(client, base_url) .map_err(|e| SourceInitError::QobuzError(format!("Failed to create source: {}", e)))?; // Enregistrer la source @@ -154,13 +157,16 @@ impl SourcesExt for Server { tracing::info!("Initializing Qobuz source with explicit credentials..."); + // Obtenir l'URL de base du serveur + let base_url = self.base_url(); + // Créer le client avec credentials let client = QobuzClient::new(username, password) .await .map_err(|e| SourceInitError::QobuzError(format!("Failed to authenticate: {}", e)))?; // Créer la source depuis le registry - let source = QobuzSource::from_registry(client) + let source = QobuzSource::from_registry(client, base_url) .map_err(|e| SourceInitError::QobuzError(format!("Failed to create source: {}", e)))?; // Enregistrer la source diff --git a/pmomediaserver/src/sources_api.rs b/pmomediaserver/src/sources_api.rs index 7034056c..675d392f 100644 --- a/pmomediaserver/src/sources_api.rs +++ b/pmomediaserver/src/sources_api.rs @@ -26,6 +26,9 @@ pub struct QobuzCredentials { pub username: Option, /// Mot de passe Qobuz (optionnel, lu depuis la config si absent) pub password: Option, + /// URL de base du serveur (optionnelle, "http://localhost:8080" par défaut) + #[serde(default)] + pub base_url: Option, } /// Paramètres pour Radio Paradise @@ -75,6 +78,11 @@ async fn register_qobuz(Json(creds): Json) -> impl IntoRespons use pmoqobuz::{QobuzClient, QobuzSource}; use pmosource::api::register_source; + // Utiliser l'URL de base depuis les params ou une valeur par défaut + let base_url = creds + .base_url + .unwrap_or_else(|| "http://localhost:8080".to_string()); + // Créer le client selon les credentials fournis let client_result = if let (Some(username), Some(password)) = (creds.username, creds.password) { QobuzClient::new(&username, &password).await @@ -96,7 +104,7 @@ async fn register_qobuz(Json(creds): Json) -> impl IntoRespons }; // Créer et enregistrer la source depuis le registry - let source = match QobuzSource::from_registry(client) { + let source = match QobuzSource::from_registry(client, base_url) { Ok(s) => Arc::new(s), Err(e) => { return ( diff --git a/pmoqobuz/src/lib.rs b/pmoqobuz/src/lib.rs index 7856d9a2..ce3b7f5f 100644 --- a/pmoqobuz/src/lib.rs +++ b/pmoqobuz/src/lib.rs @@ -124,7 +124,7 @@ //! let cover_cache = Arc::new(CoverCache::new("./cache/covers", 500)?); //! let audio_cache = Arc::new(AudioCache::new("./cache/audio", 100)?); //! -//! let source = QobuzSource::new(client, cover_cache, audio_cache); +//! let source = QobuzSource::new(client, cover_cache, audio_cache, "http://localhost:8080"); //! # Ok(()) //! # } //! ``` @@ -145,7 +145,7 @@ //! let cover_cache = Arc::new(CoverCache::new("./cache/covers", 500)?); //! let audio_cache = Arc::new(AudioCache::new("./cache/audio", 100)?); //! -//! let source = QobuzSource::new(client, cover_cache, audio_cache); +//! let source = QobuzSource::new(client, cover_cache, audio_cache, "http://localhost:8080"); //! //! // Add a track with caching //! let tracks = source.client().get_favorite_tracks().await?; diff --git a/pmoqobuz/src/source.rs b/pmoqobuz/src/source.rs index b7f2d3a0..84b5bbc3 100644 --- a/pmoqobuz/src/source.rs +++ b/pmoqobuz/src/source.rs @@ -56,7 +56,7 @@ const DEFAULT_IMAGE: &[u8] = include_bytes!("../assets/default.webp"); /// let client = QobuzClient::from_config().await?; /// let cover_cache = Arc::new(cover_cache::new_cache("/tmp/qobuz_covers", 256)?); /// let audio_cache = Arc::new(audio_cache::new_cache("/tmp/qobuz_audio", 64)?); -/// let source = QobuzSource::new(client, cover_cache, audio_cache); +/// let source = QobuzSource::new(client, cover_cache, audio_cache, "http://localhost:8080"); /// /// println!("Source: {}", source.name()); /// println!("Supports FIFO: {}", source.supports_fifo()); @@ -80,6 +80,9 @@ struct QobuzSourceInner { /// Cache manager (centralisé) cache_manager: SourceCacheManager, + /// Base URL for streaming server (e.g., "http://192.168.0.138:8080") + base_url: String, + /// Update tracking update_counter: tokio::sync::RwLock, last_change: tokio::sync::RwLock, @@ -100,12 +103,13 @@ impl QobuzSource { /// # Arguments /// /// * `client` - Authenticated Qobuz API client + /// * `base_url` - Base URL for streaming server (e.g., "http://192.168.0.138:8080") /// /// # Errors /// /// Returns an error if the caches are not initialized in the registry #[cfg(feature = "server")] - pub fn from_registry(client: QobuzClient) -> Result { + pub fn from_registry(client: QobuzClient, base_url: impl Into) -> Result { let cache_manager = SourceCacheManager::from_registry("qobuz".to_string())?; let client = Arc::new(client); cache_manager.register_lazy_provider(Arc::new(QobuzLazyProvider::new(client.clone()))); @@ -114,6 +118,7 @@ impl QobuzSource { inner: Arc::new(QobuzSourceInner { client, cache_manager, + base_url: base_url.into(), update_counter: tokio::sync::RwLock::new(0), last_change: tokio::sync::RwLock::new(SystemTime::now()), }), @@ -127,10 +132,12 @@ impl QobuzSource { /// * `client` - Authenticated Qobuz API client /// * `cover_cache` - Cover image cache (required) /// * `audio_cache` - Audio cache (required) + /// * `base_url` - Base URL for streaming server (e.g., "http://192.168.0.138:8080") pub fn new( client: QobuzClient, cover_cache: Arc, audio_cache: Arc, + base_url: impl Into, ) -> Self { let cache_manager = SourceCacheManager::new("qobuz".to_string(), cover_cache, audio_cache); let client = Arc::new(client); @@ -140,6 +147,7 @@ impl QobuzSource { inner: Arc::new(QobuzSourceInner { client, cache_manager, + base_url: base_url.into(), update_counter: tokio::sync::RwLock::new(0), last_change: tokio::sync::RwLock::new(SystemTime::now()), }), @@ -578,6 +586,15 @@ impl QobuzSource { } else { warn!("No qobuz_track_id metadata for {}", pk); } + + // Convertir URL relative en URL absolue + // From: /audio/flac/QOBUZ:123 + // To: http://192.168.0.138:8080/audio/flac/QOBUZ:123 + if let Some(resource) = item.resources.first_mut() { + if resource.url.starts_with('/') { + resource.url = format!("{}{}", self.inner.base_url, resource.url); + } + } } item.parent_id = parent_id.clone(); diff --git a/pmosource/src/cache.rs b/pmosource/src/cache.rs index 426fff56..57c50742 100755 --- a/pmosource/src/cache.rs +++ b/pmosource/src/cache.rs @@ -217,6 +217,29 @@ impl SourceCacheManager { } } + /// Obtenir l'URL absolue d'un fichier audio en cache à partir de son pk + /// + /// # Arguments + /// + /// * `pk` - Clé primaire du fichier audio dans le cache + /// + /// # Returns + /// + /// L'URL absolue complète du fichier audio (ex: http://localhost:8080/audio/flac/QOBUZ:123) + pub fn audio_url(&self, _pk: &str) -> Result { + #[cfg(feature = "server")] + { + pmoupnp::cache_registry::build_audio_url(_pk, None) + .map_err(|e| MusicSourceError::CacheError(e.to_string())) + } + #[cfg(not(feature = "server"))] + { + Err(MusicSourceError::CacheError( + "Server feature not enabled - cannot build audio URL".to_string(), + )) + } + } + /// Cacher une piste audio depuis une URL /// /// Utilise la collection de cette source pour organiser les pistes.