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:
@@ -71,18 +71,22 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.await
|
||||
.expect("Failed to register MediaRenderer");
|
||||
|
||||
info!(
|
||||
"✅ MediaRenderer ready at {}{}",
|
||||
renderer_instance.base_url(),
|
||||
renderer_instance.description_route()
|
||||
);
|
||||
tracing::warn!("🔍 DEBUG: MediaRenderer registered, getting base_url...");
|
||||
let base_url = renderer_instance.base_url();
|
||||
tracing::warn!("🔍 DEBUG: Got base_url, getting description_route...");
|
||||
let desc_route = renderer_instance.description_route();
|
||||
tracing::warn!("🔍 DEBUG: Got description_route, logging...");
|
||||
info!("✅ MediaRenderer ready at {}{}", base_url, desc_route);
|
||||
tracing::warn!("🔍 DEBUG: Log complete!");
|
||||
|
||||
tracing::warn!("🔍 DEBUG: About to register MediaServer...");
|
||||
let server_instance = server
|
||||
.write()
|
||||
.await
|
||||
.register_device(MEDIA_SERVER.clone())
|
||||
.await
|
||||
.expect("Failed to register MediaServer");
|
||||
tracing::warn!("🔍 DEBUG: MediaServer registered successfully");
|
||||
|
||||
// Enregistrer l'instance ContentDirectory pour les notifications GENA
|
||||
if let Some(cd_service) = server_instance.get_service("ContentDirectory") {
|
||||
|
||||
@@ -645,10 +645,10 @@ fn map_didl_entries(xml: &str) -> Result<Vec<MediaEntry>, ControlPointError> {
|
||||
is_container: true,
|
||||
class: container.class,
|
||||
resources: Vec::new(),
|
||||
artist: None,
|
||||
artist: container.artist,
|
||||
album: None,
|
||||
genre: None,
|
||||
album_art_uri: None,
|
||||
album_art_uri: container.album_art,
|
||||
date: None,
|
||||
track_number: None,
|
||||
creator: None,
|
||||
|
||||
@@ -665,6 +665,15 @@ impl ToXmlElement for Container {
|
||||
elem.children
|
||||
.push(XMLNode::Element(text_element("upnp:class", &self.class)));
|
||||
|
||||
if let Some(ref artist) = self.artist {
|
||||
elem.children
|
||||
.push(XMLNode::Element(text_element("upnp:artist", artist)));
|
||||
}
|
||||
if let Some(ref art) = self.album_art {
|
||||
elem.children
|
||||
.push(XMLNode::Element(text_element("upnp:albumArtURI", art)));
|
||||
}
|
||||
|
||||
for c in &self.containers {
|
||||
elem.children.push(XMLNode::Element(c.to_xml_element()));
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ impl ParadiseStreamingExt for pmoserver::Server {
|
||||
|
||||
// Récupérer ou initialiser les caches singletons
|
||||
info!("📦 Getting cache singletons...");
|
||||
tracing::warn!("🔍 DEBUG: About to check get_cover_cache()");
|
||||
let cover_cache = match get_cover_cache() {
|
||||
Some(cache) => {
|
||||
info!(" ✅ Using existing cover cache singleton");
|
||||
@@ -82,14 +83,17 @@ impl ParadiseStreamingExt for pmoserver::Server {
|
||||
}
|
||||
None => {
|
||||
info!(" 📦 Initializing new cover cache singleton");
|
||||
tracing::warn!("🔍 DEBUG: About to call init_cover_cache_configured()");
|
||||
let cache = self
|
||||
.init_cover_cache_configured()
|
||||
.await
|
||||
.context("Failed to initialize cover cache")?;
|
||||
tracing::warn!("🔍 DEBUG: init_cover_cache_configured() completed");
|
||||
register_cover_cache(cache.clone());
|
||||
cache
|
||||
}
|
||||
};
|
||||
tracing::warn!("🔍 DEBUG: Cover cache ready, checking audio cache...");
|
||||
|
||||
let audio_cache = match get_audio_cache() {
|
||||
Some(cache) => {
|
||||
@@ -100,15 +104,18 @@ impl ParadiseStreamingExt for pmoserver::Server {
|
||||
}
|
||||
None => {
|
||||
info!(" 📦 Initializing new audio cache singleton");
|
||||
tracing::warn!("🔍 DEBUG: About to call init_audio_cache_configured()");
|
||||
let cache = self
|
||||
.init_audio_cache_configured()
|
||||
.await
|
||||
.context("Failed to initialize audio cache")?;
|
||||
tracing::warn!("🔍 DEBUG: init_audio_cache_configured() completed");
|
||||
register_audio_cache(cache.clone());
|
||||
register_playlist_audio_cache(cache.clone());
|
||||
cache
|
||||
}
|
||||
};
|
||||
tracing::warn!("🔍 DEBUG: Audio cache ready, creating ParadiseChannelManager...");
|
||||
|
||||
// Créer le builder d'historique
|
||||
let mut history_builder = ParadiseHistoryBuilder::default();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
281
toto
Normal file
281
toto
Normal file
@@ -0,0 +1,281 @@
|
||||
Modified regular file PMOMusic/src/main.rs:
|
||||
...
|
||||
71 71: .await
|
||||
72 72: .expect("Failed to register MediaRenderer");
|
||||
73 73:
|
||||
74 : info!(
|
||||
75 : "✅ MediaRenderer ready at {}{}",
|
||||
76 : renderer_instance.base_url(),
|
||||
77 : renderer_instance.description_route()
|
||||
78 : );
|
||||
74: tracing::warn!("🔍 DEBUG: MediaRenderer registered, getting base_url...");
|
||||
75: let base_url = renderer_instance.base_url();
|
||||
76: tracing::warn!("🔍 DEBUG: Got base_url, getting description_route...");
|
||||
77: let desc_route = renderer_instance.description_route();
|
||||
78: tracing::warn!("🔍 DEBUG: Got description_route, logging...");
|
||||
79: info!("✅ MediaRenderer ready at {}{}", base_url, desc_route);
|
||||
80: tracing::warn!("🔍 DEBUG: Log complete!");
|
||||
79 81:
|
||||
82: tracing::warn!("🔍 DEBUG: About to register MediaServer...");
|
||||
80 83: let server_instance = server
|
||||
81 84: .write()
|
||||
82 85: .await
|
||||
83 86: .register_device(MEDIA_SERVER.clone())
|
||||
84 87: .await
|
||||
85 88: .expect("Failed to register MediaServer");
|
||||
89: tracing::warn!("🔍 DEBUG: MediaServer registered successfully");
|
||||
86 90:
|
||||
87 91: // Enregistrer l'instance ContentDirectory pour les notifications GENA
|
||||
88 92: if let Some(cd_service) = server_instance.get_service("ContentDirectory") {
|
||||
...
|
||||
Modified regular file pmocontrol/src/media_server.rs:
|
||||
...
|
||||
645 645: is_container: true,
|
||||
646 646: class: container.class,
|
||||
647 647: resources: Vec::new(),
|
||||
648 648: artist: Nonecontainer.artist,
|
||||
649 649: album: None,
|
||||
650 650: genre: None,
|
||||
651 651: album_art_uri: Nonecontainer.album_art,
|
||||
652 652: date: None,
|
||||
653 653: track_number: None,
|
||||
654 654: creator: None,
|
||||
...
|
||||
Modified regular file pmodidl/src/lib.rs:
|
||||
...
|
||||
665 665: elem.children
|
||||
666 666: .push(XMLNode::Element(text_element("upnp:class", &self.class)));
|
||||
667 667:
|
||||
668: if let Some(ref artist) = self.artist {
|
||||
669: elem.children
|
||||
670: .push(XMLNode::Element(text_element("upnp:artist", artist)));
|
||||
671: }
|
||||
672: if let Some(ref art) = self.album_art {
|
||||
673: elem.children
|
||||
674: .push(XMLNode::Element(text_element("upnp:albumArtURI", art)));
|
||||
675: }
|
||||
676:
|
||||
668 677: for c in &self.containers {
|
||||
669 678: elem.children.push(XMLNode::Element(c.to_xml_element()));
|
||||
670 679: }
|
||||
...
|
||||
Modified regular file pmomediaserver/src/paradise_streaming.rs:
|
||||
...
|
||||
75 75:
|
||||
76 76: // Récupérer ou initialiser les caches singletons
|
||||
77 77: info!("📦 Getting cache singletons...");
|
||||
78: tracing::warn!("🔍 DEBUG: About to check get_cover_cache()");
|
||||
78 79: let cover_cache = match get_cover_cache() {
|
||||
79 80: Some(cache) => {
|
||||
80 81: info!(" ✅ Using existing cover cache singleton");
|
||||
81 82: cache
|
||||
82 83: }
|
||||
83 84: None => {
|
||||
84 85: info!(" 📦 Initializing new cover cache singleton");
|
||||
86: tracing::warn!("🔍 DEBUG: About to call init_cover_cache_configured()");
|
||||
85 87: let cache = self
|
||||
86 88: .init_cover_cache_configured()
|
||||
87 89: .await
|
||||
88 90: .context("Failed to initialize cover cache")?;
|
||||
91: tracing::warn!("🔍 DEBUG: init_cover_cache_configured() completed");
|
||||
89 92: register_cover_cache(cache.clone());
|
||||
90 93: cache
|
||||
91 94: }
|
||||
92 95: };
|
||||
96: tracing::warn!("🔍 DEBUG: Cover cache ready, checking audio cache...");
|
||||
93 97:
|
||||
94 98: let audio_cache = match get_audio_cache() {
|
||||
95 99: Some(cache) => {
|
||||
...
|
||||
100 104: }
|
||||
101 105: None => {
|
||||
102 106: info!(" 📦 Initializing new audio cache singleton");
|
||||
107: tracing::warn!("🔍 DEBUG: About to call init_audio_cache_configured()");
|
||||
103 108: let cache = self
|
||||
104 109: .init_audio_cache_configured()
|
||||
105 110: .await
|
||||
106 111: .context("Failed to initialize audio cache")?;
|
||||
112: tracing::warn!("🔍 DEBUG: init_audio_cache_configured() completed");
|
||||
107 113: register_audio_cache(cache.clone());
|
||||
108 114: register_playlist_audio_cache(cache.clone());
|
||||
109 115: cache
|
||||
110 116: }
|
||||
111 117: };
|
||||
118: tracing::warn!("🔍 DEBUG: Audio cache ready, creating ParadiseChannelManager...");
|
||||
112 119:
|
||||
113 120: // Créer le builder d'historique
|
||||
114 121: let mut history_builder = ParadiseHistoryBuilder::default();
|
||||
...
|
||||
Modified regular file pmoupnp/src/ssdp/server.rs:
|
||||
...
|
||||
83 83: &"0.0.0.0".parse().unwrap(),
|
||||
84 84: )?;
|
||||
85 85:
|
||||
86 : socket.set_read_timeout(Some(Duration::from_secs(1)))?;
|
||||
87 86: socket.set_multicast_loop_v4(false)?;
|
||||
88 87:
|
||||
89 88: let socket = Arc::new(socket);
|
||||
...
|
||||
118 117:
|
||||
119 118: // Envoyer alive pour tous les NTs
|
||||
120 119: if let Some(ref socket) = self.socket {
|
||||
121 : for nt in device.get_notification_types() {
|
||||
122 : self.send_alive(socket, &device, nt);
|
||||
120: let nts = device.get_notification_types();
|
||||
121: for nt in nts.iter() {
|
||||
122: Self::send_alive(socket, &device, nt, false);
|
||||
123: // Petit délai pour éviter de saturer le buffer UDP sur macOS
|
||||
124: std::thread::sleep(Duration::from_millis(5));
|
||||
123 125: }
|
||||
124 126: }
|
||||
125 127: }
|
||||
...
|
||||
146 148: }
|
||||
147 149:
|
||||
148 150: /// Envoie un NOTIFY alive
|
||||
149 151: fn send_alive(&self, socket: &UdpSocket, device: &SsdpDevice, nt: &str, is_periodic: bool) {
|
||||
150 152: let usn = if nt.starts_with("uuid:") {
|
||||
151 153: format!("{}", nt)
|
||||
152 154: } else {
|
||||
...
|
||||
172 174:
|
||||
173 175: match socket.send_to(msg.as_bytes(), addr) {
|
||||
174 176: Ok(_) => {
|
||||
175 177: let label = if is_periodic { " (periodic)" } else { "" };
|
||||
175 178: info!("✅ NOTIFY alive{}: {} (NT={})", label, usn, nt);
|
||||
176 179: debug!(
|
||||
177 180: "📣 NOTIFY alive{} payload\n<details>\n\n```\n{}\n```\n</details>\n",
|
||||
178 181: label, msg
|
||||
179 182: );
|
||||
180 183: }
|
||||
184:
|
||||
181 185: Err(e) => {
|
||||
186: let label = if is_periodic { "periodic " } else { "" };
|
||||
181 187: warn!("❌ Failed to send {}NOTIFY alive for {}: {}", label, usn, e),;
|
||||
181 188: }
|
||||
182 189: }
|
||||
183 190: }
|
||||
184 191:
|
||||
...
|
||||
226 233: debug!("⏰ SSDP periodic announcement tick");
|
||||
227 234: std::thread::sleep(period);
|
||||
228 235:
|
||||
229 236: // Clone la liste des devices pour libérer le lock rapidement
|
||||
237: let devices_snapshot: Vec<SsdpDevice> = {
|
||||
229 238: let devices = devices.read().unwrap();
|
||||
230 239: devices.values().cloned().collect()
|
||||
240: };
|
||||
230 241: for device in devices.values()&devices_snapshot {
|
||||
231 242: for nt in device.get_notification_types() {
|
||||
232 243: Self::send_alive_staticsend_alive(&socket, device, nt, true);
|
||||
233 244: }
|
||||
234 245: }
|
||||
235 246: }
|
||||
236 247: });
|
||||
237 248: }
|
||||
238 249:
|
||||
239 : /// Version statique de send_alive pour les threads
|
||||
240 : fn send_alive_static(socket: &UdpSocket, device: &SsdpDevice, nt: &str) {
|
||||
241 : let usn = if nt.starts_with("uuid:") {
|
||||
242 : format!("{}", nt)
|
||||
243 : } else {
|
||||
244 : format!("uuid:{}::{}", device.uuid, nt)
|
||||
245 : };
|
||||
246 :
|
||||
247 : let msg = format!(
|
||||
248 : "NOTIFY * HTTP/1.1\r\n\
|
||||
249 : HOST: {}:{}\r\n\
|
||||
250 : CACHE-CONTROL: max-age={}\r\n\
|
||||
251 : LOCATION: {}\r\n\
|
||||
252 : NT: {}\r\n\
|
||||
253 : NTS: ssdp:alive\r\n\
|
||||
254 : SERVER: {}\r\n\
|
||||
255 : USN: {}\r\n\
|
||||
256 : \r\n",
|
||||
257 : SSDP_MULTICAST_ADDR, SSDP_PORT, MAX_AGE, device.location, nt, device.server, usn
|
||||
258 : );
|
||||
259 :
|
||||
260 : let addr: SocketAddr = format!("{}:{}", SSDP_MULTICAST_ADDR, SSDP_PORT)
|
||||
261 : .parse()
|
||||
262 : .unwrap();
|
||||
263 :
|
||||
264 : match socket.send_to(msg.as_bytes(), addr) {
|
||||
265 : Ok(_) => {
|
||||
266 : info!("✅ NOTIFY alive (periodic): {} (NT={})", usn, nt);
|
||||
267 : debug!(
|
||||
268 : "📣 NOTIFY alive (periodic) payload\n<details>\n\n```\n{}\n```\n</details>\n",
|
||||
269 : msg
|
||||
270 : );
|
||||
271 : }
|
||||
272 : Err(e) => warn!("❌ Failed to send periodic NOTIFY alive for {}: {}", usn, e),
|
||||
273 : }
|
||||
274 : }
|
||||
275 :
|
||||
276 250: /// Démarre l'écoute des M-SEARCH
|
||||
277 251: fn start_msearch_listener(&self, socket: Arc<UdpSocket>) {
|
||||
278 252: let devices = Arc::clone(&self.devices);
|
||||
...
|
||||
289 263: src, data
|
||||
290 264: );
|
||||
291 265: if let Some(st) = Self::parse_st(&data) {
|
||||
292 266: // Clone la liste des devices pour libérer le lock rapidement
|
||||
267: let devices_snapshot: Vec<SsdpDevice> = {
|
||||
292 268: let devices = devices.read().unwrap();
|
||||
293 269: devices.values().cloned().collect()
|
||||
270: };
|
||||
293 271: for device in devices.values()&devices_snapshot {
|
||||
294 272: Self::handle_msearch(&socket, &src, &st, device);
|
||||
295 273: }
|
||||
296 274: }
|
||||
297 275: }
|
||||
298 276: }
|
||||
299 : Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||
300 : // Timeout, continuer
|
||||
301 : continue;
|
||||
302 : }
|
||||
303 277: Err(e) => {
|
||||
304 278: warn!("❌ SSDP read error: {}", e);
|
||||
305 279: }
|
||||
...
|
||||
Modified regular file pmoupnp/src/upnp_server.rs:
|
||||
...
|
||||
250 250: }
|
||||
251 251:
|
||||
252 252: // Enregistrer les URLs dans le serveur web
|
||||
253: tracing::warn!("🔍 DEBUG: About to register_urls for device {}", di.udn());
|
||||
253 254: di.register_urls(self).await?;
|
||||
255: tracing::warn!("🔍 DEBUG: register_urls completed for device {}", di.udn());
|
||||
254 256:
|
||||
255 257: // Ajouter au registre pour l'introspection
|
||||
258: tracing::warn!("🔍 DEBUG: Adding to DEVICE_REGISTRY...");
|
||||
256 259: DEVICE_REGISTRY
|
||||
257 260: .write()
|
||||
258 261: .unwrap()
|
||||
259 262: .register(di.clone())
|
||||
260 263: .map_err(|e| DeviceError::UrlRegistrationError(e))?;
|
||||
264: tracing::warn!("🔍 DEBUG: Added to DEVICE_REGISTRY");
|
||||
261 265:
|
||||
262 266: // Annoncer via SSDP (si initialisé)
|
||||
267: tracing::warn!("🔍 DEBUG: Checking SSDP...");
|
||||
263 268: if self.ssdp_enabled() {
|
||||
269: tracing::warn!("🔍 DEBUG: SSDP enabled, getting lock...");
|
||||
264 270: let ssdp_opt = SSDP_SERVER.read().unwrap();
|
||||
265 271: if let Some(ref ssdp) = *ssdp_opt {
|
||||
272: tracing::warn!("🔍 DEBUG: SSDP server exists, announcing...");
|
||||
266 273: use crate::config_ext::UpnpConfigExt;
|
||||
267 274: let config = pmoconfig::get_config();
|
||||
268 275: let manufacturer = config
|
||||
...
|
||||
271 278: let ssdp_device = di.to_ssdp_device(&manufacturer, "1.0");
|
||||
272 279: ssdp.add_device(ssdp_device);
|
||||
273 280: info!("✅ SSDP announcement for {}", di.udn());
|
||||
281: tracing::warn!("🔍 DEBUG: SSDP announcement complete");
|
||||
274 282: }
|
||||
275 283: }
|
||||
284: tracing::warn!("🔍 DEBUG: Returning device instance");
|
||||
276 285:
|
||||
277 286: Ok(di)
|
||||
278 287: }
|
||||
...
|
||||
Added regular file toto:
|
||||
(empty)
|
||||
Reference in New Issue
Block a user