- 0 Talk
-
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()