LOKLOK System — Overview
LOKLOK is an automated, AI-assisted online radio broadcast system built and operated by MediaHub Network. It runs a 24/7 music channel ("LOKLOK FM"), a live studio injection flow, a mobile WebRTC "Go Live" takeover, listener-submitted social audio with ranked on-air injection, a live radio PWA, chat, and a companion Next.js player, a native Flutter app, and a branded 1080p video channel — all driven from a single Node.js control plane.
This documentation is the complete engineering guide for the system. It covers:
| Area | Where to read |
|---|---|
| System structure & data flow | Architecture |
| Automatic playout decisioning | Smart Scheduler |
| Content acquisition & quality | Content Pipeline |
| Control plane & live events | Server & API |
Agent control (/api/station/*) | Station Control |
| Live channel takeover | Live Manager & Live ingest |
| Social audio platform | Social Platform |
| Web & player frontends | Web Frontends |
| The live broadcast chain | Broadcast Chain |
| Operations | Runbooks and Scripts & Tools |
What the system does
At a high level, the system continuously:
- Discovers and downloads music from YouTube and other sources via
yt-dlp, keeping per-category playlists fresh. - Sanitizes metadata — every track is probed with
ffprobe, its ID3 tags are cleansed, and the result is cached in a media registry (meta_registry.json). - Builds radio logs with a Smart Scheduler that uses Dirichlet sampling, energy-based sequencing, per-track cooldowns, genre adjacency, and reinforcement-learning mood arms so the sound stays dynamic but never repeats.
- Loudness-normalizes every file to EBU R128 (−14 LUFS) using
ffmpegloudnorm, so songs, sweepers, jingles, and ads all sit at broadcast level. - Generates its own imaging —
imaging-engine.jssynthesizes sweepers, station IDs and layered stingers;fun-jingles.jsadds Arabic karaoke jingles and a sound-design FX library (see Imaging and Fun jingles). - Injects live: station producers or the LiveManager can restream a
ranked live stream — or a mobile browser mic via the WHIP server — into the
Liquidsoap
channel_liveharbor, taking the whole channel live. - Ships the content to the production radio server (
10.10.8.230), where Liquidsoap picks the next track from the generated M3U playlists and crossfades it onto a live Icecast stream. Media is pushed over HTTP pull (scp was retired — see Media sync). - Streams over both Icecast (OGG) and MediaMTX (HLS), renders a
branded 1080p video program ("LOKLOK TV", see Video channel),
and serves Socket.io live events, web frontends, a PWA, a Next.js player, a
native Flutter app, and a companion chat server (long-poll REST on
:8094).
Two runtime locations
The project has a local (developer) node and a production (broadcast) node.
Local node — C:\Users\omar\radionew (Windows)
- All source code, schedulers, pipeline scripts.
- Media library on
E:/radionew(media, playlists, archives, models, downloads). - Dev API on port
5000; Next.js dev player on port3456. - Builds M3U playlists and syncs them (plus media) to the production node over
HTTP pull (token-gated
/api/sync/*). - Hosts the control-plane modules: social-platform.js, live-manager.js,
station-control.js, mcp-radio.js, station-config.js, plus the
content pipeline (
content-pipeline.js) with a hands-free 6-hour auto-refresh and a pool-health monitor that self-heals deck sizes. - Supervised by
LOKLOK_ServerGuard(SYSTEM scheduled task, 2-minute ticks) — a dead server is relaunched automatically even on console close/logoff.
Production node — 10.10.8.230 (/home/sms/radio, Ubuntu)
- Runs the broadcast worker: Liquidsoap (
radio_worker.liq, ducking + live switch), Icecast (files.mediahubnetwork.net:8005), the ffmpeg HLS bridge, MediaMTX (HLS on:19351, rtmp:19350, api:19352, listener proxy:19353, rtsp:19354, webrtc:19355), the WHIP server (whip_server.py:8089), the chat server (chat-server.js:8094), the Go server (:8093) serving the public PWA +/docs, watch/start scripts, and as-run logs. - Receives playlists + media from the local node over HTTP pull (curl) as user
sms; the file bytes never travel over ssh. - Hosts the installable radio PWA at
files.mediahubnetwork.net, this documentation site under/docs, the Next.js player at:4477, and the Flutter APK build at~/loklok_radio_app.
Key technologies
- Node.js control plane (Express 5 + Socket.io)
- smart-scheduler.js — Dirichlet / reinforcement-learning playout engine
- content-pipeline.js — content acquisition, sanitization, loudness, sync
- Gemini AI — content quality filtering & decisions
- yt-dlp — downloader; ffmpeg / ffprobe — processing & analysis
- fpcalc — Audio Fingerprinting (AcoustID Chromaprint) for duplicate detection
- Liquidsoap — live radio automation, crossfade, ducking, live switch
- Icecast + MediaMTX — OGG + HLS delivery
- aiortc / WHIP — mobile WebRTC live ingest (
whip_server.py) - Next.js 16 / React 19 — the companion player web app
- Socket.io — realtime events to every web frontend
- chat-server.js (zero-dep Node, remote) — long-poll chat + presence
- ai-providers.js — zero-dep multi-provider LLM client (12 providers)
Repository layout
radionew/
├── server.js # control plane: Socket.io + HTTP API
├── radio-engine.js # sync bridge (HTTP-pull) + telemetry
├── smart-scheduler.js # primary playout engine (anti-repeat + RL)
├── smart-scheduler-parameters.js
├── smart-scheduler-HOLISTC.js # experimental variants
├── smart-scheduler-MLRL.js
├── smart-scheduler-NN1.js
├── content-pipeline.js # discovery -> sanitize -> loudness -> decks
├── imaging-engine.js # sweeper/ID/voice-drop/stinger synthesis
├── fun-jingles.js # karaoke jingles + sound-design FX
├── bulletin-engine.js # top-of-hour spoken NEWS + Cairo time + weather (edge-tts)
├── content-manager.js # category manifests / singer catalogs
├── content-manager-gemini.js # Gemini-managed content decisions
├── gemini-service.js # Gemini API client helpers
├── paths.js # central path configuration (RADIO_ROOT/MEDIA_ROOT/…)
├── social-platform.js # listener-submitted audio overlay
├── live-manager.js # live stream ranking + restream -> harbor :8006
├── station-control.js # /api/station/* agent API
├── station-config.js # persisted config read/write (station-config.json)
├── ai-providers.js # zero-dep multi-provider LLM client
├── mcp-radio.js # MCP stdio server wrapping /api/station/*
├── server_guard.cmd # SYSTEM watchdog loop for server.js
├── radio_worker.liq # Liquidsoap broadcast script (backup copy)
├── public/ # classic web frontends (Socket.io)
├── radio-web/ # Next.js HLS player app
├── social-platform.js # listener-submitted audio overlay
└── scripts/ # operational utilities
Remote node key files (/home/sms/radio)
radio_worker.liq # Liquidsoap broadcast script (live)
playlists/main.m3u # the on-air rotation Liquidsoap watches (32768-byte padded)
html/ # public PWA (index.html) + APK (LOKLOK_FM.apk) + docs build (html/docs)
radio-docs/ # this Docusaurus docs source + deploy.sh
radio_server.py # python http server on :4479 (serves html/; also /abr_loklok.m3u8)
start_loklok_bridge.sh # ffmpeg HLS bridge watchdog (Icecast OGG -> MediaMTX RTMP 256k)
video_feed/ # LOKLOK TV renderer (video_bridge.sh, video_ticker.js, watch_video.sh)
radio-web/ # Next.js player + start-player.sh watchdog
chat-server.js # long-poll chat + presence (:8094)
mmtx_listeners_proxy.py # read-only MediaMTX listener proxy (:19353)
whip_server.py # WebRTC WHIP ingest (:8089) + go-live.html
whep_live.wav # FIFO: WHIP PCM -> ffmpeg -> harbor :8006
news.txt # bulletin + ticker headlines (1 per line, Arabic)
watch_*.sh / start_*.sh # self-healing watchdogs (cron)
Read on in Architecture for the end-to-end data flow, or jump to any section from the sidebar.