push-pqqsxyupswry #21

Merged
eric merged 76 commits from push-pqqsxyupswry into main 2025-12-06 12:49:47 +01:00
4 changed files with 116 additions and 12 deletions
Showing only changes of commit 128fa823a9 - Show all commits

View File

@@ -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)));

View File

@@ -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(),

View File

@@ -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(),

View File

@@ -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],