New UI for player screen

This commit is contained in:
JISAUAY committed 2025-10-02 16:20:20 -05:00
1 parent c0b99d8e1e
commit ada1ef5fba
2 files changed
+187 -18

No files matched your search

+117 -18
View File
@@ -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()
+70
View File
@@ -87,3 +87,73 @@
width: 100%;
text-align: center;
}
# Player Screen
#playerTrackTitleBox {
padding: 1 0 0 0;
}
#playerArtc {
align: center middle;
# width: 1fr;
}
.playerTrackinfo {
# text-align: center;
# content-align: center middle;
height: 1;
width: auto;
}
.playerArt {
content-align: center middle;
height: 1fr;
width: 0.5fr;
padding: 1 0 0 0;
}
#progressBarWrapper {
height: 3;
width: 0.75fr;
padding: 1 0 0 0;
}
.playerProgressBarTime {
width: 0.1fr;
text-align: center;
}
.playerProgressBarTotalTime {
width: 0.1fr;
text-align: center;
}
#percentage {
visibility: hidden;
}
#eta {
visibility: hidden;
}
.playerProgressBar {
height: 3;
width: 0.75fr;
content-align: center middle;
}
.playerProgressBar #bar {
width: auto;
}
#playerButtons {
height: 0.25fr;
padding-bottom: 1;
align: center middle;
}
.playerButton {
width: 10%;
}