Skip to content

feat: Multi-Source Walls foundation — PlaylistManager + scene presets (Epic 4, #9)#15

Merged
tcconnally merged 1 commit into
mainfrom
feat/epic4-multi-source
Jul 2, 2026
Merged

feat: Multi-Source Walls foundation — PlaylistManager + scene presets (Epic 4, #9)#15
tcconnally merged 1 commit into
mainfrom
feat/epic4-multi-source

Conversation

@tcconnally

Copy link
Copy Markdown
Owner

Implements the foundation for Epic 4: Multi-Source Walls (advances #9) — per-source-group playout + named scene presets.

Stacked on #14 (Epic 3) → #13#12. Base branch is feat/epic3-test-harness, so this diff shows only Epic 4. Merge order: #12#13#14 → this.

Why

Today every cell draws from one shared shuffled deque. That gives nice global de-dup but can't express "monitor 1 = Music Videos, monitor 2 = Movies." This PR adds the plumbing for per-monitor/per-cell sourcing and recallable scene presets — without changing the default single-source behavior at all.

What changed

New hyperwall/playlist.pyPlaylistManager: one shuffled queue per source group; cells in a group share it for global de-dup (no repeat until the group's pool is exhausted). The default group "all" reproduces the prior single global deque exactly. Per-monitor sourcing simply assigns different group keys via cell._source_group. wall.py now delegates all playout to it and the inline deque+random.shuffle (and their now-unused imports) are gone.

New hyperwall/scenes.py — named {grid, screens, libraries, filter} presets: normalize / serialize / deserialize to compact JSON; malformed input degrades to a default scene instead of raising, so one bad config entry can't crash startup. config.py persists them in a [Scenes] section (optionxform=str to preserve scene-name case) and HyperwallConfig gains a frozen scenes tuple field.

Latent bug fix: app.py's post-wizard config rewrite silently dropped cleanup_on_startup (reset to False on every launch) and would also have dropped scenes. Both are now carried through.

Validation (Linux) — 66 tests green

  • +22 new tests: test_playlist.py (9 — per-group de-dup, refill/reshuffle, group independence, shuffle invocation), test_scenes.py (8 — round-trip + malformed-input safety), config scene round-trip (+2).
  • Default-wall parity explicitly verified: the single-group path yields the same global de-dup guarantee, and optionxform=str leaves [Login]/[Settings] round-tripping unchanged (spot-checked).
  • py_compile clean; python tests/run_all.pyOVERALL: PASS.

Scope note — this is the foundation half

The pure, CI-verifiable core (playout engine + scene persistence) lands here. The UI surface — wiring the wizard to bind libraries per monitor and exposing scene save/recall on the web remote / POST /api/scene/<name> — touches PyQt/Flask and is better validated on skyhawk against a live wall. I've built the tested backbone so that UI work is a thin, low-risk layer on top. Happy to do it as a follow-up PR once this stack is reviewed.

Milestone: v10 (v10.1) · roadmap #5.

Introduces the plumbing for per-monitor/per-cell sourcing and named scene
presets, keeping the default single-source wall behavior byte-identical. Pure
logic extracted into testable modules (Epic 2/3 pattern).

New hyperwall/playlist.py — PlaylistManager:
- one shuffled queue per source *group*; cells in a group share it for global
  de-dup (no repeat until the group pool is exhausted). Default group "all" =
  the prior single global deque exactly. Per-monitor sourcing just assigns
  different group keys (cell._source_group).
- wall.py now delegates to it: next_video() pulls from the cell's group,
  _on_items_loaded/_set_filter feed the pool. Removed the inline deque +
  random.shuffle (and now-unused imports).

New hyperwall/scenes.py — named {grid, screens, libraries, filter} presets:
- normalize/serialize/deserialize to compact JSON; malformed input degrades to
  a default scene instead of raising (one bad config entry can't crash startup).
- config.py persists them in a [Scenes] section (optionxform=str to preserve
  scene-name case); HyperwallConfig gains a frozen `scenes` tuple field.

Also fixes a latent bug: app.py's post-wizard config rewrite dropped
cleanup_on_startup (reset to False every launch) and would have dropped scenes;
both are now carried through.

Tests (+22, 66 total green): tests/test_playlist.py (9), tests/test_scenes.py
(8), config scene round-trip (+2 in test_config), wired into run_all.py.
Default-wall parity verified: global de-dup + config Login/Settings unchanged.

Refs #9
@tcconnally tcconnally added this to the v10 milestone Jul 2, 2026
@tcconnally tcconnally changed the base branch from feat/epic3-test-harness to main July 2, 2026 14:23
@tcconnally tcconnally merged commit 78191c0 into main Jul 2, 2026
1 check passed
@tcconnally tcconnally deleted the feat/epic4-multi-source branch July 2, 2026 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants