86 lines
2.7 KiB
Python
86 lines
2.7 KiB
Python
"""Radio Paradise's public endpoints, and nothing but.
|
|
|
|
* ``https://api.radioparadise.com/api/now_playing?chan=N`` -> the current track
|
|
(artist, title, album, year, cover art, seconds remaining)
|
|
* ``https://stream.radioparadise.com/<name>`` -> the icecast relays
|
|
|
|
Radio Paradise curates five mixes; each is one relay and one channel number on
|
|
the now-playing API. The main mix carries lossy relays as well as FLAC; the
|
|
alternate mixes are FLAC only.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
import httpx
|
|
|
|
from ...models import Song
|
|
from ..base import fetch_json
|
|
|
|
NOW_URL = "https://api.radioparadise.com/api/now_playing?chan={chan}"
|
|
STREAM_URL = "https://stream.radioparadise.com/{name}"
|
|
|
|
FORMAT_LABELS = {"flac": ("flac", "highest"), "aac-320": ("aac", "high")}
|
|
|
|
#: (station id, channel, title, genre, relay names)
|
|
CHANNELS: tuple[tuple[str, int, str, str, tuple[str, ...]], ...] = (
|
|
(
|
|
"main",
|
|
0,
|
|
"The Main Mix",
|
|
"eclectic|rock",
|
|
("flac", "aac-320", "mp3-320", "mp3-128"),
|
|
),
|
|
("mellow", 1, "Mellow Mix", "chill|eclectic", ("mellow-flac",)),
|
|
("rock", 2, "RockIt!", "rock|alternative", ("rock-flac",)),
|
|
("global", 3, "The Globe", "world|eclectic", ("global-flac",)),
|
|
("beyond", 5, "Beyond...", "ambient|experimental", ("beyond-flac",)),
|
|
)
|
|
|
|
CHANNEL_BY_STATION = {station_id: chan for station_id, chan, *_ in CHANNELS}
|
|
|
|
|
|
def parse_now(payload: Any) -> Song | None:
|
|
if not isinstance(payload, dict):
|
|
return None
|
|
artist = str(payload.get("artist") or "").strip()
|
|
title = str(payload.get("title") or "").strip()
|
|
if not (artist or title):
|
|
return None
|
|
return Song(
|
|
artist=artist,
|
|
title=title,
|
|
album=str(payload.get("album") or "").strip(),
|
|
art_url=str(payload.get("cover") or payload.get("cover_med") or "").strip(),
|
|
)
|
|
|
|
|
|
async def fetch_now(client: httpx.AsyncClient, chan: int) -> Song | None:
|
|
try:
|
|
payload = await fetch_json(client, NOW_URL.format(chan=chan), attempts=1)
|
|
except RuntimeError:
|
|
return None
|
|
return parse_now(payload)
|
|
|
|
|
|
def playlists(names: tuple[str, ...]) -> list[dict[str, str]]:
|
|
"""The relay list for one mix, best first."""
|
|
entries: list[dict[str, str]] = []
|
|
for name in names:
|
|
fmt, quality = FORMAT_LABELS.get(name, (name.split("-")[0], "high"))
|
|
bitrate = ""
|
|
if fmt == "flac":
|
|
bitrate = "1411" # nominal CD-quality FLAC rate
|
|
elif name.startswith(("aac-", "mp3-")):
|
|
bitrate = name.split("-", 1)[1]
|
|
entries.append(
|
|
{
|
|
"url": STREAM_URL.format(name=name),
|
|
"format": fmt,
|
|
"quality": quality,
|
|
"bitrate": bitrate,
|
|
}
|
|
)
|
|
return entries
|