From 916d96c4b63a4ea96e024ee042a1852dd7ce6a1f Mon Sep 17 00:00:00 2001 From: GuardKenzie Date: Tue, 11 May 2021 01:37:57 +0000 Subject: [PATCH] Added a new window for the playlist Added a new config option `show_playlist` to specify if the playlist should be drawn Added a feature where when the window proportions exceed 1:3, 40% will be used for album art and time info while the remaining 60% will be used for playlist display --- bin/miniplayer | 229 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 178 insertions(+), 51 deletions(-) diff --git a/bin/miniplayer b/bin/miniplayer index 557d2f0..6b23d69 100755 --- a/bin/miniplayer +++ b/bin/miniplayer @@ -21,7 +21,8 @@ if "player" not in config.sections(): "image_method": "pixcat", "album_art_only": False, "volume_step": 5, - "auto_close": False + "auto_close": False, + "show_playlist": True, } if "mpd" not in config.sections(): @@ -87,6 +88,12 @@ VOLUMESTEP = player_config.getint("volume_step", 5) # Autoclose boolean AUTOCLOSE = player_config.getboolean("auto_close", False) +# Playlist padding +PLAYLISTMARGIN = 4 + +# Config option to display the playlist +DISABLEPLAYLIST = not player_config.getboolean("show_playlist", True) + def albumArtSize(album_space, window_width): """ @@ -104,6 +111,26 @@ def albumArtSize(album_space, window_width): return image_width_px, image_width, image_height +def drawPlaylist(height, width): + """ + A function that checks if the playlist display should be drawn + based on the provided height and width + """ + return height / width < 1/3 and not DISABLEPLAYLIST + + +def albumArtWinWidth(height, width): + """ + A function that calculates the album art window height and + width based on the window height and width + """ + if drawPlaylist(height, width): + return height, round(width * 2/5) + else: + return height, width + + + class Player: def __init__(self): # Curses initialisation @@ -129,15 +156,39 @@ class Player: self.last_song = None - # Curses window - self.window_height, self.window_width = self.stdscr.getmaxyx() - self.win = curses.newwin(self.window_height, self.window_width, 0, 0) + # Album art only flag + self.album_art_only = player_config.getboolean("album_art_only", False) - self.text_start = int(self.window_height - 5) + # Screen size + maxyx = self.stdscr.getmaxyx() + self.screen_height, self.screen_width = maxyx + + # Album art window + self.art_window_height, self.art_window_width = albumArtWinWidth(*maxyx) + self.art_win = curses.newwin( + self.art_window_height, self.art_window_width, + 0, 0 + ) + + # Playlist window + if drawPlaylist(*maxyx) and not self.album_art_only: + self.draw_playlist = True + self.playlist_window_width = maxyx[1] - self.art_window_width - PLAYLISTMARGIN + self.playlist_window_height = maxyx[0] + + self.playlist_win = curses.newwin( + self.playlist_window_height, self.playlist_window_width, + 0, self.art_window_width + PLAYLISTMARGIN + ) + else: + self.draw_playlist = False + self.playlist_win = None + + self.text_start = int(self.art_window_height - 5) self.album_space = self.text_start - 2 # Calculate the size of the image - self.image_width_px, self.image_width, self.image_height = albumArtSize(self.album_space, self.window_width) + self.image_width_px, self.image_width, self.image_height = albumArtSize(self.album_space, self.art_window_width) self.image_y_pos = (self.album_space - self.image_height) // 2 + 1 # Album art location @@ -153,9 +204,6 @@ class Player: # Update needed flag self.update_needed = False - # Album art only flag - self.album_art_only = player_config.getboolean("album_art_only", False) - # Flag to check if any music has been played self.has_music_been_played = False @@ -169,7 +217,7 @@ class Player: song = self.title album = self.album artist = self.artist - width = self.window_width + width = self.art_window_width if len(song) > width: song = song[:width - len(song)] @@ -199,30 +247,66 @@ class Player: """ A function to check if the window size changed """ - new_height, new_width = self.stdscr.getmaxyx() + window_height, window_width = self.stdscr.getmaxyx() - if (new_height, new_width) != (self.window_height, self.window_width) or force_update: - self.win.clear() + if (window_height, window_width) != (self.screen_height, self.screen_width) or force_update: - # Curses window - self.window_height, self.window_width = self.stdscr.getmaxyx() + if drawPlaylist(window_height, window_width) and not self.album_art_only: + self.draw_playlist = True + else: + self.draw_playlist = False + + # Album art window + self.art_window_height, self.art_window_width = albumArtWinWidth(window_height, window_width) + + # Playlist window + if self.draw_playlist: + self.playlist_window_width = window_width - self.art_window_width - PLAYLISTMARGIN + self.playlist_window_height = window_height + + # Close the playlist window if it exists + elif self.playlist_win is not None: + del self.playlist_win + self.playlist_win = None # Check if we are drawing info if self.album_art_only: - self.text_start = int(self.window_height) + self.text_start = int(self.art_window_height) self.album_space = self.text_start - 1 else: - self.text_start = int(self.window_height - 5) + self.text_start = int(self.art_window_height - 5) self.album_space = self.text_start - 2 # Calculate the size of the image - self.image_width_px, self.image_width, self.image_height = albumArtSize(self.album_space, self.window_width) + self.image_width_px, self.image_width, self.image_height = albumArtSize(self.album_space, self.art_window_width) self.image_y_pos = (self.album_space - self.image_height) // 2 + 1 - # Resize the window - self.win.resize(self.window_height, self.window_width) + # Check if playlist window exists and if we are drawing it + if self.playlist_win is not None and self.draw_playlist: + self.playlist_win.clear() + self.playlist_win.refresh() + + self.playlist_win.resize( + self.playlist_window_height, + self.playlist_window_width + ) + + self.playlist_win.mvwin(0, self.art_window_width + PLAYLISTMARGIN) + + elif self.draw_playlist: + self.playlist_win = curses.newwin( + self.playlist_window_height, self.playlist_window_width, + 0, self.art_window_width + PLAYLISTMARGIN + ) + self.last_song = None + # Resize the window + self.art_win.clear() + self.art_win.resize(self.art_window_height, self.art_window_width) + + self.screen_height, self.screen_width = window_height, window_width + def getAlbumArt(self, song_file): """ @@ -288,7 +372,7 @@ class Player: self.progress = self.elapsed/self.duration if self.last_song != song: - self.win.clear() + self.art_win.clear() try: self.album = song["album"] @@ -326,9 +410,9 @@ class Player: """ self.album_art_only = not self.album_art_only - self.win.clear() + self.art_win.clear() self.updateWindowSize(force_update=True) - self.win.refresh() + self.art_win.refresh() def handleKeypress(self): @@ -415,41 +499,84 @@ class Player: if state == 0: # Everything fits - self.win.addstr(self.text_start, 0, f"{title}") - self.win.addstr(self.text_start + 1, 0, f"{artist}{seperator}{album}") + self.art_win.addstr(self.text_start, 0, f"{title}") + self.art_win.addstr(self.text_start + 1, 0, f"{artist}{seperator}{album}") elif state == 1: # Too wide - self.win.addstr(self.text_start - 1, 0, f"{title}") - self.win.addstr(self.text_start, 0, f"{album}") - self.win.addstr(self.text_start + 1, 0, f"{artist}") + self.art_win.addstr(self.text_start - 1, 0, f"{title}") + self.art_win.addstr(self.text_start, 0, f"{album}") + self.art_win.addstr(self.text_start + 1, 0, f"{artist}") else: # No album - self.win.addstr(self.text_start, 0, f"{title}") - self.win.addstr(self.text_start + 1, 0, f"{artist}") + self.art_win.addstr(self.text_start, 0, f"{title}") + self.art_win.addstr(self.text_start + 1, 0, f"{artist}") # Progress bar song_duration = (int(self.duration / 60), round(self.duration % 60)) song_elapsed = (int(self.elapsed / 60), round(self.elapsed % 60)) - self.win.addstr( + self.art_win.addstr( self.text_start + 2, 0, - "-"*(int((self.window_width - 1) * self.progress)) + ">", + "-"*(int((self.art_window_width - 1) * self.progress)) + ">", curses.color_pair(1) ) # Duration string time_string = f"{song_elapsed[0]}:{song_elapsed[1]:02d}/{song_duration[0]}:{song_duration[1]:02d}" - self.win.addstr( + self.art_win.addstr( self.text_start + 3, 0, - f"{time_string:>{self.window_width}}", + f"{time_string:>{self.art_window_width}}", curses.color_pair(2) ) - self.win.refresh() + self.art_win.refresh() + + # Draw playlist + if self.draw_playlist: + playlist = self.client.playlistinfo() + currentsong = self.client.currentsong() + + currentpos = int(currentsong["pos"]) + + # Determine where to start the playlist + if currentpos > self.playlist_window_height // 2 and len(playlist) > self.playlist_window_height: + start = currentpos - (self.playlist_window_height - 1) // 2 + else: + start = 0 + + start = min(abs(len(playlist) - self.playlist_window_height), start) + + line = 0 + while line < self.playlist_window_height: + # Check if playlist is empty + if line + start < len(playlist): + playlist_item = playlist[start + line] + else: + playlist_item = None + + # Decide color + if playlist_item == currentsong: + pair = curses.A_REVERSE | curses.color_pair(2) + else: + pair = 0 + + # Move and write text + self.playlist_win.move(line, 0) + + if playlist_item is not None: + self.playlist_win.addstr( + f"{playlist_item['artist']} - {playlist_item['title']}"[:self.playlist_window_width], + pair + ) + + self.playlist_win.clrtoeol() + line += 1 + + self.playlist_win.refresh() def hideAlbumArt(self): @@ -467,7 +594,7 @@ class Player: if IMAGEMETHOD == "ueberzug": # Figure out new placement - self.art_placement.x = (self.window_width - self.image_width)//2 + self.art_placement.x = (self.art_window_width - self.image_width)//2 self.art_placement.y = self.image_y_pos # Figure out height and width @@ -484,7 +611,7 @@ class Player: ( pixcat.Image(self.album_art_loc) .thumbnail(self.image_width_px ) - .show(x=(self.window_width - self.image_width)//2, y=self.image_y_pos) + .show(x=(self.art_window_width - self.image_width)//2, y=self.image_y_pos) ) @@ -498,8 +625,8 @@ class Player: string -- The string to draw """ - x_pos = self.window_width / 2 - len(string) / 2 - self.win.addstr(y, int(x_pos), string) + x_pos = self.art_window_width / 2 - len(string) / 2 + self.art_win.addstr(y, int(x_pos), string) def drawHelp(self): @@ -512,18 +639,18 @@ class Player: # Left and right margin pct lr_margin_pct = 0.1 - lr_margin = round(self.window_width * lr_margin_pct) + lr_margin = round(self.art_window_width * lr_margin_pct) # Actual space for text - x_space = self.window_width - 2 * (lr_margin) + x_space = self.art_window_width - 2 * (lr_margin) # Check if window has been cleared if not self.cleared: - self.win.clear() + self.art_win.clear() self.cleared = True # Figure out center, y_start and x_start - center_y, center_x = (self.window_height // 2, self.window_width // 2) + center_y, center_x = (self.art_window_height // 2, self.art_window_width // 2) y_start = top_vspace x_start = int(lr_margin) @@ -535,9 +662,9 @@ class Player: y_start += 1 sep = "." * (x_space - len(key) - len(desc) - 2) desc = desc.replace("_", " ").capitalize() - self.win.addstr(y_start, x_start, f"{key} {sep} {desc}") + self.art_win.addstr(y_start, x_start, f"{key} {sep} {desc}") - self.win.refresh() + self.art_win.refresh() def draw(self): @@ -545,12 +672,12 @@ class Player: The function that draws the now playing window """ if not self.cleared: - self.win.clear() + self.art_win.clear() self.cleared = True # Force window nings - self.win.redrawln(0, 1) - self.win.addstr(0, 0, " ") + self.art_win.redrawln(0, 1) + self.art_win.addstr(0, 0, " ") # Get mpd state state = self.checkSongUpdate() @@ -561,13 +688,13 @@ class Player: # Check if the playlist has concluded and if we should close raise KeyboardInterrupt - self.win.clear() + self.art_win.clear() self.hideAlbumArt() infomsg = "Put some beats on!" - self.win.addstr(self.window_height // 2, (self.window_width - len(infomsg)) // 2, infomsg) - self.win.refresh() + self.art_win.addstr(self.art_window_height // 2, (self.art_window_width - len(infomsg)) // 2, infomsg) + self.art_win.refresh() return