Files
pmomusic/pmocontrol/src/registry.rs
2025-12-26 20:45:16 +01:00

216 lines
7.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::collections::HashMap;
use std::time::SystemTime;
use crate::avtransport_client::AvTransportClient;
use crate::connection_manager_client::ConnectionManagerClient;
use crate::media_server::{MediaServerInfo, ServerId};
use crate::model::{RendererId, RendererInfo};
use crate::rendering_control_client::RenderingControlClient;
use tracing::debug;
#[derive(Clone, Debug)]
enum DeviceKey {
Renderer(RendererId),
Server(ServerId),
}
#[derive(Debug, Default)]
pub struct DeviceRegistry {
renderers: HashMap<RendererId, RendererInfo>,
servers: HashMap<ServerId, MediaServerInfo>,
udn_index: HashMap<String, DeviceKey>,
}
/// Read-only view / trait for registry access.
///
/// Pour linstant, on ne rajoute pas AVTransport ici, on se contente
/// dajouter les helpers dans `impl DeviceRegistry`.
pub trait DeviceRegistryRead {
fn list_renderers(&self) -> Vec<RendererInfo>;
fn list_servers(&self) -> Vec<MediaServerInfo>;
fn get_renderer(&self, id: &RendererId) -> Option<RendererInfo>;
fn get_server(&self, id: &ServerId) -> Option<MediaServerInfo>;
}
impl DeviceRegistryRead for DeviceRegistry {
fn list_renderers(&self) -> Vec<RendererInfo> {
self.renderers.values().cloned().collect()
}
fn list_servers(&self) -> Vec<MediaServerInfo> {
self.servers.values().cloned().collect()
}
fn get_renderer(&self, id: &RendererId) -> Option<RendererInfo> {
self.renderers.get(id).cloned()
}
fn get_server(&self, id: &ServerId) -> Option<MediaServerInfo> {
self.servers.get(id).cloned()
}
}
#[derive(Debug)]
pub enum DeviceUpdate {
RendererOnline(RendererInfo),
RendererOfflineById(RendererId),
RendererOfflineByUdn(String),
ServerOnline(MediaServerInfo),
ServerOfflineById(ServerId),
ServerOfflineByUdn(String),
}
impl DeviceRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn apply_update(&mut self, update: DeviceUpdate) {
match update {
DeviceUpdate::RendererOnline(info) => {
let udn = info.udn.to_ascii_lowercase();
let id = info.id.clone();
let mut info = info;
info.online = true;
info.last_seen = SystemTime::now();
self.renderers.insert(id.clone(), info);
self.udn_index.insert(udn, DeviceKey::Renderer(id));
}
DeviceUpdate::RendererOfflineById(id) => {
if let Some(info) = self.renderers.get_mut(&id) {
info.online = false;
info.last_seen = SystemTime::now();
}
}
DeviceUpdate::RendererOfflineByUdn(udn) => {
let lookup = udn.to_ascii_lowercase();
if let Some(DeviceKey::Renderer(id)) = self.udn_index.get(&lookup) {
if let Some(info) = self.renderers.get_mut(id) {
info.online = false;
info.last_seen = SystemTime::now();
}
}
}
DeviceUpdate::ServerOnline(info) => {
let udn = info.udn.to_ascii_lowercase();
let id = info.id.clone();
let mut info = info;
info.online = true;
info.last_seen = SystemTime::now();
self.servers.insert(id.clone(), info);
self.udn_index.insert(udn, DeviceKey::Server(id));
}
DeviceUpdate::ServerOfflineById(id) => {
if let Some(info) = self.servers.get_mut(&id) {
info.online = false;
info.last_seen = SystemTime::now();
}
}
DeviceUpdate::ServerOfflineByUdn(udn) => {
let lookup = udn.to_ascii_lowercase();
if let Some(DeviceKey::Server(id)) = self.udn_index.get(&lookup) {
if let Some(info) = self.servers.get_mut(id) {
info.online = false;
info.last_seen = SystemTime::now();
}
}
}
}
}
/// Helper: get a renderer by UDN (case-insensitive, via udn_index).
pub fn get_renderer_by_udn(&self, udn: &str) -> Option<RendererInfo> {
let lookup = udn.to_ascii_lowercase();
match self.udn_index.get(&lookup) {
Some(DeviceKey::Renderer(id)) => self.renderers.get(id).cloned(),
_ => None,
}
}
/// Helper: get a server by UDN (case-insensitive, via udn_index).
pub fn get_server_by_udn(&self, udn: &str) -> Option<MediaServerInfo> {
let lookup = udn.to_ascii_lowercase();
match self.udn_index.get(&lookup) {
Some(DeviceKey::Server(id)) => self.servers.get(id).cloned(),
_ => None,
}
}
/// Construct an AvTransportClient for a given renderer id, if possible.
///
/// Returns:
/// - Some(client) if the renderer exists AND has avtransport_* fields set
/// - None if renderer not found or no AVTransport service.
pub fn avtransport_client_for_renderer(&self, id: &RendererId) -> Option<AvTransportClient> {
let info = self.renderers.get(id)?;
let service_type = info.avtransport_service_type.as_ref()?;
let control_url = info.avtransport_control_url.as_ref()?;
Some(AvTransportClient::new(
control_url.clone(),
service_type.clone(),
))
}
/// Construct an AvTransportClient for a given UDN, if possible.
pub fn avtransport_client_for_udn(&self, udn: &str) -> Option<AvTransportClient> {
let info = self.get_renderer_by_udn(udn)?;
let service_type = info.avtransport_service_type?;
let control_url = info.avtransport_control_url?;
Some(AvTransportClient::new(control_url, service_type))
}
/// Construct a RenderingControlClient for a given renderer id, if possible.
pub fn rendering_control_client_for_renderer(
&self,
id: &RendererId,
) -> Option<RenderingControlClient> {
let info = self.renderers.get(id)?;
let service_type = info.rendering_control_service_type.as_ref()?;
let control_url = info.rendering_control_control_url.as_ref()?;
Some(RenderingControlClient::new(
control_url.clone(),
service_type.clone(),
))
}
/// Construct a ConnectionManagerClient for a given renderer id, if possible.
pub fn connection_manager_client_for_renderer(
&self,
id: &RendererId,
) -> Option<ConnectionManagerClient> {
let info = self.renderers.get(id)?;
let service_type = info.connection_manager_service_type.as_ref()?;
let control_url = info.connection_manager_control_url.as_ref()?;
Some(ConnectionManagerClient::new(
control_url.clone(),
service_type.clone(),
))
}
pub fn mark_renderer_supports_set_next(&mut self, id: &RendererId) {
if let Some(info) = self.renderers.get_mut(id) {
if !info.capabilities.has_avtransport_set_next {
info.capabilities.has_avtransport_set_next = true;
debug!(
renderer = id.0.as_str(),
"Renderer now marked as supporting AVTransport.SetNextAVTransportURI"
);
}
}
}
}