Skip to main content

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

  1. smart-scheduler.js writes an M3U into PLAYLIST_ROOT.
  2. radio-engine.js ships the playlist (and any new media) to the production node over HTTP pull (see Media Sync).
  3. 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.
  4. The scheduler keeps a fresh playlist staged so rotation never empties.

Crossfade rules

Liquidsoap applies different transitions depending on content type:

SegmentTransition
Music → Music2-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_live harbor (:8006); the .liq switch 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 uses reload_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 /metadata webhook, 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 decks
  • beds — underscore beds (talking under, live shows)
  • ads — commercials
  • jingles, sweepers — station imaging
  • sfx — sound effects
  • mashups — mashup content
  • shorts — TikTok-style clips (hard-cut, 12–60s)
  • talkshow, programs — longer-form content
  • influencers — social-influencer voice clips

See Rundown generation for the full mapping and rundown JSON format.