1"""
2Display songs currently playing in DeaDBeeF.
3
4Configuration parameters:
5    cache_timeout: refresh interval for this module (default 5)
6    format: display format for this module (default '[{artist} - ][{title}]')
7    sleep_timeout: when deadbeef is not running, this interval will be used
8        to allow faster refreshes with time-related placeholders and/or
9        to refresh few times per minute rather than every few seconds
10        (default 20)
11
12Format placeholders:
13    {album} name of the album
14    {artist} name of the artist
15    {length} length time in [HH:]MM:SS
16    {playback_time} elapsed time in [HH:]MM:SS
17    {title} title of the track
18    {tracknumber} track number in two digits
19    {year} year in four digits
20
21    For more placeholders, see title formatting 2.0 in 'deadbeef --help'
22    or https://github.com/DeaDBeeF-Player/deadbeef/wiki/Title-formatting-2.0
23    Not all of Foobar2000 remapped metadata fields will work with deadbeef and
24    a quick reminder about using {placeholders} here instead of %placeholder%.
25
26Color options:
27    color_paused: Paused, defaults to color_degraded
28    color_playing: Playing, defaults to color_good
29    color_stopped: Stopped, defaults to color_bad
30
31Requires:
32    deadbeef: a GTK+ audio player for GNU/Linux
33
34Examples:
35```
36# see 'deadbeef --help' for more buttons
37deadbeef {
38    on_click 1 = 'exec deadbeef --play-pause'
39    on_click 8 = 'exec deadbeef --random'
40}
41```
42
43@author mrt-prodz
44
45SAMPLE OUTPUT
46{'color': '#00ff00', 'full_text': 'Music For Programming - Lackluster'}
47
48paused
49{'color': '#ffff00', 'full_text': 'Music For Programming - Lackluster'}
50"""
51
52STRING_NOT_INSTALLED = "not installed"
53
54
55class Py3status:
56    """
57    """
58
59    # available configuration parameters
60    cache_timeout = 5
61    format = "[{artist} - ][{title}]"
62    sleep_timeout = 20
63
64    class Meta:
65        deprecated = {
66            "remove": [{"param": "delimiter", "msg": "obsolete parameter"}],
67            "rename_placeholder": [
68                {
69                    "placeholder": "elapsed",
70                    "new": "playback_time",
71                    "format_strings": ["format"],
72                },
73                {
74                    "placeholder": "tracknum",
75                    "new": "tracknumber",
76                    "format_strings": ["format"],
77                },
78            ],
79        }
80
81    def post_config_hook(self):
82        if not self.py3.check_commands("deadbeef"):
83            raise Exception(STRING_NOT_INSTALLED)
84
85        self.separator = "|SEPARATOR|"
86        self.placeholders = list(
87            set(self.py3.get_placeholders_list(self.format) + ["isplaying"])
88        )
89        self.deadbeef_command = 'deadbeef --nowplaying-tf "{}"'.format(
90            self.separator.join(f"%{x}%" for x in self.placeholders)
91        )
92        self.color_paused = self.py3.COLOR_PAUSED or self.py3.COLOR_DEGRADED
93        self.color_playing = self.py3.COLOR_PLAYING or self.py3.COLOR_GOOD
94        self.color_stopped = self.py3.COLOR_STOPPED or self.py3.COLOR_BAD
95
96    def _is_running(self):
97        try:
98            self.py3.command_output(["pgrep", "deadbeef"])
99            return True
100        except self.py3.CommandError:
101            return False
102
103    def _get_deadbeef_data(self):
104        # Deadbeef can generate lot of startup noises with or without error
105        # codes. Running command sometimes change how things behaves onscreen
106        # too. We used subprocess in the past to ignore error codes. We also
107        # use pgrep and hidden placeholders to dictate how status output and
108        # color should look... mainly to stay consistency in multiple versions
109        # (e.g., Python2.7 to Python3+ and nonstop deadbeef-git commits).
110        try:
111            return self.py3.command_output(self.deadbeef_command)
112        except self.py3.CommandError as ce:
113            return ce.output
114
115    def deadbeef(self):
116        beef_data = {}
117        cached_until = self.sleep_timeout
118        color = self.color_stopped
119
120        if self._is_running():
121            line = self._get_deadbeef_data()
122            beef_data = dict(zip(self.placeholders, line.split(self.separator)))
123            cached_until = self.cache_timeout
124
125            if beef_data["isplaying"]:
126                color = self.color_playing
127            else:
128                color = self.color_paused
129
130        return {
131            "cached_until": self.py3.time_in(cached_until),
132            "full_text": self.py3.safe_format(self.format, beef_data),
133            "color": color,
134        }
135
136
137if __name__ == "__main__":
138    """
139    Run module in test mode.
140    """
141    from py3status.module_test import module_test
142
143    module_test(Py3status)
144