Mediabot can take a music request from IRC, find or download the track, register it in an MP3 catalogue and send it to the radio. Artist and title follow the request into playback, and the channel can see what is waiting without becoming a wall of queue announcements.
The feature lives on the 3.6dev development line and is enabled per channel with +Radio. Radio is optional: an ordinary Mediabot installation does not need a streaming server or the radio API.
Most installations will have one Mediabot and, optionally, one radio on the same machine. My own deployment adds several IRC instances, so the implementation also supports a shared radio server. You do not need to reproduce my collection of servers to request a song. One common room is quite enough. 🏰
The examples use m, my configured command prefix. Use your own prefix, such as !, where appropriate.
Command on a +Radio channel |
What it does | Access |
|---|---|---|
m play Michael Jackson Billie Jean |
Searches YouTube for a suitable recording, reuses or downloads the MP3, registers it and queues it. | Channel participants |
m play https://www.youtube.com/watch?v=VIDEO_ID |
Requests that particular video instead of a text search. | Channel participants |
m rplay Radiohead |
Picks a random available catalogue track matching the artist pattern. Falls back to title matches when needed. | Channel participants |
m rplay Idioteque |
Uses the same catalogue lookup with a title pattern. | Channel participants |
m song |
Shows the current Icecast title and listening link. | Existing song command |
m queue / m radioqueue |
Shows the current title and the first three waiting requests. | Channel participants |
m nextsong |
Skips the selected track and reports the observed new source and title. | Authenticated Administrator+ |
m deltrack 28 |
Withdraws and blocks central catalogue entry id_mp3=28. |
Authenticated Master+ |
rplay means random play within a catalogue pattern. It does not perform a YouTube search or download a replacement when the catalogue has no match.
Public radio requests require both the caller and the bot to be present in the enabled channel. IRC operator status alone does not grant Mediabot’s Administrator or Master permissions.
The radio feature adds a small authenticated HTTP service beside the bot. Even when everything runs on one machine, the same division of work applies:
| Component | Responsibility |
|---|---|
| Mediabot | Reads IRC commands, checks channel membership and permissions, sends requests and formats replies. |
| Radio API and worker | Handles downloads, validates audio, updates the catalogue and submits requests to Liquidsoap. |
MariaDB MP3 table |
Stores the music catalogue: track IDs, artists, titles, video IDs and file locations. |
| Private SQLite ledger | Stores request states, shared limits, duplicate protection and withdrawal records. This is separate from the MP3 catalogue. |
| Liquidsoap | Selects and plays live input, queued requests or the background playlist. |
| Icecast | Serves the resulting stream to listeners and exposes its current metadata. |
In this installation, the bot contacts http://127.0.0.1:8765. The API and Liquidsoap control socket remain on loopback. No SSH key, shared network filesystem or HTTPS certificate is needed for that local bot-to-API connection.
Listening from another machine is a separate matter: expose Icecast appropriately, directly or through an HTTPS reverse proxy. The stream URL is not the request API URL.
Start with a working Mediabot and an optional radio whose Liquidsoap queue and Icecast output have been checked.
Install the radio dependencies on the radio host. These include Python 3, Waitress, ffmpeg/ffprobe, yt-dlp and a supported JavaScript runtime, alongside the existing Perl DBI/MariaDB dependencies. Configure yt-dlp and any required private session before testing downloads.
Identify the actual Liquidsoap queue. Its exposed name is not necessarily the name you expected from a configuration variable. Our running queue is request_queue; the sample bot configuration uses mediabot_queue. Set the real LIQUIDSOAP_QUEUE_ID, loopback host and control port in the bot’s existing [radio] section before initialization. A streaming port and a control port are different endpoints.
Create the shared audio store. With the example accounts:
sudo install -d -o mediabot -g liquidsoap -m 2750 \
/var/lib/mediabot-radio \
/var/lib/mediabot-radio/incoming
Adapt the accounts to your installation. New final MP3s are group-readable; private state and tokens are not. Liquidsoap must be able to traverse the audio directory, but the bot’s repository can stay private.
Initialize once, as the bot account, from its repository:
python3 -B contrib/radio_init.py --bot-config "$PWD/mediabot.conf"
If the catalogue is empty or has several owners, add --owner-id followed by an existing user ID in the central Mediabot database. This account owns new catalogue entries. A remote bot’s user ID must never be substituted for it. Initialization creates private control/service.json, a client token and the state directory; it is not an update command.
Install and adapt the service template at tools/systemd/mediabot-radio.service.example: account, repository, configuration path and writable storage must match your host. Run one API process, then enable and start mediabot-radio.service through systemd.
Merge these keys into the existing [radio] section:
RADIO_API_ENABLED=1
RADIO_API_URL=http://127.0.0.1:8765
RADIO_API_TOKEN_FILE=/var/lib/mediabot-radio/control/client.token
Configure the Icecast status URL, public listening base URL and mount separately. The token file must be private and owned by the bot account; its contents do not belong in Git or IRC.
Use the normal migration/update workflow to register Radio from install/migrations/20260911_radio_chanset.sql, restart the selected bot and enable one channel:
m chanset +Radio
Test an existing catalogue track with rplay, listen to the output, then test one short YouTube request and check song and queue.
Registering the chanset does not activate it on every channel. m chanset -Radio closes that channel to new public requests; it does not cancel jobs already accepted or flush the player.
The API reads its own control/service.json at startup. Some initial values are copied from the bot configuration by the initializer; later INI edits do not automatically rewrite the API configuration. This matters when changing downloader, JavaScript runtime or cookie paths.
The radio runs on teuk.org, alongside the development checkout at /home/mediabot/mediabot_v3. It owns the central catalogue in mediabotv3.MP3, the MP3 files, the request ledger and the only radio worker.
The confirmed request paths are the development bot on teuk.org and the Epiknet bot on nbot. The other instances can join the same arrangement after their own configuration and activation:
| IRC instance | Host | Network | Radio integration |
|---|---|---|---|
mediabotv3 — dev |
teuk.org |
TeuK | Local API client; same host as the central radio. |
mediabot — nbot |
nbot.soyou.rocks |
Epiknet | HTTPS client; requests from #i/o have been heard on the central stream. |
mediabot — Undernet production |
teuk.org |
Undernet | Can use the same local API with a separate instance identity and token. |
ubot |
ubot.teuk.org |
Undernet | Can join as another HTTPS client. |
WitchHunter |
witch.soyou.rocks |
Undernet | Can join as another HTTPS client. |
This is one shared queue and one programme, not a separate station for each IRC network. A request from nbot joins the same queue as one from dev. Catalogue IDs, downloads and queue capacity belong to the central service.
A remote instance keeps its own normal Mediabot database for IRC accounts and configuration. Its radio requests do not need access to the central MariaDB server, a local music directory, yt-dlp, ffmpeg or Liquidsoap. They need outbound HTTPS and a private instance token.
The storage choice also remains installation-specific:
| Purpose | Standard installation | My radio host |
|---|---|---|
| Radio storage root | /var/lib/mediabot-radio |
/home/mediabot-radio |
| Incoming MP3 files | /var/lib/mediabot-radio/incoming |
/home/mediabot-radio/incoming |
The /home override belongs to my deployment; /var/lib remains the public default.
| URL | Purpose |
|---|---|
| Radio stream | Listen to the Icecast 8000 programme. |
| Icecast administration | Existing Icecast administration, with its own credentials. |
https://teuk.org/mediabot-radio |
Authenticated request API used by remote Mediabot instances. |
Apache terminates HTTPS and proxies the request API to 127.0.0.1:8765. Liquidsoap control stays on 127.0.0.1:1235. The public API proxy accepts the required GET/POST operations with bounded request bodies; the bearer token identifies the calling instance.
Each additional bot gets a different random token. Only its SHA-256 digest is registered under a unique logical name in the API’s private token_hashes mapping. The client receives the corresponding private token file. Names must be unique even if several systemd instances happen to be called prod.
For an already installed remote client, the radio settings have this shape:
RADIO_API_ENABLED=1
RADIO_API_URL=https://teuk.org/mediabot-radio
RADIO_API_TOKEN_FILE=/path/to/this-bots-private.token
RADIO_ICECAST_STATUS_BASE_URL=https://teuk.org/radio
RADIO_ICECAST_PUBLIC_BASE_URL=https://teuk.org/radio
RADIO_ICECAST_PRIMARY_MOUNT=/radio.mp3
The last three settings let that bot read the actual remote Icecast status and produce the correct listening link. Pointing its status URL at its own 127.0.0.1:8000 would query the wrong machine.
Radiocapsule on port 15000 remains a separate station. Its Icecast/Liquidsoap services and control port are outside this radio’s request path. Sharing a host does not make the two stations interchangeable.
A successful request confirms Artist — Song, its central MP3 ID when available, and its observed position at addition. If the track has already left the waiting list or the position cannot be confirmed, the reply says so.
An acknowledged push means Liquidsoap accepted the request. It does not prove the track is already on air. song reads Icecast’s actual current title; it does not repeat the last requested song as if it were playing.
Queue and addition messages use the orange/grey theme of song. queue and radioqueue share one public view per channel per minute, showing the current title and three following requests. Further eligible consultations use NOTICE. Public queue views, additions and next-track confirmations also share a 15-second spacing; preparation, errors and refusals stay private.
Successful request cooldowns adapt to shared waiting tracks and jobs being prepared:
| Shared load | Same caller | Same channel |
|---|---|---|
| 0 | 5 s | 5 s |
| 1 | 15 s | 5 s |
| 2 | 30 s | 10 s |
| 3 | 45 s | 15 s |
| 4 | 60 s | 20 s |
| 5+ | 90 s | 30 s |
Delays are measured from acceptance of the preceding request. An existing request from the same caller must still finish. Empty searches allow a correction after five seconds; failures, uncertain submissions and duplicates have separate protection. API submissions allow up to six waiting tracks. These shared controls apply across configured instances, while IRC display budgets belong to each bot/channel.
nextsong: skip what is actually playingAn empty request queue and a playing background playlist are different states. Sending request_queue.skip blindly does not advance that playlist and, in the tested player, can leave a skip affecting a later request.
The selected-source controller handles the actual situation:
| Current source | Result |
|---|---|
| Request queue, another ready request | Move to the next queued track. |
| Last queued request | Return to the interrupted background playlist, which may resume its previous track. |
| Background playlist | Advance the playlist. |
| Live input | Refuse the skip and preserve the live programme. |
| Track changed before execution | Refuse the stale request rather than skip another track. |
| Lost acknowledgement | Keep an uncertain result; do not automatically send another skip. |
Only authenticated Administrator+ can request this. The IRC bot checks the account role; the central API trusts its authenticated bot clients to enforce that permission. A confirmed transition reports the new title and whether it comes from the queue or global playlist. Skips share a 15-second global interval.
This feature requires the supplied contrib/liquidsoap/mediabot-next.liq controller to wrap the final Liquidsoap selector used by the output. It was qualified against Liquidsoap 2.3.2 with isolated audio tests. It is not installed simply by changing an IRC chanset.
On an existing radio, review and validate that integration first, then restart only the selected Liquidsoap service during a suitable window. Our configuration preserves live > request queue > background playlist. Its existing track_sensitive=false policy can interrupt the background playlist when a request becomes ready; this feature does not promise that every playing track will finish uninterrupted.
Text play ranks up to five search candidates by relevance. It prefers official/artist-channel hints and filters unsolicited covers, tutorials, reactions, karaoke and speed edits. Explicitly requested versions remain possible; a direct video URL selects a particular recording.
This is a metadata heuristic. It cannot listen to the recording and certify that a convincing title contains the right music. It also does not retrospectively purge the existing catalogue.
That is the purpose of Master+ deltrack <central id_mp3>. It archives the original row in the private ledger, removes that exact catalogue row and durably blocks its ID, video ID and path from reuse through the radio API, including other instances and restarts.
The MP3 file is retained. Already queued or playing audio is left intact, and an independently managed background playlist is not rewritten. Use nextsong separately when a playing item needs to be skipped. The withdrawal reply is private; repeated withdrawal requests are handled without broad deletion. A re-upload with a different video identity is not automatically recognized as the same recording.
For my existing setup, development changes are validated and published first. The central API/controller must support the new operations; remote bots then receive the published client code through the normal update flow, including m update now on nbot.
Already configured MB734 clients need no additional keys for the new queue, next-track and withdrawal handlers. A completely new instance still needs its own registered token, endpoint settings and channel activation. Central changes alone cannot replace an old IRC command handler on a remote bot.
The pilot exercised real requests from IRC through to audible playback and correct Icecast metadata. Isolated Liquidsoap checks covered queue/playlist transitions, empty-queue behavior, live protection and stale commands. Development rounds use targeted and fast validation; publication uses one visible full suite before commit.
YouTube remains an external dependency. Tools and accepted sessions need maintenance; a valid cookie-file format does not establish that the session still works. Follow the official yt-dlp cookie guidance when a session is required. The service pauses new downloads after authentication or rate-limit failures while valid cached music remains usable. It does not repeatedly retry an uncertain queue push.
The detailed installation guide, controller integration and operational limits are in docs/RADIO.md, with the code in Mediabot v3.
The Great Hall now takes requests. The queue is shared, the privileges stay explicit, and the portraits can keep sleeping between songs. 🎧✨
You must be logged in to reply.