feat(radiobrowser): add search, paging and favourite lookup

This commit is contained in:
beefcarver committed 2026-11-04 15:11:42 -05:00
1 parent f96923b5b7
commit 638cc9eddb
8 files changed
+1018 -45

No files matched your search

@@ -1,8 +1,9 @@
"""Radio Browser: a directory of the community's stations rather than a station.
Three hundred of the most-clicked working relays, keyless and public-domain.
There is no now-playing API, so this catalogue offers breadth, not history: pick
a station and the stream speaks for itself.
The most-clicked working relays are loaded a page at a time, and the rest of
the 60,000-station directory is searched on the server as you type. There is no
now-playing API, so this catalogue offers breadth, not history: pick a station
and the stream speaks for itself.
"""
from __future__ import annotations
@@ -11,7 +12,7 @@ import httpx
from ... import config
from ...models import Song, Station
from ..base import RadioProvider
from ..base import RadioProvider, StationPage
from . import api, fallback
from . import hero as hero_art
@@ -23,8 +24,8 @@ class RadioBrowserProvider(RadioProvider):
name = "Radio Browser"
homepage = "radio-browser.info"
brand = "RADIO BROWSER"
tagline = "community directory · 50,000 stations"
station_source = "radio-browser.info (top 300)"
tagline = "community directory · live search"
station_source = "radio-browser.info (paged + search)"
history_source = ""
hero = hero_art.HERO
@@ -33,6 +34,14 @@ class RadioBrowserProvider(RadioProvider):
# A directory has no show schedule and no Bandcamp worth scraping.
bandcamp = False
# The full directory is much larger than the list fetched at startup; the
# app asks the API when the filter changes.
server_search = True
# The directory is loaded a page at a time, in click-count order.
paginated = True
preserve_order = True
@property
def user_agent(self) -> str:
return f"{config.USER_AGENT}; +https://www.radio-browser.info"
@@ -40,6 +49,21 @@ class RadioBrowserProvider(RadioProvider):
async def fetch_stations(self, client: httpx.AsyncClient) -> list[Station]:
return await api.fetch_stations(client)
async def fetch_station_page(
self, client: httpx.AsyncClient, offset: int = 0
) -> StationPage:
return await api.fetch_station_page(client, offset)
async def fetch_stations_by_ids(
self, client: httpx.AsyncClient, station_ids: list[str]
) -> list[Station]:
return await api.fetch_stations_by_ids(client, station_ids)
async def search_stations(
self, client: httpx.AsyncClient, query: str
) -> list[Station]:
return await api.search_stations(client, query)
def load_cached_stations(self) -> list[Station]:
return api.load_cached_stations()