fix(media_player): keep blank app names out of the source list (t49 F1)

t48 F1: source_list stringified the app map's values without filtering, so a
hand-edited map with a blank name ("com.x=") put an empty string in the dropdown.
That entry is worse than useless — it is a dropdown item whose only possible outcome
is an error, because select_source has to reject an empty name as an unknown source.

source_list now drops blank and whitespace-only names. The values themselves are
untouched: no name is rewritten or trimmed here, because the reverse lookup matches on
them, so for a well-formed map the list stays byte-equal to the Browse tree's child
titles (same _app_directory accessor, same order) — pinned by comparing the two
directly, for the user's map and for the curated default. The reverse lookup is
unchanged.

Tests (tests/test_media_sources.py, 3 new; tests/test_standalone_setup.py, 1 new):
a hand-edited map with "" and "   " entries offers only the real names; the offered
list equals the Browse tree titles for well-formed maps (both map and curated
default); the surviving entries still resolve AND launch through select_source, and a
blank name still resolves to nothing; and the editor keeps rejecting "com.x="
(_app_map_from_text returns None, the page answers invalid_app_map and stores nothing
half-way), so a blank name cannot get in that way either.

RED PROOF: with the filter removed, exactly the two blank-name tests fail — the blank
appears in the list, and the entry has no package to resolve to (1 passed / 2 failed;
the byte-equality test passes either way, which is the "well-formed list unchanged"
half of the claim) — while the rest of the file is green; restored, 20 pass.

Verified: 458 passed / 2 skipped, 459 / 1 with androidtvremote2, ruff clean, drift
engine exit 0 under real HA (23 modules, 58 imports, 164 relative imports, 36 call
arguments, 7 awaited HA calls).

Residual, deliberately NOT touched here (out of this repair's scope, and pre-existing):
a blank map entry still reaches the Browse tree as an empty child title, and a PADDED
name ("  TiviMate ", hand-edited only — the editor strips) still fails the reverse
lookup because the map value is matched unstripped. Both are hand-edited-map edges;
say the word and either can be a follow-up.
This commit is contained in:
James Mackay committed 2026-10-11 07:17:56 +01:00
1 parent 1c6c223f27
commit 97615408c0
3 files changed
+81 -1

No files matched your search

@@ -729,8 +729,15 @@ class NvidiaShieldMediaPlayer(MediaPlayerEntity):
Directory order (the user's map order, or the curated list), so rebuilding
the page does not shuffle it.
A blank or whitespace-only name is dropped (t49): the apps map is hand-editable
(``com.x=``), and an empty entry would sit in the dropdown as something to pick
that select_source then rejects as an unknown source — an option that can only
fail. The VALUES themselves are untouched, so for a well-formed map the list
stays byte-equal to the Browse tree's child titles (same accessor, same order);
names are never rewritten here, because the reverse lookup matches on them.
"""
return [str(name) for name in self._app_directory.values()]
return [str(name) for name in self._app_directory.values() if str(name).strip()]
@property
def source(self) -> str | None: