1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-01 19:18:28 -04:00

refactor: unify media control calls to MprisController sync

This commit is contained in:
purian23
2026-07-15 14:15:30 -04:00
parent fb2dbced08
commit dc924618fb
7 changed files with 51 additions and 63 deletions
+1 -3
View File
@@ -531,9 +531,7 @@ Item {
} }
function next(): void { function next(): void {
if (MprisController.activePlayer && MprisController.activePlayer.canGoNext) { MprisController.next();
MprisController.activePlayer.next();
}
} }
function stop(): void { function stop(): void {
+4 -4
View File
@@ -129,7 +129,7 @@ BasePill {
if (deltaY > 0) { if (deltaY > 0) {
MprisController.previousOrRewind(); MprisController.previousOrRewind();
} else { } else {
activePlayer.next(); MprisController.next();
} }
} else { } else {
scrollAccumulatorY += deltaY; scrollAccumulatorY += deltaY;
@@ -137,7 +137,7 @@ BasePill {
if (scrollAccumulatorY > 0) { if (scrollAccumulatorY > 0) {
MprisController.previousOrRewind(); MprisController.previousOrRewind();
} else { } else {
activePlayer.next(); MprisController.next();
} }
scrollAccumulatorY = 0; scrollAccumulatorY = 0;
} }
@@ -266,7 +266,7 @@ BasePill {
} else if (mouse.button === Qt.MiddleButton) { } else if (mouse.button === Qt.MiddleButton) {
MprisController.previousOrRewind(); MprisController.previousOrRewind();
} else if (mouse.button === Qt.RightButton) { } else if (mouse.button === Qt.RightButton) {
activePlayer.next(); MprisController.next();
} }
} }
} }
@@ -532,7 +532,7 @@ BasePill {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (activePlayer) { if (activePlayer) {
activePlayer.next(); MprisController.next();
} }
} }
} }
@@ -662,11 +662,12 @@ Item {
weight: 500 weight: 500
} }
MouseArea { StateLayer {
anchors.fill: parent id: playPauseArea
hoverEnabled: true disabled: !root.activePlayer || !root.activePlayer.canTogglePlaying
cursorShape: Qt.PointingHandCursor stateColor: root.onAccent
onClicked: activePlayer && activePlayer.togglePlaying() cornerRadius: parent.radius
onClicked: root.activePlayer.togglePlaying()
} }
ElevationShadow { ElevationShadow {
@@ -706,7 +707,7 @@ Item {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: activePlayer && activePlayer.next() onClicked: MprisController.next()
} }
} }
} }
@@ -190,7 +190,7 @@ Card {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: activePlayer?.next() onClicked: MprisController.next()
} }
} }
} }
@@ -1635,7 +1635,7 @@ Item {
enabled: MprisController.activePlayer?.canGoNext ?? false enabled: MprisController.activePlayer?.canGoNext ?? false
hoverEnabled: enabled hoverEnabled: enabled
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: MprisController.activePlayer?.next() onClicked: MprisController.next()
} }
} }
} }
+9 -24
View File
@@ -48,22 +48,12 @@ Singleton {
target: root.activePlayer target: root.activePlayer
function onTrackTitleChanged() { function onTrackTitleChanged() {
root.activePlayerStableLength = (root.activePlayer && root.activePlayer.lengthSupported && root.activePlayer.length > 1) ? root.activePlayer.length : 0; 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() { function onLengthChanged() {
if (root.activePlayer && root.activePlayer.lengthSupported && root.activePlayer.length > 1) { if (root.activePlayer && root.activePlayer.lengthSupported && root.activePlayer.length > 1) {
root.activePlayerStableLength = root.activePlayer.length; root.activePlayerStableLength = root.activePlayer.length;
} }
} }
function onPlaybackStateChanged() {
if (root.isIdle(root.activePlayer))
root._resolveActivePlayer();
}
} }
onActivePlayerChanged: { onActivePlayerChanged: {
@@ -73,18 +63,6 @@ Singleton {
onAvailablePlayersChanged: _resolveActivePlayer() onAvailablePlayersChanged: _resolveActivePlayer()
Component.onCompleted: _resolveActivePlayer() Component.onCompleted: _resolveActivePlayer()
Instantiator {
model: root.availablePlayers
delegate: Connections {
required property MprisPlayer modelData
target: modelData
function onIsPlayingChanged() {
if (modelData.isPlaying)
root._resolveActivePlayer();
}
}
}
function isIdle(player: MprisPlayer): bool { function isIdle(player: MprisPlayer): bool {
return player return player
&& player.playbackState === MprisPlaybackState.Stopped && player.playbackState === MprisPlaybackState.Stopped
@@ -93,14 +71,15 @@ Singleton {
} }
function _resolveActivePlayer(): void { function _resolveActivePlayer(): void {
// Keep the selected player stable across transient metadata changes.
if (activePlayer && availablePlayers.indexOf(activePlayer) >= 0)
return;
const playing = availablePlayers.find(p => p.isPlaying); const playing = availablePlayers.find(p => p.isPlaying);
if (playing) { if (playing) {
activePlayer = playing; activePlayer = playing;
_persistIdentity(playing.identity); _persistIdentity(playing.identity);
return; return;
} }
if (activePlayer && availablePlayers.indexOf(activePlayer) >= 0 && !isIdle(activePlayer))
return;
const savedId = SessionData.lastPlayerIdentity; const savedId = SessionData.lastPlayerIdentity;
if (savedId) { if (savedId) {
const match = availablePlayers.find(p => p.identity === savedId); const match = availablePlayers.find(p => p.identity === savedId);
@@ -150,4 +129,10 @@ Singleton {
else if (activePlayer.canGoPrevious) else if (activePlayer.canGoPrevious)
activePlayer.previous(); activePlayer.previous();
} }
function next(): void {
const player = activePlayer;
if (player?.canGoNext)
player.next();
}
} }
+28 -24
View File
@@ -56,13 +56,13 @@ Singleton {
return ""; return "";
} }
function _commit(u) { function _commit(u, artKey, srcUrl) {
resolvedArtUrl = u; resolvedArtUrl = u;
_committedArtKey = u !== "" ? _pendingArtKey : ""; _committedArtKey = u !== "" ? artKey : "";
_committedSrcUrl = u !== "" ? _lastArtUrl : ""; _committedSrcUrl = u !== "" ? srcUrl : "";
} }
function loadArtwork(url) { function loadArtwork(url, artKey, requestSerial) {
if (!url || url === "") { if (!url || url === "") {
// Keep stale art; only blank once the empty url debounce settles. // Keep stale art; only blank once the empty url debounce settles.
_lastArtUrl = ""; _lastArtUrl = "";
@@ -85,11 +85,11 @@ Singleton {
// 1. First, check if the file already exists locally // 1. First, check if the file already exists locally
Proc.runCommand(null, ["test", "-f", filePath], (output, exitCode) => { Proc.runCommand(null, ["test", "-f", filePath], (output, exitCode) => {
if (_lastArtUrl !== targetUrl) if (_lastArtUrl !== targetUrl || _requestSerial !== requestSerial)
return; return;
if (exitCode === 0) { if (exitCode === 0) {
_commit(localFileUrl); _commit(localFileUrl, artKey, targetUrl);
loading = false; loading = false;
} else { } else {
const dlCmd = "mkdir -p \"$(dirname \"$1\")\" && curl -f -s -L -o \"$1\" \"$2\" && mv \"$1\" \"$3\" || { rm -f \"$1\"; exit 1; }"; const dlCmd = "mkdir -p \"$(dirname \"$1\")\" && curl -f -s -L -o \"$1\" \"$2\" && mv \"$1\" \"$3\" || { rm -f \"$1\"; exit 1; }";
@@ -102,18 +102,18 @@ Singleton {
const tmpPath = filePath + ".tmp"; const tmpPath = filePath + ".tmp";
Proc.runCommand(null, ["sh", "-c", dlCmd, "sh", tmpPath, maxresUrl, filePath], (maxOutput, maxExitCode) => { Proc.runCommand(null, ["sh", "-c", dlCmd, "sh", tmpPath, maxresUrl, filePath], (maxOutput, maxExitCode) => {
if (_lastArtUrl !== targetUrl) if (_lastArtUrl !== targetUrl || _requestSerial !== requestSerial)
return; return;
if (maxExitCode === 0) { if (maxExitCode === 0) {
_commit(localFileUrl); _commit(localFileUrl, artKey, targetUrl);
loading = false; loading = false;
} else { } else {
Proc.runCommand(null, ["sh", "-c", dlCmd, "sh", tmpPath, mqUrl, filePath], (mqOutput, mqExitCode) => { Proc.runCommand(null, ["sh", "-c", dlCmd, "sh", tmpPath, mqUrl, filePath], (mqOutput, mqExitCode) => {
if (_lastArtUrl !== targetUrl) if (_lastArtUrl !== targetUrl || _requestSerial !== requestSerial)
return; return;
_commit(mqExitCode === 0 ? localFileUrl : targetUrl); _commit(mqExitCode === 0 ? localFileUrl : targetUrl, artKey, targetUrl);
loading = false; loading = false;
}, 50, 15000); }, 50, 15000);
} }
@@ -122,10 +122,10 @@ Singleton {
// Standard curl download for other remote URLs (e.g. SoundCloud) // Standard curl download for other remote URLs (e.g. SoundCloud)
const tmpPath = filePath + ".tmp"; const tmpPath = filePath + ".tmp";
Proc.runCommand(null, ["sh", "-c", dlCmd, "sh", tmpPath, targetUrl, filePath], (dlOutput, dlExitCode) => { Proc.runCommand(null, ["sh", "-c", dlCmd, "sh", tmpPath, targetUrl, filePath], (dlOutput, dlExitCode) => {
if (_lastArtUrl !== targetUrl) if (_lastArtUrl !== targetUrl || _requestSerial !== requestSerial)
return; return;
_commit(dlExitCode === 0 ? localFileUrl : targetUrl); _commit(dlExitCode === 0 ? localFileUrl : targetUrl, artKey, targetUrl);
loading = false; loading = false;
}, 50, 15000); }, 50, 15000);
} }
@@ -141,7 +141,7 @@ Singleton {
// Cover lands after metadata, so poll; commit a content-addressed copy so identical bytes keep an identical url // Cover lands after metadata, so poll; commit a content-addressed copy so identical bytes keep an identical url
const script = "f=\"$1\"; d=\"$2\"; i=0; while [ ! -f \"$f\" ] && [ \"$i\" -lt 20 ]; do sleep 0.15; i=$((i + 1)); done; [ -f \"$f\" ] || exit 1; s=$(sha1sum \"$f\" | cut -c1-40); if [ ! -f \"$d/art_$s\" ]; then mkdir -p \"$d\" && cp \"$f\" \"$d/art_$s.tmp\" && mv \"$d/art_$s.tmp\" \"$d/art_$s\" || exit 1; fi; echo \"$s\""; const script = "f=\"$1\"; d=\"$2\"; i=0; while [ ! -f \"$f\" ] && [ \"$i\" -lt 20 ]; do sleep 0.15; i=$((i + 1)); done; [ -f \"$f\" ] || exit 1; s=$(sha1sum \"$f\" | cut -c1-40); if [ ! -f \"$d/art_$s\" ]; then mkdir -p \"$d\" && cp \"$f\" \"$d/art_$s.tmp\" && mv \"$d/art_$s.tmp\" \"$d/art_$s\" || exit 1; fi; echo \"$s\"";
Proc.runCommand(null, ["sh", "-c", script, "sh", filePath, cacheDir], (output, exitCode) => { Proc.runCommand(null, ["sh", "-c", script, "sh", filePath, cacheDir], (output, exitCode) => {
if (_lastArtUrl !== localUrl) if (_lastArtUrl !== localUrl || _requestSerial !== requestSerial)
return; return;
loading = false; loading = false;
if (exitCode !== 0) if (exitCode !== 0)
@@ -149,7 +149,7 @@ Singleton {
const sha = (output || "").trim(); const sha = (output || "").trim();
if (_artHashDenylist.indexOf(sha) !== -1) if (_artHashDenylist.indexOf(sha) !== -1)
return; return;
_commit("file://" + cacheDir + "/art_" + sha); _commit("file://" + cacheDir + "/art_" + sha, artKey, localUrl);
}, 50, 5000); }, 50, 5000);
} }
@@ -158,7 +158,7 @@ Singleton {
interval: 800 interval: 800
onTriggered: { onTriggered: {
if (root._lastArtUrl === "") if (root._lastArtUrl === "")
root._commit(""); root._commit("", "", "");
} }
} }
@@ -167,6 +167,7 @@ Singleton {
property string _committedArtKey: "" property string _committedArtKey: ""
property string _pendingArtKey: "" property string _pendingArtKey: ""
property string _committedSrcUrl: "" property string _committedSrcUrl: ""
property int _requestSerial: 0
onActivePlayerChanged: _updateArtUrl() onActivePlayerChanged: _updateArtUrl()
@@ -182,9 +183,10 @@ Singleton {
const p = activePlayer; const p = activePlayer;
if (!p) if (!p)
return ""; return "";
// +title/artist: KDEconnect reuses one trackid forever; album excluded: Chrome delivers it late, orphaning the lock // Scope artwork ownership to the MPRIS object and track.
const playerId = p.uniqueId || p.identity || "";
const tid = p.metadata && p.metadata["mpris:trackid"] ? p.metadata["mpris:trackid"].toString() : ""; const tid = p.metadata && p.metadata["mpris:trackid"] ? p.metadata["mpris:trackid"].toString() : "";
return tid + " " + (p.trackTitle || "") + " " + (p.trackArtist || ""); return playerId + " " + tid + " " + (p.trackTitle || "") + " " + (p.trackArtist || "");
} }
function artReadyFor(player) { function artReadyFor(player) {
@@ -194,17 +196,19 @@ Singleton {
function _updateArtUrl() { function _updateArtUrl() {
const key = _trackKey(); const key = _trackKey();
// Skip once real art is committed for this track (dedup Chrome's multi-size if (key !== _pendingArtKey) {
// re-publish). The lock is set in _commit(), never optimistically, so a rejected _requestSerial++;
// placeholder or a short-circuited duplicate url can't wedge the real cover out. loading = false;
if (key !== "" && key === _committedArtKey) }
return;
_pendingArtKey = key; _pendingArtKey = key;
const url = getArtworkUrl(activePlayer); const url = getArtworkUrl(activePlayer);
// Ignore Chrome's same-track thumbnail size updates.
if (key !== "" && key === _committedArtKey)
return;
if (key !== "" && url !== "" && url === _committedSrcUrl) { if (key !== "" && url !== "" && url === _committedSrcUrl) {
_committedArtKey = key; // Chrome can publish track metadata before its new artwork URL.
return; return;
} }
loadArtwork(url); loadArtwork(url, key, _requestSerial);
} }
} }