aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xmpris_to_text.py220
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
3import sys 3import sys
4import re 4import re
@@ -17,29 +17,31 @@ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
17 17
18 18
19class MetaWriter: 19class 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
52class PlayerSelector(threading.Thread): 54class 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
155class Menu(threading.Thread): 166class 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
203class Input(threading.Thread): 218class 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
239def create_writer(): 254def 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
283term = Terminal() 299term = Terminal()
284meta_writer = create_writer() 300meta_writer = create_writer()
285 301
286signal.signal(signal.SIGWINCH, on_resize) 302signal.signal(signal.SIGWINCH, on_resize)