]> git.sesse.net Git - vlc/blob - extras/misc/mpris.py
mpris.py: convert from glade to gtk builder
[vlc] / extras / misc / mpris.py
1 #!/usr/bin/env python
2 # -*- coding: utf8 -*-
3 #
4 # Copyright © 2006-2007 Rafaël Carré <funman at videolanorg>
5 #
6 # $Id$
7
8 #  This program is free software; you can redistribute it and/or modify
9 #  it under the terms of the GNU General Public License as published by
10 #  the Free Software Foundation; either version 2 of the License, or
11 #  (at your option) any later version.
12
13 #  This program is distributed in the hope that it will be useful,
14 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #  GNU General Public License for more details.
17
18 #  You should have received a copy of the GNU General Public License
19 #  along with this program; if not, write to the Free Software
20 #  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 #
22
23 #
24 # NOTE: This controller is a SAMPLE, and thus doesn't use all the
25 # Media Player Remote Interface Specification (MPRIS for short) capabilities
26 #
27 # MPRIS:  http://wiki.xmms2.xmms.se/index.php/Media_Player_Interfaces
28 #
29 # You'll need pygtk >= 2.10 to use gtk.StatusIcon
30 #
31 # TODO
32 #   Ability to choose the Media Player if several are connected to the bus
33
34 # core dbus stuff
35 import dbus
36 import dbus.glib
37
38 # core interface stuff
39 import gtk
40
41 # timer
42 import gobject
43
44 # file loading
45 import os
46
47 global win_position # store the window position on the screen
48
49 global playing
50 playing = False
51
52 global shuffle      # playlist will play randomly
53 global repeat       # repeat the playlist
54 global loop         # loop the current element
55
56 # mpris doesn't support getting the status of these (at the moment)
57 shuffle = False
58 repeat = False
59 loop = False
60
61 # these are defined on the mpris detected unique name
62 global root         # / org.freedesktop.MediaPlayer
63 global player       # /Player org.freedesktop.MediaPlayer
64 global tracklist    # /Tracklist org.freedesktop.MediaPlayer
65
66 global bus          # Connection to the session bus
67 global identity     # MediaPlayer Identity
68
69
70 # If a Media Player connects to the bus, we'll use it
71 # Note that we forget the previous Media Player we were connected to
72 def NameOwnerChanged(name, new, old):
73     if old != "" and "org.mpris." in name:
74         Connect(name)
75
76 # Callback for when "TrackChange" signal is emitted
77 def TrackChange(Track):
78     # the only mandatory metadata is "location"
79     try:
80         a = Track["artist"]
81     except:
82         a = ""
83     try:
84         t = Track["title"]
85     except:
86         t = Track["location"]
87     try:
88         length = Track["length"]
89     except:
90         length = 0
91     if length > 0:
92         time_s.set_range(0,Track["length"])
93         time_s.set_sensitive(True)
94     else:
95         # disable the position scale if length isn't available
96         time_s.set_sensitive(False)
97     # update the labels
98     l_artist.set_text(a)
99     l_title.set_text(t)
100
101 # Connects to the Media Player we detected
102 def Connect(name):
103     global root, player, tracklist
104     global playing, identity
105
106     # first we connect to the objects
107     root_o = bus.get_object(name, "/")
108     player_o = bus.get_object(name, "/Player")
109     tracklist_o = bus.get_object(name, "/TrackList")
110
111     # there is only 1 interface per object
112     root = dbus.Interface(root_o, "org.freedesktop.MediaPlayer")
113     tracklist  = dbus.Interface(tracklist_o, "org.freedesktop.MediaPlayer")
114     player = dbus.Interface(player_o, "org.freedesktop.MediaPlayer")
115
116     # connect to the TrackChange signal
117     player_o.connect_to_signal("TrackChange", TrackChange, dbus_interface="org.freedesktop.MediaPlayer")
118
119     # determine if the Media Player is playing something
120     if player.GetStatus() == 0:
121         playing = True
122         TrackChange(player.GetMetadata())
123
124     # gets its identity (name and version)
125     identity = root.Identity()
126     window.set_title(identity)
127
128 #plays an element
129 def AddTrack(widget):
130     mrl = e_mrl.get_text()
131     if mrl != None and mrl != "":
132         tracklist.AddTrack(mrl, True)
133         e_mrl.set_text('')
134     else:
135         mrl = bt_file.get_filename()
136         if mrl != None and mrl != "":
137             tracklist.AddTrack("directory://" + mrl, True)
138     update(0)
139
140 # basic control
141
142 def Next(widget):
143     player.Next(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
144     update(0)
145
146 def Prev(widget):
147     player.Prev(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
148     update(0)
149
150 def Stop(widget):
151     player.Stop(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
152     update(0)
153
154 def Quit(widget):
155     root.Quit(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
156     l_title.set_text("")
157
158 def Pause(widget):
159     player.Pause()
160     status = player.GetStatus()
161     if status == 0:
162         img_bt_toggle.set_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_SMALL_TOOLBAR)
163     else:
164         img_bt_toggle.set_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_SMALL_TOOLBAR)
165     update(0)
166
167 def Repeat(widget):
168     global repeat
169     repeat = not repeat
170     player.Repeat(repeat)
171
172 def Shuffle(widget):
173     global shuffle
174     shuffle = not shuffle
175     tracklist.SetRandom(shuffle)
176
177 def Loop(widget):
178     global loop
179     loop = not loop
180     tracklist.SetLoop(loop)
181
182 # update status display
183 def update(widget):
184     Track = player.GetMetadata()
185     vol.set_value(player.VolumeGet())
186     try: 
187         a = Track["artist"]
188     except:
189         a = ""
190     try:
191         t = Track["title"]
192     except:        
193         t = ""
194     if t == "":
195         try:
196             t = Track["location"]
197         except:
198             t = ""
199     l_artist.set_text(a)
200     l_title.set_text(t)
201     try:
202         length = Track["length"]
203     except:
204         length = 0
205     if length > 0:
206         time_s.set_range(0,Track["length"])
207         time_s.set_sensitive(True)
208     else:
209         # disable the position scale if length isn't available
210         time_s.set_sensitive(False)
211     GetPlayStatus(0)
212
213 # callback for volume change
214 def volchange(widget):
215     player.VolumeSet(vol.get_value_as_int(), reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
216
217 # callback for position change
218 def timechange(widget, x=None, y=None):
219     player.PositionSet(int(time_s.get_value()), reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
220
221 # refresh position change
222 def timeset():
223     global playing
224     if playing == True:
225         try:
226             time_s.set_value(player.PositionGet())
227         except:
228             playing = False
229     return True
230
231 # toggle simple/full display
232 def expander(widget):
233     if exp.get_expanded() == False:
234         exp.set_label("Less")
235     else:
236         exp.set_label("More")
237
238 # close event : hide in the systray
239 def delete_event(self, widget):
240     self.hide()
241     return True
242
243 # shouldn't happen
244 def destroy(widget):
245     gtk.main_quit()
246
247 # hide the controller when 'Esc' is pressed
248 def key_release(widget, event):
249     if event.keyval == gtk.keysyms.Escape:
250         global win_position
251         win_position = window.get_position()
252         widget.hide()
253
254 # callback for click on the tray icon
255 def tray_button(widget):
256     global win_position
257     if window.get_property('visible'):
258         # store position
259         win_position = window.get_position()
260         window.hide()
261     else:
262         # restore position
263         window.move(win_position[0], win_position[1])
264         window.show()
265
266 # hack: update position, volume, and metadata
267 def icon_clicked(widget, event):
268     update(0)
269
270 # get playing status, modify the Play/Pause button accordingly
271 def GetPlayStatus(widget):
272     global playing
273     global shuffle
274     global loop
275     global repeat
276     status = player.GetStatus()
277
278     playing = status[0] == 0
279     if playing:
280         img_bt_toggle.set_from_stock("gtk-media-pause", gtk.ICON_SIZE_SMALL_TOOLBAR)
281     else:
282         img_bt_toggle.set_from_stock("gtk-media-play", gtk.ICON_SIZE_SMALL_TOOLBAR)
283     shuffle = status[1] == 1
284     bt_shuffle.set_active( shuffle )
285     loop = status[2] == 1
286     bt_loop.set_active( loop )
287     repeat = status[3] == 1
288     bt_repeat.set_active( repeat )
289 # loads UI file from the directory where the script is,
290 # so we can use /path/to/mpris.py to execute it.
291 import sys
292 xml = gtk.Builder()
293 gtk.Builder.add_from_file(xml, os.path.join(os.path.dirname(sys.argv[0]) , 'mpris.xml'))
294
295 # ui setup
296 bt_close    = xml.get_object('close')
297 bt_quit     = xml.get_object('quit')
298 bt_file     = xml.get_object('ChooseFile')
299 bt_next     = xml.get_object('next')
300 bt_prev     = xml.get_object('prev')
301 bt_stop     = xml.get_object('stop')
302 bt_toggle   = xml.get_object('toggle')
303 bt_mrl      = xml.get_object('AddMRL')
304 bt_shuffle  = xml.get_object('shuffle')
305 bt_repeat   = xml.get_object('repeat')
306 bt_loop     = xml.get_object('loop')
307 l_artist    = xml.get_object('l_artist')
308 l_title     = xml.get_object('l_title')
309 e_mrl       = xml.get_object('mrl')
310 window      = xml.get_object('window1')
311 img_bt_toggle=xml.get_object('image6')
312 exp         = xml.get_object('expander2')
313 expvbox     = xml.get_object('expandvbox')
314 audioicon   = xml.get_object('eventicon')
315 vol         = xml.get_object('vol')
316 time_s      = xml.get_object('time_s')
317 time_l      = xml.get_object('time_l')
318
319 # connect to the different callbacks
320
321 window.connect('delete_event',  delete_event)
322 window.connect('destroy',       destroy)
323 window.connect('key_release_event', key_release)
324
325 tray = gtk.status_icon_new_from_icon_name("audio-x-generic")
326 tray.connect('activate', tray_button)
327
328 bt_close.connect('clicked',     destroy)
329 bt_quit.connect('clicked',      Quit)
330 bt_mrl.connect('clicked',       AddTrack)
331 bt_toggle.connect('clicked',    Pause)
332 bt_next.connect('clicked',      Next)
333 bt_prev.connect('clicked',      Prev)
334 bt_stop.connect('clicked',      Stop)
335 bt_loop.connect('clicked',      Loop)
336 bt_repeat.connect('clicked',    Repeat)
337 bt_shuffle.connect('clicked',   Shuffle)
338 exp.connect('activate',         expander)
339 vol.connect('changed',          volchange)
340 time_s.connect('adjust-bounds', timechange)
341 audioicon.set_events(gtk.gdk.BUTTON_PRESS_MASK) # hack for the bottom right icon
342 audioicon.connect('button_press_event', icon_clicked) 
343 time_s.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
344
345 library = "/media/mp3" # editme
346
347 # set the Directory chooser to a default location
348 try:
349     os.chdir(library)
350     bt_file.set_current_folder(library)
351 except:
352     bt_file.set_current_folder(os.path.expanduser("~"))
353
354 # connect to the bus
355 bus = dbus.SessionBus()
356 dbus_names = bus.get_object( "org.freedesktop.DBus", "/org/freedesktop/DBus" )
357 dbus_names.connect_to_signal("NameOwnerChanged", NameOwnerChanged, dbus_interface="org.freedesktop.DBus") # to detect new Media Players
358
359 dbus_o = bus.get_object("org.freedesktop.DBus", "/")
360 dbus_intf = dbus.Interface(dbus_o, "org.freedesktop.DBus")
361 name_list = dbus_intf.ListNames()
362
363 # connect to the first Media Player found
364 for n in name_list:
365     if "org.mpris." in n:
366         Connect(n)
367         window.set_title(identity)
368         vol.set_value(player.VolumeGet())
369         update(0)
370         break
371
372 # run a timer to update position
373 gobject.timeout_add( 1000, timeset)
374
375 window.set_icon_name('audio-x-generic')
376 window.show()
377
378 icon_theme = gtk.icon_theme_get_default()
379 try:
380     pix = icon_theme.load_icon("audio-x-generic",24,0)
381     window.set_icon(pix)
382 except:
383     True
384
385 win_position = window.get_position()
386
387 gtk.main() # execute the main loop