fix: watch pos not updated after starting as paused
This commit is contained in:
@@ -78,12 +78,11 @@ class PositionTracker:
|
||||
if player_changed or track_changed:
|
||||
# reset to 0 so stale position from a previous track doesn't bleed through
|
||||
self._position_ms = 0
|
||||
# only poll MPRIS when something changed and the player is actually running;
|
||||
# avoids an unnecessary D-Bus round-trip on every calibration-loop tick
|
||||
should_calibrate_now = (
|
||||
self._is_playing
|
||||
and bool(self._active_player)
|
||||
and (player_changed or track_changed or status_changed_to_playing)
|
||||
# poll MPRIS on any identity change (player, track, or resume) so a paused
|
||||
# mid-song player gets its position anchored immediately; calibration-loop
|
||||
# ticks are excluded because they pass the same player/track/status
|
||||
should_calibrate_now = bool(self._active_player) and (
|
||||
player_changed or track_changed or status_changed_to_playing
|
||||
)
|
||||
self._track_key = track_key
|
||||
self._last_tick = time.monotonic()
|
||||
|
||||
@@ -149,6 +149,20 @@ def test_position_tracker_resume_via_playback_status_calibrates() -> None:
|
||||
asyncio.run(_run())
|
||||
|
||||
|
||||
def test_position_tracker_paused_start_calibrates_initial_position() -> None:
|
||||
"""set_active_player with Paused must still calibrate position — player may be mid-song."""
|
||||
|
||||
async def _run() -> None:
|
||||
tracker = PositionTracker(lambda _: asyncio.sleep(0, result=45000), TEST_CONFIG)
|
||||
await tracker.start()
|
||||
await tracker.set_active_player(BUS, "Paused", "track-A")
|
||||
pos = await tracker.get_position_ms()
|
||||
await tracker.stop()
|
||||
assert pos >= 45000
|
||||
|
||||
asyncio.run(_run())
|
||||
|
||||
|
||||
def test_position_tracker_resume_via_set_active_player_calibrates() -> None:
|
||||
async def _run() -> None:
|
||||
tracker = PositionTracker(lambda _: asyncio.sleep(0, result=42000), TEST_CONFIG)
|
||||
@@ -458,6 +472,66 @@ def test_coordinator_fetches_while_paused() -> None:
|
||||
asyncio.run(_run())
|
||||
|
||||
|
||||
def test_coordinator_paused_start_emits_correct_line_after_fetch() -> None:
|
||||
"""After fetch completes with a mid-song paused player, the current lyric line must render."""
|
||||
|
||||
async def _run() -> None:
|
||||
received: list[WatchState] = []
|
||||
|
||||
class _CaptureOutput(BaseOutput):
|
||||
position_sensitive = True
|
||||
|
||||
async def on_state(self, state: WatchState) -> None:
|
||||
received.append(state)
|
||||
|
||||
class _Manager:
|
||||
def fetch_for_track(self, *_a, **_kw):
|
||||
return None
|
||||
|
||||
PAUSED_MS = 45000
|
||||
lrc = LRCData("[00:43.00]a\n[00:44.00]b\n[00:46.00]c")
|
||||
|
||||
session = WatchCoordinator(
|
||||
_Manager(), # type: ignore
|
||||
_CaptureOutput(),
|
||||
player_hint=None,
|
||||
config=TEST_CONFIG,
|
||||
)
|
||||
session._tracker = PositionTracker(
|
||||
lambda _bus: asyncio.sleep(0, result=PAUSED_MS),
|
||||
TEST_CONFIG,
|
||||
)
|
||||
await session._tracker.start()
|
||||
|
||||
# Calibrate tracker directly (tracker-level behavior already covered by
|
||||
# test_position_tracker_paused_start_calibrates_initial_position)
|
||||
await session._tracker.set_active_player(BUS, "Paused", "Artist - Song")
|
||||
|
||||
# Put model in the state _on_player_change would have produced
|
||||
session._model.active_player = BUS
|
||||
session._model.active_track_key = "Artist - Song"
|
||||
session._model.status = WatchStatus.FETCHING
|
||||
session._player_monitor.players = {BUS: _pstate("Paused")}
|
||||
session._last_emit_signature = (
|
||||
"status",
|
||||
WatchStatus.FETCHING,
|
||||
BUS,
|
||||
"Artist - Song",
|
||||
)
|
||||
|
||||
await session._on_lyrics_update(lrc)
|
||||
|
||||
last_ok = next(
|
||||
(s for s in reversed(received) if s.status == WatchStatus.OK), None
|
||||
)
|
||||
assert last_ok is not None, "no OK state emitted after lyrics loaded"
|
||||
assert last_ok.position_ms >= PAUSED_MS
|
||||
|
||||
await session._tracker.stop()
|
||||
|
||||
asyncio.run(_run())
|
||||
|
||||
|
||||
def test_coordinator_fetches_on_track_change() -> None:
|
||||
async def _run() -> None:
|
||||
session = _make_coordinator()
|
||||
|
||||
Reference in New Issue
Block a user