Port music_track_listener to GTK dbus

This commit is contained in:
André Apitzsch 2017-12-17 01:58:42 +01:00
parent 84aa61335f
commit 81a039854f
3 changed files with 111 additions and 235 deletions

View File

@ -56,9 +56,10 @@ from gajim.common import events
from gajim.common import dbus_support from gajim.common import dbus_support
if dbus_support.supported: if dbus_support.supported:
from gajim.music_track_listener import MusicTrackListener
import dbus import dbus
from gajim.music_track_listener import MusicTrackListener
if app.HAVE_GEOCLUE: if app.HAVE_GEOCLUE:
from gajim.common import location_listener from gajim.common import location_listener
@ -1135,7 +1136,7 @@ class Interface:
if connected == invisible_show: if connected == invisible_show:
return return
# send currently played music # 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')): app.config.get_per('accounts', account, 'publish_tune')):
self.enable_music_listener() self.enable_music_listener()
# enable location listener # enable location listener
@ -2192,8 +2193,6 @@ class Interface:
if not self.music_track_changed_signal: if not self.music_track_changed_signal:
self.music_track_changed_signal = listener.connect( self.music_track_changed_signal = listener.connect(
'music-track-changed', self.music_track_changed) 'music-track-changed', self.music_track_changed)
track = listener.get_playing_track()
self.music_track_changed(listener, track)
def disable_music_listener(self): def disable_music_listener(self):
listener = MusicTrackListener.get() listener = MusicTrackListener.get()
@ -2207,9 +2206,7 @@ class Interface:
else: else:
accounts = [account] accounts = [account]
is_paused = hasattr(music_track_info, 'paused') and \ if music_track_info is None or music_track_info.paused:
music_track_info.paused == 0
if not music_track_info or is_paused:
artist = title = source = '' artist = title = source = ''
else: else:
artist = music_track_info.artist artist = music_track_info.artist

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
## src/music_track_listener.py ## gajim/music_track_listener.py
## ##
## Copyright (C) 2006 Gustavo Carneiro <gjcarneiro AT gmail.com> ## Copyright (C) 2006 Gustavo Carneiro <gjcarneiro AT gmail.com>
## Nikos Kouremenos <kourem AT gmail.com> ## Nikos Kouremenos <kourem AT gmail.com>
@ -23,25 +23,40 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
from gi.repository import GObject import logging
if __name__ == '__main__':
# install _() func before importing dbus_support from gi.repository import GObject
from gajim.common import i18n 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): class MusicTrackInfo(object):
__slots__ = ['title', 'album', 'artist', 'duration', 'track_number', __slots__ = ['title', 'album', 'artist', 'duration', 'track_number',
'paused'] 'paused']
class MusicTrackListener(GObject.GObject): class MusicTrackListener(GObject.GObject):
__gsignals__ = { __gsignals__ = {
'music-track-changed': (GObject.SignalFlags.RUN_LAST, None, (object,)), 'music-track-changed': (GObject.SignalFlags.RUN_LAST, None, (object,)),
} }
_instance = None _instance = None
@classmethod @classmethod
def get(cls): def get(cls):
if cls._instance is None: if cls._instance is None:
@ -51,253 +66,117 @@ class MusicTrackListener(GObject.GObject):
def __init__(self): def __init__(self):
super(MusicTrackListener, self).__init__() super(MusicTrackListener, self).__init__()
self._last_playing_music = None 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 def _appeared(self, connection, name, name_owner, *user_data):
bus.add_signal_receiver(self._mpris_music_track_change_cb, 'TrackChange', '''Set up a listener for music player signals'''
'org.freedesktop.MediaPlayer') log.info('%s appeared', name)
bus.add_signal_receiver(self._mpris_playing_changed_cb, 'StatusChange', self.con[name] = connection.signal_subscribe(
'org.freedesktop.MediaPlayer') name,
bus.add_signal_receiver(self._player_name_owner_changed, 'org.freedesktop.DBus.Properties',
'NameOwnerChanged', 'org.freedesktop.DBus', 'PropertiesChanged',
arg0='org.freedesktop.MediaPlayer') '/org/mpris/MediaPlayer2',
None,
Gio.DBusSignalFlags.NONE,
self._signal_received,
name)
## Muine info = self.get_playing_track(name)
bus.add_signal_receiver(self._muine_music_track_change_cb, 'SongChanged', if info is not None:
'org.gnome.Muine.Player') self._last_playing_music = info
bus.add_signal_receiver(self._player_name_owner_changed, self.emit('music-track-changed', info)
'NameOwnerChanged', 'org.freedesktop.DBus', arg0='org.gnome.Muine')
bus.add_signal_receiver(self._player_playing_changed_cb, 'StateChanged',
'org.gnome.Muine.Player')
## Rhythmbox def _vanished(self, connection, name, *user_data):
bus.add_signal_receiver(self._player_name_owner_changed, log.info('%s vanished', name)
'NameOwnerChanged', 'org.freedesktop.DBus', arg0='org.gnome.Rhythmbox') if name in self.con:
bus.add_signal_receiver(self._rhythmbox_playing_changed_cb, connection.signal_unsubscribe(
'playingChanged', 'org.gnome.Rhythmbox.Player') self.con[name])
bus.add_signal_receiver(self._player_playing_song_property_changed_cb, self.con.pop(name)
'playingSongPropertyChanged', 'org.gnome.Rhythmbox.Player')
## 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) self.emit('music-track-changed', None)
def _player_playing_changed_cb(self, playing): def _signal_received(self, connection, sender_name, object_path,
if playing: interface_name, signal_name, parameters, *user_data):
self.emit('music-track-changed', self._last_playing_music) '''Signal handler for PropertiesChanged event'''
else:
self.emit('music-track-changed', None)
def _player_playing_song_property_changed_cb(self, a, b, c, d): if 'PlaybackStatus' not in parameters[1]:
if b == 'rb:stream-song-title': return
self.emit('music-track-changed', self._last_playing_music)
def _mpris_properties_extract(self, song): log.info('Signal received: %s - %s', interface_name, parameters)
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
def _mpris_playing_changed_cb(self, playing): info = self.get_playing_track(user_data[0])
if type(playing) is dbus.Struct: self._last_playing_music = info
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)
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) self.emit('music-track-changed', info)
def _rhythmbox_playing_changed_cb(self, playing): def _properties_extract(self, properties):
if playing: meta = properties.get('Metadata')
info = self.get_playing_track() if meta is None or not meta:
self.emit('music-track-changed', info) 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: 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 return info
def _banshee_state_changed_cb(self, state): def get_playing_track(self, name):
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):
'''Return a MusicTrackInfo for the currently playing '''Return a MusicTrackInfo for the currently playing
song, or None if no song is 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() try:
result = proxy.call_sync(
## Check Muine playing track "GetAll",
test = False GLib.Variant('(s)', ('org.mpris.MediaPlayer2.Player',)),
if hasattr(bus, 'name_has_owner'): Gio.DBusCallFlags.NONE,
if bus.name_has_owner('org.gnome.Muine'): -1,
test = True None)
elif dbus.dbus_bindings.bus_name_has_owner(bus.get_connection(), except GLib.Error as e:
'org.gnome.Muine'): if e.domain == 'g-dbus-error-quark':
test = True log.debug("Could not enable music listener: %s", e.message)
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:
return None return None
props = rbshell.getSongProperties(uri) else:
info = self._rhythmbox_properties_extract(props) raise
else:
info = self._properties_extract(result[0])
self._last_playing_music = info self._last_playing_music = info
return 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 :) # here we test :)
if __name__ == '__main__': if __name__ == '__main__':
def music_track_change_cb(listener, music_track_info): 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!') print('Stop!')
else: else:
print(music_track_info.title) print(music_track_info.title)
listener = MusicTrackListener.get() listener = MusicTrackListener.get()
listener.connect('music-track-changed', music_track_change_cb) 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() GObject.MainLoop().run()

View File

@ -40,6 +40,7 @@ from gi.repository import GObject
from gi.repository import GLib from gi.repository import GLib
from gi.repository import Gio from gi.repository import Gio
import os import os
import sys
import time import time
import locale import locale
import hashlib import hashlib
@ -67,7 +68,6 @@ from gajim.common import i18n
if app.HAVE_GEOCLUE: if app.HAVE_GEOCLUE:
from gajim.common import location_listener from gajim.common import location_listener
from gajim.common import ged from gajim.common import ged
from gajim.common import dbus_support
from gajim.message_window import MessageWindowMgr from gajim.message_window import MessageWindowMgr
from nbxmpp.protocol import NS_FILE, NS_ROSTERX, NS_CONFERENCE from nbxmpp.protocol import NS_FILE, NS_ROSTERX, NS_CONFERENCE
@ -4963,7 +4963,7 @@ class RosterWindow:
item = Gtk.CheckMenuItem(_('Publish Tune')) item = Gtk.CheckMenuItem(_('Publish Tune'))
pep_submenu.append(item) pep_submenu.append(item)
if not dbus_support.supported: if sys.platform != 'linux':
item.set_sensitive(False) item.set_sensitive(False)
else: else:
activ = app.config.get_per('accounts', account, activ = app.config.get_per('accounts', account,