feat(tui): implement SomaFM terminal radio player
This commit is contained in:
1 parent
941e45a20f
commit
237de2efe9
21 files changed
+4937
No files matched your search
@@ -0,0 +1,90 @@
|
||||
"""One-line status / key hint bar."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
from rich.cells import cell_len
|
||||
from rich.console import RenderableType
|
||||
from rich.text import Text
|
||||
from textual.widget import Widget
|
||||
|
||||
from .common import compact_count, state_glyph, truncate
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..app import SomaFMApp
|
||||
|
||||
HINTS_LONG = (
|
||||
"SPACE pause ↑↓ station ENTER tune +/- vol f ★ / filter "
|
||||
"v visual r refresh ? help q quit"
|
||||
)
|
||||
HINTS_MEDIUM = (
|
||||
"space pause · ↑↓ station · enter tune · +/- vol · f ★ · / filter · ? help · q quit"
|
||||
)
|
||||
HINTS_SHORT = "↑↓ station · space play · ? help · q quit"
|
||||
|
||||
|
||||
class StatusBar(Widget):
|
||||
can_focus = False
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
app = cast("SomaFMApp", self.app)
|
||||
width = max(20, self.size.width)
|
||||
glyph, colour = state_glyph(app.playing_state)
|
||||
frame = app.spectrum.snapshot()
|
||||
mpris = app.mpris.status_label
|
||||
backend = app.player.backend or "no audio"
|
||||
analyser = frame.label
|
||||
|
||||
listeners = ""
|
||||
if app.current is not None and app.current.listeners:
|
||||
listeners = f"♪ {compact_count(app.current.listeners)} "
|
||||
state = app.playing_state
|
||||
volume = f"vol {app.volume:>3}%"
|
||||
|
||||
# From most to least informative; pick the first combination that fits.
|
||||
right_variants = (
|
||||
f"{state} {listeners}{volume} {backend} mpris {mpris} {analyser}",
|
||||
f"{state} {volume} {analyser}",
|
||||
f"{state} {volume}",
|
||||
volume,
|
||||
)
|
||||
options = (
|
||||
(HINTS_LONG, right_variants[0]),
|
||||
(HINTS_LONG, right_variants[1]),
|
||||
(HINTS_MEDIUM, right_variants[0]),
|
||||
(HINTS_MEDIUM, right_variants[1]),
|
||||
(HINTS_SHORT, right_variants[1]),
|
||||
(HINTS_SHORT, right_variants[2]),
|
||||
(HINTS_SHORT, right_variants[3]),
|
||||
)
|
||||
|
||||
chosen: tuple[int, str, str] | None = None
|
||||
for hint, right_text in options:
|
||||
right_width = cell_len(glyph) + 1 + cell_len(right_text)
|
||||
if cell_len(hint) + right_width + 2 <= width:
|
||||
chosen = (right_width, right_text, hint)
|
||||
break
|
||||
|
||||
if chosen is None:
|
||||
right_text = right_variants[-1]
|
||||
right_width = cell_len(glyph) + 1 + cell_len(right_text)
|
||||
if width <= right_width + 2:
|
||||
line = Text(no_wrap=True)
|
||||
line.append(
|
||||
truncate(f"{glyph} {right_text}", width), style="dim"
|
||||
)
|
||||
return line
|
||||
chosen = (right_width, right_text, "")
|
||||
|
||||
right_width, right_text, hint = chosen
|
||||
left_width = max(0, width - right_width - 2)
|
||||
left = app.notice or hint
|
||||
left_style = "italic yellow" if app.notice else "dim"
|
||||
|
||||
line = Text(no_wrap=True)
|
||||
line.append(truncate(left, left_width), style=left_style)
|
||||
line.append(" " * max(0, width - left_width - right_width))
|
||||
line.append(f"{glyph} ", style=f"bold {colour}")
|
||||
line.append(right_text, style="dim")
|
||||
return line
|
||||
Reference in new issue
Block a user