commit d3f34301de042fc976fa5e86981030b3f6ea9b12 Author: mux Date: Mon Oct 20 23:27:25 2025 -0300 initial commit diff --git a/.config/emacs/config.org b/.config/emacs/config.org new file mode 100644 index 0000000..16dd86b --- /dev/null +++ b/.config/emacs/config.org @@ -0,0 +1,629 @@ +#+TITLE: >." . ignore)) + (meow-leader-define-key + '("1" . meow-digit-argument) + '("2" . meow-digit-argument) + '("3" . meow-digit-argument) + '("4" . meow-digit-argument) + '("5" . meow-digit-argument) + '("6" . meow-digit-argument) + '("7" . meow-digit-argument) + '("8" . meow-digit-argument) + '("9" . meow-digit-argument) + '("0" . meow-digit-argument) + '("/" . meow-keypad-describe-key) + '("?" . meow-cheatsheet)) + + (meow-thing-register 'angle + '(pair ("<") (">")) + '(pair ("<") (">"))) + + (setq meow-char-thing-table + '((?f . round) + (?d . square) + (?s . curly) + (?a . angle) + (?r . string) + (?v . paragraph) + (?c . line) + (?x . buffer))) + + (meow-normal-define-key + ;; Expansion + '("0" . meow-expand-0) + '("1" . meow-expand-1) + '("2" . meow-expand-2) + '("3" . meow-expand-3) + '("4" . meow-expand-4) + '("5" . meow-expand-5) + '("6" . meow-expand-6) + '("7" . meow-expand-7) + '("8" . meow-expand-8) + '("9" . meow-expand-9) + '("'" . meow-reverse) + + ;; Movement + '("i" . meow-prev) + '("k" . meow-next) + '("j" . meow-left) + '("l" . meow-right) + '("y" . meow-search) + '("/" . meow-visit) + + ;; Expansion + '("I" . meow-prev-expand) + '("K" . meow-next-expand) + '("J" . meow-left-expand) + '("L" . meow-right-expand) + + '("u" . meow-back-word) + '("U" . meow-back-symbol) + '("o" . meow-next-word) + '("O" . meow-next-symbol) + + '("a" . meow-mark-word) + '("A" . meow-mark-symbol) + '("s" . meow-line) + '("S" . meow-goto-line) + '("w" . meow-block) + '("q" . meow-join) + '("g" . meow-grab) + '("G" . meow-pop-grab) + '("m" . meow-swap-grab) + '("M" . meow-sync-grab) + '("p" . meow-cancel-selection) + '("P" . meow-pop-selection) + + '("x" . meow-till) + '("z" . meow-find) + + '("," . meow-beginning-of-thing) + '("." . meow-end-of-thing) + '("<" . meow-inner-of-thing) + '(">" . meow-bounds-of-thing) + + ;; Editing + '("d" . meow-kill) + '("f" . meow-change) + '("t" . meow-delete) + '("c" . meow-save) + '("v" . meow-yank) + '("V" . meow-yank-pop) + + '("e" . meow-insert) + '("E" . meow-open-above) + '("r" . meow-append) + '("R" . meow-open-below) + + '("h" . undo-only) + '("H" . undo-redo) + + '("b" . open-line) + '("B" . split-line) + + '("[" . indent-rigidly-left-to-tab-stop) + '("]" . indent-rigidly-right-to-tab-stop) + + ;; Prefix commands + '("nf" . meow-comment) + '("nt" . meow-start-kmacro-or-insert-counter) + '("nr" . meow-start-kmacro) + '("ne" . meow-end-or-call-kmacro) + + '(";f" . save-buffer) + '(";F" . save-some-buffers) + '(";d" . meow-query-replace-regexp) + + ;; Buffer navigation + '(";[b" . previous-buffer) + '(";]b" . next-buffer) + '(";b" . switch-to-buffer) + '(";nb" . rename-buffer) + '(";B" . revert-buffer) + + '("" . ignore))) + + :config + (meow-ergo-setup) + (meow-global-mode 1)) +#+end_src + +** Formatter +#+begin_src emacs-lisp + (use-package format-all + :ensure t + :preface + (defun refmt () + "Auto-format whole buffer." + (interactive) + (cond + ((derived-mode-p 'prolog-mode) + (prolog-indent-buffer)) + ((derived-mode-p 'emacs-lisp-mode) + (indent-region (point-min) (point-max))) + ((derived-mode-p 'org-mode) + (org-indent-region (point-min) (point-max))) + (t + (format-all-buffer)))) + :config + (global-set-key (kbd "M-f") #'refmt) + (add-hook 'prog-mode-hook #'format-all-ensure-formatter)) +#+end_src + +* PROGRAMMING FEATURES +** Syntax highlighting +#+begin_src emacs-lisp + (use-package treesit-auto + :ensure t + :custom + (treesit-auto-install 'prompt) + :config + (treesit-auto-add-to-auto-mode-alist 'all) + (global-treesit-auto-mode)) +#+end_src + +** LSP +#+begin_src emacs-lisp + (use-package lsp-mode + :ensure t + :hook + (prog-mode . lsp-deferred) + (lsp-mode . lsp-enable-which-key-integration) + :commands (lsp lsp-deferred) + :init + (setq lsp-keymap-prefix "C-c l") + :custom + (lsp-idle-delay 0.500) + (lsp-log-io nil) + (lsp-completion-provider :none) + (lsp-headerline-breadcrumb-enable nil)) + + (use-package flycheck + :ensure t + :config + (global-flycheck-mode +1)) +#+end_src + +** Indentation preferences +#+begin_src emacs-lisp + (setq-default + indent-tabs-mode t + tab-width 4 + standard-indent 4) + (setq tab-always-indent 1) + + (use-package dtrt-indent + :ensure t + :hook (prog-mode . dtrt-indent-mode)) + + (use-package whitespace + :hook (prog-mode . whitespace-mode) + :custom + (whitespace-style '(face tabs tab-mark trailing)) + (whitespace-display-mappings + '((tab-mark ?\t [?→ ?\t] [?> ?\t])))) +#+end_src + +* COMPLETION SYSTEM +** Helpful documentation +#+begin_src emacs-lisp + (use-package helpful + :ensure t + :bind + ([remap describe-function] . helpful-callable) + ([remap describe-variable] . helpful-variable) + ([remap describe-key] . helpful-key)) +#+end_src + +** Minibuffer completion (Vertico + Marginalia) +#+begin_src emacs-lisp + (use-package marginalia + :ensure t + :bind (:map minibuffer-local-map + ("M-A" . marginalia-cycle)) + :init + (marginalia-mode)) + + (use-package vertico + :ensure t + :custom + (vertico-cycle t) + :init + (vertico-mode)) + + (use-package savehist + :init + (savehist-mode)) + + (use-package orderless + :ensure t + :custom + (completion-styles '(orderless basic)) + (completion-category-defaults nil) + (completion-category-overrides '((file (styles partial-completion))))) +#+end_src + +** Minibuffer settings +#+begin_src emacs-lisp + (use-package emacs + :custom + (context-menu-mode t) + (enable-recursive-minibuffers t) + (read-extended-command-predicate #'command-completion-default-include-p) + (text-mode-ispell-word-completion nil) + (minibuffer-prompt-properties + '(read-only t cursor-intangible t face minibuffer-prompt))) +#+end_src + +** In-buffer completion (Corfu + Cape) +#+begin_src emacs-lisp + (use-package corfu + :ensure t + :custom + (corfu-auto t) + (corfu-cycle t) + (corfu-preselect 'prompt) + :init + (global-corfu-mode) + (corfu-history-mode) + (corfu-popupinfo-mode) + :bind + (:map corfu-map + ("TAB" . corfu-next) + ([tab] . corfu-next) + ("S-TAB" . corfu-previous) + ([backtab] . corfu-previous))) + + (use-package cape + :ensure t + :bind ("C-c p" . cape-prefix-map) + :hook + ((completion-at-point-functions . cape-dabbrev) + (completion-at-point-functions . cape-file) + (completion-at-point-functions . cape-elisp-block))) + +#+end_src + +#+RESULTS: +: [nil 26870 55996 618175 nil elpaca-process-queues nil nil 712000 nil] + +* VISUAL APPEARANCE +** Theme and colors +#+begin_src emacs-lisp + (use-package doom-themes + :ensure t + :custom + (doom-themes-enable-bold t) + (doom-themes-enable-italic t) + :config + (load-theme 'doom-moonlight t) + (doom-themes-visual-bell-config) + (doom-themes-org-config) + + (let ((bg "#16191C") + (fg "#C5CACE") + (bg-alt "#252A2E") + (fg-alt "#A3AAAF") + (fg-muted "#50555A")) + (custom-set-faces + `(default ((t (:background ,bg :foreground ,fg)))) + `(fringe ((t (:background ,bg)))) + `(hl-line ((t (:background ,bg-alt)))) + `(mode-line ((t (:background ,bg-alt :foreground ,fg)))) + `(mode-line-inactive ((t (:background ,bg :foreground ,fg-muted)))) + `(line-number ((t (:foreground ,fg-muted :background ,bg)))) + `(line-number-current-line ((t (:foreground ,fg-alt :background ,bg-alt :weight bold)))) + `(font-lock-comment-face ((t (:foreground ,fg-alt))))))) + + (add-hook 'org-mode-hook + (lambda () + (set-face-background 'org-block "#16191C") + (set-face-foreground 'org-block "#C5CACE") + (set-face-background 'org-block-begin-line "#252A2E") + (set-face-foreground 'org-block-begin-line "#A3AAAF") + (set-face-background 'org-block-end-line "#252A2E") + (set-face-foreground 'org-block-end-line "#A3AAAF"))) +#+end_src + +** Modeline +#+begin_src emacs-lisp + (use-package doom-modeline + :ensure t + :init (doom-modeline-mode +1)) +#+end_src + +** Color preview +#+begin_src emacs-lisp + (use-package colorful-mode + :ensure t + :custom + (colorful-use-prefix t) + (colorful-only-strings 'only-prog) + (css-fontify-colors nil) + :config + (global-colorful-mode t) + (add-to-list 'global-colorful-modes 'helpful-mode)) + + (use-package rainbow-delimiters + :ensure t + :hook (prog-mode . rainbow-delimiters-mode)) +#+end_src + +#+RESULTS: +: [nil 26870 59644 357807 nil elpaca-process-queues nil nil 727000 nil] + +* INTEGRATIONS & UTILITIES +** Discord presence +#+begin_src emacs-lisp + (use-package elcord + :ensure t + :config + (elcord-mode +1)) +#+end_src + +** Project management +#+begin_src emacs-lisp + (use-package projectile + :ensure t + :config + (projectile-mode +1) + (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)) + + (use-package editorconfig + :ensure t + :config + (editorconfig-mode 1)) +#+end_src +** Dashboard +#+begin_src emacs-lisp + (use-package page-break-lines + :ensure t + :config + (page-break-lines-mode +1)) + + (use-package all-the-icons + :ensure t + :if (display-graphic-p)) + + (use-package dashboard + :ensure t + :custom + (dashboard-banner-logo-title "DMs~ ;)") + (dashboard-startup-banner "~/.config/emacs/dash.gif") + :config + (add-hook 'elpaca-after-init-hook #'dashboard-insert-startupify-lists) + (add-hook 'elpaca-after-init-hook #'dashboard-initialize) + (dashboard-setup-startup-hook)) +#+end_src + +** File tree +#+begin_src emacs-lisp + (use-package treemacs + :ensure t + :bind ("C-c t" . treemacs) + :config + (setq treemacs-width 30)) + + (use-package treemacs-projectile + :ensure t + :after (treemacs projectile)) + + (use-package treemacs-all-the-icons + :ensure t + :after (treemacs all-the-icons) + :config + (treemacs-load-theme "all-the-icons")) + + (use-package treemacs-icons-dired + :ensure t + :hook (dired-mode . treemacs-icons-dired-enable-once)) + +#+end_src + +#+RESULTS: +: [nil 26870 60289 404113 nil elpaca-process-queues nil nil 420000 nil] diff --git a/.config/emacs/dash.gif b/.config/emacs/dash.gif new file mode 100644 index 0000000..bcc31d2 Binary files /dev/null and b/.config/emacs/dash.gif differ diff --git a/.config/emacs/early-init.el b/.config/emacs/early-init.el new file mode 100644 index 0000000..512068a --- /dev/null +++ b/.config/emacs/early-init.el @@ -0,0 +1 @@ +(setq package-enable-at-startup nil) diff --git a/.config/emacs/init.el b/.config/emacs/init.el new file mode 100644 index 0000000..3b74f4e --- /dev/null +++ b/.config/emacs/init.el @@ -0,0 +1,12 @@ +;(add-to-list 'load-path +; (expand-file-name +; "tao-theme-emacs/" +; user-emacs-directory)) +; +;(load "tao-yin-theme.el") +;(load-theme 'tao-yin t) + +(org-babel-load-file + (expand-file-name + "config.org" + user-emacs-directory)) diff --git a/.config/fastfetch/config.jsonc b/.config/fastfetch/config.jsonc new file mode 100644 index 0000000..b56569c --- /dev/null +++ b/.config/fastfetch/config.jsonc @@ -0,0 +1,44 @@ +{ + "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", + "modules": [ + "title", + "break", + { + "type": "kernel", + "key": "system" + }, + { + "type": "uptime", + "key": "uptime" + }, + { + "type": "packages", + "key": "bloatware" + }, + { + "type": "wm", + "key": "compositor" + }, + { + "type": "cpu", + "key": "cpu" + }, + { + "type": "gpu", + "key": "gpu" + } + ], + "logo": { + "type": "small", + "color": { + "1": "#9e9b93" + } + }, + "display": { + "separator": " ", // Separator between keys and values + "color": { + "keys": "#9e9b93", // Key color + "title": "#9e9b93" // Title color + } + } +} diff --git a/.config/mako/config b/.config/mako/config new file mode 100644 index 0000000..33ee734 --- /dev/null +++ b/.config/mako/config @@ -0,0 +1,7 @@ +background-color=#16191C +border-color=#252A2E +icons=0 +ignore-timeout=1 +sort=-time +text-color=#A3AAAF +margin=4 diff --git a/.config/sway/config b/.config/sway/config new file mode 100644 index 0000000..0abc3cb --- /dev/null +++ b/.config/sway/config @@ -0,0 +1,170 @@ +exec autotiling + +exec systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP +exec dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=sway + +### Variables +# +# Logo key. Use Mod1 for Alt. +set $mod Mod4 +set $left j +set $down k +set $up i +set $right l +# Your preferred terminal emulator +set $term alacritty +# Your preferred application launcher +set $menu wmenu-run + +default_border pixel 1 +client.focused #252a2e #171717 #E9E9E9 #262626 #252a2e +client.focused_inactive #252a2e #171717 #E9E9E9 #262626 #262626 +client.unfocused #252a2e #171717 #E9E9E9 #262626 #262626 +client.urgent #252a2e #171717 #E9E9E9 #262626 #262626 +client.placeholder #252a2e #171717 #E9E9E9 #262626 #262626 + +gaps inner 2 +gaps outer 2 + +input type:pointer { + accel_profile "flat" + pointer_accel 0.0 +} + +output * bg #02060A solid_color + +input type:keyboard { + xkb_layout "us" + xkb_variant "intl" + xkb_options "caps:swapescape" +} + +floating_modifier $mod normal + +bindsym $mod+Shift+c reload + +bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit' + +# +# Workspaces mode: +# +mode "normal" { + # Switch to workspace + bindsym 1 workspace number 1; mode "default" + bindsym 2 workspace number 2; mode "default" + bindsym 3 workspace number 3; mode "default" + bindsym 4 workspace number 4; mode "default" + bindsym 5 workspace number 5; mode "default" + bindsym 6 workspace number 6; mode "default" + bindsym 7 workspace number 7; mode "default" + bindsym 8 workspace number 8; mode "default" + bindsym 9 workspace number 9; mode "default" + bindsym 0 workspace number 10; mode "default" + + # Move focused container to workspace + bindsym Shift+1 move container to workspace number 1; mode "default" + bindsym Shift+2 move container to workspace number 2; mode "default" + bindsym Shift+3 move container to workspace number 3; mode "default" + bindsym Shift+4 move container to workspace number 4; mode "default" + bindsym Shift+5 move container to workspace number 5; mode "default" + bindsym Shift+6 move container to workspace number 6; mode "default" + bindsym Shift+7 move container to workspace number 7; mode "default" + bindsym Shift+8 move container to workspace number 8; mode "default" + bindsym Shift+9 move container to workspace number 9; mode "default" + bindsym Shift+0 move container to workspace number 10; mode "default" + + # + # Moving around: + # + # Move your focus + bindsym left focus left; mode "default" + bindsym down focus down; mode "default" + bindsym up focus up; mode "default" + bindsym right focus right; mode "default" + + # Move the focused window + bindsym Shift+left move left; mode "default" + bindsym Shift+down move down; mode "default" + bindsym Shift+up move up; mode "default" + bindsym Shift+right move right; mode "default" + + bindsym Return exec $term; mode "default" + bindsym q kill; mode "default" + + # + # Layout stuff: + # + bindsym b splith; mode "default" # horizontal split + bindsym v splitv; mode "default" # vertical split + bindsym s layout stacking; mode "default" + bindsym w layout tabbed; mode "default" + bindsym e layout toggle split; mode "default" + bindsym f fullscreen; mode "default" + bindsym Shift+space floating toggle; mode "default" + bindsym space focus mode_toggle; mode "default" + bindsym a focus parent; mode "default" + + bindsym d exec $menu; mode "default" + + # Exit mode back to default + bindsym Escape mode "default" +} + +# Activate workspaces mode with Super+x +bindsym $mod+x mode "normal" + +# +# Resizing containers: +# +mode "resize" { + # left will shrink the containers width + # right will grow the containers width + # up will shrink the containers height + # down will grow the containers height + bindsym $left resize shrink width 10px + bindsym $down resize grow height 10px + bindsym $up resize shrink height 10px + bindsym $right resize grow width 10px + + # Ditto, with arrow keys + bindsym Left resize shrink width 10px + bindsym Down resize grow height 10px + bindsym Up resize shrink height 10px + bindsym Right resize grow width 10px + + # Return to default mode + bindsym Return mode "default" + bindsym Escape mode "default" + + bindsym p exec shotman --capture window + bindsym Shift+p exec shotman --capture region + bindsym Ctrl+p exec shotman --capture output +} +bindsym $mod+r mode "resize" +# +# Utilities: +# + # Special keys to adjust volume via PulseAudio + bindsym --locked XF86AudioMute exec pactl set-sink-mute \@DEFAULT_SINK@ toggle + bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume \@DEFAULT_SINK@ -5% + bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume \@DEFAULT_SINK@ +5% + bindsym --locked XF86AudioMicMute exec pactl set-source-mute \@DEFAULT_SOURCE@ toggle + + # Special key to take a screenshot with grim + bindsym $mod+z exec grim -g "$(slurp)" + bindsym $mod+y exec grim - | wl-copy + +# +# Status Bar: +# +# Read `man 5 sway-bar` for more information about this section. +bar { + position top + swaybar_command waybar +} + +output * { + mode 1920x1080@120Hz +} + +include /usr/local/etc/sway/config.d/* diff --git a/.config/waybar/config.jsonc b/.config/waybar/config.jsonc new file mode 100644 index 0000000..b55ded3 --- /dev/null +++ b/.config/waybar/config.jsonc @@ -0,0 +1,87 @@ +// -*- mode: jsonc -*- +{ + // "layer": "top", // Waybar at top layer + "position": "top", // Waybar position (top|bottom|left|right) + "height": 30, // Waybar height (to be removed for auto height) + "spacing": 4, // Gaps between modules (4px) + "margin-left": 4, + "margin-right": 4, + "margin-top": 4, + "modules-left": [ + "sway/workspaces", + "sway/mode" + ], + "modules-center": [ + "custom/media" + ], + "modules-right": [ + "pulseaudio", + "clock", + "tray" + ], + // Modules configuration + // "sway/workspaces": { + // "disable-scroll": true, + // "all-outputs": true, + // "warp-on-scroll": false, + // "format": "{name}: {icon}", + // "format-icons": { + // "1": "", + // "2": "", + // "3": "", + // "4": "", + // "5": "", + // "urgent": "", + // "focused": "", + // "default": "" + // } + // }, + "sway/mode": { + "format": "{}" + }, + "tray": { + // "icon-size": 21, + "spacing": 10, + // "icons": { + // "blueman": "bluetooth", + // "TelegramDesktop": "$HOME/.local/share/icons/hicolor/16x16/apps/telegram.png" + // } + }, + "clock": { + // "timezone": "America/New_York", + "format": " {:%H:%M}", + "format-alt": "{:%A, %B %d, %Y (%R)}" + }, + "pulseaudio": { + // "scroll-step": 1, // %, can be a float + "format": "{icon} {volume}%", + "format-bluetooth": "{volume}% {icon} {format_source}", + "format-bluetooth-muted": " {icon} {format_source}", + "format-muted": " 0%", + "format-source": "{volume}% ", + "format-source-muted": "", + "format-icons": { + "headphone": "", + "hands-free": "", + "headset": "", + "phone": "", + "portable": "", + "car": "", + "default": ["", "", ""] + }, + "on-click": "pavucontrol" + }, + "custom/media": { + "format": "{text}", + "return-type": "json", + "max-length": 40, + "format-icons": { + "spotify": "", + "default": "🎜" + }, + "escape": true, + "exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null", + "on-click": "playerctl play-pause" // Script in resources folder + // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name + }, +} diff --git a/.config/waybar/mediaplayer.py b/.config/waybar/mediaplayer.py new file mode 100755 index 0000000..524d4d2 --- /dev/null +++ b/.config/waybar/mediaplayer.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +import gi +gi.require_version("Playerctl", "2.0") +from gi.repository import Playerctl, GLib +from gi.repository.Playerctl import Player +import argparse +import logging +import sys +import signal +import gi +import json +import os +from typing import List + +logger = logging.getLogger(__name__) + +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}") + + output = {"text": text, + "class": "custom-" + player.props.player_name, + "alt": player.props.player_name} + + sys.stdout.write(json.dumps(output) + "\n") + 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() + artist = artist.replace("&", "&") + 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 = " " + track_info + else: + track_info = " " + 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") + + # 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(): + 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}") + + player = PlayerManager(arguments.player, arguments.exclude) + player.run() + + +if __name__ == "__main__": + main() diff --git a/.config/waybar/style.css b/.config/waybar/style.css new file mode 100644 index 0000000..15ece98 --- /dev/null +++ b/.config/waybar/style.css @@ -0,0 +1,35 @@ +* { + min-height: 0; + min-width: 0; + font-family: "ShureTechMono Nerd Font"; + font-size: 16px; + font-weight: 500; +} + +window#waybar { + background-color: transparent; +} + +#workspaces, +#custom-media, +#clock, +#pulseaudio, +#mode, +#tray +{ + padding: 0.5em 1.25em; + background-color: #16191C; + border: 1px solid #252A2E; + color: #A3AAAF; +} + +#workspaces button, +#mode { + color: #50555A; + font-weight: 400; +} + +#workspaces button.visible { + color: #A3AAAF; + font-weight: 600; +}