flatten directory structure, remove system/ nesting

This commit is contained in:
2026-07-12 01:06:23 -04:00
parent 2eced0627e
commit c101d1f3ca
4 changed files with 0 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
# audio_toggle.sh
Switch PulseAudio/PipeWire audio output sinks.
## Usage
```bash
./audio_toggle.sh [OPTION]
```
## Options
| Flag | Description |
|------|-------------|
| *(no args)* | Switch to the next available sink |
| `--to <pattern>` | Switch to a sink whose name matches `<pattern>` (case-insensitive) |
| `--prev`, `--previous` | Switch back to the previously active sink |
| `--list` | List all available sinks with descriptions |
| `--current` | Show the current default sink |
| `--move-streams` | Also move existing audio streams to the new sink |
| `--help`, `-h` | Show help message |
## Examples
```bash
audio_toggle.sh # next sink
audio_toggle.sh --to hdmi # switch to HDMI sink
audio_toggle.sh --prev # back to previous sink
audio_toggle.sh --list # see what's available
```
## Notes
- Sinks named `easyeffects` are automatically filtered out.
- The previous sink is stored in `~/.cache/audio-toggle-prev-sink`.
- Streams are not moved by default; use `--move-streams` to relocate them.
+136
View File
@@ -0,0 +1,136 @@
#!/bin/bash
# Description: Switch PulseAudio/PipeWire audio output sinks
STATE_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/audio-toggle-prev-sink"
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTION]
Toggle or switch PulseAudio/PipeWire audio output sinks.
Options:
--to <pattern> Switch to a sink whose name matches <pattern>
--prev, --previous Switch back to the previously active sink
--list List available sinks
--current Show the current default sink
--move-streams Also move existing audio streams to the new sink
--help, -h Show this help message
EOF
exit 0
}
get_current_sink() {
pactl info | sed -En 's/Default Sink: (.*)/\1/p'
}
get_sink_description() {
local sink="$1"
pactl list sinks | awk -v s="$sink" '
/^[[:space:]]*Sink #[0-9]+/ { in_block=""; name="" }
$1 == "Name:" { name = $2 }
name == s && /Description:/ { $1=""; sub(/^[[:space:]]+/, ""); print; exit }
'
}
MOVE_STREAMS=0
MODE="toggle"
TARGET_SINK=""
while [ $# -gt 0 ]; do
case "$1" in
--help|-h) usage ;;
--list)
pactl list short sinks | grep -v easyeffects | while read -r line; do
name=$(echo "$line" | awk '{print $2}')
desc=$(get_sink_description "$name")
echo "$name ($desc)"
done
exit 0
;;
--current)
sink=$(get_current_sink)
desc=$(get_sink_description "$sink")
echo "$sink ($desc)"
exit 0
;;
--prev|--previous)
if [ ! -f "$STATE_FILE" ]; then
notify-send "Audio Toggle" "No previous sink saved"
exit 1
fi
prev=$(cat "$STATE_FILE")
if ! pactl list short sinks | awk '{print $2}' | grep -qxF "$prev"; then
notify-send "Audio Toggle" "Previous sink no longer available"
exit 1
fi
current=$(get_current_sink)
echo "$current" > "$STATE_FILE"
TARGET_SINK="$prev"
MODE="switch"
;;
--to)
shift
[ $# -eq 0 ] && echo "Error: --to requires a pattern" >&2 && exit 1
matched=$(pactl list short sinks | grep -v easyeffects | awk '{print $2}' | grep -i "$1" | head -1)
[ -z "$matched" ] && echo "Error: no sink matching '$1'" >&2 && exit 1
current=$(get_current_sink)
echo "$current" > "$STATE_FILE"
TARGET_SINK="$matched"
MODE="switch"
;;
--move-streams) MOVE_STREAMS=1 ;;
*)
echo "Unknown option: $1" >&2
usage
exit 1
;;
esac
shift
done
SINKS=$(pactl list short sinks | grep -v easyeffects)
SINK_COUNT=$(echo "$SINKS" | wc -l)
if [ "$SINK_COUNT" -eq 0 ]; then
notify-send -u critical "Audio Toggle" "No audio sinks available"
exit 1
fi
CURRENT_SINK=$(get_current_sink)
if [ "$MODE" = "switch" ] && [ -n "$TARGET_SINK" ]; then
NEW_SINK="$TARGET_SINK"
else
SINK_INDEX=$(echo "$SINKS" | awk -v s="$CURRENT_SINK" '$2 == s { print NR; exit }')
SINK_INDEX=${SINK_INDEX:-0}
NEXT_INDEX=$((SINK_INDEX % SINK_COUNT + 1))
NEW_SINK=$(echo "$SINKS" | awk "NR == $NEXT_INDEX { print \$2; exit }")
echo "$CURRENT_SINK" > "$STATE_FILE"
fi
NEW_DESC=$(get_sink_description "$NEW_SINK")
notify-send "Audio Output" "Switched to $NEW_DESC" --icon=audio-speakers
MAX_RETRIES=6
RETRIES=0
while true; do
[ "$RETRIES" -ge "$MAX_RETRIES" ] && notify-send -u critical "Audio Toggle" "Failed after $MAX_RETRIES attempts" && break
pactl set-default-sink "$NEW_SINK"
[ "$(get_current_sink)" = "$NEW_SINK" ] && break
RETRIES=$((RETRIES + 1))
NEXT_INDEX=$(((SINK_INDEX + RETRIES) % SINK_COUNT + 1))
NEW_SINK=$(echo "$SINKS" | awk "NR == $NEXT_INDEX { print \$2; exit }")
NEW_DESC=$(get_sink_description "$NEW_SINK")
notify-send "Audio Toggle" "Sink unavailable, trying $NEW_DESC..."
done
if [ "$MOVE_STREAMS" = 1 ]; then
pactl list short sink-inputs | grep -Eo '^[0-9]+' | while read -r id; do
pactl move-sink-input "$id" "$NEW_SINK"
done
fi