Spotify: Song change notifications with AlbumArt using libnotify
I have borrowed the original script from here.
But, the script had minor issues. I have updated the script to use “mpris:artUrl” instead of taking the artwork from last.fm. No need of xml module anymore. Also, if the first notification was left unattended, I wasn't shown the notification for next song. Corrected that too.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Title: Spotify Notification Demo
Author: Stuart Colville, http://muffinresearch.co.uk
License: BSD
"""
import os
import dbus
import gobject
import pynotify
import httplib2
from urllib import quote
from cgi import escape
from hashlib import md5
from dbus.mainloop.glib import DBusGMainLoop
from dbus.exceptions import DBusException
HTTP_CACHE_DIR = os.path.expanduser("~/.cache/spotify/http/")
IMAGE_CACHE_DIR = os.path.expanduser("~/.cache/spotify/art/")
MISSING = os.path.realpath(os.path.join(os.path.dirname(__file__), "missing.png"))
if not os.path.isdir(HTTP_CACHE_DIR):
os.makedirs(HTTP_CACHE_DIR)
if not os.path.isdir(IMAGE_CACHE_DIR):
os.makedirs(IMAGE_CACHE_DIR)
class SpotifyNotifier(object):
def __init__(self):
"""initialise."""
bus_loop = DBusGMainLoop(set_as_default=True)
self.bus = dbus.SessionBus(mainloop=bus_loop)
loop = gobject.MainLoop()
self.http = httplib2.Http(HTTP_CACHE_DIR)
self.notify_id = None
try:
self.props_changed_listener()
except DBusException, e:
if not ("org.mpris.MediaPlayer2.spotify "
"was not provided") in e.get_dbus_message():
raise
self.session_bus = self.bus.get_object("org.freedesktop.DBus",
"/org/freedesktop/DBus")
self.session_bus.connect_to_signal("NameOwnerChanged",
self.handle_name_owner_changed,
arg0="org.mpris.MediaPlayer2.spotify")
loop.run()
def props_changed_listener(self):
"""Hook up callback to PropertiesChanged event."""
self.spotify = self.bus.get_object("org.mpris.MediaPlayer2.spotify",
"/org/mpris/MediaPlayer2")
self.spotify.connect_to_signal("PropertiesChanged",
self.handle_properties_changed)
def handle_name_owner_changed(self, name, older_owner, new_owner):
"""Introspect the NameOwnerChanged signal to work out if spotify has started."""
if name == "org.mpris.MediaPlayer2.spotify":
if new_owner:
# spotify has been launched - hook it up.
self.props_changed_listener()
else:
self.spotify = None
def handle_properties_changed(self, interface, changed_props, invalidated_props):
"""Handle track changes."""
metadata = changed_props.get("Metadata", {})
if metadata:
if pynotify.init("Spotify Notifier Demo"):
title = unicode(metadata.get("xesam:title").encode('utf8'))
album = unicode(metadata.get("xesam:album").encode('utf8'))
artist = unicode(metadata.get("xesam:artist")[0].encode('utf8'))
image_url = unicode(metadata.get("mpris:artUrl"))
hash_ = md5()
hash_.update("%s-%s" % (artist, album))
hash_path = hash_.hexdigest()
image_path = os.path.join(IMAGE_CACHE_DIR, hash_path)
if not os.path.exists(image_path):
try:
response, image_contents = self.http.request(image_url)
if image_contents:
fh = open(image_path, "w")
fh.write(image_contents)
fh.close()
except:
image_path = MISSING
if image_path != MISSING:
alert = pynotify.Notification(title.replace("&","&"),"by <b>%s</b> from <b>%s</b>" % (artist.replace("&","&"), album.replace("&","&")), image_path)
if self.notify_id:
alert.props.id = self.notify_id
alert.set_urgency (pynotify.URGENCY_CRITICAL)
alert.set_timeout(3)
alert.show()
alert.close()
self.notify_id = alert.props.id
if __name__ == "__main__":
SpotifyNotifier()