CCCPaste
from PyQt6.QtCore import QObject, QTimer, pyqtSlot as Slot
from PyQt6.QtDBus import QDBusConnection, QDBusInterface, QDBusMessage, QDBusVariant, QDBusReply


class MPRISMonitor(QObject):
    def __init__(self):
        super().__init__()

        self.updateTimer: QTimer = QTimer()
        self.updateTimer.timeout.connect(self.updatePosition)

        self.session = QDBusConnection.sessionBus()
        self.playerList: list[str] = self.getPlayers()

        self.playerInterface: QDBusInterface | None = None
        self.connectedService: str = ""
        if len(self.playerList) > 0:
            self.playerInterface = MPRISMonitor.getPlayerInterface(self.playerList[0])

            self.session.connect(
                self.connectedService,
                "/org/mpris/MediaPlayer2",
                "org.freedesktop.DBus.Properties",
                "PropertiesChanged",
                self.handlePropertiesChanged
            )

        self.metadata: dict = {}
        self.state: str = ""
        self.position: int = 0

        self.updateTimer.start(5000)

    def getPlayers(self) -> list[str]:
        """Return a list of all active MPRIS player service names."""
        reply = self.session.interface().registeredServiceNames()
        mediaPlayers: list[str] = []
        for s in str(reply.value()).split("\', \'"):
            if s.startswith("org.mpris.MediaPlayer2."):
                mediaPlayers.append(s)
        return mediaPlayers

    @staticmethod
    def getPlayerInterface(serviceName: str):
        """Get a DBus interface for a given player."""
        return QDBusInterface(
            serviceName,
            "/org/mpris/MediaPlayer2",
            "org.freedesktop.DBus.Properties",
            QDBusConnection.sessionBus()
        )

    def getMetadata(self):
        msg: QDBusMessage = self.playerInterface.call("Get", "org.mpris.MediaPlayer2.Player", "Metadata")
        if msg.arguments():
            self.metadata = msg.arguments()[0].variant()

        return self.metadata

    def getState(self):
        msg = self.playerInterface.call("Get", "org.mpris.MediaPlayer2.Player", "PlaybackStatus")
        if msg.arguments():
            return msg.arguments()[0].variant()
        return None

    def getPosition(self):
        if self.playerInterface is None: return 0
        msg = self.playerInterface.call("Get", "org.mpris.MediaPlayer2.Player", "Position")
        if type(msg.arguments()[0]) is int:
            return msg.arguments()[0] / 1000000  # µs → s
        print(type(msg.arguments()))
        return 0

    @Slot(QDBusMessage)
    def handlePropertiesChanged(self, msg: QDBusMessage):
        args = msg.arguments()
        if len(args) < 2:
            return
        changed = args[1]  # dict of changed properties
        if not isinstance(changed, dict):
            return

        if "Metadata" in changed:
            cMetadata = changed["Metadata"]
            if cMetadata != self.metadata:
                self.metadata = cMetadata

        if "PlaybackStatus" in changed:
            cState = changed["PlaybackStatus"]
            if cState != self.state:
                print(f"▶️  Playback state: {cState}")
                self.state = cState

        print("Change detected! TBI")

    def updatePosition(self):
        pos = self.getPosition()
        if abs(pos - self.position) > 2:
            print(f"⏱️  Position: {pos:.1f}s")
            self.position = pos