Compare commits

..

11 Commits
tui ... master

Author SHA1 Message Date
fde738910d
README: update project url 2025-01-06 16:53:15 +01:00
546d921ec4 flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/d8fe5e6c92d0d190646fb9f1056741a229980089' (2024-03-29)
  → 'github:NixOS/nixpkgs/c04d5652cfa9742b1d519688f65d1bbccea9eb7e' (2024-09-19)
2024-09-20 21:52:14 +02:00
cb292a56b1 Merge pull request 'webui: handle playback_pos response with empty contents' (#13) from handle-empty-playback-pos-response into master
Reviewed-on: Projects/grzegorz-clients#13
Reviewed-by: Peder Bergebakken Sundt <pederbs@pvv.ntnu.no>
2024-08-05 21:16:57 +02:00
132cabf434
webui: handle playback_pos response with empty contents 2024-08-05 17:53:15 +02:00
4617f83f5c Merge pull request 'cli: fix relative volume adjustment' (#12) from fix-new-cli-features into master
Reviewed-on: Projects/grzegorz-clients#12
2024-06-02 02:38:17 +02:00
047d09ffb1 cli: fix relative volume adjustment 2024-05-25 23:58:08 +02:00
b9444658fb
Merge pull request #11 from Programvareverkstedet/more_cli_features
More stuff please
2024-05-19 12:48:15 +02:00
0c45cbe1f6 flake: add grzegorzctl-only package which installs shell-completions 2024-05-19 06:39:23 +02:00
ca78aa9e22 cli: allow relative volume adjustment using {+,-}<n>{,%} 2024-05-19 06:15:54 +02:00
2b8ecf124d cli: add function to toggle playback status 2024-05-19 06:15:54 +02:00
1009b29995 cli: fix spelling error in queue adding commands 2024-05-19 06:07:52 +02:00
9 changed files with 454 additions and 1023 deletions

View File

@ -13,7 +13,7 @@ A set of simple API endpoints and ready-to-go clients to interface with the [Grz
## How to run this
pip install --user git+https://github.com/Programvareverkstedet/grzegorz_clients.git#master
pip install --user git+https://git.pvv.ntnu.no/Grzegorz/grzegorz-clients.git#master
### cli
@ -23,7 +23,7 @@ A set of simple API endpoints and ready-to-go clients to interface with the [Grz
As the user intended to run the server:
pip install --user git+https://github.com/Programvareverkstedet/grzegorz_clients.git#master
pip install --user git+https://git.pvv.ntnu.no/Grzegorz/grzegorz-clients.git#master
grzegorz-webui --host-name 0.0.0.0 --port 80
It's rather insecure and could use a reverse proxy and some whitelisting. ;)

6
flake.lock generated
View File

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1711703276,
"narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=",
"lastModified": 1726755586,
"narHash": "sha256-PmUr/2GQGvFTIJ6/Tvsins7Q43KTMvMFhvG6oaYK+Wk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d8fe5e6c92d0d190646fb9f1056741a229980089",
"rev": "c04d5652cfa9742b1d519688f65d1bbccea9eb7e",
"type": "github"
},
"original": {

View File

@ -57,14 +57,28 @@
nativeBuildInputs = [ poetry-core ];
propagatedBuildInputs = [ setuptools flakes.self.pkgs.remi requests typer rich urllib3 ];
};
default = flakes.self.pkgs.grzegorz-clients;
grzegorzctl = pkgs.runCommandNoCCLocal "grzegorzctl" (
{
nativeBuildInputs = [ pkgs.installShellFiles ];
} //
{ inherit (flakes.self.pkgs.grzegorz-clients) meta; } //
{ meta.mainProgram = "grzegorzctl"; }
)''
mkdir -p $out/bin
ln -s "${flakes.self.pkgs.grzegorz-clients}/bin/grzegorzctl" $out/bin/grzegorzctl
installShellCompletion --cmd grzegorzctl \
--bash <($out/bin/grzegorzctl --show-completion bash) \
--zsh <($out/bin/grzegorzctl --show-completion zsh) \
--fish <($out/bin/grzegorzctl --show-completion fish)
'';
default = flakes.self.pkgs.grzegorzctl;
});
apps = forAllSystems ({ system, ...}: rec {
grzegorz-webui.type = "app";
grzegorz-webui.program = "${self.packages.${system}.grzegorz-clients}/bin/grzegorz-webui";
grzegorzctl.type = "app";
grzegorzctl.program = "${self.packages.${system}.grzegorz-clients}/bin/grzegorzctl";
grzegorzctl.program = "${self.packages.${system}.grzegorzctl}/bin/grzegorzctl";
default = grzegorzctl;
});

View File

@ -64,7 +64,7 @@ def _add(
api.playlist_goto(current_index if put_pre else current_index + 1)
api.set_playing(True)
@cli.command(help="Add one ore more items to the playlist")
@cli.command(help="Add one or more items to the playlist")
def play(
urls: list[str],
pre: bool = False,
@ -72,14 +72,14 @@ def play(
):
_add(urls, put_post=not pre, put_pre=pre, play=True, api_base=api_base)
@cli.command(help="Add one ore more items to the playlist")
@cli.command(help="Add one or more items to the playlist")
def next(
urls: list[str],
api_base: str = DEFAULT_API_BASE,
):
_add(urls, put_post=True, api_base=api_base)
@cli.command(help="Add one ore more items to the playlist")
@cli.command(help="Add one or more items to the playlist")
def queue(
urls: list[str],
play: bool = True,
@ -107,6 +107,12 @@ def pause( api_base: str = DEFAULT_API_BASE ):
api.set_endpoint(api_base)
rich.print(api.set_playing(False), file=sys.stderr)
@cli.command(help="Toggle playback")
def toggle(api_base: str = DEFAULT_API_BASE):
api.set_endpoint(api_base)
playing = api.is_playing()
rich.print(api.set_playing(not playing), file=sys.stderr)
@cli.command(help="Goto next item in playlist")
def skip( api_base: str = DEFAULT_API_BASE ):
api.set_endpoint(api_base)
@ -154,11 +160,21 @@ def status(
@cli.command(help="Set the playback volume")
def set_volume(
volume: int,
volume: str,
api_base: str = DEFAULT_API_BASE,
):
api.set_endpoint(api_base)
rich.print(api.set_volume(volume), file=sys.stderr)
volume = volume.removesuffix("%")
if volume.startswith("+") or volume.startswith("-"):
current_volume = api.get_volume()
new_volume = max(0, min(100, current_volume + int(volume)))
new_volume = int(new_volume)
else:
new_volume = int(volume)
rich.print(api.set_volume(new_volume), file=sys.stderr)
if __name__ == "__main__":

View File

@ -1,23 +0,0 @@
/* https://textual.textualize.io/guide/design/#theme-reference */
Screen {
align: center top;
box-sizing: border-box;
}
Horizontal {
height: auto;
}
#header {
height: 1;
overflow: hidden;
}
#controls {
background: $background-lighten-2;
align: center middle;
}
#volumebar {
}
#seekbar {
}
DataTable#playlist {
width: 100%;
}

View File

@ -294,7 +294,7 @@ class RemiApp(App):
except api.APIError:
playback_pos = None
if playback_pos:
if playback_pos and isinstance(playback_pos, dict) and playback_pos["current"] and playback_pos["total"]:
slider_pos = playback_pos["current"] / playback_pos["total"] * 100
current = seconds_to_timestamp(playback_pos["current"])
total = seconds_to_timestamp(playback_pos["total"])

View File

@ -1,172 +0,0 @@
from . import api, utils
from pathlib import Path
from datetime import timedelta
import json
import os
import rich
import shutil
import subprocess
import sys
import typer
from textual.app import App, ComposeResult
from textual.containers import Vertical, Horizontal, VerticalScroll
from textual.widgets import Button, Static, ProgressBar, DataTable
from textual.timer import Timer
from textual import on
from textual.app import App, ComposeResult
import textual.containers as c
from textual.reactive import var
import textual.widgets as w
from textual.events import Mount
from textual.widgets import DirectoryTree, Footer, Header, Static
# export GRZEGORZ_DEFAULT_API_BASE="https://brzeczyszczykiewicz.pvv.ntnu.no/api"
# export GRZEGORZ_DEFAULT_API_BASE="https://georg.pvv.ntnu.no/api"
DEFAULT_API_BASE = os.environ.get("GRZEGORZ_DEFAULT_API_BASE", "https://brzeczyszczykiewicz.pvv.ntnu.no/api")
# url input
# prev - Button
# play/pause - Button
# next - Button
# loop - Switch
# shuffle - Button
# clear - Button
# position - ProgressBar
# playlist - datatable
# waveform - Sparkline
# LoadingIndicator
class GrzegorzApp(App):
CSS_PATH = "gzregorz.tcss"
#DEFAULT_CSS = ""
BINDINGS = [
("q", "quit", "Quit"),
]
refresh_timer: Timer
def compose(self) -> ComposeResult:
yield Static("Now playing: ", id="header")
yield Horizontal(
ProgressBar(id="seekbar", total=0, show_percentage=False, show_eta=False),
Static(" --:-- --:--", id="playtime"),
classes="center"
)
yield Horizontal(
ProgressBar(id="volumebar", total=100, show_eta=False),
classes="center"
)
yield Horizontal(
Button.error("Clear", id="btn-clear"),
Button("Prev", id="btn-prev"),
Button.success("Play", id="btn-play"),
Button("Next", variant="primary", id="btn-next"),
Button.warning("Shuffle", id="btn-shuffle"),
id="controls",
)
yield VerticalScroll(
DataTable(
id="playlist",
zebra_stripes=True,
cursor_type="row",
),
)
def on_mount(self) -> None:
api.set_endpoint("https://georg.pvv.ntnu.no/api")
self.refresh_timer = self.set_interval(1 / 1, self.do_update) # threaded
# self.log(api.get_playlist())
playlist: DataTable = self.query_one("#playlist")
playlist.add_columns("#", "Name", "length")
def do_update(self):
self.log("do_update")
# update status
vol = api.get_volume()
pos = api.get_playback_pos() or {"current": 0, "left": 0, "total": 0}
loop = api.get_playlist_looping()
play = api.is_playing()
self.log(f"""
{vol = }
{pos = }
{loop = }
{play = }
""")
# todo: sliders?
seek_var: ProgressBar = self.query_one("#seekbar")
seek_var.total = pos.get("total", 0)
seek_var.progress = pos.get("current", 0)
playtime: Static = self.query_one("#playtime")
playtime.update(f" --:-- - --:--" if not play else f" {timedelta(seconds=int(pos.get('current', 0)))} - {timedelta(seconds=int(pos.get('total', 0)))}")
vol_var: ProgressBar = self.query_one("#volumebar")
vol_var.progress = vol
btn_play: Button = self.query_one("#btn-play")
btn_play.label = "Pause" if play else "Play"
# btn_play.variant = "success" if play else "default"
# update playlist
playlist_data = api.get_playlist()
table = [
(
item.get("index", -1),
item.get("data", {}).get("title", None) or item["filename"],
utils.seconds_to_timestamp(item["data"]["duration"]) if "duration" in item.get("data", {}) else "--:--",
)
for item in playlist_data
]
current, = [item for item in playlist_data if item.get("current", False)][:1] or [None]
if current is not None:
self.query_one("#header").update("Now playing: " +
current.get("data", {}).get("title", None) or current["filename"]
)
playlist: DataTable = self.query_one("#playlist")
for r, row in enumerate(table[:playlist.row_count]):
for c, col in enumerate(row):
playlist.update_cell_at((r, c), col)
if playlist.row_count < len(table): # add more rows
playlist.add_rows(table[playlist.row_count:])
elif playlist.row_count > len(table): # remove extra rows
for i in range(playlist.row_count-1, len(table)-1, -1):
playlist.remove_row(playlist._row_locations.get_key(i))
def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-clear":
api.playlist_clear()
elif event.button.id == "btn-prev":
api.playlist_previous()
elif event.button.id == "btn-play":
api.set_playing(not api.is_playing())
elif event.button.id == "btn-next":
api.playlist_next()
elif event.button.id == "btn-shuffle":
api.playlist_shuffle()
@on(DataTable.RowSelected)
def on_data_table_row_selected(self, event: DataTable.RowSelected):
self.log("spismeg")
self.log(f"{event = }")
self.log(f"{event.cursor_row = }")
self.log(f"{event.row_key = }")
self.log("spismeg")
def main():
app = GrzegorzApp()
app.run()
if __name__ == "__main__":
main()

1217
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -6,22 +6,19 @@ authors = ["Peder Bergebakken Sundt <pbsds@hotmail.com>"]
license = "MIT"
[tool.poetry.dependencies]
python = ">=3.8.1,<4.0"
python = ">=3.7,<4.0"
remi = "1.0"
requests = ">=2.27.1,<3"
rich = ">=13.3.5"
typer = ">=0.4.0"
urllib3 = ">=1.26.8,<3"
textual = {version = "^0.61.0", extras = ["tui"]}
[tool.poetry.group.dev.dependencies]
python-lsp-server = {extras = ["all"], version = "^1.11.0"}
textual-dev = "^1.5.1"
[tool.poetry.dev-dependencies]
python-lsp-server = {extras = ["all"], version = "^1.5.0"}
[tool.poetry.scripts]
grzegorz-webui = "grzegorz_clients.__main__:cli"
grzegorzctl = "grzegorz_clients.cli:cli"
grzegorztui = "grzegorz_clients.tui:main"
[build-system]
requires = ["poetry-core>=1.0.0"]