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
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,12 @@ captures **no images, screenshots, or video**. Use `--decoders no` to isolate
server auto-transcoding from the Mac decoder, or select another decoder for a
separate decoder experiment. `--expected-cells 4` or `--expected-cells 8`
blocks a phase if the final stats contain a different number of cells. A
source-health failure is reported separately from a client/decoder failure.
The current checkout still requires one manual SetupWizard acceptance per
source-health failure is reported separately from a client/decoder failure. To
run a controlled known-good or malformed-resource decoder phase, pass the exact
Emby item ID with `--item-id <id>`. The selector takes precedence over the
broad pool filter, requires exactly one matching item, records the ID in the
private phase manifest, and fails closed on a missing or duplicate match. The
current checkout still requires one manual SetupWizard acceptance per
live phase; the runner prints this notice rather than automating GUI clicks.

The default 10-minute phases are a pilot. Run the full soak only after the
Expand Down
3 changes: 3 additions & 0 deletions docs/performance-roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ another 30- or 60-minute soak.
unchanged, and Windows/Linux ignore this variable.
- Decoder telemetry records the requested and active decoder, hardware attempts
and activations, software fallbacks, exhausted recovery, and quarantine.
- `scripts/run-soak-diagnostics.py --item-id <id>` selects one exact Emby item
for a controlled decoder phase. It fails closed when the source response has
zero or multiple matches and records the selector in the private manifest.
- `scripts/profile-macos-render.py` parses native captures. Its `--matrix`
mode accepts normalized profile JSON or `analyze_run()` reports and selects
the highest passing 4/6/8-cell mode. Missing evidence blocks selection.
Expand Down
19 changes: 16 additions & 3 deletions hyperwall/soak_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,25 @@
def apply_initial_filter(
items: list[dict[str, Any]],
mode: str | None,
*,
item_id: str | None = None,
) -> tuple[list[dict[str, Any]], str]:
"""Return the initial playback pool and its normalized filter mode.
"""Return the initial soak pool and its normalized selection 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.
``item_id`` is an opt-in exact-resource selector for controlled native
decoder experiments. It takes precedence over the broad pool filter and
fails closed when the source response does not contain exactly one match.
Normal launches pass no selector and retain the existing all/favorites
behavior.
"""
if item_id not in (None, ""):
matches = [item for item in items if item.get("Id") == item_id]
if not matches:
return [], "item-not-found"
if len(matches) != 1:
return [], "item-ambiguous"
return list(matches), "item"

normalized = str(mode or "").strip().lower()
if normalized != "favorites":
return list(items), "all"
Expand Down
17 changes: 11 additions & 6 deletions hyperwall/wall.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,13 @@ def _start_async_load(self) -> None:
self.loader.start()

def _on_items_loaded(self, items: list[dict[str, Any]]) -> None:
soak_active = os.environ.get("HYPERWALL_SOAK_ACTIVE") == "1"
initial_filter = (
os.environ.get("HYPERWALL_SOAK_FILTER", "") if soak_active else ""
)
initial_item_id = (
os.environ.get("HYPERWALL_SOAK_ITEM_ID") if soak_active else None
)
source_items = select_playback_candidates(
list(items),
direct_only=getattr(self, "_stable_direct_only", False),
Expand All @@ -709,14 +716,12 @@ def _on_items_loaded(self, items: list[dict[str, Any]]) -> None:
"auto-transcode enabled for heavy or unmeasured sources.",
len(source_items),
)
self.all_items = source_items
initial_filter = (
os.environ.get("HYPERWALL_SOAK_FILTER", "")
if os.environ.get("HYPERWALL_SOAK_ACTIVE") == "1"
else ""
selection_items = (
list(items) if initial_item_id not in (None, "") else source_items
)
self.all_items = selection_items
self.filtered, self.filter_mode = apply_initial_filter(
source_items, initial_filter
selection_items, initial_filter, item_id=initial_item_id
)
self.playlists.set_source(self.filtered, DEFAULT_GROUP)
if self.filter_mode != "all":
Expand Down
1 change: 1 addition & 0 deletions launch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ if [ "${HYPERWALL_SOAK_ACTIVE:-0}" != "1" ]; then
unset HYPERWALL_SOAK_ACTIONS
unset HYPERWALL_SOAK_PROFILE
unset HYPERWALL_SOAK_FILTER
unset HYPERWALL_SOAK_ITEM_ID
unset HYPERWALL_SOAK_REPORT_DIR
unset HYPERWALL_SOAK_REPORT_ROOT
unset HYPERWALL_NO_RELAUNCH
Expand Down
22 changes: 19 additions & 3 deletions scripts/run-soak-diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,8 @@ def _validate_endpoint(url: str | None) -> None:
def _safe_env_manifest(env: object) -> dict[str, object]:
keys = (
"HYPERWALL_STATS", "HYPERWALL_PERFTRACE", "HYPERWALL_SOAK_MINUTES",
"HYPERWALL_SOAK_DWELL_S", "HYPERWALL_SOAK_PROFILE", "HYPERWALL_HWDEC",
"HYPERWALL_SOAK_DWELL_S", "HYPERWALL_SOAK_PROFILE", "HYPERWALL_SOAK_FILTER",
"HYPERWALL_SOAK_ITEM_ID", "HYPERWALL_HWDEC",
"HYPERWALL_CACHE_BUDGET_MB", "HYPERWALL_DEMUXER_PER_CELL_MB",
"HYPERWALL_AUTO_TRANSCODE", "HYPERWALL_STABLE_DIRECT_ONLY",
"HYPERWALL_STABLE_MAX_FPS", "HYPERWALL_STABLE_MAX_BITRATE_MBPS",
Expand All @@ -269,13 +270,20 @@ def _force_private_permissions(path: Path) -> None:
force_private_permissions(path, mode)


def _base_env(report_dir: Path, minutes: int, dwell: int) -> dict[str, str]:
def _base_env(
report_dir: Path,
minutes: int,
dwell: int,
*,
item_id: str | None = None,
) -> dict[str, str]:
env = os.environ.copy()
for key in (
"HYPERWALL_HWDEC",
"HYPERWALL_VO",
"HYPERWALL_GPU_API",
"HYPERWALL_PROFILE",
"HYPERWALL_SOAK_ITEM_ID",
):
env.pop(key, None)
env.update({
Expand All @@ -292,6 +300,8 @@ def _base_env(report_dir: Path, minutes: int, dwell: int) -> dict[str, str]:
"HYPERWALL_NO_LOG_SETUP": "1",
"LC_NUMERIC": "C",
})
if item_id is not None:
env["HYPERWALL_SOAK_ITEM_ID"] = item_id
return env


Expand Down Expand Up @@ -330,10 +340,11 @@ def _run_live_phase(
dwell: int,
watchdog: int,
expected_cells: int | None = None,
item_id: str | None = None,
) -> int:
phase_dir.mkdir(parents=True, exist_ok=True)
_force_private_permissions(phase_dir)
env = _base_env(phase_dir, minutes, dwell)
env = _base_env(phase_dir, minutes, dwell, item_id=item_id)
env["HYPERWALL_HWDEC"] = decoder
_write_run_metadata(
phase_dir / "runner.json",
Expand Down Expand Up @@ -549,6 +560,10 @@ def main(argv: list[str] | None = None) -> int:
"--expected-cells", type=int, default=None,
help="Require this many final stats cells; mismatch blocks the phase.",
)
parser.add_argument(
"--item-id", default=None,
help="Select exactly one Emby item ID for controlled decoder phases.",
)
parser.add_argument(
"--decoders", default=",".join(DEFAULT_DECODERS),
help="Comma-separated decoder A/B phases; use one value to run one phase.",
Expand Down Expand Up @@ -640,6 +655,7 @@ def main(argv: list[str] | None = None) -> int:
dwell=args.dwell,
watchdog=args.watchdog_grace,
expected_cells=args.expected_cells,
item_id=args.item_id,
)
result = _analyze_phase(
phase_dir,
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_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:-}"
printf 'env=HYPERWALL_SOAK_MINUTES=%s HYPERWALL_SOAK_DWELL_S=%s HYPERWALL_SOAK_PROFILE=%s HYPERWALL_SOAK_FILTER=%s HYPERWALL_SOAK_ITEM_ID=%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_SOAK_ITEM_ID:-}" "${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
18 changes: 18 additions & 0 deletions tests/test_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import sys
import tempfile
from pathlib import Path, PurePosixPath
from unittest import mock

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

Expand Down Expand Up @@ -63,6 +64,23 @@ def test_runner_records_only_safe_environment_fields():
assert "os.environ" in source


def test_runner_propagates_exact_item_id_into_safe_manifest():
runner = _load_runner_module()
with tempfile.TemporaryDirectory() as tmp:
env = runner._base_env(Path(tmp), 1, 0, item_id="known-good-id")
assert env["HYPERWALL_SOAK_ITEM_ID"] == "known-good-id"
assert runner._safe_env_manifest(env)["HYPERWALL_SOAK_ITEM_ID"] == "known-good-id"


def test_runner_clears_ambient_item_id_without_explicit_selector():
runner = _load_runner_module()
with tempfile.TemporaryDirectory() as tmp, mock.patch.dict(
os.environ, {"HYPERWALL_SOAK_ITEM_ID": "stale-id"}, clear=False
):
env = runner._base_env(Path(tmp), 1, 0)
assert "HYPERWALL_SOAK_ITEM_ID" not in env


def test_runner_has_safe_preflight_and_manual_wizard_notice():
source = open(
os.path.join(
Expand Down
36 changes: 36 additions & 0 deletions tests/test_soak_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,46 @@ def test_initial_filter_is_all_by_default_and_for_unknown_modes():
assert filtered is not items


def test_exact_item_selection_overrides_pool_filter():
items = [
{"Id": "good", "UserData": {"IsFavorite": False}},
{"Id": "other", "UserData": {"IsFavorite": True}},
]

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

assert mode == "item"
assert filtered == [items[0]]


def test_exact_item_selection_fails_closed_when_missing_or_ambiguous():
items = [
{"Id": "good"},
{"Id": "duplicate"},
{"Id": "duplicate"},
]

missing, missing_mode = apply_initial_filter(
items, "favorites", item_id="absent"
)
ambiguous, ambiguous_mode = apply_initial_filter(
items, "favorites", item_id="duplicate"
)

assert missing == []
assert missing_mode == "item-not-found"
assert ambiguous == []
assert ambiguous_mode == "item-ambiguous"


def run_all() -> int:
tests = [
test_initial_favorites_filter_selects_only_favorites,
test_initial_filter_is_all_by_default_and_for_unknown_modes,
test_exact_item_selection_overrides_pool_filter,
test_exact_item_selection_fails_closed_when_missing_or_ambiguous,
]
failures = 0
for test in tests:
Expand Down
2 changes: 2 additions & 0 deletions tests/test_soak_telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ def test_macos_soak_launcher_collects_system_telemetry():
"HYPERWALL_SOAK_PROFILE:-audio",
"HYPERWALL_SOAK_REPORT_DIR",
"HYPERWALL_SOAK_FILTER",
"HYPERWALL_SOAK_ITEM_ID",
"HYPERWALL_STATS=1",
"HYPERWALL_PERFTRACE=1",
"powermetrics",
Expand Down Expand Up @@ -241,6 +242,7 @@ def test_normal_launcher_clears_stale_soak_environment():
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_SOAK_ITEM_ID' in source
assert 'unset HYPERWALL_NO_LOG_SETUP' in source


Expand Down
Loading