pragma Singleton import QtQuick import Quickshell.Services.Mpris QtObject { id: root readonly property var players: (Mpris.players.values ?? []).filter(p => p.trackTitle || p.playbackState === MprisPlaybackState.Playing || p.playbackState === MprisPlaybackState.Paused) property int playerIdx: 0 readonly property MprisPlayer player: players[playerIdx] ?? players[0] ?? null readonly property bool playing: player?.playbackState === MprisPlaybackState.Playing // Track the identity of the selected player so we can find it after list changes property string _selectedIdentity: "" // Track which player played most recently (by identity) property var _lastPlayedTime: ({}) onPlayerChanged: { if (player) _selectedIdentity = player.identity ?? ""; } onPlayersChanged: { // Try to find the previously selected player by identity if (_selectedIdentity) { const idx = players.findIndex(p => (p.identity ?? "") === _selectedIdentity); if (idx >= 0) { playerIdx = idx; return; } // Selected player disappeared - switch to best alternative _switchToBest(); } else if (playerIdx >= players.length) { playerIdx = 0; } } // Watch playback state changes on all players property Connections _mprisConn: Connections { target: Mpris function onPlayersChanged() { // Re-evaluate when the global player list changes Qt.callLater(_reconnectWatchers); } } property var _watchedPlayers: [] function _reconnectWatchers() { // Clean up old watchers for (const w of _watchedPlayers) { if (w.conn) w.conn.destroy(); } _watchedPlayers = []; // Watch each player's playback state for (const p of players) { const conn = _watcherComp.createObject(root, { target: p }); _watchedPlayers.push({ player: p, conn: conn }); } } property Component _watcherComp: Component { Connections { function onPlaybackStateChanged() { root._onPlaybackStateChanged(target); } } } function _onPlaybackStateChanged(p) { if (!p) return; const identity = p.identity ?? ""; if (p.playbackState === MprisPlaybackState.Playing) { // Record this as the most recently playing const times = Object.assign({}, _lastPlayedTime); times[identity] = Date.now(); _lastPlayedTime = times; // If current player is not playing, switch to this one if (player !== p && player?.playbackState !== MprisPlaybackState.Playing) { const idx = players.indexOf(p); if (idx >= 0) { playerIdx = idx; _selectedIdentity = identity; } } } else if (p.playbackState === MprisPlaybackState.Paused && p === player) { // Current player paused - switch to a playing one if available _switchToPlaying(); } } function _switchToPlaying() { // Find a playing player, prefer most recently started let best = -1; let bestTime = 0; for (let i = 0; i < players.length; i++) { if (players[i].playbackState === MprisPlaybackState.Playing) { const t = _lastPlayedTime[players[i].identity ?? ""] || 0; if (best < 0 || t > bestTime) { best = i; bestTime = t; } } } if (best >= 0) { playerIdx = best; _selectedIdentity = players[best].identity ?? ""; } } function _switchToBest() { // Switch to playing player, or most recently played let best = -1; let bestTime = 0; let bestPlaying = -1; let bestPlayingTime = 0; for (let i = 0; i < players.length; i++) { const identity = players[i].identity ?? ""; const t = _lastPlayedTime[identity] || 0; if (players[i].playbackState === MprisPlaybackState.Playing) { if (bestPlaying < 0 || t > bestPlayingTime) { bestPlaying = i; bestPlayingTime = t; } } if (best < 0 || t > bestTime) { best = i; bestTime = t; } } const chosen = bestPlaying >= 0 ? bestPlaying : (best >= 0 ? best : 0); playerIdx = chosen; if (players[chosen]) _selectedIdentity = players[chosen].identity ?? ""; } function switchPlayer(idx) { playerIdx = idx; if (players[idx]) _selectedIdentity = players[idx].identity ?? ""; } Component.onCompleted: Qt.callLater(_reconnectWatchers) }