Files
pmomusic/pmocontrol/src/registry.rs

216 lines
7.3 KiB
Rust
Raw Normal View History

2025-11-29 17:51:53 +01:00
use std::collections::HashMap;
use std::time::SystemTime;
2025-11-29 22:59:07 +01:00
use crate::avtransport_client::AvTransportClient;
2025-11-30 10:48:58 +01:00
use crate::connection_manager_client::ConnectionManagerClient;
use crate::media_server::{MediaServerInfo, ServerId};
use crate::model::{RendererId, RendererInfo};
2025-11-30 23:28:30 +01:00
use crate::rendering_control_client::RenderingControlClient;
use tracing::debug;
2025-11-29 17:51:53 +01:00
#[derive(Clone, Debug)]
enum DeviceKey {
Renderer(RendererId),
Server(ServerId),
2025-11-29 17:51:53 +01:00
}
#[derive(Debug, Default)]
pub struct DeviceRegistry {
renderers: HashMap<RendererId, RendererInfo>,
servers: HashMap<ServerId, MediaServerInfo>,
2025-11-29 17:51:53 +01:00
udn_index: HashMap<String, DeviceKey>,
}
2025-11-29 22:59:07 +01:00
/// Read-only view / trait for registry access.
///
/// Pour linstant, on ne rajoute pas AVTransport ici, on se contente
/// dajouter les helpers dans `impl DeviceRegistry`.
2025-11-29 17:51:53 +01:00
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>;
2025-11-29 17:51:53 +01:00
}
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> {
2025-11-29 17:51:53 +01:00
self.servers.get(id).cloned()
}
}
#[derive(Debug)]
pub enum DeviceUpdate {
RendererOnline(RendererInfo),
RendererOfflineById(RendererId),
RendererOfflineByUdn(String),
ServerOnline(MediaServerInfo),
ServerOfflineById(ServerId),
2025-11-29 17:51:53 +01:00
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();
}
}
}
}
}
2025-11-29 22:59:07 +01:00
/// 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,
}
}
2025-12-26 19:44:19 +01:00
/// 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,
}
}
2025-11-29 22:59:07 +01:00
/// 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.
2025-11-30 23:28:30 +01:00
pub fn avtransport_client_for_renderer(&self, id: &RendererId) -> Option<AvTransportClient> {
2025-11-29 22:59:07 +01:00
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))
}
2025-11-30 09:26:33 +01:00
/// 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(),
))
}
2025-11-30 10:48:58 +01:00
/// 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"
);
}
}
}
2025-11-29 17:51:53 +01:00
}