release: v0.0.40 — dock bridge fixes + render quality pass#30
Merged
Conversation
Co-Authored-By: Claude <noreply@anthropic.com>
Highlights
- Multi-profile renders: --render-profile / -r is now repeatable on
render short and render apply. The dock passes both player-overlay
and slowmo-ten-second-clip as separate flags; the previous single
str option silently kept only the last value, dropping one of the
user's selected profiles.
- Smart-zoom smoothness: input keyframes are pre-smoothed and then
resampled along a Catmull-Rom spline before downsampling to ffmpeg's
8-segment limit. Motion has continuous velocity through every
keyframe instead of the visible "kicks" the previous straight
downsample produced at zoom_frames=16+.
- Encoder quality defaults for short-form output: -pix_fmt yuv420p
(universal player compatibility), -tune film (motion estimation
tuned for live action), -movflags +faststart (web-streaming
moov-atom placement). CRF default lowered 18 → 16. Each flag is
configurable via new VideoConfig fields; set to "" to opt out.
- Team-colors plumbed through to the overlay engine. Goal overlays
now use the saved team palette instead of falling back to flat gray.
- New: reeln game set-tournament <name> command.
Dock-bridge fixes (every one had the dock failing silently)
- _resolve_player_numbers now reads event.metadata["team"] as
team_hint for resolve_scoring_team. Without this, dock-tagged
events (generic event_type="goal" + team metadata) all resolved
to the home team — away goals rendered with the home roster.
- _config_base_dir() honors the explicit --config path before
REELN_CONFIG / platform default. Fixes "Team profile not found"
when the dock invoked reeln from a GUI launch without inheriting
the user's shell env.
- validate_config() accepts both string and {name, team_specific}
object forms for event_types, and includes both shapes in
iterations cross-validation. The dock writes the object form by
default; the validator was incorrectly flagging every entry.
State-mutation boundary
- GameState mutations (events, segments, finish flag, livestreams,
highlight merge state, tournament) now route through reeln_native
JSON mutations instead of direct Python assignment. Matches the
dock's state-mutation-boundary contract so CLI and dock share one
canonical implementation.
Tests
- 2511 unit tests pass (28 new): Catmull-Rom smoothness regressions,
encoder flag emission, validator object-form, team-hint
resolution, multi-profile routing, team-color plumbing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
| @pytest.fixture(autouse=True) | ||
| def _reset_active_config_path() -> Iterator[None]: | ||
| """Ensure tests don't leak ``_ACTIVE_CONFIG_PATH`` between runs.""" | ||
| import reeln.core.config as _cfg_mod |
| path) without exporting REELN_CONFIG. Even if a stale REELN_CONFIG is | ||
| present, the explicit --config must win for team/roster lookup. | ||
| """ | ||
| import reeln.core.config as _cfg_mod |
| monkeypatch: pytest.MonkeyPatch, | ||
| ) -> None: | ||
| """Active config path is used when REELN_CONFIG is absent.""" | ||
| import reeln.core.config as _cfg_mod |
|
|
||
| def test_load_config_sets_active_config_path(tmp_path: Path) -> None: | ||
| """``load_config`` records the resolved path so teams/rosters resolve.""" | ||
| import reeln.core.config as _cfg_mod |
| tmp_path: Path, | ||
| ) -> None: | ||
| """Implicit (default) load_config must not poison the active path.""" | ||
| import reeln.core.config as _cfg_mod |
2 tasks
Reflects the companion plugin releases: - reeln-plugin-google 0.13.1 — suppress discovery_cache warning - reeln-plugin-openai 0.10.1 — graceful per-frame zoom skip Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
_build_speed_segments_chain previously hard-coded a height-based, crop-like scale even when the user explicitly set crop_mode=PAD. Result: in a multi-profile render where one profile has speed_segments and another doesn't, the speed_segments iteration filled the 9:16 frame while the no-speed iteration letterboxed — exact dock-reported symptom: "the first of the replay iterations was a small clip". Fix routes the three cases distinctly: - smart pad: height-based scale (smart needs wider video for panning) - non-smart pad: width-based scale + pad filter (true letterbox, matches the non-speed-segments path) - crop: height-based scale + crop filter (matches non-speed path) New regression test asserts that PAD-mode framing dimensions match exactly between speed_segments and non-speed_segments paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The TeamLine in goal_overlay.ass renders ``{{goal_scorer_team}} -
{{team_level}}`` on a single line. When a tournament is configured the
template promoted the tournament to ``goal_scorer_team`` and built
``team_level`` as ``{TEAM}/{LEVEL}`` — e.g. ``2026 SUMMER SHOWDOWN -
MACHINE ORANGE/2014``. The clip region for that line stops at x=1717
(200px reserved for the logo), so the level digits got chopped to
``MACHINE ORANGE/2`` whenever the team name was long.
Drop the level suffix when a tournament is present — the tournament
name already carries the season/division context. Users who want it
back can override the bundled template.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- RUF003 reeln/core/zoom.py:171 -- ambiguous `×` in comment - SIM300 tests/unit/core/test_config.py:174 -- Yoda condition - B007 tests/unit/core/test_zoom.py:203 -- unused loop var `v` -> `_v` - RUF003 tests/unit/core/test_zoom.py:273 -- ambiguous `×` in comment Pure cleanup, no behavioural change. `make check` passes. Co-Authored-By: Claude <noreply@anthropic.com>
4 tasks
…dings release/v0.0.40 calls reeln_native.add_event, mark_highlighted, add_render, mark_finished, update_event_field, bulk_update_event_type -- functions introduced in reeln-native 0.3.0 (StreamnDad/reeln-core#5). The previous constraint allowed resolution to 0.2.3, which lacks them, breaking CI. Co-Authored-By: Claude <noreply@anthropic.com>
Bumping reeln-native >=0.3.0 made several call sites reachable that weren't exercised on the 0.2.3 stub path. Adds focused tests and marks two genuinely-defensive branches as no-cover: - game.py:set-tournament: happy path + config-error path - core/events.py: tag_event metadata walk over multi-event state (89->88 branch); the "event id present in state but absent from JSON dict" branch (88->92) is marked `pragma: no branch` since resolve_event_id has already proven the id exists. - core/zoom.py: float-drift timestamp smoke test; the `u < 0` / `u > 1` clamps stay marked `pragma: no cover` -- defensive code documented as guarding against float wiggle that the monotone segment walk already prevents. - core/shorts.py: extend empty-quality-flag test to also cover the ``pix_fmt=""`` opt-out path. - commands/render.py: --player-numbers with an unknown --event id and with an event whose metadata lacks a usable ``team`` key both fall back to event-type-prefix resolution. Coverage: 99.68% -> 100.00% (2533 passed). Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three lines of work bundled into one release:
Dock-bridge fixes — every one had the dock failing silently
_resolve_player_numbersnow readsevent.metadata["team"]as theteam_hintforresolve_scoring_team. Dock-tagged events (event_type="goal"+ team metadata) previously all resolved to the home roster — away goals rendered with the wrong players._config_base_dir()honors the explicit--configpath before falling back toREELN_CONFIG/ platform default. Fixes "Team profile not found" when the dock invoked the CLI from a GUI launch without inheriting the shell env.validate_config()accepts both string and{name, team_specific}object forms forevent_types(the dock writes object form by default) and includes both shapes in iterations cross-validation.Multi-profile rendering + quality pass
--render-profile/-ris now repeatable onrender shortandrender apply. Renders each profile and concatenates via the existingrender_iterationsengine, producing one queue entry and one render history entry.zoom_frames=16+.-pix_fmt yuv420p,-tune film,-movflags +faststart. CRF default lowered 18 → 16 (~30% larger files, visibly crisper detail after vertical crop+upscale). Each flag is configurable via newVideoConfigfields; set to""to opt out.State-mutation boundary
GameStatemutations (events, segments, finish flag, livestreams, highlight merge, tournament) now route throughreeln_nativeJSON mutations instead of direct Python assignment — matches the dock's state-mutation-boundary contract so CLI and dock share one canonical implementation.reeln game set-tournament <name>command.Test plan
uv run pytest tests/unit -q— 2511 unit tests pass (28 new)build_short_commandmetadata["team"]="away"resolves to away team; missing event_id falls back to event_type prefixrender_iterations; single profile preserved on existing pathbuild_overlay_context.home_colors; empty list falls back to default (None) instead of[]zoom_frames=18with two profiles selected — verify both pass through, smoother tracking, correct team roster on away events🤖 Generated with Claude Code