Skip to main content

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:

AreaWhere to read
System structure & data flowArchitecture
Automatic playout decisioningSmart Scheduler
Content acquisition & qualityContent Pipeline
Control plane & live eventsServer & API
Agent control (/api/station/*)Station Control
Live channel takeoverLive Manager & Live ingest
Social audio platformSocial Platform
Web & player frontendsWeb Frontends
The live broadcast chainBroadcast Chain
OperationsRunbooks and Scripts & Tools

What the system does

At a high level, the system continuously:

  1. Discovers and downloads music from YouTube and other sources via yt-dlp, keeping per-category playlists fresh.
  2. 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).
  3. 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.
  4. Loudness-normalizes every file to EBU R128 (−14 LUFS) using ffmpeg loudnorm, so songs, sweepers, jingles, and ads all sit at broadcast level.
  5. Generates its own imagingimaging-engine.js synthesizes sweepers, station IDs and layered stingers; fun-jingles.js adds Arabic karaoke jingles and a sound-design FX library (see Imaging and Fun jingles).
  6. 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_live harbor, taking the whole channel live.
  7. 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).
  8. 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 port 3456.
  • 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.