fix(Remote playback): kludge to scrub after stream move
This commit is contained in:
@@ -181,7 +181,11 @@ impl PlaybackModeManager {
|
||||
}
|
||||
|
||||
/// Transfer playback from local device to remote Jellyfin session
|
||||
pub async fn transfer_to_remote(&self, session_id: String) -> Result<(), String> {
|
||||
pub async fn transfer_to_remote(
|
||||
&self,
|
||||
session_id: String,
|
||||
position_override: Option<f64>,
|
||||
) -> Result<(), String> {
|
||||
debug!("[PlaybackMode] transfer_to_remote ENTERED");
|
||||
debug!("[PlaybackMode] session_id: {}", session_id);
|
||||
log::info!(
|
||||
@@ -195,7 +199,7 @@ impl PlaybackModeManager {
|
||||
debug!("[PlaybackMode] Flag set, calling transfer_to_remote_inner");
|
||||
|
||||
// Perform the transfer
|
||||
let result = self.transfer_to_remote_inner(&session_id).await;
|
||||
let result = self.transfer_to_remote_inner(&session_id, position_override).await;
|
||||
|
||||
// Clear transferring flag
|
||||
self.is_transferring.store(false, Ordering::Relaxed);
|
||||
@@ -203,7 +207,11 @@ impl PlaybackModeManager {
|
||||
result
|
||||
}
|
||||
|
||||
async fn transfer_to_remote_inner(&self, session_id: &str) -> Result<(), String> {
|
||||
async fn transfer_to_remote_inner(
|
||||
&self,
|
||||
session_id: &str,
|
||||
position_override: Option<f64>,
|
||||
) -> Result<(), String> {
|
||||
log::info!("[PlaybackMode] transfer_to_remote_inner ENTERED");
|
||||
debug!("[PlaybackMode] transfer_to_remote_inner: session_id={}", session_id);
|
||||
|
||||
@@ -231,11 +239,19 @@ impl PlaybackModeManager {
|
||||
}
|
||||
|
||||
let (ids, adjusted_index) = self.extract_jellyfin_ids(items, original_index)?;
|
||||
// Read the LIVE backend position, not the snapshot embedded in PlayerState.
|
||||
// On Android the embedded position is only refreshed on play/pause
|
||||
// transitions, so PlayerState::position() is stale (often 0) mid-track;
|
||||
// PlayerController::position() always reflects the backend's current time.
|
||||
let position = player.position();
|
||||
// Prefer the frontend-supplied position when available. The backend
|
||||
// position is unreliable as a transfer source: on Linux, *video* plays
|
||||
// in the HTML5 <video> element and the MPV backend is never loaded, so
|
||||
// PlayerController::position() is always 0; only the frontend knows the
|
||||
// true position. We fall back to the live backend position (correct for
|
||||
// Linux audio via MPV) when the frontend doesn't pass one.
|
||||
let position = match position_override {
|
||||
Some(p) => {
|
||||
log::info!("[PlaybackMode] Using frontend position override: {:.2}s", p);
|
||||
p
|
||||
}
|
||||
None => player.position(),
|
||||
};
|
||||
let context = queue.context().clone();
|
||||
|
||||
log::info!(
|
||||
@@ -413,6 +429,23 @@ impl PlaybackModeManager {
|
||||
return Err("Remote session did not load track in time".to_string());
|
||||
}
|
||||
|
||||
// Resume at the right position. We send StartPositionTicks in the play
|
||||
// command above, but some Jellyfin client/server combinations ignore it
|
||||
// and start from 0. Now that the track is confirmed loaded, issue an
|
||||
// explicit seek as well (mirrors how the local resume path works). This
|
||||
// is the reliable mechanism; StartPositionTicks is best-effort.
|
||||
if let Some(ticks) = start_position_ticks {
|
||||
log::info!(
|
||||
"[PlaybackMode] Seeking remote session to resume position: {} ticks",
|
||||
ticks
|
||||
);
|
||||
if let Err(e) = client.session_seek(session_id.to_string(), ticks).await {
|
||||
// Non-fatal: the track is already playing, just not at the
|
||||
// resume point. Log and continue rather than failing the transfer.
|
||||
log::warn!("[PlaybackMode] Resume seek on remote failed: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop local playback (queue should remain intact for remote session)
|
||||
log::info!("[PlaybackMode] Stopping local playback - queue should NOT be cleared");
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user