now_playing.py 3.42 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
#!/usr/bin/env python3

"""
A script for Polybar which listens to an MPRIS-compatible media player's
events and outputs the current playback status, i.e., the current track.

This rudimentary quick-and-dirty script should only be temporary since a
native MPRIS/Playerctl module for Polybar is planned for the near future.

Required Python packages:
  - PyGObject (https://pygobject.readthedocs.io/en/latest/)
Required applications:
  - playerctl (https://github.com/acrisci/playerctl)

Author: Benedikt Vollmerhaus
License: MIT
"""

import sys
import time

import gi

gi.require_version('Playerctl', '1.0')
from gi.repository import Playerctl, GLib


28
class NowPlaying:
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
    """
    Retrieve and print the metadata of the currently playing track.
    """

    ARTIST = 'xesam:artist'
    TITLE = 'xesam:title'

    def __init__(self) -> None:
        self.player = None

        self.icon: str = '栗'
        self.status: str = ''
        self.artist: str = ''
        self.title: str = ''

    def listen(self) -> None:
        """
        Start the main event loop once a player has been launched.

        :return: None
        """
        self.wait_for_player()
        main = GLib.MainLoop()
        main.run()

    def wait_for_player(self) -> None:
        """
        Wait for an MPRIS-compatible player and bind its events.

        :return: None
        """
        while True:
            try:
                self.player = Playerctl.Player()
                self.player.on('play', self.on_play)
                self.player.on('pause', self.on_pause)
                self.player.on('metadata', self.on_metadata)
                self.player.on('exit', self.on_exit)
                print('%{T2}栗%{T-}  Stopped', flush=True)
                break

            except GLib.Error:
                print('%{T2}ﱘ%{T-}  No player running', flush=True)
                time.sleep(2)  # Wait before searching again

    def on_play(self, _player) -> None:
        """
        :param _player: The player that started playing
        :return: None
        """
        self.icon = '契'
        self.print_status()

    def on_pause(self, _player) -> None:
        """
        :param _player: The player that got paused
        :return: None
        """
        self.icon = ''
        self.print_status()

    def on_metadata(self, _player, data) -> None:
        """
        :param _player: The player whose metadata to display
        :param data: The currently playing track's metadata
        :return: None
        """
        if self.ARTIST in data.keys() and self.TITLE in data.keys():
            self.artist = data[self.ARTIST][0]
            self.title = data[self.TITLE]
            self.print_status()

    def on_exit(self, _player) -> None:
        """
        :param _player: The player that got closed
        :return: None
        """
        self.wait_for_player()

    def print_status(self) -> None:
        """
        Print the status icon, currently playing track and its artist.

        :return: None
        """
        # Flushing the buffer forces Python to write to stdout. This is
        # required due to the script being continuously tail'ed through
        # Polybar, which would otherwise lead to not getting any output.
        print(f'%{{T2}}{self.icon}%{{T-}}  {self.artist} - {self.title}',
              flush=True)


if __name__ == '__main__':
    try:
        NowPlaying().listen()
    except KeyboardInterrupt:
        print('\rBye!')
        sys.exit(0)