contrib: nfs: update to 4.0.0
[vlc.git] / extras / misc / mpris.py
blobd7063e2578e8ce575d10428aca4c5a1a90b03e9a
1 #!/usr/bin/env python
2 # -*- coding: utf8 -*-
4 # Copyright © 2006-2011 Rafaël Carré <funman at videolanorg>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 # NOTE: This controller is a SAMPLE, and thus doesn't use all the
23 # Media Player Remote Interface Specification (MPRIS for short) capabilities
25 # MPRIS: http://www.mpris.org/2.1/spec/
27 # You'll need pygtk >= 2.12
29 # TODO
30 # Ability to choose the Media Player if several are connected to the bus
32 # core dbus stuff
33 import dbus
34 import dbus.glib
36 # core interface stuff
37 import gtk
39 # timer
40 from gobject import timeout_add
42 # file loading
43 import os
45 global win_position # store the window position on the screen
47 global playing
48 playing = False
50 global shuffle
52 global root
53 global player
54 global tracklist
55 global props
57 global bus # Connection to the session bus
59 mpris='org.mpris.MediaPlayer2'
61 # If a Media Player connects to the bus, we'll use it
62 # Note that we forget the previous Media Player we were connected to
63 def NameOwnerChanged(name, new, old):
64 if old != '' and mpris in name:
65 Connect(name)
67 def PropGet(prop):
68 global props
69 return props.Get(mpris + '.Player', prop)
71 def PropSet(prop, val):
72 global props
73 props.Set(mpris + '.Player', prop, val)
75 # Callback for when 'TrackChange' signal is emitted
76 def TrackChange(Track):
77 try:
78 a = Track['xesam:artist']
79 except:
80 a = ''
81 try:
82 t = Track['xesam:title']
83 except:
84 t = Track['xesam:url']
85 try:
86 length = Track['mpris:length']
87 except:
88 length = 0
89 if length > 0:
90 time_s.set_range(0, length)
91 time_s.set_sensitive(True)
92 else:
93 # disable the position scale if length isn't available
94 time_s.set_sensitive(False)
95 # update the labels
96 l_artist.set_text(a)
97 l_title.set_text(t)
99 # Connects to the Media Player we detected
100 def Connect(name):
101 global root, player, tracklist, props
102 global playing, shuffle
104 root_o = bus.get_object(name, '/org/mpris/MediaPlayer2')
105 root = dbus.Interface(root_o, mpris)
106 tracklist = dbus.Interface(root_o, mpris + '.TrackList')
107 player = dbus.Interface(root_o, mpris + '.Player')
108 props = dbus.Interface(root_o, dbus.PROPERTIES_IFACE)
110 # FIXME : doesn't exist anymore in mpris 2.1
111 # connect to the TrackChange signal
112 # root_o.connect_to_signal('TrackChange', TrackChange, dbus_interface=mpris)
114 # determine if the Media Player is playing something
115 if PropGet('PlaybackStatus') == 'Playing':
116 playing = True
117 TrackChange(PropGet('Metadata'))
119 window.set_title(props.Get(mpris, 'Identity'))
121 #plays an element
122 def AddTrack(widget):
123 mrl = e_mrl.get_text()
124 if mrl != None and mrl != '':
125 tracklist.AddTrack(mrl, '/', True)
126 e_mrl.set_text('')
127 else:
128 mrl = bt_file.get_filename()
129 if mrl != None and mrl != '':
130 tracklist.AddTrack('directory://' + mrl, '/', True)
131 update(0)
133 # basic control
135 def Next(widget):
136 player.Next(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
137 update(0)
139 def Prev(widget):
140 player.Prev(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
141 update(0)
143 def Stop(widget):
144 player.Stop(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
145 update(0)
147 def Quit(widget):
148 global props
149 if props.Get(mpris, 'CanQuit'):
150 root.Quit(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
151 l_title.set_text('')
152 window.set_title('')
154 def Pause(widget):
155 player.PlayPause()
156 if PropGet('PlaybackStatus') == 'Playing':
157 icon = gtk.STOCK_MEDIA_PAUSE
158 else:
159 icon = gtk.STOCK_MEDIA_PLAY
160 img_bt_toggle.set_from_stock(icon, gtk.ICON_SIZE_SMALL_TOOLBAR)
161 update(0)
163 def Shuffle(widget):
164 global shuffle
165 shuffle = not shuffle
166 PropSet('Shuffle', shuffle)
168 # update status display
169 def update(widget):
170 Track = PropGet('Metadata')
171 vol.set_value(PropGet('Volume') * 100.0)
172 TrackChange(Track)
173 GetPlayStatus(0)
175 # callback for volume change
176 def volchange(widget):
177 PropSet('Volume', vol.get_value_as_int() / 100.0)
179 # callback for position change
180 def timechange(widget, x=None, y=None):
181 player.SetPosition(PropGet('Metadata')['mpris:trackid'],
182 time_s.get_value(),
183 reply_handler=(lambda *args: None),
184 error_handler=(lambda *args: None))
186 # refresh position change
187 def timeset():
188 global playing
189 if playing == True:
190 try:
191 time_s.set_value(PropGet('Position'))
192 except:
193 playing = False
194 return True
196 # toggle simple/full display
197 def expander(widget):
198 if exp.get_expanded() == False:
199 exp.set_label('Less')
200 else:
201 exp.set_label('More')
203 # close event : hide in the systray
204 def delete_event(self, widget):
205 self.hide()
206 return True
208 # shouldn't happen
209 def destroy(widget):
210 gtk.main_quit()
212 # hide the controller when 'Esc' is pressed
213 def key_release(widget, event):
214 if event.keyval == gtk.keysyms.Escape:
215 global win_position
216 win_position = window.get_position()
217 widget.hide()
219 # callback for click on the tray icon
220 def tray_button(widget):
221 global win_position
222 if window.get_property('visible'):
223 # store position
224 win_position = window.get_position()
225 window.hide()
226 else:
227 # restore position
228 window.move(win_position[0], win_position[1])
229 window.show()
231 # hack: update position, volume, and metadata
232 def icon_clicked(widget, event):
233 update(0)
235 # get playing status, modify the Play/Pause button accordingly
236 def GetPlayStatus(widget):
237 global playing
238 global shuffle
240 playing = PropGet('PlaybackStatus') == 'Playing'
241 if playing:
242 img_bt_toggle.set_from_stock('gtk-media-pause', gtk.ICON_SIZE_SMALL_TOOLBAR)
243 else:
244 img_bt_toggle.set_from_stock('gtk-media-play', gtk.ICON_SIZE_SMALL_TOOLBAR)
245 shuffle = PropGet('Shuffle')
246 bt_shuffle.set_active( shuffle )
248 # loads UI file from the directory where the script is,
249 # so we can use /path/to/mpris.py to execute it.
250 import sys
251 xml = gtk.Builder()
252 gtk.Builder.add_from_file(xml, os.path.join(os.path.dirname(sys.argv[0]) , 'mpris.xml'))
254 # ui setup
255 bt_close = xml.get_object('close')
256 bt_quit = xml.get_object('quit')
257 bt_file = xml.get_object('ChooseFile')
258 bt_next = xml.get_object('next')
259 bt_prev = xml.get_object('prev')
260 bt_stop = xml.get_object('stop')
261 bt_toggle = xml.get_object('toggle')
262 bt_mrl = xml.get_object('AddMRL')
263 bt_shuffle = xml.get_object('shuffle')
264 l_artist = xml.get_object('l_artist')
265 l_title = xml.get_object('l_title')
266 e_mrl = xml.get_object('mrl')
267 window = xml.get_object('window1')
268 img_bt_toggle=xml.get_object('image6')
269 exp = xml.get_object('expander2')
270 expvbox = xml.get_object('expandvbox')
271 audioicon = xml.get_object('eventicon')
272 vol = xml.get_object('vol')
273 time_s = xml.get_object('time_s')
274 time_l = xml.get_object('time_l')
276 # connect to the different callbacks
278 window.connect('delete_event', delete_event)
279 window.connect('destroy', destroy)
280 window.connect('key_release_event', key_release)
282 tray = gtk.status_icon_new_from_icon_name('audio-x-generic')
283 tray.connect('activate', tray_button)
285 bt_close.connect('clicked', destroy)
286 bt_quit.connect('clicked', Quit)
287 bt_mrl.connect('clicked', AddTrack)
288 bt_toggle.connect('clicked', Pause)
289 bt_next.connect('clicked', Next)
290 bt_prev.connect('clicked', Prev)
291 bt_stop.connect('clicked', Stop)
292 bt_shuffle.connect('clicked', Shuffle)
293 exp.connect('activate', expander)
294 vol.connect('changed', volchange)
295 time_s.connect('adjust-bounds', timechange)
296 audioicon.set_events(gtk.gdk.BUTTON_PRESS_MASK) # hack for the bottom right icon
297 audioicon.connect('button_press_event', icon_clicked)
298 time_s.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
300 library = '/media/mp3' # editme
302 # set the Directory chooser to a default location
303 try:
304 os.chdir(library)
305 bt_file.set_current_folder(library)
306 except:
307 bt_file.set_current_folder(os.path.expanduser('~'))
309 # connect to the bus
310 bus = dbus.SessionBus()
311 dbus_names = bus.get_object( 'org.freedesktop.DBus', '/org/freedesktop/DBus' )
312 dbus_names.connect_to_signal('NameOwnerChanged', NameOwnerChanged, dbus_interface='org.freedesktop.DBus') # to detect new Media Players
314 dbus_o = bus.get_object('org.freedesktop.DBus', '/')
315 dbus_intf = dbus.Interface(dbus_o, 'org.freedesktop.DBus')
317 # connect to the first Media Player found
318 for n in dbus_intf.ListNames():
319 if mpris in n:
320 Connect(n)
321 vol.set_value(PropGet('Volume') * 100.0)
322 update(0)
323 break
325 # run a timer to update position
326 timeout_add( 1000, timeset)
328 window.set_icon_name('audio-x-generic')
329 window.show()
331 window.set_icon(gtk.icon_theme_get_default().load_icon('audio-x-generic',24,0))
332 win_position = window.get_position()
334 gtk.main() # execute the main loop