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
This commit is contained in:
@@ -51,9 +51,13 @@ class WinampMPRIS:
|
||||
<property name="PlaybackStatus" type="s" access="read">
|
||||
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
|
||||
</property>
|
||||
<property name="LoopStatus" type="s" access="read" />
|
||||
<property name="Rate" type="d" access="read" />
|
||||
<property name="Shuffle" type="b" access="read" />
|
||||
<property name="LoopStatus" type="s" access="readwrite">
|
||||
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
|
||||
</property>
|
||||
<property name="Rate" type="d" access="readwrite" />
|
||||
<property name="Shuffle" type="b" access="readwrite">
|
||||
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
|
||||
</property>
|
||||
<property name="Metadata" type="a{{sv}}" access="read">
|
||||
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
|
||||
</property>
|
||||
@@ -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
|
||||
# <p>Repeat is <b>off</b> ... <br>Random is <b>off</b> ... </p>
|
||||
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*(?:<b>)?(\w+)(?:</b>)?", text, re.I)
|
||||
if m_rep:
|
||||
is_repeat = m_rep.group(1).lower() == "on"
|
||||
|
||||
m_rand = re.search(r"Random is\s*(?:<b>)?(\w+)(?:</b>)?", 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
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user