diff --git a/.config/xfce4/panel/scripts/mpris-status.py b/.config/xfce4/panel/scripts/mpris-status.py new file mode 100755 index 0000000..67d0f02 --- /dev/null +++ b/.config/xfce4/panel/scripts/mpris-status.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 + +from textwrap import shorten +from typing import Optional + +from dbus.exceptions import DBusException +from mpris2 import Player, get_players_uri +from mpris2.types import Metadata_Map +from mpris2.utils import get_player_id_from_uri + +MAX_OUTPUT = 80 + + +class MagicMetadata: + _metadata: Metadata_Map + + def __init__(self, metadata: Metadata_Map): + self._metadata = metadata + + def __getattr__(self, attr: str): + """lookup attributes via pre-defined constants in Metadata_Map""" + return self._metadata.get(getattr(self._metadata, attr.upper())) + + +def format_duration(duration: Optional[int]): + if duration is None: + return "?" + + hours, rem = divmod(duration // 1000 // 1000, 3600) + minutes, seconds = divmod(rem, 60) + if hours: + return f"{hours:02d}:{minutes:02d}:{seconds:02d}" + else: + return f"{minutes:02d}:{seconds:02d}" + + +def format_player(player: Player, short: bool = False) -> str: + metadata = MagicMetadata(player.Metadata) + try: + position = player.Position.real + except DBusException: + position = None + + if position is None and metadata.length is None: + position_and_length = "" + else: + position_and_length = f" {format_duration(position) }/{ format_duration(metadata.length) }" + + if short: + status = "" + else: + status = { + "Playing": "▶", + "Paused": "⏸️", + "Stopped": "⏹️", + }.get(player.PlaybackStatus, "❓") + " " + + artist = ",".join(metadata.artist) + album = metadata.album + title = metadata.title + if short: + # TODO: could be a bit more clever here to use the whole space + artist = shorten(artist, 40, placeholder="…") + album = shorten(album, 40, placeholder="…") + title = shorten(title, 60, placeholder="…") + + if album: + album = f" / { album }" + + return f"{status}{ artist }{ album } | { title }{ position_and_length }" + + +def status_key(player: Player) -> int: + return { + "Playing": 0, + "Paused": 1, + "Stopped": 2, + }.get(player.PlaybackStatus, 100) + + +def status_icon(player: Player) -> str: + return { + "Playing": "exaile-play", + "Paused": "exaile-pause", + "Stopped": "media-player-banshee-stopped", + }.get(player.PlaybackStatus, "unknown") + + +players = [Player(dbus_interface_info={"dbus_uri": uri}) for uri in get_players_uri()] + +players.sort(key=status_key) +active_player_id = get_player_id_from_uri(players[0]._dbus_interface_info.uri) +print( + "" + + (f"[{ len(players) }] " if len(players) > 1 else "") + + format_player(players[0], short=True) + + "" +) +print(f"playerctl --player={ active_player_id } play-pause") +print(f"{ status_icon(players[0]) }") +print("" + "\n".join(format_player(player) for player in players) + "")