Playback Engine
The playback engine is the bridge between the scheduler's playlists and the live broadcast stream. It determines how a list of files actually becomes sound on air.
How a playlist becomes air
smart-scheduler.jswrites an M3U intoPLAYLIST_ROOT.radio-engine.jsships the playlist (and any new media) to the production node over HTTP pull (see Media Sync).- Liquidsoap (
radio_worker.liq) picks up the newest M3U and starts rotating: it reads the file paths, plays each track, then advances to the next entry. - The scheduler keeps a fresh playlist staged so rotation never empties.
Crossfade rules
Liquidsoap applies different transitions depending on content type:
| Segment | Transition |
|---|---|
| Music → Music | 2-second crossfade (overlapped fade in/out) |
| Imaging (sweepers, jingles, ads, sfx, shorts) | Hard cut — instant switch, radio-style |
Imaging files cut hard so they punch through cleanly; songs blend so the music never dies between tracks. These rules are defined in the Liquidsoap script; see Liquidsoap for the exact implementation.
Live injection (Studio)
The scheduler's playlists are the automated path. Two live paths exist on top:
- Studio injector (
studio.html) — push buttons send Socket.io events to the server, which relays them to the broadcast engine so Liquidsoap switches from the automation queue to the injected asset. The on-air sign lights up while injection is active. - Channel takeover — LiveManager (or the mobile WHIP ingest) restreams a live
source into the Liquidsoap
channel_liveharbor (:8006); the.liqswitch gives it priority over the mix and returns automatically when it stops. See Live ingest.
M3U rotation & as-run logging
- The on-air rotation is a stable M3U buffer (default 120 tracks) that is
rebuilt only when ~85% of it has been consumed (
REFRESH_FRACTION). Liquidsoap usesreload_mode="watch", so rewriting the file on every batch would reload it and restart from the head — the old behaviour that caused songs to repeat. See Anti-repeat & M3U rotation. - Every playable is logged as-run (timestamp + file) into the log root, which feeds later analysis and the RL arms' reward signal.
- The authoritative "track finished on-air" signal is the
/metadatawebhook, which advances the rotation cursor (markOnAirTrack) and cooldowns the exact track that just played.
Content types in the rotation
The engine treats several categories as imaging / non-music content. The
regenerate-rundown.js tool maps categories to live directories including:
arabic_hits,arab_techno,egyptian_trends— music decksbeds— underscore beds (talking under, live shows)ads— commercialsjingles,sweepers— station imagingsfx— sound effectsmashups— mashup contentshorts— TikTok-style clips (hard-cut, 12–60s)talkshow,programs— longer-form contentinfluencers— social-influencer voice clips
See Rundown generation for the full mapping and rundown JSON format.