WIP: tui
This commit is contained in:
parent
76aaaa0a45
commit
d716b5628a
|
@ -0,0 +1,23 @@
|
|||
/* 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%;
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
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()
|
Loading…
Reference in New Issue