Streaming audio server with a Raspberry Pi 5 and Mopidy – Never Mind

Well, this is a bummer. The Iris interface on Mopidy is nice, and when I’m doing everything manually, it works fine. BUT, I wanted to do things with scripting. I can somewhat get things to work, but not reliably. So I’m going to give up on Mopidy and go back to MPD (Music Player Demon).

I’d managed to build a little routine that, once an hour, generates a sound file with the current time in it. I copied it to the right place, updated the file cache so that the new file is the current one, and then threw (using CURL) the command to add the file to the Mopidy queue. For testing, I had cron run it every two minutes.

# Add the current_time.mp3 file to the queue

/usr/bin/curl -s -X POST "$MOPIDY_URL" \
-H "Content-Type: application/json" \
-d '{
"method": "core.tracklist.add",
"params": {
"uris": ["'"$FILE_URI"'"],
"at_position": 1
},
"id": 1,
"jsonrpc": "2.0"
}'

But, if the queue is empty, Mopidy isn’t going to play anything. So then I had to add:

# Start playback

/usr/bin/curl -s -X POST "$MOPIDY_URL" \
-H "Content-Type: application/json" \
-d '{
"method": "core.playback.play",
"id": 2,
"jsonrpc": "2.0"
}'

And now I’ve three problems.

The first is that there is a weird loop problem where once: the file plays correctly. I’ll hear “The current time is 16:40.” The next time the cron job runs the bash script, I’ll hear “The current time is 16:42. The current time is 16:42.” The third time it runs, I’ll hear “The current time is 16:44. The current time is 16:44. The curren” and the playback is interrupted.

It is always in triples; 16:46 would play fine, 16:48 would play twice, and 16:50 would attempt to play three times but get cut off.

So that is one problem. The next is that I prefer to do my scripting in Perl, and I know that Perl has been doing JSON-RPC for a really long time now. But the Perl script I wrote doesn’t seem to be connecting to the Mopidy server. It tells me that it connected, and shows me a result (instead of a refusal to connect), but the Mopidy debug log does not acknowledge that a connection was made.

The third is that even when I add a song manually using the Iris interface, that “core.playback.play” method would break the playback of the currently playing song. The idea was that if I only create a current_time.mp3 file at the top of the hour, and add it to the queue, then within a few minutes, I’ll hear was hour it is. But now I’ve got the worst of both worlds: if the queue was empty, no playback of the current_time.mp3, but if I trigger the playback, the current song gets clobbered. Sigh.

I really liked that Mopidy has the nice Iris web client; but, if the only way to interact with the server is via mouse and keyboard, well then I’m out.

Streaming audio server with a Raspberry Pi 5 and Mopidy

My first attempt at an MPD streaming box was a tough config, because I was learning along the way, hitting every stumbling block there is. Getting the Music Player Demon running wasn’t terrible, but getting my various client machines to listen to the streaming server was tough. Okay, I am going to try again, and hopefully, it actually works with Mopidy this time.

The Iris plugin to Mopidy is nice. Previously I used Cantata, which was fine, but it was a local install on each machine. Iris is good because it runs on the server, and I don’t need to do a local install on each machine just to control the music stream.

On to the build!


I used the Raspberry Pi Imager program to put the Raspberry Pi OS Lite (64-bit) on the SD card. Then I booted the device, ssh’d in and ran

sudo apt update

followed by

sudo apt upgrade -y

followed by a sudo reboot now

sudo raspi-config

I’m changing the hostname. Also, because I prefer vim to nano:

sudo apt install vim -y
sudo update-alternatives --config editor

And of course, now I get to install my favorite aliases and history search keystrokes. These are detailed here.

Next, I’m going to follow the instructions at Mopidy Installation

I am following the section Install from apt.mopidy.com:

sudo mkdir -p /etc/apt/keyrings
sudo wget -q -O /etc/apt/keyrings/mopidy-archive-keyring.gpg https://apt.mopidy.com/mopidy.gpg
sudo wget -q -O /etc/apt/sources.list.d/mopidy.list https://apt.mopidy.com/bookworm.list

So one difference is that the instructions say to wget from https://apt.mopidy.com/bullseye.list but this version of Debian on the Raspberry Pi is bookworm (Debian 12 instead of 11). We’ll find out if I just shot myself in the foot. 😉

sudo apt update
sudo apt install mopidy -y
sudo apt install mopidy-mpd -y
sudo apt install mopidy-local -y
sudo apt install snapserver snapclient -y
sudo apt install python3-pip -y
sudo python3 -m pip install Mopidy-Iris --break-system-packages

The instructions I found say to simply do sudo python3 -m pip install Mopidy-Iris but Debian (or perhaps it is Python) barks at me with “error: externally-managed-environment – This environment is externally managed” and some potential way around the problem. That’s a nope: I want to run as a systemd service, and isolating stuff into unreachable environments is not the battle I wish to fight today.

And another thing: the Snapcast instructions assume that Snapweb is installed, but it never is. First, I downloaded it from https://github.com/badaix/snapweb/releases and picked the file snapweb.zip

Then I copied it to the Mopidy server, and did this:

sudo mkdir -p /usr/share/snapweb
sudo unzip snapweb.zip -d /usr/share/snapweb

Okay, I’m almost ready to start configuring things.

Later on down the line, the mopidy.conf file will consider that the files to serve up are at a certain location. What location? Here’s how to find out:

sudo mopidyctl config

and look in the [local] section for media_dir = /var/lib/mopidy/media

Now would be a great time to mount my music files via a CIFS / SMB share:

sudo apt install cifs-utils -y
touch /root/credentials.smb
vim /root/credentials.smb
username=epstein_did_not_kill_himself
password=Apparently-Child-Rape-Is-Okay-If-The-Deep-State-Does-It,-Because-With-The-Deep-State,-The-Ends-(Blackmail)-Justify-The-Means-(Child-Rape).--Everyone-Who-Is-Human-Recognizes-The-Evil,-But-The-People-Who-Make-Up-The-Deep-State-Are-Sociopaths.--Unfortunately,-The-People-In-The-Current-Administration-Who-Promised-To-Drain-The-Swamp-Have-Been-Broken,-Most-Likely-With-Threats-To-Their-Family.--What-A-World,-Amirite?
sudo chmod 600 /root/credentials.smb

Then we can edit /etc/fstab

//hostname/smbsharename/data /var/lib/mopidy/media cifs vers=3.0,credentials=/root/credentials.smb,_netdev,iocharset=utf8 0 0

I’m also going to try to keep from wearing out the SD card so quickly, so I’ll try this:

tmpfs   /var/cache/mopidy   tmpfs   size=1G,mode=1777   0 0

Okay, things I’ve changed in /etc/mopidy/mopidy.conf :

[http]
hostname = ::

[mpd]
hostname = ::

This was previously hostname = 127.0.0.1

I changed to listen on all interfaces (that’s what :: does) because I do want any of my machines on my local network to be able to interact with the Mopidy server. Note that I am planning on never putting this box on the public Internet.

There is another change I made in /etc/mopidy/mopidy.conf :


output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! filesink location=/tmp/snapfifo

Then I get to edit /etc/snapserver.conf:

sudo vim /etc/snapserver.conf
source = pipe:///tmp/snapfifo?name=Mopidy

I’m going to try to make sure Snapcast loads first, before Mopidy, because Snapcast needs to be writing into /tmp/snapfifo before Mopidy starts reading it.

sudo systemctl edit mopidy.service
[Unit]
After=snapserver.service
Requires=snapserver.service

To configure the Snapweb server:

sudo vim /etc/snapserver.conf

Find the line #doc_root = /usr/share/snapserver/snapweb and change it to:

doc_root = /usr/share/snapweb

And I think I am at the moment of truth:

sudo systemctl enable snapserver.service
sudo systemctl enable mopidy.service

sudo systemctl restart snapserver.service
sudo systemctl restart mopidy.service

If things are looking correctly, my next step is….

sudo mopidyctl local scan

Well, it almost worked right on the first try. I also needed to do this:

echo "fs.protected_fifos = 0" | sudo tee /etc/sysctl.d/snapcast-unprotect-fifo.conf
sudo sysctl --system

Hooray! My streaming music server is up and running within my local network. This was fun. 🙂