diff --git a/tests/test_media_mpris.py b/tests/test_media_mpris.py index e3dd14d..c8f1f4c 100644 --- a/tests/test_media_mpris.py +++ b/tests/test_media_mpris.py @@ -173,3 +173,27 @@ async def test_pause_is_noop_when_bus_unreachable(monkeypatch, caplog): assert controller._paused == [] assert any("D-Bus" in r.message or "bus" in r.message.lower() for r in caplog.records) + + +@pytest.mark.asyncio +async def test_pause_skips_reconnect_after_bus_failure(monkeypatch): + """Bus-Connect-Fehler darf nicht bei jedem Aufruf erneut versucht werden.""" + from whisper_local.media._mpris import MprisController + + connect_calls = 0 + + async def failing_connect(self): + nonlocal connect_calls + connect_calls += 1 + raise RuntimeError("no session bus") + + monkeypatch.setattr( + "dbus_next.aio.MessageBus.connect", failing_connect, raising=False + ) + controller = MprisController() + + await controller.pause() + await controller.pause() + await controller.pause() + + assert connect_calls == 1 diff --git a/whisper_local/media/_mpris.py b/whisper_local/media/_mpris.py index 502af07..232a8ed 100644 --- a/whisper_local/media/_mpris.py +++ b/whisper_local/media/_mpris.py @@ -18,8 +18,11 @@ class MprisController: def __init__(self) -> None: self._paused: list[str] = [] self._bus: Any = None + self._bus_broken: bool = False async def _ensure_bus(self) -> Any: + if self._bus_broken: + raise RuntimeError("D-Bus session bus is unavailable") if self._bus is None: from dbus_next.aio import MessageBus self._bus = await MessageBus().connect() @@ -65,7 +68,11 @@ class MprisController: try: names = await self._list_player_names() except Exception as e: - logger.warning("D-Bus nicht erreichbar, überspringe Media-Pause: %s", e) + if not self._bus_broken: + logger.warning( + "D-Bus nicht erreichbar, Media-Pause dauerhaft deaktiviert: %s", e + ) + self._bus_broken = True self._paused = [] return results = await asyncio.gather(