music_track_listener.py 10.7 KB
Newer Older
1
# -*- coding: utf-8 -*-
roidelapluie's avatar
roidelapluie committed
2
## src/music_track_listener.py
3
##
roidelapluie's avatar
roidelapluie committed
4 5 6 7 8 9
## Copyright (C) 2006 Gustavo Carneiro <gjcarneiro AT gmail.com>
##                    Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2008 Jean-Marie Traissard <jim AT lapin.org>
##                    Jonathan Schleifer <js-gajim AT webkeks.org>
##                    Stephan Erb <steve-e AT h3c.de>
10
##
11 12 13
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
14
## it under the terms of the GNU General Public License as published
15
## by the Free Software Foundation; version 3 only.
16
##
17
## Gajim is distributed in the hope that it will be useful,
18
## but WITHOUT ANY WARRANTY; without even the implied warranty of
roidelapluie's avatar
roidelapluie committed
19
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 21
## GNU General Public License for more details.
##
22
## You should have received a copy of the GNU General Public License
roidelapluie's avatar
roidelapluie committed
23
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
24
##
roidelapluie's avatar
roidelapluie committed
25

26
import gobject
27 28 29
if __name__ == '__main__':
	# install _() func before importing dbus_support
	from common import i18n
nkour's avatar
nkour committed
30

31
from common import dbus_support
32 33 34
if dbus_support.supported:
	import dbus
	import dbus.glib
35 36

class MusicTrackInfo(object):
37 38
	__slots__ = ['title', 'album', 'artist', 'duration', 'track_number',
		'paused']
39 40

class MusicTrackListener(gobject.GObject):
41 42 43
	__gsignals__ = {
		'music-track-changed': (gobject.SIGNAL_RUN_LAST, None, (object,)),
	}
44 45 46 47 48 49 50 51 52 53

	_instance = None
	@classmethod
	def get(cls):
		if cls._instance is None:
			cls._instance = cls()
		return cls._instance
	
	def __init__(self):
		super(MusicTrackListener, self).__init__()
54 55
		self._last_playing_music = None
		
56
		bus = dbus.SessionBus()
57

58 59 60
		## MPRIS
		bus.add_signal_receiver(self._mpris_music_track_change_cb, 'TrackChange',
			'org.freedesktop.MediaPlayer')
61
		bus.add_signal_receiver(self._mpris_playing_changed_cb, 'StatusChange',
62
			'org.freedesktop.MediaPlayer')
63
		bus.add_signal_receiver(self._player_name_owner_changed,
64 65
			'NameOwnerChanged', 'org.freedesktop.DBus',
			arg0='org.freedesktop.MediaPlayer')
66

67
		## Muine
68 69
		bus.add_signal_receiver(self._muine_music_track_change_cb, 'SongChanged',
			'org.gnome.Muine.Player')
70 71 72 73 74 75 76 77
		bus.add_signal_receiver(self._player_name_owner_changed,
			'NameOwnerChanged', 'org.freedesktop.DBus', arg0='org.gnome.Muine')
		bus.add_signal_receiver(self._player_playing_changed_cb, 'StateChanged',
			'org.gnome.Muine.Player')

		## Rhythmbox
		bus.add_signal_receiver(self._player_name_owner_changed,
			'NameOwnerChanged', 'org.freedesktop.DBus', arg0='org.gnome.Rhythmbox')
78
		bus.add_signal_receiver(self._rhythmbox_playing_changed_cb,
79
			'playingChanged', 'org.gnome.Rhythmbox.Player')
80 81
		bus.add_signal_receiver(self._player_playing_song_property_changed_cb,
			'playingSongPropertyChanged', 'org.gnome.Rhythmbox.Player')
82

83
		## Banshee
84 85 86
		bus.add_signal_receiver(self._banshee_state_changed_cb,
			'StateChanged', 'org.bansheeproject.Banshee.PlayerEngine')
		bus.add_signal_receiver(self._player_name_owner_changed,
87 88
			'NameOwnerChanged', 'org.freedesktop.DBus',
			arg0='org.bansheeproject.Banshee')
89

90 91 92 93 94 95 96 97 98 99 100
		## Quod Libet
		bus.add_signal_receiver(self._quodlibet_state_change_cb,
			'SongStarted', 'net.sacredchao.QuodLibet')
		bus.add_signal_receiver(self._quodlibet_state_change_cb,
			'Paused', 'net.sacredchao.QuodLibet')
		bus.add_signal_receiver(self._quodlibet_state_change_cb,
			'Unpaused', 'net.sacredchao.QuodLibet')
		bus.add_signal_receiver(self._player_name_owner_changed,
			'NameOwnerChanged', 'org.freedesktop.DBus',
			arg0='net.sacredchao.QuodLibet')

Yann Leboulanger's avatar
Yann Leboulanger committed
101 102 103 104 105 106 107 108 109
		## Totem
		## patched by Lucky <lucky1.data@gmail.com>
		## used with Totem DBus plugin:
		## http://lucky.awardspace.co.uk/home/totem-plugins
		bus.add_signal_receiver(self._totem_playing_started_cb,
			'playingStarted', 'org.gnome.Totem')
		bus.add_signal_receiver(self._totem_playing_stopped_cb,
			'playingStopped', 'org.gnome.Totem')

110 111 112 113 114 115 116 117 118
	def _player_name_owner_changed(self, name, old, new):
		if not new:
			self.emit('music-track-changed', None)

	def _player_playing_changed_cb(self, playing):
		if playing:
			self.emit('music-track-changed', self._last_playing_music)
		else:
			self.emit('music-track-changed', None)
119

120 121 122 123
	def _player_playing_song_property_changed_cb(self, a, b, c, d):
		if b == 'rb:stream-song-title':
			self.emit('music-track-changed', self._last_playing_music)

124 125
	def _mpris_properties_extract(self, song):
		info = MusicTrackInfo()
126 127 128 129
		info.title = song.get('title', '')
		info.album = song.get('album', '')
		info.artist = song.get('artist', '')
		info.duration = int(song.get('length', 0))
130 131
		return info

132
	def _mpris_playing_changed_cb(self, playing):
133 134 135 136 137 138 139 140 141 142
		if type(playing) is dbus.Struct:
			if playing[0]:
				self.emit('music-track-changed', None)
			else:
				self.emit('music-track-changed', self._last_playing_music)
		else: # Workaround for e.g. Audacious
			if playing:
				self.emit('music-track-changed', None)
			else:
				self.emit('music-track-changed', self._last_playing_music)
143

144
	def _mpris_music_track_change_cb(self, arg):
Yann Leboulanger's avatar
Yann Leboulanger committed
145
		self._last_playing_music = self._mpris_properties_extract(arg)
146
		self.emit('music-track-changed', self._last_playing_music)
147

148
	def _muine_properties_extract(self, song_string):
149 150
		d = dict((x.strip() for x in  s1.split(':', 1)) for s1 in \
			song_string.split('\n'))
151 152 153 154 155 156
		info = MusicTrackInfo()
		info.title = d['title']
		info.album = d['album']
		info.artist = d['artist']
		info.duration = int(d['duration'])
		info.track_number = int(d['track_number'])
157 158 159 160
		return info

	def _muine_music_track_change_cb(self, arg):
		info = self._muine_properties_extract(arg)
161 162
		self.emit('music-track-changed', info)

163 164 165 166 167 168 169
	def _rhythmbox_playing_changed_cb(self, playing):
		if playing:
			info = self.get_playing_track()
			self.emit('music-track-changed', info)
		else:
			self.emit('music-track-changed', None)

170
	def _rhythmbox_properties_extract(self, props):
171
		info = MusicTrackInfo()
172 173 174 175 176
		info.title = props.get('title', None)
		info.album = props.get('album', None)
		info.artist = props.get('artist', None)
		info.duration = int(props.get('duration', 0))
		info.track_number = int(props.get('track-number', 0))
177
		return info
178

179 180 181
	def _banshee_state_changed_cb(self, state):
		if state == 'playing':
			bus = dbus.SessionBus()
182 183
			banshee = bus.get_object('org.bansheeproject.Banshee',
				'/org/bansheeproject/Banshee/PlayerEngine')
184
			currentTrack = banshee.GetCurrentTrack()
185 186
			self._last_playing_music = self._banshee_properties_extract(
				currentTrack) 
187 188 189
			self.emit('music-track-changed', self._last_playing_music)
		elif state == 'paused':
			self.emit('music-track-changed', None)
190 191 192

	def _banshee_properties_extract(self, props):
		info = MusicTrackInfo()
193 194 195 196
		info.title = props.get('name', None)
		info.album = props.get('album', None)
		info.artist = props.get('artist', None)
		info.duration = int(props.get('length', 0))
197 198
		return info

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
	def _quodlibet_state_change_cb(self, state=None):
		info = self.get_playing_track()
		if info:
			self.emit('music-track-changed', info)
		else:
			self.emit('music-track-changed', None)

	def _quodlibet_properties_extract(self, props):
		info = MusicTrackInfo()
		info.title = props.get('title', None)
		info.album = props.get('album', None)
		info.artist = props.get('artist', None)
		info.duration = int(props.get('~#length', 0))
		return info

Yann Leboulanger's avatar
Yann Leboulanger committed
214 215 216 217 218 219 220 221 222 223 224
	def _totem_playing_started_cb(self, title, album, artist, duration):
		self._last_playing_music = MusicTrackInfo()
		self._last_playing_music.title = title
		self._last_playing_music.album = album
		self._last_playing_music.artist = artist
		self._last_playing_music.duration = duration
		self.emit('music-track-changed', self._last_playing_music)

	def _totem_playing_stopped_cb(self):
		self.emit('music-track-changed', None)

225 226 227 228 229 230 231
	def get_playing_track(self):
		'''Return a MusicTrackInfo for the currently playing
		song, or None if no song is playing'''

		bus = dbus.SessionBus()

		## Check Muine playing track
232 233 234 235 236 237 238 239
		test = False
		if hasattr(bus, 'name_has_owner'):
			if bus.name_has_owner('org.gnome.Muine'):
				test = True
		elif dbus.dbus_bindings.bus_name_has_owner(bus.get_connection(),
		'org.gnome.Muine'):
			test = True
		if test:
240 241 242 243 244
			obj = bus.get_object('org.gnome.Muine', '/org/gnome/Muine/Player')
			player = dbus.Interface(obj, 'org.gnome.Muine.Player')
			if player.GetPlaying():
				song_string = player.GetCurrentSong()
				song = self._muine_properties_extract(song_string)
245
				self._last_playing_music = song
246 247 248
				return song

		## Check Rhythmbox playing song
249 250 251 252 253 254 255 256
		test = False
		if hasattr(bus, 'name_has_owner'):
			if bus.name_has_owner('org.gnome.Rhythmbox'):
				test = True
		elif dbus.dbus_bindings.bus_name_has_owner(bus.get_connection(),
		'org.gnome.Rhythmbox'):
			test = True
		if test:
257 258
			rbshellobj = bus.get_object('org.gnome.Rhythmbox',
				'/org/gnome/Rhythmbox/Shell')
259
			player = dbus.Interface(
260 261
				bus.get_object('org.gnome.Rhythmbox',
				'/org/gnome/Rhythmbox/Player'), 'org.gnome.Rhythmbox.Player')
262
			rbshell = dbus.Interface(rbshellobj, 'org.gnome.Rhythmbox.Shell')
263 264 265 266
			try:
				uri = player.getPlayingUri()
			except dbus.DBusException:
				uri = None
267 268
			if not uri:
				return None
269 270
			props = rbshell.getSongProperties(uri)
			info = self._rhythmbox_properties_extract(props)
271
			self._last_playing_music = info
272 273
			return info

274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
		## Check Banshee playing track
		test = False
		if hasattr(bus, 'name_has_owner'):
			if bus.name_has_owner('org.bansheeproject.Banshee'):
				test = True
		elif dbus.dbus_bindings.bus_name_has_owner(bus.get_connection(),
		'org.bansheeproject.Banshee'):
			test = True
		if test:
			banshee = bus.get_object('org.bansheeproject.Banshee',
				'/org/bansheeproject/Banshee/PlayerEngine')
			currentTrack = banshee.GetCurrentTrack()
			if currentTrack:
				song = self._banshee_properties_extract(currentTrack)
				self._last_playing_music = song
				return song

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
		## Check Quod Libet playing track
		test = False
		if hasattr(bus, 'name_has_owner'):
			if bus.name_has_owner('net.sacredchao.QuodLibet'):
				test = True
		elif dbus.dbus_bindings.bus_name_has_owner(bus.get_connection(),
		'net.sacredchao.QuodLibet'):
			test = True
		if test:
			quodlibet = bus.get_object('net.sacredchao.QuodLibet',
				'/net/sacredchao/QuodLibet')
			if quodlibet.IsPlaying():
				currentTrack = quodlibet.CurrentSong()
				song = self._quodlibet_properties_extract(currentTrack)
				self._last_playing_music = song
				return song

308 309
		return None

310 311 312
# here we test :)
if __name__ == '__main__':
	def music_track_change_cb(listener, music_track_info):
313
		if music_track_info is None:
314
			print 'Stop!'
315 316
		else:
			print music_track_info.title
317 318 319 320 321 322 323
	listener = MusicTrackListener.get()
	listener.connect('music-track-changed', music_track_change_cb)
	track = listener.get_playing_track()
	if track is None:
		print 'Now not playing anything'
	else:
		print 'Now playing: "%s" by %s' % (track.title, track.artist)
324
	gobject.MainLoop().run()
325

roidelapluie's avatar
roidelapluie committed
326
# vim: se ts=3: