From 8f12b04a020db42efa88e104c10a7df432248469 Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Fri, 11 Jul 2025 06:47:39 +0545 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(music=5Fplayer,?= =?UTF-8?q?=20mpris):=20add=20recursion=20protection,=20debouncing,=20and?= =?UTF-8?q?=20state=20tracking=20in=20player=20management?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/hypr/colors.conf | 42 ++++++++-------- modules/dock/components/music_player.py | 64 +++++++++++++++++++------ services/mpris.py | 47 +++++++++++++++--- 3 files changed, 111 insertions(+), 42 deletions(-) diff --git a/config/hypr/colors.conf b/config/hypr/colors.conf index c08933d..22535e2 100644 --- a/config/hypr/colors.conf +++ b/config/hypr/colors.conf @@ -1,27 +1,27 @@ -$wallpaper = /home/sankalp/Pictures/wallpapers/48.png -$background = 0f1417 -$foreground = dee3e6 +$wallpaper = /home/sankalp/Pictures/wallpapers/17.png +$background = 1a120d +$foreground = f0dfd7 -$primary = 89d0ed -$secondary = b3cad5 -$tertiary = c4c3eb -$surface = 0f1417 -$surface_bright = 353a3d -$outline = 8a9296 +$primary = ffb68c +$secondary = e5bfaa +$tertiary = ccc992 +$surface = 1a120d +$surface_bright = 413732 +$outline = 9f8d84 $error = ff5945 $shadow = 000000 -$red = ffb2b9 -$green = 95d5a7 -$yellow = b8cf84 -$blue = afc6ff -$magenta = e4b7f3 -$cyan = 82d3e2 +$red = ffb59b +$green = b7d085 +$yellow = dec56e +$blue = cebdfe +$magenta = fcb0d5 +$cyan = 84d5c4 $white = 82d3e0 -$red_dim = ff4c5c -$green_dim = 4cb86a -$yellow_dim = 8baa43 -$blue_dim = 497dff -$magenta_dim = c360e4 -$cyan_dim = 30b5ce +$red_dim = ff6a35 +$green_dim = 89ad42 +$yellow_dim = bb9b2b +$blue_dim = 8359fc +$magenta_dim = f84ea1 +$cyan_dim = 3eb59c diff --git a/modules/dock/components/music_player.py b/modules/dock/components/music_player.py index 4b236f7..d117463 100644 --- a/modules/dock/components/music_player.py +++ b/modules/dock/components/music_player.py @@ -219,6 +219,11 @@ class MusicPlayer(Box): 2, self.check_active_player ) + # Add recursion protection and debouncing + self._in_player_switch = False + self._last_player_switch_time = 0 + self._switch_debounce_ms = 500 # 500ms debounce + # Connect to destroy signal for cleanup self.connect("destroy", self.on_destroy) @@ -358,6 +363,10 @@ class MusicPlayer(Box): def check_active_player(self): """Periodically check for the currently playing player and switch if needed""" + # Prevent recursion during player checking + if self._in_player_switch: + return True # Continue the timeout + try: current_time = time.time() @@ -473,6 +482,18 @@ class MusicPlayer(Box): def set_current_player(self, player): """Set the current active player""" + # Prevent recursion during player switching + if self._in_player_switch: + return + + # Add debouncing to prevent rapid switching + import time + current_time = time.time() * 1000 # Convert to milliseconds + if current_time - self._last_player_switch_time < self._switch_debounce_ms: + return + + self._in_player_switch = True + self._last_player_switch_time = current_time try: # Validate player before proceeding if not player or not hasattr(player, 'props') or not hasattr(player.props, 'player_name'): @@ -493,16 +514,15 @@ class MusicPlayer(Box): except Exception: pass - # Disconnect signals - only if they were actually connected - # Use a more robust disconnection approach - try: - # Get all signal handlers and disconnect them - if hasattr(self.current_player_service, 'disconnect'): - # Try to disconnect all signals from this service - self.current_player_service.disconnect() - except Exception: - # If bulk disconnect fails, try individual disconnections - pass + # Disconnect signals safely - avoid bulk disconnect which can cause recursion + # Store signal handler IDs for proper cleanup + if hasattr(self.current_player_service, '_signal_handlers'): + try: + for handler_id in self.current_player_service._signal_handlers: + self.current_player_service.disconnect(handler_id) + self.current_player_service._signal_handlers.clear() + except Exception: + pass except Exception: pass @@ -519,13 +539,24 @@ class MusicPlayer(Box): self.current_player_service = PlayerService(player=player) - # Connect to player events with error handling + # Connect to player events with error handling and store handler IDs if self.current_player_service: try: - self.current_player_service.connect("track-position", self.on_track_position) - self.current_player_service.connect("play", self.on_play) - self.current_player_service.connect("pause", self.on_pause) - self.current_player_service.connect("meta-change", self.on_metadata) + # Initialize signal handlers list for proper cleanup + self.current_player_service._signal_handlers = [] + + # Connect signals and store handler IDs + handler_id = self.current_player_service.connect("track-position", self.on_track_position) + self.current_player_service._signal_handlers.append(handler_id) + + handler_id = self.current_player_service.connect("play", self.on_play) + self.current_player_service._signal_handlers.append(handler_id) + + handler_id = self.current_player_service.connect("pause", self.on_pause) + self.current_player_service._signal_handlers.append(handler_id) + + handler_id = self.current_player_service.connect("meta-change", self.on_metadata) + self.current_player_service._signal_handlers.append(handler_id) except Exception: # If signal connection fails, clean up the service if hasattr(self.current_player_service, 'cleanup'): @@ -548,6 +579,9 @@ class MusicPlayer(Box): # Reset to safe state self.current_player = None self.current_player_service = None + finally: + # Always reset recursion protection flag + self._in_player_switch = False def on_track_position(self, service, pos, dur): """Handle track position updates""" diff --git a/services/mpris.py b/services/mpris.py index e584437..5e54d67 100644 --- a/services/mpris.py +++ b/services/mpris.py @@ -42,22 +42,44 @@ class PlayerService(Service): poll_from=lambda f, *_: self._player.get_position(), on_changed=lambda f, *_: self.fabricating(), ) + # Add state tracking to prevent repeated operations + self._fabricator_running = False + self._cleanup_done = False self.poll_progress() def cleanup(self): """Clean up resources when service is no longer needed""" + if self._cleanup_done: + return # Already cleaned up + try: if hasattr(self, 'pos_fabricator') and self.pos_fabricator: - self.pos_fabricator.stop() + if self._fabricator_running: + self.pos_fabricator.stop() + self._fabricator_running = False + self.pos_fabricator = None + self._cleanup_done = True except Exception as e: print(f"[DEBUG] Error during PlayerService cleanup: {e}") def on_seeked(self, player, position): - if self.status.value_name == "PLAYERCTL_PLAYBACK_STATUS_PLAYING": - self.pos_fabricator.start() + if (not self._cleanup_done and + self.status.value_name == "PLAYERCTL_PLAYBACK_STATUS_PLAYING" and + not self._fabricator_running): + try: + self.pos_fabricator.start() + self._fabricator_running = True + except Exception: + pass # Ignore if already started def set_position(self, pos: float): - self.pos_fabricator.stop() + if not self._cleanup_done and self._fabricator_running: + try: + self.pos_fabricator.stop() + self._fabricator_running = False + except Exception: + pass # Ignore if already stopped + micro_pos = int(pos * 1_000_000) try: self._player.set_position(micro_pos) @@ -65,10 +87,23 @@ class PlayerService(Service): print(f"Failed to seek: {e}") def poll_progress(self): + if self._cleanup_done: + return # Don't operate on cleaned up fabricator + if self.status.value_name == "PLAYERCTL_PLAYBACK_STATUS_PLAYING": - self.pos_fabricator.start() + if not self._fabricator_running: + try: + self.pos_fabricator.start() + self._fabricator_running = True + except Exception: + pass # Ignore if already started else: - self.pos_fabricator.stop() + if self._fabricator_running: + try: + self.pos_fabricator.stop() + self._fabricator_running = False + except Exception: + pass # Ignore if already stopped def fabricating(self): try: