4 # Copyright © 2006-2007 Rafaël Carré <funman at videolanorg>
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
32 # Ability to choose the Media Player if several are connected to the bus
38 # core interface stuff
47 global win_position # store the window position on the screen
52 global shuffle # playlist will play randomly
53 global repeat # repeat the playlist
54 global loop # loop the current element
56 # mpris doesn't support getting the status of these (at the moment)
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
66 global bus # Connection to the session bus
67 global identity # MediaPlayer Identity
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:
76 # Callback for when "TrackChange" signal is emitted
77 def TrackChange(Track):
78 # the only mandatory metadata is "location"
88 length = Track["length"]
92 time_s.set_range(0,Track["length"])
93 time_s.set_sensitive(True)
95 # disable the position scale if length isn't available
96 time_s.set_sensitive(False)
101 # Connects to the Media Player we detected
103 global root, player, tracklist
104 global playing, identity
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")
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")
116 # connect to the TrackChange signal
117 player_o.connect_to_signal("TrackChange", TrackChange, dbus_interface="org.freedesktop.MediaPlayer")
119 # determine if the Media Player is playing something
120 if player.GetStatus() == 0:
122 TrackChange(player.GetMetadata())
124 # gets its identity (name and version)
125 identity = root.Identity()
126 window.set_title(identity)
129 def AddTrack(widget):
130 mrl = e_mrl.get_text()
131 if mrl != None and mrl != "":
132 tracklist.AddTrack(mrl, True)
135 mrl = bt_file.get_filename()
136 if mrl != None and mrl != "":
137 tracklist.AddTrack("directory://" + mrl, True)
143 player.Next(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
147 player.Prev(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
151 player.Stop(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
155 root.Quit(reply_handler=(lambda *args: None), error_handler=(lambda *args: None))
160 status = player.GetStatus()
162 img_bt_toggle.set_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_SMALL_TOOLBAR)
164 img_bt_toggle.set_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_SMALL_TOOLBAR)
170 player.Repeat(repeat)
174 shuffle = not shuffle
175 tracklist.SetRandom(shuffle)
180 tracklist.SetLoop(loop)
182 # update status display
184 Track = player.GetMetadata()
185 vol.set_value(player.VolumeGet())
196 t = Track["location"]
202 length = Track["length"]
206 time_s.set_range(0,Track["length"])
207 time_s.set_sensitive(True)
209 # disable the position scale if length isn't available
210 time_s.set_sensitive(False)
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))
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))
221 # refresh position change
226 time_s.set_value(player.PositionGet())
231 # toggle simple/full display
232 def expander(widget):
233 if exp.get_expanded() == False:
234 exp.set_label("Less")
236 exp.set_label("More")
238 # close event : hide in the systray
239 def delete_event(self, widget):
247 # hide the controller when 'Esc' is pressed
248 def key_release(widget, event):
249 if event.keyval == gtk.keysyms.Escape:
251 win_position = window.get_position()
254 # callback for click on the tray icon
255 def tray_button(widget):
257 if window.get_property('visible'):
259 win_position = window.get_position()
263 window.move(win_position[0], win_position[1])
266 # hack: update position, volume, and metadata
267 def icon_clicked(widget, event):
270 # get playing status, modify the Play/Pause button accordingly
271 def GetPlayStatus(widget):
276 status = player.GetStatus()
278 playing = status[0] == 0
280 img_bt_toggle.set_from_stock("gtk-media-pause", gtk.ICON_SIZE_SMALL_TOOLBAR)
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.
293 gtk.Builder.add_from_file(xml, os.path.join(os.path.dirname(sys.argv[0]) , 'mpris.xml'))
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')
319 # connect to the different callbacks
321 window.connect('delete_event', delete_event)
322 window.connect('destroy', destroy)
323 window.connect('key_release_event', key_release)
325 tray = gtk.status_icon_new_from_icon_name("audio-x-generic")
326 tray.connect('activate', tray_button)
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)
345 library = "/media/mp3" # editme
347 # set the Directory chooser to a default location
350 bt_file.set_current_folder(library)
352 bt_file.set_current_folder(os.path.expanduser("~"))
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
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()
363 # connect to the first Media Player found
365 if "org.mpris." in n:
367 window.set_title(identity)
368 vol.set_value(player.VolumeGet())
372 # run a timer to update position
373 gobject.timeout_add( 1000, timeset)
375 window.set_icon_name('audio-x-generic')
378 icon_theme = gtk.icon_theme_get_default()
380 pix = icon_theme.load_icon("audio-x-generic",24,0)
385 win_position = window.get_position()
387 gtk.main() # execute the main loop