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 )