From a7b5bd22410a52947f534ad663364cd1856b01c2 Mon Sep 17 00:00:00 2001 From: Vitali Graf Date: Wed, 15 Apr 2026 20:02:25 +0200 Subject: [PATCH] docs: Spec auf pywinrt umgestellt (winsdk veraltet) winsdk wird seit fast 3 Jahren nicht mehr gepflegt; pywinrt ist der aktive Nachfolger. Alle Paketnamen, Imports und Dependencies aktualisiert, API gegen echte pywinrt-Installation auf Windows verifiziert. Co-Authored-By: Claude Sonnet 4.6 --- ...6-04-15-media-pause-windows-smtc-design.md | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/docs/superpowers/specs/2026-04-15-media-pause-windows-smtc-design.md b/docs/superpowers/specs/2026-04-15-media-pause-windows-smtc-design.md index 38ec624..b530215 100644 --- a/docs/superpowers/specs/2026-04-15-media-pause-windows-smtc-design.md +++ b/docs/superpowers/specs/2026-04-15-media-pause-windows-smtc-design.md @@ -14,9 +14,9 @@ systemweit alle Mediaplayer (Spotify, Browser-Videos, Groove Music, etc.). **In Scope:** -- Windows-Implementierung via `winsdk.windows.media.control` +- Windows-Implementierung via `pywinrt` (`winrt.windows.media.control`) - Factory-Dispatch für `sys.platform == "win32"` → `SmtcController` -- `winsdk` als `win32`-only Dependency in `pyproject.toml` +- `pywinrt`-Pakete als `win32`-only Dependencies in `pyproject.toml` - Tests analog zur MPRIS-Testsuite **Out of Scope:** @@ -53,21 +53,23 @@ Der bestehende Fallback-Zweig (`NoopController`) bleibt für alle anderen Plattf ### Session-Identifikation Sessions werden über ihre `source_app_user_model_id` (AUMID) identifiziert — -der systemweit eindeutige App-Bezeichner (z. B. `"Spotify.exe"`, -`"msedge.exe"`). Das ist das Windows-Äquivalent zum MPRIS-Bus-Namen. +der systemweit eindeutige App-Bezeichner (z. B. +`"SpotifyAB.SpotifyMusic_zpdnekdrzrea0!Spotify"`, `"msedge.exe"`). +Das ist das Windows-Äquivalent zum MPRIS-Bus-Namen. ### `pause()` 1. Manager lazy via `GlobalSystemMediaTransportControlsSessionManager.request_async()` holen. + (`request_async` ist eine Metaclass-Methode und wird direkt auf der Klasse aufgerufen.) 2. `manager.get_sessions()` — synchron, gibt alle aktiven Sessions zurück. -3. Für jede Session: `get_playback_info().playback_status` prüfen. +3. Für jede Session: `session.get_playback_info().playback_status` prüfen. 4. Sessions mit Status `PLAYING` sequenziell pausieren via `await session.try_pause_async()`. 5. AUMID jeder erfolgreich pausierten Session in `self._paused: list[str]` speichern. 6. Fehler pro Session loggen, andere Sessions nicht blockieren. ### `resume()` -1. Aktuelle Sessions vom Manager frisch laden: `dict[aumid, session]`. +1. Aktuelle Sessions vom Manager frisch laden: `{s.source_app_user_model_id: s for s in manager.get_sessions()}`. 2. Für jede AUMID in `self._paused` die Session suchen. 3. Gefundene Sessions: `await session.try_play_async()`. 4. Nicht gefundene Sessions (App seit `pause()` beendet): still überspringen. @@ -82,20 +84,29 @@ unangetastet. ### Circuit-Breaker -Schlägt `request_async()` fehl (z. B. kein WinRT-Support, Headless-Session), +Schlägt `request_async()` fehl (z. B. Headless-Session ohne WinRT-Desktop-Kontext), wird `_broken = True` gesetzt. Alle weiteren Aufrufe sind No-Ops. Die Warnung wird einmalig geloggt — exakt dasselbe Muster wie `MprisController._bus_broken`. ## Dependencies -`pyproject.toml`: +`pyproject.toml` (alle `win32`-only): ```toml -"winsdk>=1.0.0b10; sys_platform == 'win32'", +"winrt-Windows.Media.Control>=3.2.1; sys_platform == 'win32'", +"winrt-Windows.Foundation>=3.2.1; sys_platform == 'win32'", +"winrt-Windows.Foundation.Collections>=3.2.1; sys_platform == 'win32'", ``` -`winsdk` liefert async-native WinRT-Bindings, die direkt mit `asyncio` -zusammenarbeiten. Keine zusätzliche Systemabhängigkeit nötig. +`winrt-runtime` wird automatisch als transitive Dependency von +`winrt-Windows.Media.Control` mitgezogen. + +**Warum `pywinrt` statt `winsdk`:** `winsdk` (https://github.com/pywinrt/python-winsdk) +wird seit fast drei Jahren nicht mehr weiterentwickelt. `pywinrt` +(https://github.com/pywinrt/pywinrt) ist der aktive Nachfolger mit demselben +Maintainer, unterstützt Python 3.10–3.14 und liefert async-native WinRT-Bindings. +Die API ist nahezu identisch, der Import-Pfad ändert sich von +`winsdk.windows.media.control` zu `winrt.windows.media.control`. ## Tests @@ -106,10 +117,10 @@ zusammenarbeiten. Keine zusätzliche Systemabhängigkeit nötig. Alle SMTC-Calls werden mit `unittest.mock.AsyncMock` / `MagicMock` gemockt — kein echter WinRT-Zugriff im Test. -**Hilfsfunktion** `_make_session(status)` — analog zu `_make_player()` in MPRIS-Tests: +**Hilfsfunktion** `_make_session(aumid, status)` — analog zu `_make_player()` in MPRIS-Tests: ```python -def _make_session(aumid: str, status) -> MagicMock: +def _make_session(aumid: str, status: int) -> MagicMock: session = MagicMock() session.source_app_user_model_id = aumid info = MagicMock() @@ -120,6 +131,10 @@ def _make_session(aumid: str, status) -> MagicMock: return session ``` +Der `status`-Wert für PLAYING ist `4` (Integer-Wert von +`GlobalSystemMediaTransportControlsSessionPlaybackStatus.PLAYING`). +Im Test wird der Enum-Import gemockt, um WinRT-Abhängigkeit zu vermeiden. + **Szenarien:** 1. Keine Sessions vorhanden → `pause()` ist No-Op, `_paused` bleibt leer, kein Fehler. @@ -132,10 +147,12 @@ def _make_session(aumid: str, status) -> MagicMock: ### `tests/test_media_factory.py` — Ergänzungen - `sys.platform == "win32"` + `enabled=True` → `SmtcController` -- Bestehenden Test `test_factory_returns_noop_on_non_linux` zu `test_factory_returns_noop_on_other_platforms` umbennen und auf `darwin` patchen (war Windows-Patch, wird jetzt durch eigenen win32-Test ersetzt). +- Bestehenden Test `test_factory_returns_noop_on_non_linux` umbenennen zu + `test_factory_returns_noop_on_other_platforms` und auf `darwin` patchen + (bisher war Windows der Testfall, wird jetzt durch eigenen win32-Test ersetzt). ## Offene Fragen / bewusst verschoben - macOS-Implementierung: kein Äquivalent geplant. -- `winsdk`-Versionsuntergrenze `1.0.0b10` — sollte beim Einbau gegen aktuelle - PyPI-Version geprüft werden. +- `winrt-*`-Versionsuntergrenzen auf `3.2.1` gesetzt (verifiziert am 2026-04-15); + beim Einbau gegen aktuelle PyPI-Version prüfen.