diff --git a/Cargo.lock b/Cargo.lock index 9a26616a..ef59d884 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "PMOMusic" -version = "0.3.42" +version = "0.3.44" dependencies = [ "axum 0.8.7", "console-subscriber", diff --git a/PMOMusic/Cargo.toml b/PMOMusic/Cargo.toml index 2e016542..693f6f52 100644 --- a/PMOMusic/Cargo.toml +++ b/PMOMusic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "PMOMusic" -version = "0.3.42" +version = "0.3.45" edition = "2024" [dependencies] diff --git a/pmocontrol/src/music_renderer/musicrenderer.rs b/pmocontrol/src/music_renderer/musicrenderer.rs index cd05675e..40376f37 100644 --- a/pmocontrol/src/music_renderer/musicrenderer.rs +++ b/pmocontrol/src/music_renderer/musicrenderer.rs @@ -1129,14 +1129,21 @@ impl MusicRenderer { /// Play from a specific index in the queue. pub fn play_from_index(&self, index: usize) -> Result<(), ControlPointError> { - // Reset the has_played flag before starting playback to prevent - // auto-advance on transient STOPPED states during track initialization. - // The flag will be set back to true when PLAYING state is detected. + // ✅ CORRECTIF BUG SHUFFLE: Quand on change d'index manuellement + // (par exemple shuffle, clique sur un titre), on réinitialise OBLIGATOIREMENT + // le flag has_played. Sinon quand le titre se termine l'auto-avance + // pense qu'il n'a jamais démarré et s'arrête. + tracing::debug!( + index = index, + renderer = self.info.friendly_name(), + "🎯 play_from_index appelé, réinitialisation has_played_flag" + ); self.clear_has_played_flag(); self.lock_backend_for("play_from_index") .play_from_index(index)?; - self.emit_queue_updated(); + + self.set_playback_source(PlaybackSource::FromQueue); Ok(()) } @@ -1159,6 +1166,11 @@ impl MusicRenderer { let queue_not_empty = backend.len().unwrap_or(0) > 0; if queue_not_empty { + // Set playback_source to FromQueue BEFORE calling backend + // to prevent race condition where watcher sees STOPPED before + // source is set, breaking auto-advance + self.set_playback_source(PlaybackSource::FromQueue); + // Si on a des items dans la queue, jouer le track courant (ou le premier si aucun n'est sélectionné) // Cela fonctionne pour tous les backends (UPnP interne, OpenHome, etc.) backend.play_from_queue() @@ -1624,6 +1636,11 @@ impl MusicRenderer { // The flag will be set back to true when PLAYING state is detected. self.clear_has_played_flag(); + // Set playback_source to FromQueue BEFORE calling backend + // to prevent race condition where watcher sees STOPPED before + // source is set, breaking auto-advance + self.set_playback_source(PlaybackSource::FromQueue); + self.lock_backend_for("play_from_queue").play_from_queue() } @@ -1843,8 +1860,9 @@ impl MusicRenderer { // 5. Replace the queue with shuffled items, starting at index 0 self.replace_queue(shuffled_items, Some(0))?; - // 6. Start playback from the first track - self.play_from_index(0)?; + // 6. Start playback from the first track with retry for JBL-like renderers + // that fail the first Play command due to timing issues + self.play_current_from_queue_with_retry()?; Ok(()) } diff --git a/pmocontrol/src/music_renderer/upnp_renderer.rs b/pmocontrol/src/music_renderer/upnp_renderer.rs index 11578c5a..ad57b3cc 100644 --- a/pmocontrol/src/music_renderer/upnp_renderer.rs +++ b/pmocontrol/src/music_renderer/upnp_renderer.rs @@ -218,9 +218,22 @@ impl QueueTransportControl for UpnpRenderer { // Détecte si l'URL est un flux continu en interrogeant le serveur HTTP let is_stream = crate::music_renderer::is_continuous_stream_url(&item.uri); *self.continuous_stream.lock().unwrap() = is_stream; + + // Log current queue state for debugging + let queue_state = { + let queue = self.queue.lock().unwrap(); + let idx = queue.current_index().unwrap_or(None); + let len = queue.len().unwrap_or(0); + let uri = item.uri.clone(); + let title = item.metadata.as_ref().and_then(|m| m.title.clone()); + (idx, len, uri, title) + }; tracing::debug!( - "UpnpRenderer play_from_queue: URI={}, continuous_stream={}", - item.uri, + "UpnpRenderer play_from_queue: index={:?}/{}, uri={}, title={:?}, continuous_stream={}", + queue_state.0, + queue_state.1, + queue_state.2, + queue_state.3, is_stream ); @@ -243,11 +256,28 @@ impl QueueTransportControl for UpnpRenderer { } fn play_next(&self) -> Result<(), ControlPointError> { + let current_idx = { + let queue = self.queue.lock().unwrap(); + let idx = queue.current_index().unwrap_or(None); + let len = queue.len().unwrap_or(0); + tracing::debug!( + current_index = ?idx, + queue_len = len, + "play_next: attempting to advance" + ); + idx + }; { let mut queue = self.queue.lock().unwrap(); if !queue.advance()? { return Err(ControlPointError::QueueError("No next track".into())); } + let new_idx = queue.current_index().unwrap_or(None); + tracing::debug!( + previous_index = ?current_idx, + new_index = ?new_idx, + "play_next: advanced" + ); } self.play_from_queue() @@ -269,6 +299,9 @@ impl QueueTransportControl for UpnpRenderer { let mut queue = self.queue.lock().unwrap(); queue.set_index(Some(index))?; } + // CORRECTIF: Quand on change l'index manuellement (shuffle, sélection d'un titre) + // on logue pour être sûr que c'est bien appelé + tracing::debug!(index = index, "✅ SHUFFLE / SEEK: play_from_index appelé"); self.play_from_queue() } diff --git a/version.txt b/version.txt index 0bdfd66f..5d8b11af 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.3.42 +0.3.45