999vradio
Automated HLS stream capture and real-time keyword extraction from live radio broadcasts using Ollama LLM analysis.
A production pipeline that captures Virgin Radio Toronto's 24/7 HLS stream, transcribes it with whisper.cpp, and uses Ollama to identify contest keywords in real-time. Keyword detections flow into MySQL, trigger Discord notifications, and optionally send SMS alerts.
- Node.js
- whisper.cpp
- Ollama
- FFmpeg
- MySQL
- HLS (HTTP Live Streaming)
- Discord webhooks
- voip.ms SMS API
The core pipeline runs in Node.js and polls the Virgin Radio HLS manifest every 2 seconds, detecting segment boundaries by title metadata. When a new segment arrives, ffmpeg converts the AAC chunks to MP3 and streams them into transcription. I integrated whisper.cpp for local speech-to-text because cloud APIs would be too slow and expensive for continuous 24-hour capture.
Transcription output feeds into Ollama (locally hosted LLM) with a carefully tuned prompt that identifies contest keywords. The system recognizes multiple patterns—copular phrases like 'the keyword is X', appositive noun phrases, even letter-by-letter spelling ('S-U-M-M-E-R'). To avoid false positives, keywords are deduplicated per day and suppressed if a slot is already filled mid-hour; late-minute announcements (after :55) are allowed to advance to the next hour slot. Keywords live in MySQL, timestamped with their hourly contest slot.
Notifications reach Discord via webhook, SMS via voip.ms API, and desktop alerts via node-notifier. The system handles stream restarts gracefully—a 410 Gone error triggers re-fetch of the current playlist URL. I added extensive hardening flags (feature-gated with environment variables) for presampling, JSON output validation, child process cleanup, and stdin write guards to prevent race conditions during long-running captures.
- HLS manifest polling with automatic bandwidth selection—chooses the highest-quality stream variant available from StreamTheWorld's multi-bitrate ladder
- Real-time speech-to-text via whisper.cpp using local GGML quantized models, avoiding cloud API latency and cost
- Prompt-engineered keyword detection with Ollama (supports gemma4 and other models) including pattern recognition for copular, appositive, and spelled-out keywords
- Multi-channel notifications: Discord webhooks, SMS via voip.ms API, and desktop alerts; keyword deduplication and per-day slot collision handling
- MySQL persistence with connection pooling and auto-reconnect; captures organized by hourly subdirectories (YYYY/MM/DD/HH structure)