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 playerCanControl(player) { return !!(player && (player.canTogglePlaying || player.canPlay || player.canPause || player.canGoNext || player.canGoPrevious)) } function canHandleAction(player, action) { if (!player) return false if (action === "next") return !!player.canGoNext if (action === "previous") return !!player.canGoPrevious if (action === "play") return !!(player.canPlay || player.canTogglePlaying) if (action === "pause") return !!(player.canPause || player.canTogglePlaying) if (action === "playPause") return !!(player.canTogglePlaying || player.canPlay || player.canPause) return false } function canCycleSource(player) { return !!(player && hasMetadata(player) && (player.isPlaying || player.canPlay)) } function nodeProps(node) { return node && node.ready && node.properties ? node.properties : {} } function isPlaybackStream(node) { if (!node || !node.isStream) return false if (node.isSink === true) return true var mediaClass = String(node.type || "") return mediaClass.indexOf("Stream/Output/Audio") !== -1 || mediaClass.indexOf("AudioOutStream") !== -1 || mediaClass.indexOf("Output") !== -1 } function streamLabelKey(label) { var key = String(label || "").toLowerCase() key = key.replace(/^pipewire alsa \[/, "") key = key.replace(/\]$/, "") key = key.replace(/^alsa playback \[/, "") key = key.replace(/[^a-z0-9]+/g, "") return key } function rawStreamLabel(node) { if (!node) return "" var p = nodeProps(node) return p["application.name"] || node.description || p["media.name"] || p["node.name"] || node.name } function playerAppLabel(player) { if (!player) return "" var dbus = String(player.dbusName || "") dbus = dbus.replace(/^org\.mpris\.MediaPlayer2\./, "") dbus = dbus.replace(/\.instance[0-9]+$/, "") return player.desktopEntry || player.identity || dbus } function playerHasPlaybackStream(player, playbackStreams) { var playerKey = streamLabelKey(playerAppLabel(player)) if (!playerKey) return false var streams = Array.isArray(playbackStreams) ? playbackStreams : [] for (var i = 0; i < streams.length; i++) { var streamKey = streamLabelKey(rawStreamLabel(streams[i])) if (!streamKey) continue if (streamKey === playerKey || streamKey.indexOf(playerKey) !== -1 || playerKey.indexOf(streamKey) !== -1) return true } return false } function playerKey(player) { if (!player) return "" return String(player.dbusName || player.desktopEntry || player.identity || "") } 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 } if (typeof module !== "undefined") { module.exports = { isProxyPlayer: isProxyPlayer, hasMetadata: hasMetadata, hasTrackMetadata: hasTrackMetadata, playerCanControl: playerCanControl, canHandleAction: canHandleAction, canCycleSource: canCycleSource, nodeProps: nodeProps, isPlaybackStream: isPlaybackStream, streamLabelKey: streamLabelKey, rawStreamLabel: rawStreamLabel, playerAppLabel: playerAppLabel, playerHasPlaybackStream: playerHasPlaybackStream, playerKey: playerKey, labelFor: labelFor, osdMessage: osdMessage } }