feat(tilderadio): add the tildeverse community stream as a catalogue

This commit is contained in:
beefcarver committed 2026-11-03 17:51:23 -05:00
1 parent 58e9202e25
commit 058d336a2c
8 files changed
+883 -4

No files matched your search

+38 -1
View File
@@ -40,6 +40,25 @@ class Song:
return f"{self.artist}\x1f{self.title}\x1f{self.album}"
@dataclass(slots=True)
class Broadcast:
"""One entry in a catalogue's schedule: a show, a DJ slot, a stream."""
name: str
dj: str = ""
start: datetime | None = None
end: datetime | None = None
#: True for the broadcast that is on air right now.
live: bool = False
@property
def display_name(self) -> str:
return self.name.strip() or self.dj.strip() or "untitled broadcast"
def key(self) -> str:
return f"{self.name}\x1f{self.start.isoformat() if self.start else ''}"
@dataclass(slots=True)
class Station:
id: str
@@ -47,6 +66,9 @@ class Station:
description: str = ""
genre: str = ""
dj: str = ""
#: Who is presenting right now when that is more than a DJ name —
#: ``"auto dj · library rotation"`` for a station between live shows.
presenter: str = ""
image: str = ""
listeners: int = 0
last_playing: str = ""
@@ -69,6 +91,15 @@ class Station:
def genre_label(self) -> str:
return self.genre.replace("|", " · ") or "eclectic"
@property
def presenter_label(self) -> str:
"""What the ON AIR metadata line says about who is on the desk."""
if self.presenter.strip():
return self.presenter.strip()
if self.dj.strip():
return f"dj {self.dj.strip()}"
return ""
def sorted_playlists(self) -> list[dict[str, str]]:
return sorted(
self.playlists,
@@ -87,7 +118,7 @@ class Station:
fmt = _FORMAT_LABEL.get(
first.get("format", ""), first.get("format") or "stream"
)
return f"{fmt} · {guess_bitrate(first.get('url', ''))}k"
return f"{fmt} · {playlist_bitrate(first)}k"
# --------------------------------------------------------------------------- #
@@ -95,6 +126,12 @@ class Station:
# --------------------------------------------------------------------------- #
def playlist_bitrate(playlist: dict[str, str]) -> int:
"""The bitrate a catalogue declared, else the best guess from the URL."""
declared = to_int(playlist.get("bitrate"))
return declared or guess_bitrate(playlist.get("url", ""))
def guess_bitrate(url: str) -> int:
match = re.search(
r"(256|192|130|128|96|64|48|32)(?=\.pls|\.m3u|-mp3|-aac)", url