From 33d76dad992d25843711596b527be8ec45d7b65a Mon Sep 17 00:00:00 2001 From: ergosteur Date: Sat, 21 Mar 2026 10:51:49 -0400 Subject: [PATCH] Implement Shuffle and Repeat (LoopStatus) support - Add Shuffle and LoopStatus readwrite properties to WinampMPRIS - Robust regex-based parsing of Repeat/Random status from Web UI - Dynamic property change notifications via PropertiesChanged signal - Optimized update_loop to track and reflect playback mode changes --- winamp_mpris.py | 76 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/winamp_mpris.py b/winamp_mpris.py index 2d0c1a2..d11f7ce 100755 --- a/winamp_mpris.py +++ b/winamp_mpris.py @@ -51,9 +51,13 @@ class WinampMPRIS: - - - + + + + + + + @@ -83,6 +87,8 @@ class WinampMPRIS: self._last_position_us = 0 self._total_length_us = 0 self._last_update_ts = time.time() + self._shuffle = False + self._loop_status = "None" def _request(self, endpoint): print(f"COMMAND RECEIVED: {endpoint}") @@ -127,12 +133,33 @@ class WinampMPRIS: # --- Player Properties --- @property def PlaybackStatus(self): return self._status + @property - def LoopStatus(self): return "None" + def LoopStatus(self): return self._loop_status + @LoopStatus.setter + def LoopStatus(self, value): + # MPRIS: "None", "Track", "Playlist" + # Winamp Web Interface has "on" or "off" for Repeat (playlist-wide) + # We'll map "Playlist" and "Track" both to Repeat ON. + if value in ["Playlist", "Track"]: + self._request("playmode?repeat=on") + else: + self._request("playmode?repeat=off") + @property def Rate(self): return 1.0 + @Rate.setter + def Rate(self, value): pass + @property - def Shuffle(self): return False + def Shuffle(self): return self._shuffle + @Shuffle.setter + def Shuffle(self, value): + if value: + self._request("playmode?random=on") + else: + self._request("playmode?random=off") + @property def CanGoNext(self): return True @property @@ -223,6 +250,8 @@ def update_loop(player): last_known_status = "" last_known_album = "" last_time_str = "" + last_known_shuffle = False + last_known_loop = "None" offline_logged = False while True: @@ -271,6 +300,31 @@ def update_loop(player): player._status = new_status + # Parse Repeat and Random status + #

Repeat is off ...
Random is off ...

+ repeat_tag = None + for p in p_tags: + if "Repeat is" in p.text: + repeat_tag = p + break + + if repeat_tag: + # Extract "on" or "off" status + text = repeat_tag.get_text() + is_repeat = False + is_random = False + + m_rep = re.search(r"Repeat is\s*(?:)?(\w+)(?:)?", text, re.I) + if m_rep: + is_repeat = m_rep.group(1).lower() == "on" + + m_rand = re.search(r"Random is\s*(?:)?(\w+)(?:)?", text, re.I) + if m_rand: + is_random = m_rand.group(1).lower() == "on" + + player._loop_status = "Playlist" if is_repeat else "None" + player._shuffle = is_random + # Always update time from Web UI match_time = re.search(r"\((\d+:?\d*:\d+) / (\d+:?\d*:\d+)\)", status_raw) if match_time: @@ -301,7 +355,9 @@ def update_loop(player): if (player._status != last_known_status or player._title != last_known_title or - player._album != last_known_album): + player._album != last_known_album or + player._shuffle != last_known_shuffle or + player._loop_status != last_known_loop): # Fetch art if album/artist changed if player._artist != "Unknown" and player._album: @@ -312,15 +368,19 @@ def update_loop(player): last_known_status = player._status last_known_title = player._title last_known_album = player._album + last_known_shuffle = player._shuffle + last_known_loop = player._loop_status album_str = f" [{player._album}]" if player._album else "" - print(f"UPDATE: [{player._status}] {player._artist}{album_str} - {player._title}") + print(f"UPDATE: [{player._status}] Shuffle: {player._shuffle}, Loop: {player._loop_status}, {player._artist}{album_str} - {player._title}") player.PropertiesChanged( "org.mpris.MediaPlayer2.Player", { "PlaybackStatus": player.PlaybackStatus, - "Metadata": player.Metadata + "Metadata": player.Metadata, + "Shuffle": player.Shuffle, + "LoopStatus": player.LoopStatus }, [] )