Station Control (station-control.js)
station-control.js exposes the full scheduler/content agent API under
/api/station/*. It is what MCP (mcp-radio.js) and the studio UI talk to, and
is the single place where a human or an AI agent can steer the radio: moods,
genres, rotation windows, dayparts, singer catalogs, content fetches, and live
takeovers.
Mounting
mountStationControl(app, {
radio, // the radio engine bridge
contentPipeline, // the fetch/R128/rundown pipeline
SCHEDULER_CONFIG, // the smart-scheduler defaults
station, // station-config read/write helpers
live, // the LiveManager instance
runContentRefresh, // the full refresh function from server.js
})
Auth
If the STATION_KEY env var is set, every /api/station/* route requires the
x-station-key header (or Authorization). Empty/unset = open.
Config persistence
All scheduler knobs live in station-config.json (written via
station-config.js) and are re-applied on every boot (and after every
PUT /api/station/config). This means an agent can change the vibe and it stays
changed across restarts.
Key persisted blocks:
scheduler—mood,rotationBuffer,freshWindow,cooldown, genre split, mashup/sfx chances, weightsgenres— category → desired percentage (theGENRE_SPLIT)dayparts— window →{weights, hardGenre?}(applied atgetDaypart())singers— category → singer catalog (used to prune/reweight the pool)queries— category → trend queries (for the content pipeline)content— caps/purge counts per categorylive.streams— the LiveManager stream registry (see below)
Key behaviours
- State snapshot —
GET /api/station/statereturns rotation array, queue depth, per-category deck counts, current config, and live status in one call. This is the "am I healthy?" endpoint and the agent's first call. - Mood presets —
PUT /api/station/moodwith{preset:'chill'|'balanced'| 'party'|'night'|'morning'|'underground'}rewrites the scheduler weights in one shot (or accepts a custom full config object). - Dayparts —
PUT /api/station/daypartswith a daypart object ({daypart:{start,end},window:{weights,hardGenre?}});POST /api/station/dayparts/clearremoves all. - Content fetch —
POST /api/station/content/fetchwith{category,target,kind}runs a targeted fetch viacontentPipeline.fetchForCategory; the result goes through R128, into the deck, and the deck is re-shuffled + rotation rebuilt. - Rotation rebuild —
POST /api/station/rotation/rebuildfully regenerates the on-air rotation from the current config;skippops the head (keeps cooldown state);playforce-injects a chosen track. - Config save/reset —
savepersists the live config;resetdeletesstation-config.jsonand returns to built-in defaults.
What it does NOT do
- It never edits
smart-scheduler.jscode — it mutates runtime state + the persisted config file only. Live code reload is not supported (restart the node process to pick up code changes). - It does not directly stream audio; that is Icecast/Liquidsoap on the remote.
See also
- API endpoints — the exact route table.
- MCP radio — the agent-facing wrapper.
- Live manager — the
liveinstance it drives. - Smart scheduler — the config it mutates.