diff --git a/pmoaudiocache/src/metadata.rs b/pmoaudiocache/src/metadata.rs index 51cea0d8..858bcfd0 100644 --- a/pmoaudiocache/src/metadata.rs +++ b/pmoaudiocache/src/metadata.rs @@ -224,7 +224,9 @@ impl AudioMetadata { /// ``` pub fn to_didl_resource(&self, url: String) -> pmodidl::Resource { pmodidl::Resource { - protocol_info: "http-get:*:audio/flac:*".to_string(), + // Aligne le protocolInfo sur ce que le renderer déclare dans Sink (audio/flac). + // PN explicite pour compatibilité DLNA, sinon fallback générique. + protocol_info: "http-get:*:audio/flac:DLNA.ORG_PN=FLAC".to_string(), bits_per_sample: None, sample_frequency: self.sample_rate.map(|sr| sr.to_string()), nr_audio_channels: self.channels.map(|ch| ch.to_string()), diff --git a/pmoaudiocache/src/metadata_ext.rs b/pmoaudiocache/src/metadata_ext.rs index ac2b1653..4c8b5a29 100644 --- a/pmoaudiocache/src/metadata_ext.rs +++ b/pmoaudiocache/src/metadata_ext.rs @@ -117,7 +117,8 @@ impl TrackMetadataDidlExt for dyn TrackMetadata { }); pmodidl::Resource { - protocol_info: "http-get:*:audio/flac:*".to_string(), + // Aligne sur Sink du renderer (audio/flac) avec PN explicite. + protocol_info: "http-get:*:audio/flac:DLNA.ORG_PN=FLAC".to_string(), bits_per_sample: self.get_bits_per_sample().await.ok().flatten().map(|b| b.to_string()), sample_frequency: self.get_sample_rate().await.ok().flatten().map(|sr| sr.to_string()), nr_audio_channels: self.get_channels().await.ok().flatten().map(|ch| ch.to_string()), diff --git a/pmomediaserver/Cargo.toml b/pmomediaserver/Cargo.toml index e25a2196..e6ab1e52 100644 --- a/pmomediaserver/Cargo.toml +++ b/pmomediaserver/Cargo.toml @@ -18,6 +18,7 @@ quick-xml = { version = "0.38.3", features = ["serialize"] } thiserror = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +pmoutils = { path = "../pmoutils" } # Optional dependencies axum = { version = "0.8", optional = true } diff --git a/pmomediaserver/src/content_handler.rs b/pmomediaserver/src/content_handler.rs index 99c2ead6..b84f3ee7 100644 --- a/pmomediaserver/src/content_handler.rs +++ b/pmomediaserver/src/content_handler.rs @@ -10,6 +10,7 @@ //! - **Search** : Recherche dans les sources qui le supportent //! - **Update ID** : Suivi des changements pour les notifications UPnP +use pmoutils::ToXmlElement; use pmodidl::{Container, DIDLLite}; use pmosource::api::{get_source as get_source_from_registry, list_all_sources}; use pmosource::{BrowseResult, MusicSource, MusicSourceError}; @@ -28,12 +29,9 @@ fn to_didl_lite(containers: &[Container], items: &[pmodidl::Item]) -> Result{}", - body - )) + // Retourne uniquement le corps DIDL, sans préfixer une seconde déclaration XML. + let body = didl.to_xml(); + Ok(body) } /// Handler pour le service ContentDirectory diff --git a/pmoupnp/src/actions/arg_set_methods.rs b/pmoupnp/src/actions/arg_set_methods.rs index 74a60798..00ce44a5 100644 --- a/pmoupnp/src/actions/arg_set_methods.rs +++ b/pmoupnp/src/actions/arg_set_methods.rs @@ -1,7 +1,7 @@ use crate::UpnpModel; use crate::actions::ArgInstanceSet; use crate::{UpnpObject, actions::ArgumentSet}; -use xmltree::Element; +use xmltree::{Element, XMLNode}; impl UpnpObject for ArgumentSet { // Méthode pour convertir en XML (à implémenter avec une librairie XML) @@ -9,11 +9,8 @@ impl UpnpObject for ArgumentSet { let mut elem = Element::new("argumentList"); for arg in self.all() { - let arg_elem = arg.to_xml_element(); // toujours un contenant 1 ou 2 - - // Pour InOut, on ajoute tous les enfants du généré - for child in arg_elem.children { - elem.children.push(child); + for arg_elem in arg.to_xml_elements() { + elem.children.push(XMLNode::Element(arg_elem)); } } diff --git a/pmoupnp/src/actions/argument_methods.rs b/pmoupnp/src/actions/argument_methods.rs index c8263f5a..78c956d5 100644 --- a/pmoupnp/src/actions/argument_methods.rs +++ b/pmoupnp/src/actions/argument_methods.rs @@ -16,31 +16,8 @@ impl UpnpTyped for Argument { impl UpnpObject for Argument { fn to_xml_element(&self) -> Element { - let mut parent = Element::new("argumentList"); - - if self.is_in() && self.is_out() { - // InOut → deux arguments - parent.children.push(XMLNode::Element(make_argument_elem( - self.get_name(), - "in", - self.state_variable().get_name(), - ))); - parent.children.push(XMLNode::Element(make_argument_elem( - self.get_name(), - "out", - self.state_variable().get_name(), - ))); - } else { - // Cas simple - let direction = if self.is_in() { "in" } else { "out" }; - parent.children.push(XMLNode::Element(make_argument_elem( - self.get_name(), - direction, - self.state_variable().get_name(), - ))); - } - - parent + // Compat: retourne le premier argument (utile si consommé isolément) + self.to_xml_elements().into_iter().next().unwrap_or_else(|| Element::new("argument")) } } @@ -91,6 +68,23 @@ impl Argument { pub fn is_out(&self) -> bool { self.is_out } + + /// Retourne les éléments XML (1 ou 2 si InOut) + pub fn to_xml_elements(&self) -> Vec { + if self.is_in() && self.is_out() { + vec![ + make_argument_elem(self.get_name(), "in", self.state_variable().get_name()), + make_argument_elem(self.get_name(), "out", self.state_variable().get_name()), + ] + } else { + let direction = if self.is_in() { "in" } else { "out" }; + vec![make_argument_elem( + self.get_name(), + direction, + self.state_variable().get_name(), + )] + } + } } /// Fabrique un complet avec ses sous-éléments diff --git a/pmoupnp/src/services/service_instance.rs b/pmoupnp/src/services/service_instance.rs index 6a703a4f..032c57fe 100644 --- a/pmoupnp/src/services/service_instance.rs +++ b/pmoupnp/src/services/service_instance.rs @@ -626,16 +626,16 @@ impl ServiceInstance { elem.children.push(XMLNode::Element(spec)); - // actionList - if !self.actions.all().is_empty() { + // actionList (depuis le modèle) + if !self.model.actions.all().is_empty() { elem.children - .push(XMLNode::Element(self.actions.to_xml_element())); + .push(XMLNode::Element(self.model.actions.to_xml_element())); } - // serviceStateTable - if !self.statevariables.all().is_empty() { + // serviceStateTable (depuis le modèle) + if !self.model.state_table.all().is_empty() { elem.children - .push(XMLNode::Element(self.statevariables.to_xml_element())); + .push(XMLNode::Element(self.model.state_table.to_xml_element())); } elem