diff --git a/textual1/SubSonic.py b/textual1/SubSonic.py index 8ec0a0b..b653a72 100644 --- a/textual1/SubSonic.py +++ b/textual1/SubSonic.py @@ -73,6 +73,7 @@ class Song: channelCount: int samplingRate: int playCount: int | None + coverArtId: str | None def __init__(self, info: dict | None = {}): self.id = info['id'] if 'id' in info else "" @@ -92,6 +93,7 @@ class Song: self.channelCount = int(info['channelCount']) if 'channelCount' in info else "" self.samplingRate = int(info['samplingRate']) if 'samplingRate' in info else "" self.playCount = int(info['playCount']) if 'playCount' in info else "" + self.coverArtId = info['coverArt'] if 'coverArt' in info else None class Artist: id: str diff --git a/textual1/app.py b/textual1/app.py index 57977a4..c8e5ed3 100644 --- a/textual1/app.py +++ b/textual1/app.py @@ -344,26 +344,50 @@ class QueueTable(Widget): return table # Fine to be a static renderable because it's not big enough to need a scroll -class PlayingInfo(Widget): +class NowPlayingInfo(Container): + song = reactive[SubSonic.Song | None](None, layout=True) + + # Place is from pygame.mixer.music.get_pos and is in ms + place: reactive[int] = reactive(0) def render(self) -> RenderResult: - table = Table() + if self.song: + if self.song.title and self.song.artist: - table.add_column("") - table.add_column("", min_width=10) - table.show_header = False + # Convert seconds to MM:SS + duration_min, duration_sec = divmod(self.song.duration, 60) + duration_formatted = f"{duration_min:02d}:{duration_sec:02d}" - if app.currently_playing: - song = app.currently_playing - table.add_row("Title", song.title) - table.add_row("Artist", song.artist) - table.add_row("Album", song.album) - table.add_row("Plays", str(song.playCount) if song.playCount else "0") - table.add_row("Duration", str(song.duration)) - table.add_row("Track Number", str(song.trackNumber)) - table.add_row("Bit Rate", str(song.bitRate)) + 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 table + s = f"Song: {self.song.title}\nAlbum: {self.song.album}\nArtist: {self.song.artist}\nProgress: {place_formatted} / {duration_formatted}\n" + + return s + return "Nothing Playing" + +class NowPlaying(Widget): + def compose(self) -> ComposeResult: + yield NowPlayingInfo(id="textinfo") + yield CoverArtPixels(id="coverart") + + def update_song(self, song: SubSonic.Song) -> None: + info = self.query_one(NowPlayingInfo) + info.song = song + + if song.albumId: + 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 set_place(self, place: int) -> None: + info = self.query_one(NowPlayingInfo) + info.place = place # https://textual.textualize.io/widgets/data_table/#labeled-rows # Add this ^ to queue sometime plz @@ -374,10 +398,8 @@ class MainView(Screen): yield Header(icon="") with Horizontal(): with Vertical(classes="info", id='info'): - yield Label("Playing") - yield PlayingInfo() + yield NowPlaying(id="now-playing") yield ProgressBar() - yield CoverArtPixels(id="coverart") with Vertical(classes="column"): yield Label("Queue") yield DataTable(id="queue", cursor_type='row') @@ -392,6 +414,7 @@ class MainView(Screen): def screen_resumed(self, event: events.ScreenResume) -> None: app.sub_title = "Overview" self.update_queue() + app.update_now_playing() def update_queue(self) -> None: table = self.query_one(DataTable) @@ -457,8 +480,10 @@ class TextualSubpyPlayer(App): pygame.mixer.music.play() self.check_playing_timer = self.set_interval(1 / 5, self.check_done_playing, pause=False) + self.progess_timer = self.set_interval(0.3, self.update_progress, pause=False) self.update_queue_table() + self.update_now_playing() # playingLabel: Label = self.query_one(Label) # playingLabel. @@ -497,8 +522,10 @@ class TextualSubpyPlayer(App): self.play_song(self.play_history.pop()) - # def update_progress(self) -> None: - # self.query_one(ProgressBar).advance(1) + 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()) def queue_song(self, song: SubSonic.Song) -> None: logging.debug(f'Queue\'d song "{song.title}" by {song.artist}.') @@ -522,6 +549,12 @@ class TextualSubpyPlayer(App): pygame.mixer.music.unpause() 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) + def update_queue_table(self) -> None: if isinstance(self.screen, MainView): logging.debug("Updating queue through app") diff --git a/textual1/main.tcss b/textual1/main.tcss index 9c063e7..af536ae 100644 --- a/textual1/main.tcss +++ b/textual1/main.tcss @@ -2,3 +2,12 @@ width: 30%; padding-right: 2; } + +#textinfo { + height: 6; +} + +#coverart { + width: 100%; + height: 100%; +}