Typo
[vlc.git] / extras / mpris.py
blob3f25494b770db128e7a3bba7f5543777574349f4
1 #!/usr/bin/python
2 # -*- coding: utf8 -*-
4 # Copyright © 2006-2007 Rafaël Carré <funman at videolanorg>
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.
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.
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.
24 # NOTE: This controller is a SAMPLE, and thus doesn't use all the
25 # Media Player Remote Interface Specification (MPRIS for short) capabilities
27 # MPRIS: http://wiki.xmms2.xmms.se/index.php/Media_Player_Interfaces
29 # You'll need pygtk >= 2.10 to use gtk.StatusIcon
31 # TODO
32 # Ability to choose the Media Player if several are connected to the bus
34 # core dbus stuff
35 import dbus
36 import dbus.glib
38 # core interface stuff
39 import gtk
40 import gtk.glade
42 # timer
43 import gobject
45 # file loading
46 import os
48 global win_position # store the window position on the screen
50 global playing
51 playing = False
53 global shuffle # playlist will play randomly
54 global repeat # repeat the playlist
55 global loop # loop the current element
57 # mpris doesn't support getting the status of these (at the moment)
58 shuffle = False
59 repeat = False
60 loop = False
62 # these are defined on the mpris detected unique name
63 global root # / org.freedesktop.MediaPlayer
64 global player # /Player org.freedesktop.MediaPlayer
65 global tracklist # /Tracklist org.freedesktop.MediaPlayer
67 global bus # Connection to the session bus
68 global identity # MediaPlayer Identity
71 # If a Media Player connects to the bus, we'll use it
72 # Note that we forget the previous Media Player we were connected to
73 def NameOwnerChanged(name, new, old):
74 if old != "" and "org.mpris." in name:
75 Connect(name)
77 # Callback for when "TrackChange" signal is emitted
78 def TrackChange(Track):
79 # the only mandatory metadata is "URI"
80 try:
81 a = Track["artist"]
82 except:
83 a = ""
84 try:
85 t = Track["title"]
86 except:
87 t = Track["URI"]
88 try:
89 length = Track["length"]
90 except:
91 length = 0
92 if length > 0:
93 time_s.set_range(0,Track["length"])
94 time_s.set_sensitive(True)
95 else:
96 # disable the position scale if length isn't available
97 time_s.set_sensitive(False)
98 # update the labels
99 l_artist.set_text(a)
100 l_title.set_text(t)
102 # Connects to the Media Player we detected
103 def Connect(name):
104 global root, player, tracklist
105 global playing, identity
107 # first we connect to the objects
108 root_o = bus.get_object(name, "/")
109 player_o = bus.get_object(name, "/Player")
110 tracklist_o = bus.get_object(name, "/TrackList")
112 # there is only 1 interface per object
113 root = dbus.Interface(root_o, "org.freedesktop.MediaPlayer")
114 tracklist = dbus.Interface(tracklist_o, "org.freedesktop.MediaPlayer")
115 player = dbus.Interface(player_o, "org.freedesktop.MediaPlayer")
117 # connect to the TrackChange signal
118 player_o.connect_to_signal("TrackChange", TrackChange, dbus_interface="org.freedesktop.MediaPlayer")
120 # determine if the Media Player is playing something
121 if player.GetStatus() == 0:
122 playing = True
123 TrackChange(player.GetMetadata())
125 # gets its identity (name and version)
126 identity = root.Identity()
127 window.set_title(identity)
129 #plays an element
130 def AddTrack(widget):
131 mrl = e_mrl.get_text()
132 if mrl != None and mrl != "":
133 tracklist.AddTrack(mrl, True)
134 e_mrl.set_text('')
135 else:
136 mrl = bt_file.get_filename()
137 if mrl != None and mrl != "":
138 tracklist.AddTrack("directory://" + mrl, True)
139 update(0)
141 # basic control
143 def Next(widget):
144 player.Next(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
145 update(0)
147 def Prev(widget):
148 player.Prev(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
149 update(0)
151 def Stop(widget):
152 player.Stop(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
153 update(0)
155 def Quit(widget):
156 player.Quit(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
157 l_title.set_text("")
159 def Pause(widget):
160 player.Pause()
161 status = player.GetStatus()
162 if status == 0:
163 img_bt_toggle.set_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_SMALL_TOOLBAR)
164 else:
165 img_bt_toggle.set_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_SMALL_TOOLBAR)
166 update(0)
168 def Repeat(widget):
169 global repeat
170 repeat = not repeat
171 player.Repeat(repeat)
173 def Shuffle(widget):
174 global shuffle
175 shuffle = not shuffle
176 tracklist.Random(shuffle)
178 def Loop(widget):
179 global loop
180 loop = not loop
181 tracklist.Loop(loop)
183 # update status display
184 def update(widget):
185 item = tracklist.GetMetadata(tracklist.GetCurrentTrack())
186 vol.set_value(player.VolumeGet())
187 try:
188 a = item["artist"]
189 except: a = ""
190 try:
191 t = item["title"]
192 except: t = ""
193 if t == "":
194 try:
195 t = item["URI"]
196 except:
197 t = ""
198 l_artist.set_text(a)
199 l_title.set_text(t)
200 GetPlayStatus(0)
202 # callback for volume change
203 def volchange(widget, data):
204 player.VolumeSet(vol.get_value_as_int(), reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
206 # callback for position change
207 def timechange(widget, x=None, y=None):
208 player.PositionSet(int(time_s.get_value()), reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
210 # refresh position change
211 def timeset():
212 global playing
213 if playing == True:
214 time_s.set_value(player.PositionGet())
215 return True
217 # toggle simple/full display
218 def expander(widget):
219 if exp.get_expanded() == False:
220 exp.set_label("Less")
221 else:
222 exp.set_label("More")
224 # close event : hide in the systray
225 def delete_event(self, widget):
226 self.hide()
227 return True
229 # shouldn't happen
230 def destroy(widget):
231 gtk.main_quit()
233 # hide the controller when 'Esc' is pressed
234 def key_release(widget, event):
235 if event.keyval == gtk.keysyms.Escape:
236 global win_position
237 win_position = window.get_position()
238 widget.hide()
240 # callback for click on the tray icon
241 def tray_button(widget):
242 global win_position
243 if window.get_property('visible'):
244 # store position
245 win_position = window.get_position()
246 window.hide()
247 else:
248 # restore position
249 window.move(win_position[0], win_position[1])
250 window.show()
252 # hack: update position, volume, and metadata
253 def icon_clicked(widget, event):
254 update(0)
256 # get playing status, modify the Play/Pause button accordingly
257 def GetPlayStatus(widget):
258 global playing
259 status = player.GetStatus()
260 if status == 0:
261 img_bt_toggle.set_from_stock("gtk-media-pause", gtk.ICON_SIZE_SMALL_TOOLBAR)
262 playing = True
263 else:
264 img_bt_toggle.set_from_stock("gtk-media-play", gtk.ICON_SIZE_SMALL_TOOLBAR)
265 playing = False
267 # loads glade file from the directory where the script is,
268 # so we can use /path/to/mpris.py to execute it.
269 import sys
270 xml = gtk.glade.XML(os.path.dirname(sys.argv[0]) + '/mpris.glade')
272 # ui setup
273 bt_close = xml.get_widget('close')
274 bt_quit = xml.get_widget('quit')
275 bt_file = xml.get_widget('ChooseFile')
276 bt_next = xml.get_widget('next')
277 bt_prev = xml.get_widget('prev')
278 bt_stop = xml.get_widget('stop')
279 bt_toggle = xml.get_widget('toggle')
280 bt_mrl = xml.get_widget('AddMRL')
281 bt_shuffle = xml.get_widget('shuffle')
282 bt_repeat = xml.get_widget('repeat')
283 bt_loop = xml.get_widget('loop')
284 l_artist = xml.get_widget('l_artist')
285 l_title = xml.get_widget('l_title')
286 e_mrl = xml.get_widget('mrl')
287 window = xml.get_widget('window1')
288 img_bt_toggle=xml.get_widget('image6')
289 exp = xml.get_widget('expander2')
290 expvbox = xml.get_widget('expandvbox')
291 audioicon = xml.get_widget('eventicon')
292 vol = xml.get_widget('vol')
293 time_s = xml.get_widget('time_s')
294 time_l = xml.get_widget('time_l')
296 # connect to the different callbacks
298 window.connect('delete_event', delete_event)
299 window.connect('destroy', destroy)
300 window.connect('key_release_event', key_release)
302 tray = gtk.status_icon_new_from_icon_name("audio-x-generic")
303 tray.connect('activate', tray_button)
305 bt_close.connect('clicked', destroy)
306 bt_quit.connect('clicked', Quit)
307 bt_mrl.connect('clicked', AddTrack)
308 bt_toggle.connect('clicked', Pause)
309 bt_next.connect('clicked', Next)
310 bt_prev.connect('clicked', Prev)
311 bt_stop.connect('clicked', Stop)
312 bt_loop.connect('clicked', Loop)
313 bt_repeat.connect('clicked', Repeat)
314 bt_shuffle.connect('clicked', Shuffle)
315 exp.connect('activate', expander)
316 vol.connect('change-value', volchange)
317 vol.connect('scroll-event', volchange)
318 time_s.connect('adjust-bounds', timechange)
319 audioicon.set_events(gtk.gdk.BUTTON_PRESS_MASK) # hack for the bottom right icon
320 audioicon.connect('button_press_event', icon_clicked)
321 time_s.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
323 library = "/media/mp3" # editme
325 # set the Directory chooser to a default location
326 try:
327 os.chdir(library)
328 bt_file.set_current_folder(library)
329 except:
330 bt_file.set_current_folder(os.path.expanduser("~"))
332 # connect to the bus
333 bus = dbus.SessionBus()
334 dbus_names = bus.get_object( "org.freedesktop.DBus", "/org/freedesktop/DBus" )
335 dbus_names.connect_to_signal("NameOwnerChanged", NameOwnerChanged, dbus_interface="org.freedesktop.DBus") # to detect new Media Players
337 dbus_o = bus.get_object("org.freedesktop.DBus", "/")
338 dbus_intf = dbus.Interface(dbus_o, "org.freedesktop.DBus")
339 name_list = dbus_intf.ListNames()
341 # connect to the first Media Player found
342 for n in name_list:
343 if "org.mpris." in n:
344 Connect(n)
345 window.set_title(identity)
346 vol.set_value(player.VolumeGet())
347 update(0)
348 break
350 # run a timer to update position
351 gobject.timeout_add( 1000, timeset)
353 window.set_icon_name('audio-x-generic')
354 window.show()
356 icon_theme = gtk.icon_theme_get_default()
357 try:
358 pix = icon_theme.load_icon("audio-x-generic",24,0)
359 window.set_icon(pix)
360 except:
361 True
363 win_position = window.get_position()
365 gtk.main() # execute the main loop