refactor!: rename somafm-tui to beefy-player
The app is now a general internet-radio player rather than a SomaFM client, so the package, console script, MPRIS identity, mpv namespaces, macOS tap names, XDG directories and docs all move to beefy-player. Environment variables are now BEEFY_* (the old SOMAFM_* spellings are still honoured) and an existing ~/.config/somafm-tui/config.json is migrated on first run, so favourites and volume survive the rename. The visible interface is untouched: same layout, same logo, same keys.
This commit is contained in:
1 parent
4bed90b489
commit
e30ca09627
44 files changed
+329
-192
No files matched your search
@@ -0,0 +1,97 @@
|
||||
"""Full-screen keyboard help."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from rich.console import Group
|
||||
from rich.table import Table
|
||||
from rich.text import Text
|
||||
from textual.app import ComposeResult
|
||||
from textual.screen import ModalScreen
|
||||
from textual.widgets import Static
|
||||
|
||||
from .common import SPECTRUM_STOPS, gradient
|
||||
|
||||
HELP_ROWS: tuple[tuple[str, str], ...] = (
|
||||
("↑ / ↓ j / k", "move through the station list"),
|
||||
("enter", "tune in to the selected station"),
|
||||
("space", "play / pause the current station"),
|
||||
("s", "stop playback"),
|
||||
("] / [", "next / previous station (tunes immediately)"),
|
||||
("+ / -", "volume up / down"),
|
||||
("m", "mute / unmute"),
|
||||
("f", "favourite — favourites float to the top"),
|
||||
("/", "filter stations by title, genre or description"),
|
||||
("esc", "clear the filter / close overlays"),
|
||||
(
|
||||
"v",
|
||||
"cycle visualiser: mirror → bars → wave → waterfall → radial → braille",
|
||||
),
|
||||
("r", "refresh station list and song history"),
|
||||
("g / G", "jump to the top / bottom of the list"),
|
||||
("? ", "this help"),
|
||||
("q", "quit"),
|
||||
)
|
||||
|
||||
ABOUT_COMMON = (
|
||||
(
|
||||
"Stations and song history come from somafm.com "
|
||||
"(channels.json + songs/<station>.json)."
|
||||
),
|
||||
)
|
||||
|
||||
ABOUT_LINUX = (
|
||||
(
|
||||
"The spectrum captures your actual PulseAudio/PipeWire output with "
|
||||
"parec (SYNC); otherwise it falls back to a muted ffmpeg stream tap "
|
||||
"(FFT, can drift) or a synthesized display (SIM)."
|
||||
),
|
||||
(
|
||||
"The desktop picks us up over MPRIS as a "
|
||||
"somafmtui media player — try `playerctl -l` / `playerctl metadata`."
|
||||
),
|
||||
)
|
||||
|
||||
ABOUT_MACOS = (
|
||||
(
|
||||
"The spectrum captures your Mac's real audio output (SYNC) with a "
|
||||
"native Core Audio process tap — nothing to install. macOS asks for "
|
||||
"System Audio Recording permission the first time; without it the "
|
||||
"analyser falls back to a muted ffmpeg stream tap (FFT, can drift) "
|
||||
"or a synthesized display (SIM)."
|
||||
),
|
||||
(
|
||||
"mpv publishes what is playing to the macOS Now Playing session, so "
|
||||
"Control Center and the keyboard media keys control this app."
|
||||
),
|
||||
)
|
||||
|
||||
ABOUT = ABOUT_COMMON + (ABOUT_MACOS if sys.platform == "darwin" else ABOUT_LINUX)
|
||||
|
||||
|
||||
class HelpScreen(ModalScreen[None]):
|
||||
def compose(self) -> ComposeResult:
|
||||
table = Table.grid(padding=(0, 2), expand=False)
|
||||
table.add_column(style="bold cyan", no_wrap=True, justify="right")
|
||||
table.add_column(style="")
|
||||
for key, description in HELP_ROWS:
|
||||
table.add_row(key, description)
|
||||
|
||||
header = Text()
|
||||
header.append("◤ ", style=f"bold {gradient(SPECTRUM_STOPS, 0.9)}")
|
||||
header.append("SOMAFM TUI", style=f"bold {gradient(SPECTRUM_STOPS, 0.55)}")
|
||||
header.append(" keyboard & credits", style="dim")
|
||||
|
||||
footer = Text()
|
||||
for paragraph in ABOUT:
|
||||
for line in textwrap.wrap(paragraph, width=70):
|
||||
footer.append(line + "\n", style="dim")
|
||||
|
||||
body = Group(header, Text(""), table, Text(""), footer)
|
||||
yield Static(body, id="help")
|
||||
|
||||
def on_key(self, event) -> None:
|
||||
event.stop()
|
||||
self.dismiss(None)
|
||||
Reference in new issue
Block a user