Compare commits
10 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
03e3953eb8 | ||
![]() |
f08b857aff | ||
![]() |
f78ae9d183 | ||
![]() |
4c7b600c68 | ||
![]() |
e32fb0a9d2 | ||
![]() |
01dffe289a | ||
![]() |
6c9e822ccf | ||
![]() |
d93979c527 | ||
![]() |
43f4bd632b | ||
![]() |
67cbc09f43 |
3 changed files with 128 additions and 68 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@ debian/debhelper-build-stamp
|
|||
debian/files
|
||||
debian/pulseaudio-tcp.substvars
|
||||
debian/pulseaudio-tcp
|
||||
*.sw*
|
||||
|
|
178
pulseaudio-tcp
178
pulseaudio-tcp
|
@ -22,16 +22,24 @@
|
|||
|
||||
usage() {
|
||||
cat >&2 <<EOF
|
||||
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.
|
||||
# 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\`
|
||||
EOF
|
||||
}
|
||||
|
||||
|
@ -44,7 +52,7 @@ log() {
|
|||
|
||||
if [[ -t 1 ]] && $gui && [[ -n $(type -p zenity) ]] ; then
|
||||
case "$level" in
|
||||
ERROR)
|
||||
ERROR|WARNING)
|
||||
zenity --error --text="$msg"
|
||||
;;
|
||||
*)
|
||||
|
@ -58,20 +66,22 @@ error() {
|
|||
log ERROR "$@"
|
||||
}
|
||||
|
||||
warning() {
|
||||
log WARNING "$@"
|
||||
}
|
||||
|
||||
info() {
|
||||
log INFO "$@"
|
||||
}
|
||||
|
||||
debug() {
|
||||
if "$debug" ; then
|
||||
log DEBUG "$@"
|
||||
else
|
||||
gui=false log DEBUG "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
_ssh() {
|
||||
if ssh -o PasswordAuthentication=no -S "$USER"-pulseaudio "$remote_user"@"$remote_ip" "$@" ; then
|
||||
if ssh "${_ssh_arguments[@]}" -S "$USER"-pulseaudio "$remote_user"@"$remote_ip" "$@" ; then
|
||||
return 0
|
||||
else
|
||||
error "SSH remote_ip=$remote_ip failed."
|
||||
|
@ -233,17 +243,23 @@ 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
|
||||
|
||||
|
@ -251,6 +267,8 @@ 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
|
||||
|
||||
|
@ -263,9 +281,20 @@ do_status() {
|
|||
return "$rv"
|
||||
}
|
||||
|
||||
# Acquire PulseAudio cookie from remote host
|
||||
# Use PulseAudio cookie from --cookie option value or remote host
|
||||
sync_pa_cookie() {
|
||||
if _scp "$remote_user"@"$remote_ip":.config/pulse/cookie ~/.config/pulse/cookie ; then
|
||||
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
|
||||
debug "Synced PulseAudio cookie from remote host $remote_ip."
|
||||
return 0
|
||||
else
|
||||
|
@ -446,10 +475,27 @@ do_stop() {
|
|||
##
|
||||
# Arguments
|
||||
|
||||
operation=
|
||||
operations=()
|
||||
cookie=
|
||||
no_more_options=false
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
while [[ $# -gt 0 ]] ; do
|
||||
arg=$1
|
||||
shift
|
||||
|
||||
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
|
||||
;;
|
||||
|
@ -460,11 +506,10 @@ for arg in "$@" ; do
|
|||
help_cmdline=true
|
||||
;;
|
||||
start|stop|restart|setup|status)
|
||||
[[ -z $operation ]] || { error "Multiple operations are not supported." ; exit 1 ; }
|
||||
operation=$arg
|
||||
operations+=("$arg")
|
||||
;;
|
||||
*)
|
||||
error "Unsupported argument (try \"--help\")"
|
||||
gui=false error "Unsupported argument (try \"--help\")"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
@ -473,6 +518,16 @@ 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
|
||||
|
||||
|
@ -489,6 +544,15 @@ 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
|
||||
|
@ -496,46 +560,46 @@ fi
|
|||
##
|
||||
# Main Program
|
||||
|
||||
rv=0
|
||||
for operation in "${operations[@]}" ; do
|
||||
rv=0
|
||||
|
||||
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 [[ "$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 [[ -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
|
||||
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
|
||||
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
|
||||
|
||||
required_cmds=( jq pactl ssh )
|
||||
"$gui" && required_cmds+=( zenity )
|
||||
if [[ $rv -ne 0 ]] ; then
|
||||
error "Preliminary checks failed, skipping operation."
|
||||
break
|
||||
fi
|
||||
|
||||
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
|
||||
|
@ -563,6 +627,8 @@ else
|
|||
rv=1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
[[ $rv -ne 0 ]] && break
|
||||
done
|
||||
|
||||
exit "$rv"
|
||||
|
|
|
@ -1,34 +1,27 @@
|
|||
_pulseaudio_tcp_completions() {
|
||||
local \
|
||||
command_list \
|
||||
command_pattern \
|
||||
commands \
|
||||
cur \
|
||||
option_list \
|
||||
options \
|
||||
word
|
||||
|
||||
options=( "--debug" "--help" "--nogui" )
|
||||
options=( "--debug" "--help" "--no-gui" "--" )
|
||||
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
|
||||
|
||||
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
|
||||
"$more_options" && option_list="${options[*]}"
|
||||
command_list="${commands[*]}"
|
||||
|
||||
mapfile -t COMPREPLY < <( compgen -W "$option_list $command_list" -- "$cur")
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue