From a143e394225f6ba312af0fde7680c983c9244d38 Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Sun, 4 Jan 2026 09:01:17 +0100 Subject: [PATCH] =?UTF-8?q?corrige=20un=20bug=20de=20d=C3=A9tection=20des?= =?UTF-8?q?=20servers=20on=20line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pmocontrol/src/discovery/upnp_discovery.rs | 26 +++++++++----- .../src/music_renderer/musicrenderer.rs | 14 ++++---- pmocontrol/src/registry.rs | 35 +++++++++++++++++++ 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/pmocontrol/src/discovery/upnp_discovery.rs b/pmocontrol/src/discovery/upnp_discovery.rs index 8cfbc46e..b39c52cf 100644 --- a/pmocontrol/src/discovery/upnp_discovery.rs +++ b/pmocontrol/src/discovery/upnp_discovery.rs @@ -12,7 +12,8 @@ pub struct UpnpDiscoveryManager { } impl UpnpDiscoveryManager { - pub fn new(device_registry: Arc>, + pub fn new( + device_registry: Arc>, udn_cache: Arc>, ) -> Self { Self { @@ -42,10 +43,15 @@ impl UpnpDiscoveryManager { if let Some(udn) = extract_udn_from_usn(&usn) { if alive { - // ✅ Check cache - if UDNRegistry::should_fetch(self.udn_cache.clone(), &udn, max_age as u64) { - // ✅ Fetch + parse - if let Ok(info) = ParsedDeviceDescription::new(&udn, &location, &server_header,5) { + // Check if we should fetch the full device description + let should_fetch = + UDNRegistry::should_fetch(self.udn_cache.clone(), &udn, max_age as u64); + + if should_fetch { + // Fetch + parse the device description + if let Ok(info) = + ParsedDeviceDescription::new(&udn, &location, &server_header, 5) + { if let Some(renderer_info) = info.build_renderer() { if let Ok(mut reg) = self.device_registry.write() { reg.push_renderer(&renderer_info, max_age); @@ -56,6 +62,13 @@ impl UpnpDiscoveryManager { } } } + } else { + // Even if we don't fetch, we MUST update last_seen to prevent timeout + // This is critical: SSDP Alive messages arrive more frequently than max_age/2, + // and we need to acknowledge them to keep the device online + if let Ok(mut reg) = self.device_registry.write() { + reg.refresh_device_presence(&udn, max_age); + } } } else { if let Ok(mut reg) = self.device_registry.write() { @@ -64,11 +77,8 @@ impl UpnpDiscoveryManager { } } } - - } - fn extract_udn_from_usn(usn: &str) -> Option { let lower = usn.trim().to_ascii_lowercase(); if let Some(idx) = lower.find("uuid:") { diff --git a/pmocontrol/src/music_renderer/musicrenderer.rs b/pmocontrol/src/music_renderer/musicrenderer.rs index 90a2600f..3ba8de50 100644 --- a/pmocontrol/src/music_renderer/musicrenderer.rs +++ b/pmocontrol/src/music_renderer/musicrenderer.rs @@ -130,46 +130,46 @@ impl MusicRenderer { } /// Returns the protocol. - fn protocol(&self) -> RendererProtocol { + pub fn protocol(&self) -> RendererProtocol { self.info.protocol() } - fn is_upnp(&self) -> bool { + pub fn is_upnp(&self) -> bool { match &*self.backend.lock().expect("Backend mutex poisoned") { MusicRendererBackend::Upnp(_) => true, _ => false, } } - fn is_openhome(&self) -> bool { + pub fn is_openhome(&self) -> bool { match &*self.backend.lock().expect("Backend mutex poisoned") { MusicRendererBackend::OpenHome(_) => true, _ => false, } } - fn is_linkplay(&self) -> bool { + pub fn is_linkplay(&self) -> bool { match &*self.backend.lock().expect("Backend mutex poisoned") { MusicRendererBackend::LinkPlay(_) => true, _ => false, } } - fn is_arylictcp(&self) -> bool { + pub fn is_arylictcp(&self) -> bool { match &*self.backend.lock().expect("Backend mutex poisoned") { MusicRendererBackend::ArylicTcp(_) => true, _ => false, } } - fn is_chromecast(&self) -> bool { + pub fn is_chromecast(&self) -> bool { match &*self.backend.lock().expect("Backend mutex poisoned") { MusicRendererBackend::Chromecast(_) => true, _ => false, } } - fn is_hybridupnparylic(&self) -> bool { + pub fn is_hybridupnparylic(&self) -> bool { match &*self.backend.lock().expect("Backend mutex poisoned") { MusicRendererBackend::HybridUpnpArylic { .. } => true, _ => false, diff --git a/pmocontrol/src/registry.rs b/pmocontrol/src/registry.rs index c36e8ce1..b6772f34 100644 --- a/pmocontrol/src/registry.rs +++ b/pmocontrol/src/registry.rs @@ -257,6 +257,41 @@ impl DeviceRegistry { } } + /// Updates the last_seen timestamp for a device without fetching its full description. + /// + /// This is critical for keeping devices online when SSDP Alive messages arrive + /// more frequently than the UDN cache refresh interval (max_age/2). + pub fn refresh_device_presence(&mut self, udn: &str, max_age: u32) { + let lookup = udn.to_ascii_lowercase(); + + if let Some(id) = self.udn_index.get(&lookup) { + if let Some(device) = self.devices.get(id) { + let was_online = device.is_online(); + device.has_been_seen_now(max_age); + + // If device was offline and came back online, broadcast Online event + if !was_online { + if device.is_a_music_renderer() { + if let Ok(renderer) = device.as_music_renderer() { + self.renderer_bus.broadcast(RendererEvent::Online { + id: id.clone(), + info: renderer.info().basic_info(), + }); + } + } + if device.is_a_music_server() { + if let Ok(server) = device.as_music_server() { + self.server_bus.broadcast(MediaServerEvent::Online { + server_id: id.clone(), + info: server.basic_info(), + }); + } + } + } + } + } + } + pub fn device_says_byebye(&mut self, udn: &str) { let lookup = udn.to_ascii_lowercase();