Skip to content

Scheduling Guide

code edited this page Mar 2, 2026 · 1 revision

Scheduling Guide

This guide explains how to fill your station with programming using Grimnir Radio's scheduling system. Whether you're building an all-music station, a talk/podcast format, or a mix of webstreams and local content, this is how you make it happen.

How Scheduling Works (The Big Picture)

Grimnir Radio uses a three-layer scheduling system:

┌─────────────────────────────────────────────────────┐
│  1. CLOCK TEMPLATES  (what plays each hour)         │
│     Define the structure: "6am-10am = Morning Mix"  │
├─────────────────────────────────────────────────────┤
│  2. SMART BLOCKS  (what content fills each slot)    │
│     Define the rules: "Play Rock from the 90s,      │
│     no repeats within 4 hours"                      │
├─────────────────────────────────────────────────────┤
│  3. SCHEDULE ENTRIES  (concrete timeline)           │
│     The scheduler materializes clocks + blocks      │
│     into actual entries: "13:04:22 → track.mp3"     │
└─────────────────────────────────────────────────────┘

Every 30 seconds, the scheduler looks at your clock templates, runs the smart block rules, and writes concrete schedule entries into the timeline. This happens automatically — you set up the rules and the system fills the schedule.

Step-by-Step: Fill a Station with Music

Step 1: Upload Your Media

Go to Dashboard → Media Library → Upload and upload your audio files. Each file gets analyzed automatically (duration, loudness, waveform). You can set metadata during or after upload:

  • Title, Artist, Album — standard tags
  • Genre — critical for smart block filtering (e.g., "Rock", "Jazz", "Podcast")
  • Mood — optional vibe tag (e.g., "Upbeat", "Chill", "Intense")
  • Tags — custom labels you create (e.g., "morning-friendly", "explicit", "sunday-show")
  • Year, BPM, Language — additional filtering criteria

Tip: Be consistent with your genre and mood names. Smart blocks filter on exact matches, so "Rock" and "rock" are the same, but "Classic Rock" and "Rock" are different genres.

Step 2: Create Smart Blocks

Smart blocks are rule-based content selectors. They answer the question: "Given these rules, what tracks should play?"

Go to Dashboard → Smart Blocks → New and configure:

Basic Filters (Include Rules)

Filter What it does Example
Genre Only pick tracks matching this genre "Jazz"
Mood Only pick tracks matching this mood "Upbeat"
Artist Only pick tracks by this artist "Miles Davis"
Tags Only pick tracks with these tags "morning-friendly"
BPM Range Only pick tracks in a tempo range 120-140 BPM
Year Range Only pick tracks from a year range 1990-1999
Language Only pick tracks in this language "en"
Text Search Search across title/artist/album "love"
Source Playlists Only pick from specific playlists Select playlists

Exclude Rules — same fields, but tracks matching these are rejected.

Separation Rules — prevent repetition:

  • Artist Separation: Don't repeat the same artist within X minutes (e.g., 60 min)
  • Title Separation: Don't repeat the same track within X minutes (e.g., 240 min)
  • Album Separation: Don't repeat tracks from the same album within X minutes
  • Label Separation: Don't repeat tracks from the same label within X minutes

Weights — nudge the selection toward preferred content:

  • Genre/Mood/Tag weight: Boost tracks matching a value (e.g., give "Upbeat" mood a +2.0 boost)
  • New Release weight: Boost tracks uploaded within the last N days (e.g., 7 days)

Target Duration: How long the block should fill (e.g., 55 minutes for a 1-hour clock slot, leaving room for bumpers).

Example: "Daytime Rock Mix"

Include: genre = "Rock"
Exclude: explicit = true
Separation: artist 60 min, title 240 min
Weight: mood "Upbeat" +1.5, new_release 14 days +2.0
Target: 55 minutes

This produces a ~55-minute playlist of non-explicit Rock, favoring upbeat tracks and anything uploaded in the last two weeks, with no artist repeating within an hour.

Step 3: Create Clock Templates

Clock templates define what type of content plays during each hour of the day. Each template covers a time window (e.g., 6am-10am) and contains one or more slots.

Go to Dashboard → Clocks → New:

  • Name: Descriptive label (e.g., "Morning Drive")
  • Start Hour: When this clock begins (0-23, in your station's timezone)
  • End Hour: When this clock ends (1-24, exclusive)

Then add slots to the clock:

Slot Type What it does Key setting
Smart Block Picks tracks using rules Select which smart block
Playlist Plays a specific playlist in order Select which playlist
Hard Item Plays one specific file Select which media item
Webstream Relays an internet stream Select which webstream
Stopset Commercial/underwriting break Select playlist or media

Each slot has:

  • Position: Order within the hour (0, 1, 2...)
  • Offset: When within the hour it starts (e.g., 0:00 for top of hour, 0:30 for half past)
  • Duration: How long the slot lasts (optional — smart blocks use their target duration)

Example: Full-Day Music Station

Clock Hours Slot Content
Morning Drive 6-10 Smart Block "Morning Mix" (upbeat pop/rock)
Midday 10-14 Smart Block "Variety Mix" (all genres)
Afternoon 14-18 Smart Block "Afternoon Chill" (mellow)
Evening 18-22 Smart Block "Evening Groove" (R&B/soul)
Late Night 22-2 Smart Block "Late Night Jazz"
Overnight 2-6 Smart Block "Ambient Overnight"

Step 4: Let the Scheduler Run

Once your clocks and smart blocks are set up, the scheduler automatically:

  1. Reads your clock templates
  2. Determines which clock applies to each hour
  3. Runs the smart block rules to pick specific tracks
  4. Creates concrete schedule entries in the timeline

You can see the results in Dashboard → Schedule (calendar view).

To force an immediate refresh: click Refresh Schedule or use the API:

POST /api/v1/schedule/refresh
{"station_id": "your-station-id"}

Clock Priority: Narrow Beats Broad

When multiple clocks overlap in time, the narrowest window wins. This lets you set up a catch-all clock and override specific hours:

Clock A: 0-24 (all day)     → "General Mix"     (24-hour window)
Clock B: 6-10 (morning)     → "Morning Drive"   (4-hour window)  ← WINS 6-10
Clock C: 8-9  (breakfast)   → "Breakfast Show"   (1-hour window)  ← WINS 8-9

At 8am, Clock C wins (narrowest). At 7am, Clock B wins. At midnight, Clock A wins.

Scheduling a Weekly Show

Scenario: Your friend has a show every Sunday 1-3pm. They upload their files a day or two before.

Method 1: Tagged Smart Block + Manual Schedule Entry

  1. Tag the show files: When uploading, set a unique tag or genre:

    • Genre: "Sunday Show" or
    • Tag: "sunday-show"
  2. Create a smart block called "Sunday Show Block":

    • Include filter: tags = "sunday-show" (or genre = "Sunday Show")
    • Weight: new_release = 7 days, weight +5.0 (heavily favor newest uploads)
    • Target duration: 120 minutes (2 hours)
    • No separation rules needed (show files are unique each week)
  3. Create a recurring schedule entry (via Dashboard → Schedule):

    • Source: Smart Block → "Sunday Show Block"
    • Start: Sunday 1:00 PM
    • End: Sunday 3:00 PM
    • Recurrence: Weekly
  4. Each week: Your friend uploads their files tagged "sunday-show". The smart block automatically picks the newest ones for Sunday's slot.

Method 2: Dedicated Playlist (Manual Curation)

If your friend wants exact control over track order:

  1. Create a playlist called "This Week's Sunday Show"
  2. Upload files and add them to the playlist in order
  3. Create a recurring schedule entry:
    • Source: Playlist → "This Week's Sunday Show"
    • Start: Sunday 1:00 PM
    • Recurrence: Weekly
  4. Each week: Replace the playlist contents with new files

Method 3: Clock Template with Override

  1. Create a "Sunday Afternoon" clock: hours 13-15
  2. Add a smart block slot pointing to the Sunday show block
  3. Create a separate weekday clock for the same hours (e.g., "Weekday Afternoon" 13-15)

Note: Clocks don't currently filter by day-of-week, so you'll need to use the recurring schedule entry approach (Method 1 or 2) for day-specific shows. The clock will generate entries every day; you'd override Sundays with the recurring entry, which takes priority.

Keeping Show Files from Playing at Other Times

To prevent your friend's show files from appearing in other smart blocks during the week:

  • Use a unique genre or tag that no other smart block filters for
  • Add an exclude rule to your regular smart blocks: exclude tags = "sunday-show"
  • The files will only be picked up by the "Sunday Show Block" which is only scheduled on Sundays

Scheduling Podcasts

Podcasts work similarly to weekly shows. The key difference is that podcast episodes are typically single long files rather than a playlist of tracks.

Single Episode Per Week

  1. Upload the episode with genre "Podcast" and a tag like "tech-podcast"
  2. Create a smart block:
    • Include: tags = "tech-podcast"
    • Weight: new_release = 7 days, weight +10.0
    • Target duration: match your clock slot length
  3. Schedule it via clock template or recurring entry

Multiple Podcast Slots

Create separate smart blocks for each podcast feed, each filtering on a different tag. Place them in different clock slots:

Clock: "Podcast Hour" (12-13)
  Slot 0: Smart Block "Tech Podcast" (tags = "tech-pod", 30 min)
  Slot 1: Smart Block "News Brief"   (tags = "news-pod", 15 min)
  Slot 2: Smart Block "Filler Music"  (genre = "Ambient", 15 min)

Mixing Webstreams with Local Content

You can schedule webstreams (internet radio relays) alongside your own media:

  1. Add webstreams via Dashboard → Webstreams (provide the stream URL)
  2. Create clock slots of type "Webstream" pointing to each stream
  3. Mix with smart blocks in the same day:
Clock: "Morning" (6-10)  → Smart Block "Morning Mix"
Clock: "Midday"  (10-12) → Webstream "BBC Radio 6 Music"
Clock: "Afternoon" (12-18) → Smart Block "Afternoon Variety"
Clock: "Evening" (18-22) → Webstream "SomaFM Drone Zone"
Clock: "Night" (22-6) → Smart Block "Late Night Ambient"

Webstream entries automatically span their clock's full time window. For HLS streams (.m3u8 URLs), metadata like track titles is polled from the stream manifest. For Icecast/SHOUTcast streams, ICY metadata is read inline.

Advanced: Bumpers and Interstitials

Bumpers are short audio clips (station IDs, jingles, stingers) that play between segments.

Using Hard Items as Bumpers

Add a hard_item slot at a specific offset in your clock:

Clock: "Morning Drive" (6-10)
  Slot 0 (offset 0:00): Smart Block "Morning Mix" (55 min)
  Slot 1 (offset 0:55): Hard Item "station-id-jingle.mp3"

Using a Bumper Playlist

  1. Create a playlist of all your bumper/jingle files
  2. Use a playlist slot with a short duration at transition points
  3. The system picks from the playlist in order

Smart Block Interstitials

Smart blocks support built-in interstitial insertion:

  • Every N tracks: Insert an interstitial (e.g., every 4 songs, play a jingle)
  • Source: Playlist or specific media item
  • Per Break: How many interstitials per break (1-3)

Configure this in the smart block's advanced settings under "Interstitials".

Understanding the Schedule Calendar

The Dashboard → Schedule page shows a FullCalendar view of your programming:

  • Week/Day/List views: Toggle between calendar layouts
  • Color coding: Different source types have different colors
  • Click an entry: See details including the source smart block, track list, and metadata
  • Drag to reschedule: Move entries (creates override instances for recurring entries)
  • Validation: The system checks for gaps and overlaps

Schedule Entry Types

Source Type What it is
media A specific audio file (from smart block materialization or hard item)
playlist A playlist (resolved to tracks at playout time)
smart_block A smart block slot (materialized into individual track entries)
webstream An internet stream relay
stopset A commercial/underwriting break
live A live DJ session
show A scheduled show

Warnings to Watch For

The scheduler may add warnings to entries when constraints couldn't be fully met:

Warning Meaning
constraint_relaxed:1 Artist/title separation rules were dropped to find enough tracks
constraint_relaxed:2 Separation and quota rules were both dropped
constraint_relaxed:3 All constraints dropped (only basic filters remain)
underfilled_target Ran out of matching tracks before filling the slot
emergency_fallback No tracks matched at all; a random track was used
used_fallback:<id> A fallback smart block was used

If you see these regularly, it means your smart block rules are too restrictive for your library size. Either upload more content or loosen the filters.

Quick Reference: Smart Block Filter Fields

Field Type Example
genre Exact match "Rock"
mood Exact match "Chill"
artist Normalized match "The Beatles" (matches "the beatles", "THEBEATLES")
album Exact match "Abbey Road"
title Exact match "Yesterday"
label Exact match "Atlantic Records"
language Exact match "en"
tags Tag UUIDs (selected in UI)
text_search Fuzzy across title/artist/album "love"
bpm Range (min-max) 120-140
year Range (min-max) 1990-1999
explicit Boolean true/false
source_playlists Playlist UUIDs (selected in UI)

Quick Reference: Clock Slot Types

Type Use For Duration Behavior
smart_block Music/podcast automation Uses block's target duration or slot duration
playlist Curated playlists Plays through playlist
hard_item Specific file (jingle, promo) Uses file's actual duration
webstream Internet stream relay Spans the clock's full time window
stopset Commercial breaks Uses configured duration

Troubleshooting

"No plans to generate" in logs

  • Check that your station has at least one clock template with slots
  • Verify the clock's hour window covers the current time

Schedule shows 1-minute entries for webstreams

  • Make sure webstream slots don't have a duration_ms in their payload (let it auto-expand to the clock window)

Smart block keeps picking the same tracks

  • Increase your separation windows (artist: 60-120 min, title: 240 min)
  • Upload more content to give the engine a larger pool

"Emergency fallback" entries appearing

  • Your smart block rules are too restrictive — no tracks match
  • Check that your media has the correct genre/tags/mood metadata
  • Check that media analysis completed successfully (analysis_state = "complete")

Recurring show plays wrong content

  • Verify the tag/genre filter is unique to the show files
  • Add exclude rules to your regular smart blocks for the show's tag
  • Use the new_release weight to heavily favor newest uploads

Clone this wiki locally