nova-shell/shell/services/MprisService.qml

126 lines
3.9 KiB
QML

pragma Singleton
import QtQuick
import QtQml.Models
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
property string _selectedIdentity: ""
property var _lastPlayedTime: ({})
onPlayerChanged: {
if (player)
_selectedIdentity = player.identity ?? "";
}
onPlayersChanged: {
if (_selectedIdentity) {
const idx = players.findIndex(p => (p.identity ?? "") === _selectedIdentity);
if (idx >= 0) {
playerIdx = idx;
} else {
_switchToBest();
}
} else if (playerIdx >= players.length) {
playerIdx = 0;
}
}
// Watch all raw players for state changes via Instantiator -
// delegates persist across state changes since the model only
// changes on player add/remove, not property changes
property Instantiator _stateWatchers: Instantiator {
model: Mpris.players
delegate: QtObject {
required property MprisPlayer modelData
property Connections _conn: Connections {
target: modelData
function onPlaybackStateChanged() {
root._onPlaybackStateChanged(modelData);
}
}
}
}
function _onPlaybackStateChanged(p) {
if (!p)
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;
}
}
} else if (p.playbackState === MprisPlaybackState.Paused && p === player) {
_switchToPlaying();
}
}
function _switchToPlaying() {
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() {
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 ?? "";
}
}