pympris2 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pympris2-0.2.0/PKG-INFO +50 -0
- pympris2-0.2.0/README.md +39 -0
- pympris2-0.2.0/pyproject.toml +64 -0
- pympris2-0.2.0/src/pympris2/__init__.py +213 -0
- pympris2-0.2.0/src/pympris2/cli.py +152 -0
- pympris2-0.2.0/src/pympris2/py.typed +3 -0
pympris2-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pympris2
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: An implementation of an MPRIS D-Bus client
|
|
5
|
+
Author: Stefan Tatschner
|
|
6
|
+
Author-email: Stefan Tatschner <stefan.tatschner@mailbox.org>
|
|
7
|
+
License-Expression: EUPL-1.2
|
|
8
|
+
Requires-Dist: sdbus>=0.14.2
|
|
9
|
+
Requires-Python: >=3.14
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
<!--
|
|
13
|
+
SPDX-FileCopyrightText: 2026 Stefan Tatschner
|
|
14
|
+
|
|
15
|
+
SPDX-License-Identifier: CC0-1.0
|
|
16
|
+
-->
|
|
17
|
+
|
|
18
|
+
# pympris2
|
|
19
|
+
|
|
20
|
+
An implementation of the [MPRIS D-Bus Interface Specification](https://specifications.freedesktop.org/mpris/latest/index.html) using sdbus.
|
|
21
|
+
|
|
22
|
+
## Example
|
|
23
|
+
|
|
24
|
+
``` python
|
|
25
|
+
import asyncio
|
|
26
|
+
|
|
27
|
+
from pympris2 import get_active_players, MPRISInterface
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def main() -> None:
|
|
31
|
+
player = await get_active_players()[0]
|
|
32
|
+
proxy = MPRISInterface.connect(player)
|
|
33
|
+
await proxy.play_pause()
|
|
34
|
+
print(await proxy.volume)
|
|
35
|
+
await proxy.volume.set_async(0.5)
|
|
36
|
+
# …
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
asyncio.run(main())
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
All methods and properties of the implemented interfaces are available in the `proxy` object.
|
|
43
|
+
There is also a CLI tool `mprisctl` available; just check `--help`.
|
|
44
|
+
|
|
45
|
+
## Implementation Status
|
|
46
|
+
|
|
47
|
+
* [org.mpris.MediaPlayer2](https://specifications.freedesktop.org/mpris/latest/Media_Player.html) implemented
|
|
48
|
+
* [org.mpris.MediaPlayer2.Player](https://specifications.freedesktop.org/mpris/latest/Player_Interface.html) implemented
|
|
49
|
+
* [org.mpris.MediaPlayer2.TrackList](https://specifications.freedesktop.org/mpris/latest/Track_List_Interface.html) not implemented
|
|
50
|
+
* [org.mpris.MediaPlayer2.Playlists](https://specifications.freedesktop.org/mpris/latest/Playlists_Interface.html) not implemented
|
pympris2-0.2.0/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Stefan Tatschner
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC0-1.0
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# pympris2
|
|
8
|
+
|
|
9
|
+
An implementation of the [MPRIS D-Bus Interface Specification](https://specifications.freedesktop.org/mpris/latest/index.html) using sdbus.
|
|
10
|
+
|
|
11
|
+
## Example
|
|
12
|
+
|
|
13
|
+
``` python
|
|
14
|
+
import asyncio
|
|
15
|
+
|
|
16
|
+
from pympris2 import get_active_players, MPRISInterface
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def main() -> None:
|
|
20
|
+
player = await get_active_players()[0]
|
|
21
|
+
proxy = MPRISInterface.connect(player)
|
|
22
|
+
await proxy.play_pause()
|
|
23
|
+
print(await proxy.volume)
|
|
24
|
+
await proxy.volume.set_async(0.5)
|
|
25
|
+
# …
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
asyncio.run(main())
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
All methods and properties of the implemented interfaces are available in the `proxy` object.
|
|
32
|
+
There is also a CLI tool `mprisctl` available; just check `--help`.
|
|
33
|
+
|
|
34
|
+
## Implementation Status
|
|
35
|
+
|
|
36
|
+
* [org.mpris.MediaPlayer2](https://specifications.freedesktop.org/mpris/latest/Media_Player.html) implemented
|
|
37
|
+
* [org.mpris.MediaPlayer2.Player](https://specifications.freedesktop.org/mpris/latest/Player_Interface.html) implemented
|
|
38
|
+
* [org.mpris.MediaPlayer2.TrackList](https://specifications.freedesktop.org/mpris/latest/Track_List_Interface.html) not implemented
|
|
39
|
+
* [org.mpris.MediaPlayer2.Playlists](https://specifications.freedesktop.org/mpris/latest/Playlists_Interface.html) not implemented
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Stefan Tatschner
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: CC0-1.0
|
|
4
|
+
|
|
5
|
+
[build-system]
|
|
6
|
+
requires = ["uv_build>=0.11.15,<0.12.0"]
|
|
7
|
+
build-backend = "uv_build"
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
name = "pympris2"
|
|
11
|
+
version = "0.2.0"
|
|
12
|
+
description = "An implementation of an MPRIS D-Bus client"
|
|
13
|
+
license = "EUPL-1.2"
|
|
14
|
+
readme = "README.md"
|
|
15
|
+
authors = [
|
|
16
|
+
{ name = "Stefan Tatschner", email = "stefan.tatschner@mailbox.org" }
|
|
17
|
+
]
|
|
18
|
+
requires-python = ">=3.14"
|
|
19
|
+
dependencies = [
|
|
20
|
+
"sdbus>=0.14.2",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
"mprisctl" = "pympris2.cli:main"
|
|
25
|
+
|
|
26
|
+
[dependency-groups]
|
|
27
|
+
dev = [
|
|
28
|
+
"mypy>=2.1.0",
|
|
29
|
+
"reuse>=6.2.0",
|
|
30
|
+
"ruff>=0.15.16",
|
|
31
|
+
"ty>=0.0.46",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[tool.mypy]
|
|
35
|
+
strict = true
|
|
36
|
+
native_parser = true
|
|
37
|
+
num_workers = 4
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
[tool.ruff.lint]
|
|
41
|
+
select = [
|
|
42
|
+
"B", # flake8-bugbear
|
|
43
|
+
"A", # flake8-builtins
|
|
44
|
+
"ASYNC",# flake8-async
|
|
45
|
+
"C4", # flake8-comprehensions
|
|
46
|
+
"E", # pycodestlye
|
|
47
|
+
"F", # pyflakes
|
|
48
|
+
"FA", # flake8-future-annotations
|
|
49
|
+
"FLY", # flynt
|
|
50
|
+
"FURB", # refurb
|
|
51
|
+
"I", # isort
|
|
52
|
+
"IC", # flake8-import-conventions
|
|
53
|
+
"LOG", # flake8-logging
|
|
54
|
+
"PGH", # pygrep-hooks
|
|
55
|
+
"PIE", # flake8-pie
|
|
56
|
+
"PL", # pylint
|
|
57
|
+
"PTH", # flake8-use-pathlib
|
|
58
|
+
"Q", # flake8-quotes
|
|
59
|
+
"RSE", # flake8-raise
|
|
60
|
+
"RUF100", # unused-noqa
|
|
61
|
+
"TD", # flake8-todos
|
|
62
|
+
"TID", # flake8-tidy-imports
|
|
63
|
+
"UP", # pyupgrade
|
|
64
|
+
]
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Stefan Tatschner
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
|
|
5
|
+
from typing import Self
|
|
6
|
+
|
|
7
|
+
from sdbus import (
|
|
8
|
+
DbusInterfaceCommonAsync,
|
|
9
|
+
dbus_method_async,
|
|
10
|
+
dbus_property_async,
|
|
11
|
+
sd_bus_open_user,
|
|
12
|
+
)
|
|
13
|
+
from sdbus_async.dbus_daemon import FreedesktopDbus
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def get_active_players() -> list[str]:
|
|
17
|
+
bus = sd_bus_open_user()
|
|
18
|
+
proxy = FreedesktopDbus(bus)
|
|
19
|
+
|
|
20
|
+
all_services = await proxy.list_names()
|
|
21
|
+
|
|
22
|
+
return [name for name in all_services if name.startswith("org.mpris.MediaPlayer2.")]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MediaPlayer2Interface(
|
|
26
|
+
DbusInterfaceCommonAsync,
|
|
27
|
+
interface_name="org.mpris.MediaPlayer2",
|
|
28
|
+
):
|
|
29
|
+
# Raise () → nothing
|
|
30
|
+
@dbus_method_async(method_name="Raise")
|
|
31
|
+
async def raise_(self) -> None:
|
|
32
|
+
raise NotImplementedError
|
|
33
|
+
|
|
34
|
+
# Quit () → nothing
|
|
35
|
+
@dbus_method_async()
|
|
36
|
+
async def quit(self) -> None:
|
|
37
|
+
raise NotImplementedError
|
|
38
|
+
|
|
39
|
+
# CanQuit b Read only
|
|
40
|
+
@dbus_property_async("b")
|
|
41
|
+
def can_quit(self) -> bool:
|
|
42
|
+
raise NotImplementedError
|
|
43
|
+
|
|
44
|
+
# Fullscreen b Read/Write
|
|
45
|
+
@dbus_property_async("b")
|
|
46
|
+
def fullscreen(self) -> bool:
|
|
47
|
+
raise NotImplementedError
|
|
48
|
+
|
|
49
|
+
# CanSetFullscreen b Read only
|
|
50
|
+
@dbus_property_async("b")
|
|
51
|
+
def can_set_fullscreen(self) -> bool:
|
|
52
|
+
raise NotImplementedError
|
|
53
|
+
|
|
54
|
+
# CanRaise b Read only
|
|
55
|
+
@dbus_property_async("b")
|
|
56
|
+
def can_raise(self) -> bool:
|
|
57
|
+
raise NotImplementedError
|
|
58
|
+
|
|
59
|
+
# HasTrackList b Read only
|
|
60
|
+
@dbus_property_async("b")
|
|
61
|
+
def has_track_list(self) -> bool:
|
|
62
|
+
raise NotImplementedError
|
|
63
|
+
|
|
64
|
+
# Identity s Read only
|
|
65
|
+
@dbus_property_async("s")
|
|
66
|
+
def identity(self) -> str:
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
|
|
69
|
+
# DesktopEntry s Read only
|
|
70
|
+
@dbus_property_async("s")
|
|
71
|
+
def desktop_entry(self) -> str:
|
|
72
|
+
raise NotImplementedError
|
|
73
|
+
|
|
74
|
+
# SupportedUriSchemes as Read only
|
|
75
|
+
@dbus_property_async("as")
|
|
76
|
+
def supported_uri_schemes(self) -> list[str]:
|
|
77
|
+
raise NotImplementedError
|
|
78
|
+
|
|
79
|
+
# SupportedMimeTypes as Read only
|
|
80
|
+
@dbus_property_async("as")
|
|
81
|
+
def supported_mime_types(self) -> list[str]:
|
|
82
|
+
raise NotImplementedError
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class MediaPlayer2PlayerInterface(
|
|
86
|
+
DbusInterfaceCommonAsync,
|
|
87
|
+
interface_name="org.mpris.MediaPlayer2.Player",
|
|
88
|
+
):
|
|
89
|
+
# Next () → nothing
|
|
90
|
+
@dbus_method_async()
|
|
91
|
+
async def next(self) -> None:
|
|
92
|
+
raise NotImplementedError
|
|
93
|
+
|
|
94
|
+
# Previous () → nothing
|
|
95
|
+
@dbus_method_async()
|
|
96
|
+
async def previous(self) -> None:
|
|
97
|
+
raise NotImplementedError
|
|
98
|
+
|
|
99
|
+
# Pause () → nothing
|
|
100
|
+
@dbus_method_async()
|
|
101
|
+
async def pause(self) -> None:
|
|
102
|
+
raise NotImplementedError
|
|
103
|
+
|
|
104
|
+
# PlayPause () → nothing
|
|
105
|
+
@dbus_method_async()
|
|
106
|
+
async def play_pause(self) -> None:
|
|
107
|
+
raise NotImplementedError
|
|
108
|
+
|
|
109
|
+
# Stop () → nothing
|
|
110
|
+
@dbus_method_async()
|
|
111
|
+
async def stop(self) -> None:
|
|
112
|
+
raise NotImplementedError
|
|
113
|
+
|
|
114
|
+
# Play () → nothing
|
|
115
|
+
@dbus_method_async()
|
|
116
|
+
async def play(self) -> None:
|
|
117
|
+
raise NotImplementedError
|
|
118
|
+
|
|
119
|
+
# Seek (x: Offset) → nothing
|
|
120
|
+
@dbus_method_async("x")
|
|
121
|
+
async def seek(self) -> None:
|
|
122
|
+
raise NotImplementedError
|
|
123
|
+
|
|
124
|
+
# SetPosition (o: TrackId, x: Position) → nothing
|
|
125
|
+
@dbus_method_async("ox")
|
|
126
|
+
async def set_position(self) -> None:
|
|
127
|
+
raise NotImplementedError
|
|
128
|
+
|
|
129
|
+
# OpenUri (s: Uri) → nothing
|
|
130
|
+
@dbus_method_async("s")
|
|
131
|
+
async def open_uri(self, uri: str) -> None:
|
|
132
|
+
raise NotImplementedError
|
|
133
|
+
|
|
134
|
+
# PlaybackStatus s ( Playback_Status) Read only
|
|
135
|
+
@dbus_property_async("s")
|
|
136
|
+
def playback_status(self) -> str:
|
|
137
|
+
raise NotImplementedError
|
|
138
|
+
|
|
139
|
+
# LoopStatus s ( Loop_Status) Read/Write
|
|
140
|
+
@dbus_property_async("s")
|
|
141
|
+
def loop_status(self) -> str:
|
|
142
|
+
raise NotImplementedError
|
|
143
|
+
|
|
144
|
+
# Rate d ( Playback_Rate) Read/Write
|
|
145
|
+
@dbus_property_async("d")
|
|
146
|
+
def rate(self) -> int:
|
|
147
|
+
raise NotImplementedError
|
|
148
|
+
|
|
149
|
+
# Shuffle b Read/Write
|
|
150
|
+
@dbus_property_async("b")
|
|
151
|
+
def shuffle(self) -> bool:
|
|
152
|
+
raise NotImplementedError
|
|
153
|
+
|
|
154
|
+
# Metadata a{sv} ( Metadata_Map) Read only
|
|
155
|
+
@dbus_property_async("a{sv}")
|
|
156
|
+
def metadata(self) -> dict[str, tuple[str, str | int]]:
|
|
157
|
+
raise NotImplementedError
|
|
158
|
+
|
|
159
|
+
# Volume d ( Volume) Read/Write
|
|
160
|
+
@dbus_property_async("d")
|
|
161
|
+
def volume(self) -> int:
|
|
162
|
+
raise NotImplementedError
|
|
163
|
+
|
|
164
|
+
# Position x ( Time_In_Us) Read only
|
|
165
|
+
@dbus_property_async("x")
|
|
166
|
+
def position(self) -> int:
|
|
167
|
+
raise NotImplementedError
|
|
168
|
+
|
|
169
|
+
# MinimumRate d ( Playback_Rate) Read only
|
|
170
|
+
@dbus_property_async("d")
|
|
171
|
+
def minumum_rate(self) -> int:
|
|
172
|
+
raise NotImplementedError
|
|
173
|
+
|
|
174
|
+
# MaximumRate d ( Playback_Rate) Read only
|
|
175
|
+
@dbus_property_async("b")
|
|
176
|
+
def maximum_rate(self) -> int:
|
|
177
|
+
raise NotImplementedError
|
|
178
|
+
|
|
179
|
+
# CanGoNext b Read only
|
|
180
|
+
@dbus_property_async("b")
|
|
181
|
+
def can_go_next(self) -> bool:
|
|
182
|
+
raise NotImplementedError
|
|
183
|
+
|
|
184
|
+
# CanGoPrevious b Read only
|
|
185
|
+
@dbus_property_async("b")
|
|
186
|
+
def can_go_previous(self) -> bool:
|
|
187
|
+
raise NotImplementedError
|
|
188
|
+
|
|
189
|
+
# CanPlay b Read only
|
|
190
|
+
@dbus_property_async("b")
|
|
191
|
+
def can_play(self) -> bool:
|
|
192
|
+
raise NotImplementedError
|
|
193
|
+
|
|
194
|
+
# CanPause b Read only
|
|
195
|
+
@dbus_property_async("b")
|
|
196
|
+
def can_pause(self) -> bool:
|
|
197
|
+
raise NotImplementedError
|
|
198
|
+
|
|
199
|
+
# CanSeek b Read only
|
|
200
|
+
@dbus_property_async("b")
|
|
201
|
+
def can_seek(self) -> bool:
|
|
202
|
+
raise NotImplementedError
|
|
203
|
+
|
|
204
|
+
# CanControl b Read only
|
|
205
|
+
@dbus_property_async("b")
|
|
206
|
+
def can_control(self) -> bool:
|
|
207
|
+
raise NotImplementedError
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class MPRISInterface(MediaPlayer2Interface, MediaPlayer2PlayerInterface):
|
|
211
|
+
@classmethod
|
|
212
|
+
def connect(cls, player: str) -> Self:
|
|
213
|
+
return cls.new_proxy(player, "/org/mpris/MediaPlayer2")
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Stefan Tatschner
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import asyncio
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from pympris2 import MPRISInterface, get_active_players
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def parse_args() -> tuple[argparse.Namespace, argparse.ArgumentParser]:
|
|
14
|
+
parser = argparse.ArgumentParser()
|
|
15
|
+
parser.add_argument(
|
|
16
|
+
"-p",
|
|
17
|
+
"--player",
|
|
18
|
+
metavar="NAME",
|
|
19
|
+
help="connect to this mpris2 player, default: first available player",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
subparsers = parser.add_subparsers(title="available commands", required=True)
|
|
23
|
+
|
|
24
|
+
sp = subparsers.add_parser("list", help="list the names of available players")
|
|
25
|
+
sp.set_defaults(cmd="list")
|
|
26
|
+
|
|
27
|
+
sp = subparsers.add_parser("play", help="starts or resumes playback")
|
|
28
|
+
sp.set_defaults(cmd="play")
|
|
29
|
+
|
|
30
|
+
sp = subparsers.add_parser("pause", help="pause playback")
|
|
31
|
+
sp.set_defaults(cmd="pause")
|
|
32
|
+
|
|
33
|
+
sp = subparsers.add_parser("play_pause", help="pause or pause playback")
|
|
34
|
+
sp.set_defaults(cmd="play_pause")
|
|
35
|
+
|
|
36
|
+
sp = subparsers.add_parser("stop", help="stop playback")
|
|
37
|
+
sp.set_defaults(cmd="stop")
|
|
38
|
+
|
|
39
|
+
sp = subparsers.add_parser("next", help="skip to the next track in the tracklist")
|
|
40
|
+
sp.set_defaults(cmd="next")
|
|
41
|
+
|
|
42
|
+
sp = subparsers.add_parser(
|
|
43
|
+
"previous", help="skip to the next track in the tracklist"
|
|
44
|
+
)
|
|
45
|
+
sp.set_defaults(cmd="previous")
|
|
46
|
+
|
|
47
|
+
sp = subparsers.add_parser("status", help="current playback status")
|
|
48
|
+
sp.set_defaults(cmd="status")
|
|
49
|
+
|
|
50
|
+
sp = subparsers.add_parser(
|
|
51
|
+
"metadata", help="print metadata information for the current track"
|
|
52
|
+
)
|
|
53
|
+
sp.add_argument("--json", action="store_true", help="output as json")
|
|
54
|
+
sp.set_defaults(cmd="metadata")
|
|
55
|
+
|
|
56
|
+
sp = subparsers.add_parser("volume", help="print or set the volume to LEVEL")
|
|
57
|
+
sp.add_argument(
|
|
58
|
+
"VOLUME",
|
|
59
|
+
metavar="LEVEL",
|
|
60
|
+
type=float,
|
|
61
|
+
nargs="?",
|
|
62
|
+
help="value to set volume to from 0.0 to 1.0",
|
|
63
|
+
)
|
|
64
|
+
sp.set_defaults(cmd="volume")
|
|
65
|
+
|
|
66
|
+
sp = subparsers.add_parser("open", help="open the given URI")
|
|
67
|
+
sp.add_argument(
|
|
68
|
+
"URI",
|
|
69
|
+
help="either file path or remote URL",
|
|
70
|
+
)
|
|
71
|
+
sp.set_defaults(cmd="open")
|
|
72
|
+
|
|
73
|
+
sp = subparsers.add_parser("loop", help="print or set the loop status")
|
|
74
|
+
sp.add_argument(
|
|
75
|
+
"STATUS",
|
|
76
|
+
metavar="STATUS",
|
|
77
|
+
nargs="?",
|
|
78
|
+
choices=["None", "Track", "Playlist"],
|
|
79
|
+
help="'None', 'Track', or 'Playlist'",
|
|
80
|
+
)
|
|
81
|
+
sp.set_defaults(cmd="loop")
|
|
82
|
+
|
|
83
|
+
return parser.parse_args(), parser
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
async def get_player_target(chosen: str | None) -> str:
|
|
87
|
+
avail_players = await get_active_players()
|
|
88
|
+
|
|
89
|
+
if isinstance(chosen, str) and chosen in avail_players:
|
|
90
|
+
return chosen
|
|
91
|
+
|
|
92
|
+
return avail_players[0] if avail_players else ""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _remove_dbus_types(d: dict[str, tuple[str, str | int]]) -> dict[str, str | int]:
|
|
96
|
+
out = {}
|
|
97
|
+
for k, v in d.items():
|
|
98
|
+
out[k] = v[1]
|
|
99
|
+
return out
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
async def _main() -> None: # noqa: PLR0912
|
|
103
|
+
args, parser = parse_args()
|
|
104
|
+
|
|
105
|
+
if args.cmd == "list":
|
|
106
|
+
for player in await get_active_players():
|
|
107
|
+
print(player)
|
|
108
|
+
sys.exit(0)
|
|
109
|
+
|
|
110
|
+
player = await get_player_target(args.player)
|
|
111
|
+
if not player:
|
|
112
|
+
parser.error("player not available")
|
|
113
|
+
|
|
114
|
+
proxy = MPRISInterface.connect(player)
|
|
115
|
+
match args.cmd:
|
|
116
|
+
case "play":
|
|
117
|
+
await proxy.play()
|
|
118
|
+
case "play_pause":
|
|
119
|
+
await proxy.play_pause()
|
|
120
|
+
case "pause":
|
|
121
|
+
await proxy.pause()
|
|
122
|
+
case "stop":
|
|
123
|
+
await proxy.stop()
|
|
124
|
+
case "next":
|
|
125
|
+
await proxy.next()
|
|
126
|
+
case "previous":
|
|
127
|
+
await proxy.previous()
|
|
128
|
+
case "status":
|
|
129
|
+
print(await proxy.playback_status)
|
|
130
|
+
case "volume":
|
|
131
|
+
if args.VOLUME:
|
|
132
|
+
await proxy.volume.set_async(args.VOLUME)
|
|
133
|
+
else:
|
|
134
|
+
print(await proxy.volume)
|
|
135
|
+
case "loop":
|
|
136
|
+
if args.STATUS:
|
|
137
|
+
await proxy.loop_status.set_async(args.STATUS)
|
|
138
|
+
else:
|
|
139
|
+
print(await proxy.loop_status)
|
|
140
|
+
case "open":
|
|
141
|
+
await proxy.open_uri(args.URI)
|
|
142
|
+
case "metadata":
|
|
143
|
+
m = _remove_dbus_types(await proxy.metadata)
|
|
144
|
+
if args.json:
|
|
145
|
+
print(json.dumps(m))
|
|
146
|
+
else:
|
|
147
|
+
for k, v in m.items():
|
|
148
|
+
print(f"{k}: {v}")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def main() -> None:
|
|
152
|
+
asyncio.run(_main())
|