diff options
-rwxr-xr-x | mpris_to_text.py | 220 |
1 files changed, 118 insertions, 102 deletions
diff --git a/mpris_to_text.py b/mpris_to_text.py index 24baabf..c5d65d8 100755 --- a/mpris_to_text.py +++ b/mpris_to_text.py | |||
@@ -1,4 +1,4 @@ | |||
1 | #!/usr/bin/python | 1 | #!/usr/bin/env python |
2 | 2 | ||
3 | import sys | 3 | import sys |
4 | import re | 4 | import re |
@@ -17,29 +17,31 @@ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) | |||
17 | 17 | ||
18 | 18 | ||
19 | class MetaWriter: | 19 | class MetaWriter: |
20 | filename = "" | 20 | filename = "" |
21 | output_format = "" | 21 | output_format = "" |
22 | output_format_artist = "" | 22 | output_format_artist = "" |
23 | output_format_title = "" | 23 | output_format_title = "" |
24 | output_format_album = "" | 24 | output_format_album = "" |
25 | last_output = "" | 25 | last_output = "" |
26 | 26 | ||
27 | def __init__(self, filename, output_format, output_format_artist, output_format_title, output_format_album): | 27 | def __init__(self, filename, output_format, output_format_artist, output_format_title, output_format_album): |
28 | self.filename = filename | 28 | self.filename = filename |
29 | self.output_format = output_format | 29 | self.output_format = output_format |
30 | self.output_format_album = output_format_album | 30 | self.output_format_album = output_format_album |
31 | self.output_format_artist = output_format_artist | 31 | self.output_format_artist = output_format_artist |
32 | self.output_format_title = output_format_title | 32 | self.output_format_title = output_format_title |
33 | 33 | ||
34 | def write_meta(self, metadata): | 34 | def write_meta(self, metadata): |
35 | artist = metadata["xesam:artist"][0] if "xesam:artist" in metadata else "" | 35 | artist = metadata["xesam:artist"][0] if "xesam:artist" in metadata else "" |
36 | title = metadata["xesam:title"] if "xesam:title" in metadata else "" | 36 | title = metadata["xesam:title"] if "xesam:title" in metadata else "" |
37 | album = metadata["xesam:album"] if "xesam:album" in metadata else "" | 37 | album = metadata["xesam:album"] if "xesam:album" in metadata else "" |
38 | 38 | ||
39 | self.write(self.output_format.format( | 39 | self.write(self.output_format.format( |
40 | artist = self.output_format_artist.format(artist) if artist != "" else "", | 40 | artist=self.output_format_artist.format( |
41 | title = self.output_format_title.format(title) if title != "" else "", | 41 | artist) if artist != "" else "", |
42 | album = self.output_format_album.format(album) if album != "" else "" | 42 | title=self.output_format_title.format( |
43 | title) if title != "" else "", | ||
44 | album=self.output_format_album.format(album) if album != "" else "" | ||
43 | )) | 45 | )) |
44 | 46 | ||
45 | def write(self, text): | 47 | def write(self, text): |
@@ -50,43 +52,48 @@ class MetaWriter: | |||
50 | 52 | ||
51 | 53 | ||
52 | class PlayerSelector(threading.Thread): | 54 | class PlayerSelector(threading.Thread): |
53 | service_regex = re.compile("^org\.mpris\.MediaPlayer2\.") | 55 | service_regex = re.compile("^org\.mpris\.MediaPlayer2\.") |
54 | bus = None | 56 | bus = None |
55 | loop = None | 57 | loop = None |
56 | signal_receiver = None | 58 | signal_receiver = None |
57 | players = {} | 59 | players = {} |
58 | players_indexes = [] | 60 | players_indexes = [] |
59 | active_player = "" | 61 | active_player = "" |
60 | menu = None | 62 | menu = None |
61 | meta_writer = None | 63 | meta_writer = None |
62 | 64 | ||
63 | def __init__(self, meta_writer, menu=None): | 65 | def __init__(self, meta_writer, menu=None): |
64 | super().__init__() | 66 | super().__init__() |
65 | 67 | ||
66 | self.bus = dbus.SessionBus() | 68 | self.bus = dbus.SessionBus() |
67 | self.meta_writer = meta_writer | 69 | self.meta_writer = meta_writer |
68 | self.menu = menu | 70 | self.menu = menu |
69 | 71 | ||
70 | def set_menu(self, menu): | 72 | def set_menu(self, menu): |
71 | self.menu = menu | 73 | self.menu = menu |
72 | 74 | ||
73 | def get_players(self): | 75 | def get_players(self): |
74 | self.players = {} | 76 | self.players = {} |
75 | self.players_indexes = [] | 77 | self.players_indexes = [] |
76 | 78 | ||
77 | bus_path = self.bus.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus") | 79 | bus_path = self.bus.get_object( |
80 | "org.freedesktop.DBus", "/org/freedesktop/DBus") | ||
78 | bus_proxy = dbus.Interface(bus_path, "org.freedesktop.DBus") | 81 | bus_proxy = dbus.Interface(bus_path, "org.freedesktop.DBus") |
79 | 82 | ||
80 | for service in bus_proxy.ListNames(): | 83 | for service in bus_proxy.ListNames(): |
81 | if self.service_regex.match(service): | 84 | if self.service_regex.match(service): |
82 | split_service = service.split(".") | 85 | split_service = service.split(".") |
83 | name_start_index = len(split_service[0]) + len(split_service[1]) + len(split_service[2]) + 3 | 86 | name_start_index = len( |
84 | 87 | split_service[0]) + len(split_service[1]) + len(split_service[2]) + 3 | |
85 | player_path = self.bus.get_object(service, "/org/mpris/MediaPlayer2") | 88 | |
86 | player_props = dbus.Interface(player_path, "org.freedesktop.DBus.Properties") | 89 | player_path = self.bus.get_object( |
87 | player_name = service[name_start_index:] | 90 | service, "/org/mpris/MediaPlayer2") |
91 | player_props = dbus.Interface( | ||
92 | player_path, "org.freedesktop.DBus.Properties") | ||
93 | player_name = service[name_start_index:] | ||
88 | try: | 94 | try: |
89 | player_name = player_props.Get("org.mpris.MediaPlayer2.Player", "Identity") | 95 | player_name = player_props.Get( |
96 | "org.mpris.MediaPlayer2.Player", "Identity") | ||
90 | except dbus.exceptions.DBusException: | 97 | except dbus.exceptions.DBusException: |
91 | pass | 98 | pass |
92 | 99 | ||
@@ -112,35 +119,39 @@ class PlayerSelector(threading.Thread): | |||
112 | 119 | ||
113 | if self.active_player != "": | 120 | if self.active_player != "": |
114 | self.signal_receiver = self.bus.add_signal_receiver( | 121 | self.signal_receiver = self.bus.add_signal_receiver( |
115 | handler_function = self.playing_song_changed, | 122 | handler_function=self.playing_song_changed, |
116 | bus_name = self.active_player, | 123 | bus_name=self.active_player, |
117 | dbus_interface = "org.freedesktop.DBus.Properties", | 124 | dbus_interface="org.freedesktop.DBus.Properties", |
118 | signal_name = "PropertiesChanged", | 125 | signal_name="PropertiesChanged", |
119 | path = "/org/mpris/MediaPlayer2" | 126 | path="/org/mpris/MediaPlayer2" |
120 | ) | 127 | ) |
121 | 128 | ||
122 | player_path = self.bus.get_object(self.active_player, "/org/mpris/MediaPlayer2") | 129 | player_path = self.bus.get_object( |
123 | player_props = dbus.Interface(player_path, "org.freedesktop.DBus.Properties") | 130 | self.active_player, "/org/mpris/MediaPlayer2") |
131 | player_props = dbus.Interface( | ||
132 | player_path, "org.freedesktop.DBus.Properties") | ||
124 | 133 | ||
125 | self.meta_writer.write_meta(player_props.Get("org.mpris.MediaPlayer2.Player", "Metadata")) | 134 | self.meta_writer.write_meta(player_props.Get( |
135 | "org.mpris.MediaPlayer2.Player", "Metadata")) | ||
126 | else: | 136 | else: |
127 | self.meta_writer.write("") | 137 | self.meta_writer.write("") |
128 | 138 | ||
129 | |||
130 | def run(self): | 139 | def run(self): |
131 | bus_path = self.bus.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus") | 140 | bus_path = self.bus.get_object( |
141 | "org.freedesktop.DBus", "/org/freedesktop/DBus") | ||
132 | bus_proxy = dbus.Interface(bus_path, "org.freedesktop.DBus") | 142 | bus_proxy = dbus.Interface(bus_path, "org.freedesktop.DBus") |
133 | bus_proxy.connect_to_signal("NameOwnerChanged", self.dbus_name_owner_changed) | 143 | bus_proxy.connect_to_signal( |
144 | "NameOwnerChanged", self.dbus_name_owner_changed) | ||
134 | 145 | ||
135 | self.get_players() | 146 | self.get_players() |
136 | self.menu.refresh() | 147 | self.menu.refresh() |
137 | 148 | ||
138 | self.loop = GLib.MainLoop() | 149 | self.loop = GLib.MainLoop() |
139 | self.loop.run() | 150 | self.loop.run() |
140 | 151 | ||
141 | def quit(self): | 152 | def quit(self): |
142 | self.loop.quit() | 153 | self.loop.quit() |
143 | 154 | ||
144 | def dbus_name_owner_changed(self, name, old_owner, new_owner): | 155 | def dbus_name_owner_changed(self, name, old_owner, new_owner): |
145 | if self.service_regex.match(name): | 156 | if self.service_regex.match(name): |
146 | self.get_players() | 157 | self.get_players() |
@@ -153,36 +164,38 @@ class PlayerSelector(threading.Thread): | |||
153 | 164 | ||
154 | 165 | ||
155 | class Menu(threading.Thread): | 166 | class Menu(threading.Thread): |
156 | refresh_cond = threading.Condition() | 167 | refresh_cond = threading.Condition() |
157 | refresh_flag = True | 168 | refresh_flag = True |
158 | exit_flag = False | 169 | exit_flag = False |
159 | player_selector = None | 170 | player_selector = None |
160 | meta_writer = None | 171 | meta_writer = None |
161 | 172 | ||
162 | def __init__(self, term, player_selector, meta_writer): | 173 | def __init__(self, term, player_selector, meta_writer): |
163 | super().__init__() | 174 | super().__init__() |
164 | 175 | ||
165 | self.player_selector = player_selector | 176 | self.player_selector = player_selector |
166 | self.meta_writer = meta_writer | 177 | self.meta_writer = meta_writer |
167 | 178 | ||
168 | def refresh(self, exit_flag=False): | 179 | def refresh(self, exit_flag=False): |
169 | with self.refresh_cond: | 180 | with self.refresh_cond: |
170 | self.refresh_flag = True | 181 | self.refresh_flag = True |
171 | self.exit_flag = self.exit_flag or exit_flag | 182 | self.exit_flag = self.exit_flag or exit_flag |
172 | self.refresh_cond.notify() | 183 | self.refresh_cond.notify() |
173 | 184 | ||
174 | def run(self): | 185 | def run(self): |
175 | while not self.exit_flag: | 186 | with term.fullscreen(): |
176 | with self.refresh_cond: | 187 | while not self.exit_flag: |
177 | while not self.refresh_flag and not self.exit_flag: | 188 | with self.refresh_cond: |
178 | self.refresh_cond.wait() | 189 | while not self.refresh_flag and not self.exit_flag: |
190 | self.refresh_cond.wait() | ||
179 | 191 | ||
180 | self.refresh_flag = False | 192 | self.refresh_flag = False |
181 | 193 | ||
182 | with term.fullscreen(): | 194 | print(term.clear) |
183 | print(term.move(0, 0) + term.bold_bright_white_on_bright_black(("{0:<{width}}").format("MPRIS To Text", width=term.width)) + "\n") | 195 | print(term.move_xy(0, 0) + term.bold_bright_white_on_bright_black( |
196 | ("{0:<{width}}").format("MPRIS To Text", width=term.width)) + "\n") | ||
184 | print(term.move_x(2) + term.bold("Player: ") + term.move_up()) | 197 | print(term.move_x(2) + term.bold("Player: ") + term.move_up()) |
185 | 198 | ||
186 | for i, (id, name) in enumerate(self.player_selector.players.items()): | 199 | for i, (id, name) in enumerate(self.player_selector.players.items()): |
187 | output = "%d: %s" % (i, name) | 200 | output = "%d: %s" % (i, name) |
188 | 201 | ||
@@ -191,26 +204,28 @@ class Menu(threading.Thread): | |||
191 | else: | 204 | else: |
192 | print(term.move_x(10) + output) | 205 | print(term.move_x(10) + output) |
193 | 206 | ||
194 | print(term.move_x(2) + term.bold("File: ") + self.meta_writer.filename) | 207 | print(term.move_x(2) + term.bold("File: ") + |
195 | print(term.move_x(2) + term.bold("Output: ") + "\n".join(term.wrap(self.meta_writer.last_output, width=term.width - 10, subsequent_indent=" " * 10))) | 208 | self.meta_writer.filename) |
209 | print(term.move_x(2) + term.bold("Output: ") + "\n".join(term.wrap( | ||
210 | self.meta_writer.last_output, width=term.width - 10, subsequent_indent=" " * 10))) | ||
196 | 211 | ||
197 | print(term.move_x(0) + "\nEnter number to select player or q to exit." + term.move_up()) | 212 | print(term.move_x( |
213 | 0) + "\nEnter number to select player or q to exit." + term.move_up()) | ||
198 | 214 | ||
199 | with term.fullscreen(): | 215 | print(term.move_xy(0, 0) + "Exiting...") |
200 | print(term.move(0, 0) + "Exiting...") | ||
201 | 216 | ||
202 | 217 | ||
203 | class Input(threading.Thread): | 218 | class Input(threading.Thread): |
204 | player_selector = None | 219 | player_selector = None |
205 | meta_writer = None | 220 | meta_writer = None |
206 | menu = None | 221 | menu = None |
207 | 222 | ||
208 | def __init__(self, term, player_selector, meta_writer, menu): | 223 | def __init__(self, term, player_selector, meta_writer, menu): |
209 | super().__init__() | 224 | super().__init__() |
210 | 225 | ||
211 | self.player_selector = player_selector | 226 | self.player_selector = player_selector |
212 | self.meta_writer = meta_writer | 227 | self.meta_writer = meta_writer |
213 | self.menu = menu | 228 | self.menu = menu |
214 | 229 | ||
215 | def run(self): | 230 | def run(self): |
216 | with term.cbreak(): | 231 | with term.cbreak(): |
@@ -237,50 +252,51 @@ def on_resize(*args): | |||
237 | 252 | ||
238 | 253 | ||
239 | def create_writer(): | 254 | def create_writer(): |
240 | parser = argparse.ArgumentParser(description="Write metadata from MPRIS-compliant media players into a text file.") | 255 | parser = argparse.ArgumentParser( |
256 | description="Write metadata from MPRIS-compliant media players into a text file.") | ||
241 | parser.add_argument("--file", | 257 | parser.add_argument("--file", |
242 | type = str, | 258 | type=str, |
243 | dest = "filename", | 259 | dest="filename", |
244 | default = "/tmp/mpris_info.txt", | 260 | default="/tmp/mpris_info.txt", |
245 | help = "Full path to file (default: \"/tmp/mpris_info.txt\")" | 261 | help="Full path to file (default: \"/tmp/mpris_info.txt\")" |
246 | ) | 262 | ) |
247 | parser.add_argument("--format-artist", | 263 | parser.add_argument("--format-artist", |
248 | type = str, | 264 | type=str, |
249 | dest = "format_artist", | 265 | dest="format_artist", |
250 | default = "{} ", | 266 | default=" by {}", |
251 | help = "Format string for the artist part (default: \"{} \")" | 267 | help="Format string for the artist part (default: \" by {}\")" |
252 | ) | 268 | ) |
253 | parser.add_argument("--format-title", | 269 | parser.add_argument("--format-title", |
254 | type = str, | 270 | type=str, |
255 | dest = "format_title", | 271 | dest="format_title", |
256 | default = "\"{}\"", | 272 | default="\"{}\"", |
257 | help = "Format string for the title part (default: \"\"{}\"\")" | 273 | help="Format string for the title part (default: \"\"{}\"\")" |
258 | ) | 274 | ) |
259 | parser.add_argument("--format-album", | 275 | parser.add_argument("--format-album", |
260 | type = str, | 276 | type=str, |
261 | dest = "format_album", | 277 | dest="format_album", |
262 | default = " from \"{}\"", | 278 | default=" from \"{}\"", |
263 | help = "Format string for the album part (default: \" from \"{}\"\")" | 279 | help="Format string for the album part (default: \" from \"{}\"\")" |
264 | ) | 280 | ) |
265 | parser.add_argument("--format", | 281 | parser.add_argument("--format", |
266 | type = str, | 282 | type=str, |
267 | dest = "format", | 283 | dest="format", |
268 | default = "{artist}{title}{album} ", | 284 | default="{title}{album}{artist} ", |
269 | help = "Full format string (default: \"{artist}{title}{album} \")" | 285 | help="Full format string (default: \"{artist}{title}{album} \")" |
270 | ) | 286 | ) |
271 | 287 | ||
272 | args = parser.parse_args() | 288 | args = parser.parse_args() |
273 | 289 | ||
274 | return MetaWriter( | 290 | return MetaWriter( |
275 | filename = args.filename, | 291 | filename=args.filename, |
276 | output_format = args.format, | 292 | output_format=args.format, |
277 | output_format_artist = args.format_artist, | 293 | output_format_artist=args.format_artist, |
278 | output_format_title = args.format_title, | 294 | output_format_title=args.format_title, |
279 | output_format_album = args.format_album | 295 | output_format_album=args.format_album |
280 | ) | 296 | ) |
281 | 297 | ||
282 | 298 | ||
283 | term = Terminal() | 299 | term = Terminal() |
284 | meta_writer = create_writer() | 300 | meta_writer = create_writer() |
285 | 301 | ||
286 | signal.signal(signal.SIGWINCH, on_resize) | 302 | signal.signal(signal.SIGWINCH, on_resize) |