Audio Streaming, Chat, WebRTC & Charts http://radio.davidawindham.com
|
|
8 hours ago | |
|---|---|---|
| .vscode | 4 years ago | |
| deploy | 8 hours ago | |
| src | 1 day ago | |
| .env.example | 1 day ago | |
| .gitignore | 1 day ago | |
| .jshintrc | 10 years ago | |
| .nodemonignore | 10 years ago | |
| .tm_properties | 10 years ago | |
| README.md | 1 day ago | |
| app.js | 1 day ago | |
| build.mjs | 1 day ago | |
| ecosystem.config.cjs | 8 hours ago | |
| package-lock.json | 1 day ago | |
| package.json | 1 day ago |
Just a litle page on the intrawebs where I can broadcast and chat with friends.
Note: there is no authentication. The page, the chat socket and the Icecast stream are all reachable by anyone who has the URL. See "Auth" below before putting this anywhere public.
Needs Node 20+.
npm install
cp .env.example .env # then edit
npm run build # src/ -> app/
npm start
npm run dev rebuilds on change and reloads the server.
Config is all environment variables (see .env.example) — there are no
config.js / config-dev.js files anymore.
Nothing renders until npm run build has run: Express serves app/, which is
generated from src/ and is not in git.
Set BASE_PATH=/radio and TRUST_PROXY=1, then proxy to it from Apache — the
same shape as the existing /ask proxy in the daw vhost:
# radio: proxy /radio to the node app (mirrors /ask)
ProxyPreserveHost On
ProxyPass /radio http://127.0.0.1:3000/radio upgrade=websocket
ProxyPassReverse /radio http://127.0.0.1:3000/radio
Both sides keep the /radio prefix on purpose — the app is mounted at it and
expects to see it, and that keeps its own /radio → /radio/ redirect correct
through ProxyPassReverse.
upgrade=websocket is what carries socket.io. It needs Apache 2.4.47+, where
mod_proxy_http handles protocol upgrades itself — mod_proxy_wstunnel is not
required (on older Apache it would be). Without the upgrade, socket.io still
works but silently falls back to HTTP long-polling.
ProxyPass is matched before the request is mapped to the filesystem, so
WordPress and its .htaccess never see /radio.
If
/radioredirects you to/online-radio: that's a cached 301. The WordPress page that used to live at/radiomoved, and WP issues a permanent old-slug redirect, which browsers cache hard. Once the proxy is in place Apache answers/radiobefore WP ever sees it — but a browser that cached the 301 beforehand will keep redirecting itself. Hard-reload, or clear that entry.
Leave BASE_PATH empty to serve at a domain root instead; the client works out
where it's mounted at runtime, so the same build covers both.
The header and footer come from the main site's shared web components — the same
ones /rtc uses:
<daw-header></daw-header>
<daw-footer></daw-footer>
<script src="/embed/chrome.js"></script>
chrome.js renders into a shadow root, so the site's styles stay sealed off from
this page's Bootstrap 3 and the two can't collide. Nothing is copied into this
repo — the chrome is maintained in the main site and can't drift out of sync here.
That script tag is root-relative on purpose: in production Apache serves /embed
from the docroot alongside /radio. Locally there's no Apache in front, so set
DAW_ORIGIN=http://daw.stu and the app proxies /embed to it. Leave
DAW_ORIGIN unset in production.
| Route | Purpose |
|---|---|
/ |
the page |
/health |
ok, room count, socket count |
/api/status |
now-playing + listeners, proxied from Icecast |
/api/lastfm |
sidebar data, proxied so the API key stays server-side |
/api/broadcast |
POST {msg} — sends to every room |
All are relative to BASE_PATH.
The player streams from $STREAM_URL and gets now-playing from $STREAM_STATUS_URL.
Both must be https — the page is served over TLS, so an http:// stream is
blocked as mixed content.
Status is read from Icecast's stock status-json.xsl and proxied through
/api/status rather than being fetched from the browser. The old code called a
hand-customized status2.xsl over JSONP; Icecast upgrades overwrite those XSL
files (see the 2021 note below — it 404s today). The stock endpoint survives
upgrades, and proxying keeps the page same-origin.
Optional. Unset REDIS_URL and it runs single-process, keeping per-connection
state in memory. Set it and socket.io uses the Redis adapter so rooms and
presence work across multiple processes.
One room. The join/leave UI is gone and the server has no
subscribe/unsubscribe handlers, so the Lobby is the only room there is —
removing the buttons alone wouldn't have stopped anyone emitting subscribe by
hand.
Nicknames are opt-in: the person button beside the message box opens the nickname
dialog. Nothing is demanded on load; unnamed users chat as anonymous.
There isn't any, and there's no longer anything pretending otherwise — the old password prompt was only ever a client-side modal gate, so it was removed rather than left to imply protection it never gave.
/api/broadcast is unauthenticated, the socket handshake is unauthenticated, and
the Icecast stream can be opened directly. Doing this properly means a
server-verified session covering the socket handshake and the stream, not just
the page.