Wikia

Music Player Daemon Community Wiki

Watchlist Recent changes

Hack:mpd-notify.py

  • Description: Simple python script that displays libnotify notifications when the song or status changes. Requires python-mpd and libnotify. Use of notify-osd is recommended. Album art is taken from ~/.covers so you can use this script to get all the cover art for your music: http://pycoverart.googlecode.com/svn/trunk/cover_art.py (Edit your music location and cover art location at the top.)

Get the latest version from here: https://gist.github.com/1343124

  • Maintainer: Durand D'souza < durand1 AT gmail DOT com >




#!/usr/bin/env python

"""Notifies the user when MPD changes

This script polls mpd and sends out a notification using either pynotify or notify-send whenever either the status of
MPD changes or if a new song is being played. You can specify the cover art folder below. You can use PyCoverArt
(http://pycoverart.googlecode.com/svn/trunk/cover_art.py) to download images if necessary

Dependencies:
    - python-mpd
    - python-notify (Optional but highly recommended)"""

from __future__ import print_function
import os
import time
import sys
import socket

try:
    import mpd
except ImportError:
    print("Can't import mpd. Please install the python bindings for MPD (python-mpd or mpd-python)")
    sys.exit(2)
# Try to import pynotify and fall back if we can't
try:
    import pynotify
    PYNOTIFY_EXISTS = True
except ImportError:
    print("Can't import pynotify so we'll use notify-send instead. We cannot update notifications using this method.")
    PYNOTIFY_EXISTS = False
try:
    from daemon import daemon as Daemon
    daemon_module = True
except ImportError:
    # We couldn't find the daemon module so we'll try to download it
    import urllib2
    print("This script requires a python module to daemonize notifications. Getting it now.")
    module = urllib2.urlopen("https://raw.github.com/gist/1343075/faac02ebde4f22756857f3dbc2babd001d593303/daemon.py")
    module_file = open(sys.path[0] + "/daemon.py",'wb')
    module_file.write(module.read())
    module_file.close() # Save it to pylast.py in the current directory
    try:
        from daemon import daemon as Daemon
        daemon_module = True
        print("It worked!")
    except ImportError:
        print("It didn't work.")
        # Getting the module failed so fall back to a built in daemonizer
        daemon_module = False

__author__ = "Durand D'souza"
__email__ = "durand1 [at] gmail [dot] com"
__credits__ = ["Durand D'souza", ("Patrick Hirt", "patrick [dot] hirt [at] gmail [dot] com")]
__version__ = "06/11/11"
__license__ = "GPL v3"


# MPD settings
MPD_HOST = "localhost"
MPD_PORT = 6600

# Folder containing album covers. Filenames should be in the format artist-album.ext
COVERS_FOLDER = "~/.covers"
# Types of images in the covers folder
FILE_TYPES = ("png", "jpg", "gif", "bmp")

# Time to wait in seconds between checking mpd status changes
POLLING_INTERVAL = 0.5
# Time to wait in seconds between checking for mpd existing
SLEEP_INTERVAL = 5

def get_album_cover(artist, album):
    """This function returns a cover image path or an icon if a cover doesn't exist"""
    for file_type in FILE_TYPES:
        cover_path = os.path.join(os.path.expanduser(COVERS_FOLDER), artist + "-" + album + "." + file_type)
        if os.path.exists(cover_path):
            return cover_path
    # If an image doesn't exist, return an icon
    return "sonata"

def send_notification(summary, message="", icon=None, update=True):
    """This function takes information and sends a notification to the notification daemon using pynotify or
    notify-send. If update is set to False, a new notification will be created rather than updating the previous one"""
    if icon == None:
        icon = "sonata"

    if PYNOTIFY_EXISTS:
        global notification
        if update:
            notification.update(summary=summary, message=message, icon=icon)
        else:
            notification = pynotify.Notification(summary=summary, message=message, icon=icon)
        notification.show()
    else:
        # Escape text first
        icon = icon.replace('"', '\\"')
        summary = summary.replace('"', '\\"')
        message = message.replace('"', '\\"')
        os.popen('notify-send -i "{0}" "{1}" "{2}"'.format(icon, summary, message))

def observe_mpd(client, log_file):
    """This is the main function in the script. It observes mpd and notifies the user of any changes."""

    if PYNOTIFY_EXISTS:
        # Initialise notifications
        pynotify.init("icon-summary-body")
        global notification
        notification = pynotify.Notification("MPD notify initialising")
        #notification.show()

    # Loop and detect mpd changes
    last_status = "Initial"
    last_song = "Initial"

    while True:
        # Get status
        current_status = client.status()['state']
        # There might be errors when getting song details if there is no song in the playlist
        try:
            current_song = client.currentsong()['file']
            # Get song details
            artist = client.currentsong()['artist']
            album = client.currentsong()['album']
            title = client.currentsong()['title']
        except KeyError:
            current_song, artist, album, title = ("", "", "", "")

        # If the song or status has changed, notify the user
        if current_status != last_status and last_status != "Initial":
            print("{0}: Status change: {1}".format(time.strftime("%a, %d %b %Y %H:%M:%S"), current_status),
                  file=log_file)
            if current_status == "play": # Not sure how to make this one show in an efficient way so we won't bother
                pass # send_notification(summary="MPD", message="Playing", icon="media-playback-start")
            elif current_status == "pause":
                send_notification(summary="MPD", message="Paused", icon="media-playback-pause")
            else: # Otherwise we assume that mpd has stopped playing completely
                send_notification(summary="MPD", message="Stopped", icon="media-playback-stop")

        if (current_song != last_song and current_song != "") or (current_status != last_status and current_status ==
                                                                  "play") and last_song != "Initial":
            print("{0}: Song change: {1} by {2} in {3}".format(time.strftime("%a, %d %b %Y %H:%M:%S"), title, artist,
                                                               album), file=log_file)
            send_notification(summary=artist, message=title, icon=get_album_cover(artist, album), update=True)

        # Save current status to compare with later
        last_status = current_status
        last_song = current_song
        # Sleep for some time before checking status again
        time.sleep(POLLING_INTERVAL)

def run_notifier(self=None):
    """Runs the notifier"""
    # Opens the log file to send errors to
    log_file = open("/tmp/mpd_notify.log", "w", 0)
    # Initialise mpd client and wait till we have a connection
    while True:
        try:
            client = mpd.MPDClient()
            client.connect(MPD_HOST, int(MPD_PORT))
            print("{0}: Connected to MPD".format(time.strftime("%a, %d %b %Y %H:%M:%S")), file=log_file)
            # Run the observer but watch for mpd crashes
            observe_mpd(client, log_file)
        except KeyboardInterrupt:
            print("\nLater!")
            sys.exit()
        except (socket.error, mpd.ConnectionError):
            print("{0}: Cannot connect to MPD".format(time.strftime("%a, %d %b %Y %H:%M:%S")), file=log_file)
            time.sleep(SLEEP_INTERVAL)

if daemon_module:
    class MPDNotify(Daemon):
        """Daemon class to run notifier"""
        run = run_notifier

def daemonize(stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
    """Fork the notification daemon using an alternate method to daemon.py
    Contributed by Patrick Hirt"""
    try:
        pid = os.fork()
        if pid > 0:
            sys.exit(0)
    except OSError, e:
            sys.stderr.write('Fork #1 failed: (%d) %s\n' % (e.errno, e.strerror))
            sys.exit(1)

    os.chdir('/')
    os.umask(0)
    os.setsid()

    try:
        pid = os.fork()
        if pid > 0:
            sys.exit(0)
    except OSError, e:
        sys.stderr.write('Fork #2 failed: (%d) %s\n' % (e.errno, e.strerror))
        sys.exit(1)

    for f in sys.stdout, sys.stderr:
        f.flush()
    si = file(stdin, 'r')
    so = file(stdout, 'a+')
    se = file(stderr, 'a+', 0)
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())

if __name__ == "__main__":
    if daemon_module:
        # If we are using the daemon module
        daemon = MPDNotify("/tmp/mpd_notify.pid")
        USAGE = "Usage: {0} start|stop|restart".format(sys.argv[0])
    if len(sys.argv) == 2:
        if daemon_module:
            if sys.argv[1] == "start":
                daemon.start()
            elif sys.argv[1] == "stop":
                daemon.stop()
            elif sys.argv[1] == "restart":
                daemon.restart()
            else:
                print("Unknown command")
                print(USAGE)
                sys.exit(2)
            sys.exit(0)
        else:
            if sys.argv[1] == "daemonize":
                # If we don't use the daemon module, just use daemonize()
                daemonize()
                run_notifier()
            else:
                print("""Usage: {0} [daemonize]""".format(sys.argv[0]))

    else:
        if daemon_module:
            print(USAGE)
            sys.exit(2)
        else:
            # Just run the notifier withouth daemonizing
            print("""Couldn't import the daemon module. Get the daemon module from http://is.gd/IXTtnT and save it next\
to this script or type -h for usage""")
            run_notifier()

Pages on Music Player Daemon Community Wiki

Add a Page
543pages on
this wiki
Advertisement | Your ad here

Latest Photos

Add a Photo
114photos on this wiki
See more >

Recent Wiki Activity

See more >

Around Wikia's network

Random Wiki