Add support for fusing/unfusing JellyLMS zones into synchronized

multi-room groups, addressed by MAC (derived from the `lms-{mac}` device id).
This commit is contained in:
2026-06-26 19:27:37 +02:00
parent ff8f35084b
commit f1d25c4f4d
10 changed files with 582 additions and 6 deletions
+16
View File
@@ -51,6 +51,22 @@ pub async fn playback_mode_transfer_to_remote(
manager.0.transfer_to_remote(session_id, position).await
}
/// Set the transferring flag on the playback mode manager.
///
/// Used by the frontend remote->local flow to mark the whole two-step sequence
/// as a transfer, so `player_play_tracks` starts LOCAL playback instead of
/// casting back to the remote session it's leaving. Always pair `true` with a
/// later `false` (including on error) so the flag can't stick.
#[tauri::command]
#[specta::specta]
pub async fn playback_mode_set_transferring(
manager: State<'_, PlaybackModeManagerWrapper>,
transferring: bool,
) -> Result<(), String> {
manager.0.set_transferring(transferring);
Ok(())
}
/// Transfer playback from remote session back to local device
///
/// Parameters:
+90
View File
@@ -6,6 +6,7 @@
use tauri::State;
use super::PlayerStateWrapper;
use crate::jellyfin::client::LmsSyncGroup;
/// Play items on a remote Jellyfin session (casting)
#[tauri::command]
@@ -129,3 +130,92 @@ pub async fn remote_session_toggle_mute(
Err("Jellyfin client not configured".to_string())
}
}
// --- JellyLMS multi-room sync groups (fuse / unfuse LMS zones) --------------
//
// The frontend addresses LMS players by MAC address, which it derives from a
// session's device id (`lms-{mac}`). These commands forward to the JellyLMS
// plugin REST API via the configured JellyfinClient.
/// List current LMS sync groups.
#[tauri::command]
#[specta::specta]
pub async fn lms_get_sync_groups(
player: State<'_, PlayerStateWrapper>,
) -> Result<Vec<LmsSyncGroup>, String> {
let client_opt = {
let controller = player.0.lock().await;
controller.jellyfin_client().lock().map_err(|e| e.to_string())?.clone()
};
if let Some(client) = client_opt {
client.lms_get_sync_groups().await
} else {
Err("Jellyfin client not configured".to_string())
}
}
/// Fuse LMS zones into a sync group. `master_mac` keeps playing and the
/// `slave_macs` zones join it in sync.
#[tauri::command]
#[specta::specta]
pub async fn lms_create_sync_group(
player: State<'_, PlayerStateWrapper>,
master_mac: String,
slave_macs: Vec<String>,
) -> Result<(), String> {
log::info!("[LmsSync] Fusing zones: master={}, slaves={:?}", master_mac, slave_macs);
let client_opt = {
let controller = player.0.lock().await;
controller.jellyfin_client().lock().map_err(|e| e.to_string())?.clone()
};
if let Some(client) = client_opt {
client.lms_create_sync_group(&master_mac, slave_macs).await
} else {
Err("Jellyfin client not configured".to_string())
}
}
/// Remove a single LMS zone from its sync group (decouple one player).
#[tauri::command]
#[specta::specta]
pub async fn lms_unsync_player(
player: State<'_, PlayerStateWrapper>,
mac: String,
) -> Result<(), String> {
log::info!("[LmsSync] Decoupling zone {}", mac);
let client_opt = {
let controller = player.0.lock().await;
controller.jellyfin_client().lock().map_err(|e| e.to_string())?.clone()
};
if let Some(client) = client_opt {
client.lms_unsync_player(&mac).await
} else {
Err("Jellyfin client not configured".to_string())
}
}
/// Dissolve an entire LMS sync group, identified by its master's MAC.
#[tauri::command]
#[specta::specta]
pub async fn lms_dissolve_sync_group(
player: State<'_, PlayerStateWrapper>,
master_mac: String,
) -> Result<(), String> {
log::info!("[LmsSync] Dissolving group with master {}", master_mac);
let client_opt = {
let controller = player.0.lock().await;
controller.jellyfin_client().lock().map_err(|e| e.to_string())?.clone()
};
if let Some(client) = client_opt {
client.lms_dissolve_sync_group(&master_mac).await
} else {
Err("Jellyfin client not configured".to_string())
}
}