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
Unique identifier for this content
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.
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.
Duration (e.g., “1h”, “30m”, “2h15m”)
Content key to use if main content won’t fit
Trim the last item to fit exactly
How many items to try before giving up finding one that fits
Stop before adding items that won’t fit completely
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).
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.
Time to pad until (HH:mm format)
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.
Time to wait until (HH:mm format)
If current time is past when, wait until tomorrow
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.
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
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
Repeat Sequences
Custom Title
Shuffle Sequence
- sequence:
sequence: primetime
repeat: 2
Repeats the sequence multiple times.- sequence:
sequence: movie-block
custom_title: "Movie Marathon"
Overrides EPG titles for all items in the sequence.- shuffle_sequence: { sequence: variety-shows }
Shuffles the order of instructions within the sequence each time it plays.
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.