From 91ee7f20fc7a911bcb0897e99d44a903bbc37c3a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 25 Jun 2026 06:55:11 -0400 Subject: [PATCH] Fix media skip OSD metadata --- shell/plugins/services/media/MediaModel.js | 16 +++++++ shell/plugins/services/media/Service.qml | 49 +++++++++++++++++++++- test/shell.d/media-test.sh | 4 ++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/shell/plugins/services/media/MediaModel.js b/shell/plugins/services/media/MediaModel.js index 00cc9130..3edd3a08 100644 --- a/shell/plugins/services/media/MediaModel.js +++ b/shell/plugins/services/media/MediaModel.js @@ -93,6 +93,20 @@ function playerKey(player) { return String(player.dbusName || player.desktopEntry || player.identity || "") } +function trackSignature(player) { + if (!player) return "" + return [ + player.trackTitle || "", + player.trackArtist || "", + player.trackAlbum || "", + player.trackArtUrl || "" + ].join("\u001f") +} + +function trackChanged(previousSignature, player) { + return trackSignature(player) !== String(previousSignature || "") +} + function labelFor(player) { if (!player) return "" return player.trackTitle || player.identity || player.desktopEntry || "" @@ -120,6 +134,8 @@ if (typeof module !== "undefined") { playerAppLabel: playerAppLabel, playerHasPlaybackStream: playerHasPlaybackStream, playerKey: playerKey, + trackSignature: trackSignature, + trackChanged: trackChanged, labelFor: labelFor, osdMessage: osdMessage } diff --git a/shell/plugins/services/media/Service.qml b/shell/plugins/services/media/Service.qml index 66f9d820..e22bf236 100644 --- a/shell/plugins/services/media/Service.qml +++ b/shell/plugins/services/media/Service.qml @@ -11,6 +11,7 @@ Item { property var shell: null property string preferredPlayerKey: "" property var playerStartedAt: ({}) + property var pendingTrackOsd: null property int playSerial: 0 readonly property var players: Mpris.players ? Mpris.players.values : [] @@ -238,6 +239,10 @@ Item { return MediaModel.osdMessage(player, fallback) } + function trackSignature(player) { + return MediaModel.trackSignature(player) + } + function showOsd(actionLabel, iconName, player) { if (!shell) return shell.summon("omarchy.osd", JSON.stringify({ @@ -246,6 +251,39 @@ Item { })) } + function scheduleOsd(actionLabel, iconName, player, waitForTrackChange, beforeTrackSignature) { + if (waitForTrackChange) { + pendingTrackOsd = { + actionLabel: actionLabel, + iconName: iconName, + player: player, + playerKey: playerKey(player), + before: beforeTrackSignature, + attempts: 0 + } + trackOsdTimer.restart() + } else { + Qt.callLater(function() { root.showOsd(actionLabel, iconName, player) }) + } + } + + function flushPendingTrackOsd(force) { + var pending = pendingTrackOsd + if (!pending) return + + var player = playerForKey(pending.playerKey) || pending.player + if (force || MediaModel.trackChanged(pending.before, player) || pending.attempts >= 10) { + pendingTrackOsd = null + trackOsdTimer.stop() + root.showOsd(pending.actionLabel, pending.iconName, player) + return + } + + pending.attempts = pending.attempts + 1 + pendingTrackOsd = pending + trackOsdTimer.restart() + } + function selectPlayer(key) { var player = playerForKey(key) if (!player || !hasMetadata(player)) return false @@ -334,6 +372,7 @@ Item { var key = playerKey(player) var actionLabel = "Play/pause" var iconName = "media" + var beforeTrackSignature = trackSignature(player) var handled = false if (action === "next") { @@ -386,7 +425,8 @@ Item { } if (handled && key) preferredPlayerKey = key - if (showFeedback !== false) Qt.callLater(function() { root.showOsd(actionLabel, iconName, player) }) + if (showFeedback !== false) + scheduleOsd(actionLabel, iconName, player, handled && (action === "next" || action === "previous"), beforeTrackSignature) return handled } @@ -398,6 +438,13 @@ Item { onTriggered: root.syncPlayingOrder() } + Timer { + id: trackOsdTimer + interval: 120 + repeat: false + onTriggered: root.flushPendingTrackOsd(false) + } + PwObjectTracker { objects: root.playbackStreams } function statusJson() { diff --git a/test/shell.d/media-test.sh b/test/shell.d/media-test.sh index d7574e69..e5281306 100644 --- a/test/shell.d/media-test.sh +++ b/test/shell.d/media-test.sh @@ -33,6 +33,10 @@ assert(media.playerHasPlaybackStream( ), 'media matches players to playback streams') assertEqual(media.playerKey({ dbusName: 'org.mpris.MediaPlayer2.spotify' }), 'org.mpris.MediaPlayer2.spotify', 'media derives stable player keys') +const track = { trackTitle: 'Song', trackArtist: 'Artist', trackAlbum: 'Album', trackArtUrl: 'file:///cover.jpg' } +const trackSignature = media.trackSignature(track) +assert(!media.trackChanged(trackSignature, { ...track }), 'media detects unchanged track metadata') +assert(media.trackChanged(trackSignature, { ...track, trackTitle: 'Next song' }), 'media detects changed track metadata') assertEqual(media.labelFor({ trackTitle: 'Song', identity: 'Spotify' }), 'Song', 'media labels players by track first') assertEqual(media.osdMessage({ trackTitle: 'Song', trackArtist: 'Artist' }, 'Fallback'), 'Song - Artist', 'media builds OSD messages') assertEqual(media.osdMessage(null, 'Fallback'), 'Fallback', 'media falls back OSD messages')