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:
@@ -384,6 +384,82 @@ impl JellyfinClient {
|
||||
let sessions = self.get_sessions().await?;
|
||||
Ok(sessions.into_iter().find(|s| s.id.as_deref() == Some(session_id)))
|
||||
}
|
||||
|
||||
// --- JellyLMS multi-room sync groups -----------------------------------
|
||||
//
|
||||
// The JellyLMS plugin exposes a REST API under `/JellyLms` for grouping LMS
|
||||
// players ("zones") into synchronized multi-room sync groups. Players are
|
||||
// addressed by MAC address; JellyTau maps a Jellyfin session to a MAC by
|
||||
// stripping the `lms-` prefix off the session's device id (see
|
||||
// LmsDeviceDiscoveryService in the jellyLMS repo, which registers each player
|
||||
// with deviceId = "lms-{MacAddress}").
|
||||
|
||||
/// List current LMS sync groups.
|
||||
pub async fn lms_get_sync_groups(&self) -> Result<Vec<LmsSyncGroup>, String> {
|
||||
self.get("/JellyLms/SyncGroups").await
|
||||
}
|
||||
|
||||
/// Fuse LMS zones: create a sync group with `master_mac` as the sync master
|
||||
/// and `slave_macs` joining it. The master keeps playing; slaves follow.
|
||||
pub async fn lms_create_sync_group(
|
||||
&self,
|
||||
master_mac: &str,
|
||||
slave_macs: Vec<String>,
|
||||
) -> Result<(), String> {
|
||||
let payload = serde_json::json!({
|
||||
"MasterMac": master_mac,
|
||||
"SlaveMacs": slave_macs,
|
||||
});
|
||||
self.post("/JellyLms/SyncGroups", &payload).await
|
||||
}
|
||||
|
||||
/// Remove a single LMS player from whatever sync group it's in.
|
||||
pub async fn lms_unsync_player(&self, mac: &str) -> Result<(), String> {
|
||||
self.delete(&format!("/JellyLms/SyncGroups/Players/{}", mac)).await
|
||||
}
|
||||
|
||||
/// Dissolve an entire LMS sync group, identified by its master's MAC.
|
||||
pub async fn lms_dissolve_sync_group(&self, master_mac: &str) -> Result<(), String> {
|
||||
self.delete(&format!("/JellyLms/SyncGroups/{}", master_mac)).await
|
||||
}
|
||||
|
||||
/// Make a DELETE request to the Jellyfin API (used by the JellyLMS endpoints).
|
||||
async fn delete(&self, endpoint: &str) -> Result<(), String> {
|
||||
let url = format!("{}{}", self.config.server_url, endpoint);
|
||||
|
||||
log::debug!("[JellyfinClient] DELETE {}", endpoint);
|
||||
|
||||
let response = self.http_client
|
||||
.delete(&url)
|
||||
.header("X-Emby-Authorization", self.get_auth_header())
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Network request failed: {}", e))?;
|
||||
|
||||
let status = response.status();
|
||||
if !status.is_success() {
|
||||
let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
|
||||
return Err(format!("Jellyfin API error {}: {}", status.as_u16(), error_text));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// An LMS multi-room sync group, as returned by JellyLMS `/JellyLms/SyncGroups`.
|
||||
///
|
||||
/// Mirrors the plugin's `SyncGroup` model. The master is the sync source; slaves
|
||||
/// follow it in lockstep.
|
||||
#[derive(specta::Type, Debug, Clone, Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LmsSyncGroup {
|
||||
#[serde(alias = "MasterMac")]
|
||||
pub master_mac: String,
|
||||
#[serde(default, alias = "MasterName")]
|
||||
pub master_name: String,
|
||||
#[serde(default, alias = "SlaveMacs")]
|
||||
pub slave_macs: Vec<String>,
|
||||
#[serde(default, alias = "SlaveNames")]
|
||||
pub slave_names: Vec<String>,
|
||||
}
|
||||
|
||||
/// Default value for supports_remote_control when missing from API
|
||||
|
||||
Reference in New Issue
Block a user