Skip to main content
Sequential schedules use YAML files to define precise, instruction-based scheduling. This provides maximum control over content ordering, timing, and behavior while remaining human-readable and version-controllable.

Overview

Sequential schedules are built by the SequentialPlayoutBuilder (ErsatzTV.Core/Scheduling/YamlScheduling/SequentialPlayoutBuilder.cs:18) and consist of:
  • Content Definitions - Collections, playlists, and other content sources
  • Sequences - Reusable instruction groups
  • Playout Instructions - The main schedule flow
  • Reset Instructions - Special instructions that run when resetting

YAML Structure

A sequential schedule YAML file has this structure:
import:
  - shared-content.yaml
  - common-sequences.yaml

content:
  - key: sitcoms
    collection: Comedy Shows
    order: shuffle
  
  - key: movies
    collection: Action Movies
    order: random

sequence:
  - key: primetime
    items:
      - count: { content: sitcoms, value: 2 }
      - duration: { content: movies, value: 2h }

reset:
  - wait_until: { when: "06:00" }

playout:
  - wait_until: { when: "08:00" }
  - sequence: { sequence: primetime }
  - wait_until: { when: "06:00", tomorrow: true }
  - repeat: { instructions: -2 }

Content Definitions

Content items define reusable content sources:

Collection Content

content:
  - key: my-shows
    collection: TV Shows
    order: chronological
key
string
required
Unique identifier for this content
collection
string
required
Name of the collection
order
string
required
Playback order: chronological, random, shuffle, shuffle_in_order, multi_episode_shuffle, season_episode, random_rotation, marathon

Multi-Collection Content

content:
  - key: all-comedy
    multi_collection: Comedy Collections
    order: shuffle

Smart Collection Content

content:
  - key: recent-movies
    smart_collection: New Releases
    order: chronological

Playlist Content

content:
  - key: weekend-playlist
    playlist: Weekend Specials
    playlist_group: Holidays

Search Content

content:
  - key: star-trek
    search: "title:*Star Trek*"
    order: chronological

Show Content

content:
  - key: specific-show
    show:
      tvdb: "12345"
      imdb: "tt0123456"
    order: season_episode

Marathon Content

content:
  - key: trek-marathon
    marathon:
      searches:
        - "title:*Star Trek*"
      group_by: show
      shuffle_groups: false
      item_order: chronological
      play_all: true

Playout Instructions

The main schedule is defined in the playout section:

Add All Items

- all: { content: sitcoms }
Plays all items from the content source.

Add Count

- count: { content: movies, value: 3 }
Plays a specific number of items.
content
string
required
Content key to play from
value
int
required
Number of items to play
custom_title
string
Override EPG title
disable_watermarks
boolean
default:false
Disable watermarks for these items

Add Duration

- duration:
    content: shows
    value: 2h30m
    fallback: filler
    trim: true
    discard_attempts: 5
    stop_before_end: false
    offline_tail: false
Fills a specific duration with content.
content
string
required
Content key to play from
value
string
required
Duration (e.g., “1h”, “30m”, “2h15m”)
fallback
string
Content key to use if main content won’t fit
trim
boolean
default:false
Trim the last item to fit exactly
discard_attempts
int
default:0
How many items to try before giving up finding one that fits
stop_before_end
boolean
default:false
Stop before adding items that won’t fit completely
offline_tail
boolean
default:false
Show offline screen for remaining time (requires stop_before_end: true)

Pad to Next

- pad_to_next:
    content: filler
    minutes: 30
Pads to the next interval (e.g., next 30-minute mark).
minutes
int
required
Interval in minutes (e.g., 30 pads to :00 and :30)

Pad Until

- pad_until:
    content: filler
    when: "14:00"
    tomorrow: false
Pads until a specific time of day.
when
string
required
Time to pad until (HH:mm format)
tomorrow
boolean
default:false
If current time is past when, pad until tomorrow at that time

Control Instructions

Wait Until

- wait_until:
    when: "06:00"
    tomorrow: false
    rewind_on_reset: false
Waits until a specific time before continuing.
when
string
required
Time to wait until (HH:mm format)
tomorrow
boolean
default:false
If current time is past when, wait until tomorrow
rewind_on_reset
boolean
default:false
When resetting, rewind to this time even if it’s in the past

Repeat

- repeat: { instructions: 5 }
Jumps back a number of instructions to create loops.
instructions
int
required
Number of instructions to jump back (negative goes backward)
Be careful with repeat instructions to avoid infinite loops. The system detects cycles in sequences but not in main playout instructions.

Rewind

- rewind: { content: shows, value: 5 }
Rewinds a content enumerator by a number of items.

Skip Items

- skip_items: { content: shows, count: 3 }
Skips forward a number of items in a content enumerator.

Skip to Item

- skip_to_item:
    content: show
    season: 2
    episode: 5
Jumps to a specific season and episode.

Graphics and Watermarks

Graphics On

- graphics_on:
    elements:
      - logo
      - ticker
    variables:
      team: "Red Sox"
      score: "5-3"
Enables graphics overlays.

Graphics Off

- graphics_off:
    elements:
      - ticker
Disables graphics overlays. If no elements specified, disables all.

Watermark Control

- watermark:
    action: on
    watermarks:
      - channel-logo
action
string
required
on or off
watermarks
list
Watermark names. If empty, affects all watermarks.

EPG Control

Lock Guide Group

- epg_group:
    action: lock
    advance: true
    custom_title: "Special Event"
Locks the EPG guide group for custom titles.

Unlock Guide Group

- epg_group:
    action: unlock

Sequences

Sequences are reusable instruction groups:
sequence:
  - key: morning-block
    items:
      - count: { content: news, value: 2 }
      - duration: { content: talk-shows, value: 1h }
      - pad_to_next: { content: filler, minutes: 30 }
Use sequences in playout:
playout:
  - wait_until: { when: "06:00" }
  - sequence: { sequence: morning-block }

Sequence Features

- sequence:
    sequence: primetime
    repeat: 2
Repeats the sequence multiple times.

Imports

Share common content and sequences across multiple schedule files:
import:
  - /path/to/shared-content.yaml
  - ../common-sequences.yaml
Imported files can contain content and sequence sections. The playout and reset sections are ignored in imports.

Reset Instructions

Special instructions that run only when the schedule is reset:
reset:
  - wait_until: { when: "06:00", rewind_on_reset: true }
  - skip_to_item: { content: show, season: 1, episode: 1 }
Use reset instructions to:
  • Position the schedule at a specific time
  • Reset content enumerators to specific positions
  • Set initial graphics/watermark state

State Management

Sequential schedules save state in the playout anchor (ErsatzTV.Core/Scheduling/YamlScheduling/SequentialPlayoutBuilder.cs:306):
  • Current instruction index
  • Current time
  • All enumerator states
  • Active graphics elements
  • Active watermarks
  • Guide group state

Pre-Roll and Filler

- pre_roll:
    action: on
    content: commercials

- count: { content: shows, value: 3 }

- pre_roll: { action: off }
Pre-roll content plays automatically before each main content item.

Validation

Sequential schedules are validated by ISequentialScheduleValidator before use:
  • YAML syntax checking
  • Instruction format validation
  • Content reference validation
  • Cycle detection in sequences
  • Import path verification

Example Complete Schedule

import:
  - shared-collections.yaml

content:
  - key: sitcoms
    collection: Sitcoms
    order: shuffle
  
  - key: dramas
    collection: Dramas
    order: chronological
  
  - key: movies
    collection: Movies
    order: random
  
  - key: commercials
    playlist: Commercials
    playlist_group: Ads

sequence:
  - key: evening-block
    items:
      - count: { content: sitcoms, value: 2 }
      - duration: { content: dramas, value: 1h }
  
  - key: late-night
    items:
      - duration: { content: movies, value: 2h, trim: true }
      - pad_to_next: { content: commercials, minutes: 30 }

reset:
  - wait_until: { when: "06:00", rewind_on_reset: true }

playout:
  # Morning
  - wait_until: { when: "08:00" }
  - pre_roll: { action: on, content: commercials }
  - count: { content: sitcoms, value: 5 }
  - pre_roll: { action: off }
  
  # Afternoon
  - wait_until: { when: "14:00" }
  - duration: { content: dramas, value: 4h, stop_before_end: true }
  
  # Evening
  - wait_until: { when: "18:00" }
  - sequence: { sequence: evening-block }
  
  # Late night
  - wait_until: { when: "22:00" }
  - sequence: { sequence: late-night }
  
  # Loop to next day
  - wait_until: { when: "06:00", tomorrow: true }
  - repeat: { instructions: -8 }
Sequential schedules support up to 100 levels of sequence nesting. Beyond that, behavior is undefined to prevent infinite recursion.

Build docs developers (and LLMs) love