Configuration¶
reeln uses a layered JSON configuration system with XDG-compliant paths and environment variable overrides.
Config file locations¶
Platform |
Config directory |
|---|---|
macOS |
|
Linux |
|
Windows |
|
The main config file is config.json within the config directory.
Loading order¶
Configuration is merged in this order (later values win):
Bundled defaults — shipped with the package, always valid
User config —
config.jsonin your config directoryGame override —
game.jsonin the current game directoryEnvironment variables —
REELN_<SECTION>_<KEY>
Example config¶
{
"config_version": 1,
"sport": "hockey",
"video": {
"ffmpeg_path": null,
"default_container": "mkv",
"merge_strategy": "concat",
"codec": "libx264",
"preset": "medium",
"crf": 18,
"audio_codec": "aac",
"audio_bitrate": "128k"
},
"paths": {
"source_dir": "~/Videos/OBS",
"source_glob": "Replay_*.mkv",
"output_dir": "~/Movies",
"temp_dir": null
}
}
Paths section¶
Key |
Default |
Description |
|---|---|---|
|
|
Directory where replay files are captured (e.g. OBS output folder) |
|
|
Glob pattern for matching replay files in |
|
|
Base directory for game directories and output |
|
|
Temporary file directory (default: system temp) |
source_dir is used by both game segment (to collect replays) and render short (to auto-discover the latest clip when no argument is given).
Video section¶
Key |
Default |
Description |
|---|---|---|
|
|
Explicit path to ffmpeg binary (auto-discovered if null) |
|
|
Default output container format |
|
|
Merge strategy for segment processing |
|
|
Video codec for encoding |
|
|
Encoder preset (ultrafast → veryslow) |
|
|
Constant Rate Factor — lower is higher quality |
|
|
Audio codec |
|
|
Audio bitrate |
Video encoding settings apply to both game segment (when re-encoding mixed containers) and render short/render preview.
Render profiles section¶
Named render profiles define reusable rendering parameter overrides. Add a render_profiles section to your config:
{
"render_profiles": {
"fullspeed": {
"speed": 1.0
},
"slowmo": {
"speed": 0.5
},
"goal-overlay": {
"speed": 0.5,
"subtitle_template": "~/.config/reeln/templates/goal_overlay.ass"
},
"vertical-slowmo": {
"width": 1080,
"height": 1920,
"crop_mode": "pad",
"speed": 0.5
}
}
}
Key |
Type |
Description |
|---|---|---|
|
float |
Playback speed (e.g. 0.5 for slow motion) |
|
string |
Path to |
|
string |
Path to |
|
int |
Target width (short-form only, ignored for full-frame) |
|
int |
Target height (short-form only, ignored for full-frame) |
|
string |
|
|
float |
Crop anchor X position, 0.0–1.0 (short-form only) |
|
float |
Crop anchor Y position, 0.0–1.0 (short-form only) |
|
float |
Content scale, 0.5–3.0 (default: 1.0). Values > 1.0 zoom in. |
|
bool |
Enable smart tracking via vision plugin |
|
string |
Pad bar color (short-form only) |
|
string |
Video codec override |
|
string |
Encoder preset override |
|
int |
CRF override |
|
string |
Audio codec override |
|
string |
Audio bitrate override |
All fields are optional — null or omitted means “inherit from base config”.
Variable speed (speed_segments)¶
For variable-speed rendering within a single clip, use speed_segments instead of the scalar speed field. Each segment defines a speed and a source-time boundary:
{
"render_profiles": {
"slowmo-middle": {
"speed_segments": [
{"until": 5.0, "speed": 1.0},
{"until": 8.0, "speed": 0.5},
{"speed": 1.0}
]
}
}
}
This plays the first 5 seconds at normal speed, then 3 seconds at half speed, then the rest at normal speed. The last segment must omit until (it runs to the end of the clip).
Rules:
At least 2 segments required (use scalar
speedfor uniform speed)untilvalues must be strictly increasing and positiveSpeeds must be in the range 0.25–4.0
speedandspeed_segmentsare mutually exclusive — set one or the other, not bothspeed_segmentscannot be combined with--smarttracking
speed_segments is profile-only — there is no CLI flag for it. Configure it in a render profile and use --render-profile to apply it.
Profiles are used with --render-profile on render short, render preview, render apply, game segment, and game highlights.
Builtin templates¶
reeln ships with a bundled goal_overlay template. Reference it with the builtin: prefix instead of a file path:
{
"render_profiles": {
"player-overlay": {
"speed": 0.5,
"subtitle_template": "builtin:goal_overlay"
}
}
}
The goal_overlay template renders a lower-third banner showing the scorer name, up to two assists, and the team name. Font size scales dynamically for long names, and assists are hidden when not present.
A default player-overlay profile (native speed + goal overlay) and goal iteration mapping are included in the bundled config, so goal events render with the overlay out of the box:
# Tag a goal with player and assists
reeln game event tag <event-id> --type goal --player "#17 Smith" \
--meta "assists=#22 Jones, #5 Brown"
# Render with the overlay
reeln render short clip.mkv --render-profile player-overlay \
--game-dir . --event <event-id>
# Or use iterations (auto-applies player-overlay for goals)
reeln render short clip.mkv --iterate --game-dir . --event <event-id>
Smart tracking¶
Smart tracking (--smart or "smart": true in a profile) requires a vision plugin that handles the ON_FRAMES_EXTRACTED hook. Without one, --smart falls back to static center positioning with a warning.
The reeln-plugin-openai package provides smart zoom via OpenAI’s vision API. Enable it in plugin settings:
{
"plugins": {
"enabled": ["openai"],
"settings": {
"openai": {
"api_key": "sk-...",
"smart_zoom_enabled": true,
"smart_zoom_model": "gpt-4o"
}
}
}
}
When smart tracking is active, the render pipeline:
Extracts frames from the clip (
--zoom-framescontrols how many, default 5)Emits
ON_FRAMES_EXTRACTED— the vision plugin analyzes frames and returns a zoom pathBuilds dynamic ffmpeg expressions that follow the action throughout the clip
Smart tracking composes with both crop modes:
crop + smart — the crop window tracks the action point
pad + smart — pillarbox bars pan horizontally to center the action (vertical position stays fixed)
Iterations section¶
The iterations section maps event types to ordered lists of profile names. This is used for multi-iteration rendering where each event type gets a different sequence of render passes:
{
"iterations": {
"default": ["fullspeed"],
"goal": ["fullspeed", "slowmo", "goal-overlay"],
"save": ["slowmo"]
}
}
When rendering an event, the iterations config determines which profiles to apply based on the event type. Events without a matching type fall back to the "default" key.
Use --iterate on render or game commands to activate multi-iteration rendering:
# Render a short through iteration profiles
reeln render short clip.mkv --iterate
# Apply iterations after segment merge
reeln game segment 1 --iterate
# Apply iterations after highlights merge
reeln game highlights --iterate
Each profile in the list is applied in order, and the iteration outputs are concatenated end-to-end into a single final file. For example, a goal event with profiles ["fullspeed", "slowmo", "goal-overlay"] produces a video that plays the clip at full speed, then slow motion, then with the goal overlay — all stitched together automatically.
Branding section¶
The branding section controls the branding overlay shown at the start of rendered shorts:
{
"branding": {
"enabled": true,
"template": "builtin:branding",
"duration": 5.0
}
}
Key |
Default |
Description |
|---|---|---|
|
|
Whether to show branding overlay |
|
|
Template path — |
|
|
How long the branding is visible in seconds |
The builtin branding overlay displays “reeln v{version} by https://streamn.dad” in bold white text with a black outline at the top of the video, fading in over 300ms and fading out over 800ms. To disable branding entirely, set enabled to false or use --no-branding on the CLI.
Orchestration section¶
The orchestration section controls the plugin pipeline behavior:
{
"orchestration": {
"upload_bitrate_kbps": 5000,
"sequential": true
}
}
Key |
Default |
Description |
|---|---|---|
|
|
Maximum upload rate in KB/s (0 = unlimited) |
|
|
Run all plugin operations sequentially |
Plugins section¶
The plugins section controls plugin discovery and per-plugin settings:
{
"plugins": {
"enabled": ["youtube", "llm"],
"disabled": ["meta"],
"settings": {
"youtube": {
"api_key": "...",
"playlist_id": "..."
}
}
}
}
Key |
Default |
Description |
|---|---|---|
|
|
List of plugin names to enable (empty = all discovered) |
|
|
List of plugin names to disable |
|
|
Per-plugin configuration passed during instantiation |
|
|
Custom plugin registry URL (empty = default GitHub URL) |
|
|
Restrict plugins to hooks declared in the registry (set |
When enabled is empty, all discovered plugins are loaded except those in disabled. When enabled is non-empty, only those named plugins are loaded (minus any in disabled).
When enforce_hooks is true (the default), plugins may only register hooks declared in their registry entry. Undeclared hooks are silently blocked with a warning log. Set to false during local plugin development to bypass enforcement.
Plugin config schemas¶
Plugins can declare the configuration fields they accept via a config_schema class attribute. When a plugin declares a schema:
Default seeding — running
reeln plugins enable <name>orreeln plugins install <name>writes the plugin’s default values intoplugins.settingsautomatically, soreeln config showreflects them immediately.Validation —
reeln config doctorchecks plugin settings against declared schemas and warns about missing required fields or type mismatches.Discovery —
reeln plugins info <name>displays the schema (field names, types, required/optional, defaults, descriptions).
Default seeding only adds missing keys — it never overwrites values you’ve already set. Plugins without a schema are unaffected; they continue to work with opaque settings dicts.
Supported field types¶
Type |
JSON equivalent |
Notes |
|---|---|---|
|
string |
Default |
|
number (integer) |
Rejects booleans |
|
number |
Accepts |
|
boolean |
|
|
array |
Example schema output¶
$ reeln plugins info youtube
Name: youtube
Package: reeln-youtube
...
Config schema:
api_key: str (required) — YouTube Data API key
playlist_id: str — Default playlist for uploads
privacy: str [default: unlisted] — Video privacy setting
Logging¶
The default log level is WARNING. Override it with --log-level or the REELN_LOG_LEVEL environment variable:
# CLI flag
reeln game init --log-level DEBUG ...
# Environment variable
export REELN_LOG_LEVEL=DEBUG
reeln game init ...
Valid levels: DEBUG, INFO, WARNING, ERROR, CRITICAL (case-insensitive).
The log output format is controlled by --log-format / REELN_LOG_FORMAT (default: human). Set to json for structured logging in CI/automation.
Environment variable overrides¶
Any config value can be overridden with an environment variable using the convention REELN_<SECTION>_<KEY>:
export REELN_VIDEO_CRF=22
export REELN_VIDEO_CODEC=libx265
export REELN_VIDEO_AUDIO_CODEC=opus
export REELN_SPORT=hockey
Config file path via environment¶
You can also set which config file to load via environment variables:
Variable |
Description |
|---|---|
|
Absolute or |
|
Named profile (loads |
Priority order (highest wins):
--configCLI flagREELN_CONFIGenv var--profileCLI flagREELN_PROFILEenv varDefault XDG path (
config.json)
This priority applies to both reading and writing. When a command modifies config (e.g. reeln plugins enable), the changes are written back to the same resolved path.
# Use a specific config file
export REELN_CONFIG=~/projects/tournament/reeln.json
# Or select a named profile
export REELN_PROFILE=tournament
Named profiles¶
In addition to the default config.json, you can create named profiles:
Default:
config.jsonNamed:
config.<profile>.json
Select a profile with the --profile flag or REELN_PROFILE env var:
reeln game init --sport hockey --home team-a --away team-b --profile tournament
# Or via env var
export REELN_PROFILE=tournament
reeln game segment 1
Profiles inherit from the default config and override specific keys.
Debug artifacts¶
When any command is run with --debug, pipeline debug artifacts are written to {game_dir}/debug/. This includes:
Per-operation JSON files — ffmpeg command, filter chain, input/output file metadata (duration, fps, resolution, codec)
HTML index (
debug/index.html) — browsable summary linking to all debug artifacts and processed videos
Debug artifacts are automatically removed by game prune (no --all flag needed). Open debug/index.html in a browser for a quick overview of all operations performed on a game.
Team profiles and rosters¶
Team profiles are stored as JSON files in the config directory under teams/{level}/{slug}.json. When you initialize a game with --level, the team level and slugs are persisted in game.json, enabling roster-based player lookup during rendering.
Setting up rosters for player number lookup¶
Create team profiles with
roster_pathpointing to a CSV file:
{
"team_name": "Eagles",
"short_name": "EGL",
"level": "bantam",
"roster_path": "/path/to/eagles_roster.csv",
"colors": ["#C8102E", "#000000"]
}
Create the roster CSV with
number,name, andpositioncolumns:
number,name,position
48,John Smith,C
24,Jane Doe,D
2,Bob Jones,RW
Initialize games with
--levelto persist team profile references:
reeln game init eagles bears --level bantam --sport hockey
Use
--player-numbersduring rendering to look up players from the roster:
reeln render short clip.mkv --player-numbers 48,24,2 --event-type HOME_GOAL -r overlay
This resolves to #48 Smith (scorer) with assists #24 Doe and #2 Jones.
Schema versioning¶
Every config file includes a config_version field. When the schema changes, reeln provides migration functions to upgrade configs automatically.
Viewing and validating config¶
# Show the fully resolved config
reeln config show
# Validate config and check for issues
reeln config doctor