diff --git a/Blackboard/Report/Oubli_Main_Radio.md b/Blackboard/Report/Oubli_Main_Radio.md index 4e6d773f..396ef143 100644 --- a/Blackboard/Report/Oubli_Main_Radio.md +++ b/Blackboard/Report/Oubli_Main_Radio.md @@ -12,8 +12,10 @@ Refactoring complet pour supprimer la notion de "station virtuelle" et utiliser ### Modifications -**Fichier modifié :** +**Fichiers modifiés :** - `pmoradiofrance/src/playlist.rs` +- `pmoradiofrance/src/source.rs` +- `pmoradiofrance/src/client.rs` ### Changements structurels @@ -39,12 +41,23 @@ Refactoring complet pour supprimer la notion de "station virtuelle" et utiliser - Groupe ICI : toutes les stations triées par nom (pas de station principale) - Les stations se retrouvent toujours à la même position dans la liste +6. **Chargement parallèle des métadonnées** : + - Utilisation de `futures::stream::buffer_unordered(5)` pour charger jusqu'à 5 stations en parallèle + - Décalage de 50ms entre chaque requête pour éviter de surcharger l'API Radio France + - Amélioration significative du temps de chargement des groupes multi-stations + +7. **Filtrage de francebleu générique** : + - Application de la règle métier : `francebleu` (sans suffixe) n'est pas une vraie station + - Filtrage dans `discover_all_stations()` pour éviter qu'elle soit ajoutée à la liste + - Utilisation de `group.slug()` au lieu de `stations[0].slug` dans `source.rs` pour gérer correctement le groupe ICI + ### Résultat - **Groupes avec radio principale** (FIP, France Inter, etc.) : la radio principale apparaît à l'index 0, suivie des webradios triées alphabétiquement - **Groupe ICI** : accessible et affiche toutes les radios locales triées alphabétiquement - Code plus simple et sans logique spéciale dispersée - Interface prévisible : les stations sont toujours au même endroit +- **Chargement plus rapide** : les métadonnées sont récupérées en parallèle au lieu de séquentiellement ## Statut diff --git a/pmoradiofrance/src/client.rs b/pmoradiofrance/src/client.rs index f689640f..d6653631 100644 --- a/pmoradiofrance/src/client.rs +++ b/pmoradiofrance/src/client.rs @@ -134,13 +134,13 @@ impl RadioFranceClient { // Note: Skip francebleu because its "webradios" are actually local radios // which we get from the API with proper "ICI" names for main_station in &main_stations { - stations.push(main_station.clone()); - - // Skip francebleu - its local radios are discovered via API below + // RÈGLE MÉTIER: Filtrer francebleu (portail générique, pas une vraie station) if main_station.slug == "francebleu" { continue; } + stations.push(main_station.clone()); + // Try to discover webradios (may return empty for some stations) if let Ok(webradios) = self.discover_station_webradios(&main_station.slug).await { stations.extend(webradios); @@ -434,10 +434,10 @@ impl RadioFranceClient { return ("mouv", Some(slug)); } - // France Bleu local radios use their slug directly, no webradio param - // e.g., francebleu_alsace → francebleu_alsace/api/live + // France Bleu local radios are webradios of francebleu + // e.g., francebleu_alsace → francebleu/api/live?webradio=francebleu-alsace if slug.starts_with("francebleu_") { - return (slug, None); + return ("francebleu", Some(slug)); } // Main stations diff --git a/pmoradiofrance/src/playlist.rs b/pmoradiofrance/src/playlist.rs index ee26c7e8..a221ea52 100644 --- a/pmoradiofrance/src/playlist.rs +++ b/pmoradiofrance/src/playlist.rs @@ -150,7 +150,7 @@ impl StationGroup { } /// Retourne le slug du groupe (personnalisé ou slug de la première station) - fn slug(&self) -> &str { + pub fn slug(&self) -> &str { self.group_slug.as_deref().unwrap_or(&self.stations[0].slug) } @@ -207,13 +207,33 @@ impl StationGroup { .await } else { // Groupe multi-stations : retourner un container avec toutes les playlists en stub - let mut containers = Vec::new(); + // Chargement en parallèle avec décalage pour éviter de surcharger l'API + use futures::stream::{self, StreamExt}; - for station in &self.stations { - // Appeler to_stub() sur chaque station - let playlist_stub = station.to_stub(metadata_cache, server_base_url).await?; - containers.push(playlist_stub); - } + let futures: Vec<_> = self + .stations + .iter() + .enumerate() + .map(|(i, station)| async move { + // Décalage de 50ms entre chaque requête + if i > 0 { + tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; + } + station.to_stub(metadata_cache, server_base_url).await + }) + .collect(); + + let results: Vec> = stream::iter(futures) + .buffer_unordered(5) // Maximum 5 requêtes en parallèle + .collect() + .await; + + // to_stub() ne devrait plus échouer (utilise des valeurs par défaut) + // mais on gère quand même les erreurs par sécurité + let containers: Vec = results + .into_iter() + .filter_map(|result| result.ok()) + .collect(); let album_art = Some(format!( "{}/api/radiofrance/default-logo", @@ -244,17 +264,43 @@ impl StationGroup { impl Station { /// Niveau 2: to_stub() retourne comment cette station apparaît dans la liste d'un groupe /// - /// Retourne un container de playlist vide avec métadonnées live + /// Retourne un container de playlist vide avec métadonnées live ou par défaut pub async fn to_stub( &self, metadata_cache: &MetadataCache, - _server_base_url: &str, + server_base_url: &str, ) -> Result { let playlist_id = format!("radiofrance:{}", self.slug); let parent_id = self.compute_parent_id(); - // Récupérer les métadonnées du cache - let cached_metadata = metadata_cache.get(&self.slug).await?; + // Essayer de récupérer les métadonnées du cache + let (title, artist, album_art) = match metadata_cache.get(&self.slug).await { + Ok(cached) => ( + cached.title.clone(), + cached.artist.clone(), + cached.album_art.clone(), + ), + Err(e) => { + // Graceful degradation: utiliser des métadonnées par défaut + #[cfg(feature = "logging")] + tracing::warn!( + "Failed to get metadata for {}, using defaults: {}", + self.slug, + e + ); + + let default_art = Some(format!( + "{}/api/radiofrance/default-logo", + server_base_url.trim_end_matches('/') + )); + + ( + self.name.clone(), + Some("Radio France".to_string()), + default_art, + ) + } + }; // Construire juste le container de playlist (sans l'item stream) Ok(Container { @@ -263,10 +309,10 @@ impl Station { restricted: Some("1".to_string()), child_count: Some("1".to_string()), searchable: Some("0".to_string()), - title: cached_metadata.title.clone(), + title, class: "object.container.playlistContainer".to_string(), - artist: cached_metadata.artist.clone(), - album_art: cached_metadata.album_art.clone(), + artist, + album_art, containers: vec![], items: vec![], }) diff --git a/pmoradiofrance/src/source.rs b/pmoradiofrance/src/source.rs index 4784add2..84bf3d51 100644 --- a/pmoradiofrance/src/source.rs +++ b/pmoradiofrance/src/source.rs @@ -268,7 +268,7 @@ impl MusicSource for RadioFranceSource { let group = groups .groups .iter() - .find(|g| g.stations[0].slug == slug) + .find(|g| g.slug() == slug) .ok_or_else(|| MusicSourceError::ObjectNotFound(id.to_string()))?; let container = group