/
/
/
1"""Helper functions for Radio Paradise provider."""
2
3import time
4from typing import Any, cast
5
6from .constants import RADIO_PARADISE_CHANNELS
7
8
9def get_current_block_position(block_data: dict[str, Any]) -> int:
10 """Calculate current playback position within a Radio Paradise block.
11
12 Args:
13 block_data: Block data containing sched_time_millis
14
15 Returns:
16 Current position in milliseconds from block start
17 """
18 current_time_ms = int(time.time() * 1000)
19 sched_time = int(block_data.get("sched_time_millis", current_time_ms))
20 return current_time_ms - sched_time
21
22
23def find_current_song(
24 songs: dict[str, dict[str, Any]], current_time_ms: int
25) -> dict[str, Any] | None:
26 """Find which song should currently be playing based on elapsed time.
27
28 Args:
29 songs: Dictionary of songs from Radio Paradise block data
30 current_time_ms: Current position in milliseconds within the block
31
32 Returns:
33 The song dict that should be playing now, or None if not found
34 """
35 sorted_keys = sorted(songs.keys(), key=int)
36
37 for song_key in sorted_keys:
38 song = songs[song_key]
39 song_start = int(song.get("elapsed", 0))
40 song_duration = int(song.get("duration", 0))
41 song_end = song_start + song_duration
42
43 if song_start <= current_time_ms < song_end:
44 return song
45
46 # If no exact match, return first song
47 first_song = songs.get("0")
48 return first_song if first_song is not None else {}
49
50
51def get_next_song(songs: dict[str, Any], current_song: dict[str, Any]) -> dict[str, Any] | None:
52 """Get the next song that will play after the current song.
53
54 Args:
55 songs: Dictionary of songs from Radio Paradise block data
56 current_song: The currently playing song dictionary
57
58 Returns:
59 The next song dict, or None if no next song found
60 """
61 current_event = current_song.get("event")
62 current_elapsed = int(current_song.get("elapsed", 0))
63 sorted_keys = sorted(songs.keys(), key=int)
64
65 for song_key in sorted_keys:
66 song = cast("dict[str, Any]", songs[song_key])
67 if song.get("event") != current_event and int(song.get("elapsed", 0)) > current_elapsed:
68 return song
69 return None
70
71
72def build_stream_url(channel_id: str) -> str:
73 """Build the streaming URL for a Radio Paradise channel.
74
75 Args:
76 channel_id: Radio Paradise channel ID (0-5)
77
78 Returns:
79 Streaming URL for the channel, or empty string if not found
80 """
81 if channel_id not in RADIO_PARADISE_CHANNELS:
82 return ""
83
84 channel_info = RADIO_PARADISE_CHANNELS[channel_id]
85 return str(channel_info.get("stream_url", ""))
86
87
88def enhance_title_with_upcoming(
89 title: str,
90 current_song: dict[str, Any],
91 next_song: dict[str, Any] | None,
92 block_data: dict[str, Any] | None,
93) -> str:
94 """Enhance track title with upcoming track info for scrolling display.
95
96 Args:
97 title: Original track title
98 current_song: Current track data
99 next_song: Next track data, or None if not available
100 block_data: Full block data with all upcoming tracks
101
102 Returns:
103 Enhanced title with "Up Next" and "Later" information appended
104 """
105 enhanced_title = title
106
107 # Add next track info
108 if next_song:
109 next_artist = next_song.get("artist", "")
110 next_title = next_song.get("title", "")
111 if next_artist and next_title:
112 enhanced_title += f" | Up Next: {next_artist} - {next_title}"
113
114 # Add later artists in a single pass with deduplication
115 if block_data and "song" in block_data:
116 current_event = current_song.get("event")
117 current_elapsed = int(current_song.get("elapsed", 0))
118 next_event = next_song.get("event") if next_song else None
119
120 # Use set to deduplicate artist names (including next_song artist)
121 seen_artists = set()
122 if next_song:
123 next_artist = next_song.get("artist", "")
124 if next_artist:
125 seen_artists.add(next_artist)
126
127 later_artists = []
128 sorted_keys = sorted(block_data["song"].keys(), key=int)
129 for song_key in sorted_keys:
130 song = block_data["song"][song_key]
131 song_event = song.get("event")
132
133 # Skip current and next song, only include songs that come after current
134 if (
135 song_event not in (current_event, next_event)
136 and int(song.get("elapsed", 0)) > current_elapsed
137 ):
138 artist_name = song.get("artist", "")
139 if artist_name and artist_name not in seen_artists:
140 seen_artists.add(artist_name)
141 later_artists.append(artist_name)
142 if len(later_artists) >= 4: # Limit to 4 artists
143 break
144
145 if later_artists:
146 artists_list = ", ".join(later_artists)
147 enhanced_title += f" | Later: {artists_list}"
148
149 return enhanced_title
150