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
28
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
|
#!/usr/bin/env python3
import dbus
import signal
import time
from unicodedata import east_asian_width
# Config options
# (int) : Length of media info string. If length of string exceedes this value, the text will scroll. Default value is 20
message_display_len = 20
# (int) : Font index of polybar. this value should be 1 more than the font value specified in polybar config.
font_index = 1
# (float) : Update speed of the text in seconds.
update_delay = 0.3
# (list) : list of chars containing previous, play, pause, next glyphs for media controls in respective order
control_chars = ['','','','']
# (dict) : dict of char icons to display as prefix.
# If player name is available as key, then use the corressponding icon,
# else default key value.
# example:
display_player_prefix = {
"spotify": '',
"firefox": '',
"default": ''
}
# (list) : list of metadata fields based on mpris sepecification.
# For more details/ field names, refer [mpris sepecification](https://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata/)
metadata_fields = ["xesam:title", "xesam:artist"]
# (char) : separator for metadata fields
metadata_separator = "-"
# (bool) : Hide text when no player is available? True disables the output for no players.
hide_output = False
# Defult initialization
current_player = None
player_names = None
players = None
message = None
display_text = ""
display_prefix = ""
display_suffix = ""
last_player_name = None
session_bus = dbus.SessionBus()
def get_name(player_name ):
if player_name not in player_names:
return
name = ".".join(player_name.split(".")[3:])
return name
def get_name_by_index(index):
if index >= len(player_names):
return
return get_name(player_names[index])
def get_status(player):
status = ""
try:
status = player.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus', dbus_interface='org.freedesktop.DBus.Properties')
except Exception as e:
pass
return status
def get_metadata(player):
metadata = {}
try:
metadata = player.Get('org.mpris.MediaPlayer2.Player', 'Metadata', dbus_interface='org.freedesktop.DBus.Properties')
except Exception as e:
pass
return metadata
def update_prefix_suffix(player_name="", status=""):
global display_prefix, display_suffix, status_paused
player_option = ""
if player_name != "":
player_option = "-p " + player_name
prev_button = "%%{A:playerctl %s previous :}%c%%{A}" %(player_option,control_chars[0])
play_button = "%%{A:playerctl %s play :}%c%%{A}" %(player_option,control_chars[1])
pause_button = "%%{A:playerctl %s pause :}%c%%{A}" %(player_option,control_chars[2])
next_button = "%%{A:playerctl %s next :}%c%%{A}" %(player_option,control_chars[3])
suffix = "| " + prev_button
if status == "Playing":
suffix += " "+pause_button
status_paused = False
else:
suffix += " "+play_button
status_paused = True
suffix += " "+next_button
# print(suffix)
display_suffix = suffix
for key in display_player_prefix.keys():
if key in player_name:
display_prefix = display_player_prefix[key]
break
else:
display_prefix = display_player_prefix["default"]
def update_players():
global player_names, players, session_bus, current_player, last_player_name
player_names = [service for service in session_bus.list_names() if service.startswith('org.mpris.MediaPlayer2.')]
players = [session_bus.get_object(service, '/org/mpris/MediaPlayer2') for service in player_names]
if last_player_name != get_name(current_player):
for index, player in enumerate(player_names):
if get_name(player) == last_player_name:
current_player = index
def handle_event(*args):
global current_player, players, last_player_name
update_players()
if len(players) == 0:
return
current_player += 1
current_player %= len(players)
last_player_name = get_name_by_index(current_player)
# print("SIGUSR1: updated values - current_player = %d players len = %d"%(current_player,len(players)))
def update_message():
global players, current_player,player_names, message, display_text, message_display_len, display_suffix, last_player_name
if len(players) == 0:
tmp_message = "No player available"
update_prefix_suffix()
else:
name = get_name_by_index(current_player)
status = get_status(players[current_player])
metadata_obj = get_metadata(players[current_player])
metadata_string_list = []
for field in metadata_fields:
result = metadata_obj.get(field)
if type(result) == dbus.Array:
result = result[0]
if not result:
result = "No "+field.split(":")[1]
metadata_string_list.append(str(result))
metadata_string = (" "+metadata_separator+" ").join(metadata_string_list)
if visual_len(metadata_string) > message_display_len:
metadata_string = " " + metadata_string + " |"
update_prefix_suffix(name,status)
tmp_message = ""
if metadata_string:
tmp_message += str(metadata_string)
last_player_name = name
if message != tmp_message:
message = tmp_message
display_text = message
def scroll():
global display_text, message_display_len, status_paused
if not status_paused:
if visual_len(display_text) > message_display_len:
display_text = display_text[1:] + display_text[0]
elif visual_len(display_text) < message_display_len:
display_text += " "*(message_display_len - len(display_text))
def visual_len(text):
visual_length = 0
for ch in text:
width = east_asian_width(ch)
if width == 'W' or width == 'F':
visual_length += 2
visual_length += 1
return visual_length
def make_visual_len(text, visual_desired_length):
visual_length = 0
altered_text = ''
for char in text:
if visual_length < visual_desired_length:
width = east_asian_width(char)
if width == 'W' or width == 'F':
visual_length += 2
else:
visual_length += 1
altered_text += char
else:
break
if visual_length == visual_desired_length + 1:
altered_text = altered_text[:-1] + ' '
elif visual_length < visual_desired_length:
altered_text += ' ' * (visual_desired_length - visual_length)
return altered_text
def print_text():
global display_text, message_display_len, players, player_names, display_prefix, display_suffix
if hide_output and len(players)==0:
print("", flush = True)
return
scroll()
print(display_prefix + " " +
"%%{T%d}" % (font_index) +
make_visual_len(display_text, message_display_len) +
"%{T-}" + display_suffix, flush=True)
def main():
global current_player, players
update_players()
current_player = 0
while True:
time.sleep(update_delay)
update_players()
update_message()
print_text()
if __name__ == '__main__':
signal.signal(signal.SIGUSR1, handle_event)
main()
|