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
+6
-6
@@ -3,11 +3,11 @@ from __future__ import annotations
|
||||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
from somafm_tui import artwork
|
||||
from somafm_tui.app import SomaFMApp
|
||||
from somafm_tui.widgets import art as art_widgets
|
||||
from somafm_tui.widgets.art import ArtPane, ITerm2Image, KittyImage
|
||||
from somafm_tui.widgets.stage import StagePanel
|
||||
from beefy_player import artwork
|
||||
from beefy_player.app import BeefyPlayerApp
|
||||
from beefy_player.widgets import art as art_widgets
|
||||
from beefy_player.widgets.art import ArtPane, ITerm2Image, KittyImage
|
||||
from beefy_player.widgets.stage import StagePanel
|
||||
|
||||
|
||||
async def _no_fetch(client, url: str) -> None:
|
||||
@@ -27,7 +27,7 @@ async def test_art_pane_switches_between_image_and_mosaic(monkeypatch) -> None:
|
||||
monkeypatch.setattr(artwork, "load_cached", lambda url: None)
|
||||
monkeypatch.setattr(artwork, "fetch", _no_fetch)
|
||||
|
||||
app = SomaFMApp(autoplay=False, offline=True, audio=False)
|
||||
app = BeefyPlayerApp(autoplay=False, offline=True, audio=False)
|
||||
async with app.run_test(size=(110, 40)) as pilot:
|
||||
await pilot.pause()
|
||||
pane = app.query_one(ArtPane)
|
||||
|
||||
@@ -6,7 +6,7 @@ import httpx
|
||||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
from somafm_tui import artwork
|
||||
from beefy_player import artwork
|
||||
|
||||
|
||||
def png_bytes(size=(400, 200)) -> bytes:
|
||||
@@ -28,9 +28,11 @@ def test_protocol_detection() -> None:
|
||||
assert artwork.detect_protocol({"ZELLIJ": "1", "TERM_PROGRAM": "WezTerm"}) is None
|
||||
assert artwork.detect_protocol({"KITTY_PID": "1"}) == "tgp"
|
||||
assert artwork.detect_protocol({"NO_COLOR": "", "KITTY_WINDOW_ID": "1"}) is None
|
||||
assert artwork.detect_protocol({"SOMAFM_IMAGES": "0", "KITTY_WINDOW_ID": "1"}) is None
|
||||
assert artwork.detect_protocol({"SOMAFM_IMAGES": "tgp"}) == "tgp"
|
||||
assert artwork.detect_protocol({"SOMAFM_IMAGES": "iterm2"}) == "iterm2"
|
||||
assert artwork.detect_protocol({"BEEFY_IMAGES": "0", "KITTY_WINDOW_ID": "1"}) is None
|
||||
assert artwork.detect_protocol({"BEEFY_IMAGES": "tgp"}) == "tgp"
|
||||
assert artwork.detect_protocol({"BEEFY_IMAGES": "iterm2"}) == "iterm2"
|
||||
# the pre-rename variable still works
|
||||
assert artwork.detect_protocol({"SOMAFM_IMAGES": "0"}) is None
|
||||
|
||||
|
||||
def test_fit_image_crops_to_the_cell_box() -> None:
|
||||
|
||||
@@ -5,7 +5,7 @@ import json
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from somafm_tui import bandcamp
|
||||
from beefy_player import bandcamp
|
||||
|
||||
TRACK_PAYLOAD = {
|
||||
"auto": {
|
||||
|
||||
@@ -6,7 +6,7 @@ import types
|
||||
import pytest
|
||||
from rich.cells import cell_len
|
||||
|
||||
from somafm_tui.widgets.header import HeaderBar
|
||||
from beefy_player.widgets.header import HeaderBar
|
||||
|
||||
TIME = re.compile(r"\d\d:\d\d:\d\d$")
|
||||
DATE = re.compile(r"[A-Z][a-z]{2} \d\d [A-Z][a-z]{2}$")
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from somafm_tui import macos_tap
|
||||
from beefy_player import macos_tap
|
||||
|
||||
|
||||
def test_osstatus_decodes_four_character_codes() -> None:
|
||||
|
||||
@@ -3,8 +3,8 @@ from __future__ import annotations
|
||||
import queue
|
||||
import sys
|
||||
|
||||
from somafm_tui import mediasession
|
||||
from somafm_tui.mediasession import (
|
||||
from beefy_player import mediasession
|
||||
from beefy_player.mediasession import (
|
||||
MediaSession,
|
||||
NowPlayingSession,
|
||||
create_media_session,
|
||||
@@ -12,7 +12,7 @@ from somafm_tui.mediasession import (
|
||||
|
||||
|
||||
def test_linux_uses_mpris() -> None:
|
||||
from somafm_tui.mpris import MprisService
|
||||
from beefy_player.mpris import MprisService
|
||||
|
||||
session = create_media_session(queue.Queue(), enabled=False, platform="linux")
|
||||
assert isinstance(session, MprisService)
|
||||
|
||||
+4
-4
@@ -4,7 +4,7 @@ import queue
|
||||
|
||||
import pytest
|
||||
|
||||
from somafm_tui import mpris
|
||||
from beefy_player import mpris
|
||||
|
||||
|
||||
def test_build_metadata() -> None:
|
||||
@@ -17,16 +17,16 @@ def test_build_metadata() -> None:
|
||||
stream_url="https://ice2.somafm.com/groovesalad-256-mp3",
|
||||
track_key="groovesalad|Jens Buchert|Rauschebart|Gravity",
|
||||
)
|
||||
assert metadata["mpris:trackid"].value.startswith("/org/somafm/track/")
|
||||
assert metadata["mpris:trackid"].value.startswith("/org/beefyplayer/track/")
|
||||
assert metadata["xesam:title"].value == "Rauschebart"
|
||||
assert metadata["xesam:artist"].value == ["Jens Buchert"]
|
||||
assert metadata["xesam:album"].value == "Gravity"
|
||||
assert metadata["mpris:artUrl"].value == "https://example.com/art.jpg"
|
||||
assert metadata["xesam:url"].value.startswith("https://ice2.")
|
||||
assert metadata["xesam:comment"].value == ["SomaFM · Groove Salad"]
|
||||
assert metadata["xesam:comment"].value == ["Beefy Player · Groove Salad"]
|
||||
|
||||
minimal = mpris.build_metadata(station="X", title="", artist="")
|
||||
assert minimal["xesam:artist"].value == ["SomaFM"]
|
||||
assert minimal["xesam:artist"].value == ["Beefy Player"]
|
||||
assert "xesam:album" not in minimal
|
||||
assert "mpris:artUrl" not in minimal
|
||||
|
||||
|
||||
+11
-11
@@ -5,7 +5,7 @@ import queue
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from somafm_tui.player import Player
|
||||
from beefy_player.player import Player
|
||||
|
||||
FAKE_MPV = '''#!/usr/bin/env python3
|
||||
import json, os, socket, sys, time
|
||||
@@ -171,27 +171,27 @@ def test_backend_detection_without_tools(tmp_path, monkeypatch) -> None:
|
||||
def test_media_key_messages_become_player_events() -> None:
|
||||
player = Player()
|
||||
player._handle_mpv_message(
|
||||
{"event": "client-message", "args": ["somafm", "next"]}
|
||||
{"event": "client-message", "args": ["beefy", "next"]}
|
||||
)
|
||||
event = player.events.get_nowait()
|
||||
assert event.kind == "media"
|
||||
assert event.detail == "next"
|
||||
|
||||
player._handle_mpv_message(
|
||||
{"event": "client-message", "args": ["somafm", "toggle"]}
|
||||
{"event": "client-message", "args": ["beefy", "toggle"]}
|
||||
)
|
||||
assert player.events.get_nowait().detail == "toggle"
|
||||
|
||||
# Foreign namespaces, unknown commands and malformed payloads are ignored.
|
||||
player._handle_mpv_message({"event": "client-message", "args": ["other", "next"]})
|
||||
player._handle_mpv_message({"event": "client-message", "args": ["somafm", "seek"]})
|
||||
player._handle_mpv_message({"event": "client-message", "args": ["beefy", "seek"]})
|
||||
player._handle_mpv_message({"event": "client-message", "args": None})
|
||||
player._handle_mpv_message({"event": "client-message"})
|
||||
assert player.events.empty()
|
||||
|
||||
|
||||
def test_platform_options_never_leak_to_linux(tmp_path, monkeypatch) -> None:
|
||||
import somafm_tui.player as player_module
|
||||
import beefy_player.player as player_module
|
||||
|
||||
monkeypatch.setattr(player_module, "RUNTIME_DIR", tmp_path)
|
||||
assert Player()._platform_options("linux") == []
|
||||
@@ -203,12 +203,12 @@ def test_platform_options_never_leak_to_linux(tmp_path, monkeypatch) -> None:
|
||||
assert conf.endswith("mpv-input.conf")
|
||||
bindings = (tmp_path / "mpv-input.conf").read_text()
|
||||
for line in (
|
||||
"PLAY script-message somafm toggle",
|
||||
"PLAYONLY script-message somafm play",
|
||||
"PAUSEONLY script-message somafm pause",
|
||||
"STOP script-message somafm stop",
|
||||
"NEXT script-message somafm next",
|
||||
"PREV script-message somafm prev",
|
||||
"PLAY script-message beefy toggle",
|
||||
"PLAYONLY script-message beefy play",
|
||||
"PAUSEONLY script-message beefy pause",
|
||||
"STOP script-message beefy stop",
|
||||
"NEXT script-message beefy next",
|
||||
"PREV script-message beefy prev",
|
||||
):
|
||||
assert line in bindings
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
from somafm_tui import spectrum as spectrum_module
|
||||
from somafm_tui.spectrum import Frame, SpectrumEngine
|
||||
from beefy_player import spectrum as spectrum_module
|
||||
from beefy_player.spectrum import Frame, SpectrumEngine
|
||||
|
||||
PCM_SCRIPT = """#!/usr/bin/env python3
|
||||
import math, struct, sys, time
|
||||
@@ -42,7 +42,7 @@ def tap_pcm(
|
||||
|
||||
|
||||
class FakeTap:
|
||||
"""Stand-in for ``somafm_tui.macos_tap.SystemAudioTap``."""
|
||||
"""Stand-in for ``beefy_player.macos_tap.SystemAudioTap``."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -4,9 +4,9 @@ from __future__ import annotations
|
||||
|
||||
import math
|
||||
|
||||
from somafm_tui import config
|
||||
from somafm_tui.spectrum import Frame, SpectrumEngine
|
||||
from somafm_tui.widgets.spectrum import (
|
||||
from beefy_player import config
|
||||
from beefy_player.spectrum import Frame, SpectrumEngine
|
||||
from beefy_player.widgets.spectrum import (
|
||||
VISUALIZER_LABELS,
|
||||
WATERFALL_HISTORY,
|
||||
SpectrumPanel,
|
||||
|
||||
+9
-9
@@ -5,15 +5,15 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from somafm_tui.app import SomaFMApp
|
||||
from somafm_tui.widgets.header import HeaderBar
|
||||
from somafm_tui.widgets.search import SearchInput
|
||||
from somafm_tui.widgets.spectrum import SpectrumPanel
|
||||
from somafm_tui.widgets.stage import StagePanel
|
||||
from somafm_tui.widgets.station_list import StationList
|
||||
from somafm_tui.widgets.status import StatusBar
|
||||
from beefy_player.app import BeefyPlayerApp
|
||||
from beefy_player.widgets.header import HeaderBar
|
||||
from beefy_player.widgets.search import SearchInput
|
||||
from beefy_player.widgets.spectrum import SpectrumPanel
|
||||
from beefy_player.widgets.stage import StagePanel
|
||||
from beefy_player.widgets.station_list import StationList
|
||||
from beefy_player.widgets.status import StatusBar
|
||||
|
||||
PACKAGE = Path(__file__).resolve().parents[1] / "somafm_tui"
|
||||
PACKAGE = Path(__file__).resolve().parents[1] / "beefy_player"
|
||||
|
||||
|
||||
def test_no_rgb_colors_in_source() -> None:
|
||||
@@ -29,7 +29,7 @@ def test_no_rgb_colors_in_source() -> None:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backgrounds_use_terminal_default_and_borders_are_ansi() -> None:
|
||||
app = SomaFMApp(autoplay=False, offline=True, audio=False)
|
||||
app = BeefyPlayerApp(autoplay=False, offline=True, audio=False)
|
||||
async with app.run_test(size=(100, 30)) as pilot:
|
||||
await pilot.pause()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from somafm_tui.widgets import common
|
||||
from beefy_player.widgets import common
|
||||
|
||||
BERLIN = Path("/usr/share/zoneinfo/Europe/Berlin")
|
||||
UTC_FILE = Path("/usr/share/zoneinfo/UTC")
|
||||
|
||||
Reference in new issue
Block a user