/
/
/
1"""Parsers for Radio Paradise provider."""
2
3from typing import Any
4
5from music_assistant_models.enums import ImageType
6from music_assistant_models.media_items import (
7 MediaItemImage,
8 ProviderMapping,
9 Radio,
10)
11from music_assistant_models.streamdetails import StreamMetadata
12
13from .constants import RADIO_PARADISE_CHANNELS, STATION_ICONS_BASE_URL
14
15
16def parse_radio(channel_id: str, instance_id: str, provider_domain: str) -> Radio:
17 """Create a Radio object from cached channel information."""
18 channel_info = RADIO_PARADISE_CHANNELS.get(channel_id, {})
19
20 radio = Radio(
21 provider=instance_id,
22 item_id=channel_id,
23 name=channel_info.get("name", "Unknown Radio"),
24 provider_mappings={
25 ProviderMapping(
26 provider_domain=provider_domain,
27 provider_instance=instance_id,
28 item_id=channel_id,
29 available=True,
30 )
31 },
32 )
33
34 # Add static station icon
35 station_icon = channel_info.get("station_icon")
36 if station_icon:
37 icon_url = f"{STATION_ICONS_BASE_URL}/{station_icon}"
38 radio.metadata.add_image(
39 MediaItemImage(
40 provider=instance_id,
41 type=ImageType.THUMB,
42 path=icon_url,
43 remotely_accessible=True,
44 )
45 )
46
47 return radio
48
49
50def _build_upcoming_string(metadata: dict[str, Any], current_song: dict[str, Any]) -> str | None:
51 """Build "Up Next: Artist - Track â Later: Artist2, Artist3" string.
52
53 :param metadata: Full metadata response with next song and block data.
54 :param current_song: Current track data to exclude from upcoming list.
55 """
56 next_song = metadata.get("next")
57 if not next_song:
58 return None
59
60 next_artist = next_song.get("artist", "")
61 next_title = next_song.get("title", "")
62 if not next_artist or not next_title:
63 return None
64
65 result = f"Up Next: {next_artist} - {next_title}"
66
67 # Get additional artists from block data for "Later" section
68 block_data = metadata.get("block_data")
69 if block_data and "song" in block_data:
70 current_event = current_song.get("event")
71 next_event = next_song.get("event")
72 current_elapsed = int(current_song.get("elapsed", 0))
73
74 # Collect unique artists that come after current and next song
75 seen_artists = {next_artist}
76 later_artists = []
77
78 sorted_keys = sorted(block_data["song"].keys(), key=int)
79 for song_key in sorted_keys:
80 song = block_data["song"][song_key]
81 song_event = song.get("event")
82
83 # Skip current and next song, only include songs after current
84 if (
85 song_event not in (current_event, next_event)
86 and int(song.get("elapsed", 0)) > current_elapsed
87 ):
88 artist_name = song.get("artist", "")
89 if artist_name and artist_name not in seen_artists:
90 seen_artists.add(artist_name)
91 later_artists.append(artist_name)
92 if len(later_artists) >= 3:
93 break
94
95 if later_artists:
96 result += f" â Later: {', '.join(later_artists)}"
97
98 return result
99
100
101def build_stream_metadata(
102 current_song: dict[str, Any],
103 metadata: dict[str, Any],
104 *,
105 show_upcoming: bool = False,
106) -> StreamMetadata:
107 """Build StreamMetadata with current track info.
108
109 :param current_song: Current track data from Radio Paradise API.
110 :param metadata: Full metadata response with next song and block data.
111 :param show_upcoming: If True, show upcoming info in artist field.
112 """
113 # Extract track info
114 artist = current_song.get("artist", "Unknown Artist")
115 title = current_song.get("title", "Unknown Title")
116 album = current_song.get("album")
117 year = current_song.get("year")
118
119 # Build album string with year if available
120 album_display = album
121 if album and year:
122 album_display = f"{album} ({year})"
123 elif year:
124 album_display = str(year)
125
126 # Alternate artist field with upcoming info
127 artist_display = artist
128 if show_upcoming:
129 upcoming = _build_upcoming_string(metadata, current_song)
130 if upcoming:
131 artist_display = upcoming
132
133 # Get cover image URL
134 # Play API returns relative path (e.g., "covers/l/19806.jpg")
135 # Now playing API returns full URL (e.g., "https://img.radioparadise.com/covers/l/19806.jpg")
136 cover = current_song.get("cover")
137 image_url = None
138 if cover:
139 image_url = cover if cover.startswith("http") else f"https://img.radioparadise.com/{cover}"
140
141 # Get track duration (API returns milliseconds, convert to seconds)
142 duration = current_song.get("duration")
143 if duration:
144 duration = int(duration) // 1000
145
146 return StreamMetadata(
147 title=title,
148 artist=artist_display,
149 album=album_display,
150 image_url=image_url,
151 duration=duration,
152 )
153