feat(settings): add source priority selection

This commit is contained in:
2026-06-06 22:41:39 +00:00
parent 5a48b1b3a9
commit 88074ccb47
4 changed files with 175 additions and 41 deletions
+112 -41
View File
@@ -11,82 +11,155 @@ import org.kde.plasma.private.mpris as Mpris
QtObject {
id: root
readonly property int containerRole: Qt.UserRole + 1
property string sourceName: "any"
property string sourcePriority: ""
property var mpris2Model
property int modelRevision: 0
readonly property var normalizedSourcePriority: parseSourcePriority(sourcePriority)
readonly property var resolvedPlayer: resolvePlayer()
readonly property bool ready: {
if (!mpris2Model.currentPlayer)
if (!resolvedPlayer)
return false;
return mpris2Model.currentPlayer.desktopEntry === sourceName || sourceName === "any";
return sourceName === "any" || entriesMatch(resolvedPlayer.desktopEntry, sourceName);
}
readonly property string artists: ready ? mpris2Model.currentPlayer.artist : ""
readonly property string title: ready ? mpris2Model.currentPlayer.track : ""
readonly property int playbackStatus: ready ? mpris2Model.currentPlayer.playbackStatus : Mpris.PlaybackStatus.Unknown
readonly property int shuffle: ready ? mpris2Model.currentPlayer.shuffle : Mpris.ShuffleStatus.Unknown
readonly property string artUrl: ready ? mpris2Model.currentPlayer.artUrl : ""
readonly property int loopStatus: ready ? mpris2Model.currentPlayer.loopStatus : Mpris.LoopStatus.Unknown
readonly property double songPosition: ready ? mpris2Model.currentPlayer.position : 0
readonly property double songLength: ready ? mpris2Model.currentPlayer.length : 0
readonly property real volume: ready ? mpris2Model.currentPlayer.volume : 0
readonly property string identity: ready ? mpris2Model.currentPlayer.identity : ""
readonly property bool canGoNext: ready ? mpris2Model.currentPlayer.canGoNext : false
readonly property bool canGoPrevious: ready ? mpris2Model.currentPlayer.canGoPrevious : false
readonly property bool canPlay: ready ? mpris2Model.currentPlayer.canPlay : false
readonly property bool canPause: ready ? mpris2Model.currentPlayer.canPause : false
readonly property bool canSeek: ready ? mpris2Model.currentPlayer.canSeek : false
readonly property bool canRaise: ready ? mpris2Model.currentPlayer.canRaise : false
readonly property string artists: ready ? resolvedPlayer.artist : ""
readonly property string title: ready ? resolvedPlayer.track : ""
readonly property int playbackStatus: ready ? resolvedPlayer.playbackStatus : Mpris.PlaybackStatus.Unknown
readonly property int shuffle: ready ? resolvedPlayer.shuffle : Mpris.ShuffleStatus.Unknown
readonly property string artUrl: ready ? resolvedPlayer.artUrl : ""
readonly property int loopStatus: ready ? resolvedPlayer.loopStatus : Mpris.LoopStatus.Unknown
readonly property double songPosition: ready ? resolvedPlayer.position : 0
readonly property double songLength: ready ? resolvedPlayer.length : 0
readonly property real volume: ready ? resolvedPlayer.volume : 0
readonly property string identity: ready ? resolvedPlayer.identity : ""
readonly property bool canGoNext: ready ? resolvedPlayer.canGoNext : false
readonly property bool canGoPrevious: ready ? resolvedPlayer.canGoPrevious : false
readonly property bool canPlay: ready ? resolvedPlayer.canPlay : false
readonly property bool canPause: ready ? resolvedPlayer.canPause : false
readonly property bool canSeek: ready ? resolvedPlayer.canSeek : false
readonly property bool canRaise: ready ? resolvedPlayer.canRaise : false
// To know whether Shuffle and Loop can be changed we have to check if the property is defined,
// unlike the other commands, LoopStatus and Shuffle do not have specific properties such as
// CanPause, CanSeek, etc.
readonly property bool canChangeShuffle: ready ? mpris2Model.currentPlayer.shuffle !== undefined : false
readonly property bool canChangeLoopStatus: ready ? mpris2Model.currentPlayer.loopStatus !== undefined : false
readonly property bool canChangeShuffle: ready ? resolvedPlayer.shuffle !== undefined : false
readonly property bool canChangeLoopStatus: ready ? resolvedPlayer.loopStatus !== undefined : false
function normalizedEntry(value) {
return (value || "").toString().trim().toLowerCase();
}
function entriesMatch(left, right) {
const normalizedLeft = normalizedEntry(left);
const normalizedRight = normalizedEntry(right);
if (!normalizedLeft || !normalizedRight)
return false;
return normalizedLeft === normalizedRight;
}
function parseSourcePriority(value) {
return (value || "").split(",").map((entry) => {
return normalizedEntry(entry);
}).filter((entry) => {
return entry.length > 0;
});
}
function playerAt(row) {
if (!mpris2Model || row < 0 || row >= mpris2Model.rowCount())
return null;
return mpris2Model.data(mpris2Model.index(row, 0), containerRole);
}
function findPlayerByEntry(targetEntry) {
if (!targetEntry || !mpris2Model)
return null;
const playerCount = mpris2Model.rowCount();
for (let row = 0; row < playerCount; ++row) {
const player = playerAt(row);
if (player && entriesMatch(player.desktopEntry, targetEntry))
return player;
}
return null;
}
function resolvePlayer() {
modelRevision;
if (!mpris2Model)
return null;
for (const targetEntry of normalizedSourcePriority) {
const matchedPlayer = findPlayerByEntry(targetEntry);
if (matchedPlayer)
return matchedPlayer;
}
return mpris2Model.currentPlayer || null;
}
function togglePlayPause() {
if (mpris2Model.currentPlayer)
mpris2Model.currentPlayer.PlayPause();
if (resolvedPlayer)
resolvedPlayer.PlayPause();
}
function setPosition(position) {
mpris2Model.currentPlayer.position = position;
if (resolvedPlayer)
resolvedPlayer.position = position;
}
function next() {
if (mpris2Model.currentPlayer)
mpris2Model.currentPlayer.Next();
if (resolvedPlayer)
resolvedPlayer.Next();
}
function previous() {
if (mpris2Model.currentPlayer)
mpris2Model.currentPlayer.Previous();
if (resolvedPlayer)
resolvedPlayer.Previous();
}
function updatePosition() {
if (mpris2Model.currentPlayer)
mpris2Model.currentPlayer.updatePosition();
if (resolvedPlayer)
resolvedPlayer.updatePosition();
}
function setVolume(volume) {
mpris2Model.currentPlayer.volume = volume;
if (resolvedPlayer)
resolvedPlayer.volume = volume;
}
function changeVolume(delta, showOSD) {
mpris2Model.currentPlayer.changeVolume(delta, showOSD);
if (resolvedPlayer)
resolvedPlayer.changeVolume(delta, showOSD);
}
function setShuffle(shuffle) {
mpris2Model.currentPlayer.shuffle = shuffle;
if (resolvedPlayer)
resolvedPlayer.shuffle = shuffle;
}
function setLoopStatus(loopStatus) {
mpris2Model.currentPlayer.loopStatus = loopStatus;
if (resolvedPlayer)
resolvedPlayer.loopStatus = loopStatus;
}
function raise() {
mpris2Model.currentPlayer.Raise();
if (resolvedPlayer)
resolvedPlayer.Raise();
}
function formatTrackTime(s) {
@@ -99,13 +172,11 @@ QtObject {
}
mpris2Model: Mpris.Mpris2Model {
onRowsInserted: (_, rowIndex) => {
const CONTAINER_ROLE = Qt.UserRole + 1;
const player = this.data(this.index(rowIndex, 0), CONTAINER_ROLE);
if (player.desktopEntry === root.sourceName)
this.currentIndex = rowIndex;
}
onRowsInserted: root.modelRevision += 1
onRowsRemoved: root.modelRevision += 1
onModelReset: root.modelRevision += 1
onDataChanged: root.modelRevision += 1
onCurrentPlayerChanged: root.modelRevision += 1
}
}