feat(media_player): foreground tier in the state ladder with app-source now-playing (last-known → last_channel → audio hint), sensor reads the store's foreground priority chain (P3-3a,c)
This commit is contained in:
1 parent
1f0a4b2021
commit
bc457baae7
3 files changed
+140
-2
No files matched your search
@@ -113,6 +113,7 @@ def make_player(modules, manager=None, jellyfin_entity=None, options=None):
|
||||
player._entry = entry
|
||||
player._manager = manager
|
||||
player._intent = None
|
||||
player._last_channel = None
|
||||
player.hass = entity_stublib.FakeHass()
|
||||
player._entity_tasks = set()
|
||||
player._channel_task = None
|
||||
@@ -380,6 +381,95 @@ class TestAppFallback:
|
||||
assert player.app_id == "com.google.android.youtube.tv"
|
||||
|
||||
|
||||
class TestForegroundLadderTier:
|
||||
"""P3-3a: the ladder branch between the media-session tier and ON."""
|
||||
|
||||
def _player(self, modules, sessions=None, foreground=None, audio=None):
|
||||
player, _s, _i = make_player(modules)
|
||||
manager = player._manager
|
||||
manager.store.set("awake", True)
|
||||
if sessions:
|
||||
manager.store.set("sessions", sessions)
|
||||
if foreground:
|
||||
manager.store.set("foreground", foreground)
|
||||
if audio:
|
||||
manager.store.set("audio_playback", audio)
|
||||
player._androidtv_entity = ""
|
||||
player._read = lambda eid: None
|
||||
return player, manager
|
||||
|
||||
async def test_session_app_serves_playing_via_session_tier(self, modules):
|
||||
from nsh.ha_adb_common import MediaSessionInfo
|
||||
|
||||
sessions = [
|
||||
MediaSessionInfo(package="com.netflix.ninja", playing=True, title="Movie")
|
||||
]
|
||||
player, manager = self._player(
|
||||
modules, sessions=sessions, foreground="com.netflix.ninja"
|
||||
)
|
||||
|
||||
assert player.state.value == "playing" # session tier, not the fg tier
|
||||
np = player._get_adb_now_playing()
|
||||
assert np["source"] == "media_session"
|
||||
assert np["title"] == "Movie"
|
||||
|
||||
async def test_no_session_app_with_audio_hint(self, modules):
|
||||
player, _m = self._player(
|
||||
modules,
|
||||
foreground="ar.tvplayer.tv",
|
||||
audio=["ar.tvplayer.tv"],
|
||||
)
|
||||
|
||||
# TiviMate publishes no session (R2) — fg tier keeps the device ON
|
||||
assert player.state.value == "on"
|
||||
np = player._get_app_now_playing()
|
||||
assert np == {
|
||||
"state": "idle",
|
||||
"title": "Media recently active",
|
||||
"source": "app",
|
||||
}
|
||||
|
||||
async def test_no_session_app_without_hint_returns_none(self, modules):
|
||||
player, _m = self._player(modules, foreground="com.spocky.projengmenu")
|
||||
|
||||
assert player.state.value == "on" # launcher foreground → still ON
|
||||
assert player._get_app_now_playing() is None
|
||||
|
||||
async def test_last_known_title_beats_audio_hint(self, modules):
|
||||
from nsh.ha_adb_common import MediaSessionInfo
|
||||
|
||||
player, manager = self._player(
|
||||
modules, foreground="ar.tvplayer.tv", audio=["ar.tvplayer.tv"]
|
||||
)
|
||||
manager._clock = lambda: 0.0 # stamp 0.0, clock 0.0 → inside TTL
|
||||
info = MediaSessionInfo(package="ar.tvplayer.tv", playing=True, title="Sky News")
|
||||
manager._last_known["ar.tvplayer.tv"] = (info, 0.0)
|
||||
|
||||
np = player._get_app_now_playing()
|
||||
assert np == {"state": "playing", "title": "Sky News", "source": "app"}
|
||||
|
||||
async def test_ttl_resurrect_guard_in_the_tier(self, modules):
|
||||
from nsh.ha_adb_common import MediaSessionInfo
|
||||
|
||||
now = 1000.0
|
||||
player, manager = self._player(modules, foreground="ar.tvplayer.tv")
|
||||
player._last_channel = "101"
|
||||
|
||||
info = MediaSessionInfo(package="ar.tvplayer.tv", playing=True, title="Sky News")
|
||||
manager._clock = lambda: now # the closure reads the live `now` cell
|
||||
manager._last_known["ar.tvplayer.tv"] = (info, now)
|
||||
|
||||
# inside TTL → last-known title served
|
||||
np = player._get_app_now_playing()
|
||||
assert np == {"state": "playing", "title": "Sky News", "source": "app"}
|
||||
|
||||
# past STATE_STORE_TTL → 'playing' must NOT be resurrected;
|
||||
# the tier falls back to the last channel instead
|
||||
now = now + 61.0
|
||||
np = player._get_app_now_playing()
|
||||
assert np == {"state": "idle", "title": "101", "source": "app"}
|
||||
|
||||
|
||||
class TestVolumeAndPowerLadder:
|
||||
async def test_volume_readout_from_protocol_push(self, modules):
|
||||
player, served, _intents = make_player(modules)
|
||||
|
||||
Reference in new issue
Block a user