-
Notifications
You must be signed in to change notification settings - Fork 0
Smart Blocks
Smart blocks are rule-based content selectors that automatically pick tracks from your media library. Instead of manually building playlists, you define rules and the system builds the playlist for you — fresh every time.
A smart block has four types of rules:
| Rule Type | Purpose |
|---|---|
| Include Filters | Tracks must match ALL of these to be eligible |
| Exclude Filters | Tracks matching ANY of these are rejected |
| Weights | Preferred tracks get a score boost (more likely to be picked) |
| Quotas | Enforce min/max counts for specific categories |
Plus three behavioral settings:
| Setting | Purpose |
|---|---|
| Separation | Prevent repeating the same artist/title/album/label within a time window |
| Duration | Target length of the generated playlist |
| Fallbacks | Other smart blocks to try if this one can't fill the slot |
All include filters use AND logic — a track must match every filter to be eligible.
| Field | Match Type | Example Value | Notes |
|---|---|---|---|
genre |
Exact (case-insensitive) | "Rock" | |
mood |
Exact (case-insensitive) | "Upbeat" | |
artist |
Normalized | "The Beatles" | Strips punctuation/spaces before comparing |
album |
Exact | "Abbey Road" | |
title |
Exact | "Yesterday" | |
label |
Exact | "Atlantic Records" | |
language |
Exact | "en" | |
tags |
Tag match | (selected in UI) | Track must have at least one of the specified tags |
text_search |
Fuzzy | "love" | Searches across title, artist, and album |
bpm |
Range | min: 120, max: 140 | |
year |
Range | min: 1990, max: 1999 | |
explicit |
Boolean | true/false | |
source_playlists |
Playlist membership | (selected in UI) | Track must belong to one of these playlists |
include_public_archive |
Boolean | true | Also consider tracks from other stations marked as public |
Same fields as include filters, but inverted — any track matching an exclude rule is dropped from the candidate pool.
Common use: exclude explicit = true to keep a clean broadcast.
Weights nudge the selection toward preferred content without being absolute filters. A track with a higher score is more likely to be chosen, but lower-scored tracks can still appear.
| Field | Value | Weight | Effect |
|---|---|---|---|
genre |
"Jazz" | +2.0 | Jazz tracks are twice as likely to be picked |
mood |
"Chill" | +1.5 | Chill tracks get a moderate boost |
tag |
(tag UUID) | +3.0 | Tagged tracks get a strong boost |
new_release |
7 (days) | +2.0 | Tracks uploaded in the last 7 days get a boost |
The new_release weight is particularly useful for shows that want to feature recently added content.
- Every eligible track starts with a base score of 1.0
- Each matching weight rule adds its weight value
- Energy curve alignment adds a bonus (if configured)
- A small random jitter (0-0.1) is added to break ties
- Tracks are selected highest-score-first
Quotas enforce minimum and maximum counts for categories within the generated playlist.
| Field | Values | Min | Max | Effect |
|---|---|---|---|---|
genre |
["Pop"] | 2 | 5 | At least 2 Pop tracks, at most 5 |
artist |
["Radiohead"] | 0 | 2 | No more than 2 Radiohead tracks |
explicit |
["true"] | 0 | 1 | At most 1 explicit track |
- Max = 0 means no upper limit
- Min unmet generates a warning but doesn't prevent generation
- Max exceeded hard-stops selection of that category
Separation prevents the same content from repeating too soon. The engine checks both the play_history table (what already aired) and the in-progress playlist being built.
| Rule | Typical Value | Effect |
|---|---|---|
| Artist Separation | 60 minutes | Same artist won't appear within 60 minutes |
| Title Separation | 240 minutes | Same track won't repeat within 4 hours |
| Album Separation | 30 minutes | Different album each half hour |
| Label Separation | 0 (disabled) | No label separation |
Important: Setting separation too high for a small library will cause the engine to relax constraints (see Progressive Relaxation below).
Set how long the smart block should fill:
- Target Minutes: e.g., 55 minutes for a 1-hour slot
- Tolerance: Acceptable overshoot in seconds (e.g., 2 seconds)
If the target is not set, it defaults to the clock slot's duration. If that's also unset, it defaults to 15 minutes.
If the primary rules can't find enough tracks (library too small, rules too strict), the engine tries fallback smart blocks in order:
- Fallback Block 1 — e.g., a broader "All Music" block
- Fallback Block 2 — e.g., an even broader "Emergency Mix"
- Maximum chain depth: 3 levels
Each fallback can have a Limit (max tracks to take from it), so you can say "take at most 3 tracks from the fallback, then stop."
When the engine can't find enough candidates, it automatically relaxes constraints in stages:
| Level | What's Dropped | Warning |
|---|---|---|
| 0 | Nothing (strict mode) | — |
| 1 | Separation rules dropped | constraint_relaxed:1 |
| 2 | Separation + quotas dropped | constraint_relaxed:2 |
| 3 | Separation + quotas + exclude rules dropped | constraint_relaxed:3 |
If all 4 levels fail, the fallback chain is tried. If that also fails, the scheduler picks a random track as an emergency fallback.
The energy curve controls the "flow" of the generated playlist. Define a sequence of energy targets (0-100) and the engine tries to match each position:
Curve: [60, 70, 80, 90, 80, 70] → Build → Peak → Ease down
Track energy is derived from BPM (preferred) or ReplayGain (fallback). The engine scores each candidate based on how close its energy is to the target for that position.
- Go to Dashboard → Smart Blocks → New
- Fill in the name and description
- Configure your include/exclude filters using the dropdowns (genres, moods, and tags are populated from your library)
- Set separation rules
- Optionally configure weights, quotas, energy curve
- Set the target duration
- Click Create
After creating a block, use the Preview button to see what tracks the engine would pick. You can generate multiple variants to see the diversity of output.
POST /api/v1/smart-blocks/{blockID}/materialize
{
"station_id": "your-station-id",
"mount_id": "your-mount-id",
"duration_ms": 3600000
}Returns a GenerateResult with the track list, total duration, and any warnings.
Include: (none — picks from everything)
Exclude: explicit = true
Separation: artist 90 min, title 360 min
Target: 55 minutes
Include: genre = "Rock", year range 1990-1999
Separation: artist 60 min, title 240 min
Weight: mood "Energetic" +2.0
Target: 55 minutes
Include: genre = "Jazz", mood = "Chill"
Exclude: tags = "vocal-jazz"
Separation: artist 45 min
Weight: new_release 30 days +1.5
Target: 115 minutes (for a 2-hour slot)
Include: tags = "sunday-show"
Weight: new_release 7 days +10.0
Separation: (none — show files are unique)
Target: 120 minutes
Smart blocks support automatic insertion of short clips between tracks:
- Every N tracks: Insert a clip (e.g., every 4 songs)
- Source: A playlist of jingles/station IDs
- Per Break: How many clips per insertion point (1-3)
- Source: A playlist of bumper clips
- Max Per Gap: Maximum bumpers inserted between tracks
Configure these in the smart block's advanced settings. The engine inserts them during sequence generation, fitting them into the target duration.
- Clock Scheduling — How to assign smart blocks to time slots
- Scheduling Guide — End-to-end walkthrough for filling a station
Getting Started
Core Concepts
Scheduling
Deployment
Integration
Operations
Development
Reference