From 56abb68c0dde984a1187428594e238ca547561dd Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Nov 2025 09:44:27 +0000 Subject: [PATCH] Fix cover URL race condition in RadioParadiseStreamSource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrige un bug critique de race condition dans RadioParadiseStreamSource : - Rend song_to_metadata() async et attend que toutes les métadonnées soient configurées - Supprime le tokio::spawn() qui causait un retour prématuré des métadonnées - Garantit que cover_url est disponible quand FlacCacheSink lit les métadonnées - Ajoute des logs de debug pour tracer la configuration des métadonnées - Remplace eprintln! par tracing::warn! pour une meilleure cohérence Corrige également un warning de compilation : - Retire le `mut` inutile sur la variable `writer` dans play_and_cache.rs Le problème : song_to_metadata() retournait les métadonnées avant que la task asynchrone ne finisse de les configurer, ce qui causait un cover_url manquant quand FlacCacheSink essayait de cacher les covers. --- pmoparadise/examples/play_and_cache.rs | 2 +- .../src/radio_paradise_stream_source.rs | 41 +++++++++++-------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/pmoparadise/examples/play_and_cache.rs b/pmoparadise/examples/play_and_cache.rs index 39ae9cad..52fe6f5c 100644 --- a/pmoparadise/examples/play_and_cache.rs +++ b/pmoparadise/examples/play_and_cache.rs @@ -130,7 +130,7 @@ async fn main() -> Result<(), Box> { tracing::info!("Creating playlist: {}", playlist_id); // Créer une playlist éphémère (non persistante) pour cet exemple - let mut writer = playlist_manager.get_write_handle(playlist_id.clone()).await?; + let writer = playlist_manager.get_write_handle(playlist_id.clone()).await?; writer.set_title(format!("Radio Paradise - Channel {}", channel_id)).await?; writer.flush().await?; // Vider la playlist si elle existait tracing::debug!("Playlist created and flushed"); diff --git a/pmoparadise/src/radio_paradise_stream_source.rs b/pmoparadise/src/radio_paradise_stream_source.rs index d7282913..0eb9e1af 100644 --- a/pmoparadise/src/radio_paradise_stream_source.rs +++ b/pmoparadise/src/radio_paradise_stream_source.rs @@ -185,7 +185,7 @@ impl RadioParadiseStreamSourceLogic { if elapsed_ms >= song.elapsed { // Envoyer TrackBoundary AVANT le chunk (avec le même order) - let metadata = song_to_metadata(song, block); + let metadata = song_to_metadata(song, block).await; let timestamp_sec = total_samples as f64 / sample_rate as f64; let track_boundary = AudioSegment::new_track_boundary( *order, @@ -350,47 +350,52 @@ fn pcm_to_audio_segment( /// Convertit Song en TrackMetadata /// -/// Cette fonction est synchrone, donc on wrap la metadata dans Arc> -/// et on spawn une tâche async pour la configurer -fn song_to_metadata(song: &Song, block: &Block) -> Arc> { +/// Configure toutes les métadonnées de manière asynchrone et attend que la configuration +/// soit terminée avant de retourner, garantissant que les métadonnées (y compris cover_url) +/// sont disponibles immédiatement pour les nodes suivants +async fn song_to_metadata(song: &Song, block: &Block) -> Arc> { let metadata = MemoryTrackMetadata::new(); let metadata_arc = Arc::new(RwLock::new(metadata)) as Arc>; - let metadata_clone = metadata_arc.clone(); - // Clone des données pour la task async + // Cloner les données let title = song.title.clone(); let artist = song.artist.clone(); let album = song.album.clone(); let year = song.year; let cover_url = song.cover.as_ref().and_then(|cover| block.cover_url(cover)); - // Configurer les métadonnées de manière asynchrone - tokio::spawn(async move { - let mut meta = metadata_clone.write().await; + // Configurer les métadonnées de manière synchrone (mais async await) + { + let mut meta = metadata_arc.write().await; - // Ces méthodes peuvent échouer (retournent Result), donc on propage avec ? + // Ces méthodes peuvent échouer (retournent Result), donc on log les erreurs if let Err(e) = meta.set_title(Some(title)).await { - eprintln!("Warning: Failed to set title: {}", e); + tracing::warn!("Failed to set title: {}", e); } if let Err(e) = meta.set_artist(Some(artist)).await { - eprintln!("Warning: Failed to set artist: {}", e); + tracing::warn!("Failed to set artist: {}", e); } if let Some(album) = album { if let Err(e) = meta.set_album(Some(album)).await { - eprintln!("Warning: Failed to set album: {}", e); + tracing::warn!("Failed to set album: {}", e); } } if let Some(year) = year { if let Err(e) = meta.set_year(Some(year)).await { - eprintln!("Warning: Failed to set year: {}", e); + tracing::warn!("Failed to set year: {}", e); } } - if let Some(cover_url) = cover_url { - if let Err(e) = meta.set_cover_url(Some(cover_url)).await { - eprintln!("Warning: Failed to set cover_url: {}", e); + if let Some(ref url) = cover_url { + tracing::debug!("RadioParadiseStreamSource: Setting cover_url to: {}", url); + if let Err(e) = meta.set_cover_url(Some(url.clone())).await { + tracing::warn!("Failed to set cover_url: {}", e); + } else { + tracing::debug!("RadioParadiseStreamSource: Successfully set cover_url"); } + } else { + tracing::debug!("RadioParadiseStreamSource: No cover URL available for song"); } - }); + } metadata_arc }