This is going to be a "quick" little guide to customizing the tmux status bar to include a scrolling Spotify “Now Playing” widget. I’m going to be doing this on macOS, but you should be able to adapt it to Linux and I'll try to assist where I can. If you’re on Windows… I have no idea. Here are the tools we’re going to use:
tmux - Kind of obvious. I’m assuming you already have this.
spotify-tui - We’ll mostly just be using this for the
spt playback
CLI command to get the playback info.spotifyd- We need this for the “song changed” hook and so we don’t need the Spotify app open.
bash - You already know.
homebrew - Skip this if you’re not on macOS. I’ll be using it to download all of these tools and also to launch the
spotifyd
daemon. Again, I’m going to assume you already have this.Spotify - You'll probably need this, eh? I'm pretty sure you need Spotify Premium for this to work, but I'm not positive.
Prerequisites
Before we get started, go make a Spotify developer account if you don’t already have one. Then create a new app in the developer dashboard. The name and description don’t matter, but make sure you add http://localhost:8888/callback
to the "Redirect URIs" section - spotify-tui requires this and we’ll revisit it later.
Installing the Tools
Now let’s get our tools. If you're on macOS, use the following command:
brew install spotify-tui
brew install spotifyd
For Linux users, you can install the tools using your package manager or by following the instructions on their respective GitHub repositories (spotify-tui and spotifyd).
Creating the Script
Now we can get a little crazy with some bash scripting. We’re going to gloss over this, but essentially it uses the spt playback
subcommand from spotify-tui to get the current song in a specified format, then uses the current date in seconds to calculate the scroll position if the playback info is longer than a certain number of characters. It does some tempfile file things to store the state and saves us from calling the Spotify API too many times.
Save this script anywhere you want but let’s name it spt_status.sh
.
#!/usr/bin/env bash
tmpdir="$TMPDIR/spt_status"
data_file="$tmpdir/current.txt"
if [ ! -d "$tmpdir" ]; then
printf "\rInitializing..."
# create the directory
mkdir "$tmpdir"
# initialize the data file
echo "null" >"$data_file"
echo "false" >>"$data_file"
printf "\rInitialized successfully!"
exit 0
fi
# the first line of the data file is the output of the last run
output=$(head -n 1 "$data_file" 2>/dev/null)
# the second line is whether or not we've already updated
updated=$(tail -n 1 "$data_file" 2>/dev/null)
if [ "$1" = "--update" ]; then
printf "\rUpdating..."
# only get the playback information if we haven't already updated
if [ "$updated" != "true" ]; then
# write the playback information to the data file
echo "null" >"$data_file"
# write that we've updated to the data file
echo "true" >>"$data_file"
fi
exit 0
fi
# get the output from the data file
output=$(head -n 1 "$data_file" 2>/dev/null)
# if the output is null, get the playback information
if [ "$output" = "null" ]; then
# format options:
# %a: artist, %b: album, %p: playlist, %t: track, %h: show,
# %f: flags (shuffle, repeat, like), %s: playback status, %v: volume, %d: current device
# get the playback information
pb=$(/usr/local/bin/spt playback --format "%s %a: %t" 2>/dev/null || echo "null")
# write the playback information to the data file
echo "$pb" >"$data_file"
# write that we haven't updated to the data file
echo "false" >>"$data_file"
# get the output from the data file
output=$(head -n 1 "$data_file" 2>/dev/null)
fi
# remove all newlines from the output
output=$(echo "$output" | tr -d '\n')
# max length of the output
max_length=35
# the speed at which the text should scroll (characters per second)
scroll_speed=10
if [ "$output" = "null" ]; then
# if the output is null, print an error message and exit
printf "\rSomething went wrong..."
exit 1
fi
if [ ${#output} -gt $max_length ]; then
# get the current time in seconds
current_time=$(date +%s)
# first character is the play/pause symbol
play_pause_symbol="${output:0:2}"
# the rest of the output is the track information
output=${output:2}
# calculate the scroll position based on the current time and scroll speed
scroll_position=$((current_time * scroll_speed % (${#output} + max_length)))
# create a padded version of the output with spaces
padded_output="${output}$(printf '%*s' $((max_length)) '')${output}"
# extract the substring to display based on the scroll position
displayed_output="${padded_output:$scroll_position:(($max_length - 2))}"
printf "\r%s%s " "$play_pause_symbol" "$displayed_output"
exit 0
fi
padded_output="${output}$(printf '%*s' $((max_length + 1 - ${#output})) '')"
printf "\r%s" "$padded_output"
Make sure you run chmod +x spt_status.sh
to make it executable.
Configuring the Daemon
Now, we’re almost ready to see it in action, but there are a couple of things we need to do first.
For macOS users, we’ll make a small modification to the .plist file that brew
uses to launch the daemon. Open up /usr/local/Cellar/spotifyd/0.3.4/homebrew.mxcl.spotifyd.plist
(modify to match your version) and look for this section around line 17:
<key>ProgramArguments</key>
<array>
<string>/usr/local/opt/spotifyd/bin/spotifyd</string>
<string>--no-daemon</string>
<string>--backend</string>
<string>portaudio</string>
</array>
Add the following to the array after <string>portaudio</string>
:
<string>--on-song-change-hook</string>
<string>/path/to/spt_status.sh --update</string>
Note that this needs to be the full path to our script.
For Linux users, you can create a systemd service file to launch the daemon. Create a file named spotifyd.service
in ~/.config/systemd/user/
with the following content:
[Unit]
Description=Spotifyd Daemon
[Service]
ExecStart=/usr/bin/spotifyd --no-daemon --backend alsa --on-song-change-hook "/path/to/spt_status.sh --update"
Restart=always
RestartSec=12
[Install]
WantedBy=default.target
Replace /path/to/spt_status.sh
with the full path to the script you created earlier.
Launching the Daemon
From here, we can launch our daemon:
For macOS users:
brew services start spotifyd
For Linux users:
systemctl --user enable --now spotifyd.service
Configuring spotify-tui and tmux
We need to run spt
one time to configure it, so do that now and follow the messages, entering your Spotify app’s client and secret tokens when prompted. Then press d
and select spotifyd
from the devices list. If it’s not showing up in the list of available devices, you might need to open the official app and select “spotifyd” as your playback device. You can then close the Spotify app.
We’re almost there, I promise. All that’s left is to open up your tmux config (probably at ~/.tmux.conf) and make a couple of small tweaks:
# .tmux.conf
# ...rest of file
# update the status bar every second
set -g status-interval 1
# show our widget
set -g status-right ' #(spt_status.sh)'
# ...rest of file
Wrapping Up
Whew! We did it. If you have any open tmux sessions, exit them with tmux kill-server
and start up a new one. Play a song with either the official app or with spotify-tui, and you should see our new widget on the right.
If you liked this, let me know in the comments and give me a follow! Also, don’t hesitate to let me know if something isn’t working or if I overlooked anything.
I'm also on Twitter @techsavvytravvy and sometimes I stream on Twitch - come hang out!
Bye!