Files
pmomusic/pmoparadise/src/source.rs

386 lines
14 KiB
Rust
Raw Normal View History

2025-11-15 15:43:42 +01:00
//! RadioParadiseSource - Implementation of MusicSource for Radio Paradise
2025-10-16 22:00:35 +02:00
//!
2025-11-15 15:43:42 +01:00
//! This module provides a UPnP ContentDirectory source for Radio Paradise,
//! exposing live streams and historical playlists for all 4 channels.
2025-10-16 22:00:35 +02:00
2025-11-15 15:43:42 +01:00
use crate::channels::{ChannelDescriptor, ALL_CHANNELS};
use pmosource::pmodidl::{Container, Item, Resource};
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
use pmosource::{async_trait, BrowseResult, MusicSource, MusicSourceError, Result};
2025-11-15 15:43:42 +01:00
use std::sync::Arc;
2025-10-16 22:13:00 +02:00
use std::time::SystemTime;
2025-11-15 15:43:42 +01:00
use tokio::sync::RwLock;
#[cfg(feature = "playlist")]
use pmoplaylist::PlaylistManager;
2025-10-16 22:00:35 +02:00
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
/// Default Radio Paradise image (embedded in binary)
2025-10-16 22:00:35 +02:00
const DEFAULT_IMAGE: &[u8] = include_bytes!("../assets/default.webp");
2025-11-15 15:43:42 +01:00
/// RadioParadiseSource - UPnP ContentDirectory source for Radio Paradise
///
/// Provides access to:
/// - Live OGG streams for all 4 channels (Main, Mellow, Rock, Eclectic)
/// - Historical playlists (FIFO) for each channel
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
///
2025-11-15 15:43:42 +01:00
/// # Object ID Schema
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
///
2025-11-15 15:43:42 +01:00
/// - Root: `radio-paradise`
/// - Channel container: `radio-paradise:channel:{slug}`
/// - Live stream item: `radio-paradise:channel:{slug}:live`
/// - History container: `radio-paradise:channel:{slug}:history`
/// - History track: `radio-paradise:channel:{slug}:history:track:{pk}`
#[derive(Debug, Clone)]
2025-10-16 22:13:00 +02:00
pub struct RadioParadiseSource {
2025-11-15 15:43:42 +01:00
/// Base URL for streaming server (e.g., "http://localhost:8080")
base_url: String,
/// Update counter for change notifications
update_counter: Arc<RwLock<u32>>,
/// Last change timestamp
last_change: Arc<RwLock<SystemTime>>,
2025-10-16 22:13:00 +02:00
}
impl RadioParadiseSource {
2025-11-15 15:43:42 +01:00
/// Create a new RadioParadiseSource
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
///
2025-11-15 15:43:42 +01:00
/// # Arguments
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
///
2025-11-15 15:43:42 +01:00
/// * `base_url` - Base URL for streaming server (e.g., "http://localhost:8080")
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
///
2025-11-15 15:43:42 +01:00
/// # Note
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
///
2025-11-15 15:43:42 +01:00
/// With the "playlist" feature enabled, this source will use the global PlaylistManager
/// singleton to access history playlists.
pub fn new(base_url: impl Into<String>) -> Self {
Self {
base_url: base_url.into(),
update_counter: Arc::new(RwLock::new(0)),
last_change: Arc::new(RwLock::new(SystemTime::now())),
}
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
}
2025-11-15 15:43:42 +01:00
/// Build a live stream URL for a channel
fn build_live_url(&self, slug: &str) -> String {
format!("{}/radioparadise/stream/{}/ogg", self.base_url, slug)
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
}
2025-11-15 15:43:42 +01:00
/// Get the playlist ID for a channel's history
#[cfg(feature = "playlist")]
fn history_playlist_id(slug: &str) -> String {
format!("radioparadise-history-{}", slug)
}
/// Get channel descriptor by slug
fn get_channel_by_slug(slug: &str) -> Option<&'static ChannelDescriptor> {
ALL_CHANNELS.iter().find(|ch| ch.slug == slug)
}
/// Parse an object ID into its components
fn parse_object_id(id: &str) -> ObjectIdType {
let parts: Vec<&str> = id.split(':').collect();
match parts.as_slice() {
["radio-paradise"] => ObjectIdType::Root,
["radio-paradise", "channel", slug] => ObjectIdType::Channel {
slug: (*slug).to_string(),
},
["radio-paradise", "channel", slug, "live"] => ObjectIdType::LiveStream {
slug: (*slug).to_string(),
},
["radio-paradise", "channel", slug, "history"] => ObjectIdType::History {
slug: (*slug).to_string(),
},
["radio-paradise", "channel", slug, "history", "track", pk] => {
ObjectIdType::HistoryTrack {
slug: (*slug).to_string(),
pk: (*pk).to_string(),
}
}
_ => ObjectIdType::Unknown,
}
}
/// Build a channel container
fn build_channel_container(&self, descriptor: &ChannelDescriptor) -> Container {
Container {
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
searchable: Some("0".to_string()),
title: descriptor.display_name.to_string(),
class: "object.container".to_string(),
containers: vec![],
items: vec![],
}
}
/// Build a live stream item for a channel
fn build_live_stream_item(&self, descriptor: &ChannelDescriptor) -> Item {
let stream_url = self.build_live_url(descriptor.slug);
Item {
id: format!("radio-paradise:channel:{}:live", descriptor.slug),
parent_id: format!("radio-paradise:channel:{}", descriptor.slug),
restricted: Some("1".to_string()),
title: format!("{} - Live Stream", descriptor.display_name),
creator: Some("Radio Paradise".to_string()),
class: "object.item.audioItem.audioBroadcast".to_string(),
artist: Some("Radio Paradise".to_string()),
album: Some(descriptor.display_name.to_string()),
genre: Some("Radio".to_string()),
album_art: None,
album_art_pk: None,
date: None,
original_track_number: None,
resources: vec![Resource {
protocol_info: "http-get:*:audio/ogg:*".to_string(),
bits_per_sample: None,
sample_frequency: None,
nr_audio_channels: Some("2".to_string()),
duration: None,
url: stream_url,
}],
descriptions: vec![],
}
}
/// Build a history container for a channel
fn build_history_container(&self, descriptor: &ChannelDescriptor) -> Container {
Container {
id: format!("radio-paradise:channel:{}:history", descriptor.slug),
parent_id: format!("radio-paradise:channel:{}", descriptor.slug),
restricted: Some("1".to_string()),
child_count: None, // Will be determined by playlist
searchable: Some("1".to_string()),
title: format!("{} - History", descriptor.display_name),
class: "object.container.playlistContainer".to_string(),
containers: vec![],
items: vec![],
}
}
/// Get items from history playlist
#[cfg(feature = "playlist")]
async fn get_history_items(
&self,
slug: &str,
offset: usize,
count: usize,
) -> Result<Vec<Item>> {
let playlist_id = Self::history_playlist_id(slug);
// Get read handle for the playlist from the singleton
let manager = pmoplaylist::PlaylistManager();
let reader = manager.get_read_handle(&playlist_id).await.map_err(|e| {
MusicSourceError::BrowseError(format!("Failed to get playlist {}: {}", playlist_id, e))
})?;
2025-11-15 15:43:42 +01:00
// Get entries from playlist
let entries = reader.get_entries(offset, count).await.map_err(|e| {
MusicSourceError::BrowseError(format!("Failed to read playlist entries: {}", e))
})?;
// Convert entries to Items
let mut items = Vec::new();
for entry in entries {
if let Ok(item) = self.playlist_entry_to_item(slug, &entry).await {
items.push(item);
}
}
Ok(items)
}
/// Convert a playlist entry to a DIDL Item
#[cfg(feature = "playlist")]
async fn playlist_entry_to_item(
&self,
slug: &str,
entry: &pmoplaylist::PlaylistEntry,
) -> Result<Item> {
let metadata = &entry.metadata;
// Build audio URL from cache
let audio_url = format!("{}/cache/audio/{}", self.base_url, entry.pk);
2025-11-15 15:43:42 +01:00
// Build item
Ok(Item {
id: format!("radio-paradise:channel:{}:history:track:{}", slug, entry.pk),
parent_id: format!("radio-paradise:channel:{}:history", slug),
restricted: Some("1".to_string()),
title: metadata
.title
.clone()
.unwrap_or_else(|| "Unknown Title".to_string()),
2025-11-15 15:43:42 +01:00
creator: metadata.artist.clone(),
class: "object.item.audioItem.musicTrack".to_string(),
artist: metadata.artist.clone(),
album: metadata.album.clone(),
genre: metadata.genre.clone(),
album_art: None,
album_art_pk: metadata.cover_pk.clone(),
date: metadata.year.map(|y| y.to_string()),
original_track_number: metadata.track_number.map(|n| n.to_string()),
resources: vec![Resource {
protocol_info: "http-get:*:audio/flac:*".to_string(),
bits_per_sample: metadata.bits_per_sample.map(|b| b.to_string()),
sample_frequency: metadata.sample_rate.map(|s| s.to_string()),
nr_audio_channels: Some("2".to_string()),
duration: metadata
.duration
.map(|d| format!("{}:{:02}:{:02}", d / 3600, (d % 3600) / 60, d % 60)),
2025-11-15 15:43:42 +01:00
url: audio_url,
}],
descriptions: vec![],
})
2025-10-16 22:13:00 +02:00
}
}
2025-11-15 15:43:42 +01:00
/// Types of object IDs in the Radio Paradise source
#[derive(Debug, Clone, PartialEq)]
enum ObjectIdType {
Root,
Channel { slug: String },
LiveStream { slug: String },
History { slug: String },
HistoryTrack { slug: String, pk: String },
Unknown,
}
2025-10-16 22:13:00 +02:00
#[async_trait]
2025-10-16 22:00:35 +02:00
impl MusicSource for RadioParadiseSource {
fn name(&self) -> &str {
2025-11-15 15:43:42 +01:00
"Radio Paradise"
2025-10-16 22:00:35 +02:00
}
fn id(&self) -> &str {
2025-11-15 15:43:42 +01:00
"radio-paradise"
2025-10-16 22:00:35 +02:00
}
fn default_image(&self) -> &[u8] {
DEFAULT_IMAGE
}
2025-10-16 22:13:00 +02:00
async fn root_container(&self) -> Result<Container> {
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
Ok(Container {
2025-11-15 15:43:42 +01:00
id: "radio-paradise".to_string(),
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
parent_id: "0".to_string(),
restricted: Some("1".to_string()),
2025-11-15 15:43:42 +01:00
child_count: Some("4".to_string()), // 4 channels
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
searchable: Some("0".to_string()),
2025-11-15 15:43:42 +01:00
title: "Radio Paradise".to_string(),
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
class: "object.container".to_string(),
containers: vec![],
items: vec![],
})
2025-10-16 22:13:00 +02:00
}
2025-11-15 15:43:42 +01:00
async fn browse(&self, object_id: &str) -> Result<BrowseResult> {
match Self::parse_object_id(object_id) {
ObjectIdType::Root => {
// Return the 4 channel containers
let containers: Vec<Container> = ALL_CHANNELS
.iter()
.map(|ch| self.build_channel_container(ch))
.collect();
Ok(BrowseResult::Containers(containers))
}
ObjectIdType::Channel { slug } => {
// Return live stream item + history container
let descriptor = Self::get_channel_by_slug(&slug).ok_or_else(|| {
MusicSourceError::ObjectNotFound(format!("Unknown channel: {}", slug))
})?;
let live_item = self.build_live_stream_item(descriptor);
let history_container = self.build_history_container(descriptor);
Ok(BrowseResult::Mixed {
containers: vec![history_container],
items: vec![live_item],
})
}
ObjectIdType::History { slug } => {
// Return items from history playlist
#[cfg(feature = "playlist")]
{
let items = self.get_history_items(&slug, 0, 100).await?;
Ok(BrowseResult::Items(items))
}
#[cfg(not(feature = "playlist"))]
{
let _ = slug;
Ok(BrowseResult::Items(vec![]))
}
}
ObjectIdType::LiveStream { .. } | ObjectIdType::HistoryTrack { .. } => {
// These are leaf nodes, cannot be browsed
Err(MusicSourceError::ObjectNotFound(format!(
"Object {} is not a container",
object_id
)))
}
ObjectIdType::Unknown => Err(MusicSourceError::ObjectNotFound(format!(
"Unknown object ID: {}",
object_id
))),
}
2025-10-16 22:13:00 +02:00
}
2025-11-15 15:43:42 +01:00
async fn resolve_uri(&self, object_id: &str) -> Result<String> {
match Self::parse_object_id(object_id) {
ObjectIdType::LiveStream { slug } => {
// Return live stream URL
Ok(self.build_live_url(&slug))
}
ObjectIdType::HistoryTrack { pk, .. } => {
// Return cached audio URL
Ok(format!("{}/cache/audio/{}", self.base_url, pk))
}
_ => Err(MusicSourceError::ObjectNotFound(format!(
"Cannot resolve URI for object: {}",
object_id
))),
}
2025-10-16 22:13:00 +02:00
}
fn supports_fifo(&self) -> bool {
2025-11-15 15:43:42 +01:00
// History playlists are FIFO
cfg!(feature = "playlist")
}
2025-10-16 22:13:00 +02:00
async fn append_track(&self, _track: Item) -> Result<()> {
2025-11-15 15:43:42 +01:00
// Tracks are added automatically by FlacCacheSink
Err(MusicSourceError::NotSupported(
"Tracks are automatically added to history by the streaming system".to_string(),
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
))
2025-10-16 22:13:00 +02:00
}
async fn remove_oldest(&self) -> Result<Option<Item>> {
2025-11-15 15:43:42 +01:00
// Managed automatically by playlist FIFO
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
Ok(None)
2025-10-16 22:13:00 +02:00
}
async fn update_id(&self) -> u32 {
2025-11-15 15:43:42 +01:00
*self.update_counter.read().await
2025-10-16 22:13:00 +02:00
}
async fn last_change(&self) -> Option<SystemTime> {
2025-11-15 15:43:42 +01:00
Some(*self.last_change.read().await)
2025-10-21 14:21:17 +02:00
}
2025-11-15 15:43:42 +01:00
async fn get_items(&self, offset: usize, count: usize) -> Result<Vec<Item>> {
// For Radio Paradise, we don't have a global FIFO
// Each channel has its own history
// Return empty for now - clients should browse specific channel histories
let _ = (offset, count);
refactor: Remove obsolete orchestration layer, create RadioParadiseSource stub Major cleanup removing 2831 lines (~45%) of outdated server orchestration code. RadioParadiseStreamSource (pmoaudio integration) is now the primary implementation. ## Changes ### Removed (2393 lines) - **paradise/ module** - Complete server orchestration system: - worker.rs (1146 lines) - Background polling, caching, state machine - channel.rs (429 lines) - Channel lifecycle management - playlist.rs (294 lines) - Shared playlist management - history.rs (218 lines) - SQLite persistence - constants.rs (209 lines) - Server configuration constants - mod.rs (29 lines) - Module exports ### Replaced - **source.rs** (612 → 174 lines, -72%): - Old: Full MusicSource implementation with UPnP/DIDL integration - New: Minimal stub for backward compatibility with pmomediaserver - Returns empty results and deprecation warnings - Documents migration path to RadioParadiseStreamSource ### Updated - **config_ext.rs**: Now imports HISTORY_DEFAULT_MAX_TRACKS from channels module - **lib.rs**: - Removed paradise module - Updated documentation to focus on RadioParadiseStreamSource - Updated cargo features documentation ## Architecture **Before**: Complex orchestration with workers, channels, caching, history **After**: Simple API access + pmoaudio streaming (RadioParadiseStreamSource) ## Compatibility RadioParadiseSource stub maintains API compatibility for pmomediaserver while clearly indicating deprecation. All operations return empty results or errors with migration guidance. ## Testing - ✅ All 23 tests pass - ✅ Compilation successful with all features - ✅ pmomediaserver compatibility maintained (stub implementation) ## Migration Path Old (deprecated): ```rust let source = RadioParadiseSource::from_registry(client)?; ``` New (recommended): ```rust let stream_source = RadioParadiseStreamSource::new(client, None).await?; let node = Node::from_logic(stream_source); ```
2025-11-05 07:40:36 +00:00
Ok(vec![])
2025-10-17 08:19:10 +02:00
}
2025-10-16 22:00:35 +02:00
}