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
30 KiB
Rapport : Implémentation de pmoradiofrance (Round 3)
Date : 2026-01-22
Crate : pmoradiofrance
Statut : Implémentation initiale complète
Résumé
Création de la crate pmoradiofrance qui fournit un client Rust pour accéder aux APIs publiques de Radio France. Le client permet :
- La découverte dynamique de toutes les stations (~70+)
- La récupération des métadonnées live (émission en cours, producteur, visuels)
- L'accès aux flux audio HiFi (AAC 192 kbps, HLS)
- Le cache de la liste des stations avec TTL configurable
Fichiers créés
| Fichier | Description |
|---|---|
pmoradiofrance/Cargo.toml |
Configuration de la crate avec dépendances |
pmoradiofrance/src/lib.rs |
Point d'entrée et exports publics |
pmoradiofrance/src/error.rs |
Types d'erreurs (Error, Result) |
pmoradiofrance/src/models.rs |
Structures de données pour l'API |
pmoradiofrance/src/client.rs |
RadioFranceClient et ClientBuilder |
pmoradiofrance/src/config_ext.rs |
Extension RadioFranceConfigExt pour pmoconfig |
pmoradiofrance/examples/discover_stations.rs |
Exemple de découverte |
pmoradiofrance/examples/live_metadata.rs |
Exemple de métadonnées live |
Fichiers modifiés
| Fichier | Modification |
|---|---|
Cargo.toml (racine) |
Ajout de pmoradiofrance au workspace |
Architecture du client
RadioFranceClient
Client HTTP stateless pour interroger les APIs Radio France :
// Création
let client = RadioFranceClient::new().await?;
// Découverte des stations
let stations = client.discover_all_stations().await?;
// Métadonnées live
let metadata = client.live_metadata("franceculture").await?;
// URL du flux HiFi
let stream_url = client.get_hifi_stream_url("fip_rock").await?;
Découverte des stations
Le client découvre dynamiquement :
- Stations principales (7) : France Inter, France Info, France Culture, France Musique, FIP, Mouv', France Bleu
- Webradios (~15-20) : FIP Rock, FIP Jazz, France Musique Baroque, etc.
- Radios locales France Bleu (~44) : via le champ
now.localRadiosde l'API
Gestion des webradios
Le parsing des slugs gère automatiquement les webradios :
| Slug | Base station | Paramètre webradio |
|---|---|---|
fip |
fip |
- |
fip_rock |
fip |
?webradio=fip_rock |
francemusique_jazz |
francemusique |
?webradio=francemusique_jazz |
francebleu_alsace |
francebleu_alsace |
- (slug direct) |
Extension de configuration
Le trait RadioFranceConfigExt permet de cacher la liste des stations :
use pmoconfig::get_config;
use pmoradiofrance::RadioFranceConfigExt;
let config = get_config();
// Vérifier le cache (TTL par défaut : 7 jours)
if let Some(stations) = config.get_radiofrance_stations_cached()? {
// Utiliser les stations du cache
} else {
// Découvrir et mettre en cache
let client = RadioFranceClient::new().await?;
let stations = client.discover_all_stations().await?;
config.set_radiofrance_cached_stations(&stations)?;
}
Configuration YAML générée
sources:
radiofrance:
enabled: true
station_cache_ttl_secs: 604800 # 7 jours
station_cache:
stations: [...]
last_updated: 1769112000
version: 1
Tests d'intégration
17 tests d'intégration qui appellent la vraie API Radio France :
# Exécuter tous les tests d'intégration
cargo test -p pmoradiofrance -- --ignored
# Avec output visible
cargo test -p pmoradiofrance -- --ignored --nocapture
| Test | Description | Status |
|---|---|---|
test_client_creation |
Création du client | ✅ |
test_live_metadata_franceculture |
Métadonnées France Culture | ✅ |
test_live_metadata_franceinter |
Métadonnées France Inter | ✅ |
test_live_metadata_fip |
Métadonnées FIP (avec chanson) | ✅ |
test_live_metadata_fip_rock |
Métadonnées webradio FIP Rock | ✅ |
test_live_metadata_francemusique |
Métadonnées France Musique | ✅ |
test_live_metadata_francebleu |
Métadonnées + radios locales | ✅ |
test_live_metadata_mouv |
Métadonnées Mouv' | ✅ |
test_get_hifi_stream_url |
URLs des flux HiFi | ✅ |
test_get_available_streams |
Liste des flux disponibles | ✅ |
test_discover_main_stations |
Découverte stations principales | ✅ |
test_discover_fip_webradios |
Découverte webradios FIP | ✅ |
test_discover_francemusique_webradios |
Découverte webradios FM | ✅ |
test_discover_local_radios |
Découverte radios locales | ✅ |
test_discover_all_stations |
Découverte complète | ✅ |
test_invalid_station |
Gestion d'erreur | ✅ |
test_refresh_delay |
Calcul délai refresh | ✅ |
Correction effectuée pendant l'implémentation
Bug découvert : Le champ localRadios de l'API France Bleu est dans now.localRadios (imbriqué dans ShowMetadata), pas au niveau racine de LiveResponse.
Correction :
- Déplacé le champ
local_radiosdeLiveResponseversShowMetadata - Ajouté une méthode helper
LiveResponse::local_radios()pour accès simplifié - Mis à jour le client et les tests
Features Cargo
| Feature | Description | Dépendances |
|---|---|---|
default |
Configuration de base | pmoconfig |
pmoconfig |
Support extension config | dep:pmoconfig |
cache |
Support cache audio/covers | pmocovers, pmoaudiocache |
playlist |
Support playlists FIFO | pmoplaylist |
logging |
Logs avec tracing | - |
server |
Support serveur complet | Toutes les features |
full |
Toutes les features | server, logging |
Prochaines étapes
- Implémenter
source.rs: TraitMusicSourcepour intégration UPnP - Ajouter support FIFO : Pour les radios musicales (FIP, France Musique)
- Cache des métadonnées live : Respecter
delayToRefresh - Intégration serveur : Routes API REST via pmoserver
Exemples d'utilisation
Découverte des stations
cargo run -p pmoradiofrance --example discover_stations
Métadonnées live
# Station par défaut (France Culture)
cargo run -p pmoradiofrance --example live_metadata
# Station spécifique
cargo run -p pmoradiofrance --example live_metadata -- fip_rock
Round 4bis : Client Stateful et Support Playlist (2026-01-22)
Objectif
Compléter l'implémentation avec un client stateful qui gère automatiquement le cache et les structures pour la génération de playlists UPnP.
Fichiers créés
| Fichier | Description |
|---|---|
pmoradiofrance/src/stateful_client.rs |
RadioFranceStatefulClient avec cache automatique |
pmoradiofrance/src/playlist.rs |
Structures pour playlists UPnP (groupes, items) |
Fichiers modifiés
| Fichier | Modification |
|---|---|
pmoradiofrance/src/lib.rs |
Ajout modules stateful_client et playlist + re-exports |
pmoradiofrance/src/error.rs |
Ajout variant Config(#[from] anyhow::Error) pour conversion |
pmoradiofrance/Cargo.toml |
Ajout dépendance pmodidl pour feature playlist |
RadioFranceStatefulClient
Client de haut niveau avec gestion automatique du cache :
use pmoradiofrance::RadioFranceStatefulClient;
use pmoconfig::get_config;
let config = get_config();
let client = RadioFranceStatefulClient::new(config).await?;
// Cache automatique des stations (7 jours par défaut)
let stations = client.get_stations().await?;
// Cache intelligent des métadonnées (respecte delayToRefresh)
let metadata = client.get_live_metadata("franceculture").await?;
Caractéristiques :
-
Cache à deux niveaux :
- Liste des stations : persisté dans pmoconfig (7 jours)
- Métadonnées live : en mémoire (TTL dynamique de l'API)
-
Thread-safe : Clone + Send + Sync via
Arc<RwLock<...>> -
Gestion intelligente du TTL :
- Stations : configurable via
set_station_cache_ttl() - Métadonnées : utilise
delayToRefreshde l'API
- Stations : configurable via
Structures de Playlist
StationGroups
Organisation hiérarchique des stations pour navigation UPnP :
pub struct StationGroups {
pub standalone: Vec<Station>, // Sans webradios
pub with_webradios: Vec<StationGroup>, // Avec webradios
pub local_radios: Vec<Station>, // France Bleu/ICI
}
pub struct StationGroup {
pub main: Station,
pub webradios: Vec<Station>,
}
Logique de groupement :
- Stations standalone : France Inter, France Culture, France Info, Mouv'
- Groupes avec webradios : FIP (+ FIP Rock, Jazz...), France Musique (+ variantes)
- Radios locales : ~44 radios ICI (ex-France Bleu)
StationPlaylist
Playlist UPnP volatile pour une station :
pub struct StationPlaylist {
pub id: String,
pub station: Station,
pub stream_item: Item, // Item UPnP avec métadonnées
}
Mapping des métadonnées vers UPnP :
| Type | title | artist | album | class |
|---|---|---|---|---|
| Radio parlée | émission • titre | producteur | émission | audioBroadcast |
| Radio musicale | titre chanson | artiste(s) | album | musicTrack |
Gestion des covers :
- Extraction UUID depuis
visual_background - Cache via
pmocovers(optionnel) - URLs Pikapi haute résolution (Large: 560x960)
Corrections et améliorations
1. Gestion des erreurs
Problème : Les méthodes pmoconfig retournent anyhow::Result mais le client utilise son propre type Result<T, Error>.
Solution : Ajout d'un variant dans Error pour conversion automatique :
pub enum Error {
// ...
#[error("Configuration error: {0}")]
Config(#[from] anyhow::Error),
}
2. Feature playlist
Ajout : Dépendance pmodidl pour les structures DIDL-Lite (Item, Resource) :
[features]
playlist = ["dep:pmoplaylist", "dep:pmodidl"]
3. Doctests propres
Problème initial : Exemples marqués ignore mais testés avec --include-ignored.
Solution : Utilisation de no_run avec contexte async complet :
/// ```no_run
/// use pmoradiofrance::RadioFranceStatefulClient;
/// use pmoconfig::get_config;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let config = get_config();
/// let client = RadioFranceStatefulClient::new(config).await?;
/// // ...
/// Ok(())
/// }
/// ```
Avantages :
- Exemples compilés (vérification syntaxe)
- Pas exécutés (pas de dépendance réseau)
- Lignes de contexte cachées avec
#dans la doc générée
4. Conditional compilation propre
Feature logging pour le debug :
#[cfg(feature = "logging")]
fn remaining_ttl(&self) -> Duration { ... }
#[cfg(feature = "logging")]
tracing::debug!("Using cached metadata for {} (TTL: {:?})",
station, entry.remaining_ttl());
Tests
Résultats :
- ✅ Tests unitaires : 26/26 passés
- ✅ Tests d'intégration (API réelle) : 26/26 passés
- ✅ Doctests : 12/12 compilés
# Tests complets (unitaires + intégration + doctests)
cargo test -p pmoradiofrance -- --include-ignored
# Tests unitaires uniquement
cargo test -p pmoradiofrance --lib
Règles métier importantes
- URLs de stream constantes : L'URL du stream ne change JAMAIS, seules les métadonnées changent
- Polling intelligent : Toujours respecter
delayToRefreshde l'API - Renommage France Bleu → ICI : Les slugs restent
francebleu_*mais l'affichage utilise "ICI" - Validation du cache : Triple vérification (existence + TTL + version d'algorithme)
Prochaines étapes
-
Implémenter
source.rs: TraitMusicSourcepour intégration UPnP- Génération automatique des playlists via
StationGroups - Rafraîchissement périodique des métadonnées (respecte
delayToRefresh) - Gestion des streams live continus (pas de FIFO - API ne fournit que des flux)
- Génération automatique des playlists via
-
Intégration serveur : Routes API REST via
pmoserver/radiofrance/stations: Liste des stations groupées/radiofrance/{slug}/metadata: Métadonnées live avec cache/radiofrance/{slug}/stream: Redirection vers flux HiFi- Cache registry pour les covers
-
Optimisations :
- Pool de connexions HTTP partagé entre instances
- Préchargement intelligent des métadonnées (stations populaires)
- Métriques de cache (hit rate, age, refresh count)
- Compression des réponses API
Fin du rapport Round 4bis
Round 5 : MusicSource et Intégration Serveur (2026-01-23)
Objectif
Implémentation du trait MusicSource pour l'intégration UPnP et ajout des routes serveur REST pour l'accès HTTP aux stations Radio France.
Fichiers créés
| Fichier | Description | Lignes |
|---|---|---|
pmoradiofrance/src/source.rs |
Implémentation du trait MusicSource |
~450 |
pmoradiofrance/src/server_ext.rs |
Routes HTTP API et proxy streaming | ~280 |
pmoradiofrance/assets/radiofrance-logo.webp |
Logo placeholder WebP 1x1 | 44 octets |
Fichiers modifiés
| Fichier | Modification |
|---|---|
pmoradiofrance/src/lib.rs |
Ajout modules source et server_ext + re-exports (feature server) |
pmoradiofrance/Cargo.toml |
Ajout dépendances axum, futures, pmoserver (feature server) |
Cargo.toml (workspace) |
Ajout axum = "0.8.4" et futures = "0.3" |
RadioFranceSource (trait MusicSource)
Implémentation complète du trait MusicSource pour intégration UPnP :
use pmoradiofrance::RadioFranceSource;
use pmoconfig::get_config;
let config = get_config();
let source = RadioFranceSource::new(config).await?;
// La source génère dynamiquement l'arborescence UPnP
let root = source.root_container().await?;
Fonctionnalités :
-
Génération dynamique de l'arborescence :
- Stations standalone → Items directs (France Inter, France Info, etc.)
- Stations avec webradios → Containers (FIP/, France Musique/)
- Radios locales → Container unique "Radios ICI/"
-
Cache des playlists :
- Une
StationPlaylistpar station (métadonnées volatiles) - Item UPnP mis à jour avec les nouvelles métadonnées
- URL du stream reste constante
- Une
-
Rafraîchissement automatique :
- Tâche tokio par station streamée
- Respecte
delayToRefreshde l'API (2-5 minutes) - Arrêt automatique quand toutes les connexions sont fermées
-
Thread-safety :
Arc<RwLock<>>pour partage multi-thread- Clone + Send + Sync
- Drop trait pour nettoyage automatique
Architecture de l'arborescence UPnP générée :
Radio France/
├── France Inter (item) [standalone]
├── France Info (item) [standalone]
├── France Culture (item) [standalone]
├── Mouv' (item) [standalone]
├── FIP/ [container]
│ ├── FIP (item) [main]
│ ├── FIP Rock (item) [webradio]
│ ├── FIP Jazz (item) [webradio]
│ └── ...
├── France Musique/ [container]
│ ├── France Musique (item) [main]
│ └── ...
└── Radios ICI/ [container]
├── ICI Alsace (item)
├── ICI Paris (item)
└── ... (~44 radios)
Capacités de la source :
SourceCapabilities {
supports_fifo: false, // Streams live uniquement
supports_search: false,
supports_favorites: false,
supports_playlists: false,
supports_user_content: false,
supports_high_res_audio: false, // AAC 48kHz (pas HiRes)
max_sample_rate: Some(48000),
supports_multiple_formats: false,
supports_advanced_search: false,
supports_pagination: false,
}
RadioFranceServerState (routes HTTP)
Extension serveur avec routes API REST et proxy streaming :
use pmoradiofrance::{RadioFranceStatefulClient, RadioFranceServerState};
use std::sync::Arc;
let client = RadioFranceStatefulClient::new(config).await?;
let state = Arc::new(RadioFranceServerState::new(client));
let router = RadioFranceServerState::router().with_state(state);
// Intégrer dans votre serveur Axum
// app = app.nest("/api", router);
Routes API disponibles :
| Route | Méthode | Description | Réponse | Cache |
|---|---|---|---|---|
/radiofrance/stations |
GET | Liste groupée des stations | StationGroups JSON |
7 jours (pmoconfig) |
/radiofrance/:slug/metadata |
GET | Métadonnées live | LiveResponse JSON |
delayToRefresh (in-memory) |
/radiofrance/:slug/stream |
GET | Proxy streaming AAC | Stream audio/aac | - |
Proxy streaming :
- Passthrough AAC pur : Aucun transcodage, forward bytes tels quels
- Tracking des connexions : Registre des stations en cours d'écoute
- Mise à jour d'activité : Timestamp à chaque chunk reçu
- Rafraîchissement intelligent :
- Tâche de refresh lancée automatiquement lors du stream
- Nettoyage des connexions inactives (>30s sans chunk)
- Arrêt automatique quand toutes les connexions sont fermées
- Headers HTTP :
Content-Type: audio/aac,Transfer-Encoding: chunked
Justification du proxy (vs redirection 302) :
✅ Tracking précis des stations écoutées
✅ Rafraîchissement intelligent basé sur l'usage réel
✅ Pas de problème de décodage AAC streaming (contrainte actuelle)
✅ Consommation CPU minimale (juste forward)
❌ Bande passante serveur utilisée (acceptable en LAN domestique)
Corrections de bugs
1. tokio RwLock (source.rs:132)
Problème : if let Ok(mut pls) = playlists.write().await
Cause : tokio::sync::RwLock::write() retourne le guard directement, pas un Result
Solution : let mut pls = playlists.write().await;
2. Annotations de type (source.rs:134)
Problème : let _ = playlist ne peut inférer le type
Cause : Pattern underscore nécessite annotation explicite
Solution : let _: Result<()> = playlist
3. Cover cache (source.rs:289, 293)
Problème : .map(|c| c.as_ref()) redondant
Cause : cover_cache est déjà Option<Arc<CoverCache>>
Solution : Simplifié en cover_cache.as_ref()
4. Handlers Axum 0.8 (server_ext.rs)
Problème : Incompatibilité signatures handlers avec Axum 0.8
Cause : Gestion du state dans le router
Solution :
- Renommé handlers (
get_stations→handle_get_stations) - Changé
router()pour retournerRoutersans type de state - Le caller ajoute le state via
.with_state()
5. Dépendances workspace
Problème : axum et futures non définis dans workspace
Cause : Erreur lors de la compilation avec workspace = true
Solution : Ajouté axum = "0.8.4" et futures = "0.3" dans Cargo.toml racine
Tests et validation
Compilation :
- ✅
cargo check: OK - ✅
cargo check --features server: OK
Warnings :
- Import inutilisé
crate::error::Resultdansserver_ext.rs(mineur) - Autres warnings du workspace non liés à cette tâche
Architecture :
- ✅ Respect des patterns
pmosource - ✅ Respect des patterns
pmoserver - ✅ Feature-gating cohérent
- ✅ Utilisation correcte des dépendances workspace
Points techniques clés
1. Génération dynamique de l'arborescence
- Pas de hardcoding des containers
- Structure suit les données via
StationGroups::from_stations() - Logique simple et maintenable
2. Rafraîchissement intelligent
- Une tâche par station streamée activement
- Respect du
delayToRefresh(typiquement 2-5 minutes) - Arrêt automatique → économie de ressources
- Nettoyage périodique des connexions inactives
3. Proxy streaming AAC
- Passthrough pur : Aucun décodage/encodage
- CPU minimal : Juste forward de bytes
- Tracking précis : Savoir exactement quelles stations sont écoutées
- Bande passante : Acceptable en usage domestique LAN
4. Cache multi-niveaux
- Stations : pmoconfig (persisté), TTL 7 jours configurable
- Métadonnées : In-memory, TTL dynamique de l'API
- Covers : pmocovers (optionnel avec feature
cache)
5. Thread-safety
- Toutes les structures : Clone + Send + Sync
- Partage via
Arc<RwLock<>> - Drop trait pour nettoyage automatique des tâches
Limitations connues
1. Logo placeholder
Le fichier radiofrance-logo.webp est minimal (1x1 pixel, 44 octets).
Action requise : Remplacer par un vrai logo 300x300 pixels pour production.
2. Pas de transcodage
Le proxy streaming est AAC passthrough uniquement.
Raison : Limitations actuelles du décodage AAC streaming dans pmoflac.
Evolution future : Si pmoflac supporte AAC streaming, ajouter transcodage optionnel vers FLAC via feature flag.
3. Bande passante serveur
Le proxy consomme de la bande passante serveur (acceptable en LAN).
Alternative possible : Redirection 302 vers Radio France (mais perd le tracking).
Statistiques
Code ajouté :
- ~730 lignes de Rust
- 3 fichiers créés (source.rs, server_ext.rs, logo.webp)
- 3 fichiers modifiés (lib.rs, Cargo.toml × 2)
Complexité : Moyenne-Haute
- Intégration multi-crates
- State management async
- Tâches en arrière-plan
- Compatibilité Axum 0.8
Temps estimé : 2-3 heures de développement
Prochaines étapes
Court terme
- Remplacer le logo placeholder par un vrai logo WebP 300x300
- Tester l'intégration complète dans PMOMusic
- Ajouter tests d'intégration (actuellement marqués
#[ignore])
Moyen terme
- Intégrer
RadioFranceSourcedans le système de sources global de PMOMusic - Documenter l'utilisation dans le README principal
- Implémenter les métriques optionnelles (spécifiées dans Round 5)
Long terme
- Si
pmoflacsupporte AAC streaming : ajouter transcodage optionnel vers FLAC - Optimisation : préchargement intelligent des stations populaires
- Configuration : liste des stations à précharger au démarrage
Conformité Round 5
Objectifs du Round 5 :
- Implémentation du trait
MusicSource - Génération dynamique de l'arborescence UPnP
- Routes API REST complètes
- Proxy streaming AAC avec tracking
- Rafraîchissement automatique basé sur l'usage
- Cache multi-niveaux opérationnel
- Tests structurels (compilation)
- ~70 stations Radio France accessibles
Règles métier :
- Génération dynamique depuis StationGroups
- Pas de hardcoding des containers
- Respect du delayToRefresh
- Arrêt automatique des tâches de refresh
- Collection "radiofrance" pour les covers
- Protocol Info UPnP corrects (AAC/HLS)
Fin du rapport Round 5
Rapport : Activation de la source Radio France (Round 6)
Date : 2026-01-23
Crate : pmomediaserver, pmoradiofrance
Statut : Activation complète de la source Radio France
Résumé
Activation de la source Radio France dans le système PMOMusic en suivant le pattern établi par les sources existantes (Qobuz, Radio Paradise). Cette étape permet d'enregistrer automatiquement la source Radio France dans le serveur UPnP MediaServer.
Fichiers modifiés
| Fichier | Modification | Lignes |
|---|---|---|
pmomediaserver/Cargo.toml |
Ajout de la feature radiofrance et dépendance optionnelle |
+11 |
pmomediaserver/src/sources.rs |
Implémentation de register_radiofrance() dans SourcesExt |
+31 |
pmomediaserver/src/lib.rs |
Re-export de pmoradiofrance avec feature gate |
+3 |
pmoradiofrance/src/source.rs |
Ajout de la méthode from_registry() |
+38 |
Total : ~83 lignes ajoutées
Modifications détaillées
1. Feature radiofrance dans pmomediaserver
Fichier : pmomediaserver/Cargo.toml
Ajout de la dépendance optionnelle et de la feature :
# Dependencies
pmoradiofrance = { path = "../pmoradiofrance", optional = true }
# Features
radiofrance = [
"api",
"dep:pmoradiofrance",
"pmoradiofrance/server",
"dep:pmoconfig"
]
Conformité : Suit exactement le pattern de qobuz et paradise
2. Extension SourcesExt avec register_radiofrance()
Fichier : pmomediaserver/src/sources.rs
2.1 Ajout du type d'erreur
#[cfg(feature = "radiofrance")]
#[error("Failed to initialize Radio France: {0}")]
RadioFranceError(String),
2.2 Signature dans le trait
#[cfg(feature = "radiofrance")]
async fn register_radiofrance(&mut self) -> Result<()>;
2.3 Implémentation
#[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 Radio France
let client = RadioFranceStatefulClient::new()
.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(())
}
Points clés :
- Crée le client stateful sans authentification (Radio France est public)
- Utilise
from_registry()pour récupérer automatiquement les caches - Enregistre la source via
register_music_source() - Logging clair avec emojis pour le retour visuel
3. Méthode from_registry() dans RadioFranceSource
Fichier : pmoradiofrance/src/source.rs
#[cfg(feature = "server")]
use pmosource::SourceCacheManager;
/// Create a new Radio France source from the cache registry
///
/// This is the recommended way to create a source when using the UPnP server.
/// The cover cache is automatically retrieved from the global registry.
#[cfg(feature = "server")]
pub fn from_registry(
client: RadioFranceStatefulClient,
base_url: impl Into<String>,
) -> Result<Self> {
let cache_manager = SourceCacheManager::from_registry("radiofrance".to_string())
.map_err(|e| crate::error::RadioFranceError::Other(
format!("Cache registry error: {}", e)
))?;
Ok(Self {
client,
playlists: Arc::new(RwLock::new(HashMap::new())),
refresh_handles: Arc::new(RwLock::new(HashMap::new())),
#[cfg(feature = "cache")]
cover_cache: Some(cache_manager.cover_cache().clone()),
server_base_url: Some(base_url.into()),
update_id: Arc::new(RwLock::new(0)),
last_change: Arc::new(RwLock::new(None)),
})
}
Changements :
- Récupère le
CoverCachedepuis le registry global avec la clé"radiofrance" - Initialise
server_base_urlautomatiquement - Pattern identique à Qobuz (
from_registry())
4. Re-export dans lib.rs
Fichier : pmomediaserver/src/lib.rs
#[cfg(feature = "radiofrance")]
pub use pmoradiofrance;
Permet d'accéder à pmoradiofrance via pmomediaserver::pmoradiofrance quand la feature est activée.
Tests de compilation
Test 1 : pmomediaserver avec feature radiofrance
cargo check -p pmomediaserver --features radiofrance
Résultat : ✅ Compilation réussie (warnings uniquement sur d'autres crates)
Test 2 : pmoradiofrance avec feature server
cargo check -p pmoradiofrance --features server
Résultat : ✅ Compilation réussie
Utilisation
Dans le code du serveur PMOMusic
use pmomediaserver::sources::SourcesExt;
use pmoserver::ServerBuilder;
let mut server = ServerBuilder::new_configured().build();
// Enregistrer Radio France (nécessite la feature "radiofrance")
server.register_radiofrance().await?;
// Lister toutes les sources
let sources = server.list_music_sources().await;
println!("Sources actives :");
for source in sources {
println!(" - {} ({})", source.name(), source.id());
}
Features à activer
Dans le Cargo.toml du serveur principal :
[dependencies]
pmomediaserver = { path = "../pmomediaserver", features = ["radiofrance"] }
Pattern d'activation des sources
| Source | Feature | Client | Authentification | Registry |
|---|---|---|---|---|
| Qobuz | qobuz |
QobuzClient |
Username/Password (pmoconfig) | ✅ |
| Paradise | paradise |
RadioParadiseClient |
Aucune | ✅ |
| Radio France | radiofrance |
RadioFranceStatefulClient |
Aucune | ✅ |
Uniformité : Toutes les sources suivent le même pattern :
- Feature optionnelle dans
pmomediaserver - Méthode
register_xxx()dans le traitSourcesExt - Méthode
from_registry()dans la source - Re-export conditionnel dans
lib.rs
Prochaines étapes
Immédiat
- Tester l'intégration complète dans le serveur PMOMusic principal
- Vérifier que les ~70 stations Radio France apparaissent dans le ContentDirectory
- Tester le streaming et le rafraîchissement automatique des métadonnées
Court terme
- Documenter l'activation dans le README principal de PMOMusic
- Ajouter Radio France à la liste des sources supportées
- Vérifier la configuration du cache registry pour "radiofrance"
Conformité Round 6
Objectifs :
- Étude du pattern d'activation (Qobuz, Paradise, pmoupnp)
- Ajout de la feature
radiofrancedans pmomediaserver - Implémentation de
register_radiofrance()dansSourcesExt - Méthode
from_registry()dansRadioFranceSource - Re-export dans lib.rs
- Tests de compilation réussis
- Documentation de l'utilisation
Règles métier :
- Pattern identique aux sources existantes
- Pas d'authentification requise (Radio France est public)
- Utilisation du registry global pour les caches
- Logging clair avec tracing
- Feature-gated (compilation conditionnelle)
Fin du rapport Round 6