From 02691d4e42b4efeaf6516d2f163d3760e06b8c38 Mon Sep 17 00:00:00 2001 From: Uyanide Date: Sat, 28 Mar 2026 07:53:51 +0100 Subject: [PATCH] add mpd --- config-stow | 1 + config/mpd/.config/mpd/.gitignore | 5 + config/mpd/.config/mpd/bt-mpd-pause.py | 41 ++ config/mpd/.config/mpd/mpd.conf | 399 ++++++++++++++++++ config/mpd/.config/mpd/playlists/.gitkeep | 0 .../.config/systemd/user/bt-mpd-pause.service | 10 + config/nwg-look/.config/gtk-3.0/settings.ini | 3 +- config/nwg-look/.config/gtk-4.0/servers | 26 ++ config/scripts/.local/scripts/targo | 2 +- config/shell/.config/zsh/conf.d/10-oops.zsh | 65 ++- 10 files changed, 529 insertions(+), 23 deletions(-) create mode 100644 config/mpd/.config/mpd/.gitignore create mode 100755 config/mpd/.config/mpd/bt-mpd-pause.py create mode 100644 config/mpd/.config/mpd/mpd.conf create mode 100644 config/mpd/.config/mpd/playlists/.gitkeep create mode 100644 config/mpd/.config/systemd/user/bt-mpd-pause.service create mode 100644 config/nwg-look/.config/gtk-4.0/servers diff --git a/config-stow b/config-stow index 8b576e5..d409989 100755 --- a/config-stow +++ b/config-stow @@ -29,6 +29,7 @@ GUI_BASE_PKGS = [ "kitty", # terminal emulator "ghostty", # alternative terminal emulator "misc", # miscellaneous GUI configs (e.g. *-flags) + "mpd", # music player daemon # "mpv", # media player "wallpaper", # wallpapers & manager "kvantum", # qt theming diff --git a/config/mpd/.config/mpd/.gitignore b/config/mpd/.config/mpd/.gitignore new file mode 100644 index 0000000..017f0ea --- /dev/null +++ b/config/mpd/.config/mpd/.gitignore @@ -0,0 +1,5 @@ +playlists/* +!playlists/.gitkeep + +database +sticker.sql diff --git a/config/mpd/.config/mpd/bt-mpd-pause.py b/config/mpd/.config/mpd/bt-mpd-pause.py new file mode 100755 index 0000000..7f352eb --- /dev/null +++ b/config/mpd/.config/mpd/bt-mpd-pause.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +"""Pause MPD when a Bluetooth audio device disconnects.""" + +import os +import subprocess + +import dbus +from dbus.mainloop.glib import DBusGMainLoop +from gi.repository import GLib + +MPD_SOCKET = os.environ.get( + "MPD_SOCKET", + os.path.join( + os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}"), + "mpdsocket", + ), +) + + +def on_properties_changed(interface, changed, invalidated, path=None): + if interface != "org.bluez.Device1": + return + if "Connected" in changed and not changed["Connected"]: + subprocess.run(["mpc", "-h", MPD_SOCKET, "pause"], check=False) + + +def main(): + DBusGMainLoop(set_as_default=True) + bus = dbus.SystemBus() + bus.add_signal_receiver( + on_properties_changed, + signal_name="PropertiesChanged", + dbus_interface="org.freedesktop.DBus.Properties", + bus_name="org.bluez", + path_keyword="path", + ) + GLib.MainLoop().run() + + +if __name__ == "__main__": + main() diff --git a/config/mpd/.config/mpd/mpd.conf b/config/mpd/.config/mpd/mpd.conf new file mode 100644 index 0000000..2e00be8 --- /dev/null +++ b/config/mpd/.config/mpd/mpd.conf @@ -0,0 +1,399 @@ +# An example configuration file for MPD. +# Read the user manual for documentation: http://www.musicpd.org/doc/user/ + + +# Files and directories ####################################################### +# +# This setting controls the top directory which MPD will search to discover the +# available audio files and add them to the daemon's online database. This +# setting defaults to the XDG directory, otherwise the music directory will be +# be disabled and audio files will only be accepted over ipc socket (using +# file:// protocol) or streaming files over an accepted protocol. +# +#music_directory "$XDG_MUSIC_DIR" +music_directory "~/Music" +# +# This setting sets the MPD internal playlist directory. The purpose of this +# directory is storage for playlists created by MPD. The server will use +# playlist files not created by the server but only if they are in the MPD +# format. This setting defaults to playlist saving being disabled. +# +#playlist_directory "$XDG_CONFIG_HOME/mpd/playlists" +playlist_directory "~/.config/mpd/playlists" +# +# This setting sets the location of the MPD database. This file is used to +# load the database at server start up and store the database while the +# server is not up. This setting defaults to disabled which will allow +# MPD to accept files over ipc socket (using file:// protocol) or streaming +# files over an accepted protocol. +# +#db_file "$XDG_CACHE_HOME/mpd/database" +db_file "~/.config/mpd/database" + +# These settings are the locations for the daemon log files for the daemon. +# +# The special value "syslog" makes MPD use the local syslog daemon. This +# setting defaults to logging to syslog. +# +# If you use systemd, do not configure a log_file. With systemd, MPD +# defaults to the systemd journal, which is fine. +# +#log_file "$XDG_CACHE_HOME/mpd/log" +#log_file "~/.config/mpd/log" + +# This setting sets the location of the file which stores the process ID +# for use of mpd --kill and some init scripts. This setting is disabled by +# default and the pid file will not be stored. +# +# If you use systemd, do not configure a pid_file. +# +#pid_file "$XDG_RUNTIME_DIR/mpd/mpd.pid" +#pid_file "~/.mpd/pid" + +# This setting sets the location of the file which contains information about +# most variables to get MPD back into the same general shape it was in before +# it was brought down. This setting is disabled by default and the server +# state will be reset on server start up. +# +#state_file "$XDG_RUNTIME_DIR/mpd/state" +state_file "~/.local/state/mpd/state" +# +# The location of the sticker database. This is a database which +# manages dynamic information attached to songs. +# +#sticker_file "$XDG_CACHE_HOME/sticker.sql" +sticker_file "~/.config/mpd/sticker.sql" +# +############################################################################### + + +# General music daemon options ################################################ +# +# This setting specifies the user that MPD will run as. MPD should never run as +# root and you may use this setting to make MPD change its user ID after +# initialization. This setting is disabled by default and MPD is run as the +# current user. +# +#user "nobody" +# +# This setting sets the address for the daemon to listen on. Careful attention +# should be paid if this is assigned to anything other than the default, any. +# This setting can deny access to control of the daemon. Not effective if +# systemd socket activation is in use. +# +# For network +#bind_to_address "any" +# +# And for Unix Socket +bind_to_address "$XDG_RUNTIME_DIR/mpdsocket" +#bind_to_address "~/.mpd/socket" +# +# This setting is the TCP port that is desired for the daemon to get assigned +# to. +# +port "26262" +# +# Suppress all messages below the given threshold. Use "verbose" for +# troubleshooting. Available setting arguments are "notice", "info", "verbose", +# "warning" and "error". +# +#log_level "notice" +# +# Setting "restore_paused" to "yes" puts MPD into pause mode instead +# of starting playback after startup. +# +restore_paused "yes" +# +# This setting enables MPD to create playlists in a format usable by other +# music players. +# +#save_absolute_paths_in_playlists "no" +# +# This setting defines a list of tag types that will be extracted during the +# audio file discovery process. The complete list of possible values can be +# found in the user manual. +#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc" +# +# This example just enables the "comment" tag without disabling all +# the other supported tags: +#metadata_to_use "+comment" +# +# This setting enables automatic update of MPD's database when files in +# music_directory are changed. (Linux only) +# +auto_update "yes" +# +# Limit the depth of the directories being watched, 0 means only watch +# the music directory itself. There is no limit by default. +# +#auto_update_depth "3" +# +############################################################################### + + +# Symbolic link behavior ###################################################### +# +# If this setting is set to "yes", MPD will discover audio files by following +# symbolic links outside of the configured music_directory. +# +#follow_outside_symlinks "yes" +# +# If this setting is set to "yes", MPD will discover audio files by following +# symbolic links inside of the configured music_directory. +# +#follow_inside_symlinks "yes" +# +############################################################################### + + +# Zeroconf / Avahi Service Discovery ########################################## +# +# If this setting is set to "yes", service information will be published with +# Zeroconf / Avahi. +# +#zeroconf_enabled "yes" +# +# The argument to this setting will be the Zeroconf / Avahi unique name for +# this MPD server on the network. %h will be replaced with the hostname. +# +#zeroconf_name "Music Player @ %h" +# +############################################################################### + + +# Permissions ################################################################# +# +# If this setting is set, MPD will require password authorization. The password +# setting can be specified multiple times for different password profiles. +# +#password "password@read,add,control,admin" +# +# This setting specifies the permissions a user has who has not yet logged in. +# +#default_permissions "read,add,control,admin" +# +############################################################################### + + +# Database ####################################################################### +# +# An example of a database section instead of the old 'db_file' setting. +# It enables mounting other storages into the music directory. +# +#database { +# plugin "simple" +# path "~/.local/share/mpd/db" +# cache_directory "~/.local/share/mpd/cache" +#} +# +# An example of database config for a satellite setup +# +#music_directory "nfs://fileserver.local/srv/mp3" +#database { +# plugin "proxy" +# host "other.mpd.host" +# port "6600" +#} + +# Input ####################################################################### +# +input { + plugin "curl" +# proxy "proxy.isp.com:8080" +# proxy_user "user" +# proxy_password "password" +} + +# +############################################################################### + +# Audio Output ################################################################ +# +# MPD supports various audio output types, as well as playing through multiple +# audio outputs at the same time, through multiple audio_output settings +# blocks. Setting this block is optional, though the server will only attempt +# autodetection for one sound card. +# +# An example of an ALSA output: +# +#audio_output { +# type "alsa" +# name "My ALSA Device" +## device "hw:0,0" # optional +## mixer_type "hardware" # optional +## mixer_device "default" # optional +## mixer_control "PCM" # optional +## mixer_index "0" # optional +#} +# +# An example of an OSS output: +# +#audio_output { +# type "oss" +# name "My OSS Device" +## device "/dev/dsp" # optional +## mixer_type "hardware" # optional +## mixer_device "/dev/mixer" # optional +## mixer_control "PCM" # optional +#} +# +# An example of a shout output (for streaming to Icecast): +# +#audio_output { +# type "shout" +# encoder "vorbis" # optional +# name "My Shout Stream" +# host "localhost" +# port "8000" +# mount "/mpd.ogg" +# password "hackme" +# quality "5.0" +# bitrate "128" +# format "44100:16:1" +## protocol "icecast2" # optional +## user "source" # optional +## description "My Stream Description" # optional +## url "http://example.com" # optional +## genre "jazz" # optional +## public "no" # optional +## timeout "2" # optional +## mixer_type "software" # optional +#} +# +# An example of a recorder output: +# +#audio_output { +# type "recorder" +# name "My recorder" +# encoder "vorbis" # optional, vorbis or lame +# path "/var/lib/mpd/recorder/mpd.ogg" +## quality "5.0" # do not define if bitrate is defined +# bitrate "128" # do not define if quality is defined +# format "44100:16:1" +#} +# +# An example of a httpd output (built-in HTTP streaming server): +# +#audio_output { +# type "httpd" +# name "My HTTP Stream" +# encoder "vorbis" # optional, vorbis or lame +# port "8000" +# bind_to_address "0.0.0.0" # optional, IPv4 or IPv6 +## quality "5.0" # do not define if bitrate is defined +# bitrate "128" # do not define if quality is defined +# format "44100:16:1" +# max_clients "0" # optional 0=no limit +#} +# +# An example of a pulseaudio output (streaming to a remote pulseaudio server) +# +#audio_output { +# type "pulse" +# name "My Pulse Output" +## server "remote_server" # optional +## sink "remote_server_sink" # optional +## media_role "media_role" #optional +#} +# +# An example of a winmm output (Windows multimedia API). +# +#audio_output { +# type "winmm" +# name "My WinMM output" +## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional +# or +## device "0" # optional +## mixer_type "hardware" # optional +#} +# +# An example of a wasapi output (Windows multimedia API). +# +#audio_output { +# type "wasapi" +# name "My WASAPI output" +## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional +# or +## device "0" # optional +## mixer_type "hardware" # optional +## Exclusive mode blocks all other audio source, and get best audio quality without resampling. +## exclusive "no" # optional +## Enumerate all devices in log. +## enumerate "no" # optional +#} +# +# An example of an openal output. +# +#audio_output { +# type "openal" +# name "My OpenAL output" +## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional +#} +# +# An example of an sndio output. +# +#audio_output { +# type "sndio" +# name "sndio output" +# mixer_type "hardware" +#} +# +# An example of an OS X output: +# +#audio_output { +# type "osx" +# name "My OS X Device" +## device "Built-in Output" # optional +## channel_map "-1,-1,0,1" # optional +#} +# +## Example "pipe" output: +# +#audio_output { +# type "pipe" +# name "my pipe" +# command "aplay -f cd 2>/dev/null" +## Or if you're want to use AudioCompress +# command "AudioCompress -m | aplay -f cd 2>/dev/null" +## Or to send raw PCM stream through PCM: +# command "nc example.org 8765" +# format "44100:16:2" +#} +# +## An example of a null output (for no audio output): +# +#audio_output { +# type "null" +# name "My Null Output" +# mixer_type "none" # optional +#} +# +audio_output { + type "pipewire" + name "PipeWire output" +} +############################################################################### + + +# Normalization automatic volume adjustments ################################## +# +# This setting specifies the type of ReplayGain to use. This setting can have +# the argument "off", "album", "track" or "auto". "auto" is a special mode that +# chooses between "track" and "album" depending on the current state of +# random playback. If random playback is enabled then "track" mode is used. +# See for +# more details about ReplayGain. +# This setting is off by default. +# +#replaygain "album" +# +############################################################################### + +# Character Encoding ########################################################## +# +# If file or directory names do not display correctly for your locale then you +# may need to modify this setting. +# +#filesystem_charset "UTF-8" +# +############################################################################### diff --git a/config/mpd/.config/mpd/playlists/.gitkeep b/config/mpd/.config/mpd/playlists/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/config/mpd/.config/systemd/user/bt-mpd-pause.service b/config/mpd/.config/systemd/user/bt-mpd-pause.service new file mode 100644 index 0000000..1bb304a --- /dev/null +++ b/config/mpd/.config/systemd/user/bt-mpd-pause.service @@ -0,0 +1,10 @@ +[Unit] +Description=Pause MPD when Bluetooth sink disconnects +After=pipewire-pulse.service + +[Service] +ExecStart=%h/.config/mpd/bt-mpd-pause.py +Restart=always + +[Install] +WantedBy=default.target diff --git a/config/nwg-look/.config/gtk-3.0/settings.ini b/config/nwg-look/.config/gtk-3.0/settings.ini index 4831780..17f5189 100644 --- a/config/nwg-look/.config/gtk-3.0/settings.ini +++ b/config/nwg-look/.config/gtk-3.0/settings.ini @@ -12,9 +12,8 @@ gtk-enable-input-feedback-sounds=0 gtk-font-name=Sarasa UI SC, 10 gtk-icon-theme-name=Papirus gtk-menu-images=true -gtk-modules=colorreload-gtk-module:appmenu-gtk-module +gtk-modules=colorreload-gtk-module gtk-primary-button-warps-slider=true -gtk-shell-shows-menubar=1 gtk-sound-theme-name=ocean gtk-theme-name=catppuccin-mocha-blue-standard+default gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR diff --git a/config/nwg-look/.config/gtk-4.0/servers b/config/nwg-look/.config/gtk-4.0/servers new file mode 100644 index 0000000..cb47a1d --- /dev/null +++ b/config/nwg-look/.config/gtk-4.0/servers @@ -0,0 +1,26 @@ + + + + default on deccenia + + + + + + + + + + documents on deccenia + + + + + + + + + \ No newline at end of file diff --git a/config/scripts/.local/scripts/targo b/config/scripts/.local/scripts/targo index 8262213..79707a2 100755 --- a/config/scripts/.local/scripts/targo +++ b/config/scripts/.local/scripts/targo @@ -15,7 +15,7 @@ fi mountpoint=$(mktemp -d) cleanup() { cd / - umount "$mountpoint" 2>/dev/null || true + sudo umount "$mountpoint" 2>/dev/null || true rmdir "$mountpoint" } trap cleanup EXIT diff --git a/config/shell/.config/zsh/conf.d/10-oops.zsh b/config/shell/.config/zsh/conf.d/10-oops.zsh index b0cd2a0..24da9ee 100644 --- a/config/shell/.config/zsh/conf.d/10-oops.zsh +++ b/config/shell/.config/zsh/conf.d/10-oops.zsh @@ -1,29 +1,54 @@ +# Remove the last command from history (both memory and file). +# The alias has a leading space so "oops" itself is not recorded (HIST_IGNORE_SPACE). uy_oops_confirm() { - if [[ ! -f "$HISTFILE" ]]; then - print -P "%F{red}Failed to locate history file: $HISTFILE%f" - return 1 - fi + # Flush current session to file so we operate on the latest state + fc -W - local last_line=$(tail -n 1 "$HISTFILE") + if [[ ! -f "$HISTFILE" || ! -s "$HISTFILE" ]]; then + print -P "%F{yellow}History is empty, nothing to delete.%f" + return 1 + fi - if [[ -z "$last_line" ]]; then - print -P "%F{yellow}History file is empty, nothing to delete.%f" - return 0 - fi + # Read the last entry (may span multiple lines if it ends with \) + local -a entry + local line + while IFS= read -r line; do + entry+=("$line") + # EXTENDED_HISTORY continuation lines end with a literal backslash + [[ "$line" == *'\' ]] || break + done < <(tail -n 50 "$HISTFILE" | tac) + # entry is reversed (last line first), flip it back + local -a ordered + for (( i=${#entry[@]}; i>=1; i-- )); do + ordered+=("${entry[$i]}") + done - print -P "About to permanently delete the last command from history file:" - print -P "%F{red} $last_line%f" + if (( ${#ordered[@]} == 0 )); then + print -P "%F{yellow}Could not parse last history entry.%f" + return 1 + fi - local reply - echo -n "Proceed? [Y/n] " - read -r reply + print -P "About to permanently delete the last command from history:" + for line in "${ordered[@]}"; do + print -P " %F{red}${line}%f" + done - if [[ -z "$reply" || "$reply" == [yY]* ]]; then - sed -i '$d' "$HISTFILE" - print -P "%F{green}Command removed from history file (may still visible in current session).%f" - else - print -P "%F{yellow}Operation cancelled.%f" - fi + local reply + echo -n "Proceed? [Y/n] " + read -r reply + + if [[ -z "$reply" || "$reply" == [yY]* ]]; then + # Delete the last N lines (the full entry) from the file + local n=${#ordered[@]} + head -n -"$n" "$HISTFILE" > "$HISTFILE.tmp" && mv "$HISTFILE.tmp" "$HISTFILE" + # Reload history from the updated file + fc -R + print -P "%F{green}Deleted ($n line(s) removed).%f" + else + # Reload anyway to stay in sync + fc -R + print -P "%F{yellow}Cancelled.%f" + fi } alias oops=' uy_oops_confirm'