The DIY Music Streamer Part 4.



Time to polish our basic installation of shairport-sync and Mopidy a bit. Mopidy is a great music server and extensible platform. It got a builtin web-server but it is not primarily a web-server. It is a bit crude to access our web-client through a port like 6680 and a root relative path like "/iris/".

It would be nice to access it on port 80 and the root path "/" instead. For that we can use a reverse proxy. It would also be nice if we could offload Mopidy a bit serving static files from a cache. For that we can use a cache server. Luckily we can have both in a single package like NGINX.

Logon to your Raspberry Pi and install NGINX with the command:

sudo apt install nginx

Enable it and restart it with the commands:

sudo systemctl enable nginx
sudo systemctl restart nginx


You should now be served with the NGINX default page if you browse to http://192.168.1.10

Great we got a web-server installed! Now create a directory for the cache with the following commands:

sudo mkdir /var/cache/nginx
sudo chown root:www-data /var/cache/nginx/


Next, remove the default NGINX page:

sudo rm /etc/nginx/sites-enabled/default

And open a new one for editing:

sudo nano /etc/nginx/sites-enabled/mopidy

Add the following text, save and exit with Ctrl+X then Y.

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:1m max_size=1g inactive=60m use_temp_path=off;

server {
        listen 80;

        server_name  _;

        proxy_http_version 1.1;
        proxy_read_timeout 600s;

        location = / {
                return 301 /iris/;
        }

        location / {
                proxy_cache my_cache;
                proxy_cache_revalidate on;
                proxy_cache_min_uses 1;
                proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
                proxy_cache_background_update on;
                proxy_cache_lock on;
                proxy_pass http://127.0.0.1:6680/;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $http_connection;
        }

}


I will not go into detail what all of these lines do. There are tons of NGINX tutorials online.

Restart NGINX with the following command:

sudo systemctl restart nginx

You should now be able to access Mopidy Iris from http://192.168.1.10

You will probably get an error message from Iris about not being able to connect to Mopidy. The root cause is the last couple of lines in the [http] section of the Mopidy configuration file (marked in red):

[http]
enabled = true
hostname = 127.0.0.1
port = 6680
zeroconf = Mopidy HTTP server on $hostname
allowed_origins = 127.0.0.1
csrf_protection = true


Mopidy http is bound to 127.0.0.1 (local host) and that is great from a security perspective. Your browser is accessing NGINX from an interface bound to 192.168.1.10 and the line allowed_origins = 127.0.0.1 is telling Mopidy to only allow access requests from 127.0.0.1. There are two ways to fix this problem. Either change allowed_origins to your streamers address (192.168.1.10 in this example) and keep csrf_protection = true (witch is also good from a security perspective) or change csrf_protection to false. It will disable cross-site request forgery protection. It is less secure but maybe not much of a problem as long as you plan to only access Mopidy from your local network.

Open the configuration file for editing:
  
sudo nano /etc/mopidy/mopidy.conf

And change the [http] section to something like:

[http]
enabled = true
hostname = 127.0.0.1
port = 6680
zeroconf = Mopidy HTTP server on $hostname
allowed_origins = 192.168.1.10
csrf_protection = true


Save and exit with Ctrl+X, then Y and restart Mopidy: 

sudo systemctl restart mopidy

The next problemt that needs to be addressed is the fact that this Raspberry Pi is serving two music streaming services at the same time, AirPlay trough shairport-sync and Spotify + local media trough Mopidy. What if Mopidy is playing while someone is trying to connect to the AirPlay Reciever? The way I personally want it is to be able to interrupt any AirPlay session with a new one. I also want any Mopidy playback to be stopped if an AirPlay session is started.

Shairport-sync got settings for running scripts when a session is started and stopped but we need a way to control Mopidy from the command line. I will use the builtin support for the MPD protocol in combination with the mpc command line tool.

Install mpc with the following command:

sudo apt install npc

Open the Mopidy configuration file for editing and add the following section:

[mpd]
enabled = true
hostname = 127.0.0.1
port = 6600
password =
max_connections = 10
connection_timeout = 10
zeroconf = Mopidy MPD server on $hostname
command_blacklist = listall,listallinfo


Save and exit with Ctrl+X, then Y and restart Mopidy:

sudo systemctl restart mopidy

You should now be able to stop Mopidy playback from the command line with the command:

mpc stop

Next up is shairport-sync, create a directory to hold our scripts:

sudo mkdir /etc/shairport

Create a start script and open it for editing:

sudo nano /etc/shairport/shairport-start.sh

Add the following text, save and exit with Ctrl+X, then Y.

#!/bin/sh
mpc stop
exit 0


Create a stop script and open it for editing:

sudo nano /etc/shairport/shairport-stop.sh

Add the following text, save and exit with Ctrl+X, then Y.

#!/bin/sh
mpc volume 20
exit 0


It will set volume to 20 % by Mopidy when the session is stopped. I like to bring it down so I don't get an unpleasant surprise if I start Mopidy again it previously was blasting at full volume. Only your imagination is the limit for what you can put into these scripts. You finally have to make the scripts executable with the following command:

sudo chmod +x /etc/shairport/*

Next up is the shairport-sync configuration file, open it for editing:

sudo nano /etc/shairport-sync.conf

Make sure the following text is added to the configuration:

sessioncontrol = 
{
        run_this_before_play_begins = "/etc/shairport/shairport-start.sh";
        run_this_after_play_ends = "/etc/shairport/shairport-stop.sh";
        wait_for_completion = "yes";
        allow_session_interruption = "yes";
        session_timeout = 10;
}; 


The last two lines will make it possible to interrupt an ongoing session with a new one and it will timeout any inactive session after 10 seconds. Restart shairport-sync with the following command:

sudo systemctl restart shairport-sync

This system is now pretty much done from a functional point of view but we are not done yet. My next post will be about things like input level matching and the difference between software and hardware volume control.