fredag 15 mars 2013

Spotify coverart, Artist, Album and Title in Notification

Hi,
I have made a Python script that shows Spotify cover art in notifications. It is just a thumbnail, 90x90 and it has a little Spotify logo in the lower right corner. I am getting the cover from the tracks artUrl tag that Spotify provides. If Spotify don't provide a cove art, then it will be no image in the notification :)  It works both with local music and streamed tracks from Spotify if just Spotify finds the cover.

I had a Next button in the notification but I have commented it out. It is easier to use global short-cuts for Next/Previous/PlayPause. The button also just worked for 7 seconds, as long as the notification is shown. :)

It is two scripts, make sure to keep the file names as they are, atleast the "handle_notification-spotify.py" becouse it is used in "main_handle-spotify.py".

Then install python3-dbus, it is used to communicate with Spotify.
sudo apt-get install python3-dbus

Make sure to make the scripts Executable. Then start Spotify and start "./main_handle-spotify.py" from a terminal, it will give you some information on what is going on.

If all works well the notification will start to work and if you change track in Spotify it will show you the notification containing the thumbnail of the cover and what Artist, Album and Title is playing.

The script automatic shuts down within 7 seconds from that you exits Spotify. If you want to terminate it before you have to do Ctrl+c in the terminal you started the main script. :)

Just want to draw your particular attention to this part of the scripts:

#    This script is NOT supported by or made by Spotify.
#    Spotify are NOT responsible for anything that goes wrong.
#
#    This script is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY.

Have fun! :)


main_handle-spotify.py
       

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# --------------------------------------
#
#    Author: Jonas Lindberg
#    Contact: badomen02 gmail
#    Created: March 10, 2013
#
#    Purpose: To show pop-up notification about current track when track changes in Spotify.
#    Usage: Start Spotify, make sure 'handle_notification-spotify.py' is in the same folder as this script, then start this script. This script will self exit when spotify exits or if you press ctrl+c.
#    Dependencies: python3-dbus, sudo apt-get install python3-dbus to install it.
#    Troubleshooting: The Printed output will give you a hint where the problem lies. Also test this ./handle_notification-spotify.py "Artis" "Album" "Title". Hopefully it will show a simple Notification or an error that will tell you something :).
#
#   
#    This script is NOT supported by or made by Spotify.
#    Spotify are NOT responsible for anything that goes wrong.
#
#    This script is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY.
#
# --------------------------------------


from gi.repository import GLib
import subprocess
from subprocess import Popen, PIPE, STDOUT

import sys
import dbus
from dbus.exceptions import DBusException
from dbus.mainloop.glib import DBusGMainLoop
import re


def handle_properties_changed(interface, changed_props, invalidated_props):
    """Handle track changes."""
   
    # changed_props is a dict, in it there is a key that is named 'Metadata' and is containing another dictionary as Value. An empty dict will be created if 'Metadata' does not exist.
    metadata =changed_props.get("Metadata", {})
   
    # if pause is pressed then metadata will be an empty dict, i.e. False.
    if(bool(metadata)):
        sArtist = metadata['xesam:artist'][0]
        sAlbum = metadata['xesam:album']
        sTitle = metadata['xesam:title']

        #Check if the track has an artUrl
        try:
            sIcon = str(metadata['mpris:artUrl'])
        except:
            sIcon = ""
       
        # Just make the pop-up notification if a new track is playing.
        if (bool(re.search(r"(?i)spotify|http:", sArtist)))or (bool(re.search(r"(?i)spotify|http:", sTitle))) or (bool(re.search(r"(?i)spotify|http:", sAlbum))) :
            pass
        else:
            cmd = ["./handle_notification-spotify.py", sArtist, sAlbum, sTitle, sIcon]
            # If I do not PIPE I get GLib-GObject-WARNING. In Python 3.3 I think DEVNULL will work instead of PIPE. 
            try:
                subprocess.Popen(cmd , shell=False, stderr=subprocess.PIPE)
                print("Notification sent")

            except OSError as e:
                print("In line 57: ",e)
                print("Could not send notification. Make sure the handle_notification-spotify.py is in the same directory as this script.")
                loop.quit()               

           
def isSpotify():
    """This checks if Spotify service is running. Its name is 'org.mpris.MediaPlayer2.spotify' """
    player = bus_Session.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
    iface = dbus.Interface(player, 'org.freedesktop.DBus')
    l=list(iface.ListNames())
    running = False
    for name in l:
        name=str(name)
        if name=="org.mpris.MediaPlayer2.spotify":
           running=True
             
    if bool(running):
        pass
    else:
        print("Spotify is NOT running.")
        # Quit the main loop if it is running.
        if bool(loop.is_running()):
            loop.quit()   

    return running
 
       
def connectToSpotify():
    if bool(isSpotify()):
        print("Spotify is running")
        props_changed_listener()
        return True
    else:
        sys.exit()
       

def props_changed_listener():
    """Hook up callback to PropertiesChanged event."""
    print("Trying to connect with spotify ")
    try:
        spotify = bus_Session.get_object("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
        spotify.connect_to_signal("PropertiesChanged", handle_properties_changed)
        print("Connection Succesful")
    except:
        print("Connection Faild.")     

   
if __name__ == '__main__':
   
    loop = GLib.MainLoop(None, False)
    dbus_loop = DBusGMainLoop(set_as_default=True)
   
    bus_Session = dbus.SessionBus(mainloop=dbus_loop)
   
    #This is making the connection to spotify or exit if it is not running.
    connectToSpotify()
    #checks every 7 sec if spotify is running, so that if you quit spotify this script will exit in less then 7 sec.
    GLib.timeout_add_seconds(7,isSpotify)
    loop.run()
    print("Script Exit")

 
handle_notification-spotify.py
       


#!/usr/bin/env python
# -*- coding: utf-8 -*-


# --------------------------------------
#
#    Author: Jonas Lindberg
#    Contact: badomen02 gmail
#    Created: March 10, 2013
#
#    Purpose: To show pop-up notification about current track when track changes in Spotify.
#    Usage: Start Spotify, then run the script 'main_handle-spotify.py' it will call This script with the argumets "Artis" "Album" "Title" "artUrl". The script will self exit in 8 seconds.
#
#    Dependencies: python3-dbus, sudo apt-get install python3-dbus to install it.
#    Troubleshooting: Test this ./handle_notification-spotify.py "Artis" "Album" "Title". Hopefully it will show a simple Notification or an error that will tell you something :).
#
#   
#    This script is NOT supported or created by Spotify.
#    Spotify is NOT responsible for anything that goes wrong.
#
#    This script is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY.
#
# --------------------------------------

from gi.repository import GLib, Gio
from gi.repository import Notify
from gi.repository.GdkPixbuf import Pixbuf

import sys
import dbus
from dbus.mainloop.glib import DBusGMainLoop
import re


def next_cb(n, action, data):
    try:
        spotify = session_bus.get_object("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
        iface = dbus.Interface(spotify, 'org.mpris.MediaPlayer2.Player')
        iface.Next()
    except:
        #print("Spotify is not running")
       loop.quit()

def closed_cb(user_data):
    Notify.uninit()
    loop.quit()


if __name__ == '__main__':
    artist = sys.argv[1]
    album = sys.argv[2]
    title = sys.argv[3]

    #Checks if the artUrl is sent. Sets the icon to a Pixbuf or None.
    if (len(sys.argv)==5 and bool(sys.argv[4])):
        artUrl = str(sys.argv[4])
        file = Gio.File.new_for_uri(artUrl)
        icon =Pixbuf.new_from_stream(file.read(None), None)
    else:
        icon=None

    sLabel = artist
    sBody='Album: ' +album+'\n'+ 'Track: ' + title
    sActionlabel = "Next"
   
    loop = GLib.MainLoop(None, False)
    dbus_loop = DBusGMainLoop(set_as_default=True)
    session_bus = dbus.SessionBus(mainloop=dbus_loop)

    # Any name will do.
    Notify.init("Played on Spotify")
   
    n = Notify.Notification.new(sLabel, sBody, None)
    try:
        n.set_image_from_pixbuf(icon)
    except TypeError as e:
        #print("No Art Url, Spotify can't find the cover.",e)
        pass
   
    n.set_urgency(Notify.Urgency.CRITICAL)
    n.connect("closed", closed_cb)
    # Uncomment the next line to get a "Next" button. It only works while the notification is displayed, 7 seconds.
    #n.add_action("next", sActionlabel, next_cb, None, None)
    n.set_timeout(7000)
    loop=GLib.MainLoop()
    Notify.Notification.show(n)
    loop.run()