Skip to main content
Scripted schedules give you full programmatic control over playout construction by calling ErsatzTV’s Scripted Schedule REST API from a Python script. Where YAML schedules express a fixed declarative loop, a scripted schedule can inspect the current playout context, make branching decisions, query external data, and call any API endpoint in any order. This makes scripted schedules ideal for complex or dynamic channel programming — nightly builds that vary by the day of the week, channels that react to new media additions, or deeply customized marathon layouts that require logic beyond what YAML can express. The API is auto-generated from the OpenAPI specification at ErsatzTV/wwwroot/openapi/scripted-schedule.json. A Python client library (etv_client) is generated from this spec and is imported by your scripts.

How Scripted Schedules Work

When ErsatzTV triggers a scripted playout build, it calls entrypoint.py with four arguments: the ErsatzTV host URL, a build ID (UUID), the build mode (reset or continue), and the name of your script module. The entrypoint imports your script, creates an API client, fetches the current PlayoutContext, and then calls three functions from your script in sequence:
  1. define_content — declare all content sources the playout will use.
  2. reset_playout — (called only in reset mode) set the playout’s starting time and state.
  3. build_playout — add content, padding, and control instructions to the playout.

Build Modes


The Entrypoint

The entrypoint script (scripts/scripted-schedules/entrypoint.py) is the runtime harness for all scripted schedules. You do not modify this file — it handles argument parsing, module loading, API client setup, and orchestration:
Your script file lives at scripts/<script_name>.py relative to the entrypoint directory and must export exactly three functions: define_content, reset_playout, and build_playout.

Required Script Functions

Every scripted schedule module must implement the following three functions with the exact signatures shown:

define_content(api, context, build_id)

Called first on every build. Use this function to register all content sources the playout will reference. Each add_* call below adds a named content source to the build session.

reset_playout(api, context, build_id)

Called only in reset mode. Use this function to position the playout’s start time (via wait_until or wait_until_exact) and perform any one-time initialization. Return a new PlayoutContext if you want build_playout to see the updated time, or return None to let the entrypoint re-fetch it automatically.

build_playout(api, context, build_id)

Called on every build (both reset and continue). This is where you add the actual programming — content items, padding, EPG groups, watermarks, and overlays. Called in a loop by the playout engine until context.is_done is True.

The PlayoutContext

Every API call that adds or modifies playout content returns (or the entrypoint fetches) a PlayoutContext with four fields: Use context.is_done in a loop to continue building until the target time is filled.

Content Registration API

Call these methods inside define_content to register named content sources:

Playout Building API

Call these methods inside build_playout to add content and control playback:

Add Content

Padding

Timing Control

Skip and Peek

EPG Grouping

Overlay Controls


A Complete Minimal Example


Script Creation and Execution Flow

1

Install dependencies

Ensure etv_client is installed in the Python environment used by ErsatzTV. The client is generated from ErsatzTV/wwwroot/openapi/scripted-schedule.json using an OpenAPI Python generator.
2

Write your script module

Create a file at scripts/<your_script_name>.py in the scripted-schedules directory. Implement the three required functions: define_content, reset_playout, and build_playout. Each receives (api: ScriptedScheduleApi, context: PlayoutContext, build_id: UUID) as arguments.
3

Configure the playout in ErsatzTV

In the ErsatzTV UI, navigate to your channel’s Playout settings and set the Schedule Kind to Scripted. Point the Script field to your script module name (without the .py extension). Save the playout.
4

Trigger a reset build

Force a full rebuild by triggering a Reset on the playout. ErsatzTV calls entrypoint.py with mode reset, which runs all three of your functions. Inspect the playout timeline in the UI to verify the output looks correct.
5

Monitor and iterate

Check the ErsatzTV logs for any ApiException or AttributeError messages from the entrypoint. Fix script errors, then trigger another reset. Once the script is working, ErsatzTV will call it automatically in continue mode as the playout needs extending.

Tips and Best Practices

Always check is_done

Wrap your build_playout loop body in while not context.is_done: and refresh context from each API response. This prevents the script from overrunning the build window or running infinitely.

Use peek_next for look-ahead

Call peek_next(build_id, content_key) to inspect the duration of the next item before committing to add_count or add_duration. This enables smarter scheduling decisions, such as skipping a film that is too long to finish before a hard time boundary.

Register all content in define_content

All add_collection, add_show, and similar calls must happen in define_content, not in build_playout. This ensures content is available in both reset and continue mode builds without duplication.

Use EPG groups for multi-segment programs

Wrap related content items in start_epg_group / stop_epg_group calls to merge them into a single EPG entry. This is especially useful when a movie is followed by its trailer or a news block is followed by a station ID.
The full Scripted Schedule REST API reference — including all request and response schemas — is available at /api/scripted-schedule.