replace polling with signal-based mpris state tracking via raw player list

This commit is contained in:
Damocles 2026-04-18 13:27:02 +02:00
parent 09c2de136c
commit 0ca190a4a6

View file

@ -11,14 +11,9 @@ QtObject {
readonly property MprisPlayer player: players[playerIdx] ?? players[0] ?? null readonly property MprisPlayer player: players[playerIdx] ?? players[0] ?? null
readonly property bool playing: player?.playbackState === MprisPlaybackState.Playing 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: "" property string _selectedIdentity: ""
// Track which player played most recently (by identity)
property var _lastPlayedTime: ({}) property var _lastPlayedTime: ({})
property var _watchers: []
// Track previous playing state per player to detect transitions
property var _wasPlaying: ({})
onPlayerChanged: { onPlayerChanged: {
if (player) if (player)
@ -26,13 +21,11 @@ QtObject {
} }
onPlayersChanged: { onPlayersChanged: {
// Try to find the previously selected player by identity
if (_selectedIdentity) { if (_selectedIdentity) {
const idx = players.findIndex(p => (p.identity ?? "") === _selectedIdentity); const idx = players.findIndex(p => (p.identity ?? "") === _selectedIdentity);
if (idx >= 0) { if (idx >= 0) {
playerIdx = idx; playerIdx = idx;
} else { } else {
// Selected player disappeared - switch to best alternative
_switchToBest(); _switchToBest();
} }
} else if (playerIdx >= players.length) { } else if (playerIdx >= players.length) {
@ -40,46 +33,60 @@ QtObject {
} }
} }
// Poll playback states to detect transitions - Connections on dynamic // Watch raw Mpris.players for add/remove - this does NOT fire on state changes
// targets get destroyed during list re-evaluation, losing the signal property Connections _mprisConn: Connections {
property Timer _statePoll: Timer { target: Mpris
interval: 250 function onPlayersChanged() {
running: root.players.length > 0 root._reconnectWatchers();
repeat: true }
onTriggered: root._checkStates()
} }
function _checkStates() { function _reconnectWatchers() {
const prevWas = _wasPlaying; for (const w of _watchers)
const nextWas = {}; w.destroy();
for (let i = 0; i < players.length; i++) { const raw = Mpris.players.values ?? [];
const p = players[i]; const newWatchers = [];
const identity = p.identity ?? ""; for (const p of raw) {
const isPlaying = p.playbackState === MprisPlaybackState.Playing; const conn = _watcherComp.createObject(root, {
nextWas[identity] = isPlaying; target: p
});
newWatchers.push(conn);
}
_watchers = newWatchers;
}
if (isPlaying && !prevWas[identity]) { property Component _watcherComp: Component {
// This player just started playing Connections {
const times = Object.assign({}, _lastPlayedTime); function onPlaybackStateChanged() {
times[identity] = Date.now(); root._onPlaybackStateChanged(target);
_lastPlayedTime = times; }
}
}
// Always switch to the newly playing player function _onPlaybackStateChanged(p) {
if (player !== p) { if (!p)
playerIdx = i; return;
const identity = p.identity ?? "";
if (p.playbackState === MprisPlaybackState.Playing) {
const times = Object.assign({}, _lastPlayedTime);
times[identity] = Date.now();
_lastPlayedTime = times;
// Always switch to the newly playing player
if (player !== p) {
const idx = players.indexOf(p);
if (idx >= 0) {
playerIdx = idx;
_selectedIdentity = identity; _selectedIdentity = identity;
} }
} }
} } else if (p.playbackState === MprisPlaybackState.Paused && p === player) {
// Current player paused - switch to a playing one if available
// Check if current player just paused
const curIdentity = player?.identity ?? "";
if (prevWas[curIdentity] && !nextWas[curIdentity]) {
_switchToPlaying(); _switchToPlaying();
} }
_wasPlaying = nextWas;
} }
function _switchToPlaying() { function _switchToPlaying() {
@ -132,4 +139,6 @@ QtObject {
if (players[idx]) if (players[idx])
_selectedIdentity = players[idx].identity ?? ""; _selectedIdentity = players[idx].identity ?? "";
} }
Component.onCompleted: _reconnectWatchers()
} }