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

refactor(mpris): consolidate equivalent-player resolution & update track matching

Port 1.5

(cherry picked from commit 19d919ed5c)
This commit is contained in:
purian23
2026-08-02 18:09:18 -04:00
committed by dms-ci[bot]
parent 6edb985847
commit 52afbc9801
3 changed files with 31 additions and 23 deletions
+4 -3
View File
@@ -156,9 +156,10 @@ DankOSD {
if (MprisController.isFirefoxYoutubeHoverPreview(player)) if (MprisController.isFirefoxYoutubeHoverPreview(player))
return; return;
const newTitle = player.trackTitle || ""; const metaPlayer = MprisController.bestMetadataPlayer(player);
const newArtist = player.trackArtist || ""; const newTitle = MprisController.displayTrackTitle(metaPlayer);
const newAlbum = player.trackAlbum || ""; const newArtist = metaPlayer.trackArtist || "";
const newAlbum = metaPlayer.trackAlbum || "";
const trackChanged = newTitle !== root._displayTitle || newArtist !== root._displayArtist || newAlbum !== root._displayAlbum; const trackChanged = newTitle !== root._displayTitle || newArtist !== root._displayArtist || newAlbum !== root._displayAlbum;
root._displayTitle = newTitle; root._displayTitle = newTitle;
+24 -13
View File
@@ -93,7 +93,7 @@ Singleton {
} }
if (isFirefoxYoutubeHoverPreview(p)) if (isFirefoxYoutubeHoverPreview(p))
return; return;
const metadataPlayer = _bestMetadataPlayer(p); const metadataPlayer = bestMetadataPlayer(p);
const nextTitle = displayTrackTitle(metadataPlayer); const nextTitle = displayTrackTitle(metadataPlayer);
const trackChanged = nextTitle && stableTitle && nextTitle.toLowerCase() !== stableTitle.toLowerCase(); const trackChanged = nextTitle && stableTitle && nextTitle.toLowerCase() !== stableTitle.toLowerCase();
if (trackChanged) { if (trackChanged) {
@@ -171,8 +171,7 @@ Singleton {
return player && player.playbackState === MprisPlaybackState.Stopped && !player.trackTitle && !player.trackArtist; return player && player.playbackState === MprisPlaybackState.Stopped && !player.trackTitle && !player.trackArtist;
} }
// App-name suffixes that browsers/integrations append as "<title> | <App>". // Known "<title> | <App>" suffixes stripped for matching only; display keeps the full title
// Only these are stripped for track matching; display always keeps the full title.
readonly property var _appTitleSuffixes: ["youtube", "youtube music", "soundcloud", "spotify", "chrome", "chromium", "firefox", "brave", "vivaldi", "twitch"] readonly property var _appTitleSuffixes: ["youtube", "youtube music", "soundcloud", "spotify", "chrome", "chromium", "firefox", "brave", "vivaldi", "twitch"]
function _stripAppTitleSuffix(title: string): string { function _stripAppTitleSuffix(title: string): string {
@@ -183,12 +182,10 @@ Singleton {
return _appTitleSuffixes.indexOf(suffix) !== -1 ? title.substring(0, idx).trim() : title; return _appTitleSuffixes.indexOf(suffix) !== -1 ? title.substring(0, idx).trim() : title;
} }
// Matching key: strip only known app suffixes so equivalent players line up.
function normalizedTrackTitle(player: MprisPlayer): string { function normalizedTrackTitle(player: MprisPlayer): string {
return _stripAppTitleSuffix((player?.trackTitle || "").trim()).toLowerCase(); return _stripAppTitleSuffix((player?.trackTitle || "").trim()).toLowerCase();
} }
// Display: never strip — a generic " | " cut would mangle legitimate titles.
function displayTrackTitle(player: MprisPlayer): string { function displayTrackTitle(player: MprisPlayer): string {
return (player?.trackTitle || "").trim(); return (player?.trackTitle || "").trim();
} }
@@ -197,14 +194,24 @@ Singleton {
return (player?.trackArtist || "").trim().toLowerCase(); return (player?.trackArtist || "").trim().toLowerCase();
} }
// Artist missing on either side: fall back to URL, then album, before trusting a title-only match
function isSameTrack(first: MprisPlayer, second: MprisPlayer): bool { function isSameTrack(first: MprisPlayer, second: MprisPlayer): bool {
const firstTitle = normalizedTrackTitle(first); const firstTitle = normalizedTrackTitle(first);
const secondTitle = normalizedTrackTitle(second); if (!firstTitle || firstTitle !== normalizedTrackTitle(second))
if (!firstTitle || firstTitle !== secondTitle)
return false; return false;
const firstArtist = normalizedTrackArtist(first); const firstArtist = normalizedTrackArtist(first);
const secondArtist = normalizedTrackArtist(second); const secondArtist = normalizedTrackArtist(second);
return !firstArtist || !secondArtist || firstArtist === secondArtist; if (firstArtist && secondArtist)
return firstArtist === secondArtist;
const firstUrl = (first?.metadata?.["xesam:url"] || "").toString();
const secondUrl = (second?.metadata?.["xesam:url"] || "").toString();
if (firstUrl && secondUrl)
return firstUrl === secondUrl;
const firstAlbum = (first?.trackAlbum || "").trim().toLowerCase();
const secondAlbum = (second?.trackAlbum || "").trim().toLowerCase();
if (firstAlbum && secondAlbum)
return firstAlbum === secondAlbum;
return true;
} }
function metadataQuality(player: MprisPlayer): int { function metadataQuality(player: MprisPlayer): int {
@@ -218,10 +225,16 @@ Singleton {
return quality; return quality;
} }
function _bestMetadataPlayer(player: MprisPlayer): MprisPlayer { function equivalentPlayers(player: MprisPlayer): var {
const equivalents = availablePlayers.filter(candidate => { if (!player)
return [];
return availablePlayers.filter(candidate => {
return candidate.playbackState !== MprisPlaybackState.Stopped && isSameTrack(player, candidate); return candidate.playbackState !== MprisPlaybackState.Stopped && isSameTrack(player, candidate);
}); });
}
function bestMetadataPlayer(player: MprisPlayer): MprisPlayer {
const equivalents = equivalentPlayers(player);
if (equivalents.length === 0) if (equivalents.length === 0)
return player; return player;
return equivalents.reduce((best, candidate) => { return equivalents.reduce((best, candidate) => {
@@ -238,9 +251,7 @@ Singleton {
if (activePlayer?.isPlaying) { if (activePlayer?.isPlaying) {
if (activePlayer.canControl || controllable.length === 0) if (activePlayer.canControl || controllable.length === 0)
return activePlayer; return activePlayer;
// Active source is playing but not controllable: only hand ownership to a // Playing but not controllable: only a same-track controllable peer may take over
// controllable *equivalent* peer (same track). Never let an unrelated
// player steal the active source while it is still playing.
const mirror = controllable.find(player => isSameTrack(activePlayer, player)); const mirror = controllable.find(player => isSameTrack(activePlayer, player));
return mirror || activePlayer; return mirror || activePlayer;
} }
+3 -7
View File
@@ -59,11 +59,8 @@ Singleton {
if (directUrl !== "") if (directUrl !== "")
return directUrl; return directUrl;
const equivalent = MprisController.availablePlayers.find(candidate => { const equivalent = MprisController.equivalentPlayers(player).find(candidate => {
return candidate !== player return candidate !== player && _directArtworkUrl(candidate) !== "";
&& candidate.playbackState !== MprisPlaybackState.Stopped
&& MprisController.isSameTrack(player, candidate)
&& _directArtworkUrl(candidate) !== "";
}); });
return _directArtworkUrl(equivalent); return _directArtworkUrl(equivalent);
} }
@@ -229,8 +226,7 @@ Singleton {
} }
_pendingArtKey = key; _pendingArtKey = key;
const url = getArtworkUrl(activePlayer); const url = getArtworkUrl(activePlayer);
// Ignore duplicate notifications, but allow a richer peer to replace // Ignore duplicate notifications, but let a richer peer replace same-track art
// the artwork for the same canonical track.
if (key !== "" && key === _committedArtKey && url === _committedSrcUrl) if (key !== "" && key === _committedArtKey && url === _committedSrcUrl)
return; return;
if (key !== "" && url !== "" && url === _committedSrcUrl) { if (key !== "" && url !== "" && url === _committedSrcUrl) {