music_track_listener.py 9.25 KB
Newer Older
1 2 3 4 5 6
# -*- coding: utf-8 -*-
##	musictracklistener.py
##
## Copyright (C) 2006 Gustavo Carneiro <gjcarneiro@gmail.com>
## Copyright (C) 2006 Nikos Kouremenos <kourem@gmail.com>
##
7 8 9
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
10
## it under the terms of the GNU General Public License as published
11
## by the Free Software Foundation; version 3 only.
12
##
13
## Gajim is distributed in the hope that it will be useful,
14 15 16 17
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
18 19 20
## You should have received a copy of the GNU General Public License
## along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
##
21
import os
22
import gobject
23 24 25
if __name__ == '__main__':
	# install _() func before importing dbus_support
	from common import i18n
nkour's avatar
nkour committed
26

27
from common import dbus_support
28 29 30
if dbus_support.supported:
	import dbus
	import dbus.glib
31 32

class MusicTrackInfo(object):
33 34
	__slots__ = ['title', 'album', 'artist', 'duration', 'track_number',
		'paused']
35 36 37


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

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

55 56 57
		## MPRIS
		bus.add_signal_receiver(self._mpris_music_track_change_cb, 'TrackChange',
			'org.freedesktop.MediaPlayer')
58
		bus.add_signal_receiver(self._mpris_playing_changed_cb, 'StatusChange',
59 60 61
			'org.freedesktop.MediaPlayer')
			

62
		## Muine
63 64
		bus.add_signal_receiver(self._muine_music_track_change_cb, 'SongChanged',
			'org.gnome.Muine.Player')
65 66 67 68 69 70 71 72
		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')
73
		bus.add_signal_receiver(self._rhythmbox_playing_changed_cb,
74
			'playingChanged', 'org.gnome.Rhythmbox.Player')
75 76
		bus.add_signal_receiver(self._player_playing_song_property_changed_cb,
			'playingSongPropertyChanged', 'org.gnome.Rhythmbox.Player')
77

78
		## Banshee
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
		# Banshee sucks because it only supports polling.
		# Thus, we only register this is we are very sure that it's
		# installed.
		if os.name == 'posix' and os.system('which banshee >/dev/null 2>&1') == 0:
			banshee_bus = dbus.SessionBus()
			dubus = banshee_bus.get_object('org.freedesktop.DBus',
				'/org/freedesktop/dbus')
			self.dubus_methods = dbus.Interface(dubus, 'org.freedesktop.DBus')
			self.current_banshee_title = ''
			self.banshee_paused_before = False
			self.banshee_is_here = False
			gobject.timeout_add(10000, self._check_if_banshee_bus)
			if self.dubus_methods.NameHasOwner('org.gnome.Banshee'):
				self._get_banshee_bus()
				self.banshee_is_here = True
			# Otherwise, it opens Banshee!
			self.banshee_props ={}
			gobject.timeout_add(1000, self._banshee_check_track_status)
97

98 99 100 101 102 103 104
	def _check_if_banshee_bus(self):
		if self.dubus_methods.NameHasOwner('org.gnome.Banshee'):
			self._get_banshee_bus()
			self.banshee_is_here = True
		else:
			self.banshee_is_here = False
		return True
105 106 107 108 109

	def _get_banshee_bus(self):
		bus = dbus.SessionBus()
		banshee = bus.get_object('org.gnome.Banshee', '/org/gnome/Banshee/Player')
		self.banshee_methods = dbus.Interface(banshee, 'org.gnome.Banshee.Core')
110

111 112 113 114
	def do_music_track_changed(self, info):
		if info is not None:
			self._last_playing_music = info

115 116 117 118 119 120 121 122 123
	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)
124

125 126 127 128
	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)

129 130 131 132 133 134 135 136
	def _mpris_properties_extract(self, song):
		info = MusicTrackInfo()
		info.title = song['title']
		info.album = song['album']
		info.artist = song['artist']
		info.duration = int(song['length'])
		return info

137 138 139 140
	def _mpris_playing_changed_cb(self, playing):
		if playing == 2:
			self.emit('music-track-changed', None)

141 142 143 144
	def _mpris_music_track_change_cb(self, arg):
		info = self._mpris_properties_extract(arg)
		self.emit('music-track-changed', info)

145
	def _muine_properties_extract(self, song_string):
146 147
		d = dict((x.strip() for x in  s1.split(':', 1)) for s1 in \
			song_string.split('\n'))
148 149 150 151 152 153
		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'])
154 155 156 157
		return info

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

160 161 162 163 164 165 166
	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)

167
	def _rhythmbox_properties_extract(self, props):
168 169 170 171 172 173
		info = MusicTrackInfo()
		info.title = props['title']
		info.album = props['album']
		info.artist = props['artist']
		info.duration = int(props['duration'])
		info.track_number = int(props['track-number'])
174
		return info
175

176 177 178 179 180
	def _banshee_check_track_status(self):
		if self.dubus_methods.NameHasOwner('org.gnome.Banshee') and \
		not hasattr(self, 'banshee_methods'):
			self._get_banshee_bus()

181 182 183 184 185 186 187
		if self.dubus_methods.NameHasOwner('org.gnome.Banshee') and self.banshee_is_here:
			try:
				self.banshee_props['title'] = self.banshee_methods.GetPlayingTitle()
				self.banshee_props['album'] = self.banshee_methods.GetPlayingAlbum()
				self.banshee_props['artist'] = self.banshee_methods.\
					GetPlayingArtist()
				self.banshee_props['duration'] = \
188
				self.banshee_methods.GetPlayingDuration()
189 190 191 192 193 194 195 196 197 198
				self.banshee_props['paused'] = self.banshee_methods.\
					GetPlayingStatus()
				info = self._banshee_properties_extract(self.banshee_props)
			except dbus.DBusException, err:
				info = None

				for key in self.banshee_props.keys():
					self.banshee_props[key] = ''
				self.banshee_is_here = False

199 200
			if self.current_banshee_title != self.banshee_props['title']:
				self.emit('music-track-changed', info)
201 202 203 204 205 206 207 208 209
				self.banshee_paused_before = False
			if self.banshee_props['paused'] == 0 and self.banshee_paused_before ==\
			False:
				self.emit('music-track-changed', info)
				self.banshee_paused_before = True
			else: 
				if self.banshee_paused_before and self.banshee_props['paused'] == 1:
					self.emit('music-track-changed', info)
					self.banshee_paused_before = False
210 211 212 213 214 215 216 217 218 219 220 221 222
			self.current_banshee_title = self.banshee_props['title']
		return 1

	def _banshee_music_track_change_cb(self, arg):
		info = self._banshee_properties_extract(arg)
		self.emit('music-track-changed', info)

	def _banshee_properties_extract(self, props):
		info = MusicTrackInfo()
		info.title = props['title']
		info.album = props['album']
		info.artist = props['artist']
		info.duration = int(props['duration'])
223
		info.paused = props['paused']
224 225
		return info

226 227 228 229 230 231 232
	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
233 234 235 236 237 238 239 240
		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:
241 242 243 244 245
			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)
246
				self._last_playing_music = song
247 248 249
				return song

		## Check Rhythmbox playing song
250 251 252 253 254 255 256 257
		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:
258 259
			rbshellobj = bus.get_object('org.gnome.Rhythmbox',
				'/org/gnome/Rhythmbox/Shell')
260
			player = dbus.Interface(
261 262
				bus.get_object('org.gnome.Rhythmbox',
				'/org/gnome/Rhythmbox/Player'), 'org.gnome.Rhythmbox.Player')
263 264
			rbshell = dbus.Interface(rbshellobj, 'org.gnome.Rhythmbox.Shell')
			uri = player.getPlayingUri()
265 266
			if not uri:
				return None
267 268
			props = rbshell.getSongProperties(uri)
			info = self._rhythmbox_properties_extract(props)
269
			self._last_playing_music = info
270 271 272 273
			return info

		return None

274 275 276
# here we test :)
if __name__ == '__main__':
	def music_track_change_cb(listener, music_track_info):
277 278 279 280
		if music_track_info is None:
			print "Stop!"
		else:
			print music_track_info.title
281 282 283 284 285 286 287
	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)
288
	gobject.MainLoop().run()