diff --git a/catppuccin-macchiato/colors.css b/catppuccin-macchiato/colors.css new file mode 100644 index 0000000..b1934e7 --- /dev/null +++ b/catppuccin-macchiato/colors.css @@ -0,0 +1,26 @@ +@define-color rosewater #F4DBD6; +@define-color flamingo #F0C6C6; +@define-color pink #F5BDE6; +@define-color mauve #C6A0F6; +@define-color red #ED8796; +@define-color maroon #EE99A0; +@define-color peach #F5A97F; +@define-color yellow #EED49F; +@define-color green #A6DA95; +@define-color teal #8BD5CA; +@define-color sky #91D7E3; +@define-color sapphire #7DC4E4; +@define-color blue #8AADF4; +@define-color lavender #B7BDF8; +@define-color text #CAD3F5; +@define-color subtext1 #A5ADCB; +@define-color subtext0 #A5ADCB; +@define-color overlay2 #939AB7; +@define-color overlay1 #8087A2; +@define-color overlay0 #6E738D; +@define-color surface2 #5B6078; +@define-color surface1 #494D64; +@define-color surface0 #363A4F; +@define-color base #24273A; +@define-color mantle #1E2030; +@define-color crust #181926; diff --git a/catppuccin-macchiato/config.jsonc b/catppuccin-macchiato/config.jsonc new file mode 100644 index 0000000..1c8cfcb --- /dev/null +++ b/catppuccin-macchiato/config.jsonc @@ -0,0 +1,270 @@ +{ + "layer": "top", + "position": "top", + "height": 40, + "width": 2550, + "spacing": 1, + "reload_style_on_change": true, + "margin": "10 5 0 5", + "modules-left": [ + "custom/launcher", + "hyprland/workspaces", + "tray", + "hyprland/mode", + "custom/mpv-scroll", + "custom/firefox", + ], + "modules-center": ["hyprland/window"], + "modules-right": [ + "hyprland/scratchpad", + "idle_inhibitor", + "custom/updates", + "custom/kernel", + // "disk#ssd", + // "temperature", + "cpu", + "memory", + "bluetooth", + "network", + "pulseaudio", + "clock", + "custom/weather", + ], + + "hyprland/workspaces": { + "disable-scroll": false, + "all-outputs": true, + "warp-on-scroll": false, + "format": "{icon}", + "format-icons": { + // "1": "", + // "2": "", + // "3": "", + // "4": "", + // "5": "", + "urgent": "", + // "focused": "", + // "default": "" + }, + }, + "bluetooth": { + "format-on": "bt ", + "format-off": "bt ({status}) ", + "format-connected": "{device_alias} ", + "format-connected-battery": "{device_alias} [{device_battery_percentage}%] ", + "format-device-preference": ["ugreen_1", "ugreen_2"], + "tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected", + "tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}", + "tooltip-format-enumerate-connected": "{device_alias}\t{device_address}", + "tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%", + "on-click": "blueman-manager", + }, + "hyprland/mode": { + "format": "{}", + }, + "hyprland/scratchpad": { + "format": "{icon} {count}", + "show-empty": false, + "format-icons": ["", ""], + "tooltip": true, + "tooltip-format": "{app}: {title}", + }, + "idle_inhibitor": { + "format": "{icon}", + "format-icons": { + "activated": "", + "deactivated": "", + }, + }, + "tray": { + "icon-size": 14, + "spacing": 10, + }, + "clock": { + "interval": 60, + "timezone": "America/Los_Angeles", + "format": "{:%F %R }", + "tooltip-format": "{calendar}", + "calendar": { + "mode": "year", + "mode-mon-col": 3, + "weeks-pos": "right", + "on-scroll": 1, + "on-click-right": "mode", + "format": { + "months": "{}", + "days": "{}", + "weeks": "W{}", + "weekdays": "{}", + "today": "{}", + }, + }, + "actions": { + "on-click-right": "mode", + "on-click-forward": "tz_up", + "on-click-backward": "tz_down", + "on-scroll-up": "shift_up", + "on-scroll-down": "shift_down", + }, + }, + "cpu": { + "interval": 3, + "format": "{usage}% ", + "on-click": "ghostty --initial-command=btop", + }, + "memory": { + "interval": 3, + "format": "{}% ", + "on-click": "ghostty --initial-command=btop", + "tooltip-format": "Used: {used:0.1f}G/{total:0.1f}G. Swap: {swapUsed:0.1f}G/{swapTotal:0.1f}G", + "states": { + "critical": 80, + }, + }, + "temperature": { + "interval": 3, + "hwmon-path": "/sys/class/hwmon/hwmon1/temp1_input", + "critical-threshold": 90, + "format-critical": "{temperatureC}°C {icon}", + "format": "{temperatureC}°C {icon}", + "format-icons": ["", "", ""], + }, + "disk#ssd": { + "interval": 60, + "format": "{path} {free} ", + "path": "/", + "tooltip": true, + "warning": 80, + "critical": 90, + }, + "network": { + "interval": 60, + "interface-ethernet": "enp1s*", + "interface-wifi": "wlan0", + "format-ethernet": "eth ", + "format-wifi": "{essid} ({signalStrength}%) ", + "tooltip-format-ethernet": "{ifname}: {ipaddr}/{cidr} ", + "tooltip-format-wifi": "{ifname}: {ipaddr}/{cidr} ", + "format-linked": "(No IP) ", + "format-disconnected": "Disconnected ⚠", + }, + "custom/weather": { + "interval": 600, + "exec": "~/.config/waybar/scripts/wttr.sh Los_Angeles", + "return-type": "json", + "format": "{}", + "tooltip": true, + }, + "custom/kernel": { + "exec": "uname -r | sed -E 's/^([0-9]+\\.[0-9]+\\.[0-9]+)-.*-([a-zA-Z0-9]+)/\\1-\\2/'", + "format": "{} ", + }, + "pulseaudio": { + "scroll-step": 2, + "format": "{volume}% {icon} {format_source}", + "format-bluetooth": "{volume}% {icon} {format_source}", + "format-bluetooth-muted": " {icon} {format_source}", + "format-muted": " {format_source}", + "format-source": "{volume}% ", + "format-source-muted": "", + "format-icons": { + "headphone": " ", + "hands-free": "", + "headset": "", + "phone": "", + "portable": "", + "car": "", + }, + "on-click": "pavucontrol", + }, + "custom/pipewire": { + "tooltip": false, + "max-length": 6, + "exec": "$HOME/.config/waybar/scripts/pipewire.sh", + "on-click-right": "qpwgraph", + "scroll-step": 2, + "format": "{volume}% {icon} {format_source}", + "format-bluetooth": "{volume}% {icon} {format_source}", + "format-bluetooth-muted": " {icon} {format_source}", + "format-muted": " {format_source}", + "format-source": "{volume}% ", + "format-source-muted": "", + "format-icons": { + "headphone": " ", + "hands-free": "", + "headset": "", + "phone": "", + "portable": "", + "car": "", + }, + "on-click": "pavucontrol", + }, + "hyprland/workspaces": { + "format": "{icon}", + "format-icons": { + "1": "一", + "2": "二", + "3": "三", + "4": "四", + "5": "五", + "6": "六", + "7": "七", + "8": "八", + "9": "九", + "10": "十", + // "11": "一", + // "12": "二", + // "13": "三", + // "14": "四", + // "15": "五", + // "16": "六", + // "17": "七", + // "18": "八", + // "19": "九", + // "20": "十", + }, + "persistent-workspaces": { + // "*": 10, + "*": 5, + }, + "sort-by": "number", + "all-outputs": false, + }, + "hyprland/window": { + "max-length": 88, + }, + "custom/updates": { + "format": "{} {icon}", + "return-type": "json", + "format-icons": { + "has-updates": "󱍷", + "updated": "", + }, + "exec-if": "which waybar-module-pacman-updates", + "exec": "waybar-module-pacman-updates --no-zero-output --tooltip-align-columns", + }, + "custom/launcher": { + "format": "{icon}", + "format-icons": [" "], + "on-click": "rofi -show drun", + "tooltip": false, + }, + "custom/firefox": { + "exec": "$HOME/.config/waybar/scripts/scroll-firefox.sh", + "format": "{}", + "max-length": 35, + "on-click": "playerctl -p firefox play-pause", + "hide-empty-text": true, + }, + "custom/mpv-scroll": { + "escape": "true", + "exec": "$HOME/.config/waybar/scripts/scroll-mpd.sh", + "format": "{}", + "max-length": 35, + "on-click": "mpc toggle", + "on-click-right": "ghostty --initial-command=ncmpcpp", + "on-scroll-down": "mpc volume -5", + "on-scroll-up": "mpc volume +5", + "hide-empty-text": true, + }, +} diff --git a/catppuccin-macchiato/style.css b/catppuccin-macchiato/style.css new file mode 100644 index 0000000..550bc2f --- /dev/null +++ b/catppuccin-macchiato/style.css @@ -0,0 +1,267 @@ +@import url("./colors.css"); + +* { + border: none; + font-family: + JetBrainsMono Nerd Font, + Font Awesome; + font-size: 13px; + min-height: 0; + border-radius: 0.69em; +} + +window#waybar { + background-color: @mantle; + color: @text; + /* border-radius: 0.69em; */ +} + +window#waybar.hidden { + opacity: 0.2; +} + +tooltip { + background-color: @base; + border: 1px solid @subtext0; +} + +tooltip label { + color: @text; +} + +button { + box-shadow: inset 0 -3px transparent; + border: none; +} + +button:hover { + background: inherit; + box-shadow: inset 0 -3px @text; +} + +#workspaces { + background-color: transparent; + margin: 0 5px 0 0; +} + +#workspaces button { + border-radius: 0; + margin: 5px 0; + padding: 0.24em; + color: @mauve; +} + +#workspaces button:hover { + text-shadow: inherit; + background-image: linear-gradient(0deg, @surface1, @mantle); + margin: 5px 0; + background-color: @overlay3; + box-shadow: inset 0 -3px @green; +} + +#workspaces button.focused { + background-image: linear-gradient(0deg, @mauve, @surface1); + /* box-shadow: inset 0 -3px @text; */ + margin: 5px 0; + /* box-shadow: inset 0 -3px @mauve; */ + background-color: transparent; + box-shadow: inset 0 -3px @mauve; +} + +#workspaces button.empty { + color: @subtext0; +} + +#workspaces button.active { + color: @green; +} + +#workspaces button.urgent { + background-image: linear-gradient(0deg, @red, @mantle); + margin: 5px 0; +} + +#taskbar button.active { + background-image: linear-gradient(0deg, @surface1, @mantle); +} + +#mode { + background-color: @base; + box-shadow: inset 0 -2px @text; +} + +#custom-weather, +#clock, +#language, +#pulseaudio, +#bluetooth, +#network, +#memory, +#cpu, +#temperature, +#disk, +#custom-kernel, +#idle_inhibitor, +#scratchpad, +#mode, +#tray { + padding: 0 10px; + margin: 5px 1px; + color: @text; +} + +.modules-left > widget:first-child > #workspaces { + margin-left: 0; +} + +.modules-right > widget:last-child > #workspaces { + margin-right: 0; +} + +#custom-weather { + background-color: @teal; + color: @mantle; + margin-right: 5px; +} + +#custom-kernel { + background-color: @rosewater; + color: @mantle; +} + +#clock { + background-color: @green; + color: @mantle; +} + +@keyframes blink { + to { + background-color: @mantle; + color: @text; + } +} + +label:focus { + background-color: @mantle; +} + +#cpu { + background-color: @mauve; + color: @mantle; + min-width: 45px; +} + +#memory { + background-color: @red; + color: @mantle; +} + +#disk { + background-color: @flamingo; + color: @mantle; +} + +#network { + background-color: @peach; + color: @mantle; +} + +#network.disconnected { + background-color: red; + color: @mantle; +} + +#bluetooth { + background-color: @maroon; + color: @mantle; + min-width: 40px; +} + +#pulseaudio { + background-color: @yellow; + color: @mantle; +} + +#pulseaudio.muted { + background-color: red; + color: @mantle; +} + +#temperature { + background-color: @pink; + color: @mantle; + min-width: 37px; +} + +#temperature.critical { + background-color: red; + color: @mantle; + min-width: 37px; +} + +#tray { + /* background-color: @overlay0; */ + background-color: @surface1; + color: @text; +} + +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: @mantle; +} + +#idle_inhibitor { + background-color: @base; + color: @text; + font-family: Inter; +} + +#idle_inhibitor.activated { + background-color: @text; + color: @base; +} + +#scratchpad { + background-color: @base; + color: @text; +} + +#scratchpad.empty { + background-color: transparent; +} + +#custom-launcher { + margin: 0 4px; + padding: 0 10px; + color: #1793d1; +} + +#custom-mpv-scroll { + color: @base; + background-color: @blue; + padding: 0 10px; + margin: 5px 1px; +} +#custom-updates { + background-color: @yellow; + color: @base; + padding: 0 10px; + margin: 5px 1px; +} + +#custom-launcher { + color: #1793d1; + background-color: inherit; + margin: 0 0 0 0.24em; +} + +#custom-firefox { + color: @base; + background-color: @pink; + padding: 0 10px; + margin: 5px 1px; +} diff --git a/config.jsonc b/default/config.jsonc similarity index 83% rename from config.jsonc rename to default/config.jsonc index f478363..9f9fe59 100644 --- a/config.jsonc +++ b/default/config.jsonc @@ -3,7 +3,7 @@ "layer": "top", // Waybar at top layer // "position": "bottom", // Waybar position (top|bottom|left|right) "height": 30, // Waybar height (to be removed for auto height) - // "width": 1280, // Waybar width + // "width": 2560, // Waybar width "spacing": 4, // Gaps between modules (4px) // Choose the order of the modules "reload_style_on_change": true, @@ -12,21 +12,22 @@ "hyprland/workspaces", "hyprland/mode", "hyprland/scratchpad", - "custom/media", ], - "modules-center": ["hyprland/window"], + "modules-center": ["custom/media", "hyprland/window"], "modules-right": [ - "mpd", + // "mpd", // "idle_inhibitor", - "pulseaudio", + // "pulseaudio", + "custom/pipewire", "network", // "power-profiles-daemon", // "cpu", // "memory", - // "temperature", + "temperature", "backlight", // "keyboard-state", "hyprland/language", + "custom/pacman", "battery", "battery#bat2", "clock", @@ -69,12 +70,22 @@ "8": "八", "9": "九", "10": "十", + "11": "一", + "12": "二", + "13": "三", + "14": "四", + "15": "五", + "16": "六", + "17": "七", + "18": "八", + "19": "九", + "20": "十", // "active": "", // "default": "", // "empty": "", }, "persistent-workspaces": { - "*": 5, + "*": 10, }, "sort-by": "number", // "format-window-separator": "\n", @@ -162,7 +173,7 @@ // "thermal-zone": 2, // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input", "critical-threshold": 80, - // "format-critical": "{temperatureC}°C {icon}", + "format-critical": "{temperatureC}°C {icon}", "format": "{temperatureC}°C {icon}", "format-icons": ["", "", ""], }, @@ -229,16 +240,21 @@ "on-click": "pavucontrol", }, "custom/media": { - "format": "{icon} {}", - "return-type": "json", - "max-length": 40, - "format-icons": { - "spotify": "", - "default": "🎜", - }, - "escape": true, - "exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null", // Script in resources folder - // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name + "escape": "true", + "exec": "$HOME/.config/waybar/scripts/current-song.sh", + "format": "{}", + "max-length": 100, + "on-click": "mpc toggle", + "on-click-right": "ghostty --initial-command=ncmpcpp", + "on-scroll-down": "mpc volume -5", + "on-scroll-up": "mpc volume +5", + }, + "custom/pipewire": { + "tooltip": false, + "max-length": 6, + "exec": "$HOME/.config/waybar/scripts/pipewire.sh", + "on-click": "pavucontrol", + "on-click-right": "qpwgraph", }, "custom/power": { "format": "⏻ ", @@ -252,4 +268,12 @@ "hibernate": "systemctl hibernate", }, }, + "custom/pacman": { + "format": "{} ", + "interval": 3600, // every hour + "exec": "checkupdates | wc -l", // # of updates + "exec-if": "exit 0", // always run; consider advanced run conditions + "on-click": "termite -e 'sudo pacman -Syu'; pkill -SIGRTMIN+8 waybar", // update system + "signal": 8, + }, } diff --git a/style.css b/default/style.css similarity index 94% rename from style.css rename to default/style.css index be2f102..98b4bbf 100644 --- a/style.css +++ b/default/style.css @@ -1,13 +1,13 @@ * { /* `otf-font-awesome` is required to be installed for icons */ font-family: - JetBrainsMonoNerdFont, FontAwesome, Roboto, Helvetica, Arial, sans-serif; - font-size: 13px; + "JetBrainsMono Nerd Font", "FiraCode Nerd Font", "Noto Sans CJK JP", + FontAwesome, Roboto, Helvetica, Arial, sans-serif; + font-size: 14px; } window#waybar { background-color: rgba(54, 58, 79, 0.5); - /* border-bottom: 3px solid rgba(100, 114, 125, 0.5); */ color: #cad3f5; transition-property: background-color; transition-duration: 0.5s; @@ -54,11 +54,9 @@ button:hover { background-color: #a37800; } -/* #workspaces { */ -/* background-color: rgba(54, 58, 79, 0.5); */ -/* border-radius: 0.5em; */ -/* padding: 0 0.5em; */ -/* } */ +#workspaces { + font-size: 24px; +} #workspaces button { padding: 1px 0.5em; @@ -131,6 +129,8 @@ button:hover { } #workspaces { + margin: 0 -1em; + padding: 0px; color: #8aadf4; } @@ -353,3 +353,8 @@ label:focus { padding: 0 10px; color: #1793d1; } + +#custom-media { + color: rgba(198, 160, 246, 1); + background-color: rgba(110, 115, 141, 0.5); +} diff --git a/scripts/firefox-status.sh b/scripts/firefox-status.sh new file mode 100755 index 0000000..4d3defa --- /dev/null +++ b/scripts/firefox-status.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +STATUS="$(playerctl -p firefox status)" + +if [ -z "$STATUS" ] || [ "$STATUS" = "Stopped" ]; then + exit 0 +elif [ "$STATUS" = "Paused" ]; then + STATUS=" " +elif [ "$STATUS" = "Playing" ]; then + STATUS=" " +else + exit 0 +fi + +TITLE="$(playerctl -p firefox metadata title)" +ARTIST="$(playerctl -p firefox metadata artist)" + +printf "%s\n" "$STATUS$TITLE - $ARTIST" diff --git a/scripts/media-player.log b/scripts/media-player.log new file mode 100644 index 0000000..9532318 --- /dev/null +++ b/scripts/media-player.log @@ -0,0 +1,19 @@ +2025-03-15 11:14:36,792 __main__ INFO:209 Creating player manager +2025-03-15 11:14:36,792 __main__ INFO:211 Filtering for player: /usr/bin/mpv +2025-03-15 11:14:36,795 __main__ INFO:56 Starting main loop +2025-03-15 11:14:46,385 __main__ INFO:20 Received signal to stop, exiting +2025-03-15 11:16:38,985 __main__ INFO:209 Creating player manager +2025-03-15 11:16:38,985 __main__ INFO:211 Filtering for player: zen-browser +2025-03-15 11:16:38,987 __main__ INFO:56 Starting main loop +2025-03-15 11:17:04,285 __main__ INFO:155 Player has appeared: firefox +2025-03-15 11:17:12,010 __main__ INFO:20 Received signal to stop, exiting +2025-03-15 11:17:14,562 __main__ INFO:209 Creating player manager +2025-03-15 11:17:14,562 __main__ INFO:211 Filtering for player: firefox +2025-03-15 11:17:14,565 __main__ INFO:60 Initialize new player: firefox +2025-03-15 11:17:14,566 __main__ INFO:56 Starting main loop +2025-03-15 11:19:35,500 __main__ INFO:20 Received signal to stop, exiting +2025-03-15 12:35:35,101 __main__ INFO:209 Creating player manager +2025-03-15 12:35:35,101 __main__ INFO:211 Filtering for player: firefox +2025-03-15 12:35:35,104 __main__ INFO:60 Initialize new player: firefox +2025-03-15 12:35:35,105 __main__ INFO:56 Starting main loop +2025-03-15 12:35:56,559 __main__ INFO:20 Received signal to stop, exiting diff --git a/scripts/mediaplayer.py b/scripts/mediaplayer.py new file mode 100755 index 0000000..75122a8 --- /dev/null +++ b/scripts/mediaplayer.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python3 +import gi + +gi.require_version("Playerctl", "2.0") +import argparse +import json +import logging +import os +import signal +import sys +from typing import List + +import gi +from gi.repository import GLib, Playerctl +from gi.repository.Playerctl import Player + +logger = logging.getLogger(__name__) +is_plain = False + + +def signal_handler(sig, frame): + logger.info("Received signal to stop, exiting") + sys.stdout.write("\n") + sys.stdout.flush() + # loop.quit() + sys.exit(0) + + +class PlayerManager: + def __init__(self, selected_player=None, excluded_player=[]): + self.manager = Playerctl.PlayerManager() + self.loop = GLib.MainLoop() + self.manager.connect( + "name-appeared", lambda *args: self.on_player_appeared(*args) + ) + self.manager.connect( + "player-vanished", lambda *args: self.on_player_vanished(*args) + ) + + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + self.selected_player = selected_player + self.excluded_player = excluded_player.split(",") if excluded_player else [] + + self.init_players() + + def init_players(self): + for player in self.manager.props.player_names: + if player.name in self.excluded_player: + continue + if self.selected_player is not None and self.selected_player != player.name: + logger.debug(f"{player.name} is not the filtered player, skipping it") + continue + self.init_player(player) + + def run(self): + logger.info("Starting main loop") + self.loop.run() + + def init_player(self, player): + logger.info(f"Initialize new player: {player.name}") + player = Playerctl.Player.new_from_name(player) + player.connect("playback-status", self.on_playback_status_changed, None) + player.connect("metadata", self.on_metadata_changed, None) + self.manager.manage_player(player) + self.on_metadata_changed(player, player.props.metadata) + + def get_players(self) -> List[Player]: + return self.manager.props.players + + def write_output(self, text, player): + logger.debug(f"Writing output: {text}") + + if not is_plain: + output = { + "text": text, + "class": "custom-" + player.props.player_name, + "alt": player.props.player_name, + } + + sys.stdout.write(json.dumps(output)) + sys.stdout.flush() + else: + sys.stdout.write(text + "\n") + # sys.stdout.write(text) + sys.stdout.flush() + + def clear_output(self): + sys.stdout.write("\n") + sys.stdout.flush() + + def on_playback_status_changed(self, player, status, _=None): + logger.debug( + f"Playback status changed for player {player.props.player_name}: {status}" + ) + self.on_metadata_changed(player, player.props.metadata) + + def get_first_playing_player(self): + players = self.get_players() + logger.debug(f"Getting first playing player from {len(players)} players") + if len(players) > 0: + # if any are playing, show the first one that is playing + # reverse order, so that the most recently added ones are preferred + for player in players[::-1]: + if player.props.status == "Playing": + return player + # if none are playing, show the first one + return players[0] + else: + logger.debug("No players found") + return None + + def show_most_important_player(self): + logger.debug("Showing most important player") + # show the currently playing player + # or else show the first paused player + # or else show nothing + current_player = self.get_first_playing_player() + if current_player is not None: + self.on_metadata_changed(current_player, current_player.props.metadata) + else: + self.clear_output() + + def on_metadata_changed(self, player, metadata, _=None): + logger.debug(f"Metadata changed for player {player.props.player_name}") + player_name = player.props.player_name + artist = player.get_artist() + title = player.get_title() + title = title.replace("&", "&") + + track_info = "" + if ( + player_name == "spotify" + and "mpris:trackid" in metadata.keys() + and ":ad:" in player.props.metadata["mpris:trackid"] + ): + track_info = "Advertisement" + elif artist is not None and title is not None: + track_info = f"{artist} - {title}" + else: + track_info = title + + if track_info: + if player.props.status == "Playing": + track_info = "playing " + track_info + else: + track_info = "paused " + track_info + # only print output if no other player is playing + current_playing = self.get_first_playing_player() + if ( + current_playing is None + or current_playing.props.player_name == player.props.player_name + ): + self.write_output(track_info, player) + else: + logger.debug( + f"Other player {current_playing.props.player_name} is playing, skipping" + ) + + def on_player_appeared(self, _, player): + logger.info(f"Player has appeared: {player.name}") + if player.name in self.excluded_player: + logger.debug( + "New player appeared, but it's in exclude player list, skipping" + ) + return + if player is not None and ( + self.selected_player is None or player.name == self.selected_player + ): + self.init_player(player) + else: + logger.debug( + "New player appeared, but it's not the selected player, skipping" + ) + + def on_player_vanished(self, _, player): + logger.info(f"Player {player.props.player_name} has vanished") + self.show_most_important_player() + + +def parse_arguments(): + parser = argparse.ArgumentParser() + + # Increase verbosity with every occurrence of -v + parser.add_argument("-v", "--verbose", action="count", default=0) + + parser.add_argument("-x", "--exclude", "- Comma-separated list of excluded player") + parser.add_argument( + "-p", "--plain", action="store_true", help="Plain text output", default=False + ) + + # Define for which player we"re listening + parser.add_argument("--player") + + parser.add_argument("--enable-logging", action="store_true") + + return parser.parse_args() + + +def main(): + global is_plain + arguments = parse_arguments() + + # Initialize logging + if arguments.enable_logging: + logfile = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "media-player.log" + ) + logging.basicConfig( + filename=logfile, + level=logging.DEBUG, + format="%(asctime)s %(name)s %(levelname)s:%(lineno)d %(message)s", + ) + + # Logging is set by default to WARN and higher. + # With every occurrence of -v it's lowered by one + logger.setLevel(max((3 - arguments.verbose) * 10, 0)) + + logger.info("Creating player manager") + if arguments.player: + logger.info(f"Filtering for player: {arguments.player}") + if arguments.exclude: + logger.info(f"Exclude player {arguments.exclude}") + if arguments.plain: + logger.info("Plain text output") + is_plain = arguments.plain + + player = PlayerManager(arguments.player, arguments.exclude) + player.run() + + +if __name__ == "__main__": + + main() diff --git a/scripts/pipewire.sh b/scripts/pipewire.sh new file mode 100755 index 0000000..125a77e --- /dev/null +++ b/scripts/pipewire.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +set -e + +# https://blog.dhampir.no/content/sleeping-without-a-subprocess-in-bash-and-how-to-sleep-forever +snore() { + local IFS + [[ -n "${_snore_fd:-}" ]] || exec {_snore_fd}<> <(:) + read -r ${1:+-t "$1"} -u $_snore_fd || : +} + +DELAY=0.2 + +while snore $DELAY; do + WP_OUTPUT=$(wpctl get-volume @DEFAULT_AUDIO_SINK@) + + if [[ $WP_OUTPUT =~ ^Volume:[[:blank:]]([0-9]+)\.([0-9]{2})([[:blank:]].MUTED.)?$ ]]; then + if [[ -n ${BASH_REMATCH[3]} ]]; then + printf "MUTE\n" + else + VOLUME=$((10#${BASH_REMATCH[1]}${BASH_REMATCH[2]})) + ICON=( + "" + "" + "" + ) + + if [[ $VOLUME -gt 50 ]]; then + printf "%s" "${ICON[0]} " + elif [[ $VOLUME -gt 25 ]]; then + printf "%s" "${ICON[1]} " + elif [[ $VOLUME -ge 0 ]]; then + printf "%s" "${ICON[2]} " + fi + + printf "$VOLUME%%\n" + fi + fi +done + +exit 0 diff --git a/scripts/scroll-firefox.sh b/scripts/scroll-firefox.sh new file mode 100755 index 0000000..2f634e2 --- /dev/null +++ b/scripts/scroll-firefox.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +zscroll -p ' | ' --delay 0.2 \ + --length 30 \ + --match-command "$HOME/.config/waybar/scripts/firefox-status.sh" \ + --match-text ' ' "" \ + --match-text ' ' "--scroll 0" \ + --update-interval 1 \ + --update-check true "$HOME/.config/waybar/scripts/firefox-status.sh" & +wait diff --git a/scripts/scroll-mpd.sh b/scripts/scroll-mpd.sh new file mode 100755 index 0000000..9487f3c --- /dev/null +++ b/scripts/scroll-mpd.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +zscroll -p ' | ' --delay 0.2 --before-text " x" \ + --length 30 \ + --match-command "mpc status" \ + --match-text "playing" "--before-text ' '" \ + --match-text "paused" "--before-text ' ' --scroll 0" \ + --update-interval 1 \ + --update-check true "mpc current" & +wait diff --git a/scripts/wttr.sh b/scripts/wttr.sh new file mode 100755 index 0000000..51c6faf --- /dev/null +++ b/scripts/wttr.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +for _ in {1..5}; do + text="$(curl -s "https://wttr.in/$1?format=1" | sed -E "s/\s+/ /g")" + if [[ "$?" = 0 ]]; then + # text="$(echo "$text" | awk '{print $2}')" + tooltip=$(curl -s "https://wttr.in/$1?format=4") + if [ "$?" = 0 ]; then + tooltip=$(echo "$tooltip" | sed -E "s/\s+/ /g") + echo "{\"text\":\"$text\", \"tooltip\":\"$tooltip\"}" + exit + fi + fi + sleep 2 +done +echo "{\"text\":\"error\", \"tooltip\":\"error\"}"