Skip to main content

Configuration

MPD

MPD's config lives at /etc/mpd.conf. Key settings:

music_directory "/mnt/music"
playlist_directory "/var/lib/mpd/playlists"
db_file "/var/lib/mpd/tag_cache"
user "mpd"
bind_to_address "0.0.0.0"
port "6600"

Audio output — Icecast stream:

audio_output {
type "shout"
name "Radio Stream"
host "localhost"
port "8000"
mount "/radio.mp3"
password "YOUR_ICECAST_SOURCE_PASSWORD"
format "44100:16:2"
encoding "mp3"
bitrate "128"
}

The password here must match the <source-password> in your Icecast config. If they don't match, MPD will fail to connect to Icecast and you'll get silence.

Buffer tuning for the Pi:

audio_buffer_size "8192"
buffer_before_play "10%"
max_output_buffer_size "32768"

These values are conservative — tuned to avoid buffer underruns on the Pi's limited RAM.

After editing, restart MPD:

sudo systemctl restart mpd

Icecast2

Edit /etc/icecast2/icecast.xml. The important section:

<authentication>
<source-password>YOUR_SOURCE_PASSWORD</source-password>
<relay-password>YOUR_RELAY_PASSWORD</relay-password>
<admin-user>admin</admin-user>
<admin-password>YOUR_ADMIN_PASSWORD</admin-password>
</authentication>

<hostname>your-pi-ip</hostname>

<listen-socket>
<port>8000</port>
</listen-socket>

The <source-password> must match the password in mpd.conf. Set the others to something strong.

Allow cross-origin requests (needed for the web player):

<http-headers>
<header name="Access-Control-Allow-Origin" value="*" />
</http-headers>

Restart Icecast after changes:

sudo systemctl restart icecast2

Nginx

The Nginx config proxies everything through a single host. Create /etc/nginx/sites-enabled/radio:

server {
listen 80;

root /var/www/radio/public;
index index.html;

location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache, must-revalidate";
}

location /api/ {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}

location /stream {
proxy_pass http://127.0.0.1:5000/stream$is_args$args;
proxy_buffering off;
proxy_read_timeout 300s;
}

location /live {
proxy_pass http://127.0.0.1:8000/radio.mp3;
}
}

Remove the default site and reload:

sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

The Programming Schedule

The schedule is stored in schedule.json — a simple JSON file that maps hours of the day to programming types:

{
"schedule": [
{ "hour": 6, "type": "genre", "value": "Rock" },
{ "hour": 10, "type": "podcast", "value": "podcasts" },
{ "hour": 11, "type": "genre", "value": "Reggae" },
{ "hour": 17, "type": "artist", "value": "The Tragically Hip" },
{ "hour": 21, "type": "genre", "value": "Folk" },
{ "hour": 22, "type": "metal", "value": "metal" }
],
"default": "random"
}

Any hour not listed falls back to "random" — a shuffle of the full library. The scheduler reads this file every hour and switches the MPD queue accordingly.

You can edit the schedule through the web UI or directly on the Pi. Changes take effect at the next hourly cron run.

Secrets in the Frontend

The frontend JavaScript contains two placeholder strings that are replaced at deploy time:

  • INJECT_OPENWEATHER_API_KEY → your actual API key
  • INJECT_MOCK_MODEfalse (production) or true (staging)

This keeps secrets out of source control. The replacement is done by sed as part of the deploy script.