From 8a4be4936a6cdc833c4ea3f776764c6a4d458e3e Mon Sep 17 00:00:00 2001 From: Connor Welsh Date: Thu, 28 May 2026 00:19:34 -0400 Subject: [PATCH] Hide MPRIS players that have nothing to play (#2509) * fix(Mpris): exclude idle players from active-player selection Add MprisController.isIdle() (player Stopped with empty title and artist). _resolveActivePlayer excludes idle players from every selection path, and re-resolves when the active player itself goes idle. Existing triggers (availablePlayers change, isPlaying becoming true) do not fire for a player merely stopping. The fallback now gates on !isIdle(p) instead of p.canPlay. canPlay describes whether Play() would succeed; !isIdle describes whether there is anything to surface. canControl unchanged. When no eligible player remains, activePlayer becomes null and consumers that gate on it unload (the bar media widget via WidgetHost; the dash via the companion fix). * fix(DankDash): show no-player state when active player resolves to null showNoPlayerNow gated on _noneAvailable (player count === 0) or activePlayer being idle. Neither covers activePlayer being null while players remain registered, which is now possible (an always-on player sitting stopped with empty metadata). Key showNoPlayerNow on !activePlayer; drop the unreachable _trulyIdle. * add(MprisController): add track artist change handling to active player resolution --------- --- .../Modules/DankDash/MediaPlayerTab.qml | 3 +-- quickshell/Services/MprisController.qml | 23 ++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/quickshell/Modules/DankDash/MediaPlayerTab.qml b/quickshell/Modules/DankDash/MediaPlayerTab.qml index 2070081e..7214d09f 100644 --- a/quickshell/Modules/DankDash/MediaPlayerTab.qml +++ b/quickshell/Modules/DankDash/MediaPlayerTab.qml @@ -65,8 +65,7 @@ Item { // Derived "no players" state: always correct, no timers. readonly property int _playerCount: allPlayers ? allPlayers.length : 0 readonly property bool _noneAvailable: _playerCount === 0 - readonly property bool _trulyIdle: activePlayer && activePlayer.playbackState === MprisPlaybackState.Stopped && !activePlayer.trackTitle && !activePlayer.trackArtist - readonly property bool showNoPlayerNow: (!_switchHold) && (_noneAvailable || _trulyIdle) + readonly property bool showNoPlayerNow: (!_switchHold) && (_noneAvailable || !activePlayer) property bool _switchHold: false Timer { diff --git a/quickshell/Services/MprisController.qml b/quickshell/Services/MprisController.qml index d7b29485..14db41b6 100644 --- a/quickshell/Services/MprisController.qml +++ b/quickshell/Services/MprisController.qml @@ -17,12 +17,22 @@ Singleton { target: root.activePlayer function onTrackTitleChanged() { root.activePlayerStableLength = (root.activePlayer && root.activePlayer.lengthSupported && root.activePlayer.length > 1) ? root.activePlayer.length : 0; + if (root.isIdle(root.activePlayer)) + root._resolveActivePlayer(); + } + function onTrackArtistChanged() { + if (root.isIdle(root.activePlayer)) + root._resolveActivePlayer(); } function onLengthChanged() { if (root.activePlayer && root.activePlayer.lengthSupported && root.activePlayer.length > 1) { root.activePlayerStableLength = root.activePlayer.length; } } + function onPlaybackStateChanged() { + if (root.isIdle(root.activePlayer)) + root._resolveActivePlayer(); + } } onActivePlayerChanged: { @@ -44,6 +54,13 @@ Singleton { } } + function isIdle(player: MprisPlayer): bool { + return player + && player.playbackState === MprisPlaybackState.Stopped + && !player.trackTitle + && !player.trackArtist; + } + function _resolveActivePlayer(): void { const playing = availablePlayers.find(p => p.isPlaying); if (playing) { @@ -51,17 +68,17 @@ Singleton { _persistIdentity(playing.identity); return; } - if (activePlayer && availablePlayers.indexOf(activePlayer) >= 0) + if (activePlayer && availablePlayers.indexOf(activePlayer) >= 0 && !isIdle(activePlayer)) return; const savedId = SessionData.lastPlayerIdentity; if (savedId) { const match = availablePlayers.find(p => p.identity === savedId); - if (match) { + if (match && !isIdle(match)) { activePlayer = match; return; } } - activePlayer = availablePlayers.find(p => p.canControl && p.canPlay) ?? null; + activePlayer = availablePlayers.find(p => p.canControl && !isIdle(p)) ?? null; if (activePlayer) _persistIdentity(activePlayer.identity); }