111 lines
3.0 KiB
Bash
Executable File
111 lines
3.0 KiB
Bash
Executable File
#!/usr/bin/env zsh
|
|
|
|
setopt pipefail typeset_silent
|
|
|
|
get_default_sink() {
|
|
local -a first_line
|
|
first_line=${${(f)"$(wpctl inspect @DEFAULT_AUDIO_SINK@)"}[1]}
|
|
if (( ${?} )); then
|
|
echo "error: getting default sink with wpctl failed with error code ${?}" >&2
|
|
exit 1
|
|
elif [[ ${first_line} =~ '^id ([0-9]+), ' ]]; then
|
|
pw-dump | jq -r --argjson def_sink "${match}" \
|
|
'.[] | select(.id == $def_sink)
|
|
| .info.props
|
|
| "\(if .["node.nick"]
|
|
then "\(.["node.nick"]) - "
|
|
else "" end)\(.["node.description"]) (\($def_sink))"'
|
|
if (( ${?} )); then
|
|
echo "error: pw-dump or jq failed with error code ${?}" >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "error: getting default sink with wpctl failed" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
local current_sink_desc="$(get_default_sink)"
|
|
|
|
local JQ_SCRIPT=$(<<'EOF'
|
|
. as $input
|
|
| .[]
|
|
| select(.type == "PipeWire:Interface:Node"
|
|
and .info.props.["media.class"] == "Audio/Sink")
|
|
| .id as $id
|
|
| .info.props
|
|
| if .["node.nick"] then
|
|
"\(.["node.nick"]) - "
|
|
else
|
|
""
|
|
end as $nick_srt
|
|
| .["node.description"] as $desc
|
|
| if .["device.id"] then
|
|
.["device.id"] as $did
|
|
| $input.[]
|
|
| select(.id == $did)
|
|
| .info.props.["device.icon-name"] // ""
|
|
else
|
|
.["device.icon-name"] // ""
|
|
end as $icon
|
|
| debug($icon)
|
|
| "\($id)\u0000\($nick_srt)\($desc) (\($id))\u0000\($icon)\u0000"
|
|
EOF
|
|
)
|
|
|
|
local -a id_desc_pairs
|
|
|
|
id_desc_pairs=(${(0)"$(pw-dump | jq -rj "${JQ_SCRIPT}")"})
|
|
if (( ${?} )); then
|
|
echo "error: pw-dump or jq failed with error code ${?}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
generate_icon_names() {
|
|
# I got this idea from the pavucontrol source
|
|
# 1. First try the icon name as is
|
|
# 2. Then concatenate "-symbolic"
|
|
# 3. Then remove the last dash (-) character and everything after it
|
|
# 4. Then again try that with "-symbolic"
|
|
# 5. Repeat 3 and 4 while there is a dash (-) in the name
|
|
local nonl="${1//%'\n'/}"
|
|
printf '%s,%s' "${nonl}" "${nonl}-symbolic"
|
|
while [[ ${nonl} = *-* ]]; do
|
|
printf ',%s,%s' "${nonl%-*}" "${nonl%-*}-symbolic"
|
|
nonl="${nonl%-*}"
|
|
done
|
|
}
|
|
|
|
local -a ids descs fuzzel_ents
|
|
|
|
for id desc icon in ${id_desc_pairs}; do
|
|
ids+=("${id}")
|
|
descs+=("${desc}")
|
|
generate_icon_names "${icon}"
|
|
echo
|
|
fuzzel_ents+=("${desc//$'\n'/}" "$(generate_icon_names "${icon}")")
|
|
done
|
|
|
|
local fuzzel_index
|
|
|
|
fuzzel_index="$(printf '%s\0icon\x1f%s\n' ${fuzzel_ents} \
|
|
| fuzzel --mesg "Current: ${current_sink_desc}" --dmenu --index)"
|
|
if (( ${?} )); then
|
|
echo "Canceled by user" >&2
|
|
exit 0
|
|
elif [[ ${fuzzel_res} = -1 ]]; then
|
|
echo "User selected non-existant entry" >&2
|
|
exit 0
|
|
fi
|
|
|
|
let new_id=$(( ids[fuzzel_index + 1] ))
|
|
|
|
wpctl set-default ${new_id}
|
|
if (( ${?} )); then
|
|
echo 'error: wpctl failed with exit code ${?}' >&2
|
|
exit 1
|
|
fi
|
|
|
|
notify-send -t 5000 'Default audio sink changed' \
|
|
"${descs[${fuzzel_index} + 1]}"
|