<filename>irc/weechat/mpris2_np.py
# -*- coding: utf-8 -*-

###
# Copyright (c) 2011, <NAME> <<EMAIL>>
# Copyright (c) 2014, <NAME> <<EMAIL>>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###

###
# ChangeLog:
#  0.4.4 - grawity:
#   * trim date to actually show just the year
#  0.4.3 - grawity:
#   * allow the default player to be set
#  0.4.2 - grawity:
#   * show a better message in "Stopped" state
#  0.4.1 - grawity:
#   * inform the user about MPRIS v1 players
#  0.4 - grawity:
#   * ported to MPRIS v2
#  0.3 - Johannes:
#   * don't excpect every metadata key to be available
#  0.2 - Johannes:
#   * fix an error when the player isn't running
#   * don't display the song if it playing is paused/stopped
#   * fix an error when the player is changed while the plugin is loaded
#   * make fewer dbus calls
#  0.1 - Johannes:
#   * first version
###

from __future__ import print_function

import dbus

try:
    import weechat
    weechat.register('mpris2_np',
                     '<NAME> <<EMAIL>>',
                     '0.4.3',
                     'BSD',
                     'Print information on the currently played song',
                     '',
                     '')
except ImportError:
    print('This script must be called from inside weechat')
    raise

BUS_NAME_PREFIX_V2 = 'org.mpris.MediaPlayer2.'
BUS_NAME_PREFIX_V1 = 'org.mpris.'

IF_MPRIS_ROOT   = 'org.mpris.MediaPlayer2'
IF_MPRIS_PLAYER = 'org.mpris.MediaPlayer2.Player'
IF_DBUS_PROP    = 'org.freedesktop.DBus.Properties'

class FubarException(Exception):
    """

    For some reason, reloading either mpris2_np or the python plugin always
    breaks the DBus connection irrecoverably.

    """

def list_players(prefix=BUS_NAME_PREFIX_V2):
    bus = dbus.SessionBus()
    return [name[len(prefix):] for name in bus.list_names()
                               if name.startswith(prefix)]

def get_player(name):
    bus = dbus.SessionBus()
    try:
        return bus.get_object(BUS_NAME_PREFIX_V2 + name, '/org/mpris/MediaPlayer2')
    except TypeError:
        raise FubarException()

def print_info(data, buffer, args):
    err = None
    msg = None

    player_name = args.strip()

    if not player_name:
        player_name = weechat.config_get_plugin('default_player')

    if player_name:
        try:
            player_obj = get_player(player_name)
            prop_if = dbus.Interface(player_obj, IF_DBUS_PROP)

            _props = prop_if.GetAll(IF_MPRIS_ROOT)
            identity = _props.get('Identity', player_name)

            _props = prop_if.GetAll(IF_MPRIS_PLAYER)
            status   = _props.get('PlaybackStatus', 'Stopped')
            metadata = _props.get('Metadata', {})

            if status == 'Stopped':
                msg = u'not listening to anything on %s' % identity
            else:
                artist = u', '.join(metadata.get('xesam:artist', [u'Unknown artist']))
                genre  = u'/'.join(metadata.get('xesam:genre', []))
                album  = metadata.get('xesam:album', None)
                track  = metadata.get('xesam:trackNumber', None)
                title  = metadata.get('xesam:title', u'Unknown title')
                year   = metadata.get('xesam:contentCreated', None)

                msg = u'"%s" by %s' % (title, artist)
                if album:
                    msg += u' from "%s"' % album
                if year:
                    msg += u' (%s)' % year.split(u'-')[0]

            msg = msg.encode('utf-8')

        except dbus.exceptions.DBusException as e:
            if e.get_dbus_name() == 'org.freedesktop.DBus.Error.ServiceUnknown':
                err = 'player "%s" is not running' % player_name
            else:
                err = 'DBus error %s: "%s"' % (e.get_dbus_name(), e.get_dbus_message())

        except FubarException:
            err = 'player "%s" is not running (or pydbus is broken again)' % player_name

    else:
        players = list_players()
        if players:
            players.sort()
            err = 'running players: %s' % ', '.join(players)
        else:
            err = 'no MPRIS v2 players are running'
            v1_players = [p for p in list_players(BUS_NAME_PREFIX_V1) if '.' not in p]
            if v1_players:
                err += ' (I found "%s", but it only supports MPRIS v1)' \
                       % ('" and "'.join(v1_players))

    if err:
        weechat.prnt(buffer, 'np: %s' % err)
    else:
        weechat.prnt(buffer, 'np: %s' % msg)
        weechat.command(buffer, 'np: %s' % msg)
    return weechat.WEECHAT_RC_OK

if __name__ == '__main__':
    weechat.hook_command('np',
                         'Print information on the currently played song',
                         '',
                         '',
                         '',
                         'print_info',
                         '')

    if not weechat.config_is_set_plugin('default_player'):
        weechat.config_set_plugin('default_player', '')
        weechat.config_set_desc_plugin('default_player',
            'Player name to use for "/np" (default: "", shows a list)')

<filename>irc/weechat/mpris2_np.py
# -*- coding: utf-8 -*-

###
# Copyright (c) 2011, <NAME> <<EMAIL>>
# Copyright (c) 2014, <NAME> <<EMAIL>>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###

###
# ChangeLog:
#  0.4.4 - grawity:
#   * trim date to actually show just the year
#  0.4.3 - grawity:
#   * allow the default player to be set
#  0.4.2 - grawity:
#   * show a better message in "Stopped" state
#  0.4.1 - grawity:
#   * inform the user about MPRIS v1 players
#  0.4 - grawity:
#   * ported to MPRIS v2
#  0.3 - Johannes:
#   * don't excpect every metadata key to be available
#  0.2 - Johannes:
#   * fix an error when the player isn't running
#   * don't display the song if it playing is paused/stopped
#   * fix an error when the player is changed while the plugin is loaded
#   * make fewer dbus calls
#  0.1 - Johannes:
#   * first version
###

from __future__ import print_function

import dbus

try:
    import weechat
    weechat.register('mpris2_np',
                     '<NAME> <<EMAIL>>',
                     '0.4.3',
                     'BSD',
                     'Print information on the currently played song',
                     '',
                     '')
except ImportError:
    print('This script must be called from inside weechat')
    raise

BUS_NAME_PREFIX_V2 = 'org.mpris.MediaPlayer2.'
BUS_NAME_PREFIX_V1 = 'org.mpris.'

IF_MPRIS_ROOT   = 'org.mpris.MediaPlayer2'
IF_MPRIS_PLAYER = 'org.mpris.MediaPlayer2.Player'
IF_DBUS_PROP    = 'org.freedesktop.DBus.Properties'

class FubarException(Exception):
    """

    For some reason, reloading either mpris2_np or the python plugin always
    breaks the DBus connection irrecoverably.

    """

def list_players(prefix=BUS_NAME_PREFIX_V2):
    bus = dbus.SessionBus()
    return [name[len(prefix):] for name in bus.list_names()
                               if name.startswith(prefix)]

def get_player(name):
    bus = dbus.SessionBus()
    try:
        return bus.get_object(BUS_NAME_PREFIX_V2 + name, '/org/mpris/MediaPlayer2')
    except TypeError:
        raise FubarException()

def print_info(data, buffer, args):
    err = None
    msg = None

    player_name = args.strip()

    if not player_name:
        player_name = weechat.config_get_plugin('default_player')

    if player_name:
        try:
            player_obj = get_player(player_name)
            prop_if = dbus.Interface(player_obj, IF_DBUS_PROP)

            _props = prop_if.GetAll(IF_MPRIS_ROOT)
            identity = _props.get('Identity', player_name)

            _props = prop_if.GetAll(IF_MPRIS_PLAYER)
            status   = _props.get('PlaybackStatus', 'Stopped')
            metadata = _props.get('Metadata', {})

            if status == 'Stopped':
                msg = u'not listening to anything on %s' % identity
            else:
                artist = u', '.join(metadata.get('xesam:artist', [u'Unknown artist']))
                genre  = u'/'.join(metadata.get('xesam:genre', []))
                album  = metadata.get('xesam:album', None)
                track  = metadata.get('xesam:trackNumber', None)
                title  = metadata.get('xesam:title', u'Unknown title')
                year   = metadata.get('xesam:contentCreated', None)

                msg = u'"%s" by %s' % (title, artist)
                if album:
                    msg += u' from "%s"' % album
                if year:
                    msg += u' (%s)' % year.split(u'-')[0]

            msg = msg.encode('utf-8')

        except dbus.exceptions.DBusException as e:
            if e.get_dbus_name() == 'org.freedesktop.DBus.Error.ServiceUnknown':
                err = 'player "%s" is not running' % player_name
            else:
                err = 'DBus error %s: "%s"' % (e.get_dbus_name(), e.get_dbus_message())

        except FubarException:
            err = 'player "%s" is not running (or pydbus is broken again)' % player_name

    else:
        players = list_players()
        if players:
            players.sort()
            err = 'running players: %s' % ', '.join(players)
        else:
            err = 'no MPRIS v2 players are running'
            v1_players = [p for p in list_players(BUS_NAME_PREFIX_V1) if '.' not in p]
            if v1_players:
                err += ' (I found "%s", but it only supports MPRIS v1)' \
                       % ('" and "'.join(v1_players))

    if err:
        weechat.prnt(buffer, 'np: %s' % err)
    else:
        weechat.prnt(buffer, 'np: %s' % msg)
        weechat.command(buffer, 'np: %s' % msg)
    return weechat.WEECHAT_RC_OK

if __name__ == '__main__':
    weechat.hook_command('np',
                         'Print information on the currently played song',
                         '',
                         '',
                         '',
                         'print_info',
                         '')

    if not weechat.config_is_set_plugin('default_player'):
        weechat.config_set_plugin('default_player', '')
        weechat.config_set_desc_plugin('default_player',
            'Player name to use for "/np" (default: "", shows a list)')

# -*- coding: utf-8 -*- ### # Copyright (c) 2011, <NAME> <<EMAIL>> # Copyright (c) 2014, <NAME> <<EMAIL>> # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, #    this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright #    notice, this list of conditions and the following disclaimer in the #    documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. ### ### # ChangeLog: #  0.4.4 - grawity: #   * trim date to actually show just the year #  0.4.3 - grawity: #   * allow the default player to be set #  0.4.2 - grawity: #   * show a better message in "Stopped" state #  0.4.1 - grawity: #   * inform the user about MPRIS v1 players #  0.4 - grawity: #   * ported to MPRIS v2 #  0.3 - Johannes: #   * don't excpect every metadata key to be available #  0.2 - Johannes: #   * fix an error when the player isn't running #   * don't display the song if it playing is paused/stopped #   * fix an error when the player is changed while the plugin is loaded #   * make fewer dbus calls #  0.1 - Johannes: #   * first version ### For some reason, reloading either mpris2_np or the python plugin always     breaks the DBus connection irrecoverably.