# Radio
A small, self-hostable website for an internet radio station. Point it at your
stream, edit one `.env` file, and you get a live player with album art, a
"now playing" title, a recently-played history, listener counts, and optional
lyrics — no code changes required.
Originally built for [radio.dou.bet](https://radio.dou.bet); this version is
generic so anyone can drop in their own configuration.
## Features
- Custom player — play/pause, seek + volume, elapsed time, and a **LIVE** badge for live streams (or a plain native player with `PLAYER_STYLE=simple`).
- Now playing from **Icecast, AzuraCast, Shoutcast, or any JSON feed**, normalized server-side.
- Album art and lyrics via **Genius**, with an optional **Last.fm** art fallback.
- Page background that recolors to the current album art (auto light/dark text).
- **MediaSession** integration so track info appears in OS/lock-screen media controls.
- Configurable title cleanup (strip `(Remastered)`, `[Explicit]`, `.mp3`, …).
- Recently-played history, live listener count, and optional Matomo analytics.
- Fully themeable from `.env` (CSS variables) — no CSS editing required.
## How it works
```
browser ──▶ index.php / assets/* (the page + player)
browser ──▶ api/nowplaying.php ──▶ Icecast / AzuraCast / Shoutcast / custom
browser ──▶ api/artwork.php ──▶ Genius API (optional)
browser ──▶ api/lyrics.php ──▶ Genius song page (optional)
```
The browser only ever talks to *this* site. PHP fetches your stream backend
server-side and returns one normalized JSON shape, which means no CORS headaches
and your internal backend address (e.g. `127.0.0.1:8000` behind nginx) is never
exposed to visitors. A typical deployment is **nginx → PHP-FPM** on the web box,
with nginx also proxying the audio stream from Icecast.
## Requirements
- PHP 7.4+ (8.x recommended) with cURL, served by any web server (nginx + PHP-FPM, Apache, Caddy…)
- A running stream with a now-playing endpoint — Icecast, AzuraCast, Shoutcast, or any JSON feed
- *(optional)* a free [Genius API token](https://genius.com/api-clients) for album art and lyrics
## Quick start
```bash
git clone https://git.dou.bet/iamdoubz/radio.git
cd radio
cp .env.example .env
# edit .env — at minimum set SITE_NAME, STREAM_URL, and NOWPLAYING_URL
php -S localhost:8000 # for a quick local test
```
Then open . For production, serve the folder with
nginx/Apache and PHP-FPM (see the example config near the bottom).
## Configuration
Everything lives in `.env` — see [`.env.example`](.env.example) for the fully
documented list. The main groups are:
- **Branding & metadata** — `SITE_NAME`, `SITE_DESCRIPTION`, `SITE_KEYWORDS`, `SITE_URL`, `OG_IMAGE`.
- **Stream** — `STREAM_URL` (what the browser plays) and `STREAM_TYPE`.
- **Now playing** — `NOWPLAYING_PROVIDER`, `NOWPLAYING_URL`, and provider-specific keys (below).
- **Artwork & lyrics** — `ARTWORK_SOURCE`, `LYRICS_ENABLED`, `GENIUS_TOKEN`.
- **Theme & colors** — `THEME_BG`, `THEME_ACCENT`, `THEME_TEXT`, `THEME_FONT`, … (drive CSS variables directly).
### Now-playing providers
Set `NOWPLAYING_PROVIDER` and `NOWPLAYING_URL`:
| Provider | `NOWPLAYING_URL` example | Notes |
|-------------|--------------------------|-------|
| `icecast` | `http://127.0.0.1:8000/status-json.xsl` | Set `ICECAST_MOUNT` if you run several mounts. Splits the stream title on `TITLE_DELIMITER`. |
| `azuracast` | `https://azura.example.com/api/nowplaying/your_station` | Richest option — provides artist, title, album, **art**, and listeners natively (no Genius needed). |
| `shoutcast` | `http://127.0.0.1:8000/stats?json=1` | Splits the stream title on `TITLE_DELIMITER`. |
| `custom` | any URL returning JSON | Map fields with `CUSTOM_ARTIST_PATH`, `CUSTOM_TITLE_PATH`, `CUSTOM_ART_PATH`, etc. (dot notation, e.g. `data.track.0.artist`). |
`TITLE_DELIMITER` is how a combined `Artist – Title` string is split. The
original dou.bet station uses `" <-|-> "`; most setups use `" - "`.
Use `TITLE_CLEANUP` to strip junk from titles (e.g. `.mp3`, `[Explicit]`,
`(Remastered)`). Separate entries with `||`; set `CLEANUP_IS_REGEX=true` to
treat each entry as a case-insensitive regex.
### Artwork & lyrics
`ARTWORK_SOURCE` controls where cover art comes from:
- `auto` — use the art your provider supplies (e.g. AzuraCast); otherwise look it up on Genius; otherwise show the placeholder.
- `provider` — only ever use provider art.
- `genius` — always look art up on Genius.
- `none` — always show `FALLBACK_ART`.
If Genius has no cover, set `LASTFM_API_KEY` to fall back to Last.fm artwork.
(`ARTWORK_SOURCE=lastfm` uses Last.fm only; `auto` tries Genius then Last.fm.)
Lyrics require `LYRICS_ENABLED=true` **and** a Genius token. Lyrics are scraped
from the public Genius song page and returned as plain text; if Genius changes
their page layout, `api/lyrics.php` may need a tweak. Set
`LYRICS_PREFER_ENGLISH=true` to prefer the first English-language match when a
song has several Genius entries.
### Player & appearance
- `PLAYER_STYLE` — `rich` (custom controls, default) or `simple` (native `