Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

17 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“»

AI Radio Station

Your own AI-powered radio station β€” live, personalised, and always on.

Demo Β· Features Β· Setup Β· Usage Β· Configuration


AI Radio Station Screenshot

AI Radio Station turns your Spotify playlists into a real radio experience. An AI host introduces songs, delivers weather and news updates, and keeps the energy going between tracks β€” all spoken aloud by a natural-sounding voice. Fully configurable via a single YAML file.


Features

πŸŽ™οΈ AI Radio Host

Define a host persona with a name, tone, and style. The AI generates contextual on-air commentary β€” song intros, time checks, energy bursts β€” that sounds like a real radio DJ, not a robot.

🎡 Spotify Integration

Plays music from your own Spotify playlists and shuffles between them. No separate audio setup required β€” it works with any active Spotify device or can register directly as a Spotify Connect device on your machine.

πŸ”Š Text-to-Speech via ElevenLabs

All AI-generated commentary is synthesised with ElevenLabs, giving your host a consistent, lifelike voice. Choose any voice from the ElevenLabs library and swap models for different quality/latency tradeoffs.

🌀️ Live Weather Updates

The built-in weather tool fetches real-time conditions for any location using the Open-Meteo API (no key required). The host weaves current temperature, wind, and sky conditions into natural-sounding updates on a schedule you control.

πŸ“° Live News Briefings

Configure any RSS feed (BBC, Tagesschau, or your own) and the host will summarise the latest headlines in a conversational way β€” no verbatim reading, just the vibe of a real news break.

πŸ“‚ Genre Blocks & Group Rotation

Group your playlists by genre or mood and have the station rotate through them one block at a time. Each group plays for a configured duration or number of songs, then the host delivers a narrated transition into the next genre. Groups play in random order and loop indefinitely.

πŸŽ›οΈ Audio Ducking

When the host speaks over music, the volume fades down automatically and fades back up when the segment ends β€” exactly like a real radio broadcast. Requires the librespot Spotify Connect device; falls back to pause/resume without it.

πŸ“… Flexible Segment Scheduling

Control when and how often the host speaks with a simple trigger system:

Trigger Description
show_start Fires once when the station launches
show_end Fires once when the station stops
before_song Fires before a new track plays (with song name & artist)
after_song Fires after a track ends
interval_minutes Fires on a repeating timer (e.g. every 30 minutes)
group_transition Fires between genre/group blocks (with {{group.name}} and {{group.next_name}})

🌍 Multi-language Support

Set a language code and pair it with a multilingual ElevenLabs model to run your entire show in any language.

πŸ€– Multi-provider AI

Supports Anthropic, OpenAI, and Google (Gemini) out of the box. Switch providers with a single line in your config.

πŸ”Œ MCP Server Integration

Connect to any Model Context Protocol server over HTTP/SSE to pull live data into your segments. Define servers once in your config, then reference individual tools per segment β€” the result is injected into your prompt as {{mcp.<server>.<tool>}}.

🍎 macOS Integration

On macOS, the station integrates natively with the operating system:

  • Now Playing widget β€” current song, artist, and album art appear in the macOS media controls (Control Centre, lock screen, and Touch Bar). Pause, resume, and skip work from any standard media key or the widget itself.
  • Menu bar icon β€” a β™ͺ icon in the menu bar gives quick access to all controls without switching to the terminal: pause/resume, skip song, switch genre group or playlist, trigger any segment, and quit.

Requires the native helper binary β€” see Setup step 2 for build instructions.

⌨️ Interactive Controls

Control playback from the terminal while the show runs:

Key Action
Space Pause / Resume
N Skip to next song
S Transition to next song or segment
H Show help
Ctrl+C Stop the show

Demo

See the project in action with this quick demo of a show running with a mix of segments, including song intros, weather updates, and genre transitions (sound on πŸ”Š):

ai-radio-station.mp4

In the demo the segments were triggered manually via the interactive controls for demonstration purposes, but in a real show you'd rely on the automatic triggers (e.g. before_song, interval_minutes, etc.) to schedule everything.

Prerequisites

  • Bun runtime
  • A Spotify account with a registered app (free)
  • An ElevenLabs API key
  • An API key for your chosen AI provider (Anthropic / OpenAI / Google)
  • (Optional) librespot for audio ducking: brew install librespot
  • (Optional, macOS only) Xcode Command Line Tools for the Now Playing widget and menu bar: xcode-select --install

Setup

1. Clone and install dependencies

git clone https://github.com/BetaHuhn/ai-radio-station
cd ai-radio-station
bun install

2. Build the macOS native helper (optional, macOS only)

This compiles the Swift helper that powers the Now Playing widget and menu bar:

bun run build:native

The binary is written to bin/NowPlayingHelper. If you skip this step the station runs fine β€” you just won't get the macOS integrations.

3. Configure environment variables

Create a .env file in the project root:

# Spotify β€” from https://developer.spotify.com/dashboard
SPOTIFY_CLIENT_ID=your_spotify_client_id

# ElevenLabs β€” from https://elevenlabs.io/app/settings/api-keys
ELEVENLABS_API_KEY=your_elevenlabs_api_key

# AI provider β€” set the key for whichever provider you choose
ANTHROPIC_API_KEY=your_anthropic_api_key
# OPENAI_API_KEY=your_openai_api_key
# GOOGLE_GENERATIVE_AI_API_KEY=your_google_api_key

When registering your Spotify app, add http://localhost:8888/callback as a Redirect URI.

4. Authenticate with Spotify

bun run auth

This opens a browser for OAuth and saves tokens to ~/.ai-radio/spotify-tokens.json.

5. Configure your show

Copy the example config and customise it:

cp show.example.yaml show.yaml

Edit show.yaml to set your host name, tone, playlists, AI provider, ElevenLabs voice, and segments. See Configuration below.


Running

# Start the show
bun start

# Start with a custom config file
bun start --config my-show.yaml

# List available Spotify Connect devices
bun start --list-devices

# Target a specific device
bun start --device "Living Room Speaker"

Configuration

All show settings live in show.yaml. Here's a quick overview of the main sections:

show:
  name: "Morning Drive"         # Show name used in prompts
  host: "Alex"                  # Host name used in prompts
  tone: "upbeat, energetic"     # Tone used in prompts
  style: "morning drive radio"  # Style used in prompts
  language: "en"                # BCP 47 language code
  location: "New York, US"      # Location for weather tool
  news_feed: "https://..."      # RSS feed URL for news tool
  connect_device_name: "AI Radio Station"  # librespot device name
  segment_audio: "duck"         # "duck" or "pause"
  duck_volume: 15               # Volume % while host is speaking

ai:
  provider: "anthropic"         # anthropic | openai | google
  model: "claude-sonnet-4-6"

elevenlabs:
  voice_id: "..."               # From elevenlabs.io/app/voice-library
  model_id: "eleven_turbo_v2_5"

spotify:
  # Flat playlist mode
  playlists:
    - id: "..."
      name: "My Playlist"

  # Or group playlists by genre/mood for block rotation
  groups:
    - id: country
      name: Country Roads
      transition_after_minutes: 20   # switch after 20 minutes …
      transition_after_songs: 8      # … or after 8 songs (whichever fires first)
      playlists:
        - id: "..."
          name: "Dirt Roads"
    - id: pop
      name: Pop Hits
      transition_after_minutes: 15
      playlists:
        - id: "..."
          name: "Top Pop"

segments:
  - id: "intro"
    trigger: "show_start"
    prompt: >
      Kick off {{show.name}} with a short energetic intro...

Segment Triggers

Trigger Description
show_start Fires once at the start of the show
show_end Fires once at the end of the show
before_song Fires before a new song starts (has access to {{track.name}} and {{track.artist}})
after_song Fires after a song ends (has access to {{track.name}} and {{track.artist}})
interval_minutes Fires at regular time intervals (e.g. every 30 minutes, has access to {{time}})
group_transition Fires between genre/group blocks when using spotify.groups (has access to {{group.name}} and {{group.next_name}})

Segment Intervals

For before_song and after_song triggers, you can specify an interval to control how often the segment fires. For example, interval: 4 means the segment will fire every 4 songs.

For interval_minutes, specify the number of minutes between firings with interval_minutes: 30 to fire every 30 minutes.

Segment Tools

Segments can optionally use tools to fetch live data. Just add a tools list to the segment config:

- id: "update"
  trigger: "interval_minutes"
  interval_minutes: 30
  tools:
    - name: weather
      location: "Los Angeles, US"
    - name: news
      url: "https://feeds.bbci.co.uk/news/world/rss.xml"
      count: 3
  prompt: >
    Give a quick weather update for {{weather.location}} β€” right now it's {{weather.temperature}}Β°C and {{weather.description}}.
    Also, summarise the latest news: {{news.headlines}}.

Built-in tools include:

  • weather: Fetches current weather conditions for the show's configured location using the Open-Meteo API.
  • news: Fetches the latest headlines from the show's configured RSS feed.

MCP Server Tools

You can connect to any MCP server that exposes an SSE endpoint and call its tools from segments. Declare your servers in the top-level mcp block, then reference them in a segment's tools list:

mcp:
  servers:
    - name: my-server           # Unique name used to reference this server
      url: http://localhost:3000/sse

segments:
  - id: "tech_update"
    trigger: "interval_minutes"
    interval_minutes: 45
    tools:
      - mcp: my-server
        tool: get_headlines
        arguments:
          category: technology
          count: 3
    prompt: >
      Here are the latest tech headlines: {{mcp.my-server.get_headlines}}
      Summarise them in a casual, radio-friendly way.

The tool result is available as {{mcp.<server-name>.<tool-name>}} in the segment prompt.

Custom Tools

You can also built and integrate your own custom tools by specifying a TS file to run which exports a default function that returns data to be used in prompts. See tools/weather.ts for an example.

To use a custom tool, add it to the tools list for a segment and specify the path to the tool file:

- id: "custom_update"
  trigger: "interval_minutes"
  interval_minutes: 30
  tools:
    - path: ./my-tool.ts
  prompt: >
      Here's a custom update: {{custom_data.value}}

Segment Prompt Variables

Prompts support template variables that are resolved at runtime:

Variable Available in
{{show.name}}, {{host}}, {{tone}} All triggers
{{track.name}}, {{track.artist}} before_song, after_song, group_transition
{{time}} interval_minutes
{{group.name}}, {{group.next_name}} group_transition
{{weather.*}} Segments with tools: [weather]
{{news.source}}, {{news.headlines}} Segments with tools: [news]
{{mcp.<server>.<tool>}} Segments with an MCP tool entry

CLI Reference

bun start [options]

Options:
  --config <path>     Path to show YAML config (default: show.yaml)
  --auth              Authenticate with Spotify
  --list-devices      List available Spotify Connect devices
  --device <name>     Target a specific Spotify Connect device
  --help, -h          Show help

Contributors

Languages