commit mpDris2 for openSUSE:Factory
Hello community, here is the log from the commit of package mpDris2 for openSUSE:Factory checked in at 2018-12-31 09:47:26 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/mpDris2 (Old) and /work/SRC/openSUSE:Factory/.mpDris2.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "mpDris2" Mon Dec 31 09:47:26 2018 rev:4 rq:662115 version:0.7 Changes: -------- --- /work/SRC/openSUSE:Factory/mpDris2/mpDris2.changes 2018-08-20 16:22:34.513090110 +0200 +++ /work/SRC/openSUSE:Factory/.mpDris2.new.28833/mpDris2.changes 2018-12-31 09:48:03.222134624 +0100 @@ -1,0 +2,13 @@ +Sun Dec 23 10:49:50 UTC 2018 - sogal@opensuse.org + +- Fix interpreter.patch + +------------------------------------------------------------------- +Sun Dec 23 10:28:41 UTC 2018 - sogal@opensuse.org + +- Update to current Github version, including the following: + * Catch Mutagen exceptions for malformed files + * Allow a custom bus name to be specified (for multi-instance) + * Use SafeConfigParser#read() and Python3 interpreter + +------------------------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ interpreter.patch ++++++ --- /var/tmp/diff_new_pack.K4cjDh/_old 2018-12-31 09:48:03.658134267 +0100 +++ /var/tmp/diff_new_pack.K4cjDh/_new 2018-12-31 09:48:03.658134267 +0100 @@ -3,7 +3,7 @@ --- mpDris2-0.7/src/mpDris2.in.py.orig 2018-05-08 21:18:40.591530305 +0200 +++ mpDris2-0.7/src/mpDris2.in.py 2018-05-08 21:19:35.171342273 +0200 @@ -1,4 +1,4 @@ --#!/usr/bin/env python +-#!/usr/bin/env python3 +#!/usr/bin/python3 # -*- coding: utf-8 -*- # ++++++ mpDris2-0.7.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpDris2-0.7/NEWS new/mpDris2-0.7/NEWS --- old/mpDris2-0.7/NEWS 2015-12-18 17:32:13.000000000 +0100 +++ new/mpDris2-0.7/NEWS 2018-11-07 22:15:32.000000000 +0100 @@ -1,3 +1,12 @@ +mpDris2 v0.8 (unreleased) + + - "Playing" and "Paused" notifications now show song information (#71). + + - Path to config file can be changed via command line. + + - There is a hidden option to change the D-Bus service name, for advanced + multi-instance configurations. + mpDris2 v0.7 (December 18, 2015) - Now compatible with both pygobject 2.x and 3.x (#62). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpDris2-0.7/configure.ac new/mpDris2-0.7/configure.ac --- old/mpDris2-0.7/configure.ac 2015-12-18 17:32:13.000000000 +0100 +++ new/mpDris2-0.7/configure.ac 2018-11-07 22:15:32.000000000 +0100 @@ -8,7 +8,11 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) -AM_PATH_PYTHON([2.5],, [:]) +AM_PATH_PYTHON([3.4],, [:]) + +define([gitversion], esyscmd([sh -c "which git > /dev/null && (git describe | tr -d '\n' || false)"])) +GITVERSION="gitversion" +AC_SUBST(GITVERSION) GETTEXT_PACKAGE=mpDris2 AC_SUBST(GETTEXT_PACKAGE) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpDris2-0.7/src/Makefile.am new/mpDris2-0.7/src/Makefile.am --- old/mpDris2-0.7/src/Makefile.am 2015-12-18 17:32:13.000000000 +0100 +++ new/mpDris2-0.7/src/Makefile.am 2018-11-07 22:15:32.000000000 +0100 @@ -21,7 +21,9 @@ mpDris2 edit = sed -e 's|@bindir[@]|$(bindir)|g' \ - -e 's|@datadir[@]|$(datadir)|g' + -e 's|@datadir[@]|$(datadir)|g' \ + -e 's|@gitversion[@]|$(GITVERSION)|g' \ + -e 's|@version[@]|$(VERSION)|g' mpDris2: mpDris2.in.py Makefile $(AM_V_GEN) $(edit) $< > $@.tmp && mv $@.tmp $@ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpDris2-0.7/src/mpDris2.in.py new/mpDris2-0.7/src/mpDris2.in.py --- old/mpDris2-0.7/src/mpDris2.in.py 2015-12-18 17:32:13.000000000 +0100 +++ new/mpDris2-0.7/src/mpDris2.in.py 2018-11-07 22:15:32.000000000 +0100 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # This program is free software: you can redistribute it and/or modify @@ -19,13 +19,13 @@ # Based on mpDris from: Erik Karlsson <pilo@ayeon.org> # Some bits taken from quodlibet mpris plugin by <christoph.reiter@gmx.at> + from __future__ import print_function import os import sys import re import shlex -import signal import socket import getopt import mpd @@ -36,6 +36,10 @@ import gettext import time import tempfile +import base64 + +__version__ = "@version@" +__git_version__ = "@gitversion@" try: import mutagen @@ -50,7 +54,7 @@ try: import gi gi.require_version('Notify', '0.7') -except ImportError: +except (ImportError, ValueError): pass using_gi_glib = False @@ -71,7 +75,7 @@ try: import pynotify using_old_notify = True - except: + except ImportError: pass _ = gettext.gettext @@ -81,18 +85,28 @@ params = { 'progname': sys.argv[0], # Connection - 'host': 'localhost', - 'port': 6600, + 'host': None, + 'port': None, 'password': None, + 'bus_name': None, # Library 'music_dir': '', - 'cover_regex': re.compile(r'^(album|cover|\.?folder|front).*\.(gif|jpeg|jpg|png)$', - re.I | re.X), + 'cover_regex': None, # Bling 'mmkeys': True, 'notify': (using_gi_notify or using_old_notify), } +defaults = { + # Connection + 'host': 'localhost', + 'port': 6600, + 'password': None, + 'bus_name': None, + # Library + 'cover_regex': r'^(album|cover|\.?folder|front).*\.(gif|jpeg|jpg|png)$', +} + notification = None # MPRIS allowed metadata tags @@ -232,6 +246,7 @@ urlhandlers = ['http://'] downloaded_covers = ['~/.covers/%s-%s.jpg'] + class MPDWrapper(object): """ Wrapper of mpd.MPDClient to handle socket errors and similar @@ -242,7 +257,7 @@ self._dbus = dbus self._params = params - self._dbus_service = False + self._dbus_service = None self._can_single = False self._can_idle = False @@ -259,6 +274,7 @@ 'repeat': None, } self._metadata = {} + self._temp_song_url = None self._temp_cover = None self._position = 0 self._time = 0 @@ -277,6 +293,10 @@ else: return True + @property + def connected(self): + return self.client._sock is not None + def my_connect(self): """ Init MPD connection """ try: @@ -285,8 +305,12 @@ self._can_single = False self.client.connect(self._params['host'], self._params['port']) - if params['password']: - self.password(self._params['password']) + if self._params['password']: + try: + self.client.password(self._params['password']) + except mpd.CommandError as e: + logger.error(e) + sys.exit(1) commands = self.commands() # added in 0.11 @@ -310,7 +334,7 @@ self.client._sock.settimeout(5.0) # Export our DBUS service if not self._dbus_service: - self._dbus_service = MPRISInterface(self._params['music_dir']) + self._dbus_service = MPRISInterface(self._params) else: # Add our service to the session bus #self._dbus_service.add_to_connection(dbus.SessionBus(), @@ -349,16 +373,14 @@ if self._errors == 6: logger.info('Continue to connect but going silent') return True - except mpd.CommandError as e: - logger.error('MPD command error: %s' % e) - return True def reconnect(self): logger.warning("Disconnected") notification.notify(identity, _('Disconnected'), 'error') # Release the DBus name and disconnect from bus - self._dbus_service.release_name() + if self._dbus_service is not None: + self._dbus_service.release_name() #self._dbus_service.remove_from_connection() # Stop monitoring @@ -379,6 +401,7 @@ self.run() def disconnect(self): + self._temp_song_url = None if self._temp_cover: self._temp_cover.close() self._temp_cover = None @@ -458,18 +481,18 @@ logger.debug('Got GNOME mmkey "%s" for "%s"' % (key, appname)) if key == 'Play': if self._status['state'] == 'play': - notification.notify(identity, _('Paused')) - self.pause() + self.pause(1) + self.notify_about_state('pause') else: - notification.notify(identity, _('Playing')) self.play() + self.notify_about_state('play') elif key == 'Next': self.next() elif key == 'Previous': self.previous() elif key == 'Stop': - notification.notify(identity, _('Stopped')) self.stop() + self.notify_about_state('stop') def last_currentsong(self): return self._currentsong.copy() @@ -550,9 +573,13 @@ if 'file' in mpd_meta: song_url = mpd_meta['file'] if not any([song_url.startswith(prefix) for prefix in urlhandlers]): - song_url = os.path.join(params['music_dir'], song_url) + song_url = os.path.join(self._params['music_dir'], song_url) self._metadata['xesam:url'] = song_url - cover = self.find_cover(song_url) + try: + cover = self.find_cover(song_url) + except mutagen.MutagenError as e: + logger.error("Can't extract covers from %r: %r" % (song_url, e)) + cover = None if cover: self._metadata['mpris:artUrl'] = cover @@ -565,26 +592,89 @@ logger.error("Can't cast value %r to %s" % (value, allowed_tags[key])) + def notify_about_track(self, meta, state='play'): + uri = 'sound' + if 'mpris:artUrl' in meta: + uri = meta['mpris:artUrl'] + + title = 'Unknown Title' + if 'xesam:title' in meta: + title = meta['xesam:title'] + elif 'xesam:url' in meta: + title = meta['xesam:url'].split('/')[-1] + + artist = 'Unknown Artist' + if 'xesam:artist' in meta: + artist = ", ".join(meta['xesam:artist']) + + body = _('by %s') % artist + + if state == 'pause': + uri = 'media-playback-pause-symbolic' + body += ' (%s)' % _('Paused') + + notification.notify(title, body, uri) + + def notify_about_state(self, state): + if state == 'stop': + notification.notify(identity, _('Stopped'), 'media-playback-stop-symbolic') + else: + self.notify_about_track(self.metadata, state) + def find_cover(self, song_url): if song_url.startswith('file://'): song_path = song_url[7:] song_dir = os.path.dirname(song_path) + + # Try existing temporary file if self._temp_cover: - self._temp_cover.close() + if song_url == self._temp_song_url: + logger.debug("find_cover: Reusing old image at %r" % self._temp_cover.name) + return 'file://' + self._temp_cover.name + else: + logger.debug("find_cover: Cleaning up old image at %r" % self._temp_cover.name) + self._temp_song_url = None + self._temp_cover.close() # Search for embedded cover art if mutagen and os.path.exists(song_path): song = mutagen.File(song_path) - if 'APIC:' in song.tags: - self._temp_cover = tempfile.NamedTemporaryFile(suffix='.jpg') - self._temp_cover.write(song.tags['APIC:'].data) - self._temp_cover.flush() - return 'file://' + self._temp_cover.name + if song.tags: + # present but null for some file types + for tag in song.tags.keys(): + if tag.startswith("APIC:"): + for pic in song.tags.getall(tag): + if pic.type == mutagen.id3.PictureType.COVER_FRONT: + self._temp_song_url = song_url + return self._create_temp_cover(pic) + if hasattr(song, "pictures"): + # FLAC + for pic in song.pictures: + if pic.type == mutagen.id3.PictureType.COVER_FRONT: + self._temp_song_url = song_url + return self._create_temp_cover(pic) + elif song.tags and 'metadata_block_picture' in song.tags: + # OGG + for b64_data in song.get("metadata_block_picture", []): + try: + data = base64.b64decode(b64_data) + except (TypeError, ValueError): + continue + + try: + pic = mutagen.flac.Picture(data) + except mutagen.flac.error: + continue + + if pic.type == mutagen.id3.PictureType.COVER_FRONT: + self._temp_song_url = song_url + return self._create_temp_cover(pic) + # Look in song directory for common album cover files if os.path.exists(song_dir): for f in os.listdir(song_dir): - if params['cover_regex'].match(f): + if self._params['cover_regex'].match(f): return 'file://' + os.path.join(song_dir, f) # Search the shared cover directories @@ -597,6 +687,20 @@ return 'file://' + f return None + def _create_temp_cover(self, pic): + """ + Create a temporary file containing pic, and return it's location + """ + extension = {'image/jpeg': '.jpg', + 'image/png': '.png', + 'image/gif': '.gif'} + + self._temp_cover = tempfile.NamedTemporaryFile(prefix='cover-', suffix=extension.get(pic.mime, '.jpg')) + self._temp_cover.write(pic.data) + self._temp_cover.flush() + logger.debug("find_cover: Storing embedded image at %r" % self._temp_cover.name) + return 'file://' + self._temp_cover.name + def last_status(self): if time.time() - self._time >= 2: self.timer_callback() @@ -653,29 +757,16 @@ self.update_metadata() new_meta = self._dbus_service.update_property('org.mpris.MediaPlayer2.Player', 'Metadata') - if self._params['notify']: - uri = 'sound' - if 'mpris:artUrl' in new_meta: - uri = new_meta['mpris:artUrl'] - title = 'Unknown Title' - if 'xesam:title' in new_meta: - title = new_meta['xesam:title'] - elif 'xesam:url' in new_meta: - title = new_meta['xesam:url'].split('/')[-1] - artist = 'Unknown Artist' - if 'xesam:artist' in new_meta: - artist = ", ".join(new_meta['xesam:artist']) + if self._params['notify'] and new_status['state'] != 'stop': if old_meta.get('xesam:artist', None) != new_meta.get('xesam:artist', None) \ or old_meta.get('xesam:album', None) != new_meta.get('xesam:album', None) \ or old_meta.get('xesam:title', None) != new_meta.get('xesam:title', None) \ or old_meta.get('xesam:url', None) != new_meta.get('xesam:url', None): - # FIXME: maybe this could be done in a nicer way? - notification.notify(title, _('by %s') % artist, uri) + self.notify_about_track(new_meta, new_status['state']) # "mixer" subsystem - - if old_status['volume'] != new_status['volume']: + if old_status.get('volume') != new_status.get('volume'): self._dbus_service.update_property('org.mpris.MediaPlayer2.Player', 'Volume') @@ -707,8 +798,13 @@ def register_mediakeys(self): try: - gsd_object = self._bus.get_object("org.gnome.SettingsDaemon", - "/org/gnome/SettingsDaemon/MediaKeys") + try: + gsd_object = self._bus.get_object("org.gnome.SettingsDaemon.MediaKeys", + "/org/gnome/SettingsDaemon/MediaKeys") + except: + # Try older name. + gsd_object = self._bus.get_object("org.gnome.SettingsDaemon", + "/org/gnome/SettingsDaemon/MediaKeys") gsd_object.GrabMediaPlayerKeys("mpDris2", 0, dbus_interface="org.gnome.SettingsDaemon.MediaKeys") except: @@ -734,10 +830,19 @@ return self.client.fileno() else: def fileno(self): - if self.client._sock is None: + if not self.connected: raise mpd.ConnectionError("Not connected") return self.client._sock.fileno() + ## Access to python-mpd internal APIs + + # We use _write_command("idle") to manually enter idle mode, as it has no + # immediate response to fetch. + # + # Similarly, we use _write_command("noidle") + _fetch_object() to manually + # leave idle mode (for reasons I don't quite remember). The result of + # _fetch_object() is not used. + if hasattr(mpd.MPDClient, "_write_command"): def _write_command(self, *args): return self.client._write_command(*args) @@ -745,20 +850,34 @@ def _write_command(self, *args): return self.client._writecommand(*args) - if hasattr(mpd.MPDClient, "_fetch_object"): - def _fetch_object(self, *args): - return self.client._fetch_object(*args) + if hasattr(mpd.MPDClient, "_parse_objects_direct"): + def _fetch_object(self): + objs = self._fetch_objects() + if not objs: + return {} + return objs[0] + elif hasattr(mpd.MPDClient, "_fetch_object"): + def _fetch_object(self): + return self.client._fetch_object() elif hasattr(mpd.MPDClient, "_getobject"): - def _fetch_object(self, *args): - return self.client._writecommand(*args) + def _fetch_object(self): + return self.client._getobject() + + # We use _fetch_objects("changed") to receive unprompted idle events on + # socket activity. - if hasattr(mpd.MPDClient, "_fetch_objects"): + if hasattr(mpd.MPDClient, "_parse_objects_direct"): + def _fetch_objects(self, *args): + return list(self.client._parse_objects_direct(self.client._read_lines(), *args)) + elif hasattr(mpd.MPDClient, "_fetch_objects"): def _fetch_objects(self, *args): return self.client._fetch_objects(*args) elif hasattr(mpd.MPDClient, "_getobjects"): def _fetch_objects(self, *args): return self.client._getobjects(*args) + # Wrapper to catch connection errors when calling mpd client methods. + def __getattr__(self, attr): if attr[0] == "_": raise AttributeError(attr) @@ -766,7 +885,6 @@ return self.call(attr, *a, **kw) return fn - # Catch connection errors when calling mpd client methods def call(self, command, *args): fn = getattr(self.client, command) try: @@ -792,8 +910,15 @@ self._notification = Notify.Notification() self._notification.set_hint("desktop-entry", GLib.Variant("s", "mpdris2")) self._notification.set_hint("transient", GLib.Variant("b", True)) + try: + self._notification.update("mpdris2 %s started" % __version__) + self._notification.show() + except GLib.GError as err: + logger.error("Failed to init libnotify: %s", err) + self._notification = None else: logger.error("Failed to init libnotify; disabling notifications") + self._notification = None elif using_old_notify: logger.debug("Initializing old pynotify") if pynotify.init(identity): @@ -802,25 +927,31 @@ self._notification.set_hint("transient", True) else: logger.error("Failed to init libnotify; disabling notifications") + self._notification = None def notify(self, title, body, uri=''): if self._notification: - self._notification.update(title, body, uri) - self._notification.show() + try: + self._notification.update(title, body, uri) + self._notification.show() + except GLib.GError as err: + logger.error("Failed to show notification: %s" % err) class MPRISInterface(dbus.service.Object): ''' The base object of an MPRIS player ''' - __name = "org.mpris.MediaPlayer2.mpd" __path = "/org/mpris/MediaPlayer2" __introspect_interface = "org.freedesktop.DBus.Introspectable" __prop_interface = dbus.PROPERTIES_IFACE - def __init__(self, bus, path=""): + def __init__(self, params): dbus.service.Object.__init__(self, dbus.SessionBus(), MPRISInterface.__path) - self.path = path + self._params = params or {} + self._name = self._params["bus_name"] or "org.mpris.MediaPlayer2.mpd" + if not self._name.startswith("org.mpris.MediaPlayer2."): + logger.warn("Configured bus name %r is outside MPRIS2 namespace" % self._name) self._bus = dbus.SessionBus() self._uname = self._bus.get_unique_name() @@ -828,12 +959,12 @@ "/org/freedesktop/DBus") self._dbus_obj.connect_to_signal("NameOwnerChanged", self._name_owner_changed_callback, - arg0=self.__name) + arg0=self._name) self.acquire_name() def _name_owner_changed_callback(self, name, old_owner, new_owner): - if name == self.__name and old_owner == self._uname and new_owner != "": + if name == self._name and old_owner == self._uname and new_owner != "": try: pid = self._dbus_obj.GetConnectionUnixProcessID(new_owner) except: @@ -842,7 +973,7 @@ loop.quit() def acquire_name(self): - self._bus_name = dbus.service.BusName(MPRISInterface.__name, + self._bus_name = dbus.service.BusName(self._name, bus=self._bus, allow_replacement=True, replace_existing=True) @@ -908,7 +1039,7 @@ return dbus.Dictionary(mpd_wrapper.metadata, signature='sv') def __get_volume(): - vol = float(mpd_wrapper.last_status()['volume']) + vol = float(mpd_wrapper.last_status().get('volume', 0)) if vol > 0: return vol / 100.0 else: @@ -1020,31 +1151,31 @@ @dbus.service.method(__player_interface, in_signature='', out_signature='') def Pause(self): - mpd_wrapper.pause() - notification.notify(identity, _('Paused')) + mpd_wrapper.pause(1) + mpd_wrapper.notify_about_state('pause') return @dbus.service.method(__player_interface, in_signature='', out_signature='') def PlayPause(self): status = mpd_wrapper.status() if status['state'] == 'play': - mpd_wrapper.pause() - notification.notify(identity, _('Paused')) + mpd_wrapper.pause(1) + mpd_wrapper.notify_about_state('pause') else: mpd_wrapper.play() - notification.notify(identity, _('Playing')) + mpd_wrapper.notify_about_state('play') return @dbus.service.method(__player_interface, in_signature='', out_signature='') def Stop(self): mpd_wrapper.stop() - notification.notify(identity, _('Stopped')) + mpd_wrapper.notify_about_state('stop') return @dbus.service.method(__player_interface, in_signature='', out_signature='') def Play(self): mpd_wrapper.play() - notification.notify(identity, _('Playing')) + mpd_wrapper.notify_about_state('play') return @dbus.service.method(__player_interface, in_signature='x', out_signature='') @@ -1142,15 +1273,18 @@ def usage(params): print("""\ -Usage: %(progname)s [OPTION]... [MPD_HOST] [MPD_PORT] +Usage: %(progname)s [OPTION]... + + -c, --config=PATH Read a custom configuration file -Note: Environment variables MPD_HOST and MPD_PORT can be used instead of above - arguments. + -h, --host=ADDR Set the mpd server address + --port=PORT Set the TCP port + --music-dir=PATH Set the music library path - -p, --path=PATH Sets the library path of MPD to PATH -d, --debug Run in debug mode + -v, --version mpDris2 version -Default: MPD_HOST: %(host)s, MPD_PORT: %(port)s +Environment variables MPD_HOST and MPD_PORT can be used. Report bugs to https://github.com/eonpatapon/mpDris2/issues""" % params) @@ -1160,72 +1294,99 @@ gettext.bindtextdomain('mpDris2', '@datadir@/locale') gettext.textdomain('mpDris2') - config = configparser.SafeConfigParser() - config.read(['/etc/mpDris2.conf'] + - list(reversed(each_xdg_config('mpDris2/mpDris2.conf')))) - - if config.has_option('Connection', 'host'): - params['host'] = config.get('Connection', 'host') - if config.has_option('Connection', 'port'): - params['port'] = config.get('Connection', 'port') - if config.has_option('Connection', 'password'): - params['password'] = config.get('Connection', 'password') - - if 'MPD_HOST' in os.environ: - params['host'] = os.environ['MPD_HOST'] - if 'MPD_PORT' in os.environ: - params['port'] = os.environ['MPD_PORT'] - - if config.has_option('Library', 'music_dir'): - music_dir = config.get('Library', 'music_dir') - elif config.has_option('Connection', 'music_dir'): - music_dir = config.get('Connection', 'music_dir') - else: - music_dir = find_music_dir() - - if config.has_option('Library', 'cover_regex'): - params['cover_regex'] = re.compile(config.get('Library', 'cover_regex'), re.I | re.X) - - for bling in ['mmkeys', 'notify']: - if config.has_option('Bling', bling): - params[bling] = config.getboolean('Bling', bling) + log_format = '%(asctime)s %(module)s %(levelname)s: %(message)s' + log_level = logging.INFO + config_file = None + music_dir = None + # Parse command line try: - (opts, args) = getopt.getopt(sys.argv[1:], 'hdp:', ['help', 'debug', 'path=']) + (opts, args) = getopt.getopt(sys.argv[1:], 'c:dh:p:v', + ['help', 'bus-name=', 'config=', + 'debug', 'host=', 'music-dir=', + 'path=', 'port=', 'version']) except getopt.GetoptError as ex: (msg, opt) = ex.args - print("%s: %s" % (sys.argv[0], msg)) - print() + print("%s: %s" % (sys.argv[0], msg), file=sys.stderr) + print(file=sys.stderr) usage(params) sys.exit(2) - log_format = '%(asctime)s %(module)s %(levelname)s: %(message)s' - log_level = logging.INFO - for (opt, arg) in opts: - if opt in ['-h', '--help']: + if opt in ['--help']: usage(params) sys.exit() - elif opt in ['-p', '--path']: - music_dir = arg + elif opt in ['--bus-name']: + params['bus_name'] = arg + elif opt in ['-c', '--config']: + config_file = arg elif opt in ['-d', '--debug']: log_level = logging.DEBUG - - logging.basicConfig(format=log_format, level=log_level) - logger = logging.getLogger('mpDris2') + elif opt in ['-h', '--host']: + params['host'] = arg + elif opt in ['-p', '--path', '--music-dir']: + music_dir = arg + elif opt in ['--port']: + params['port'] = int(arg) + elif opt in ['-v', '--version']: + v = __version__ + if __git_version__: + v = __git_version__ + print("mpDris2 version %s" % v) + sys.exit() if len(args) > 2: usage(params) sys.exit() + logging.basicConfig(format=log_format, level=log_level) + logger = logging.getLogger('mpDris2') + + # Pick up the server address (argv -> environment -> config) for arg in args[:2]: if arg.isdigit(): params['port'] = arg else: params['host'] = arg + if not params['host']: + if 'MPD_HOST' in os.environ: + params['host'] = os.environ['MPD_HOST'] + if not params['port']: + if 'MPD_PORT' in os.environ: + params['port'] = os.environ['MPD_PORT'] + + # Read configuration + config = configparser.SafeConfigParser() + if config_file: + with open(config_file) as fh: + config.read(config_file) + else: + config.read(['/etc/mpDris2.conf'] + + list(reversed(each_xdg_config('mpDris2/mpDris2.conf')))) + + for p in ['host', 'port', 'password', 'bus_name']: + if not params[p]: + # TODO: switch to get(fallback=…) when possible + if config.has_option('Connection', p): + params[p] = config.get('Connection', p) + else: + params[p] = defaults[p] + if '@' in params['host']: - params['password'], params['host'] = params['host'].split('@', 1) + params['password'], params['host'] = params['host'].rsplit('@', 1) + + for p in ['mmkeys', 'notify']: + if config.has_option('Bling', p): + params[p] = config.getboolean('Bling', p) + + if not music_dir: + if config.has_option('Library', 'music_dir'): + music_dir = config.get('Library', 'music_dir') + elif config.has_option('Connection', 'music_dir'): + music_dir = config.get('Connection', 'music_dir') + else: + music_dir = find_music_dir() if music_dir: # Ensure that music_dir starts with an URL scheme. @@ -1241,6 +1402,14 @@ logger.warning('By not supplying a path for the music library ' 'this program will break the MPRIS specification!') + if config.has_option('Library', 'cover_regex'): + cover_regex = config.get('Library', 'cover_regex') + else: + cover_regex = defaults['cover_regex'] + params['cover_regex'] = re.compile(cover_regex, re.I | re.X) + + logger.debug('Parameters: %r' % params) + if mutagen: logger.info('Using Mutagen to read covers from music files.') else:
participants (1)
-
root