feat(ui): add catalogue picker, schedule pane and per-provider hero
This commit is contained in:
1 parent
058d336a2c
commit
b31ad3f6fb
16 files changed
+1211
-65
No files matched your search
@@ -18,6 +18,7 @@ from __future__ import annotations
|
||||
|
||||
from .base import RadioProvider
|
||||
from .somafm import SomaFMProvider
|
||||
from .tilderadio import TilderadioProvider
|
||||
|
||||
__all__ = [
|
||||
"DEFAULT_PROVIDER",
|
||||
@@ -29,7 +30,7 @@ __all__ = [
|
||||
|
||||
#: Every catalogue the app can talk to, keyed by its slug.
|
||||
PROVIDERS: dict[str, RadioProvider] = {
|
||||
provider.id: provider for provider in (SomaFMProvider(),)
|
||||
provider.id: provider for provider in (SomaFMProvider(), TilderadioProvider())
|
||||
}
|
||||
|
||||
#: Provider used when nothing else is configured.
|
||||
|
||||
@@ -24,7 +24,7 @@ from urllib.parse import quote
|
||||
import httpx
|
||||
|
||||
from .. import config
|
||||
from ..models import Song, Station
|
||||
from ..models import Broadcast, Song, Station
|
||||
|
||||
__all__ = [
|
||||
"PLAYLIST_TTL",
|
||||
@@ -92,6 +92,28 @@ class RadioProvider(ABC):
|
||||
#: ``User-Agent`` for this catalogue's requests (SomaFM likes to know).
|
||||
user_agent: str = ""
|
||||
|
||||
# -- header hero ----------------------------------------------------- #
|
||||
|
||||
#: Rows of header art drawn at the top left (rich markup is allowed).
|
||||
hero: tuple[str, ...] = ()
|
||||
#: Paint :attr:`hero` with the app's spectrum gradient (a wordmark) instead
|
||||
#: of the colours the art carries itself (a picture).
|
||||
hero_gradient: bool = False
|
||||
#: Short lines printed beside the hero, top to bottom.
|
||||
hero_lines: tuple[str, ...] = ()
|
||||
|
||||
# -- left pane ------------------------------------------------------- #
|
||||
|
||||
#: Show a broadcast schedule in the left pane instead of a station list.
|
||||
#: Set by single-stream catalogues, where a list of one station is a waste
|
||||
#: of the pane and "what is on, what is next" is the useful view.
|
||||
has_schedule: bool = False
|
||||
|
||||
#: Look the current track up on Bandcamp and offer a link when it is found.
|
||||
#: Off for catalogues whose rotation is unlikely to be there (and for
|
||||
#: stations that would rather not be scraped).
|
||||
bandcamp: bool = True
|
||||
|
||||
# -- catalogue ------------------------------------------------------- #
|
||||
|
||||
@abstractmethod
|
||||
@@ -118,6 +140,16 @@ class RadioProvider(ABC):
|
||||
def load_cached_history(self, station_id: str) -> list[Song]:
|
||||
"""The last good song history from disk, or ``[]``."""
|
||||
|
||||
# -- schedule -------------------------------------------------------- #
|
||||
|
||||
async def fetch_schedule(self, client: httpx.AsyncClient) -> list[Broadcast]:
|
||||
"""Upcoming broadcasts, soonest first (the live one, if any, first)."""
|
||||
return []
|
||||
|
||||
def load_cached_schedule(self) -> list[Broadcast]:
|
||||
"""The last good schedule from disk, or ``[]``."""
|
||||
return []
|
||||
|
||||
# -- streams --------------------------------------------------------- #
|
||||
|
||||
def fallback_urls(self, station: Station) -> list[str]:
|
||||
|
||||
@@ -15,6 +15,7 @@ from ... import config
|
||||
from ...models import Song, Station
|
||||
from ..base import RadioProvider
|
||||
from . import api, fallback
|
||||
from . import hero as hero_art
|
||||
|
||||
__all__ = ["SomaFMProvider"]
|
||||
|
||||
@@ -28,6 +29,10 @@ class SomaFMProvider(RadioProvider):
|
||||
station_source = "somafm.com/channels.json"
|
||||
history_source = "songs/<station>.json"
|
||||
|
||||
hero = hero_art.LOGO
|
||||
hero_gradient = True
|
||||
hero_lines = hero_art.TAGLINES
|
||||
|
||||
@property
|
||||
def user_agent(self) -> str:
|
||||
# SomaFM asks clients to identify themselves, so say who we are and
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
"""SomaFM's header hero: the block-letter wordmark and the three lines beside it.
|
||||
|
||||
The glyphs are plain half-blocks; the header paints them with the app's
|
||||
spectrum gradient (``hero_gradient``), so the logo takes on the terminal's own
|
||||
colours instead of carrying RGB values around.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
LOGO: tuple[str, ...] = (
|
||||
"▄▀▀▀ ▄▀▀▄ █▄ ▄█ ▄▀▀▄",
|
||||
"▀▀▀▄ █ █ █ █ █ █▀▀█",
|
||||
"▄▄▄▀ ▀▄▄▀ █ █ █ █",
|
||||
)
|
||||
|
||||
TAGLINES: tuple[str, ...] = (
|
||||
"SOMA FM RADIO",
|
||||
"LISTENER SUPPORTED",
|
||||
"SAN FRANCISCO",
|
||||
)
|
||||
Reference in new issue
Block a user