From 81a039854f7e86c19a6ce479420ba6e4e31d344e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Sun, 17 Dec 2017 01:58:42 +0100 Subject: [PATCH] Port music_track_listener to GTK dbus --- gajim/gui_interface.py | 11 +- gajim/music_track_listener.py | 331 +++++++++++----------------------- gajim/roster_window.py | 4 +- 3 files changed, 111 insertions(+), 235 deletions(-) diff --git a/gajim/gui_interface.py b/gajim/gui_interface.py index 3aee96df1..aca8c573b 100644 --- a/gajim/gui_interface.py +++ b/gajim/gui_interface.py @@ -56,9 +56,10 @@ from gajim.common import events from gajim.common import dbus_support if dbus_support.supported: - from gajim.music_track_listener import MusicTrackListener import dbus +from gajim.music_track_listener import MusicTrackListener + if app.HAVE_GEOCLUE: from gajim.common import location_listener @@ -1135,7 +1136,7 @@ class Interface: if connected == invisible_show: return # send currently played music - if (obj.conn.pep_supported and dbus_support.supported and + if (obj.conn.pep_supported and sys.platform == 'linux' and app.config.get_per('accounts', account, 'publish_tune')): self.enable_music_listener() # enable location listener @@ -2192,8 +2193,6 @@ class Interface: if not self.music_track_changed_signal: self.music_track_changed_signal = listener.connect( 'music-track-changed', self.music_track_changed) - track = listener.get_playing_track() - self.music_track_changed(listener, track) def disable_music_listener(self): listener = MusicTrackListener.get() @@ -2207,9 +2206,7 @@ class Interface: else: accounts = [account] - is_paused = hasattr(music_track_info, 'paused') and \ - music_track_info.paused == 0 - if not music_track_info or is_paused: + if music_track_info is None or music_track_info.paused: artist = title = source = '' else: artist = music_track_info.artist diff --git a/gajim/music_track_listener.py b/gajim/music_track_listener.py index 355025e4c..309483204 100644 --- a/gajim/music_track_listener.py +++ b/gajim/music_track_listener.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -## src/music_track_listener.py +## gajim/music_track_listener.py ## ## Copyright (C) 2006 Gustavo Carneiro ## Nikos Kouremenos @@ -23,25 +23,40 @@ ## along with Gajim. If not, see . ## -from gi.repository import GObject -if __name__ == '__main__': - # install _() func before importing dbus_support - from gajim.common import i18n +import logging + +from gi.repository import GObject +from gi.repository import Gio, GLib + +log = logging.getLogger('gajim.music_track_listener') + + +def _get_music_players(): + players = [ + 'org.mpris.MediaPlayer2.audacious', + 'org.mpris.MediaPlayer2.bmp', + 'org.mpris.MediaPlayer2.GnomeMusic', + 'org.mpris.MediaPlayer2.quodlibet', + 'org.mpris.MediaPlayer2.rhythmbox', + 'org.mpris.MediaPlayer2.vlc', + 'org.mpris.MediaPlayer2.xmms2' + ] + + return players -from gajim.common import dbus_support -if dbus_support.supported: - import dbus class MusicTrackInfo(object): __slots__ = ['title', 'album', 'artist', 'duration', 'track_number', - 'paused'] + 'paused'] + class MusicTrackListener(GObject.GObject): __gsignals__ = { - 'music-track-changed': (GObject.SignalFlags.RUN_LAST, None, (object,)), + 'music-track-changed': (GObject.SignalFlags.RUN_LAST, None, (object,)), } _instance = None + @classmethod def get(cls): if cls._instance is None: @@ -51,253 +66,117 @@ class MusicTrackListener(GObject.GObject): def __init__(self): super(MusicTrackListener, self).__init__() self._last_playing_music = None + self.con = {} - bus = dbus.SessionBus() + players = _get_music_players() + for name in players: + Gio.bus_watch_name( + Gio.BusType.SESSION, + name, + Gio.BusNameWatcherFlags.NONE, + self._appeared, + self._vanished) - ## MPRIS - bus.add_signal_receiver(self._mpris_music_track_change_cb, 'TrackChange', - 'org.freedesktop.MediaPlayer') - bus.add_signal_receiver(self._mpris_playing_changed_cb, 'StatusChange', - 'org.freedesktop.MediaPlayer') - bus.add_signal_receiver(self._player_name_owner_changed, - 'NameOwnerChanged', 'org.freedesktop.DBus', - arg0='org.freedesktop.MediaPlayer') + def _appeared(self, connection, name, name_owner, *user_data): + '''Set up a listener for music player signals''' + log.info('%s appeared', name) + self.con[name] = connection.signal_subscribe( + name, + 'org.freedesktop.DBus.Properties', + 'PropertiesChanged', + '/org/mpris/MediaPlayer2', + None, + Gio.DBusSignalFlags.NONE, + self._signal_received, + name) - ## Muine - bus.add_signal_receiver(self._muine_music_track_change_cb, 'SongChanged', - 'org.gnome.Muine.Player') - 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') + info = self.get_playing_track(name) + if info is not None: + self._last_playing_music = info + self.emit('music-track-changed', info) - ## Rhythmbox - bus.add_signal_receiver(self._player_name_owner_changed, - 'NameOwnerChanged', 'org.freedesktop.DBus', arg0='org.gnome.Rhythmbox') - bus.add_signal_receiver(self._rhythmbox_playing_changed_cb, - 'playingChanged', 'org.gnome.Rhythmbox.Player') - bus.add_signal_receiver(self._player_playing_song_property_changed_cb, - 'playingSongPropertyChanged', 'org.gnome.Rhythmbox.Player') + def _vanished(self, connection, name, *user_data): + log.info('%s vanished', name) + if name in self.con: + connection.signal_unsubscribe( + self.con[name]) + self.con.pop(name) - ## Banshee - bus.add_signal_receiver(self._banshee_state_changed_cb, - 'StateChanged', 'org.bansheeproject.Banshee.PlayerEngine') - bus.add_signal_receiver(self._player_name_owner_changed, - 'NameOwnerChanged', 'org.freedesktop.DBus', - arg0='org.bansheeproject.Banshee') - - ## 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') - - 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) + def _signal_received(self, connection, sender_name, object_path, + interface_name, signal_name, parameters, *user_data): + '''Signal handler for PropertiesChanged event''' - 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) + if 'PlaybackStatus' not in parameters[1]: + return - def _mpris_properties_extract(self, song): - info = MusicTrackInfo() - info.title = song.get('title', '') - info.album = song.get('album', '') - info.artist = song.get('artist', '') - info.duration = int(song.get('length', 0)) - return info + log.info('Signal received: %s - %s', interface_name, parameters) - def _mpris_playing_changed_cb(self, playing): - 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) + info = self.get_playing_track(user_data[0]) + self._last_playing_music = info - def _mpris_music_track_change_cb(self, arg): - self._last_playing_music = self._mpris_properties_extract(arg) - self.emit('music-track-changed', self._last_playing_music) - - def _muine_properties_extract(self, song_string): - d = dict((x.strip() for x in s1.split(':', 1)) for s1 in \ - song_string.split('\n')) - 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']) - return info - - def _muine_music_track_change_cb(self, arg): - info = self._muine_properties_extract(arg) self.emit('music-track-changed', info) - def _rhythmbox_playing_changed_cb(self, playing): - if playing: - info = self.get_playing_track() - self.emit('music-track-changed', info) + def _properties_extract(self, properties): + meta = properties.get('Metadata') + if meta is None or not meta: + return None + + info = MusicTrackInfo() + info.title = meta.get('xesam:title') + info.album = meta.get('xesam:album') + artist = meta.get('xesam:artist') + if artist is not None and len(artist): + info.artist = artist[0] else: - self.emit('music-track-changed', None) + info.artist = None + info.duration = float(meta.get('mpris:length', 0)) + info.track_number = meta.get('xesam:trackNumber', 0) + + status = properties.get('PlaybackStatus') + info.paused = status is not None and status == 'Paused' - def _rhythmbox_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('duration', 0)) - info.track_number = int(props.get('track-number', 0)) return info - def _banshee_state_changed_cb(self, state): - if state == 'playing': - bus = dbus.SessionBus() - banshee = bus.get_object('org.bansheeproject.Banshee', - '/org/bansheeproject/Banshee/PlayerEngine') - currentTrack = banshee.GetCurrentTrack() - self._last_playing_music = self._banshee_properties_extract( - currentTrack) - self.emit('music-track-changed', self._last_playing_music) - elif state == 'paused': - self.emit('music-track-changed', None) - - def _banshee_properties_extract(self, props): - info = MusicTrackInfo() - 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)) - return info - - 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 = float(props.get('~#length', 0)) - return info - - def get_playing_track(self): + def get_playing_track(self, name): '''Return a MusicTrackInfo for the currently playing song, or None if no song is playing''' + proxy = Gio.DBusProxy.new_for_bus_sync( + Gio.BusType.SESSION, + Gio.DBusProxyFlags.NONE, + None, + name, + '/org/mpris/MediaPlayer2', + 'org.freedesktop.DBus.Properties', + None) - bus = dbus.SessionBus() - - ## Check Muine playing track - 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: - 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) - self._last_playing_music = song - return song - - ## Check Rhythmbox playing song - 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: - rbshellobj = bus.get_object('org.gnome.Rhythmbox', - '/org/gnome/Rhythmbox/Shell') - player = dbus.Interface( - bus.get_object('org.gnome.Rhythmbox', - '/org/gnome/Rhythmbox/Player'), 'org.gnome.Rhythmbox.Player') - rbshell = dbus.Interface(rbshellobj, 'org.gnome.Rhythmbox.Shell') - try: - uri = player.getPlayingUri() - except dbus.DBusException: - uri = None - if not uri: + try: + result = proxy.call_sync( + "GetAll", + GLib.Variant('(s)', ('org.mpris.MediaPlayer2.Player',)), + Gio.DBusCallFlags.NONE, + -1, + None) + except GLib.Error as e: + if e.domain == 'g-dbus-error-quark': + log.debug("Could not enable music listener: %s", e.message) return None - props = rbshell.getSongProperties(uri) - info = self._rhythmbox_properties_extract(props) + else: + raise + else: + info = self._properties_extract(result[0]) self._last_playing_music = info return info - ## 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 - - ## 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 - - return None # here we test :) if __name__ == '__main__': def music_track_change_cb(listener, music_track_info): - if music_track_info is None: + if music_track_info is None or music_track_info.paused: print('Stop!') else: print(music_track_info.title) 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)) GObject.MainLoop().run() diff --git a/gajim/roster_window.py b/gajim/roster_window.py index dffc7d378..4793fdae4 100644 --- a/gajim/roster_window.py +++ b/gajim/roster_window.py @@ -40,6 +40,7 @@ from gi.repository import GObject from gi.repository import GLib from gi.repository import Gio import os +import sys import time import locale import hashlib @@ -67,7 +68,6 @@ from gajim.common import i18n if app.HAVE_GEOCLUE: from gajim.common import location_listener from gajim.common import ged -from gajim.common import dbus_support from gajim.message_window import MessageWindowMgr from nbxmpp.protocol import NS_FILE, NS_ROSTERX, NS_CONFERENCE @@ -4963,7 +4963,7 @@ class RosterWindow: item = Gtk.CheckMenuItem(_('Publish Tune')) pep_submenu.append(item) - if not dbus_support.supported: + if sys.platform != 'linux': item.set_sensitive(False) else: activ = app.config.get_per('accounts', account,