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