From 16db9c6309f47a43046eec6a38e779729fb5624c Mon Sep 17 00:00:00 2001 From: ABelliqueux Date: Sun, 24 Mar 2024 17:34:48 +0100 Subject: [PATCH] Add browse logic --- mpdlisten.py | 174 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 142 insertions(+), 32 deletions(-) diff --git a/mpdlisten.py b/mpdlisten.py index fdeb210..b4aaa1e 100644 --- a/mpdlisten.py +++ b/mpdlisten.py @@ -55,32 +55,37 @@ ui_vol_y = 4 ui_vol_x = device.width - ui_vol_width - 8 ui_vol_icon_coords = (ui_vol_x - 10, 4) ui_vol_icon_polygon = [0,3,3,3,8,0,8,8,3,5,0,5] - play_icon = [0,0,8,4,0,8] +# ~ play_icon_ = [0,0,8,4,0,8] pause_icon = [0,0,3,0,3,8,5,8,5,0,8,0,8,8,0,8] stop_icon = [0,0,8,0,8,8,0,8] next_icon = [0,0,3,3,3,0,8,4,3,8,3,5,0,8] prev_icon = [0,4,3,0,3,3,8,0,8,8,3,5,3,8] -menu_icon = [0,0, 5,0, 4,1, 8,4, 6,6, 2,4, 0,6] +menu_icon = [0,0, 5,0, 4,2, 8,5, 10,10, 7,7, 2,4, 0,6] +menu_icon_ = [0,0, 5,0, 4,2, 8,5, 10,10, 7,7, 2,4, 0,6] down_icon = [0,0, 8,0, 4,8] up_icon = [0,8, 4,0, 8,8] add_icon = [0,3, 3,3, 3,0, 6,0, 6,3, 8,3, 8,6, 6,6, 6,8, 3,8, 3,6, 0,6] + +ui_state = {'cursor_pos': 0, 'prev_cursor_pos': 0, 'current_selection': [], 'current_selection_level': 0, 'current_artist': None, 'current_album': None } MODES_ORDER = ['playback', 'browse'] MODES = { 'playback' : dict(BTN_1=dict(FUNCTION='prev', ICON=prev_icon, XY=(((btn_width+1)*0.5), (menu_bar_y + 2)) ), BTN_2=dict(FUNCTION='stop', ICON=stop_icon, XY=(((btn_width+1)*1.5), (menu_bar_y + 2))), BTN_3=dict(FUNCTION='toggle', ICON=play_icon, XY=(((btn_width+1)*2.5), (menu_bar_y + 2))), BTN_4=dict(FUNCTION='next', ICON=next_icon, XY=(((btn_width+1)*3.5), (menu_bar_y + 2))), - BTN_5=dict(FUNCTION='menu', ICON=prev_icon, XY=(((btn_width+1)*4.5), (menu_bar_y + 2))), + BTN_5=dict(FUNCTION='menu', ICON=menu_icon, XY=(((btn_width+1)*4.5), (menu_bar_y + 2))), ), - 'browse' : dict(BTN_1=dict(FUNCTION='none', ICON=down_icon, XY=(((btn_width+1)*0.5), (menu_bar_y + 2)) ), - BTN_2=dict(FUNCTION='none', ICON=up_icon, XY=(((btn_width+1)*1.5), (menu_bar_y + 2))), - BTN_3=dict(FUNCTION='none', ICON=add_icon, XY=(((btn_width+1)*2.5), (menu_bar_y + 2))), - BTN_4=dict(FUNCTION='none', ICON=play_icon, XY=(((btn_width+1)*3.5), (menu_bar_y + 2))), + 'browse' : dict(BTN_1=dict(FUNCTION='down', ICON=down_icon, XY=(((btn_width+1)*0.5), (menu_bar_y + 2)) ), + BTN_2=dict(FUNCTION='up', ICON=up_icon, XY=(((btn_width+1)*1.5), (menu_bar_y + 2))), + BTN_3=dict(FUNCTION='select', ICON=play_icon, XY=(((btn_width+1)*2.5), (menu_bar_y + 2))), + BTN_4=dict(FUNCTION='add', ICON=add_icon, XY=(((btn_width+1)*3.5), (menu_bar_y + 2))), BTN_5=dict(FUNCTION='menu', ICON=menu_icon, XY=(((btn_width+1)*4.5), (menu_bar_y + 2))), ), } -# Becomes true when receiving SIGINT + + + # Becomes true when receiving SIGINT ctrlc_pressed = False mpd_client_status = {'volume': 'N/A', @@ -142,12 +147,13 @@ def sectomin(sec:str): def apply_xy_offset(polygon:list, offset:tuple): + polygon_copy = [] i=0 while i < len(polygon): - polygon[i] += offset[0] - polygon[i+1] += offset[1] + polygon_copy.append(polygon[i] + offset[0]) + polygon_copy.append(polygon[i+1] + offset[1]) i+=2 - return polygon + return polygon_copy def offset_polygons(modes:dict): @@ -174,7 +180,7 @@ def generate_static_ui(mode:str): return im -def update_display(device, currentsong, status): +def update_display(device, currentsong:dict, status:dict, mode:str, cursor_pos:int): # We want to display 4 buttons at the bottom of the screen btn_width = floor((device.width-3) / 4) btn_height = 8 @@ -184,17 +190,47 @@ def update_display(device, currentsong, status): ui = static_ui.copy() draw = ImageDraw.Draw(ui) draw.text((ui_vol_x, ui_vol_y), status['volume'], fill="white") - if current_mode == 'playback': - draw.text((ui_text_x, 2), currentsong['artist'], fill="white") - draw.text((ui_text_x, 14), currentsong['title'], fill="white") - draw.text((ui_text_x, 26), currentsong['album'], fill="white") - if 'elapsed' in status: - draw.text((ui_text_x, 38), "{}/{}".format(sectomin(status['elapsed']), sectomin(status['duration'])), fill="white") + if mode == 'playback': + if len(currentsong): + draw.text((ui_text_x, 2), currentsong['artist'], fill="white") + draw.text((ui_text_x, 14), currentsong['title'], fill="white") + draw.text((ui_text_x, 26), currentsong['album'], fill="white") + if 'elapsed' in status: + draw.text((ui_text_x, 38), "{}/{}".format(sectomin(status['elapsed']), sectomin(status['duration'])), fill="white") + elif mode == 'browse': + draw.regular_polygon(bounding_circle=(ui_text_x + 2, 6, 4), n_sides=3, rotation=270, outline="white", fill="black") + if (type(ui_state['current_selection']) is list) and (len(ui_state['current_selection'])): + draw.text((ui_text_x + 10, 1), ui_state['current_selection'][cursor_pos], fill="white") + if (len(ui_state['current_selection']) > 1) and (cursor_pos < len(ui_state['current_selection'])-1): + draw.text((ui_text_x, 14), ui_state['current_selection'][cursor_pos+1], fill="white") + if len(ui_state['current_selection']) > 2 and (cursor_pos < len(ui_state['current_selection'])-2): + draw.text((ui_text_x, 26), ui_state['current_selection'][cursor_pos+2], fill="white") + else: + draw.text((ui_text_x + 10, 1), ui_state['current_selection'], fill="white") device.contrast(0) device.display(ui) -def send_mpd_cmd(client, cmd:str): +def populate_empty_tags(current_selection:list, selection_level:int): + if '' in current_selection: + if selection_level == 0: + # Empty artist + current_selection[current_selection.index('')] = '(No artist)' + if selection_level == 1: + # Empty album + current_selection[current_selection.index('')] = '(No album)' + if selection_level == 2: + # Empty title + current_selection[current_selection.index('')] = '(No title)' + + +def send_mpd_cmd(client, cmd:str, ui_state:dict): + cursor_pos = ui_state['cursor_pos'] + prev_cursor_pos = ui_state['prev_cursor_pos'] + current_selection = ui_state['current_selection'] + current_artist = ui_state['current_artist'] + current_album = ui_state['current_album'] + current_selection_level = ui_state['current_selection_level'] idle_states = ['stop', 'pause'] if cmd == 'menu': global current_mode @@ -203,11 +239,18 @@ def send_mpd_cmd(client, cmd:str): if current_mode_index >= len(MODES_ORDER)-1: current_mode_index = 0 else: - current_mode_index += 1 + current_mode_index += 1 current_mode = MODES_ORDER[current_mode_index] + # TODO : use menu button as return + # prev_cursor_pos global static_ui static_ui = generate_static_ui(current_mode) - if cmd == 'prev': + current_selection = client.list('artist') + current_selection_level = 0 + populate_empty_tags(current_selection, current_selection_level) + cursor_pos = 0 + + elif cmd == 'prev': if client.status()['state'] != 'stop': client.previous() elif cmd == 'next': @@ -220,13 +263,71 @@ def send_mpd_cmd(client, cmd:str): client.pause() elif cmd == 'stop': client.stop() - else: - return 0 - return 1 + elif cmd == 'down': + if cursor_pos in range(0, len(current_selection)-1): + cursor_pos += 1 + else: + cursor_pos = 0 + elif cmd == 'up': + if cursor_pos in range(1, len(current_selection)): + cursor_pos -= 1 + else: + cursor_pos = len(current_selection)-1 + elif cmd == 'select': + if current_selection_level == 0: + # Select artist + # ~ current_selection = current_selection[cursor_pos] + # ~ current_selection = client.list('artist') + if len(current_selection): + current_artist = current_selection[cursor_pos] + current_selection = client.list('album', 'artist', current_selection[cursor_pos]) + print("Artist:") + print(current_artist) + current_selection_level += 1 + populate_empty_tags(current_selection, current_selection_level) + prev_cursor_pos = cursor_pos + cursor_pos = 0 + elif current_selection_level == 1: + if len(current_selection): + current_album = current_selection[cursor_pos] + # List albums by selected artist + # ~ current_selection = client.list('album', 'artist', current_selection[cursor_pos]) + current_selection = client.list('title', 'album', current_selection[cursor_pos]) + print("Album:") + print(current_album) + current_selection_level += 1 + populate_empty_tags(current_selection, current_selection_level) + cursor_pos = 0 + elif current_selection_level == 2: + # List songs by selected album + # ~ current_selection = client.list('title', 'album', current_selection[cursor_pos]) + current_selection = client.list('artist') + cursor_pos = prev_cursor_pos + print("Return to menu") + print(current_selection) + # TODO : change icon to a X and return to artist list if used + current_selection_level = 0 + + elif cmd == 'add': + if current_selection_level == 0: + print(current_selection[cursor_pos]) + selected_song_files = client.find('artist', current_selection[cursor_pos]) + if current_selection_level == 1: + print(current_artist) + selected_song_files = client.find('artist', current_artist, 'album', current_selection[cursor_pos]) + if current_selection_level == 2: + print(current_album) + selected_song_files = client.find('title', current_selection[cursor_pos], 'artist', current_artist,'album', current_album ) + # ~ selected_song_file = client.find('title', current_selection[cursor_pos]) + # ~ selected_song_files = selected_song_file[0] + for file in selected_song_files: + client.add(file['file']) + state = {'cursor_pos': cursor_pos, 'prev_cursor_pos': prev_cursor_pos, 'current_selection': current_selection, 'current_selection_level': current_selection_level, 'current_artist': current_artist, 'current_album':current_album} + return state def main(args): - previous_sond_id = None + previous_song_id = None previous_state = None paused_since_seconds = 0 # MPDclient setup @@ -245,6 +346,14 @@ def main(args): print("Check host and port are correct.") # ~ print(client.status()) # duration, elapsed, volume, repeat, random, single # ~ print(client.currentsong()) # artist, title, album + # Cache MPD's artist list + global ui_state + ui_state['current_selection'] = client.list('artist') + # List albums by artist + # ~ album_by = client.list('album', 'artist', artist_list[x]) + # List song by album + # ~ songs_from_album = client.list('title', 'album', album_by_x[x]) + offset_polygons(MODES) global ui_vol_icon_polygon ui_vol_icon_polygon = apply_xy_offset(ui_vol_icon_polygon, ui_vol_icon_coords) @@ -256,12 +365,14 @@ def main(args): if len(mpd_status): mpd_client_status = mpd_status mpd_client_currentsong = client.currentsong() - play_state = mpd_client_status['state'] - current_song_id = mpd_client_status['songid'] + if 'state' in mpd_client_status: + play_state = mpd_client_status['state'] + if 'songid' in mpd_client_status: + current_song_id = mpd_client_status['songid'] if play_state in mpd_states: if play_state == 'play': paused_since_seconds = 0 - if (current_song_id != previous_sond_id) and (previous_state != play_state): + if (current_song_id != previous_song_id) and (previous_state != play_state): print("Play") # Relay on GPIO.output(RELAIS_1_GPIO, GPIO.HIGH) @@ -274,7 +385,7 @@ def main(args): # Relay off GPIO.output(RELAIS_1_GPIO, GPIO.LOW) if play_state == 'stop' or play_state == 'unknown': - previous_sond_id = None + previous_song_id = None paused_since_seconds = 0 if previous_state != play_state: print("Stopped") @@ -282,18 +393,18 @@ def main(args): GPIO.output(RELAIS_1_GPIO, GPIO.LOW) previous_state = play_state sleep(.2) - update_display(device, mpd_client_currentsong, mpd_client_status) # Handle buttons for BTN in BTNS: # Avoid double trigger by saving the previous state of the button and commpare it to current state if (GPIO.input(BTNS[BTN]['GPIO']) == 0) and (GPIO.input(BTNS[BTN]['GPIO']) != BTNS[BTN]['state']): - send_mpd_cmd(client, MODES[current_mode][BTN]['FUNCTION']) + ui_state = send_mpd_cmd(client, MODES[current_mode][BTN]['FUNCTION'], ui_state) print("{} pressed".format(MODES[current_mode][BTN]['FUNCTION'])) # Save previous state BTNS[BTN]['state'] = GPIO.input(BTNS[BTN]['GPIO']) if (GPIO.input(BTNS[BTN]['GPIO']) == 1) and (GPIO.input(BTNS[BTN]['GPIO']) != BTNS[BTN]['state']): # Save previous state BTNS[BTN]['state'] = GPIO.input(BTNS[BTN]['GPIO']) + update_display(device, mpd_client_currentsong, mpd_client_status, current_mode, ui_state['cursor_pos']) device.cleanup() client.disconnect() return 0 @@ -303,7 +414,6 @@ def signal_handler(sig, frame): global ctrlc_pressed print('You pressed Ctrl+C!') ctrlc_pressed = True - # ~ sys.exit(0) if __name__ == '__main__':