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 callsentrypoint.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:
define_content— declare all content sources the playout will use.reset_playout— (called only inresetmode) set the playout’s starting time and state.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:
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) aPlayoutContext 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 insidedefine_content to register named content sources:
Playout Building API
Call these methods insidebuild_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.
