🚀 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:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -4,7 +4,7 @@ version = 4
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "PMOMusic"
|
name = "PMOMusic"
|
||||||
version = "0.3.42"
|
version = "0.3.44"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum 0.8.7",
|
"axum 0.8.7",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "PMOMusic"
|
name = "PMOMusic"
|
||||||
version = "0.3.42"
|
version = "0.3.45"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -1129,14 +1129,21 @@ impl MusicRenderer {
|
|||||||
|
|
||||||
/// Play from a specific index in the queue.
|
/// Play from a specific index in the queue.
|
||||||
pub fn play_from_index(&self, index: usize) -> Result<(), ControlPointError> {
|
pub fn play_from_index(&self, index: usize) -> Result<(), ControlPointError> {
|
||||||
// Reset the has_played flag before starting playback to prevent
|
// ✅ CORRECTIF BUG SHUFFLE: Quand on change d'index manuellement
|
||||||
// auto-advance on transient STOPPED states during track initialization.
|
// (par exemple shuffle, clique sur un titre), on réinitialise OBLIGATOIREMENT
|
||||||
// The flag will be set back to true when PLAYING state is detected.
|
// 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.clear_has_played_flag();
|
||||||
|
|
||||||
self.lock_backend_for("play_from_index")
|
self.lock_backend_for("play_from_index")
|
||||||
.play_from_index(index)?;
|
.play_from_index(index)?;
|
||||||
self.emit_queue_updated();
|
|
||||||
|
self.set_playback_source(PlaybackSource::FromQueue);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1159,6 +1166,11 @@ impl MusicRenderer {
|
|||||||
let queue_not_empty = backend.len().unwrap_or(0) > 0;
|
let queue_not_empty = backend.len().unwrap_or(0) > 0;
|
||||||
|
|
||||||
if queue_not_empty {
|
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é)
|
// 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.)
|
// Cela fonctionne pour tous les backends (UPnP interne, OpenHome, etc.)
|
||||||
backend.play_from_queue()
|
backend.play_from_queue()
|
||||||
@@ -1624,6 +1636,11 @@ impl MusicRenderer {
|
|||||||
// The flag will be set back to true when PLAYING state is detected.
|
// The flag will be set back to true when PLAYING state is detected.
|
||||||
self.clear_has_played_flag();
|
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()
|
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
|
// 5. Replace the queue with shuffled items, starting at index 0
|
||||||
self.replace_queue(shuffled_items, Some(0))?;
|
self.replace_queue(shuffled_items, Some(0))?;
|
||||||
|
|
||||||
// 6. Start playback from the first track
|
// 6. Start playback from the first track with retry for JBL-like renderers
|
||||||
self.play_from_index(0)?;
|
// that fail the first Play command due to timing issues
|
||||||
|
self.play_current_from_queue_with_retry()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,9 +218,22 @@ impl QueueTransportControl for UpnpRenderer {
|
|||||||
// Détecte si l'URL est un flux continu en interrogeant le serveur HTTP
|
// 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);
|
let is_stream = crate::music_renderer::is_continuous_stream_url(&item.uri);
|
||||||
*self.continuous_stream.lock().unwrap() = is_stream;
|
*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!(
|
tracing::debug!(
|
||||||
"UpnpRenderer play_from_queue: URI={}, continuous_stream={}",
|
"UpnpRenderer play_from_queue: index={:?}/{}, uri={}, title={:?}, continuous_stream={}",
|
||||||
item.uri,
|
queue_state.0,
|
||||||
|
queue_state.1,
|
||||||
|
queue_state.2,
|
||||||
|
queue_state.3,
|
||||||
is_stream
|
is_stream
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -243,11 +256,28 @@ impl QueueTransportControl for UpnpRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn play_next(&self) -> Result<(), ControlPointError> {
|
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();
|
let mut queue = self.queue.lock().unwrap();
|
||||||
if !queue.advance()? {
|
if !queue.advance()? {
|
||||||
return Err(ControlPointError::QueueError("No next track".into()));
|
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()
|
self.play_from_queue()
|
||||||
@@ -269,6 +299,9 @@ impl QueueTransportControl for UpnpRenderer {
|
|||||||
let mut queue = self.queue.lock().unwrap();
|
let mut queue = self.queue.lock().unwrap();
|
||||||
queue.set_index(Some(index))?;
|
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()
|
self.play_from_queue()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
0.3.42
|
0.3.45
|
||||||
|
|||||||
Reference in New Issue
Block a user