| 1 | # -*- coding: utf-8 -*- |
|---|
| 2 | ## musictracklistener.py |
|---|
| 3 | ## |
|---|
| 4 | ## Copyright (C) 2006 Gustavo Carneiro <gjcarneiro@gmail.com> |
|---|
| 5 | ## Copyright (C) 2006 Nikos Kouremenos <kourem@gmail.com> |
|---|
| 6 | ## |
|---|
| 7 | ## This program is free software; you can redistribute it and/or modify |
|---|
| 8 | ## it under the terms of the GNU General Public License as published |
|---|
| 9 | ## by the Free Software Foundation; version 2 only. |
|---|
| 10 | ## |
|---|
| 11 | ## This program is distributed in the hope that it will be useful, |
|---|
| 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | ## GNU General Public License for more details. |
|---|
| 15 | ## |
|---|
| 16 | import gobject |
|---|
| 17 | if __name__ == '__main__': |
|---|
| 18 | # install _() func before importing dbus_support |
|---|
| 19 | from common import i18n |
|---|
| 20 | |
|---|
| 21 | from common import dbus_support |
|---|
| 22 | if dbus_support.supported: |
|---|
| 23 | import dbus |
|---|
| 24 | import dbus.glib |
|---|
| 25 | |
|---|
| 26 | class MusicTrackInfo(object): |
|---|
| 27 | __slots__ = ['title', 'album', 'artist', 'duration', 'track_number'] |
|---|
| 28 | |
|---|
| 29 | |
|---|
| 30 | class MusicTrackListener(gobject.GObject): |
|---|
| 31 | __gsignals__ = { |
|---|
| 32 | 'music-track-changed': (gobject.SIGNAL_RUN_LAST, None, (object,)), |
|---|
| 33 | } |
|---|
| 34 | |
|---|
| 35 | _instance = None |
|---|
| 36 | @classmethod |
|---|
| 37 | def get(cls): |
|---|
| 38 | if cls._instance is None: |
|---|
| 39 | cls._instance = cls() |
|---|
| 40 | return cls._instance |
|---|
| 41 | |
|---|
| 42 | def __init__(self): |
|---|
| 43 | super(MusicTrackListener, self).__init__() |
|---|
| 44 | self._last_playing_music = None |
|---|
| 45 | |
|---|
| 46 | bus = dbus.SessionBus() |
|---|
| 47 | |
|---|
| 48 | ## Muine |
|---|
| 49 | bus.add_signal_receiver(self._muine_music_track_change_cb, 'SongChanged', |
|---|
| 50 | 'org.gnome.Muine.Player') |
|---|
| 51 | bus.add_signal_receiver(self._player_name_owner_changed, |
|---|
| 52 | 'NameOwnerChanged', 'org.freedesktop.DBus', arg0='org.gnome.Muine') |
|---|
| 53 | bus.add_signal_receiver(self._player_playing_changed_cb, 'StateChanged', |
|---|
| 54 | 'org.gnome.Muine.Player') |
|---|
| 55 | |
|---|
| 56 | ## Rhythmbox |
|---|
| 57 | bus.add_signal_receiver(self._rhythmbox_music_track_change_cb, |
|---|
| 58 | 'playingUriChanged', 'org.gnome.Rhythmbox.Player') |
|---|
| 59 | bus.add_signal_receiver(self._player_name_owner_changed, |
|---|
| 60 | 'NameOwnerChanged', 'org.freedesktop.DBus', arg0='org.gnome.Rhythmbox') |
|---|
| 61 | bus.add_signal_receiver(self._player_playing_changed_cb, |
|---|
| 62 | 'playingChanged', 'org.gnome.Rhythmbox.Player') |
|---|
| 63 | |
|---|
| 64 | ## Banshee |
|---|
| 65 | banshee_bus = dbus.SessionBus() |
|---|
| 66 | dubus = banshee_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/dbus') |
|---|
| 67 | self.dubus_methods = dbus.Interface(dubus, 'org.freedesktop.DBus') |
|---|
| 68 | if self.dubus_methods.NameHasOwner('org.gnome.Banshee'): |
|---|
| 69 | self._get_banshee_bus() |
|---|
| 70 | # Otherwise, it opens Banshee! |
|---|
| 71 | self.banshee_props ={} |
|---|
| 72 | self.current_banshee_title = '' |
|---|
| 73 | gobject.timeout_add(1000, self._banshee_check_track_status) |
|---|
| 74 | |
|---|
| 75 | def _get_banshee_bus(self): |
|---|
| 76 | bus = dbus.SessionBus() |
|---|
| 77 | banshee = bus.get_object('org.gnome.Banshee', '/org/gnome/Banshee/Player') |
|---|
| 78 | self.banshee_methods = dbus.Interface(banshee, 'org.gnome.Banshee.Core') |
|---|
| 79 | |
|---|
| 80 | def do_music_track_changed(self, info): |
|---|
| 81 | if info is not None: |
|---|
| 82 | self._last_playing_music = info |
|---|
| 83 | |
|---|
| 84 | def _player_name_owner_changed(self, name, old, new): |
|---|
| 85 | if not new: |
|---|
| 86 | self.emit('music-track-changed', None) |
|---|
| 87 | |
|---|
| 88 | def _player_playing_changed_cb(self, playing): |
|---|
| 89 | if playing: |
|---|
| 90 | self.emit('music-track-changed', self._last_playing_music) |
|---|
| 91 | else: |
|---|
| 92 | self.emit('music-track-changed', None) |
|---|
| 93 | |
|---|
| 94 | def _muine_properties_extract(self, song_string): |
|---|
| 95 | d = dict((x.strip() for x in s1.split(':', 1)) for s1 in \ |
|---|
| 96 | song_string.split('\n')) |
|---|
| 97 | info = MusicTrackInfo() |
|---|
| 98 | info.title = d['title'] |
|---|
| 99 | info.album = d['album'] |
|---|
| 100 | info.artist = d['artist'] |
|---|
| 101 | info.duration = int(d['duration']) |
|---|
| 102 | info.track_number = int(d['track_number']) |
|---|
| 103 | return info |
|---|
| 104 | |
|---|
| 105 | def _muine_music_track_change_cb(self, arg): |
|---|
| 106 | info = self._muine_properties_extract(arg) |
|---|
| 107 | self.emit('music-track-changed', info) |
|---|
| 108 | |
|---|
| 109 | def _rhythmbox_properties_extract(self, props): |
|---|
| 110 | info = MusicTrackInfo() |
|---|
| 111 | info.title = props['title'] |
|---|
| 112 | info.album = props['album'] |
|---|
| 113 | info.artist = props['artist'] |
|---|
| 114 | info.duration = int(props['duration']) |
|---|
| 115 | info.track_number = int(props['track-number']) |
|---|
| 116 | return info |
|---|
| 117 | |
|---|
| 118 | def _rhythmbox_music_track_change_cb(self, uri): |
|---|
| 119 | bus = dbus.SessionBus() |
|---|
| 120 | rbshellobj = bus.get_object('org.gnome.Rhythmbox', |
|---|
| 121 | '/org/gnome/Rhythmbox/Shell') |
|---|
| 122 | rbshell = dbus.Interface(rbshellobj, 'org.gnome.Rhythmbox.Shell') |
|---|
| 123 | props = rbshell.getSongProperties(uri) |
|---|
| 124 | info = self._rhythmbox_properties_extract(props) |
|---|
| 125 | self.emit('music-track-changed', info) |
|---|
| 126 | |
|---|
| 127 | def _banshee_check_track_status(self): |
|---|
| 128 | if self.dubus_methods.NameHasOwner('org.gnome.Banshee') and \ |
|---|
| 129 | not hasattr(self, 'banshee_methods'): |
|---|
| 130 | self._get_banshee_bus() |
|---|
| 131 | |
|---|
| 132 | if self.dubus_methods.NameHasOwner('org.gnome.Banshee'): |
|---|
| 133 | self.banshee_props['title'] = self.banshee_methods.GetPlayingTitle() |
|---|
| 134 | self.banshee_props['album'] = self.banshee_methods.GetPlayingAlbum() |
|---|
| 135 | self.banshee_props['artist'] = self.banshee_methods.GetPlayingArtist() |
|---|
| 136 | self.banshee_props['duration'] = \ |
|---|
| 137 | self.banshee_methods.GetPlayingDuration() |
|---|
| 138 | info = self._banshee_properties_extract(self.banshee_props) |
|---|
| 139 | if self.current_banshee_title != self.banshee_props['title']: |
|---|
| 140 | self.emit('music-track-changed', info) |
|---|
| 141 | self.current_banshee_title = self.banshee_props['title'] |
|---|
| 142 | return 1 |
|---|
| 143 | |
|---|
| 144 | def _banshee_music_track_change_cb(self, arg): |
|---|
| 145 | info = self._banshee_properties_extract(arg) |
|---|
| 146 | self.emit('music-track-changed', info) |
|---|
| 147 | |
|---|
| 148 | def _banshee_properties_extract(self, props): |
|---|
| 149 | info = MusicTrackInfo() |
|---|
| 150 | info.title = props['title'] |
|---|
| 151 | info.album = props['album'] |
|---|
| 152 | info.artist = props['artist'] |
|---|
| 153 | info.duration = int(props['duration']) |
|---|
| 154 | return info |
|---|
| 155 | |
|---|
| 156 | def get_playing_track(self): |
|---|
| 157 | '''Return a MusicTrackInfo for the currently playing |
|---|
| 158 | song, or None if no song is playing''' |
|---|
| 159 | |
|---|
| 160 | bus = dbus.SessionBus() |
|---|
| 161 | |
|---|
| 162 | ## Check Muine playing track |
|---|
| 163 | test = False |
|---|
| 164 | if hasattr(bus, 'name_has_owner'): |
|---|
| 165 | if bus.name_has_owner('org.gnome.Muine'): |
|---|
| 166 | test = True |
|---|
| 167 | elif dbus.dbus_bindings.bus_name_has_owner(bus.get_connection(), |
|---|
| 168 | 'org.gnome.Muine'): |
|---|
| 169 | test = True |
|---|
| 170 | if test: |
|---|
| 171 | obj = bus.get_object('org.gnome.Muine', '/org/gnome/Muine/Player') |
|---|
| 172 | player = dbus.Interface(obj, 'org.gnome.Muine.Player') |
|---|
| 173 | if player.GetPlaying(): |
|---|
| 174 | song_string = player.GetCurrentSong() |
|---|
| 175 | song = self._muine_properties_extract(song_string) |
|---|
| 176 | self._last_playing_music = song |
|---|
| 177 | return song |
|---|
| 178 | |
|---|
| 179 | ## Check Rhythmbox playing song |
|---|
| 180 | test = False |
|---|
| 181 | if hasattr(bus, 'name_has_owner'): |
|---|
| 182 | if bus.name_has_owner('org.gnome.Rhythmbox'): |
|---|
| 183 | test = True |
|---|
| 184 | elif dbus.dbus_bindings.bus_name_has_owner(bus.get_connection(), |
|---|
| 185 | 'org.gnome.Rhythmbox'): |
|---|
| 186 | test = True |
|---|
| 187 | if test: |
|---|
| 188 | rbshellobj = bus.get_object('org.gnome.Rhythmbox', |
|---|
| 189 | '/org/gnome/Rhythmbox/Shell') |
|---|
| 190 | player = dbus.Interface( |
|---|
| 191 | bus.get_object('org.gnome.Rhythmbox', |
|---|
| 192 | '/org/gnome/Rhythmbox/Player'), 'org.gnome.Rhythmbox.Player') |
|---|
| 193 | rbshell = dbus.Interface(rbshellobj, 'org.gnome.Rhythmbox.Shell') |
|---|
| 194 | uri = player.getPlayingUri() |
|---|
| 195 | props = rbshell.getSongProperties(uri) |
|---|
| 196 | info = self._rhythmbox_properties_extract(props) |
|---|
| 197 | self._last_playing_music = info |
|---|
| 198 | return info |
|---|
| 199 | |
|---|
| 200 | return None |
|---|
| 201 | |
|---|
| 202 | # here we test :) |
|---|
| 203 | if __name__ == '__main__': |
|---|
| 204 | def music_track_change_cb(listener, music_track_info): |
|---|
| 205 | if music_track_info is None: |
|---|
| 206 | print "Stop!" |
|---|
| 207 | else: |
|---|
| 208 | print music_track_info.title |
|---|
| 209 | listener = MusicTrackListener.get() |
|---|
| 210 | listener.connect('music-track-changed', music_track_change_cb) |
|---|
| 211 | track = listener.get_playing_track() |
|---|
| 212 | if track is None: |
|---|
| 213 | print 'Now not playing anything' |
|---|
| 214 | else: |
|---|
| 215 | print 'Now playing: "%s" by %s' % (track.title, track.artist) |
|---|
| 216 | gobject.MainLoop().run() |
|---|