222 lines
7.0 KiB
Rust
222 lines
7.0 KiB
Rust
//! 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<String>,
|
|
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<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())
|
|
}
|
|
}
|