Repository navigation
Releases: johncrash64/radio-dj
Release list
v0.5.0 — skip & previous controls for the live broadcast
Skip & previous controls for the live broadcast
The player gets ◀◀ / ▶▶ transport keys, backed by a new POST /control endpoint.
SkipCurrent() kills only the in-flight music decoder and leaves the master Icecast source untouched — connected listeners stay on, the filtergraph just stops receiving new PCM. The radio loop then advances to the next track or replays the previous one. It's a shared broadcast: a skip changes what every listener hears.
POST /control
{ "action": "next" } // or "previous"
→ 202 {"ok":true} accepted, loop advances
→ 409 {"error":"no track playing"} nothing on air (between songs)
→ 400 {"error":"action must be ..."} invalid action
Rapid clicks coalesce into one pending action. The transport keys latch down until the track actually changes, like a real deck key staying engaged while it works, then spring back — with a 4s safety timer so a key can never stick.
Fixed
- Two data races in the control path, both reachable from an HTTP goroutine and invisible without
-race.Server.controlHandlerwas read/written unguarded while the HTTP server was already serving; and the control closure capturedstreamer, which the radio loop reassigns on a master reopen. Both are now properly synchronized. install.shcould not install a pinned version. GitHub serves the floatinglatestand a pinned tag from different URL shapes; the script built the pinned form thelatestway, soRDJ_VERSION=v0.4.0returned 404.install.sh's build-from-source fallback was broken.go.moddeclaresmodule radio-dj, not the GitHub path, sogo install github.com/johncrash64/radio-dj@...failed with a module path mismatch. It now clones the tag and builds.- Music-only stations could not open the player UI (carried over from the previous release cycle).
Added
radio-dj version— release builds report their tag, development builds reportdev. The release script had been injecting-X main.versioninto a symbol that didn't exist, so no build could report what it was.
Known limitations
previouson the very first track after startup behaves asnext— there is no previous track recorded yet.- A skip during a
previousreplay is consumed as "return to the interrupted track". - Returning from a replay re-emits the request DJ-log line for that track.
- There is no client-side reconnect, so the key springs back when the metadata changes — a buffer-depth ahead of what the listener actually hears.
Install
curl -fsSL https://github.com/johncrash64/radio-dj/raw/master/install.sh | bash
Or pin it — which, as of this release, actually works:
RDJ_VERSION=v0.5.0 bash -c "$(curl -fsSL https://github.com/johncrash64/radio-dj/raw/master/install.sh)"
v0.4.0 — hardware AAC encoder + cross-platform defaults
✨ What's new
Configurable, cross-platform broadcast encoder. The stream encoder is now platform-aware and overridable:
| Platform | Default encoder | Notes |
|---|---|---|
| macOS | aac_at |
Apple AudioToolbox, hardware-accelerated — ~128× realtime, far lower CPU than software MP3 |
| Linux / Windows | aac |
ffmpeg's built-in — always present, no extra lib |
| Override | RDJ_ENCODER=libmp3lame |
(or any ffmpeg audio encoder) |
The stream mount follows the encoder: aac* → /stream.aac, libmp3lame → /stream.mp3. Everything — icecast mount-name, reverse-proxy route, web <audio> src — is derived from one place (internal/codec), so the player can no longer desync from the stream.
⚠️ Breaking — for listeners with a hardcoded URL
The default stream URL is now /stream.aac (was /stream.mp3). To keep MP3, set RDJ_ENCODER=libmp3lame.
🐛 Fixed
The web player's <audio> src was hardcoded to stream.mp3 in the page JS — it would 404 after any encoder/mount change. Now follows the configured mount.
⚡ Performance
The always-on encoder is ~1.6× faster on Apple Silicon (aac_at 128× vs libmp3lame ~78× realtime). The live DJ ducking filtergraph remains the bulk of CPU by design — that's the cost of real-time voice-over ducking.
Install: curl -fsSL https://github.com/johncrash64/radio-dj/raw/master/install.sh | bash
Full changelog: see CHANGELOG.md
v0.3.1 — legacy dj-log render fix
🔧 Fix
Follow-up to v0.3.0's structured DJ log.
Problem: legacy HH:MM:SS [KIND] text entries (written by v0.2.0 and earlier) parse to {kind:"", text:"<whole line>"}. The structured renderer prefixed every entry with a kind bracket, so legacy lines showed a stray empty [] before the raw text — and on an upgrade, 100% of the existing log is legacy.
Fix: when kind is empty (legacy), render the line as-is with no bracket prefix. Structured entries (from v0.3.0's JSONL writer) still get the localized label.
📦 Binaries
radio-dj-{darwin,linux}-{arm64,amd64} — stripped, CGO_ENABLED=0.
v0.3.0 — structural i18n + structured DJ log
🔧 Fixes
Structural i18n — no more text-injection bugs
PR #4 hand-escaped one localized string to stop an apostrophe (The DJ hasn't spoken yet…) from breaking the player. Five more sites had the same latent bug. This release fixes the whole class structurally:
text/template→html/template— contextual auto-escaping for every HTML/attribute context.- JSON data island — JS-consumed strings travel as
<script type="application/json" id="i18n">, read viaJSON.parse. Strings are never interpolated into JS source, so a quote or apostrophe can't break the page or open XSS. Adding a UI string = add anui_*key toprompts/{lang}.json, zero plumbing.
Structured DJ log — the regex is gone
The DJ-log view parsed its own text format with a regex whose tokens (DJ|PEDIDO|HORA) were duplicated between writer and reader — rename one and the view silently broke.
/dj-lognow returns structured JSON ([{t,kind,text}]).- Shared kind enum (
status.LogKindDJ/Req/Time) used by both writer and reader — single source of truth, no regex, no token drift. - Backward compatible: legacy
HH:MM:SS [KIND] textlines degrade to raw entries, so an in-place upgrade never breaks the view. - Log-kind labels localized — EN users no longer see Spanish
PEDIDO/HORAtokens.
⬆️ Upgrade
Existing installs pick this up with no config change:
curl -fsSL https://github.com/johncrash64/radio-dj/raw/master/install.sh | bashOr pin this version: RDJ_VERSION=v0.3.0 before running the installer.
📦 Binaries
Stripped, cross-compiled (CGO_ENABLED=0):
radio-dj-darwin-arm64— Apple Siliconradio-dj-darwin-amd64— Intel Macradio-dj-linux-arm64— Raspberry Pi 4/5radio-dj-linux-amd64— x86_64 servers
Full diff: #5
v0.2.0 — OpenRC + full i18n
v0.2.0
New install targets (OpenRC) and a fully localized UI — plus several fixes
reported after the v0.1.0 launch.
✨ New
- OpenRC init support — installs as a service on Alpine, Artix, Void, and
Gentoo (systemd and macOS launchd still supported). - Full UI localization — dashboard and DJ prompts render entirely in your
RDJ_LANGUAGE(es/en): tabs, buttons, placeholders, play/connecting states,
empty views, and aria-labels. The document now also declares the correct
<html lang>for screen readers. - Prebuilt binaries for Linux/macOS (arm64 + amd64) —
install.shdownloads
the matching one and skips the Go toolchain.
🐛 Fixed
- Symlinked music libraries now scan — a NAS/gvfs-mounted library no longer
returns 0 tracks. (Thanks @vrutkovs — PR #1) - GLM provider no longer breaks Groq/OpenAI — the
thinkingfield is sent
only to GLM, so other providers stop rejecting requests. - Installer skips already-installed deps and sets up the OpenRC
conf.d
PATH correctly. - DJ clock guard made symmetric; PWA audio buffer released on stop.
Full diff: v0.1.0...v0.2.0
v0.1.0 — first release
First public release of radio-dj — a 24/7 AI-DJ internet radio in a single Go binary (~20 MB RAM).
Downloads
| File | Platform |
|---|---|
| radio-dj-darwin-arm64 | macOS (Apple Silicon) |
| radio-dj-darwin-amd64 | macOS (Intel) |
| radio-dj-linux-arm64 | Linux ARM64 (Raspberry Pi 4/5) |
| radio-dj-linux-amd64 | Linux x86_64 |
curl -fsSL https://github.com/johncrash64/radio-dj/raw/master/install.sh | bashThe installer picks the right binary for your platform automatically. See README for setup.
SHA-256 checksums in checksums-sha256.txt.