feat: Implémentation complète de la source Radio France avec intégration UPnP et serveur HTTP

Ajout de la source Radio France avec :
- Implémentation du trait MusicSource pour l'intégration UPnP
- Routes HTTP API REST pour l'accès aux stations et flux AAC
- Proxy streaming avec tracking des connexions
- Génération dynamique de l'arborescence UPnP
- Cache multi-niveaux (stations, métadonnées, covers)
- Support des ~70 stations (standalone, groupes, radios ICI)

Fichiers créés :
- pmoradiofrance/src/source.rs (implémentation MusicSource)
- pmoradiofrance/src/server_ext.rs (routes HTTP et proxy streaming)
- pmoradiofrance/assets/radiofrance-logo.webp (logo placeholder)

Fichiers modifiés :
- pmoradiofrance/src/lib.rs (re-exports et modules)
- pmoradiofrance/Cargo.toml (dépendances server)
- Cargo.toml (workspace)
- pmomediaserver/Cargo.toml (feature radiofrance)
- PMOMusic/Cargo.toml (feature radiofrance)
- PMOMusic/src/main.rs (enregistrement automatique)

Tests et validation : compilation OK, pattern respecté, feature-gating cohérent
This commit is contained in:
2026-01-23 12:18:10 +01:00
parent 8adccaa2df
commit 5c5e1d534b
39 changed files with 3174 additions and 187 deletions

View File

@@ -25,6 +25,7 @@ axum = { version = "0.8", optional = true }
utoipa = { version = "5.3", optional = true }
pmoqobuz = { path = "../pmoqobuz", optional = true }
pmoparadise = { path = "../pmoparadise", optional = true }
pmoradiofrance = { path = "../pmoradiofrance", optional = true }
pmoconfig = { path = "../pmoconfig", optional = true }
anyhow = { version = "1.0", optional = true }
pmoaudiocache = { path = "../pmoaudiocache", optional = true }
@@ -52,3 +53,10 @@ paradise = [
]
# Feature pour activer l'API REST de Radio Paradise (en plus de la source UPnP)
paradise-api = ["paradise", "pmoparadise/pmoserver"]
# Feature pour activer le support Radio France
radiofrance = [
"api",
"dep:pmoradiofrance",
"pmoradiofrance/server",
"dep:pmoconfig"
]

View File

@@ -93,3 +93,6 @@ pub use paradise_streaming::ParadiseStreamingExt;
// Re-export sources when features are enabled
#[cfg(feature = "qobuz")]
pub use pmoqobuz;
#[cfg(feature = "radiofrance")]
pub use pmoradiofrance;

View File

@@ -18,6 +18,10 @@ pub enum SourceInitError {
#[error("Failed to initialize Radio Paradise: {0}")]
ParadiseError(String),
#[cfg(feature = "radiofrance")]
#[error("Failed to initialize Radio France: {0}")]
RadioFranceError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
@@ -117,6 +121,25 @@ pub trait SourcesExt {
/// ```
#[cfg(feature = "paradise")]
async fn register_paradise(&mut self) -> Result<()>;
/// Enregistre la source Radio France
///
/// Cette méthode crée automatiquement un `RadioFranceSource` avec cache activé.
/// Radio France ne nécessite pas d'authentification.
///
/// # Erreurs
///
/// Retourne une erreur si :
/// - La connexion au client Radio France échoue
/// - La feature "radiofrance" n'est pas activée
///
/// # Examples
///
/// ```ignore
/// server.register_radiofrance().await?;
/// ```
#[cfg(feature = "radiofrance")]
async fn register_radiofrance(&mut self) -> Result<()>;
}
#[async_trait::async_trait]
@@ -217,6 +240,35 @@ impl SourcesExt for Server {
Ok(())
}
#[cfg(feature = "radiofrance")]
async fn register_radiofrance(&mut self) -> Result<()> {
use pmoradiofrance::{RadioFranceSource, RadioFranceStatefulClient};
tracing::info!("Initializing Radio France source...");
// Obtenir l'URL de base du serveur
let base_url = self.base_url();
// Créer le client stateful depuis la config
let client = RadioFranceStatefulClient::from_config()
.await
.map_err(|e| {
SourceInitError::RadioFranceError(format!("Failed to create client: {}", e))
})?;
// Créer la source depuis le registry (avec cache)
let source = RadioFranceSource::from_registry(client, base_url).map_err(|e| {
SourceInitError::RadioFranceError(format!("Failed to create source: {}", e))
})?;
// Enregistrer la source
self.register_music_source(Arc::new(source)).await;
tracing::info!("✅ Radio France source registered successfully");
Ok(())
}
}
#[cfg(test)]