diff --git a/.gitignore b/.gitignore
index 3b735ec4..c8406eed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,8 @@
# Go workspace file
go.work
+
+cadence.env
+icecast.xml
+liquidsoap.liq
+nginx.conf
diff --git a/README.md b/README.md
index a7fccb5f..0f823c1e 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,12 @@
# CadenceRadio
-**Cadence** is an all-in-one web radio suite that lets you start a self-hosted radio website.
+**Cadence** is an all-in-one suite that lets you start a self-hosted web radio website.
-In minutes, you can create an internet audio broadcast complete with library search, song request, album artwork, and a browser UI with real-time stream information working out-of-the-box.
-
-All components are mostly pre-configured so there is hardly any configuration required to get started. Simply provide target directory containing music files, set a few service passwords and hostnames, and deploy!
+In minutes, create your internet broadcast with library search, song request, album artwork, and a browser UI with real-time stream information. All components are mostly pre-configured to work out-of-the-box. Simply run an interactive installation script and deploy!
**[See a live demo!](https://cadenceradio.com/)**
-## 🖼️ Image Gallery
+## 🖼️ Gallery
Browser UI Screenshot
@@ -23,47 +21,24 @@ All components are mostly pre-configured so there is hardly any configuration re
-## 🏃 Get Started
+## 🏃 Start Here
### Requirements
-1. You must have [Docker](https://docs.docker.com/engine/install/) installed. If you are on a Linux server, additionally install the [Compose plugin](https://docs.docker.com/compose/install/linux/).
+You must have [Docker](https://docs.docker.com/engine/install/) and [Docker Compose](https://docs.docker.com/compose/install/) installed.
### Installation
-1. Edit `config/cadence.env`
- 1. Change all instances of `hackme` to a new password.
- 2. Set `CSERVER_MUSIC_DIR` to an absolute path which contains music files (`.mp3`, `.flac`) for play. The target is not recursively searched.
- 3. Set `CSERVER_REQRATELIMIT` to an integer that sets the song request cooldown period in seconds. Set this value to `0` to disable rate limiting.
-2. Edit `config/icecast.xml`
- 1. Change all instances of `hackme` to a new password.
- 2. Set the `` value to a URL you expect your audience to connect to. This value is what is set in the UI's stream source. This may be a DNS name or a public or private IP address. You can leave the default value `localhost` if your radio is meant to be accessible locally only.
-3. Edit `config/liquidsoap.liq`
- 1. Change all instances of `hackme` to a new password.
- 2. If you changed `CSERVER_MUSIC_DIR` in step 1, change any instances of the default value `/music/` to match it here.
-4. Edit `docker-compose.yml`
- 1. If you changed `CSERVER_MUSIC_DIR` in step 1, change any instances of the `Volumes` defined, replacing `/music:/music` with `/YOURDIR:/music`.
-5. (_Optional_) Edit `config/nginx.conf`
- 1. For advanced users deploying Cadence to a server with DNS, Cadence ships with a reverse proxy that can forward requests based on domain-name to backend services. Simply configure the `server_name` values with your domain names. The stream server domain should match the value you set in step 2.
-6. `docker compose up`
-
-After configuration is initially completed, you may simply run `docker compose up` again in the future to start your station.
-
-### Accessing Services
-
-- Assuming you kept the default values above, Cadence will become accessible in a browser at `localhost:8080`.
-- If you optionally followed step 4, open firewall port `80` and point DNS to your server.
-
-## 👩💻 Developing
-
-### Building the Stack
-If you changed code or updated a container image, append the `--build` flag to rebuild exactly what you have.
+```bash
+chmod +x ./install.sh
+./install.sh
+```
-1. `docker compose down; docker compose up --build`
+You will be interactively prompted for a music directory path, a stream hostname, a rate limit timeout, a service password, and optional DNS. After the last prompt, the radio stack will automatically launch and Cadence's web UI will become accessible at `localhost:8080`.
-### Enable Development API
-Cadence provides an optionally-enabled API with special administrative controls that may be useful for testing. Don't enable development mode on a production server.
+After initial installation, simply run `docker compose up` to start your station. Use `install.sh` again at any time to reconfigure inputs.
-1. Edit `config/cadence.env`.
- 1. Set `CSERVER_DEVMODE` to `1` (enabled).
+## 📚 Knowledge Base
+Cadence's GitHub Wiki provides various resources to help you use, administrate, and develop for your station. If you need any further assistance, we warmly welcome you to [open an issue](https://github.com/kenellorando/cadence/issues).
-### API Reference
-[Cadence's API Reference](https://github.com/kenellorando/cadence/wiki/API-Reference) provides usage details and complete request/response examples.
+- [API Reference](https://github.com/kenellorando/cadence/wiki/API-Reference)
+- [Development and Code Style Guide](https://github.com/kenellorando/cadence/wiki/Development-and-Code-Style)
+- [Installation Guide](https://github.com/kenellorando/cadence/wiki/Installation)
diff --git a/config/cadence.env b/config/cadence.env.example
similarity index 79%
rename from config/cadence.env
rename to config/cadence.env.example
index 2b7f64bb..9f8452b5 100644
--- a/config/cadence.env
+++ b/config/cadence.env.example
@@ -1,6 +1,6 @@
-CSERVER_MUSIC_DIR=/music/
-CSERVER_REQRATELIMIT=180
-POSTGRES_PASSWORD=hackme
+CSERVER_MUSIC_DIR=CADENCE_PATH_EXAMPLE
+CSERVER_REQRATELIMIT=CADENCE_RATE_EXAMPLE
+POSTGRES_PASSWORD=CADENCE_PASS_EXAMPLE
# ####################################################
# If you are running Cadence through Docker simply as a user,
@@ -8,8 +8,8 @@ POSTGRES_PASSWORD=hackme
# Development
CSERVER_DEVMODE=0
-CSERVER_VERSION=5.3.2
-CSERVER_LOGLEVEL=4
+CSERVER_VERSION=5.4.0-prototype
+CSERVER_LOGLEVEL=5
CSERVER_ROOTPATH=/cadence/server/
# Service Addresses
diff --git a/config/icecast.xml b/config/icecast.xml.example
similarity index 79%
rename from config/icecast.xml
rename to config/icecast.xml.example
index 35820624..89db7034 100644
--- a/config/icecast.xml
+++ b/config/icecast.xml.example
@@ -1,6 +1,6 @@
- Chicago, United States of America
- Ken Ellorando (kenellorando.com)
+ Internet
+ Source: Cadence Radio (github.com/kenellorando/cadence)
100
@@ -15,15 +15,15 @@
- hackme
+ CADENCE_PASS_EXAMPLE
- hackme
+ CADENCE_PASS_EXAMPLE
admin
- hackme
+ CADENCE_PASS_EXAMPLE
- localhost:8000
+ CADENCE_HOST_EXAMPLE
8000
diff --git a/config/liquidsoap.liq b/config/liquidsoap.liq.example
similarity index 85%
rename from config/liquidsoap.liq
rename to config/liquidsoap.liq.example
index 3b8a54c6..92c4d784 100644
--- a/config/liquidsoap.liq
+++ b/config/liquidsoap.liq.example
@@ -7,11 +7,11 @@ set("server.telnet", true)
set("server.telnet.bind_addr", "0.0.0.0")
# 1. Lowest priority: play all music in the target directory at random.
-default = mksafe(playlist(mode="randomize", "/music/"))
+default = mksafe(playlist(mode="randomize", "CADENCE_PATH_EXAMPLE"))
# 2. Next priority: play user requests first if there are any in the queue.
radio = fallback([ request.queue(id="request"), default])
# 3. Next priority: play live input stream if one is connected.
full = fallback(track_sensitive=false, [input.http("http://localhost:8000/live.ogg"), radio])
# Output the full stream in OGG
-output.icecast(%vorbis.cbr(bitrate=192), host="icecast2",port=8000,password="hackme", mount="cadence1",full)
+output.icecast(%vorbis.cbr(bitrate=192), host="icecast2",port=8000,password="CADENCE_PASS_EXAMPLE", mount="cadence1",full)
diff --git a/config/nginx.conf b/config/nginx.conf.example
similarity index 89%
rename from config/nginx.conf
rename to config/nginx.conf.example
index 6f760dae..5c4ac3b5 100644
--- a/config/nginx.conf
+++ b/config/nginx.conf.example
@@ -6,7 +6,7 @@ http {
# This server forwards requests to the Icecast service.
server {
listen 80;
- server_name stream.cadenceradio.com;
+ server_name CADENCE_STREAM_DNS_EXAMPLE;
access_log off;
location / {
proxy_pass http://icecast2:8000/;
@@ -16,7 +16,7 @@ http {
# This server forwards requests to the API/UI server.
server {
listen 80;
- server_name cadenceradio.com;
+ server_name CADENCE_WEB_DNS_EXAMPLE;
access_log off;
# Server-sent event API needs special configuration to get through the proxy.
location /api/radiodata/sse {
diff --git a/docker-compose.yml.example b/docker-compose.yml.example
new file mode 100644
index 00000000..23bda1a9
--- /dev/null
+++ b/docker-compose.yml.example
@@ -0,0 +1,94 @@
+version: "3"
+services:
+
+ redis:
+ image: redis/redis-stack-server:latest
+ container_name: redis
+ expose:
+ - 6379
+ networks:
+ internal_services:
+
+ postgres:
+ image: postgres:15-alpine
+ container_name: postgres
+ expose:
+ - 5432
+ env_file:
+ - ./config/cadence.env
+ networks:
+ internal_services:
+
+ icecast2:
+ build:
+ dockerfile: ./cadence/icecast2.Dockerfile
+ image: kenellorando/cadence_icecast2:latest
+ container_name: icecast2
+ restart: always
+ ports:
+ - 8000:8000
+ volumes:
+ - ./config/icecast.xml:/etc/icecast/cadence.xml
+ networks:
+ external_services:
+ stream_delivery:
+
+ liquidsoap:
+ build:
+ dockerfile: ./cadence/liquidsoap.Dockerfile
+ image: kenellorando/cadence_liquidsoap:latest
+ container_name: liquidsoap
+ restart: always
+ volumes:
+ - ./config/liquidsoap.liq:/etc/liquidsoap/cadence.liq
+ - CADENCE_PATH_EXAMPLE:CADENCE_PATH_EXAMPLE
+ depends_on:
+ - icecast2
+ expose:
+ - 1234
+ networks:
+ internal_services:
+ stream_delivery:
+
+ cadence:
+ build:
+ context: ./cadence
+ dockerfile: ./cadence.Dockerfile
+ image: kenellorando/cadence
+ container_name: cadence
+ restart: always
+ ports:
+ - 8080:8080
+ env_file:
+ - ./config/cadence.env
+ volumes:
+ - CADENCE_PATH_EXAMPLE:CADENCE_PATH_EXAMPLE
+ depends_on:
+ - icecast2
+ - liquidsoap
+ - redis
+ - postgres
+ networks:
+ internal_services:
+ external_services:
+
+ nginx:
+ image: nginx:latest
+ volumes:
+ - ./config/nginx.conf:/etc/nginx/nginx.conf
+ container_name: nginx
+ restart: on-failure
+ ports:
+ - 80:80
+ depends_on:
+ - cadence
+ networks:
+ external_services:
+
+networks:
+ external_services:
+ driver: bridge
+ internal_services:
+ driver: bridge
+ stream_delivery:
+ driver: bridge
diff --git a/install.sh b/install.sh
new file mode 100755
index 00000000..4d68bbcc
--- /dev/null
+++ b/install.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+echo "[1/5] Music Directory Target"
+echo "Set the absolute path of a directory containing audio files (e.g. mp3, flac)"
+echo "meant for radio play. Only files at the directory base will be seen, not those"
+echo "in nested subdirectories."
+echo "Example: /music/"
+read -p " Music path: " CADENCE_PATH
+echo "================================================================================"
+echo "[2/5] Stream Host Address"
+echo "Set the stream host address for Cadence Icecast. This may be a DNS name, public"
+echo "IP, or private IP. Set this to localhost:8000 if your Cadence instance is meant"
+echo "for local use only."
+echo "Example: localhost:8000"
+read -p " Stream address: " CADENCE_HOST
+echo "================================================================================"
+echo "[3/5] Rate Limiter Timeout"
+echo "Set a rate limit timeout in integer seconds. This prevents the same listener"
+echo "from requesting songs within the configured timeframe. Set to 0 to disable."
+echo "Example: 180"
+read -p " Rate limit: " CADENCE_RATE
+echo "================================================================================"
+echo "[4/5] Radio Service Password"
+echo "Set a secure, unique service password. Input is hidden."
+read -s -p " Password: " CADENCE_PASS
+echo ""
+echo "================================================================================"
+echo "[5/5] Domain Names - LEAVE BLANK TO SKIP"
+echo "OPTIONAL: if you are an advanced administrator routing DNS to your Cadence"
+echo "stack, provide your domain names here. You will be prompted for two domains: one"
+echo "for Cadence Icecast, one for Cadence web UI. Subdomains are acceptable."
+read -p " Cadence Audio Stream Domain: " CADENCE_STREAM_DNS
+read -p " Cadence Web UI Domain: " CADENCE_WEB_DNS
+
+if [ -z "$CADENCE_STREAM_DNS" ]
+then
+ CADENCE_STREAM_DNS=stream.cadenceradio.com
+fi
+
+if [ -z "$CADENCE_WEB_DNS" ]
+then
+ CADENCE_WEB_DNS=cadenceradio.com
+fi
+
+cp ./config/cadence.env.example ./config/cadence.env
+cp ./config/icecast.xml.example ./config/icecast.xml
+cp ./config/liquidsoap.liq.example ./config/liquidsoap.liq
+cp ./config/nginx.conf.example ./config/nginx.conf
+cp ./docker-compose.yml.example ./docker-compose.yml
+
+sed -i 's|CADENCE_PASS_EXAMPLE|'"$CADENCE_PASS"'|g' ./config/cadence.env
+sed -i 's|CADENCE_PASS_EXAMPLE|'"$CADENCE_PASS"'|g' ./config/icecast.xml
+sed -i 's|CADENCE_PASS_EXAMPLE|'"$CADENCE_PASS"'|g' ./config/liquidsoap.liq
+sed -i 's|CADENCE_RATE_EXAMPLE|'"$CADENCE_RATE"'|g' ./config/cadence.env
+sed -i 's|CADENCE_HOST_EXAMPLE|'"$CADENCE_HOST"'|g' ./config/icecast.xml
+sed -i 's|CADENCE_PATH_EXAMPLE|'"$CADENCE_PATH"'|g' ./config/cadence.env
+sed -i 's|CADENCE_PATH_EXAMPLE|'"$CADENCE_PATH"'|g' ./config/liquidsoap.liq
+sed -i 's|CADENCE_STREAM_DNS_EXAMPLE|'"$CADENCE_STREAM_DNS"'|g' ./config/nginx.conf
+sed -i 's|CADENCE_WEB_DNS_EXAMPLE|'"$CADENCE_WEB_DNS"'|g' ./config/nginx.conf
+sed -i 's|CADENCE_PATH_EXAMPLE|'"$CADENCE_PATH"'|g' ./docker-compose.yml
+
+echo "========================================="
+echo "Configuration completed."
+echo "If it does not begin automatically, run 'docker compose up' to start Cadence."
+docker compose down
+docker compose up