Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions scripts/helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -96,22 +96,46 @@ get_nowplaying_status_icon() {
}

# Parse adapter output.
# Preferred adapter format: status<TAB>artist<TAB>title.
# Plain text remains supported as playing metadata for compatibility.
# Adapter format: status<TAB>artist<TAB>title.
parse_nowplaying_adapter_output() {
local adapter_output="$1"
local status
local artist
local title
local text

if [[ "$adapter_output" == *$'\t'* ]]; then
IFS=$'\t' read -r status artist title <<< "$adapter_output"
text="$(format_nowplaying_metadata "$artist" "$title")"
printf '%s\t%s\n' "${status:-Playing}" "$text"
else
printf 'Playing\t%s\n' "$adapter_output"
local adapter_output_value="$1"
local status_output_name="$2"
local artist_output_name="$3"
local title_output_name="$4"
local parsed_status
local adapter_remainder
local parsed_artist
local parsed_title

printf -v "$status_output_name" '%s' ''
printf -v "$artist_output_name" '%s' ''
printf -v "$title_output_name" '%s' ''

case "$adapter_output_value" in
*$'\n'*|*$'\r'*) return 1 ;;
esac

if [[ "$adapter_output_value" != *$'\t'* ]]; then
return 1
fi

parsed_status="${adapter_output_value%%$'\t'*}"
adapter_remainder="${adapter_output_value#*$'\t'}"

if [[ -z "$parsed_status" || "$adapter_remainder" != *$'\t'* ]]; then
return 1
fi

parsed_artist="${adapter_remainder%%$'\t'*}"
parsed_title="${adapter_remainder#*$'\t'}"

if [[ "$parsed_title" == *$'\t'* ]]; then
return 1
fi

printf -v "$status_output_name" '%s' "$parsed_status"
printf -v "$artist_output_name" '%s' "$parsed_artist"
printf -v "$title_output_name" '%s' "$parsed_title"
}

# Get the tmux status interval to restore after temporary scrolling changes.
Expand Down
12 changes: 8 additions & 4 deletions scripts/nowplaying.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@ case "$(uname -s)" in
;;
esac

playback_status=""
track_artist=""
track_title=""
track_text=""

# If we got output, process and display it
if [ -n "$output" ]; then
parsed_output="$(parse_nowplaying_adapter_output "$output")"
IFS=$'\t' read -r playback_status track_text <<< "$parsed_output"
if [ -n "$output" ] && parse_nowplaying_adapter_output "$output" playback_status track_artist track_title; then
track_text="$(format_nowplaying_metadata "$track_artist" "$track_title")"
fi

if [ -n "${track_text:-}" ]; then
if [ -n "$track_text" ]; then
status_icon="$(get_nowplaying_status_icon "$playback_status")"

# Check if scrolling is needed
Expand Down
64 changes: 62 additions & 2 deletions scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,41 @@ assert_eq() {
printf 'ok - %s\n' "$message"
}

assert_adapter_record() {
local adapter_output="$1"
local expected_status="$2"
local expected_artist="$3"
local expected_title="$4"
local message="$5"
local playback_status="stale"
local track_artist="stale"
local track_title="stale"

if ! parse_nowplaying_adapter_output "$adapter_output" playback_status track_artist track_title; then
fail "${message} was rejected"
fi

assert_eq "$expected_status" "$playback_status" "${message} status"
assert_eq "$expected_artist" "$track_artist" "${message} artist"
assert_eq "$expected_title" "$track_title" "${message} title"
}

assert_adapter_rejected() {
local adapter_output="$1"
local message="$2"
local playback_status="stale"
local track_artist="stale"
local track_title="stale"

if parse_nowplaying_adapter_output "$adapter_output" playback_status track_artist track_title; then
fail "${message} was accepted"
fi

assert_eq "" "$playback_status" "${message} clears status"
assert_eq "" "$track_artist" "${message} clears artist"
assert_eq "" "$track_title" "${message} clears title"
}

write_tmux_mock() {
cat > "${TMP_DIR}/tmux" <<'MOCK'
#!/usr/bin/env bash
Expand Down Expand Up @@ -117,6 +152,17 @@ MOCK
' ;;
'-p paused metadata title') printf 'Track
' ;;
MOCK
;;
title_only)
cat >> "${TMP_DIR}/playerctl" <<'MOCK'
'-l') printf 'title-only
' ;;
'-p title-only status') printf 'Playing
' ;;
'-p title-only metadata artist') printf '' ;;
'-p title-only metadata title') printf 'Title
' ;;
MOCK
;;
stopped)
Expand Down Expand Up @@ -259,8 +305,18 @@ assert_eq "value " "$(resolve_with_mock @test_trailing_spaces fallback)" "mock
helper_output="$(PATH="${TMP_DIR}:${PATH}" bash -c 'source "$1"; printf "%s %s %s %s\n" "$(get_tmux_integer_option @bad_integer 50 4)" "$(get_tmux_integer_option @empty_integer 50 4)" "$(get_tmux_integer_option @low_integer 50 4)" "$(get_tmux_integer_option @high_integer 1 1 10)"' _ "${ROOT_DIR}/scripts/helpers.sh")"
assert_eq "50 50 4 10" "$helper_output" "integer option validation"

parse_output="$(PATH="${TMP_DIR}:${PATH}" bash -c 'source "$1"; parse_nowplaying_adapter_output $'"'"'Paused\tArtist\tTitle'"'"'' _ "${ROOT_DIR}/scripts/helpers.sh")"
assert_eq $'Paused\tArtist - Title' "$parse_output" "adapter output parsing"
# shellcheck source=scripts/helpers.sh
source "${ROOT_DIR}/scripts/helpers.sh"
assert_adapter_record $'Paused\tArtist\tTitle' "Paused" "Artist" "Title" "complete adapter record"
assert_adapter_record $'Playing\t\tTitle' "Playing" "" "Title" "title-only adapter record"
assert_adapter_record $'Paused\tArtist\t' "Paused" "Artist" "" "artist-only adapter record"
assert_adapter_record $'Stopped\t\t' "Stopped" "" "" "empty metadata adapter record"
assert_adapter_rejected "plain text" "plain-text adapter output"
assert_adapter_rejected $'Playing\tTitle' "one-tab adapter output"
assert_adapter_rejected $'Playing\tArtist\tTitle\tExtra' "extra-tab adapter output"
assert_adapter_rejected $'\tArtist\tTitle' "empty-status adapter output"
assert_adapter_rejected $'Playing\tArtist\tTitle\nSecond' "multiline adapter output"
assert_adapter_rejected $'Playing\tArtist\tTitle\r' "carriage-return adapter output"

write_uname_mock

Expand All @@ -271,6 +327,10 @@ assert_eq "♪ Artist - Title" "$(run_with_mocks "${ROOT_DIR}/scripts/nowplaying
write_playerctl_mock paused
assert_eq "⏸ Old - Track" "$(run_with_mocks "${ROOT_DIR}/scripts/nowplaying.sh")" "main renders paused metadata"

write_playerctl_mock title_only
assert_eq $'Playing\t\tTitle' "$(run_with_mocks "${ROOT_DIR}/scripts/nowplaying_linux.sh")" "linux adapter preserves empty artist"
assert_eq "♪ Title" "$(run_with_mocks "${ROOT_DIR}/scripts/nowplaying.sh")" "main renders title-only metadata"

write_playerctl_mock stopped
assert_eq "⏹ Done - Song" "$(run_with_mocks "${ROOT_DIR}/scripts/nowplaying.sh")" "main renders stopped metadata"

Expand Down
Loading