feat: Multi-Source Walls foundation — PlaylistManager + scene presets (Epic 4, #9)#15
Merged
Merged
Conversation
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
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.
Implements the foundation for Epic 4: Multi-Source Walls (advances #9) — per-source-group playout + named scene presets.
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.py—PlaylistManager: 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 viacell._source_group.wall.pynow delegates all playout to it and the inlinedeque+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.pypersists them in a[Scenes]section (optionxform=strto preserve scene-name case) andHyperwallConfiggains a frozenscenestuple field.Latent bug fix:
app.py's post-wizard config rewrite silently droppedcleanup_on_startup(reset toFalseon every launch) and would also have dropped scenes. Both are now carried through.Validation (Linux) — 66 tests green
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).optionxform=strleaves[Login]/[Settings]round-tripping unchanged (spot-checked).py_compileclean;python tests/run_all.py→ OVERALL: 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.