From 4adace6c4c260b646fcf84b14a23bbc796655e65 Mon Sep 17 00:00:00 2001 From: Makesesama Date: Tue, 5 May 2026 23:58:15 +0200 Subject: [PATCH] feat: subscribe to all players --- sims/modules/player.py | 107 ++++++++++++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 28 deletions(-) diff --git a/sims/modules/player.py b/sims/modules/player.py index d3b87c1..26660c6 100644 --- a/sims/modules/player.py +++ b/sims/modules/player.py @@ -1,3 +1,4 @@ +import contextlib import os import urllib.parse import urllib.request @@ -624,13 +625,13 @@ class PlayerSmall(Box): self.mpris_manager = MprisPlayerManager() self.mpris_player = None - self.current_index = 0 + self._players = {} + self._player_handlers = {} + self._last_status = {} - players = self.mpris_manager.players - if players: - mp = MprisPlayer(players[self.current_index]) - self.mpris_player = mp - self.mpris_player.connect("changed", self._on_mpris_changed) + for p in self.mpris_manager.players: + self._track_player(p) + self._select_initial_player() self._apply_mpris_properties() self.mpris_manager.connect("player-appeared", self.on_player_appeared) @@ -805,25 +806,28 @@ class PlayerSmall(Box): def _on_icon_button_press(self, widget, event): if event.type != Gdk.EventType.BUTTON_PRESS: return True - players = self.mpris_manager.players - if not players: - return True if event.button == 2: + if not self.mpris_player: + return True self._display_index = (self._display_index + 1) % len(self._display_options) self._current_display = self._display_options[self._display_index] self._apply_mpris_properties() return True + players = list(self._players.values()) + if not players: + return True + + idx = players.index(self.mpris_player) if self.mpris_player in players else -1 if event.button == 1: - self.current_index = (self.current_index + 1) % len(players) + idx = (idx + 1) % len(players) elif event.button == 3: - self.current_index = (self.current_index - 1) % len(players) + idx = (idx - 1) % len(players) else: return True - self.mpris_player = MprisPlayer(players[self.current_index]) - self.mpris_player.connect("changed", self._on_mpris_changed) + self.mpris_player = players[idx] self._apply_mpris_properties() return True @@ -857,24 +861,71 @@ class PlayerSmall(Box): ) self._update_spin() - def _on_mpris_changed(self, *args): - self._apply_mpris_properties() + def _track_player(self, playerctl_player): + mp = MprisPlayer(playerctl_player) + name = mp.player_name + handler_id = mp.connect("changed", self._on_any_player_changed) + self._players[name] = mp + self._player_handlers[name] = handler_id + self._last_status[name] = mp.playback_status + + def _untrack_player(self, name): + mp = self._players.pop(name, None) + handler_id = self._player_handlers.pop(name, None) + self._last_status.pop(name, None) + if mp and handler_id is not None: + with contextlib.suppress(Exception): + mp.disconnect(handler_id) + + def _select_initial_player(self): + for mp in self._players.values(): + if mp.playback_status == "playing": + self.mpris_player = mp + return + if self._players: + self.mpris_player = next(iter(self._players.values())) + + def _on_any_player_changed(self, player): + name = player.player_name + prev_status = self._last_status.get(name) + cur_status = player.playback_status + self._last_status[name] = cur_status + + if player is self.mpris_player: + self._apply_mpris_properties() + return + + # Auto-follow: only on a fresh transition into "playing", and only + # if the active player isn't already playing (so a manual selection + # of a paused player isn't overridden by the player it was already + # competing with). + if cur_status == "playing" and prev_status != "playing": + active_playing = ( + self.mpris_player + and self.mpris_player.playback_status == "playing" + ) + if not active_playing: + self.mpris_player = player + self._apply_mpris_properties() def on_player_appeared(self, manager, player): + self._track_player(player) if not self.mpris_player: - self.mpris_player = MprisPlayer(player) - self.mpris_player.connect("changed", self._on_mpris_changed) + name = player.get_property("player-name") + self.mpris_player = self._players.get(name) self._apply_mpris_properties() def on_player_vanished(self, manager, player_name): - players = self.mpris_manager.players - if self.mpris_player and self.mpris_player.player_name == player_name: - if players: - self.current_index = self.current_index % len(players) - self.mpris_player = MprisPlayer(players[self.current_index]) - self.mpris_player.connect("changed", self._on_mpris_changed) - else: - self.mpris_player = None - elif not players: - self.mpris_player = None - self._apply_mpris_properties() + was_active = ( + self.mpris_player and self.mpris_player.player_name == player_name + ) + self._untrack_player(player_name) + if was_active: + replacement = next( + (mp for mp in self._players.values() if mp.playback_status == "playing"), + None, + ) + if replacement is None and self._players: + replacement = next(iter(self._players.values())) + self.mpris_player = replacement + self._apply_mpris_properties()