# bash completion for Psysonic (see `psysonic --help`). # Install: source /path/to/completions/psysonic.bash # Optional: jq + prior `psysonic --player audio-device list` for device name completion. # # Uses no `mapfile` so bash 3.2 (macOS default) works. # # compopt is bash-only (programmable completion). Guard so sourcing this file # under zsh or plain sh does not print "command not found: compopt". _psysonic_compopt() { command -v compopt &>/dev/null || return 0 compopt "$@" 2>/dev/null || return 0 } _psysonic_compreply_from_compgen() { # $1 = compgen -W word list, $2 = current word COMPREPLY=() local line while IFS= read -r line; do [[ -n $line ]] && COMPREPLY+=("$line") done < <(compgen -W "$1" -- "$2") } _psysonic_audio_device_json() { local f if [[ -n ${XDG_RUNTIME_DIR:-} ]]; then f="$XDG_RUNTIME_DIR/psysonic-cli-audio-devices.json" [[ -r $f ]] && { printf '%s' "$f"; return; } fi f="${TMPDIR:-/tmp}/psysonic-cli-audio-devices.json" [[ -r $f ]] && printf '%s' "$f" } _psysonic_library_json() { local f if [[ -n ${XDG_RUNTIME_DIR:-} ]]; then f="$XDG_RUNTIME_DIR/psysonic-cli-library.json" [[ -r $f ]] && { printf '%s' "$f"; return; } fi f="${TMPDIR:-/tmp}/psysonic-cli-library.json" [[ -r $f ]] && printf '%s' "$f" } _psysonic_snapshot_json() { local f if [[ -n ${XDG_RUNTIME_DIR:-} ]]; then f="$XDG_RUNTIME_DIR/psysonic-cli-snapshot.json" [[ -r $f ]] && { printf '%s' "$f"; return; } fi f="${TMPDIR:-/tmp}/psysonic-cli-snapshot.json" [[ -r $f ]] && printf '%s' "$f" } _psysonic_complete() { local cur cur="${COMP_WORDS[COMP_CWORD]}" local prev="" (( COMP_CWORD > 0 )) && prev="${COMP_WORDS[COMP_CWORD-1]}" if [[ $prev == --tail ]]; then _psysonic_compopt -o default COMPREPLY=() return fi local i pidx=0 for (( i = 1; i < COMP_CWORD; i++ )); do [[ ${COMP_WORDS[i]} == --player ]] && pidx=$i done if (( pidx == 0 )); then local has_logs=0 for (( i = 1; i < COMP_CWORD; i++ )); do [[ ${COMP_WORDS[i]} == --logs ]] && has_logs=1 done if [[ ${COMP_WORDS[1]} == completions && COMP_CWORD -eq 2 ]]; then _psysonic_compreply_from_compgen 'help bash zsh' "$cur" return fi if (( has_logs )); then _psysonic_compreply_from_compgen '--tail -f --follow' "$cur" else _psysonic_compreply_from_compgen '--help --version --info --json --quiet --logs --player completions' "$cur" fi return fi local -a sub=() for (( i = pidx + 1; i < COMP_CWORD; i++ )); do sub+=("${COMP_WORDS[i]}") done local n=${#sub[@]} if (( n == 0 )); then _psysonic_compreply_from_compgen 'next prev play pause stop seek volume shuffle repeat mute unmute star unstar rating reload audio-device library server search mix' "$cur" return fi case ${sub[0]} in audio-device) if (( n == 1 )); then _psysonic_compreply_from_compgen 'list set' "$cur" elif [[ ${sub[1]} == set ]] && (( n == 2 )); then COMPREPLY=() local jf d jf="$(_psysonic_audio_device_json)" if [[ -n $jf ]] && command -v jq &>/dev/null; then while IFS= read -r d; do [[ -n $d && $d == "$cur"* ]] && COMPREPLY+=("$d") done < <(jq -r '.devices[]? | select(type == "string")' "$jf" 2>/dev/null) fi while IFS= read -r line; do [[ -n $line ]] && COMPREPLY+=("$line") done < <(compgen -W 'default' -- "$cur") ((${#COMPREPLY[@]})) && _psysonic_compopt -o filenames fi ;; library) if (( n == 1 )); then _psysonic_compreply_from_compgen 'list set' "$cur" elif [[ ${sub[1]} == set ]] && (( n == 2 )); then COMPREPLY=() local jf id line jf="$(_psysonic_library_json)" if [[ -n $jf ]] && command -v jq &>/dev/null; then while IFS= read -r id; do [[ -n $id && $id == "$cur"* ]] && COMPREPLY+=("$id") done < <(jq -r '.folders[]? | select(.id != null) | .id | tostring' "$jf" 2>/dev/null) fi while IFS= read -r line; do [[ -n $line ]] && COMPREPLY+=("$line") done < <(compgen -W 'all' -- "$cur") ((${#COMPREPLY[@]})) && _psysonic_compopt -o filenames fi ;; mix) (( n == 1 )) && _psysonic_compreply_from_compgen 'append new' "$cur" ;; server) if (( n == 1 )); then _psysonic_compreply_from_compgen 'list set' "$cur" elif [[ ${sub[1]} == set ]] && (( n == 2 )); then COMPREPLY=() local jf sid jf="$(_psysonic_snapshot_json)" if [[ -n $jf ]] && command -v jq &>/dev/null; then while IFS= read -r sid; do [[ -n $sid && $sid == "$cur"* ]] && COMPREPLY+=("$sid") done < <(jq -r '.servers[]? | select(.id != null) | .id | tostring' "$jf" 2>/dev/null) fi ((${#COMPREPLY[@]})) && _psysonic_compopt -o filenames fi ;; search) if (( n == 1 )); then _psysonic_compreply_from_compgen 'track album artist' "$cur" elif (( n >= 2 )); then _psysonic_compopt -o default COMPREPLY=() fi ;; repeat) (( n == 1 )) && _psysonic_compreply_from_compgen 'off all one' "$cur" ;; rating) (( n == 1 )) && _psysonic_compreply_from_compgen '0 1 2 3 4 5' "$cur" ;; seek) if (( n == 1 )); then _psysonic_compopt -o default COMPREPLY=() fi ;; volume) if (( n == 1 )); then _psysonic_compreply_from_compgen '+5 +10 -5 -10 50' "$cur" fi ;; play) if (( n == 1 )); then _psysonic_compopt -o default COMPREPLY=() fi ;; esac } complete -F _psysonic_complete psysonic