push-pqqsxyupswry #21
@@ -609,10 +609,11 @@ impl ToXmlElement for Item {
|
||||
elem.children
|
||||
.push(XMLNode::Element(text_element("upnp:genre", genre)));
|
||||
}
|
||||
if let Some(ref art) = self.album_art {
|
||||
elem.children
|
||||
.push(XMLNode::Element(text_element("upnp:albumArtURI", art)));
|
||||
}
|
||||
// NOTE: albumArtURI temporarily disabled for compatibility (some CPs misbehave)
|
||||
// if let Some(ref art) = self.album_art {
|
||||
// elem.children
|
||||
// .push(XMLNode::Element(text_element("upnp:albumArtURI", art)));
|
||||
// }
|
||||
if let Some(date) = self.normalized_date() {
|
||||
elem.children
|
||||
.push(XMLNode::Element(text_element("dc:date", &date)));
|
||||
|
||||
@@ -14,6 +14,7 @@ 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};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Convertit des containers et items en XML DIDL-Lite
|
||||
@@ -85,6 +86,16 @@ impl ContentHandler {
|
||||
"ContentDirectory::Browse"
|
||||
);
|
||||
|
||||
// Log rapide sur la branche flatten vs agrégée
|
||||
if object_id == "0" {
|
||||
let sources = list_all_sources().await;
|
||||
tracing::info!(
|
||||
"Browse root: sources.len() = {}, flatten = {}",
|
||||
sources.len(),
|
||||
sources.len() == 1
|
||||
);
|
||||
}
|
||||
|
||||
match browse_flag {
|
||||
"BrowseMetadata" => self.browse_metadata(object_id).await,
|
||||
"BrowseDirectChildren" => {
|
||||
@@ -98,7 +109,24 @@ impl ContentHandler {
|
||||
/// Browse les métadonnées d'un objet spécifique
|
||||
async fn browse_metadata(&self, object_id: &str) -> Result<(String, u32, u32, u32), String> {
|
||||
if object_id == "0" {
|
||||
// Retourner le container racine
|
||||
// Si un seul enfant, publier ce fils comme racine
|
||||
let sources = list_all_sources().await;
|
||||
if sources.len() == 1 {
|
||||
let source = sources.into_iter().next().unwrap();
|
||||
let mut container = source
|
||||
.root_container()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to get root container: {}", e))?;
|
||||
// Le présenter comme la racine (id=0, parent=-1)
|
||||
container.id = "0".to_string();
|
||||
container.parent_id = "-1".to_string();
|
||||
container.child_count = None; // compatibilité CP
|
||||
let didl = to_didl_lite(&[container], &[])?;
|
||||
let update_id = source.update_id().await.max(1);
|
||||
return Ok((didl, 1, 1, update_id));
|
||||
}
|
||||
|
||||
// Sinon retourner le container racine agrégé
|
||||
let root = self.build_root_container().await;
|
||||
let didl = to_didl_lite(&[root], &[])?;
|
||||
Ok((didl, 1, 1, 1))
|
||||
@@ -190,6 +218,53 @@ impl ContentHandler {
|
||||
requested_count: u32,
|
||||
) -> Result<(String, u32, u32, u32), String> {
|
||||
if object_id == "0" {
|
||||
// Si un seul enfant, publier directement ses enfants comme racine
|
||||
let sources = list_all_sources().await;
|
||||
if sources.len() == 1 {
|
||||
let source = sources.into_iter().next().unwrap();
|
||||
let source_id = source.id().to_string();
|
||||
|
||||
// Récupérer les enfants du container racine de la source
|
||||
let mut result = source
|
||||
.browse(&source_id)
|
||||
.await
|
||||
.map_err(|e| format!("Browse failed: {}", e))?;
|
||||
|
||||
// Re-mapper parentID sur "0" pour éviter des parents inexistants côté CP
|
||||
match &mut result {
|
||||
BrowseResult::Containers(c) => {
|
||||
for cont in c.iter_mut() {
|
||||
if cont.parent_id == source_id {
|
||||
cont.parent_id = "0".to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
BrowseResult::Items(i) => {
|
||||
for item in i.iter_mut() {
|
||||
if item.parent_id == source_id {
|
||||
item.parent_id = "0".to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
BrowseResult::Mixed { containers, items } => {
|
||||
for cont in containers.iter_mut() {
|
||||
if cont.parent_id == source_id {
|
||||
cont.parent_id = "0".to_string();
|
||||
}
|
||||
}
|
||||
for item in items.iter_mut() {
|
||||
if item.parent_id == source_id {
|
||||
item.parent_id = "0".to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self
|
||||
.browse_result_to_didl("0", result, source, starting_index, requested_count)
|
||||
.await;
|
||||
}
|
||||
|
||||
// Retourner toutes les sources comme enfants de la racine
|
||||
return self.browse_root(starting_index, requested_count).await;
|
||||
}
|
||||
@@ -308,6 +383,27 @@ impl ContentHandler {
|
||||
.collect();
|
||||
let mut items = items;
|
||||
|
||||
// Log avant déduplication
|
||||
tracing::debug!(
|
||||
"BrowseResult before dedup: containers={}, items={}",
|
||||
containers.len(),
|
||||
items.len()
|
||||
);
|
||||
|
||||
// Deduplicate containers/items by id to avoid doubles in the response
|
||||
let mut seen_containers = HashSet::new();
|
||||
containers.retain(|c| seen_containers.insert(c.id.clone()));
|
||||
|
||||
let mut seen_items = HashSet::new();
|
||||
items.retain(|i| seen_items.insert(i.id.clone()));
|
||||
|
||||
// Log après déduplication
|
||||
tracing::debug!(
|
||||
"BrowseResult after dedup: containers={}, items={}",
|
||||
containers.len(),
|
||||
items.len()
|
||||
);
|
||||
|
||||
// Calculer le total avant pagination
|
||||
let total = (containers.len() + items.len()) as u32;
|
||||
|
||||
@@ -355,7 +451,7 @@ impl ContentHandler {
|
||||
id: "0".to_string(),
|
||||
parent_id: "-1".to_string(),
|
||||
restricted: Some("1".to_string()),
|
||||
// Par cohérence avec la plupart des serveurs observés, on omet childCount sur la racine
|
||||
// Laisser childCount absent sur la racine pour maximiser la compatibilité (BubbleUPnP)
|
||||
child_count: None,
|
||||
searchable: Some("1".to_string()),
|
||||
title: "PMOMusic".to_string(),
|
||||
|
||||
@@ -187,7 +187,7 @@ impl RadioParadiseSource {
|
||||
id: format!("radio-paradise:channel:{}", descriptor.slug),
|
||||
parent_id: "radio-paradise".to_string(),
|
||||
restricted: Some("1".to_string()),
|
||||
child_count: Some("2".to_string()), // Live + History
|
||||
child_count: None,
|
||||
searchable: Some("0".to_string()),
|
||||
title: descriptor.display_name.to_string(),
|
||||
class: "object.container".to_string(),
|
||||
@@ -242,7 +242,7 @@ impl RadioParadiseSource {
|
||||
id: format!("radio-paradise:channel:{}:history", descriptor.slug),
|
||||
parent_id: format!("radio-paradise:channel:{}", descriptor.slug),
|
||||
restricted: Some("1".to_string()),
|
||||
child_count: Some("0".to_string()), // Default to 0, updated below if playlist exists
|
||||
child_count: None,
|
||||
searchable: Some("1".to_string()),
|
||||
title: format!("{} - History", descriptor.display_name),
|
||||
// Expose l'historique comme une playlist jouable
|
||||
@@ -355,7 +355,8 @@ impl MusicSource for RadioParadiseSource {
|
||||
id: "radio-paradise".to_string(),
|
||||
parent_id: "0".to_string(),
|
||||
restricted: Some("1".to_string()),
|
||||
child_count: Some("4".to_string()), // 4 channels
|
||||
// childCount retiré pour éviter les soucis de compatibilité côté CP
|
||||
child_count: None,
|
||||
searchable: Some("0".to_string()),
|
||||
title: "Radio Paradise".to_string(),
|
||||
class: "object.container".to_string(),
|
||||
|
||||
@@ -166,7 +166,7 @@ impl ReadHandle {
|
||||
id: self.playlist.id.clone(),
|
||||
parent_id: "0".to_string(),
|
||||
restricted: Some("1".to_string()),
|
||||
child_count: Some(remaining.to_string()),
|
||||
child_count: None,
|
||||
searchable: Some("0".to_string()),
|
||||
title,
|
||||
class: "object.container.playlistContainer".to_string(),
|
||||
@@ -222,6 +222,12 @@ impl ReadHandle {
|
||||
let genre = meta.get_genre().await.ok().flatten();
|
||||
let year = meta.get_year().await.ok().flatten();
|
||||
let track_number = meta.get_track_number().await.ok().flatten();
|
||||
let cover_pk = meta.get_cover_pk().await.ok().flatten();
|
||||
let cover_url = if let Some(pk) = cover_pk.as_ref() {
|
||||
Some(format!("/covers/jpeg/{}/256", pk))
|
||||
} else {
|
||||
meta.get_cover_url().await.ok().flatten()
|
||||
};
|
||||
|
||||
// Créer l'Item
|
||||
let item = Item {
|
||||
@@ -234,8 +240,8 @@ impl ReadHandle {
|
||||
artist,
|
||||
album,
|
||||
genre,
|
||||
album_art: None, // TODO: intégrer pmocovers
|
||||
album_art_pk: None,
|
||||
album_art: cover_url,
|
||||
album_art_pk: cover_pk,
|
||||
date: year.map(|y| y.to_string()),
|
||||
original_track_number: track_number.map(|n| n.to_string()),
|
||||
resources: vec![resource],
|
||||
|
||||
Reference in New Issue
Block a user