diff --git a/hm/desktop/frobar/.dev/new.py b/hm/desktop/frobar/.dev/new.py index 6233509..d2e9084 100644 --- a/hm/desktop/frobar/.dev/new.py +++ b/hm/desktop/frobar/.dev/new.py @@ -709,6 +709,39 @@ class MprisProvider(MirrorProvider): "mpv": "", } + async def run(self) -> None: + await super().run() + self.status = self.sectionType(parent=self.module, color=self.color) + self.album = self.sectionType(parent=self.module, color=self.color) + self.artist = self.sectionType(parent=self.module, color=self.color) + self.title = self.sectionType(parent=self.module, color=self.color) + + self.manager = gi.repository.Playerctl.PlayerManager() + self.manager.connect("name-appeared", self.on_name_appeared) + self.manager.connect("player-vanished", self.on_player_vanished) + + self.playerctldName = gi.repository.Playerctl.PlayerName() + self.playerctldName.name = "playerctld" + self.playerctldName.source = gi.repository.Playerctl.Source.DBUS_SESSION + + self.player: gi.repository.Playerctl.Player | None = None + self.playing = asyncio.Event() + + for name in self.manager.props.player_names: + self.init_player(name) + + self.update_sections() + + while True: + await self.playing.wait() + self.update_title() + if self.player: + pos = self.player.props.position + rem = 1 - (pos % 1000000) / 1000000 + await asyncio.sleep(rem) + else: + self.playing.clear() + @staticmethod def get( something: gi.overrides.GLib.Variant, key: str, default: typing.Any = None @@ -725,13 +758,41 @@ class MprisProvider(MirrorProvider): else: return str(datetime.timedelta(microseconds=ms)) - def show_player(self, player_: gi.repository.Playerctl.Player) -> None: - player = player_.props.player_name + def find_current_player(self) -> None: + for name in [self.playerctldName] + self.manager.props.player_names: + # TODO Test what happens when playerctld is not available + self.player = gi.repository.Playerctl.Player.new_from_name(name) + if not self.player.props.can_play: + continue + break + else: + self.player = None + + def update_sections(self) -> None: + self.find_current_player() + + if self.player is None: + self.status.setText(None) + self.album.setText(None) + self.artist.setText(None) + self.title.setText(None) + self.playing.clear() + return + + player = self.player.props.player_name player = self.PROVIDERS.get(player, player) - status = self.STATUSES.get(player_.props.playback_status, "?") + status = self.STATUSES.get(self.player.props.playback_status, "?") self.status.setText(f"{player} {status}") - metadata = player_.props.metadata + if ( + self.player.props.playback_status + == gi.repository.Playerctl.PlaybackStatus.PLAYING + ): + self.playing.set() + else: + self.playing.clear() + + metadata = self.player.props.metadata album = self.get(metadata, "xesam:album") if album: @@ -746,8 +807,14 @@ class MprisProvider(MirrorProvider): else: self.artist.setText(None) - pos = player_.props.position # In µs - # FIXME Doesn't increment during play + self.update_title() + + def update_title(self) -> None: + if self.player is None: + return + + metadata = self.player.props.metadata + pos = self.player.props.position # In µs text = f" {self.format_us(pos)}" dur = self.get(metadata, "mpris:length") if dur: @@ -757,38 +824,12 @@ class MprisProvider(MirrorProvider): text += f" {clip(title)}" self.title.setText(text) - def show_players( - self, manager: gi.repository.Playerctl.PlayerManager, exclude: str | None = None - ) -> None: - # FIXME Opening and closing a chromium player, it will stay there (as default), - # which is not the behaviour of playerctl somehow - for name in manager.props.player_names: - if name == exclude: # DEBUG - continue - player = gi.repository.Playerctl.Player.new_from_name(name) - self.show_player(player) - break - else: - self.status.setText(None) - self.album.setText(None) - self.artist.setText(None) - self.title.setText(None) - - def on_player_appear( - self, - manager: gi.repository.Playerctl.PlayerManager, - player: gi.repository.Playerctl.Player, - ) -> None: - log.debug(f"Player appeared: {player.props.player_name}") - self.show_player(player) - def on_player_vanished( self, manager: gi.repository.Playerctl.PlayerManager, player: gi.repository.Playerctl.Player, ) -> None: - log.debug(f"Player vanished: {player.props.player_name}") - self.show_player(player) + self.update_sections() def on_event( self, @@ -796,54 +837,25 @@ class MprisProvider(MirrorProvider): _: typing.Any, manager: gi.repository.Playerctl.PlayerManager, ) -> None: - log.debug(f"Player evented: {player.props.player_name}") - self.show_player(player) + self.update_sections() - def init_player( - self, manager: gi.repository.Playerctl.PlayerManager, name: str - ) -> None: + def init_player(self, name: gi.repository.Playerctl.PlayerName) -> None: player = gi.repository.Playerctl.Player.new_from_name(name) # All events will cause the active player to change, # so we listen on all events, even if the display won't change - player.connect("playback-status", self.on_event, manager) - player.connect("loop-status", self.on_event, manager) - player.connect("shuffle", self.on_event, manager) - player.connect("metadata", self.on_event, manager) - player.connect("volume", self.on_event, manager) - player.connect("seeked", self.on_event, manager) - manager.manage_player(player) + player.connect("playback-status", self.on_event, self.manager) + player.connect("loop-status", self.on_event, self.manager) + player.connect("shuffle", self.on_event, self.manager) + player.connect("metadata", self.on_event, self.manager) + player.connect("volume", self.on_event, self.manager) + player.connect("seeked", self.on_event, self.manager) + self.manager.manage_player(player) def on_name_appeared( self, manager: gi.repository.Playerctl.PlayerManager, name: str ) -> None: - log.debug(f"Player name appeared: {name}") - self.init_player(manager, name) - - def on_name_vanished( - self, - manager: gi.repository.Playerctl.PlayerManager, - name: str, - ) -> None: - log.debug(f"Player name vanished: {name}") - self.show_players(manager, exclude=name) - - async def run(self) -> None: - await super().run() - self.status = self.sectionType(parent=self.module, color=self.color) - self.album = self.sectionType(parent=self.module, color=self.color) - self.artist = self.sectionType(parent=self.module, color=self.color) - self.title = self.sectionType(parent=self.module, color=self.color) - - manager = gi.repository.Playerctl.PlayerManager() - manager.connect("name-appeared", self.on_name_appeared) - manager.connect("player-appeared", self.on_player_appear) - manager.connect("name-vanished", self.on_name_vanished) - manager.connect("player-vanished", self.on_player_vanished) - - for name in manager.props.player_names: - self.init_player(manager, name) - - self.show_players(manager) + self.init_player(name) + self.update_sections() class AlertingProvider(Provider):