🚀 Correctif bug shuffle et amélioration auto-advance

- Mise à jour de la version : `0.3.x` → `0.45`
- Correction critique du bug d'auto-skip après shuffle ou sélection manuelle : réinitialisation obligatoire de `has_played_flag` + positionnement explicite du playback source avant lancement
- Ajout de logs détaillés pour traçabilité des transitions (index, source)
- Gestion robustesse : retry automatique pour renderers JBL-like
- Meilleure synchronisation entre `play_from_index`, watcher et autoadvance
This commit is contained in:
2026-04-09 22:35:12 +02:00
parent 6fd11ab450
commit 5c6e2d43ac
5 changed files with 62 additions and 11 deletions

View File

@@ -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(())
}

View File

@@ -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()
}