import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Services.Mpris import "../../config.js" as Config Rectangle { id: musicBlock property var player: null property var supportedPlayers: ["feishin", "fooyin"] implicitWidth: visible ? Math.min(textMetrics.width + Config.blockPadding + 8, 400) : 0 implicitHeight: Config.blockHeight radius: Config.blockRadius visible: player !== null && player.isPlaying function isSupported(p) { var identity = (p.identity || "").toLowerCase() var desktopEntry = (p.desktopEntry || "").toLowerCase() for (var i = 0; i < supportedPlayers.length; i++) { var name = supportedPlayers[i] if ( identity === name || desktopEntry === name || desktopEntry.endsWith("." + name) ) return true } return false } function findPlayer() { var fallback = null for (var i = 0; i < Mpris.players.values.length; i++) { var p = Mpris.players.values[i] console.log( "MPRIS:", "identity =", p.identity, "desktopEntry =", p.desktopEntry ) if (!isSupported(p)) continue if (p.isPlaying) { player = p return } if (!fallback) fallback = p } player = fallback } function cleanTitle(title) { if (!title) return "" return title .replace(/\s*[\(\[][^)\]]*\blive\b[^)\]]*[\)\]]/gi, "") .replace(/\s*[-–—]\s*\blive\b.*$/i, "") .trim() } Component.onCompleted: { findPlayer() } Connections { target: Mpris.players function onValuesChanged() { findPlayer() } } gradient: Gradient { GradientStop { id: stop1 position: 0.0 color: Config.accentGreen } GradientStop { id: stop2 position: 0.33 color: Config.accentYellow } GradientStop { id: stop3 position: 0.66 color: Config.accentOrange } GradientStop { id: stop4 position: 1.0 color: Config.accentRed } } TextMetrics { id: textMetrics font.family: Config.fontFamily font.pixelSize: Config.fontSize font.bold: true text: player ? "󰎇 " + player.trackArtist + " — " + cleanTitle(player.trackTitle) : "" } Text { id: musicText anchors { left: parent.left right: parent.right verticalCenter: parent.verticalCenter } leftPadding: Config.blockPadding / 2 rightPadding: Config.blockPadding / 2 text: player ? "󰎇 " + player.trackArtist + " — " + cleanTitle(player.trackTitle) : "" color: Config.textDark font.family: Config.fontFamily font.pixelSize: Config.fontSize font.bold: true elide: Text.ElideRight maximumLineCount: 1 } MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { if (player && player.canRaise) player.raise() } } SequentialAnimation { running: true loops: Animation.Infinite ParallelAnimation { ColorAnimation { target: stop1 property: "color" from: Config.accentGreen to: Config.accentRed duration: Config.gradientAnimationDuration } ColorAnimation { target: stop2 property: "color" from: Config.accentYellow to: Config.accentGreen duration: Config.gradientAnimationDuration } ColorAnimation { target: stop3 property: "color" from: Config.accentOrange to: Config.accentYellow duration: Config.gradientAnimationDuration } ColorAnimation { target: stop4 property: "color" from: Config.accentRed to: Config.accentOrange duration: Config.gradientAnimationDuration } } ParallelAnimation { ColorAnimation { target: stop1 property: "color" from: Config.accentRed to: Config.accentGreen duration: Config.gradientAnimationDuration } ColorAnimation { target: stop2 property: "color" from: Config.accentGreen to: Config.accentYellow duration: Config.gradientAnimationDuration } ColorAnimation { target: stop3 property: "color" from: Config.accentYellow to: Config.accentOrange duration: Config.gradientAnimationDuration } ColorAnimation { target: stop4 property: "color" from: Config.accentOrange to: Config.accentRed duration: Config.gradientAnimationDuration } } } }