ShuffleCast is a dockerized 'radio-in-a-box' which creates internet radio streams from mp3s in subfolders. It aims to provide a super simple, barebones setup for running a multi-stream internet radio station using Docker Compose. It uses Icecast as the streaming server and Liquidsoap as a dynamic "auto-DJ" that creates streams from music folders. You can then use these streams anywhere you like: in any app that accepts stream URLs (like VLC, Foobar2000, RadioTray, Eter, [GaGa]https://github.com/Beluki/GaGa) (RIP Carlos) or Winamp), in your Home Assistent set-up, or directly in your browser.
First of all, I did not 'build' anything. :) All credit goes to the devs and maintainers of Icecast and Liquidsoap. What I did was throw a few config files together.
I wanted a super simple way to shuffle my own music on my own smart speakers. Using Plex to cast music to my speaker group was a frustratingly jittery mess, but I noticed I never had that issue when playing internet radio streams, like KEXP or WNYC. I first tried Azuracast, which works really well and is a truly awesome project with loads of configuration options, but managing it is a little involved and it was a little resource heavy on my hardware. I wanted something simple and lightweight, something I could run in my local network. Ergo, ShuffleCast! I've been using it for a few months and it has been working so well for me that I figured I'd share it here in case it's useful to others.
- Dockerized: Runs both Icecast and Liquidsoap in separate containers.
- Dynamic Streams: The Liquidsoap script automatically creates a separate stream for each subfolder you create (e.g.,
music/80s,music/Jazz) and shuffles the mp3 files. Just copy your mp3s to each folder and Bob's your proverbial uncle! - Audio Processing: Includes built-in optional compression and normalization.
- Consistent volume: Uses the
replaygain_track_gainmetatag (if available) for consistent volume. - Seasonal Streams: The configuration includes logic to automatically enable "Halloween" and "Christmas" streams during October and December, respectively (if the corresponding folders exist).
- Easy Setup: Get up and running by adding your music and running one command.
- Easy Control: Comes with a bash script to control ShuffleCast (start, restart, update, skip, echo, run & logs).
This whole shindig consists of two services managed by docker-compose.yml:
icecast: The public-facing internet radio server. It receives audio from Liquidsoap and serves it to listeners.liquidsoap: The "source" or "auto-DJ". It scans the./musicdirectory for subfolders, creates a shuffled playlist for each one, processes the audio, and feeds it to Icecast.
- Docker
- Docker Compose
- A fistful of mp3s
Your project must have the following directory structure. The docker-compose.yml file relies on these specific paths.
config/: Holds your configuration files.music/: This is your music library. Liquidsoap will turn each subfolder (80s,Jazz, etc.) into its own stream.
.
├── config/
│ ├── icecast.xml
│ └── liquidsoap.liq
├── music/
│ ├── 80s/
│ │ ├── take-on-me.mp3
│ │ └── ...
│ ├── Jazz/
│ │ ├── take-5.mp3
│ │ └── ...
│ └── Halloween/
│ │ ├── thriller.mp3
│ │ └── ...
└── docker-compose.yml
Copy your music files (preferably .mp3 with a fixed bitrate and, optionally, ReplayGain Track Gain metadata) into subfolders within the music/ directory, e.g.:
80sJazzLo-FiMusicForCatsHalloween(only active during the month of October)Christmas(only active during the month of December)
These subfolders can then be turned into their own stream by adding them to the liquidsoap.liq config file, for example:
...
make_stream("80s")
make_stream("Jazz")
make_stream("Lo-Fi")
make_stream("MusicForCats")
if month == 10 then
make_stream("Halloween")
end
if month == 12 then
make_stream("Christmas")
end
...
From the root directory (where docker-compose.yml is), run:
docker compose up -dShuffleCast should now be running!
To see what Liquidsoap is up to, run:
docker compose logs "liquidsoap" -fYour streams will be available at the mount points defined in liquidsoap.liq. Based on the previous examples, your stream URLs would be:
http://<your-server-ip>:1907/80shttp://<your-server-ip>:1907/jazzhttp://<your-server-ip>:1907/lo-fihttp://<your-server-ip>:1907/musicforcatshttp://<your-server-ip>:1907/halloween(only in Oct)http://<your-server-ip>:1907/christmas(only in Dec)
To add more songs to your streams you can simply drop more mp3s into your subfolders. Liquidsoap will pick them up automatically.
- Create a new music folder (e.g.,
music/Grunge). - Edit
config/liquidsoap.liqand add a new line near the bottom of the file:make_stream("Grunge") - Restart the Liquidsoap container to apply the changes:
docker compose restart liquidsoap
Your new stream will be available at http://<your-server-ip>:1907/grunge.
From the root directory (where docker-compose.yml is), run:
sudo chmod +x ./shufflecast.shNow you can run commands from the terminal, like so
./shufflecast.sh skip 80s # Skip current track on 80s mountpoint
./shufflecast.sh restart # Restart both containers (optional parameter: icecast|liquidsoap)
./shufflecast.sh update # Update both containers
./shufflecast.sh logs # Show logs for both containers (optional parameter: icecast|liquidsoap)
./shufflecast.sh run help # Run raw commandYou can also run Liquidsoap commands directly. For example, this is how you skip tracks on the command line:
bash -c 'echo "80s.skip" > /dev/tcp/localhost/1908' # Via bash
docker exec liquidsoap bash -c 'echo "80s.skip" > /dev/tcp/localhost/1908' # Via dockerThis is handy if you want to integrate it with Home Assistant. For example, here's how you'd make a 80s Skip Track switch:
# configuration.yaml
switch:
- platform: telnet
name: "80s Skip Track"
resource: <your-server-ip> # Replace this with your server's IP address
port: 1908
command_on: "80s.skip"
timeout: 0.5After adding this and restarting HA, you will have a "80s Skip Track" switch. When you turn it "on", it just sends the coffeeshop.skip command and then immediately turns itself back "off", acting as a perfect "skip" button.
I made this to scratch a personal itch. I'm by no means an Icecast, Liquidsoap, and/or Docker expert, so there is probably loads of room for improvement. Also note that this intended to be used locally, within your local network (which considering the bandwidth implications of opening it up to the world is probably for the best).
That being said, it's been running for a few months now without any issues. No more jitters!
Find any bugs? Or did you change anything to make it better? Please let me know and I'll consider adding it! Keep in mind that the goal is simplicity.