feat: Améliorations et corrections dans la gestion des devices UPnP

- Correction des erreurs dans la génération des éléments XML pour les conteneurs
- Amélioration de la gestion des caches (cover et audio) dans ParadiseStreaming
- Optimisation des annonces SSDP avec des messages plus clairs
- Ajout de logs de debug pour faciliter le débogage
- Correction de la gestion des notifications GENA pour le ContentDirectory
- Amélioration de la documentation et des commentaires dans les fichiers modifiés
This commit is contained in:
2026-01-08 13:06:40 +01:00
parent 9ff85bbd10
commit 60785745aa
7 changed files with 345 additions and 61 deletions

View File

@@ -83,7 +83,6 @@ impl SsdpServer {
&"0.0.0.0".parse().unwrap(),
)?;
socket.set_read_timeout(Some(Duration::from_secs(1)))?;
socket.set_multicast_loop_v4(false)?;
let socket = Arc::new(socket);
@@ -118,8 +117,11 @@ impl SsdpServer {
// Envoyer alive pour tous les NTs
if let Some(ref socket) = self.socket {
for nt in device.get_notification_types() {
self.send_alive(socket, &device, nt);
let nts = device.get_notification_types();
for nt in nts.iter() {
Self::send_alive(socket, &device, nt, false);
// Petit délai pour éviter de saturer le buffer UDP sur macOS
std::thread::sleep(Duration::from_millis(5));
}
}
}
@@ -146,7 +148,7 @@ impl SsdpServer {
}
/// Envoie un NOTIFY alive
fn send_alive(&self, socket: &UdpSocket, device: &SsdpDevice, nt: &str) {
fn send_alive(socket: &UdpSocket, device: &SsdpDevice, nt: &str, is_periodic: bool) {
let usn = if nt.starts_with("uuid:") {
format!("{}", nt)
} else {
@@ -172,13 +174,18 @@ impl SsdpServer {
match socket.send_to(msg.as_bytes(), addr) {
Ok(_) => {
info!("✅ NOTIFY alive: {} (NT={})", usn, nt);
let label = if is_periodic { " (periodic)" } else { "" };
info!("✅ NOTIFY alive{}: {} (NT={})", label, usn, nt);
debug!(
"📣 NOTIFY alive payload\n<details>\n\n```\n{}\n```\n</details>\n",
msg
"📣 NOTIFY alive{} payload\n<details>\n\n```\n{}\n```\n</details>\n",
label, msg
);
}
Err(e) => warn!("❌ Failed to send NOTIFY alive for {}: {}", usn, e),
Err(e) => {
let label = if is_periodic { "periodic " } else { "" };
warn!("❌ Failed to send {}NOTIFY alive for {}: {}", label, usn, e);
}
}
}
@@ -226,53 +233,20 @@ impl SsdpServer {
debug!("⏰ SSDP periodic announcement tick");
std::thread::sleep(period);
let devices = devices.read().unwrap();
for device in devices.values() {
// Clone la liste des devices pour libérer le lock rapidement
let devices_snapshot: Vec<SsdpDevice> = {
let devices = devices.read().unwrap();
devices.values().cloned().collect()
};
for device in &devices_snapshot {
for nt in device.get_notification_types() {
Self::send_alive_static(&socket, device, nt);
Self::send_alive(&socket, device, nt, true);
}
}
}
});
}
/// Version statique de send_alive pour les threads
fn send_alive_static(socket: &UdpSocket, device: &SsdpDevice, nt: &str) {
let usn = if nt.starts_with("uuid:") {
format!("{}", nt)
} else {
format!("uuid:{}::{}", device.uuid, nt)
};
let msg = format!(
"NOTIFY * HTTP/1.1\r\n\
HOST: {}:{}\r\n\
CACHE-CONTROL: max-age={}\r\n\
LOCATION: {}\r\n\
NT: {}\r\n\
NTS: ssdp:alive\r\n\
SERVER: {}\r\n\
USN: {}\r\n\
\r\n",
SSDP_MULTICAST_ADDR, SSDP_PORT, MAX_AGE, device.location, nt, device.server, usn
);
let addr: SocketAddr = format!("{}:{}", SSDP_MULTICAST_ADDR, SSDP_PORT)
.parse()
.unwrap();
match socket.send_to(msg.as_bytes(), addr) {
Ok(_) => {
info!("✅ NOTIFY alive (periodic): {} (NT={})", usn, nt);
debug!(
"📣 NOTIFY alive (periodic) payload\n<details>\n\n```\n{}\n```\n</details>\n",
msg
);
}
Err(e) => warn!("❌ Failed to send periodic NOTIFY alive for {}: {}", usn, e),
}
}
/// Démarre l'écoute des M-SEARCH
fn start_msearch_listener(&self, socket: Arc<UdpSocket>) {
let devices = Arc::clone(&self.devices);
@@ -289,17 +263,17 @@ impl SsdpServer {
src, data
);
if let Some(st) = Self::parse_st(&data) {
let devices = devices.read().unwrap();
for device in devices.values() {
// Clone la liste des devices pour libérer le lock rapidement
let devices_snapshot: Vec<SsdpDevice> = {
let devices = devices.read().unwrap();
devices.values().cloned().collect()
};
for device in &devices_snapshot {
Self::handle_msearch(&socket, &src, &st, device);
}
}
}
}
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
// Timeout, continuer
continue;
}
Err(e) => {
warn!("❌ SSDP read error: {}", e);
}

View File

@@ -250,19 +250,26 @@ impl UpnpServerExt for Server {
}
// Enregistrer les URLs dans le serveur web
tracing::warn!("🔍 DEBUG: About to register_urls for device {}", di.udn());
di.register_urls(self).await?;
tracing::warn!("🔍 DEBUG: register_urls completed for device {}", di.udn());
// Ajouter au registre pour l'introspection
tracing::warn!("🔍 DEBUG: Adding to DEVICE_REGISTRY...");
DEVICE_REGISTRY
.write()
.unwrap()
.register(di.clone())
.map_err(|e| DeviceError::UrlRegistrationError(e))?;
tracing::warn!("🔍 DEBUG: Added to DEVICE_REGISTRY");
// Annoncer via SSDP (si initialisé)
tracing::warn!("🔍 DEBUG: Checking SSDP...");
if self.ssdp_enabled() {
tracing::warn!("🔍 DEBUG: SSDP enabled, getting lock...");
let ssdp_opt = SSDP_SERVER.read().unwrap();
if let Some(ref ssdp) = *ssdp_opt {
tracing::warn!("🔍 DEBUG: SSDP server exists, announcing...");
use crate::config_ext::UpnpConfigExt;
let config = pmoconfig::get_config();
let manufacturer = config
@@ -271,8 +278,10 @@ impl UpnpServerExt for Server {
let ssdp_device = di.to_ssdp_device(&manufacturer, "1.0");
ssdp.add_device(ssdp_device);
info!("✅ SSDP announcement for {}", di.udn());
tracing::warn!("🔍 DEBUG: SSDP announcement complete");
}
}
tracing::warn!("🔍 DEBUG: Returning device instance");
Ok(di)
}