Gestion des renderes openhome

This commit is contained in:
2025-12-05 19:01:33 +01:00
parent 9f62191830
commit 2ffd3b5fb5
16 changed files with 2971 additions and 2877 deletions

View File

@@ -23,6 +23,7 @@ use pmocontrol::{
ControlPoint, DeviceRegistryRead, PlaybackPositionInfo, PlaybackState, RendererEvent,
RendererId, RendererInfo,
};
use pmocontrol::openhome_renderer::{format_seconds, map_openhome_state};
fn main() -> io::Result<()> {
// Logging simple (tracing_subscriber est déjà utilisé dans les autres exemples)
@@ -53,6 +54,7 @@ fn main() -> io::Result<()> {
" [{}] {} | model={} | udn={} | location={} | online={}",
idx, info.friendly_name, info.model_name, info.udn, info.location, info.online
);
print_openhome_summary(" ", info, &cp);
}
// 3. Optionnel : sélection d'un renderer par index (filtrage des événements)
@@ -126,6 +128,7 @@ fn event_matches_id(event: &RendererEvent, id: &RendererId) -> bool {
RendererEvent::MuteChanged { id: eid, .. } => eid == id,
RendererEvent::MetadataChanged { id: eid, .. } => eid == id,
RendererEvent::QueueUpdated { id: eid, .. } => eid == id,
RendererEvent::BindingChanged { id: eid, .. } => eid == id,
}
}
@@ -165,6 +168,60 @@ fn format_position(pos: &PlaybackPositionInfo) -> String {
format!("track={} rel_time={} duration={}", track, rel, dur)
}
fn print_openhome_summary(prefix: &str, info: &RendererInfo, cp: &ControlPoint) {
if !info.capabilities.has_oh_playlist
&& !info.capabilities.has_oh_info
&& !info.capabilities.has_oh_time
{
return;
}
let registry = cp.registry();
let reg = registry.read().unwrap();
let playlist_client = reg.oh_playlist_client_for_renderer(&info.id);
let info_client = reg.oh_info_client_for_renderer(&info.id);
let time_client = reg.oh_time_client_for_renderer(&info.id);
drop(reg);
if playlist_client.is_none() && info_client.is_none() && time_client.is_none() {
return;
}
println!("{prefix}OpenHome:");
if let Some(client) = playlist_client {
match client.id_array() {
Ok(ids) => println!("{prefix} Playlist tracks : {}", ids.len()),
Err(err) => println!("{prefix} Playlist tracks : <error {err}>"),
}
}
if let Some(client) = info_client {
match client.transport_state() {
Ok(state) => {
let logical = map_openhome_state(&state);
println!(
"{prefix} Transport state : {} ({:?})",
state, logical
);
}
Err(err) => println!("{prefix} Transport state : <error {err}>"),
}
}
if let Some(client) = time_client {
match client.position() {
Ok(pos) => println!(
"{prefix} Position : {}/{} (tracks={})",
format_seconds(pos.elapsed_secs),
format_seconds(pos.duration_secs),
pos.track_count
),
Err(err) => println!("{prefix} Position : <error {err}>"),
}
}
}
/// Affiche un RendererEvent avec horodatage.
fn print_event(event: &RendererEvent) {
let ts = now_hms();

View File

@@ -329,13 +329,7 @@ impl CliConfig {
}
fn pick_renderer(renderers: Vec<RendererInfo>) -> Option<RendererInfo> {
let mut candidates: Vec<RendererInfo> = renderers
.into_iter()
.filter(|info| match info.protocol {
RendererProtocol::OpenHomeOnly => false,
RendererProtocol::UpnpAvOnly | RendererProtocol::Hybrid => true,
})
.collect();
let mut candidates: Vec<RendererInfo> = renderers;
if candidates.is_empty() {
return None;

View File

@@ -302,13 +302,7 @@ impl CliConfig {
}
fn pick_renderer(renderers: Vec<RendererInfo>) -> Option<RendererInfo> {
let mut candidates: Vec<RendererInfo> = renderers
.into_iter()
.filter(|info| match info.protocol {
RendererProtocol::OpenHomeOnly => false,
RendererProtocol::UpnpAvOnly | RendererProtocol::Hybrid => true,
})
.collect();
let mut candidates: Vec<RendererInfo> = renderers;
if candidates.is_empty() {
return None;

View File

@@ -18,6 +18,8 @@ use pmocontrol::{
ControlPoint, MusicRenderer, PlaybackState, PlaybackStatus, RendererCapabilities,
RendererProtocol, TransportControl, VolumeControl,
};
use pmocontrol::model::RendererInfo;
use pmocontrol::openhome_renderer::{format_seconds, map_openhome_state};
use std::env;
use std::thread;
use std::time::Duration;
@@ -83,6 +85,7 @@ fn main() -> Result<()> {
);
print_backend(" ", r);
print_capabilities(" ", &info.capabilities, &info.protocol);
print_openhome_details(" ", info, &cp);
}
// 5. Select renderer
@@ -98,6 +101,7 @@ fn main() -> Result<()> {
println!(" Protocol : {:?}", info.protocol);
print_backend(" ", renderer);
print_capabilities(" ", &info.capabilities, &info.protocol);
print_openhome_details(" ", info, &cp);
if let Some(upnp) = renderer.as_upnp() {
println!(
@@ -224,6 +228,60 @@ fn print_capabilities(prefix: &str, caps: &RendererCapabilities, proto: &Rendere
println!("{prefix} OH Radio : {}", caps.has_oh_radio);
}
fn print_openhome_details(prefix: &str, info: &RendererInfo, cp: &ControlPoint) {
if !info.capabilities.has_oh_playlist
&& !info.capabilities.has_oh_info
&& !info.capabilities.has_oh_time
{
return;
}
let registry = cp.registry();
let reg = registry.read().unwrap();
let playlist_client = reg.oh_playlist_client_for_renderer(&info.id);
let info_client = reg.oh_info_client_for_renderer(&info.id);
let time_client = reg.oh_time_client_for_renderer(&info.id);
drop(reg);
if playlist_client.is_none() && info_client.is_none() && time_client.is_none() {
return;
}
println!("{prefix}OpenHome:");
if let Some(client) = playlist_client {
match client.id_array() {
Ok(ids) => println!("{prefix} Playlist tracks : {}", ids.len()),
Err(err) => println!("{prefix} Playlist tracks : <error {err}>"),
}
}
if let Some(client) = info_client {
match client.transport_state() {
Ok(state) => {
let logical = map_openhome_state(&state);
println!(
"{prefix} Transport state : {} ({:?})",
state, logical
);
}
Err(err) => println!("{prefix} Transport state : <error {err}>"),
}
}
if let Some(client) = time_client {
match client.position() {
Ok(pos) => println!(
"{prefix} Position : {}/{} (tracks={})",
format_seconds(pos.elapsed_secs),
format_seconds(pos.duration_secs),
pos.track_count
),
Err(err) => println!("{prefix} Position : <error {err}>"),
}
}
}
fn print_backend(prefix: &str, renderer: &MusicRenderer) {
let backend = match renderer {
MusicRenderer::Upnp(_) => "UpnpRenderer (UPnP AV / DLNA)",