Skip to content

Instantly share code, notes, and snippets.

@anupdhml
Forked from Timmmm/lastfm_to_gmusic.py
Last active November 16, 2017 06:01
Show Gist options
  • Save anupdhml/bd693e85d1ba1186e9d0 to your computer and use it in GitHub Desktop.
Save anupdhml/bd693e85d1ba1186e9d0 to your computer and use it in GitHub Desktop.
Lastfm loved tracks/playlist to Google Music playlist, either with songs from your library or from all access. Order of songs in lastfm is preserved and can export/load them from a json file.
#!/usr/bin/env python
# Lastfm loved tracks/playlist to Google Music playlist, either with songs from your library or from all access
# Order of songs in lastfm is preserved.
# First export the lastfm data in a json file, and then import it into Google Music
# You can edit the intermediate json file if you wish.
#
# Based on this script.
# https://gist.github.com/Timmmm/6572592
#
# By <anupdhml@gmail.com> Aug 23 2014
#
# Instructions:
#
# 0. Install python and pip.
# 1. Download this to a file `lastfm_to_gmusic.py`
# 2. Make it executable: `chmod +x lastfm_to_gmusic.py`
# 3. Install `gmusicapi` using `pip`: `pip install gmusicapi`
# 4. Get a last.fm API key here: http://www.last.fm/api/account/create
# 5. Run it, in one of the 4 modes
#
# Usage:
#
#./lastfm_to_gmusic.py --get-lfloved <file_to_save_loved_tracks> <lastfm_user>
#./lastfm_to_gmusic.py --get-lfplaylist <file_to_save_playlist_tracks> <lastfm_playlist_url
#./lastfm_to_gmusic.py --to-gplaylist <file_to_read_tracks> <gmusic_playlist_name
#./lastfm_to_gmusic.py --to-gplaylist-allaccess <file_to_read_tracks> <gmusic_playlist_name>
#
# I don't have all access so I have not really tested that option
#
# For user playlists, lastfm_playlist_url should be of the format lastfm://playlist/10137642,
# where the number is playlist id. You can get the id for your playlist from here
# http://ws.audioscrobbler.com/2.0/?method=user.getplaylists&user=anupdhml&api_key=6322b6b48c82cc8ac2fb55713f7d6b90
# (replace the user with your name. The api key is a test one and should work for demo. But if not, use your own key)
# Works for album and tag playlists too. See http://www.last.fm/api/playlists
#
# Troubleshooting:
#
# 1. It says "Login error": Go to your gmail and check that it didn't block any "suspicious logins".
# 2. It doesn't find any tracks: Update gmusicapi.
# 3. Something else: Email me. I might reply.
#
import sys
import simplejson
import urllib2
import gmusicapi
from xml.etree.ElementTree import *
def get_lastfm_lovedtracks(lastfm_username, lastfm_key):
loved = []
page = 1
while True:
url = "http://ws.audioscrobbler.com/2.0/?method=user.getlovedtracks&user=%s&api_key=%s&page=%d" % \
(lastfm_username, lastfm_key, page)
print "Fetching: " + url
tree = parse(urllib2.urlopen(url)).getroot()
tracks = tree.findall('lovedtracks/track')
for track in tracks:
title = track.find('name').text
artist = track.find('artist/name').text
loved.append((artist,title))
if len(tracks) < 50:
break
page += 1
return loved
def get_lastfm_playlist(lastfm_playlist_url, lastfm_key):
playlist = []
url = "http://ws.audioscrobbler.com/2.0/?method=playlist.fetch&playlistURL=%s&api_key=%s" % \
(lastfm_playlist_url, lastfm_key)
print "Fetching: " + url
tree = parse(urllib2.urlopen(url)).getroot()
# had to add that xspf prefix to make this work here.
tracks = tree.findall('{http://xspf.org/ns/0/}playlist/{http://xspf.org/ns/0/}trackList/{http://xspf.org/ns/0/}track')
for track in tracks:
title = track.find('{http://xspf.org/ns/0/}title').text
artist = track.find('{http://xspf.org/ns/0/}creator').text
playlist.append((artist,title))
return playlist
def make_gmusic_playlist(google_username, google_password, playlist_name, tracks, use_all_access=False):
# Log in.
api = gmusicapi.Mobileclient()
if not api.login(google_username, google_password):
print "Login error"
return
# Will hold tracks to add found in the library or all access
# Initializing to_add like this helps us preserve the order of songs in tracks
to_add = [None] * len(tracks)
if use_all_access:
# Search for each song in all access.
# This is quite a dirty way to do it, and the gmusicapi seems to be a little out of date
# hence the catch-all. This found 529 out of the 787 loved songs I have which is not too bad.
print "Searching all access for playlist tracks"
for track in tracks:
artist = track[0]
title = track[1]
try:
res = api.search_all_access(artist + " " + title, max_results=1)
to_add[tracks.index([artist, title])] = res["song_hits"][0]["track"]["nid"]
print 'Found: %s - %s' % (artist, title)
except:
print 'Not found: %s - %s' % (artist, title)
else:
# Get all library tracks
print "Getting all tracks in the library"
library = api.get_all_songs()
if len(library) == 0:
print 'Your library is empty'
return
# Determine which of the library tracks are to be added to the playlist
print "Checking your library for playlist tracks"
for track in library:
artist = track['artist']
title = track['title']
if [artist, title] in tracks:
to_add[tracks.index([artist, title])] = track['id']
print 'Found: %s - %s' % (artist, title)
# Now that we have the songs (either from the library or all access) create the playlist
to_add = [track_id for track_id in to_add if track_id is not None]
if (len(to_add)) > 0:
print "Adding " + str(len(to_add)) + " tracks to playlist"
playlist_id = api.create_playlist(playlist_name)
# reversing to_add here reverses the order of songs in the playlist. useful for loved tracks
#api.add_songs_to_playlist(playlist_id, to_add[::-1])
api.add_songs_to_playlist(playlist_id, to_add)
else:
print "None of the specified tracks were found so playlist was not created"
# dumps data in a json file
def put(data, filename):
try:
jsondata = simplejson.dumps(data, indent=4, skipkeys=True, sort_keys=True)
fd = open(filename, 'w')
fd.write(jsondata)
fd.close()
except:
print 'ERROR writing', filename
pass
# get data from a json file
def get(filename):
returndata = {}
try:
fd = open(filename, 'r')
text = fd.read()
fd.close()
returndata = simplejson.loads(text)
except:
print 'COULD NOT LOAD:', filename
return returndata
if __name__ == '__main__':
if len(sys.argv) != 4:
print 'usage: python lastfm_to_gmusic.py\n\
--get-lfloved <file_to_save_loved_tracks> <lastfm_user>\n\
--get-lfplaylist <file_to_save_playlist_tracks> <lastfm_playlist_url>\n\
--to-gplaylist <file_to_read_tracks> <gmusic_playlist_name>\n\
--to-gplaylist-allaccess <file_to_read_tracks> <gmusic_playlist_name>'
sys.exit(1)
# get the tracks
lftracks = gtracks = []
use_all_access = False
command = sys.argv[1]
if command == '--get-lfloved':
lastfm_key = raw_input("Lastfm API key: ").strip()
lftracks = get_lastfm_lovedtracks(sys.argv[3], lastfm_key)
elif command == '--get-lfplaylist':
lastfm_key = raw_input("Lastfm API key: ").strip()
lftracks = get_lastfm_playlist(sys.argv[3], lastfm_key)
elif command == '--to-gplaylist':
gtracks = get(sys.argv[2])
elif command == '--to-gplaylist-allaccess':
use_all_access = True
gtracks = get(sys.argv[2])
else:
print 'Invalid first argument'
sys.exit(1)
# Action after getting the tracks
if len(lftracks) > 0:
print "Got " + str(len(lftracks)) + " tracks from last.fm. Saving to the file..."
put(lftracks, sys.argv[2])
if len(gtracks) > 0:
print "Got " + str(len(gtracks)) + " tracks from the given file. Creating the google music playlist now..."
google_username = raw_input("Google username: ").strip()
google_password = raw_input("Google password: ")
make_gmusic_playlist(google_username, google_password, sys.argv[3], gtracks, use_all_access)
print 'Done!'
@AndyDaSilva52
Copy link

AndyDaSilva52 commented Aug 24, 2016

I test the script and didn't work well, so i made some changes in the code. https://gist.github.com/asgsilva/eca248ed7e79ecfbf243d80ed7d5873f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment