Compare commits

..

No commits in common. "main" and "1.1" have entirely different histories.
main ... 1.1

3 changed files with 68 additions and 128 deletions

1
.gitignore vendored
View file

@ -3,4 +3,3 @@ debian/debhelper-build-stamp
debian/files
debian/pulseaudio-tcp.substvars
debian/pulseaudio-tcp
*.sw*

View file

@ -22,24 +22,16 @@
usage() {
cat >&2 <<EOF
# pulseaudio-tcp - Connect to remote PulseAudio/Pipewire Server
## Usage:
\`$0 [OPTION ...] OPERATION ...\`
## Options:
* \`--debug\`: Enable additional debug output for start and stop operations.
* \`--no-gui\`: Do not display GUI dialogs, use terminal for input and output.
* \`--cookie COOKIE\`: Pass Pulseaudio cookie as option value instead of copying
from remote_host. Expects value to be Base64-encoded.
## Operations:
* \`setup\`: Interactively gather settings.
* \`start\`: Start redirection to remote host.
* \`stop\`: Stop redirection to remote host.
* \`restart\`: Stop and then start redirection.
* \`status\`: Check if redirection is set up and enabled.
## Environment Variables:
* \`PULSEAUDIO_TCP_SSH_ADDARGS\`:
If set, specifies additional commandline arguments for calls to \`ssh\`.
Example: \`PULSEAUDIO_TCP_SSH_ADDARGS=-v pulseaudio-tcp --no-gui start\`
Usage: $0 [OPTIONS] OPERATION
Options:
--debug Enable additional debug output for start and stop operations.
--no-gui Do not display GUI dialogs, use terminal for input and output.
Operations:
setup Interactively gather settings.
start Start redirection to remote host.
stop Stop redirection to remote host.
restart Stop and then start redirection.
status Check if redirection is set up and enabled.
EOF
}
@ -52,7 +44,7 @@ log() {
if [[ -t 1 ]] && $gui && [[ -n $(type -p zenity) ]] ; then
case "$level" in
ERROR|WARNING)
ERROR)
zenity --error --text="$msg"
;;
*)
@ -66,22 +58,20 @@ error() {
log ERROR "$@"
}
warning() {
log WARNING "$@"
}
info() {
log INFO "$@"
}
debug() {
if "$debug" ; then
log DEBUG "$@"
else
gui=false log DEBUG "$@"
fi
}
_ssh() {
if ssh "${_ssh_arguments[@]}" -S "$USER"-pulseaudio "$remote_user"@"$remote_ip" "$@" ; then
if ssh -o PasswordAuthentication=no -S "$USER"-pulseaudio "$remote_user"@"$remote_ip" "$@" ; then
return 0
else
error "SSH remote_ip=$remote_ip failed."
@ -243,23 +233,17 @@ do_status() {
if ! _ssh pactl list modules | grep -Fq "Name: module-native-protocol-tcp" ; then
errors+=("PulseAudio module \"module-native-protocol-tcp\" is not loaded on remote host $remote_ip.")
rv=1
else
debug "PulseAudio module \"module-native-protocol-tcp\" is loaded on remote host $remote_ip."
fi
if "$outbound" ; then
if ! pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then
errors+=("PulseAudio module \"module-tunnel-sink\" is not loaded.")
rv=1
else
debug "PulseAudio module \"module-tunnel-sink\" is loaded."
fi
if ! pactl get-default-sink | grep -Fq "tunnel-sink.tcp:127.0.0.1" ; then
errors+=("\"tunnel-sink.tcp:127.0.0.1\" is not the default PulseAudio sink.")
rv=1
else
debug "\"tunnel-sink.tcp:127.0.0.1\" is the default PulseAudio sink."
fi
fi
@ -267,8 +251,6 @@ do_status() {
if ! pactl list modules | grep -Fq "Name: module-tunnel-source" ; then
errors+=("PulseAudio module \"module-tunnel-source\" is not loaded.")
rv=1
else
debug "PulseAudio module \"module-tunnel-source\" is loaded."
fi
fi
@ -281,20 +263,9 @@ do_status() {
return "$rv"
}
# Use PulseAudio cookie from --cookie option value or remote host
# Acquire PulseAudio cookie from remote host
sync_pa_cookie() {
if ! [[ -d ~/.config/pulse ]] || ! [[ -w ~/.config/pulse ]] ; then
error "Local directory ~/.config/pulse does not exist or is not writeable."
return 1
elif [[ -n $cookie ]] ; then
if echo "$cookie" | base64 -d > ~/.config/pulse/cookie ; then
debug "Using PulseAudio cookie value as passed on the command line ..."
return 0
else
error "Decoding Base64 value of option --cookie failed."
return 1
fi
elif _scp "$remote_user"@"$remote_ip":.config/pulse/cookie ~/.config/pulse/cookie ; then
if _scp "$remote_user"@"$remote_ip":.config/pulse/cookie ~/.config/pulse/cookie ; then
debug "Synced PulseAudio cookie from remote host $remote_ip."
return 0
else
@ -475,27 +446,10 @@ do_stop() {
##
# Arguments
operations=()
cookie=
no_more_options=false
# shellcheck disable=SC2034
while [[ $# -gt 0 ]] ; do
arg=$1
shift
operation=
for arg in "$@" ; do
case "$arg" in
--)
no_more_options=true
;;
--*)
"$no_more_options" && { gui=false error "Option arguments may not preceed non-option arguments." ; exit 1 ; }
;;&
--cookie)
[[ $# -eq 0 ]] && { gui=false error "Missing argument for option \"--cookie\"" ; exit 1 ; }
cookie=$1
shift
;;
--debug)
debug_cmdline=true
;;
@ -506,10 +460,11 @@ while [[ $# -gt 0 ]] ; do
help_cmdline=true
;;
start|stop|restart|setup|status)
operations+=("$arg")
[[ -z $operation ]] || { error "Multiple operations are not supported." ; exit 1 ; }
operation=$arg
;;
*)
gui=false error "Unsupported argument (try \"--help\")"
error "Unsupported argument (try \"--help\")"
exit 1
;;
esac
@ -518,16 +473,6 @@ done
##
# Configuration
_ssh_arguments=(
-o
PasswordAuthentication=no
)
if [[ -n $PULSEAUDIO_TCP_SSH_ADDARGS ]] ; then
read -r -a _ssh_additional_arguments <<< "$PULSEAUDIO_TCP_SSH_ADDARGS"
_ssh_arguments+=("${_ssh_additional_arguments[@]}")
fi
config_dir="$HOME"/.config/pulseaudio-tcp
config=$config_dir/config.inc.sh
@ -544,15 +489,6 @@ fi
if [[ $gui_cmdline = false ]] ; then
gui=false
elif [[ -z $(type -p zenity) ]] ; then
gui=false
warning "Disabling GUI support, because command \"zenity\" was not found."
elif ! [[ -v DISPLAY ]] && ! [[ -v XDG_SESSION_TYPE ]] ; then
gui=false
warning "Disabling GUI support, because neither \"DISPLAY\" not \"XDG_SESSION_TYPE\ is set."
elif [[ $XDG_SESSION_TYPE = tty ]] ; then
gui=false
warning "Disabling GUI support, because \"XDG_SESSION_TYPE\ is set to \"tty\"."
else
gui=true
fi
@ -560,46 +496,46 @@ fi
##
# Main Program
for operation in "${operations[@]}" ; do
rv=0
rv=0
if [[ "$operation" != setup ]] ; then
if ! test -e "$config" ; then
error "Configfile $config does not exist (use \"$0 setup\" first)."
rv=1
elif ! test -r "$config" ; then
error "Configfile $config is not readable."
rv=1
elif ! test -f "$config" ; then
error "Configfile $config is not a regular file."
rv=1
else
. "$config"
if test "$operation" = setup ; then
info "Entering setup mode ..."
else
if ! test -e "$config" ; then
error "Configfile $config does not exist (use \"$0 setup\" first)."
rv=1
elif ! test -r "$config" ; then
error "Configfile $config is not readable."
rv=1
elif ! test -f "$config" ; then
error "Configfile $config is not a regular file."
rv=1
else
. "$config"
if [[ -z $remote_ip ]] ; then
error "\"remote_ip=<IP address>\" not set in configfile $config."
rv=1
elif [[ -z $remote_user ]] ; then
error "\"remote_user=<username>\" not set in configfile $config."
rv=1
fi
if [[ -z $remote_ip ]] ; then
error "\"remote_ip=<IP address>\" not set in configfile $config."
rv=1
elif [[ -z $remote_user ]] ; then
error "\"remote_user=<username>\" not set in configfile $config."
rv=1
fi
required_cmds=( jq pactl ssh )
for exe in "${required_cmds[@]}" ; do
if [[ -z $(type -p "$exe") ]] ; then
error "Required executable \"$exe\" not found."
rv=1
fi
done
fi
if [[ $rv -ne 0 ]] ; then
error "Preliminary checks failed, skipping operation."
break
fi
required_cmds=( jq pactl ssh )
"$gui" && required_cmds+=( zenity )
for exe in "${required_cmds[@]}" ; do
if [[ -z $(type -p "$exe") ]] ; then
error "Required executable \"$exe\" not found."
rv=1
fi
done
fi
if [[ $rv -ne 0 ]] ; then
error "Preliminary checks failed, skipping operation."
else
case "$operation" in
setup)
do_setup
@ -627,8 +563,6 @@ for operation in "${operations[@]}" ; do
rv=1
;;
esac
[[ $rv -ne 0 ]] && break
done
fi
exit "$rv"

View file

@ -1,27 +1,34 @@
_pulseaudio_tcp_completions() {
local \
command_list \
command_pattern \
commands \
cur \
option_list \
options \
word
options=( "--debug" "--help" "--no-gui" "--" )
options=( "--debug" "--help" "--nogui" )
commands=( "start" "stop" "status" "setup" "restart" )
cur=${COMP_WORDS[COMP_CWORD]}
more_options=true
for word in "${COMP_WORDS[@]}" ; do
[[ $word = "$cur" ]] && continue
[[ $word = -- ]] && more_options=false
options=("${options[@]/$word}")
commands=("${commands[@]/$word}")
done
"$more_options" && option_list="${options[*]}"
command_list="${commands[*]}"
option_list="${options[*]}"
printf -v command_pattern "%s|" "${commands[@]}"
command_pattern="(${command_pattern%?})"
if [[ ${COMP_WORDS[*]} =~ $command_pattern ]] ; then
command_list=""
else
command_list="${commands[*]}"
fi
mapfile -t COMPREPLY < <( compgen -W "$option_list $command_list" -- "$cur")