import QtQuick import Quickshell import Quickshell.Io import Quickshell.Services.Mpris Item { id: root property var shell: null property string preferredPlayerKey: "" readonly property var players: Mpris.players ? Mpris.players.values : [] readonly property var activePlayer: selectActivePlayer() readonly property bool hasMedia: activePlayer !== null && (activePlayer.trackTitle || activePlayer.trackArtist) readonly property string title: activePlayer ? (activePlayer.trackTitle || "") : "" readonly property string artist: activePlayer ? (activePlayer.trackArtist || "") : "" readonly property string album: activePlayer && activePlayer.trackAlbum ? activePlayer.trackAlbum : "" readonly property string artUrl: activePlayer && activePlayer.trackArtUrl ? activePlayer.trackArtUrl : "" readonly property string identity: activePlayer ? (activePlayer.identity || activePlayer.desktopEntry || "") : "" function isProxyPlayer(player) { var dbusName = String(player && player.dbusName || "").toLowerCase() var desktopEntry = String(player && player.desktopEntry || "").toLowerCase() return dbusName.indexOf("playerctld") !== -1 || desktopEntry === "playerctld" } function hasMetadata(player) { return !!(player && (player.trackTitle || player.trackArtist || player.identity || player.desktopEntry)) } function hasTrackMetadata(player) { return !!(player && (player.trackTitle || player.trackArtist || player.trackAlbum || player.trackArtUrl)) } function playerKey(player) { if (!player) return "" return String(player.dbusName || player.desktopEntry || player.identity || "") } function selectActivePlayer() { var playingProxy = null var preferred = null var trackPlayer = null var trackProxy = null var identityPlayer = null var identityProxy = null for (var i = 0; i < players.length; i++) { var p = players[i] if (!p) continue var proxy = isProxyPlayer(p) if (p.isPlaying) { if (!proxy) return p if (!playingProxy) playingProxy = p } if (preferredPlayerKey && playerKey(p) === preferredPlayerKey && hasMetadata(p)) preferred = p if (hasTrackMetadata(p)) { if (!proxy && !trackPlayer) trackPlayer = p else if (proxy && !trackProxy) trackProxy = p } else if (hasMetadata(p)) { if (!proxy && !identityPlayer) identityPlayer = p else if (proxy && !identityProxy) identityProxy = p } } return playingProxy || preferred || trackPlayer || trackProxy || identityPlayer || identityProxy || null } function labelFor(player) { if (!player) return "" return player.trackTitle || player.identity || player.desktopEntry || "" } function osdMessage(player, fallback) { if (!player) return fallback var label = labelFor(player) if (label && player.trackArtist) return label + " - " + player.trackArtist return label || fallback } function showOsd(actionLabel, iconName) { if (!shell) return shell.summon("omarchy.osd", JSON.stringify({ icon: iconName || "media", message: osdMessage(activePlayer, actionLabel) })) } function runAction(action, showFeedback) { var player = activePlayer var key = playerKey(player) var actionLabel = "Play/pause" var iconName = "media" var handled = false if (action === "next") { actionLabel = "Next" iconName = "media-next" if (player && player.canGoNext) { player.next() handled = true } } else if (action === "previous") { actionLabel = "Previous" iconName = "media-previous" if (player && player.canGoPrevious) { player.previous() handled = true } } else if (action === "play") { actionLabel = "Play" iconName = "media-play" if (player && player.canPlay) { player.play() handled = true } else if (player && player.canTogglePlaying && !player.isPlaying) { player.togglePlaying() handled = true } } else if (action === "pause") { actionLabel = "Pause" iconName = "media-pause" if (player && player.canPause) { player.pause() handled = true } else if (player && player.canTogglePlaying && player.isPlaying) { player.togglePlaying() handled = true } } else if (action === "playPause") { actionLabel = player && player.isPlaying ? "Pause" : "Play" iconName = player && player.isPlaying ? "media-pause" : "media-play" if (player && player.isPlaying && player.canPause) { player.pause() handled = true } else if (player && !player.isPlaying && player.canPlay) { player.play() handled = true } else if (player && player.canTogglePlaying) { player.togglePlaying() handled = true } } if (handled && key) preferredPlayerKey = key if (showFeedback !== false) Qt.callLater(function() { root.showOsd(actionLabel, iconName) }) return handled } function statusJson() { var p = activePlayer return JSON.stringify({ hasPlayer: p !== null, hasMedia: root.hasMedia, playing: p ? !!p.isPlaying : false, identity: p ? (p.identity || "") : "", desktopEntry: p ? (p.desktopEntry || "") : "", title: p ? (p.trackTitle || "") : "", artist: p ? (p.trackArtist || "") : "", album: p && p.trackAlbum ? p.trackAlbum : "", artUrl: p && p.trackArtUrl ? p.trackArtUrl : "", canGoNext: p ? !!p.canGoNext : false, canGoPrevious: p ? !!p.canGoPrevious : false, canTogglePlaying: p ? !!p.canTogglePlaying : false }) } IpcHandler { target: "media" function status(): string { return root.statusJson() } function playPause(): string { return root.runAction("playPause", true) ? "ok" : "unhandled" } function next(): string { return root.runAction("next", true) ? "ok" : "unhandled" } function previous(): string { return root.runAction("previous", true) ? "ok" : "unhandled" } function play(): string { return root.runAction("play", true) ? "ok" : "unhandled" } function pause(): string { return root.runAction("pause", true) ? "ok" : "unhandled" } function ping(): string { return "ok" } } }