I am putting this here for posterity, since Last.fm shut down their streaming service. The goal was to write a simple python player that would play only music that I had never heard – songs I have never heard before on Last.fm and songs that are not in my personal collection. This code was a proof-of-concept that just downloaded the MP3 files and tagged them appropriately, but did not do any playing.

#!/usr/bin/env python

import requests
import hashlib
import os
import pickle
import time
import sys
import subprocess
import sqlite3
import argparse
from mutagen.mp3 import MP3
from mutagen.easyid3 import EasyID3

# You have to have your own unique two values for API_KEY and API_SECRET
# Obtain yours from http://www.last.fm/api/account for Last.fm
API_KEY = ""
API_SECRET = ""
username = ""

# Where to find the banshee database
banshee_db_filename = "./.config/banshee-1/banshee.db"


def make_request(arguments, API_SECRET):
    """ Make an API request, returns JSON response as dict"""
    if API_SECRET:
        keys = arguments.keys()
        keys.sort()
        string = ""

        for key in keys:
            string += key
            string += arguments[key]

        string += API_SECRET
        sign = hashlib.md5(string)
        arguments['api_sig'] = sign.hexdigest()

    arguments["format"] = "json"
    response = requests.post("http://ws.audioscrobbler.com/2.0/?",
                             params=arguments)
    return response.json()


if __name__ == "__main__":

    # Get command line arguments
    parser = argparse.ArgumentParser(description='LastFM Player')
    parser.add_argument('artist', help='Artist for radio station')
    parser.add_argument('--genre', help='Genre to label MP3 file')
    args = parser.parse_args()

    artist = args.artist

    if args.genre:
        genre = args.genre
    else:
        genre = ""

    # Keep Track of downloaded songs in a pickle.
    try:
        with open('downloaded_songs.pickle', 'r') as f:
            database = pickle.load(f)
    except IOError:
        database = dict()

    result = make_request({'method': 'auth.getToken', 'api_key': API_KEY},
                          API_SECRET)
    token = result['token']

    # Make the user login via the web:
    print("You must login via your browser to approve this token for use:  ")
    url_test = "http://www.last.fm/api/auth?api_key=" + API_KEY + \
        "&token=" + token
    subprocess.call(["firefox", url_test])
    raw_input(url_test)

    # After authenticated, we need a session key
    result = make_request({'method': 'auth.getSession',
        'api_key': API_KEY, 'token': token}, API_SECRET)

    key = result['session']['key']

    # This is where we tune to similar artist radio station
    # for other URL formats see the API documentation
    result = make_request({'method': 'radio.tune', 'api_key': API_KEY,
        'sk': key, 'station': 'lastfm://artist/' + artist + '/similarartists'},
        API_SECRET)

    # The rest of this should loop forever!
    while True:

        # Get the playlist, which contains 5 songs
        result = make_request({'method': 'radio.getPlaylist',
            'api_key': API_KEY, 'sk': key, 'bitrate': '128'}, API_SECRET)

        tl = result['playlist']['trackList']['track']

        for track in tl:
            loc = track['location']
            title = track['title']
            identifier = track['identifier']
            album = track['album']
            creator = track['creator']
            duration = track['duration']
            image = track['image']

            # check whether song is already in the database
            if creator in database:
                if album + title + duration in database[creator]:
                    print("Skipping " + creator + " - " + title)
                    continue

            #Check whether song is in Banshee database
            conn = sqlite3.connect(banshee_db_filename)
            c = conn.cursor()
            query = "select CoreTracks.Title from CoreTracks " + \
                "INNER JOIN CoreArtists on " + \
                "(CoreArtists.ArtistID=CoreTracks.ArtistID) " + \
                "INNER JOIN CoreAlbums on " + \
                "(CoreAlbums.AlbumID=CoreTracks.AlbumID) AND " + \
                "CoreArtists.Name=\"" + creator + "\" AND " + \
                "CoreAlbums.Title=\"" + album + "\" AND " + \
                "CoreTracks.Title=\"" + title + "\""

            c.execute(query)
            r = c.fetchall()

            conn.close()

            if len(r) > 0:
                print("Skipping " + creator + " - " + title +
                    " --  Its already in Banshee!")
                continue

            print("Downloading " + creator + " - " + title)

            try:
                mw = requests.get(loc, timeout=60)
                art = requests.get(image, timeout=60)

                # Check whether folder exists
                directory = "./" + artist + "/" + creator + "/" + album + "/"
                if not os.path.exists(directory):
                    os.makedirs(directory)

                # Save the song
                with open(directory + title + '.mp3', 'w') as f:
                    f.write(mw.content)

                # Save the art
                with open(directory + 'coverart.jpg', 'w') as f:
                    f.write(art.content)
            except:
                print("Error: Could not open URL")
                continue

            if not mw:
                print("Error: Could not get URL")
                continue

            #ID3 tag it
            audiofile = MP3(directory + title + '.mp3', ID3=EasyID3)
            audiofile.add_tags(ID3=EasyID3)
            audiofile["title"] = title
            audiofile["artist"] = creator
            audiofile["album"] = album
            audiofile["genre"] = genre
            audiofile.save()

            # Add to database
            if creator in database:
                database[creator].append(album + title + duration)
            else:
                database[creator] = [album + title + duration]

            # Dump to pickle :
            with open('downloaded_songs.pickle', 'w') as f:
                pickle.dump(database, f)

        # Waiting in between
        WAIT = 15
        for i in range(WAIT):
            sys.stdout.write("Continuing in " + str(WAIT - i) + "...\r")
            sys.stdout.flush()
            time.sleep(1)