Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions hyperwall/soak_filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Pure helpers for selecting an initial soak corpus."""
from __future__ import annotations

from typing import Any


def apply_initial_filter(
items: list[dict[str, Any]],
mode: str | None,
) -> tuple[list[dict[str, Any]], str]:
"""Return the initial playback pool and its normalized filter mode.

The soak-only caller uses ``favorites`` to select a stable corpus before
any cell starts. Normal launches pass an empty mode and retain all items.
"""
normalized = str(mode or "").strip().lower()
if normalized != "favorites":
return list(items), "all"
return [
item
for item in items
if isinstance(item.get("UserData"), dict)
and item["UserData"].get("IsFavorite") is True
], "favorites"
18 changes: 16 additions & 2 deletions hyperwall/wall.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
)
from .urls import build_stream_url_for_plan, tag_names
from .playlist import PlaylistManager, DEFAULT_GROUP
from .soak_filter import apply_initial_filter

logger = logging.getLogger("HyperWall")

Expand Down Expand Up @@ -709,10 +710,23 @@ def _on_items_loaded(self, items: list[dict[str, Any]]) -> None:
len(source_items),
)
self.all_items = source_items
self.filtered = source_items[:]
initial_filter = (
os.environ.get("HYPERWALL_SOAK_FILTER", "")
if os.environ.get("HYPERWALL_SOAK_ACTIVE") == "1"
else ""
)
self.filtered, self.filter_mode = apply_initial_filter(
source_items, initial_filter
)
self.playlists.set_source(self.filtered, DEFAULT_GROUP)
if self.filter_mode != "all":
logger.info(
"Filter: %s (%d items)",
self.filter_mode.upper(),
len(self.filtered),
)
logger.info("Metadata Index: %d items loaded.", len(source_items))
if not source_items:
if not self.filtered:
logger.warning(
"No playable items returned — check config.ini libraries or "
"the Emby library response."
Expand Down
1 change: 1 addition & 0 deletions launch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ if [ "${HYPERWALL_SOAK_ACTIVE:-0}" != "1" ]; then
unset HYPERWALL_SOAK_DWELL_S
unset HYPERWALL_SOAK_ACTIONS
unset HYPERWALL_SOAK_PROFILE
unset HYPERWALL_SOAK_FILTER
unset HYPERWALL_SOAK_REPORT_DIR
unset HYPERWALL_SOAK_REPORT_ROOT
unset HYPERWALL_NO_RELAUNCH
Expand Down
2 changes: 1 addition & 1 deletion soak_wall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ PIDS=()
printf 'started_at=%s\n' "$(python3 -c 'from datetime import datetime, timezone; print(datetime.now(timezone.utc).isoformat())')"
printf 'host='; sw_vers
printf 'hardware='; system_profiler SPHardwareDataType 2>/dev/null || true
printf 'env=HYPERWALL_SOAK_MINUTES=%s HYPERWALL_SOAK_DWELL_S=%s HYPERWALL_SOAK_PROFILE=%s HYPERWALL_HWDEC=%s HYPERWALL_CACHE_BUDGET_MB=%s HYPERWALL_DEMUXER_PER_CELL_MB=%s\n' "$HYPERWALL_SOAK_MINUTES" "$HYPERWALL_SOAK_DWELL_S" "$HYPERWALL_SOAK_PROFILE" "${HYPERWALL_HWDEC:-}" "${HYPERWALL_CACHE_BUDGET_MB:-}" "${HYPERWALL_DEMUXER_PER_CELL_MB:-}"
printf 'env=HYPERWALL_SOAK_MINUTES=%s HYPERWALL_SOAK_DWELL_S=%s HYPERWALL_SOAK_PROFILE=%s HYPERWALL_SOAK_FILTER=%s HYPERWALL_HWDEC=%s HYPERWALL_CACHE_BUDGET_MB=%s HYPERWALL_DEMUXER_PER_CELL_MB=%s\n' "$HYPERWALL_SOAK_MINUTES" "$HYPERWALL_SOAK_DWELL_S" "$HYPERWALL_SOAK_PROFILE" "${HYPERWALL_SOAK_FILTER:-}" "${HYPERWALL_HWDEC:-}" "${HYPERWALL_CACHE_BUDGET_MB:-}" "${HYPERWALL_DEMUXER_PER_CELL_MB:-}"
display_probe="$(system_profiler SPDisplaysDataType 2>/dev/null || true)"
if printf '%s\n' "$display_probe" | python3 -c '
import sys
Expand Down
1 change: 1 addition & 0 deletions tests/run_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"test_freeze_visibility",
"test_platform",
"test_soak_telemetry",
"test_soak_filter",
"test_frame_pump",
"test_frame_pump_integration",
"test_frame_pump_telemetry",
Expand Down
49 changes: 49 additions & 0 deletions tests/test_soak_filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Pure tests for the soak-only initial corpus filter."""
from __future__ import annotations

from hyperwall.soak_filter import apply_initial_filter


def test_initial_favorites_filter_selects_only_favorites():
items = [
{"Id": "a", "UserData": {"IsFavorite": True}},
{"Id": "b", "UserData": {"IsFavorite": False}},
{"Id": "c", "UserData": {}},
]

filtered, mode = apply_initial_filter(items, "favorites")

assert mode == "favorites"
assert [item["Id"] for item in filtered] == ["a"]
assert [item["Id"] for item in items] == ["a", "b", "c"]


def test_initial_filter_is_all_by_default_and_for_unknown_modes():
items = [{"Id": "a"}, {"Id": "b"}]

for mode in (None, "", "all", "unexpected"):
filtered, actual_mode = apply_initial_filter(items, mode)
assert actual_mode == "all"
assert filtered == items
assert filtered is not items


def run_all() -> int:
tests = [
test_initial_favorites_filter_selects_only_favorites,
test_initial_filter_is_all_by_default_and_for_unknown_modes,
]
failures = 0
for test in tests:
try:
test()
print(f" PASS {test.__name__}")
except Exception as exc:
failures += 1
print(f" FAIL {test.__name__}: {exc}")
print(f"\n{len(tests) - failures} passed, {failures} failed")
return failures


if __name__ == "__main__":
raise SystemExit(run_all())
2 changes: 2 additions & 0 deletions tests/test_soak_telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ def test_macos_soak_launcher_collects_system_telemetry():
for expected in (
"HYPERWALL_SOAK_PROFILE:-audio",
"HYPERWALL_SOAK_REPORT_DIR",
"HYPERWALL_SOAK_FILTER",
"HYPERWALL_STATS=1",
"HYPERWALL_PERFTRACE=1",
"powermetrics",
Expand All @@ -239,6 +240,7 @@ def test_normal_launcher_clears_stale_soak_environment():
assert 'HYPERWALL_SOAK_ACTIVE:-0' in source
assert 'unset HYPERWALL_SOAK_MINUTES' in source
assert 'unset HYPERWALL_SOAK_REPORT_DIR' in source
assert 'unset HYPERWALL_SOAK_FILTER' in source
assert 'unset HYPERWALL_NO_LOG_SETUP' in source


Expand Down
Loading