Files
pmomusic/pmocontrol/examples/test_attach_playlist.rs
2025-12-21 21:04:31 +01:00

184 lines
5.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// Test example to debug playlist attachment issues
///
/// This example tests each step of attaching a playlist to an OpenHome renderer:
/// 1. Browse the MediaServer for playlist items
/// 2. Insert each item into the OpenHome renderer's playlist
/// 3. Verify the operation succeeded
///
/// Usage:
/// cargo run --example test_attach_playlist
use anyhow::{Context, Result};
use pmocontrol::{
media_server::{MediaBrowser, MediaEntry, MediaServer, ServerId},
openhome_client::OhPlaylistClient,
};
use tracing::{debug, error, info, warn};
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
#[tokio::main]
async fn main() -> Result<()> {
// Initialize logging
tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info,pmocontrol=debug")),
)
.init();
info!("🧪 Starting playlist attachment test");
// Configuration - adjust these for your setup
let media_server_url = "http://192.168.0.138:8080";
let media_server_id = "uuid:17fe2ea6-8908-4e30-bc52-b28ea4cab3e4";
let playlist_container_id = "radio-paradise:channel:mellow:liveplaylist";
let renderer_playlist_url = "http://192.168.0.200:49152/Playlist";
let renderer_service_type = "urn:av-openhome-org:service:Playlist:1";
info!("Configuration:");
info!(" MediaServer: {}", media_server_url);
info!(" Playlist: {}", playlist_container_id);
info!(" Renderer: {}", renderer_playlist_url);
info!("");
// Step 1: Create MediaServer client
info!("📡 Step 1: Connecting to MediaServer");
let content_directory_url = format!(
"{}/device/{}/service/ContentDirectory/control",
media_server_url,
media_server_id.replace("uuid:", "")
);
let server = MediaServer::new(
ServerId(media_server_id.to_string()),
"PMOMusic Test".to_string(),
content_directory_url,
);
debug!("MediaServer client created");
// Step 2: Browse the playlist container
info!("📂 Step 2: Browsing playlist container");
info!(" Container ID: {}", playlist_container_id);
let entries = match server.browse_children(playlist_container_id, 0, 10) {
Ok(entries) => {
info!("✅ Browse succeeded: {} items found", entries.len());
entries
}
Err(e) => {
error!("❌ Browse failed: {}", e);
error!(" This is where the error occurs!");
return Err(e);
}
};
if entries.is_empty() {
warn!("⚠️ Playlist is empty, nothing to insert");
return Ok(());
}
// Display the first few items
info!("📋 First items in playlist:");
for (idx, entry) in entries.iter().take(3).enumerate() {
info!(
" [{}] {} - {}",
idx,
entry.title,
entry.artist.as_deref().unwrap_or("Unknown")
);
if let Some(res) = entry.resources.first() {
debug!(" URI: {}", res.uri);
debug!(" protocolInfo: {}", res.protocol_info);
}
}
info!("");
// Step 3: Connect to OpenHome renderer
info!("🎵 Step 3: Connecting to OpenHome renderer");
let oh_client = OhPlaylistClient::new(
renderer_playlist_url.to_string(),
renderer_service_type.to_string(),
);
debug!("OpenHome client created");
// Step 4: Clear existing playlist
info!("🗑️ Step 4: Clearing existing playlist");
match oh_client.delete_all() {
Ok(_) => info!("✅ Playlist cleared"),
Err(e) => {
warn!("⚠️ Could not clear playlist: {}", e);
}
}
info!("");
// Step 5: Insert items one by one
info!(" Step 5: Inserting items into renderer");
let mut after_id = 0u32;
let mut inserted_count = 0;
for (idx, entry) in entries.iter().enumerate() {
if entry.is_container {
debug!("Skipping container: {}", entry.title);
continue;
}
let resource = match entry.resources.iter().find(|r| r.is_audio()) {
Some(r) => r,
None => {
warn!("No audio resource found for: {}", entry.title);
continue;
}
};
info!(" [{}] Inserting: {}", idx, entry.title);
debug!(" URI: {}", resource.uri);
debug!(" protocolInfo: {}", resource.protocol_info);
// Build simple DIDL-Lite metadata
let didl_metadata = format!(
r#"<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"><item id="{}" parentID="-1" restricted="1"><dc:title>{}</dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><res protocolInfo="{}">{}</res></item></DIDL-Lite>"#,
entry.id,
xmlescape(&entry.title),
resource.protocol_info,
xmlescape(&resource.uri)
);
trace!("DIDL metadata: {}", didl_metadata);
match oh_client.insert(after_id, &resource.uri, &didl_metadata) {
Ok(new_id) => {
debug!(" ✅ Inserted with ID: {}", new_id);
after_id = new_id;
inserted_count += 1;
}
Err(e) => {
error!(" ❌ Insert failed: {}", e);
error!(" This is the UPnP 501 error location!");
// Continue with next item instead of failing
warn!(" Continuing with next item...");
}
}
}
info!("");
info!(
"✅ Test completed: {}/{} items inserted successfully",
inserted_count,
entries.len()
);
Ok(())
}
/// Simple XML escaping
fn xmlescape(s: &str) -> String {
s.replace('&', "&amp;")
.replace('<', "&lt;")
.replace('>', "&gt;")
.replace('"', "&quot;")
.replace('\'', "&apos;")
}