diff options
Diffstat (limited to 'desktop/modules/sound/Player.qml')
| -rw-r--r-- | desktop/modules/sound/Player.qml | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/desktop/modules/sound/Player.qml b/desktop/modules/sound/Player.qml new file mode 100644 index 0000000..fab8083 --- /dev/null +++ b/desktop/modules/sound/Player.qml @@ -0,0 +1,56 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +pragma Singleton + +import Quickshell +import Quickshell.Services.Mpris +import QtQuick + +// Which MPRIS player the OSD should describe. +Singleton { + id: root + + // Two things proxy a real player and republish it under their own name, so + // one track can appear on three buses at once: a playing Navidrome tab + // showed up as firefox.instance_*, as plasma-browser-integration and as + // playerctld. Both proxies are skipped and the OSD talks to the browser's + // own entry, which is safe because none of the three exists until playback + // starts, and the browser's entry appeared whenever the plasma one did. An + // open but silent tab publishes nothing, so there is no row to lose. The + // plasma name is browser-only: Feishin on the same server publishes just + // its own entry and playerctld, so a native player loses nothing here. + readonly property var proxyNames: [".playerctld", ".plasma-browser-integration"] + + readonly property var real: + Mpris.players.values.filter(p => !root.proxyNames.some(n => p.dbusName.endsWith(n))) + + // Prefer something actually playing; otherwise keep the last one seen, so + // pausing does not make the track row vanish mid-look. + readonly property var current: + real.find(p => p.playbackState === MprisPlaybackState.Playing) + ?? real.find(p => p.playbackState === MprisPlaybackState.Paused) + ?? real[0] + ?? null + + readonly property bool active: current !== null + readonly property bool playing: current?.playbackState === MprisPlaybackState.Playing + + readonly property string title: current?.trackTitle ?? "" + readonly property string artist: current?.trackArtist ?? "" + + // Players that extract embedded art write it to a temp file they reuse + // per track, so the path can repeat while the image behind it changes. + // The cache buster makes Image reload instead of showing the last cover. + readonly property string artUrl: + (current?.trackArtUrl ?? "") === "" ? "" + : current.trackArtUrl + "#" + encodeURIComponent(title) +} |
