From 937b1c218c70a91462c5827e65a698a11076bdb8 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Thu, 5 Feb 2026 12:01:06 -0800 Subject: [PATCH] waybar: add nowplaying module - Add a playerctl-backed now playing widget - Track a default disk list file instead of a home-manager symlink --- dotfiles/config/waybar/config.jsonc | 19 +++- dotfiles/config/waybar/disks | 2 + dotfiles/config/waybar/scripts/nowplaying | 113 ++++++++++++++++++++++ 3 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 dotfiles/config/waybar/disks create mode 100755 dotfiles/config/waybar/scripts/nowplaying diff --git a/dotfiles/config/waybar/config.jsonc b/dotfiles/config/waybar/config.jsonc index 528deaa2..1e1c15f3 100644 --- a/dotfiles/config/waybar/config.jsonc +++ b/dotfiles/config/waybar/config.jsonc @@ -1,4 +1,5 @@ // -*- mode: jsonc -*- +// Icon sizes are synced at runtime from vars.env via scripts/render-config. { "height": 42, "spacing": 4, @@ -7,7 +8,8 @@ "hyprland/submap" ], "modules-center": [ - "hyprland/window" + "hyprland/window", + "custom/nowplaying" ], "modules-right": [ "idle_inhibitor", @@ -31,10 +33,12 @@ } }, "tray": { - "spacing": 10 + "spacing": 10, + "icon-size": 24, + "watcher": "internal" }, "clock": { - "format": "{:%H:%M}", + "format": "{:%I:%M %p}", "tooltip-format": "{:%Y %B}\n{calendar}", "format-alt": "{:%Y-%m-%d}" }, @@ -51,6 +55,15 @@ "format": "DISK {text}", "tooltip": false }, + "custom/nowplaying": { + "exec": "~/.config/waybar/scripts/nowplaying", + "exec-if": "command -v playerctl", + "return-type": "json", + "format": "NP {text}", + "interval": 2, + "max-length": 50, + "tooltip": true + }, "temperature": { "critical-threshold": 80, "format": "TEMP {temperatureC}C" diff --git a/dotfiles/config/waybar/disks b/dotfiles/config/waybar/disks new file mode 100644 index 00000000..2ec9fa04 --- /dev/null +++ b/dotfiles/config/waybar/disks @@ -0,0 +1,2 @@ +# One mountpoint per line (comments with # are ignored). +/ diff --git a/dotfiles/config/waybar/scripts/nowplaying b/dotfiles/config/waybar/scripts/nowplaying new file mode 100755 index 00000000..2088bc9c --- /dev/null +++ b/dotfiles/config/waybar/scripts/nowplaying @@ -0,0 +1,113 @@ +#!/usr/bin/env bash +set -euo pipefail + +if ! command -v playerctl >/dev/null 2>&1; then + exit 0 +fi + +preferred_players="${WAYBAR_NOWPLAYING_PLAYERS:-spotify,spotifyd,playerctld,vlc,mpv,firefox,chromium}" +IFS=',' read -r -a preferred_list <<< "$preferred_players" + +mapfile -t players < <(playerctl -l 2>/dev/null || true) +if [[ ${#players[@]} -eq 0 ]]; then + exit 0 +fi + +has_player() { + local target="$1" + for player in "${players[@]}"; do + if [[ "$player" == "$target" ]]; then + return 0 + fi + done + return 1 +} + +selected="" +selected_status="" + +for player in "${preferred_list[@]}"; do + player="${player//[[:space:]]/}" + [[ -z "$player" ]] && continue + if has_player "$player"; then + status=$(playerctl --player "$player" status 2>/dev/null || true) + if [[ "$status" == "Playing" ]]; then + selected="$player" + selected_status="$status" + break + fi + if [[ "$status" == "Paused" && -z "$selected" ]]; then + selected="$player" + selected_status="$status" + fi + fi +done + +if [[ -z "$selected" ]]; then + for player in "${players[@]}"; do + status=$(playerctl --player "$player" status 2>/dev/null || true) + if [[ "$status" == "Playing" ]]; then + selected="$player" + selected_status="$status" + break + fi + if [[ "$status" == "Paused" && -z "$selected" ]]; then + selected="$player" + selected_status="$status" + fi + done +fi + +if [[ -z "$selected" ]]; then + exit 0 +fi + +if [[ -z "$selected_status" ]]; then + selected_status=$(playerctl --player "$selected" status 2>/dev/null || true) +fi + +if [[ "$selected_status" != "Playing" && "$selected_status" != "Paused" ]]; then + exit 0 +fi + +artist=$(playerctl --player "$selected" metadata artist 2>/dev/null || true) +title=$(playerctl --player "$selected" metadata title 2>/dev/null || true) +artist="${artist//$'\n'/ }" +artist="${artist//$'\r'/ }" +title="${title//$'\n'/ }" +title="${title//$'\r'/ }" + +if [[ -z "$artist" && -z "$title" ]]; then + exit 0 +fi + +text="$title" +if [[ -n "$artist" && -n "$title" ]]; then + text="$artist - $title" +elif [[ -n "$artist" ]]; then + text="$artist" +fi + +if [[ "${WAYBAR_NOWPLAYING_SHOW_PLAYER:-0}" == "1" ]]; then + text="${selected}: ${text}" +fi + +json_escape() { + local input="$1" + input=${input//\\/\\\\} + input=${input//\"/\\\"} + input=${input//$'\n'/\\n} + printf '%s' "$input" +} + +class="paused" +if [[ "$selected_status" == "Playing" ]]; then + class="playing" +fi + +tooltip="${selected}\n${selected_status}\n${text}" + +printf '{"text":"%s","class":"%s","tooltip":"%s"}\n' \ + "$(json_escape "$text")" \ + "$(json_escape "$class")" \ + "$(json_escape "$tooltip")"