//! Remote Jellyfin session control commands (casting to another device). //! //! These thin command adapters forward control actions to the active Jellyfin //! session via the player's configured `JellyfinClient`. use tauri::State; use super::PlayerStateWrapper; use crate::jellyfin::client::LmsSyncGroup; /// Play items on a remote Jellyfin session (casting) #[tauri::command] #[specta::specta] pub async fn remote_play_on_session( player: State<'_, PlayerStateWrapper>, session_id: String, item_ids: Vec, start_index: usize, ) -> Result<(), String> { log::info!("[RemoteSession] Playing {} items on session {} (start index: {})", item_ids.len(), session_id, start_index); log::info!("[RemoteSession] Item IDs: {:?}", item_ids); 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 { log::info!("[RemoteSession] Jellyfin client IS configured, calling play_on_session"); client.play_on_session(session_id, item_ids, start_index, None).await?; log::info!("[RemoteSession] Successfully started playback on remote session"); Ok(()) } else { log::error!("[RemoteSession] Jellyfin client is NOT configured! User needs to log out/in or restart app"); Err("Jellyfin client not configured - please restart the app or log out and log back in".to_string()) } } /// Send a playback command to a remote session #[tauri::command] #[specta::specta] pub async fn remote_send_command( player: State<'_, PlayerStateWrapper>, session_id: String, command: String, ) -> Result<(), String> { log::info!("[RemoteSession] Sending command '{}' to session {}", command, session_id); 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.send_session_command(session_id, &command).await?; log::info!("[RemoteSession] Command sent successfully"); Ok(()) } else { Err("Jellyfin client not configured".to_string()) } } /// Seek on a remote session #[tauri::command] #[specta::specta] pub async fn remote_session_seek( player: State<'_, PlayerStateWrapper>, session_id: String, position_ticks: i64, ) -> Result<(), String> { log::info!("[RemoteSession] Seeking to {} ticks on session {}", position_ticks, session_id); 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.session_seek(session_id, position_ticks).await?; log::info!("[RemoteSession] Seek successful"); Ok(()) } else { Err("Jellyfin client not configured".to_string()) } } /// Set volume on a remote session #[tauri::command] #[specta::specta] pub async fn remote_session_set_volume( player: State<'_, PlayerStateWrapper>, session_id: String, volume: i32, ) -> Result<(), String> { log::info!("[RemoteSession] Setting volume to {} on session {}", volume, session_id); 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.session_set_volume(session_id, volume).await?; log::info!("[RemoteSession] Volume set successfully"); Ok(()) } else { Err("Jellyfin client not configured".to_string()) } } /// Toggle mute on a remote session #[tauri::command] #[specta::specta] pub async fn remote_session_toggle_mute( player: State<'_, PlayerStateWrapper>, session_id: String, ) -> Result<(), String> { log::info!("[RemoteSession] Toggling mute on session {}", session_id); 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.session_toggle_mute(session_id).await?; log::info!("[RemoteSession] Mute toggled successfully"); Ok(()) } else { 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, 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, ) -> 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()) } }