mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-08 14:38:30 -04:00
refactor(mpris): consolidate equivalent-player resolution & update track matching
Port 1.5
(cherry picked from commit 19d919ed5c)
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user