fix(Remote playback): kludge to scrub after stream move
This commit is contained in:
@@ -1249,6 +1249,59 @@ pub(super) fn get_queue_status(controller: &PlayerController) -> QueueStatus {
|
||||
}
|
||||
}
|
||||
|
||||
/// Start a freshly-built queue on the active remote session.
|
||||
///
|
||||
/// Used by the "play tracks"/"play album track" commands when we're in remote
|
||||
/// mode: instead of starting local MPV playback, we cast the selected tracks to
|
||||
/// the remote device. Mirrors PlaybackModeManager::transfer_to_remote's
|
||||
/// play_on_session call, but for a brand-new selection (so there's no resume
|
||||
/// position - playback starts from the chosen track's beginning).
|
||||
///
|
||||
/// Local-only items (no Jellyfin ID) can't be cast, so they're filtered out and
|
||||
/// the start index is adjusted to the remaining Jellyfin items. Returns an error
|
||||
/// if the selected track itself has no Jellyfin ID.
|
||||
async fn play_selection_on_remote(
|
||||
controller: &PlayerController,
|
||||
session_id: &str,
|
||||
media_items: &[MediaItem],
|
||||
start_index: usize,
|
||||
) -> Result<(), String> {
|
||||
// Collect Jellyfin IDs, tracking where the selected track lands after any
|
||||
// local-only items are dropped.
|
||||
let mut jellyfin_ids: Vec<String> = Vec::new();
|
||||
let mut adjusted_index: Option<usize> = None;
|
||||
for (i, item) in media_items.iter().enumerate() {
|
||||
if let Some(id) = item.jellyfin_id() {
|
||||
if i == start_index {
|
||||
adjusted_index = Some(jellyfin_ids.len());
|
||||
}
|
||||
jellyfin_ids.push(id.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let start_index = adjusted_index
|
||||
.ok_or("Cannot play on remote: selected track is not from Jellyfin")?;
|
||||
|
||||
if jellyfin_ids.is_empty() {
|
||||
return Err("Cannot play on remote: no Jellyfin tracks in selection".to_string());
|
||||
}
|
||||
|
||||
let client = {
|
||||
let client_arc = controller.jellyfin_client();
|
||||
let client_opt = client_arc.lock().map_err(|e| e.to_string())?;
|
||||
client_opt
|
||||
.as_ref()
|
||||
.ok_or("Jellyfin client not configured")?
|
||||
.clone()
|
||||
};
|
||||
|
||||
// Fresh selection: start from the beginning of the chosen track.
|
||||
client
|
||||
.play_on_session(session_id.to_string(), jellyfin_ids, start_index, None)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to start playback on remote session: {}", e))
|
||||
}
|
||||
|
||||
|
||||
/// Play a track from an album - backend fetches all album tracks and builds queue
|
||||
#[tauri::command]
|
||||
@@ -1258,6 +1311,7 @@ pub async fn player_play_album_track(
|
||||
session: State<'_, MediaSessionManagerWrapper>,
|
||||
db: State<'_, DatabaseWrapper>,
|
||||
repository_manager: State<'_, super::repository::RepositoryManagerWrapper>,
|
||||
playback_mode: State<'_, super::playback_mode::PlaybackModeManagerWrapper>,
|
||||
repository_handle: String,
|
||||
request: PlayAlbumTrackRequest,
|
||||
) -> Result<PlayerStatus, String> {
|
||||
@@ -1380,11 +1434,24 @@ pub async fn player_play_album_track(
|
||||
session_mgr.start_audio_session(first_item.clone());
|
||||
}
|
||||
|
||||
// Play the queue
|
||||
let controller = player.0.lock().await;
|
||||
controller
|
||||
.play_queue(media_items, start_index)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// When controlling a remote session, cast the selection there instead of
|
||||
// starting local MPV playback. We still load the queue locally (below) so
|
||||
// the queue/context stay in sync for the UI and for transferring back.
|
||||
let remote_session = match playback_mode.0.get_mode() {
|
||||
crate::playback_mode::PlaybackMode::Remote { session_id } => Some(session_id),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(session_id) = &remote_session {
|
||||
play_selection_on_remote(&controller, session_id, &media_items, start_index).await?;
|
||||
controller.set_queue(media_items, start_index).map_err(|e| e.to_string())?;
|
||||
} else {
|
||||
controller
|
||||
.play_queue(media_items, start_index)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
// Set the queue context for remote transfer
|
||||
{
|
||||
@@ -1426,6 +1493,7 @@ pub async fn player_play_tracks(
|
||||
session: State<'_, MediaSessionManagerWrapper>,
|
||||
db: State<'_, DatabaseWrapper>,
|
||||
repository_manager: State<'_, super::repository::RepositoryManagerWrapper>,
|
||||
playback_mode: State<'_, super::playback_mode::PlaybackModeManagerWrapper>,
|
||||
repository_handle: String,
|
||||
request: PlayTracksRequest,
|
||||
) -> Result<PlayerStatus, String> {
|
||||
@@ -1533,10 +1601,33 @@ pub async fn player_play_tracks(
|
||||
session_mgr.start_audio_session(first_item.clone());
|
||||
}
|
||||
|
||||
// Play queue
|
||||
let controller = player.0.lock().await;
|
||||
controller.play_queue_from(media_items, request.start_index, request.start_position)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// When controlling a remote session, cast the selection there instead of
|
||||
// starting local MPV playback. Skip this while a transfer is in flight: the
|
||||
// transfer-to-local path calls this command to load the queue locally and
|
||||
// the mode is still Remote until the transfer completes - routing it back to
|
||||
// the remote would undo the transfer.
|
||||
let remote_session = match playback_mode.0.get_mode() {
|
||||
crate::playback_mode::PlaybackMode::Remote { session_id }
|
||||
if !playback_mode.0.is_transferring() =>
|
||||
{
|
||||
Some(session_id)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(session_id) = &remote_session {
|
||||
play_selection_on_remote(&controller, session_id, &media_items, request.start_index)
|
||||
.await?;
|
||||
controller
|
||||
.set_queue(media_items, request.start_index)
|
||||
.map_err(|e| e.to_string())?;
|
||||
} else {
|
||||
controller
|
||||
.play_queue_from(media_items, request.start_index, request.start_position)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
// Set queue context
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user