Skip to main content

News Bulletins (news)

The news deck is the station's spoken top-of-hour bulletin — the single most "radio" element that was missing before bulletin-engine.js (2026-08-11). Each bulletin is a R128-mastered Arabic spoken piece (~35–37 s) generated entirely by the bulletin engine into media/news/NEWS_<ts>.mp3.

The bulletin recipe

A bulletin is four TTS segments concatenated with 0.55 s of silence between them:

#SegmentContent
1Station open + Cairo time check"إنها الآن الساعة التاسعة مساءً بتوقيت القاهرة" — word-hours + minutes + period (صباحاً / ظهراً / مساءً / ليلاً)
2WeatherOpen-Meteo Cairo: temperature + WMO description + wind
3Headlines"وأبرز العناوين: …" — up to 3 lines from E:\radionew\news.txt
4Hand-back"الآن عودة للموسيقى"

TTS & loudness

  • Voice: ar-EG-ShakirNeural, rate +8 % (edge-tts).
  • Text is passed via a -f temp file, never shell args — Arabic text and backticks in headlines would break command quoting.
  • Weather from Open-Meteo (free, no key) for Cairo; news.txt is editable, 1 headline per line, the first 3 are read.

The equations

Time-check sentence

Given hour h (0–23), minute m and period label, the Arabic sentence maps:

time(h,m)=word(h)+word(m)[m0]+period(h)\mathrm{time}(h,m) = \text{word}(h) + \text{word}(m) \cdot [m \neq 0] + \text{period}(h)

where [m ≠ 0] is an Iverson bracket — the minutes word is appended only when the minute is non-zero, and period(h) selects صباحاً / ظهراً / مساءً / ليلاً from the hour.

Loudness

Identical to every generator — two-pass EBU R128 to −14 LUFS / −1.5 dBTP / LRA ≤ 11, so a bulletin sits at exactly the same perceived volume as the songs:

Ltarget=14 LUFS,TPmax=1.5 dBTPL_{target} = -14\ \mathrm{LUFS},\qquad TP_{max} = -1.5\ \mathrm{dBTP}

How the scheduler slots it

  • NEWS_EVERY cadence (~every 20–24 slots ≈ hourly) forces a news pick — same pattern as the AD_EVERY slot; news airs in all dayparts including deep night.
  • Imaging cooldown (10 min); liq_cross_duration = 0.5 (below the ~35 s bulletin).
  • The news deck is exempt from the 45 s music-floor quality gate — a 35 s bulletin must not fail just because it is shorter than a song.
  • Hourly auto-generation: scheduleBulletinAutoGen() (boot +30 s, then every BULLETIN_HOURS, default 1 h, chained setTimeout; 0 disables). A generated bulletin is immediately added to the deck and the rotation resynced.

The SYSTEM-session TTS gotcha

The engine runs as SYSTEM (ServerGuard, session 0) where bare python resolves to the WindowsApps stub — edge-tts would fail silently. PYTHON_BIN defaults to the absolute C:/Users/omar/AppData/Local/Programs/Python/Python312/python.exe. If bulletin generation reports all-tts-failed, check that path first.

Endpoints & CLI

  • POST /api/bulletin/generate (generate + resync rotation)
  • GET /api/bulletin/status (latest file, headlines, live time-check sentence)
  • MCP: generate_bulletin, bulletin_status

Verify on air

  1. media/news holds NEWS_*.mp3 (typically one, replaced hourly).
  2. m3u: annotate:type=news,liq_cross_duration=0.5:.
  3. The bulletin appears ~once per hour in the rotation.
  4. ffprobe: ~35–37 s, Break-tagged, R128 ≈ −14 LUFS.
  5. To change headlines: edit E:\radionew\news.txt, then POST /api/bulletin/generate (or wait for the next hourly run).