The intelligent TV station manager with adaptive AI hosts and distributed playback for DIY hackable TV stations, radio, and digital signage.
Slatron allows you to manage content, schedule broadcasts, and control playback nodes across your network from a centralized, web-based dashboard. It features fully autonomous AI DJs that can introduce tracks, read news, and manage the vibe of your station.
- Adaptive AI DJs: Create diverse AI personalities to host your station, with support for Google Gemini, Anthropic Claude, OpenAI, Ollama, and local LLMs (Orpheus).
- Dynamic Bumpers & Station Branding: Create professional station idents, transitions, and lower thirds using MLT templates with variable substitution. Upload custom bumper backgrounds or use built-in animated gradients.
- Centralized Management: Manage multiple playback nodes (TVs, screens) from a single server.
- Flexible Scheduling: Drag-and-drop schedule grid with layered priorities and interrupt scheduling.
- Role-Based Access Control (RBAC): Secure your station with
Admin,Editor, andViewerroles. - Real-time Monitoring: Live status updates and screenshots from nodes.
- Scripting Engine: Use Rhai scripts for dynamic content loading, overlays, playback logic, and automated bumper injection.
- Resilient Playback: Local caching on nodes ensures playback continues even if the network goes down.
- Content Library: Organize video files and manage valid paths per node with YouTube playlist support.
- Rust (1.75+)
- Node.js (18+) - For building the UI
- SQLite - For the server database
- MPV - Media player with IPC support
- yt-dlp - For YouTube content support (MPV integration)
- MLT Framework (
meltcommand) - For rendering bumper templates - FFmpeg (
ffprobecommand) - For extracting video duration
- libort - Required only if building server with
ml-supportfeature for local Orpheus TTS - curl - For downloading remote bumper backs (usually pre-installed)
The server manages the database, API, and serving the web interface. It will automatically initialize the database and run migrations on startup.
cd slatron-server
# 1. Setup Database Directory
mkdir -p data
# 2. Build & Run (Embedding the UI for ease of use)
# This requires `npm` to be in your path, as it builds the UI automatically.
cargo run --release --features embed-ui -- --generate-config > config.toml
# Edit config.toml if needed, then:
cargo run --release --features embed-uiThe server will start on http://0.0.0.0:8080 (or your configured port).
Default Login: admin / admin
The node runs on the computer connected to the display (e.g., a Raspberry Pi or mini PC).
cd slatron-node
# 1. Generate a config template
cargo run --release -- --generate-config > config.toml
# 2. Edit config.toml
# - Set `server_url` to your server's WebSocket URL (e.g., ws://192.168.1.10:8080/ws)
# - You will need a `secret_key`. Log into the Web UI, go to "Nodes", create a node, and copy its secret.
# 3. Run the node
cargo run --releaseNote: Ensure mpv and yt-dlp are installed and available in the system PATH. Slatron will launch and control MPV automatically.
Installing Node Dependencies (macOS):
brew install mpv yt-dlpInstalling Node Dependencies (Linux/Debian):
sudo apt install mpv yt-dlpInstalling Server Dependencies (macOS):
brew install mlt ffmpegInstalling Server Dependencies (Linux/Debian):
sudo apt install ffmpeg mltBoth components support CLI arguments for configuration management.
- Generate Template:
slatron-server --generate-config(Prints to stdout) - Specify File:
slatron-server --config my_config.toml - Default / Interactive: Looks for
config.tomlin the current directory. If not found and running in an interactive terminal, it will launch an Onboarding Wizard to help you generate one.
[server]
host = "0.0.0.0"
port = 8080
# Optional: Path to serve UI files from.
# If omitted and compiled with `embed-ui`, it uses the internal zip.
# ui_path = "./static"
[server.https]
enabled = false
# cert_path = "/path/to/cert.pem"
# key_path = "/path/to/key.pem"
[database]
url = "sqlite://data/slatron.db"
[jwt]
secret = "CHANGE_THIS_IN_PRODUCTION"
expiration_hours = 24node_name = "Lobby Display"
server_url = "ws://localhost:8080/ws"
secret_key = "PASTE_SECRET_FROM_UI"
heartbeat_interval_secs = 5
mpv_socket_path = "/tmp/mpv-socket"Slatron features a dynamic AI DJ system that can "speak" between songs, introducing tracks, reading news, or just creating a vibe.
You can configure different LLM providers for DJ personality:
- Google Gemini: High-quality cloud-based models
- Anthropic Claude: Claude 3.5 Sonnet and other Claude models
- OpenAI: GPT-4 and other OpenAI models
- Ollama: Local LLM server for privacy and cost savings
You can configure different voice providers for DJ speech synthesis:
- Gemini TTS: High-quality cloud-based TTS from Google
- Orpheus (Local): Fully offline, local text-to-speech engine powered by specific LLM checkpoints
"Orpheus" allows you to run a high-quality DJ voice entirely on your own hardware (no API costs).
- Install LM Studio: Download and install LM Studio.
- Download Model: Search for and download the
isaiahbjork/orpheus-3b-0.1-ftmodel. - Start Local Server:
- Load the model in LM Studio.
- Go to the "Local Server" tab.
- Start the server on port
1234(default).
- Configure Slatron:
- In the Slatron Dashboard, go to Settings > AI Providers.
- Add a new provider with type Orpheus.
- Set the Endpoint URL to:
http://127.0.0.1:1234/v1/completions. - Important: You must compile the server with the
ml-supportfeature enabled (cargo run --features ml-support) as Slatron handles the audio decoding (SNAC) locally. The server when compiled with this option embeds the rather sizeable model, but obviously for space concerns it can be compiled with local orpheus support for said reason.
The Voice Name field in the DJ Profile maps differently depending on the provider:
- Gemini TTS: Use standard model names (e.g.,
Aoede,Charon,Fenrir,Puck,Zephyr). - Orpheus: Use the specific character name supported by the model (e.g.,
tarafor the default fine-tune).
DJs can be made "aware" of their environment using Context Scripts. These are server-side Rhai scripts that run before the prompt is sent to the LLM.
- Logic: The script can fetch data (weather, stock prices, internal server state) and must return a String.
- Injection: The returned string is appended to the "Context" block in the system prompt.
- Example Script:
// Get weather for station location let weather = http_get("generic.weatherapi.com/NY"); context += "Current weather in NY is " + weather + ". Mention it if relevant.";
You can also use Rhai scripts to bulk import content (e.g., from YouTube playlists or RSS feeds).
- Location: "Configuration > Scripts". Create a script of type
Content Loader. - Usage: In "Content > Add Content", switch to "Import via Script".
- Return Format: The script must return a JSON Object (for one item) or a JSON List (for bulk import).
[ { "title": "Video 1", "url": "https://...", "duration_minutes": 5 }, { "title": "Video 2", "url": "https://...", "duration_minutes": 10 } ]
Slatron includes a powerful bumper system for creating professional station branding elements like idents, transitions, and lower thirds.
The bumper system uses a two-layer approach:
-
Bumper Backs: Base video files that provide background animation or graphics
- Can be simple MLT-generated backgrounds (solid colors, gradients)
- Or user-uploaded video files (.mp4, .mov, .webm)
- Or downloaded from remote URLs
-
Bumper Templates: MLT templates that overlay station branding on top of bumper backs
- Support variable substitution:
{{STATION_NAME}},{{THEME_COLOR}},{{BUMPER_BACK_PATH}} - Rendered to final MP4 videos using the MLT Framework
- Support variable substitution:
Slatron ships with:
- 3 Simple Bumper Backs: Solid Blue, Solid Purple, Solid Grey (MLT-generated)
- 1 Station Ident Template: Displays station name with theme color outline over bumper back
-
Upload a Bumper Back Video (optional):
- Navigate to Bumpers β Bumper Backs in the UI
- Upload your animated background video or download from URL
-
Create a Bumper Template:
- Go to Bumpers β Create New Bumper
- Write an MLT XML template using variable substitution
- Link to a bumper back for the background layer
- Click "Render" to generate the final video
-
Automated Injection:
- Use the built-in "Auto Station Bumpers" global script
- Or write custom Rhai scripts using
inject_bumper("Bumper Name")
<?xml version="1.0"?>
<mlt LC_NUMERIC="C" version="7.0.0">
<profile description="HD 1080p 30 fps" width="1920" height="1080"
progressive="1" frame_rate_num="30" frame_rate_den="1"/>
<!-- Bumper back as background -->
<producer id="back" mlt_service="avformat">
<property name="resource">{{BUMPER_BACK_PATH}}</property>
</producer>
<!-- Station name overlay -->
<producer id="text" mlt_service="pango">
<property name="family">Montserrat</property>
<property name="size">150</property>
<property name="markup">{{STATION_NAME}}</property>
<property name="fgcolour">#ffffff</property>
<property name="outline">3</property>
<property name="olcolour">{{THEME_COLOR}}</property>
</producer>
<!-- Composite layers -->
<tractor id="tractor0" in="0" out="149">
<track producer="back"/>
<track producer="text"/>
<transition mlt_service="composite">
<property name="a_track">0</property>
<property name="b_track">1</property>
<property name="geometry">0/0:100%x100%:100</property>
</transition>
</tractor>
</mlt>// Queue a bumper for playback
inject_bumper("Station Ident");
// Check if it's the top of the hour (:00)
if is_top_of_hour() {
inject_bumper("Station Ident");
}
// Get current hour (0-23)
let hour = get_current_hour();
if hour >= 6 && hour < 22 {
inject_bumper("Daytime Transition");
}
To simplify deployment, you can embed the frontend files directly into the server binary.
-
Enable the
embed-uifeature during compilation:cargo build --release --bin slatron-server --features embed-ui
This triggers a build script that runs
npm run buildinslatron-uiand zips the result. -
Run the binary anywhere. It will automatically extract the UI to an
embedded_uidirectory at startup and serve it.- No need to copy a
static/folder manually!
- No need to copy a
Secure your server by enabling HTTPS in config.toml. You will need a certificate and private key (e.g., from Let's Encrypt).
- slatron-server (Rust/Axum): The brain. Handles database, API, Auth, and WebSockets.
- slatron-node (Rust): The player. Connects to server, downloads content/schedules, and controls MPV via IPC.
- slatron-ui (React/Vite): The face. communicating via REST API.
Slatron uses the Rhai scripting language for safety and flexibility.
- Content Loaders: Custom logic to fetch/prepare content (e.g., download from YouTube).
- Global Scripts: Run logic based on playback events (e.g., "If content < 10s, loop it").
Nodes send heartbeats every 5 seconds. If the server doesn't hear from a node for 30 seconds, it automatically marks it as Offline in the dashboard.
Scripts in Slatron allow you to customize playback behavior and interactivity.
These functions are called automatically by the node during the playback lifecycle.
fn transform(settings)->Map- When: Called before playback starts.
- Purpose: Update settings (volume, loop, start/end time) based on custom logic.
- Returns: A Map of settings to override.
fn on_load(settings)- When: Called immediately after playback starts.
- Purpose: Apply overlays, start auxiliary processes, or log events.
fn on_unload(settings)- When: Called before the content changes or stops.
- Purpose: Cleanup (e.g., remove overlays).
set_volume(ctx, volume: int): Set playback volume (0-100).set_loop(ctx, enabled: bool): Enable/Disable looping.set_start_time(ctx, seconds: float): Start playback from offset.set_end_time(ctx, seconds: float): Stop playback at offset.mpv_send(command_map): Send a raw JSON command to MPV (Node-side only).
download_file(url, output_path): Downloads a file. Supports~expansion on nodes.shell_execute(command): Executes a shell command and returns output.get_env(key): Returns value of an environment variable.
mpv_overlay(path, x, y, opacity): Display an image overlay.mpv_text(text, x, y, size, color): Display text overlay.mpv_remove_overlay(id): Remove an overlay.get_video_width()/get_video_height(): Get resolution of current content.
inject_bumper(name_or_id): Queues a bumper to be played immediately after the current item finishes.is_top_of_hour(): Returnstrueif the current time is within the first few minutes of the hour (useful for scheduling idents).get_current_hour(): Returns the current hour (0-23) in the station's timezone.
Slatron is licensed under the GNU Affero General Public License v3.0 (AGPLv3). See the LICENSE file for details.
Created by Justin Woodring & SLATRON AUTHORS