diff --git a/pyproject.toml b/pyproject.toml index 7be5da5..b4a0541 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,28 +9,23 @@ packages = [ ] [tool.poetry.dependencies] -python = ">=3.10,<4.0" +python = ">=3.11,<4.0" prompt-toolkit = "^3.0.38" -typer = "^0.9.0" -python-dotenv = "^0.21.0" -rich = "^13.7.0" +python-dotenv = "^1.1.1" pyyaml = "^6.0.1" paste = "^3.7.1" -python-daemon = "^3.0.1" requests = "^2.31.0" -psutil = "^5.9.8" -exscript = "^2.6.28" -python-shout = "^0.2.8" -ffmpeg-python = "^0.2.0" python-vlc = "^3.0.21203" pygobject = "3.50.0" +pytest-cov = "^7.0.0" +rich = "^14.1.0" +typer = "^0.17.4" [tool.poetry.scripts] croaker = "croaker.cli:app" [tool.poetry.group.dev.dependencies] pytest = "^8.1.1" -pytest-cov = "^5.0.0" [build-system] requires = ["poetry-core"] diff --git a/src/croaker/cli.py b/src/croaker/cli.py index 75bdb06..de0fe5a 100644 --- a/src/croaker/cli.py +++ b/src/croaker/cli.py @@ -39,9 +39,9 @@ app_state = {} logger = logging.getLogger("cli") -@app.callback() +@app.callback(invoke_without_command=True) def main( - context: typer.Context, + ctx: typer.Context, root: Optional[Path] = typer.Option( Path("~/.dnd/croaker"), help="Path to the Croaker environment", @@ -60,14 +60,15 @@ def main( format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.DEBUG if debug else logging.INFO, ) + if ctx.invoked_subcommand is None: + return start() @app.command() -def setup(context: typer.Context): +def setup(): """ (Re)Initialize Croaker. """ - sys.stderr.write( "Interactive setup is not available, but you can redirect " "this command's output to a defaults file of your choice.\n" @@ -76,13 +77,9 @@ def setup(context: typer.Context): @app.command() -def start( - context: typer.Context, - daemonize: bool = typer.Option(True, help="Daemonize the server."), - shoutcast: bool = typer.Option(True, help="Stream to shoutcast."), -): +def start(): """ - Start the Croaker command and control server. + Start the Croaker audio player. """ player = Player() player.run() @@ -114,4 +111,4 @@ def add( if __name__ == "__main__": - app.main() + app() diff --git a/src/croaker/gui.py b/src/croaker/gui.py index 292972a..f69d202 100644 --- a/src/croaker/gui.py +++ b/src/croaker/gui.py @@ -9,11 +9,10 @@ from croaker.playlist import Playlist, load_playlist gi.require_version("Gtk", "4.0") gi.require_version("Gdk", "4.0") -from gi.repository import GLib, GObject, Gdk, Gtk, Pango # noqa E402 +from gi.repository import Gdk, GLib, GObject, Gtk, Pango # noqa E402 class PlayerWindow(Gtk.ApplicationWindow): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -23,11 +22,9 @@ class PlayerWindow(Gtk.ApplicationWindow): self._artwork_height = 248 css_provider = Gtk.CssProvider() - css_provider.load_from_path(str(path.assets() / 'style.css')) + css_provider.load_from_path(str(path.assets() / "style.css")) Gtk.StyleContext.add_provider_for_display( - Gdk.Display.get_default(), - css_provider, - Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + Gdk.Display.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) self.set_title("Croaker Radio") self._root = Gtk.Fixed() @@ -41,7 +38,6 @@ class PlayerWindow(Gtk.ApplicationWindow): self._draw_window() - def _draw_window(self): margin_size = 8 label_width = self._max_width - (2 * margin_size) @@ -80,7 +76,7 @@ class PlayerWindow(Gtk.ApplicationWindow): def draw_artwork(self): image1 = Gtk.Image() - image1.set_from_file(str(path.assets() / 'froghat.png')) + image1.set_from_file(str(path.assets() / "froghat.png")) image1.set_size_request(self._artwork_width, self._artwork_height) image1.add_css_class("artwork") self._artwork.put(image1, 0, 0) diff --git a/src/croaker/path.py b/src/croaker/path.py index 601d0da..d396974 100644 --- a/src/croaker/path.py +++ b/src/croaker/path.py @@ -10,7 +10,7 @@ def root(): def assets(): - return Path(__file__).parent / 'assets' + return Path(__file__).parent / "assets" def playlist_root(): diff --git a/test/test_pidfile.py b/test/test_pidfile.py deleted file mode 100644 index fe27c2b..0000000 --- a/test/test_pidfile.py +++ /dev/null @@ -1,35 +0,0 @@ -from pathlib import Path -from unittest.mock import MagicMock - -import pytest - -from croaker import pidfile - - -@pytest.mark.parametrize( - "pid,terminate,kill_result,broken", - [ - ("pid", False, None, False), # running proc, no terminate - ("pid", True, True, False), # running proc, terminate - ("pid", True, ProcessLookupError, True), # stale pid - (None, None, None, False), # no running proc - ], -) -def test_pidfile(monkeypatch, pid, terminate, kill_result, broken): - monkeypatch.setattr( - pidfile._pidfile, - "TimeoutPIDLockFile", - MagicMock( - **{ - "return_value.read_pid.return_value": pid, - } - ), - ) - monkeypatch.setattr( - pidfile.os, - "kill", - MagicMock(**{"side_effect": kill_result if type(kill_result) is Exception else [kill_result]}), - ) - - ret = pidfile.pidfile(pidfile_path=Path("/dev/null"), terminate_if_running=terminate) - assert ret.break_lock.called == broken