I'm trying to create a system to keep track of the currently-playing media via mpris. Adapted from this question's PyQt6 answer, I have tried the following code:
from PyQt6 import QtCore, QtWidgets, QtDBus
import sys
class MainWindow(QtWidgets.QMainWindow):
def __init__ (self):
super().__init__()
service = 'org.mpris.MediaPlayer2.vlc'
path = '/org/mpris/MediaPlayer2'
iface = 'org.mpris.MediaPlayer2'
conn = QtDBus.QDBusConnection.systemBus()
conn.registerObject('/', self)
conn.connect(service, path, iface, 'PropertiesChanged', self.nochangeslot)
@QtCore.pyqtSlot(QtDBus.QDBusMessage)
def nochangeslot(self, msg):
print(f'signature: {msg.signature()!r}, '
f'arguments: {msg.arguments()!r}')
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
This should connect any VLC Media Player instance (replace with service of your choice or find it programatically) emitting PropertiesChanged to a simple function that prints the message.
PropertiesChanged should be emitted when doing things like changing the current song. However, nothing is printed when doing so. I also tried changing iface
to 'org.mrpis.MediaPlayer2.Player'
, but it didn't improve matters.
Any idea why this isn't working?
There are two problems:
You won't find MPRIS players (nor any desktop other apps) on the system bus¹. They are all connected to the user's individual session bus i.e. .sessionBus()
.
Generally the only services you will find on the system bus are those which are global to the system (e.g. NetworkManager), whereas multiple users on the same system could have their own copies of the same player or app running, so accordingly each user also has a separate "session bus" (which is now more often really a "user bus").
Use d-spy
or d-feet
, or qdbusviewer6
, or busctl --acquired
(with the --user
option for the session bus) to see which D-Bus services are active where.
$ busctl --user --acquired
NAME
org.mpris.MediaPlayer2.quodlibet
¹ (Such apps may still connect to the system bus as clients, though – just not as services.)
Since properties are a generic D-Bus concept, their signals and methods are implemented as part of the generic org.freedesktop.DBus.Properties
interface, not as part of the object's custom interfaces. (The "real" interface that each property belongs to is actually provided as an argument to those signals and methods.)
Use D-Spy/D-Feet/QDBusViewer or gdbus introspect
or busctl introspect
to see which signals exist under which interfaces.
$ busctl --user introspect \
org.mpris.MediaPlayer2.quodlibet /org/mpris/MediaPlayer2
NAME TYPE SIGNATURE RESULT/VALUE
org.freedesktop.DBus.Properties interface - -
.PropertiesChanged signal sa{sv}as -
$ gdbus introspect -e \
-d org.mpris.MediaPlayer2.quodlibet \
-o /org/mpris/MediaPlayer2
node /org/mpris/MediaPlayer2 {
interface org.freedesktop.DBus.Properties {
signals:
PropertiesChanged(s interface_name,
a{sv} changed_properties,
as invalidated_properties);
};
};
If in doubt, run dbus-monitor
or busctl monitor
(or use Bustle, or even Wireshark) to see what is being transferred over each bus.
dbus-monitor --session "type=signal,member=PropertiesChanged"
Note: With services that use the 'legacy' libdbus, there can sometimes be a mismatch between what the service declares via its .Introspect() and what it actually sends, as is a very low-level library that offers no facilities for managing objects or generating correct introspection data. (It requires the service to have its own implementation of Introspect() which often returns a hand-written XML string – such as this example – which may or may not be 100% accurate to what other parts of the code actually send or accept.) This is always a bug in the service, but it means that you should always double-check with bus monitor tools.