New UI for player screen
This commit is contained in:
1 parent
c0b99d8e1e
commit
ada1ef5fba
2 files changed
+187
-18
No files matched your search
@@ -34,9 +34,9 @@ from textual import on, events, work
|
||||
from textual.binding import Binding
|
||||
from textual.reactive import reactive
|
||||
from textual.app import App, ComposeResult, RenderResult
|
||||
from textual.widgets import Footer, Header, OptionList, DataTable, ProgressBar, Label, Input, Placeholder, Static, Rule
|
||||
from textual.widgets import Footer, Header, OptionList, DataTable, ProgressBar, Label, Input, Placeholder, Static, Rule, Button
|
||||
from textual.widget import Widget
|
||||
from textual.containers import Horizontal, Vertical, Container
|
||||
from textual.containers import Horizontal, Vertical, Container, Center
|
||||
from textual.screen import Screen
|
||||
from textual.widgets.option_list import Option
|
||||
from textual.timer import Timer
|
||||
@@ -959,6 +959,99 @@ class MainView(Screen):
|
||||
queue_table.update_queue()
|
||||
app.update_now_playing()
|
||||
|
||||
class CurrentPlace(Static):
|
||||
place: reactive[int] = reactive(0)
|
||||
|
||||
def render(self) -> RenderResult:
|
||||
# if app.currently_playing:
|
||||
# duration_min, duration_sec = divmod(app.currently_playing.duration, 60)
|
||||
# duration_formatted = f"{duration_min:02d}:{duration_sec:02d}"
|
||||
|
||||
place_in_seconds = self.place // 1000
|
||||
place_min, place_sec = divmod(place_in_seconds, 60)
|
||||
place_formatted = f"{place_min:02d}:{place_sec:02d}"
|
||||
|
||||
return place_formatted
|
||||
|
||||
class TotalDuration(Static):
|
||||
duration: reactive[int] = reactive(0)
|
||||
|
||||
def render(self) -> RenderResult:
|
||||
duration_min, duration_sec = divmod(self.duration, 60)
|
||||
duration_formatted = f"{duration_min:02d}:{duration_sec:02d}"
|
||||
|
||||
return duration_formatted
|
||||
|
||||
class Player(Screen):
|
||||
CSS_PATH = 'main.tcss'
|
||||
|
||||
def update_song(self, song: SubSonic.Song) -> None:
|
||||
# info = self.query_one(NowPlayingInfo)
|
||||
# info.song = song
|
||||
|
||||
if song.albumId:
|
||||
track_title_label: Label = self.query_one('#trackTitle', Label)
|
||||
track_artist_album_label: Label = self.query_one('#trackAlbumArtist', Label)
|
||||
|
||||
track_title_label.update(song.title)
|
||||
track_artist_album_label.update(f'{song.album} by {song.artist}')
|
||||
|
||||
cover_art = self.query_one(CoverArtPixels)
|
||||
|
||||
album = conn.getAlbumSongs(song.albumId)
|
||||
if album:
|
||||
cover_id: str | None = album[0].coverArtId
|
||||
if cover_id:
|
||||
cover_art.get_cover_art(cover_id)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Header(icon="")
|
||||
with Horizontal(id="headerbs"):
|
||||
yield Label(Text("* F1 ", style="bold orange").append_text(Text("Player", style="white")), classes="headerb")
|
||||
yield Label(Text("F2 ", style="bold orange").append_text(Text("Artists", style="white")), classes="headerb")
|
||||
yield Label(Text("F3 ", style="bold orange").append_text(Text("Albums", style="white")), classes="headerb")
|
||||
yield Label(Text("F4 ", style="bold orange").append_text(Text("Favourites", style="white")), classes="headerb")
|
||||
yield Label(Text("F5 ", style="bold orange").append_text(Text("Playlists", style="white")), classes="headerb")
|
||||
|
||||
with Center(id='playerTrackTitleBox'):
|
||||
yield Label(id='trackTitle', classes='playerTrackinfo')
|
||||
with Center():
|
||||
yield Label(id='trackAlbumArtist', classes='playerTrackinfo')
|
||||
with Container(id='playerArtc'):
|
||||
yield CoverArtPixels(classes='playerArt')
|
||||
|
||||
with Center():
|
||||
with Horizontal(id='progressBarWrapper'):
|
||||
yield CurrentPlace(classes='playerProgressBarTime')
|
||||
yield ProgressBar(classes='playerProgressBar')
|
||||
yield TotalDuration(classes='playerProgressBarTotalTime')
|
||||
|
||||
with Horizontal(id='playerButtons'):
|
||||
yield Button('|<', id='prev_track', classes='playerButton')
|
||||
yield Button('|>/||', id='play_pause_track', classes='playerButton')
|
||||
yield Button('>|', id='next_track', classes='playerButton')
|
||||
|
||||
yield Footer()
|
||||
|
||||
@on(events.ScreenResume)
|
||||
def screen_resumed(self, event: events.ScreenResume) -> None:
|
||||
app.sub_title = "Overview"
|
||||
# queue_table = self.query_one(QueueTable)
|
||||
# queue_table.update_queue()
|
||||
app.update_now_playing()
|
||||
|
||||
@on(Button.Pressed, "#prev_track")
|
||||
def prev_track_pressed(self) -> None:
|
||||
app.action_rewind()
|
||||
|
||||
@on(Button.Pressed, "#play_pause_track")
|
||||
def play_pause_track_pressed(self) -> None:
|
||||
app.action_play_pause_song()
|
||||
|
||||
@on(Button.Pressed, "#next_track")
|
||||
def next_track_pressed(self) -> None:
|
||||
app.action_skip()
|
||||
|
||||
# Startup keyboard listener for media keys
|
||||
def listen_media_keys(queue: multiprocessing.Queue):
|
||||
|
||||
@@ -998,7 +1091,7 @@ def listen_media_keys(queue: multiprocessing.Queue):
|
||||
pass
|
||||
logger.error(f'Media key listener failed: {e}')
|
||||
|
||||
class TextualSubpyPlayer(App):
|
||||
class SubPy(App):
|
||||
BINDINGS = [
|
||||
("r", "rewind()", "⏪"),
|
||||
("space", "play_pause_song()", "⏯"),
|
||||
@@ -1014,7 +1107,7 @@ class TextualSubpyPlayer(App):
|
||||
]
|
||||
|
||||
MODES = {
|
||||
"main" : MainView,
|
||||
"main" : Player,
|
||||
"artists" : ArtistsGUI,
|
||||
"albums" : Albums,
|
||||
"favourites" : Favourites,
|
||||
@@ -1081,12 +1174,6 @@ class TextualSubpyPlayer(App):
|
||||
self.update_queue_table()
|
||||
self.update_now_playing()
|
||||
|
||||
# playingLabel: Label = self.query_one(Label)
|
||||
# playingLabel.
|
||||
|
||||
# Set callback loop for progress bar
|
||||
# self.progress_timer = self.set_interval(1 / 10, self.update_progress, pause=False)
|
||||
|
||||
def _music_player_loop(self) -> None:
|
||||
if self.music_command_queue.empty():
|
||||
return
|
||||
@@ -1160,9 +1247,22 @@ class TextualSubpyPlayer(App):
|
||||
self.update_queue_table()
|
||||
|
||||
def update_progress(self) -> None:
|
||||
if isinstance(self.screen, MainView) and self.currently_playing:
|
||||
now_playing = self.screen.query_one(NowPlaying)
|
||||
now_playing.set_place(pygame.mixer.music.get_pos())
|
||||
if isinstance(self.screen, Player) and self.currently_playing:
|
||||
current_place_label = self.screen.query_one(CurrentPlace)
|
||||
place = pygame.mixer.music.get_pos()
|
||||
current_place_label.place = place
|
||||
|
||||
duration_label = self.screen.query_one(TotalDuration)
|
||||
duration_label.duration = self.currently_playing.duration
|
||||
|
||||
progressbar: ProgressBar = self.screen.query_one(ProgressBar)
|
||||
|
||||
progress: int = round(place/1000)
|
||||
|
||||
progressbar.update(
|
||||
total = self.currently_playing.duration,
|
||||
progress=progress
|
||||
)
|
||||
|
||||
def queue_song(self, song: SubSonic.Song) -> None:
|
||||
logging.debug(f'Queue\'d song "{song.title}" by {song.artist}.')
|
||||
@@ -1193,10 +1293,9 @@ class TextualSubpyPlayer(App):
|
||||
self.is_paused = False
|
||||
|
||||
def update_now_playing(self) -> None:
|
||||
if isinstance(self.screen, MainView) and self.currently_playing:
|
||||
logging.debug("Updating now playing widget")
|
||||
now_playing = self.screen.query_one(NowPlaying)
|
||||
now_playing.update_song(self.currently_playing)
|
||||
if isinstance(self.screen, Player) and self.currently_playing:
|
||||
logging.debug("Updating CoverArtPixels widget")
|
||||
self.screen.update_song(self.currently_playing)
|
||||
|
||||
def update_queue_table(self) -> None:
|
||||
if isinstance(self.screen, MainView):
|
||||
@@ -1236,7 +1335,7 @@ if __name__ == '__main__':
|
||||
p = multiprocessing.Process(target=listen_media_keys, args=(q,))
|
||||
p.start()
|
||||
|
||||
app = TextualSubpyPlayer()
|
||||
app = SubPy()
|
||||
app.music_command_queue = q
|
||||
|
||||
app.run()
|
||||
|
||||
Reference in new issue
Block a user