diff --git a/modules/controlcenter/expanded_player.py b/modules/controlcenter/expanded_player.py index 53828e5..b07f239 100644 --- a/modules/controlcenter/expanded_player.py +++ b/modules/controlcenter/expanded_player.py @@ -244,6 +244,27 @@ class EmbeddedExpandedPlayer(Box): finally: super().destroy() + def _periodic_cleanup(self): + """Light cleanup for EmbeddedExpandedPlayer reuse""" + try: + logger.debug("🧹 EmbeddedExpandedPlayer light cleanup starting") + + # Only clean up the player content lightly + if hasattr(self, 'player_content') and hasattr(self.player_content, '_periodic_cleanup'): + self.player_content._periodic_cleanup() + + # Clean global caches but don't destroy core functionality + global _artwork_cache + _artwork_cache.clear() + + # Light garbage collection + gc.collect() + + logger.debug("🧹 EmbeddedExpandedPlayer light cleanup completed") + + except Exception as e: + logger.warning(f"Error during EmbeddedExpandedPlayer light cleanup: {e}") + class PlayerBoxStack(Box): """Memory-optimized widget that displays current player information.""" @@ -298,26 +319,39 @@ class PlayerBoxStack(Box): self._cleanup_source_id = GLib.timeout_add_seconds(300, self._periodic_cleanup) # Every 5 minutes def _periodic_cleanup(self): - """Periodic cleanup to prevent memory leaks.""" + """Light cleanup for widget reuse - preserve functionality.""" try: - # Clean artwork cache + logger.debug("Starting light expanded player cleanup for reuse") + + # Clean artwork cache to reduce memory cleanup_artwork_cache() - # Remove destroyed widgets from tracking - destroyed_keys = [] - for key, widget in self._player_widgets.items(): - if not widget or not widget.get_parent(): - destroyed_keys.append(key) + # Reset visual state but preserve connections and core functionality + # Only clear the player widgets that can be recreated + for widget in list(self._player_widgets.values()): + try: + if widget and hasattr(widget, "get_parent") and widget.get_parent(): + # Only remove from parent, don't destroy (let GTK handle it) + widget.get_parent().remove(widget) + except Exception: + pass + # Don't clear the _player_widgets dict - just let them be recreated - for key in destroyed_keys: - if key in self._player_widgets: - del self._player_widgets[key] + # Reset stack to show no_media_box without destroying children + try: + if hasattr(self, 'player_stack') and hasattr(self, 'no_media_box'): + self.player_stack.set_visible_child(self.no_media_box) + self.current_stack_pos = 0 + except Exception: + pass - # Force garbage collection + # Light garbage collection gc.collect() + logger.debug("Light expanded player cleanup completed") + except Exception as e: - logger.warning(f"Error during periodic cleanup: {e}") + logger.warning(f"Error during light cleanup: {e}") return True # Continue timer diff --git a/modules/controlcenter/main.py b/modules/controlcenter/main.py index d9a2cdf..862dbc1 100644 --- a/modules/controlcenter/main.py +++ b/modules/controlcenter/main.py @@ -425,21 +425,20 @@ class ModusControlCenter(Window): except Exception: pass - # Clean up expanded player widget - BUT preserve for reuse - if self._expanded_player_widget and hasattr( - self._expanded_player_widget, "destroy" - ): + # Clean up expanded player widget - aggressive cleanup for reuse + if self._expanded_player_widget: try: - # DON'T destroy the widget, just clean its internal state - if hasattr(self._expanded_player_widget, 'player_content'): - # Clean up only the player content, keep the widget shell - if hasattr(self._expanded_player_widget.player_content, '_periodic_cleanup'): - self._expanded_player_widget.player_content._periodic_cleanup() + # Call cleanup directly on the player content (PlayerBoxStack) + if hasattr(self._expanded_player_widget, 'player_content') and \ + hasattr(self._expanded_player_widget.player_content, '_periodic_cleanup'): + self._expanded_player_widget.player_content._periodic_cleanup() + + # Also clean any EmbeddedExpandedPlayer specific state + if hasattr(self._expanded_player_widget, '_periodic_cleanup'): + self._expanded_player_widget._periodic_cleanup() + logger.debug("Cleaned expanded player internal state (preserved for reuse)") - # DON'T set to None - keep for reuse! - # self._expanded_player_widget = None - # self.expanded_player_widgets = None - # self.expanded_player_center_box = None + # DON'T destroy or set to None - keep for reuse! except Exception as e: logger.warning(f"Failed to clean expanded player state: {e}")