Commits

Mathias Panzenböck  committed 0d85eab

org.mpris.MediaPlayer2 support, and prefering it org.mpris.MediaPlayer2

  • Participants
  • Parent commits 5c2c237

Comments (0)

Files changed (1)

File contents/code/main.py

 #
 ##################################################################################
 
-import sys, weakref
+import sys, re, weakref
 from PyQt4.QtCore import Qt, QString
 from PyQt4.QtGui import QGraphicsGridLayout, QSizePolicy, \
 	QGraphicsLinearLayout, QGraphicsWidget, QMenu, QKeySequence
 import dbus
 import dbus.mainloop.qt
 
+PLAYER_NAME = re.compile('^org\\.mpris\\.(?:MediaPlayer2\\.)?(.+?)(?:-[^-]*)?$')
+
 MEDIA_PLAYER = 'org.freedesktop.MediaPlayer'
+MEDIA_PLAYER2 = 'org.mpris.MediaPlayer2'
+MEDIA_PLAYER2_PLAYER = 'org.mpris.MediaPlayer2.Player'
 UNKNOWN_METHOD_ERROR = 'org.freedesktop.DBus.Error.UnknownMethod'
 
 CAN_GO_NEXT = 1 << 0
 
 IconWidget_paint = plasmascript.Plasma.IconWidget.paint
 class PlayerIcon(plasmascript.Plasma.IconWidget):
+	__slots__ = 'applet', 'player', 'frame'
 	def __init__(self,icon,text,applet,player,parent=None):
 		plasmascript.Plasma.IconWidget.__init__(self,icon,text,parent)
 		self.applet = weakref.ref(applet)
 
 		menu.exec_(event.screenPos())
 
-class MediaPlayer(object):
+class MediaPlayerBase(object):
+	__slots__ = 'bus', 'name', 'owner'
 	def __init__(self,bus,name,owner):
 		self.bus   = bus
 		self.name  = name
 		self.owner = owner
 	
 	def _call(self,object,interface,method,*args):
-		return self.bus.get_object(self.name,object,introspect=False).get_dbus_method(method,interface)(*args)
+		return self.bus.get_object(self.name,object,introspect=False).get_dbus_method(
+			method,interface)(*args)
+	
+	def _get(self,object,interface,property):
+		return self.bus.get_object(self.name,object,introspect=False).get_dbus_method(
+			'Get',dbus_interface='org.freedesktop.DBus.Properties')(interface,property)
 
+	def _set(self,object,interface,property,value):
+		return self.bus.get_object(self.name,object,introspect=False).get_dbus_method(
+			'Set',dbus_interface='org.freedesktop.DBus.Properties')(interface,property,value)
+
+class MediaPlayer(MediaPlayerBase):
+	__slots__ = ()
 	def _mpris_call(self,method,*args):
 		return self._call('/Player',MEDIA_PLAYER,method,*args)
 
 	def quit(self):
 		return self._call('/',MEDIA_PLAYER,'Quit')
 
-	def identify(self):
+	def identity(self):
 		return self._call('/',MEDIA_PLAYER,'Identity')
 
 	def capabilities(self):
 	def position(self):
 		return self._mpris_call('PositionGet')
 	
-	def seek(self,position):
+	def set_position(self,position):
 		self._mpris_call('PositionSet',position)
 	
 	def forward(self):
-		self.seek(self.position()+10000)
+		self.set_position(self.position()+10000)
 	
 	def backward(self):
-		self.seek(max(self.position()-10000,0))
+		self.set_position(max(self.position()-10000,0))
 
 	def play(self):
 		self._mpris_call('Play')
 		else:
 			self.pause()
 
+class MediaPlayer2(MediaPlayerBase):
+	__slots__ = ()
+	def _player_call(self,method,*args):
+		return self._call('/org/mpris/MediaPlayer2',MEDIA_PLAYER2_PLAYER,method,*args)
+
+	def _player_get(self,property):
+		return self._get('/org/mpris/MediaPlayer2',MEDIA_PLAYER2_PLAYER,property)
+
+	def _general_call(self,method,*args):
+		return self._call('/org/mpris/MediaPlayer2',MEDIA_PLAYER2,method,*args)
+
+	def _general_get(self,property):
+		return self._get('/org/mpris/MediaPlayer2',MEDIA_PLAYER2,property)
+
+	def quit(self):
+		self._general_call('Quit')
+
+	def identity(self):
+		return self._general_get('Identity')
+
+	def seek(self,offset):
+		self._player_call('Seek',long(offset))
+
+	def forward(self):
+		self.seek(10000000L)
+
+	def backward(self):
+		self.seek(-10000000L)
+
+	def play(self):
+		self._player_call('Play')
+	
+	def pause(self):
+		self._player_call('Pause')
+
+	def playPause(self):
+		self._player_call('PlayPause')
+
+	def playbackStatus(self):
+		return self._player_get('PlaybackStatus')
+
+	def playing(self):
+		return self.playbackStatus() == 'Playing'
+
+	def stop(self):
+		self._player_call('Stop')
+
+	def next(self):
+		self._player_call('Next')
+		
+	def prev(self):
+		self._player_call('Previous')
+
 PLAYERS = {
 	"org.mpris.audacious": Audacious,
 	"org.mpris.vlc": VLC
 }
 
-DBUS = "org.freedesktop.DBus"
-
 class PlayControl(plasmascript.Applet):
 	def init(self):
 		self.setAspectRatioMode(plasmascript.Plasma.IgnoreAspectRatio)
 
 		self._dbus_mainloop = dbus.mainloop.qt.DBusQtMainLoop()
 		self._bus = dbus.SessionBus(mainloop=self._dbus_mainloop)
-		self._dbus = self._bus.get_object(DBUS,"/org/freedesktop/DBus")
-		self._dbus.connect_to_signal("NameOwnerChanged",self._name_owner_changed,DBUS)
+		self._dbus = self._bus.get_object(dbus.BUS_DAEMON_NAME,dbus.BUS_DAEMON_PATH)
+		self._dbus.connect_to_signal("NameOwnerChanged",self._name_owner_changed,dbus.BUS_DAEMON_IFACE)
 		self._bus.add_signal_receiver(self._track_change,
 			signal_name='TrackChange',
 			dbus_interface='org.freedesktop.MediaPlayer',
 			path='/Player',
 			sender_keyword='sender')
+		self._bus.add_signal_receiver(self._properties_changed,
+			signal_name='PropertiesChanged',
+			dbus_interface='org.freedesktop.DBus.Properties',
+			path='/org/mpris/MediaPlayer2',
+			sender_keyword='sender')
 		self._refresh()
 	
 	def contextualActions(self):
 		parent.addPage(shortcuts, "Global Media Player Shortcuts", "preferences-desktop-keyboard")
 
 	def _track_change(self,metadata,sender=None):
-		if self._selected and (self._selected.player.owner == sender or self._selected.player.playing()):
+		self._select_by_owner(sender,True)
+	
+	def _properties_changed(self,interface,changed,invalidated,sender=None):
+		if changed.get('PlaybackStatus',None) == 'Playing':
+			self._select_by_owner(sender,False)
+	
+	def _select_by_owner(self,owner,only_when_palying):
+		if self._selected and (self._selected.player.owner == owner or self._selected.player.playing()):
 			return
 
 		for i in xrange(self.scroll_layout.count()):
 			icon_widget = self.scroll_layout.itemAt(i).graphicsItem()
-			if icon_widget.player.owner == sender:
+			if icon_widget.player.owner == owner:
+				if only_when_palying and not icon_widget.player.playing():
+					break
 				old = self._selected
 				self._selected = icon_widget
 				if old:
 					old.update()
 				icon_widget.update()
+				break
 
 	def _name_owner_changed(self,name,old_owner,new_owner):
-		if name.startswith("org.mpris.") and not name.startswith("org.mpris.MediaPlayer2."):
+		if name.startswith("org.mpris."):
 			# could be done more nicely
 			self._refresh()
 
 	def _refresh(self):
 		players = {}
 		for dbus_name in self._bus.list_names():
-			if dbus_name.startswith("org.mpris.") and \
-					not dbus_name.startswith("org.mpris.MediaPlayer2."):
+			if dbus_name.startswith("org.mpris."):
 				owner = self._bus.get_name_owner(dbus_name)
-				if owner not in players:
-					player = PLAYERS.get(dbus_name,MediaPlayer)(self._bus,dbus_name,owner)
-					name = player.identify()
+				default_cls = MediaPlayer2 if dbus_name.startswith("org.mpris.MediaPlayer2.") else MediaPlayer
+				if owner not in players or default_cls is MediaPlayer2:
+					player = PLAYERS.get(dbus_name,default_cls)(self._bus,dbus_name,owner)
+					name = player.identity()
 					players[owner] = (name, player)
 		players = players.values()
 		players.sort()
 		for name, player in players:
 			icon = None
 			lower_name = unicode(name).lower()
-			lower_id = unicode(player.name[len("org.mpris."):]).lower().rsplit('-',1)[0]
+			lower_id = PLAYER_NAME.match(unicode(player.name)).group(1).lower()
 			for service in KService.allServices():
 				service_name = unicode(service.name())
 				if service_name and service_name == lower_id or \