fix(remote playback): Move audio between remote players
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 3m49s
Traceability Validation / Check Requirement Traces (pull_request) Successful in 21s
🏗️ Build and Test JellyTau / Build Android APK (pull_request) Successful in 18m37s

This commit is contained in:
2026-06-25 23:39:01 +02:00
parent 6836ce79c8
commit 4634ed595c
4 changed files with 73 additions and 10 deletions
+59 -1
View File
@@ -215,8 +215,16 @@ impl PlaybackModeManager {
log::info!("[PlaybackMode] transfer_to_remote_inner ENTERED");
debug!("[PlaybackMode] transfer_to_remote_inner: session_id={}", session_id);
// If we're already controlling a remote session, that *old* session — not
// the idle local player — is the source of truth for the current track and
// position. Capture it so we can resume there and stop it afterwards.
let previous_remote_session = match self.get_mode() {
PlaybackMode::Remote { session_id: prev } if prev != session_id => Some(prev),
_ => None,
};
// Get current player state and queue context
let (queue_ids, current_index, position_seconds, queue_context) = {
let (queue_ids, mut current_index, mut position_seconds, queue_context) = {
log::info!("[PlaybackMode] Acquiring player controller lock...");
debug!("[PlaybackMode] Acquiring player controller lock...");
let player = self.player_controller.lock().await;
@@ -313,6 +321,46 @@ impl PlaybackModeManager {
}
};
// Remote -> remote switch: take the current track and position from the
// session we're leaving, since the local player is idle and reports 0.
if let Some(ref prev_session_id) = previous_remote_session {
log::info!(
"[PlaybackMode] Remote->remote switch; reading state from previous session {}",
prev_session_id
);
match client.get_session(prev_session_id).await {
Ok(Some(session)) => {
// Resume at the previous session's position.
if let Some(ticks) = session.play_state.as_ref().and_then(|ps| ps.position_ticks) {
position_seconds = ticks as f64 / TICKS_PER_SECOND;
log::info!(
"[PlaybackMode] Using previous remote position: {:.2}s",
position_seconds
);
}
// Resume on whichever track the previous session reached.
if let Some(now_id) = session.now_playing_item.as_ref().and_then(|i| i.id.as_deref()) {
if let Some(idx) = queue_ids.iter().position(|id| id == now_id) {
log::info!(
"[PlaybackMode] Previous session is on track {} (queue index {})",
now_id,
idx
);
current_index = idx;
} else {
log::warn!(
"[PlaybackMode] Previous session's track {} not found in queue; keeping index {}",
now_id,
current_index
);
}
}
}
Ok(None) => log::warn!("[PlaybackMode] Previous remote session not found while reading state"),
Err(e) => log::warn!("[PlaybackMode] Failed to read previous remote session: {}", e),
}
}
// Calculate position in ticks (from the live position read above)
let start_position_ticks = start_position_ticks_from_seconds(position_seconds);
@@ -446,6 +494,16 @@ impl PlaybackModeManager {
}
}
// Remote -> remote switch: stop the session we just left so we don't end
// up with two devices playing at once. Do this only after the new session
// is confirmed playing, so a failure here doesn't leave us with silence.
if let Some(prev_session_id) = previous_remote_session {
log::info!("[PlaybackMode] Stopping previous remote session {}", prev_session_id);
if let Err(e) = client.send_session_command(prev_session_id, "Stop").await {
log::warn!("[PlaybackMode] Failed to stop previous remote session: {}", e);
}
}
// Stop local playback (queue should remain intact for remote session)
log::info!("[PlaybackMode] Stopping local playback - queue should NOT be cleared");
{