summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolás Reynolds <fauno@kiwwwi.com.ar>2010-10-25 15:22:02 -0300
committerNicolás Reynolds <fauno@kiwwwi.com.ar>2010-10-25 15:22:02 -0300
commit371d450b6863c240633626d000c3f03843414b71 (patch)
treef415f717ee09a62703530e0b90933d9a2ad5c769
parent3a63dcd77da6a3fb5eb6110e0784c7abc75c2a5f (diff)
Added libre.fm import/export scripts
-rwxr-xr-xbin/lastexport.py178
-rwxr-xr-xbin/libreimport.py53
-rwxr-xr-xbin/scrobble.py107
3 files changed, 338 insertions, 0 deletions
diff --git a/bin/lastexport.py b/bin/lastexport.py
new file mode 100755
index 0000000..3e8c0d8
--- /dev/null
+++ b/bin/lastexport.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python2
+#-*- coding: utf-8 -*-
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+"""
+Script for exporting tracks through audioscrobbler API.
+Usage: lastexport.py -u USER [-o OUTFILE] [-p STARTPAGE] [-s SERVER]
+"""
+
+import urllib2, urllib, sys, time
+from xml.dom import minidom
+from optparse import OptionParser
+
+def get_options(parser):
+ """ Define command line options."""
+ parser.add_option("-u", "--user", dest="username", default=None,
+ help="User name.")
+ parser.add_option("-o", "--outfile", dest="outfile", default="exported_tracks.txt",
+ help="Output file, default is exported_tracks.txt")
+ parser.add_option("-p", "--page", dest="startpage", type="int", default="1",
+ help="Page to start fetching tracks from, default is 1")
+ parser.add_option("-s", "--server", dest="server", default="last.fm",
+ help="Server to fetch track info from, default is last.fm")
+ options, args = parser.parse_args()
+
+ if not options.username:
+ sys.exit("User name not specified, see --help")
+
+ return options.username, options.outfile, options.startpage, options.server
+
+def connect_server(username, startpage):
+ """ Connect to server and get a XML page."""
+ if server == "libre.fm":
+ baseurl = 'http://alpha.libre.fm/2.0/?'
+ urlvars = dict(method='user.getrecenttracks',
+ api_key='ohaiderthisisthelastexportscript',
+ user=username,
+ page=startpage,
+ limit=200)
+
+ elif server == "last.fm":
+ baseurl = 'http://ws.audioscrobbler.com/2.0/?'
+ urlvars = dict(method='user.getrecenttracks',
+ api_key='e38cc7822bd7476fe4083e36ee69748e',
+ user=username,
+ page=startpage,
+ limit=50)
+ else:
+ sys.exit("No config exist for this server, valid servers are: last.fm, libre.fm")
+
+
+ url = baseurl + urllib.urlencode(urlvars)
+ try:
+ f = urllib2.urlopen(url)
+ except:
+ print "Failed to open page %s" % urlvars['page']
+ response = None
+ return response
+
+ response = f.read()
+ f.close()
+ return response
+
+def get_pageinfo(response):
+ """Check how many pages of tracks the user have."""
+ xmlpage = minidom.parseString(response)
+ totalpages = xmlpage.getElementsByTagName('recenttracks')[0].attributes['totalPages'].value
+ return int(totalpages)
+
+def get_tracklist(response):
+ """Read XML page and get a list of tracks and their info."""
+ xmlpage = minidom.parseString(response)
+ tracklist = xmlpage.getElementsByTagName('track')
+ return tracklist
+
+def parse_track(tracklist, i):
+ """Extract info from every track entry and output to list."""
+ track = tracklist[i].getElementsByTagName
+ try:
+ artistname = track('artist')[0].childNodes[0].data
+ except:
+ artistname = ''
+ try:
+ artistmbid = track('artist')[0].attributes['mbid'].value
+ except:
+ artistmbid = ''
+ try:
+ trackname = track('name')[0].childNodes[0].data
+ except:
+ trackname = ''
+ try:
+ trackmbid = track('mbid')[0].childNodes[0].data
+ except:
+ trackmbid = ''
+ try:
+ albumname = track('album')[0].childNodes[0].data
+ except:
+ albumname = ''
+ try:
+ albummbid = track('album')[0].attributes['mbid'].value
+ except:
+ albummbid = ''
+ try:
+ date = track('date')[0].attributes['uts'].value
+ except:
+ date = ''
+
+ output = [date, trackname, artistname, albumname, trackmbid, artistmbid, albummbid]
+
+ return output
+
+def write_tracks(trackdict, outfile, startpage, page, totalpages):
+ """Write dictionary content with all tracks to file."""
+ #create a sorted list from track dictionary.
+ sortlist = []
+ for v in trackdict.values():
+ sortlist.append(v)
+ sortlist.sort(reverse=True)
+
+ #open output file and write tracks.
+ f = open(outfile, 'a')
+ for i in sortlist:
+ #sys.stdout.write(("\t".join(trackdict[i]) + "\n").encode('utf-8'))
+ f.write(("\t".join(i) + "\n").encode('utf-8'))
+ print "Wrote page %s-%s of %s to file %s, exiting." % (startpage, page, totalpages, outfile)
+ f.close()
+
+def main(username, startpage, outfile):
+ trackdict = dict()
+ page = startpage
+ response = connect_server(username, page)
+ totalpages = get_pageinfo(response)
+ #totalpages = 2
+
+ if startpage > totalpages:
+ sys.exit("First page (%s) is higher than total pages (%s), exiting." % (startpage, totalpages))
+
+ while page <= totalpages:
+ #Skip connect if on first page, already have that one stored.
+ if page > startpage:
+ response = connect_server(username, page)
+ #If empty response, something went wrong, write tracks to file and exit.
+ if not response:
+ write_tracks(trackdict, outfile, startpage, page-1, totalpages)
+ sys.exit()
+
+ tracklist = get_tracklist(response)
+ for i in range(len(tracklist)):
+ track = parse_track(tracklist, i)
+ trackdict.setdefault(track[0], track)
+
+ if (page % 10) == 0:
+ print "Getting page %s of %s.." % (page, totalpages)
+
+ page += 1
+ time.sleep(.5)
+
+
+ write_tracks(trackdict, outfile, startpage, page-1, totalpages)
+
+if __name__ == "__main__":
+ parser = OptionParser()
+ username, outfile, startpage, server = get_options(parser)
+ main(username, startpage, outfile)
+
diff --git a/bin/libreimport.py b/bin/libreimport.py
new file mode 100755
index 0000000..e1463e2
--- /dev/null
+++ b/bin/libreimport.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python2
+
+#modified version of old import.py
+
+# Lastscrape -- recovers data from libre.fm
+# Copyright (C) 2009 Free Software Foundation, Inc
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os.path
+import sys
+sys.path.append(os.path.join(sys.path[0], '../scripts'))
+
+from datetime import datetime
+import getpass
+from scrobble import get_parser, ScrobbleServer, ScrobbleTrack
+import time
+from urllib import urlencode
+from urllib2 import urlopen
+
+
+if __name__ == '__main__':
+ usage = "%prog [-s <SERVER>] <USERNAME> <SCROBBLE DUMP>"
+ parser = get_parser(usage=usage)
+ opts,args = parser.parse_args()
+ if len(args) != 2:
+ parser.error("All arguments are required.")
+
+ username,data = args
+ server = opts.server
+ password = getpass.getpass()
+ scrobbler = ScrobbleServer(server, username, password)
+
+ n = 0
+ for line in file(data):
+ n = n + 1
+ timestamp, track, artist, album, trackmbid, artistmbid, albummbid = line.strip("\n").split("\t")
+ #submission protocol doesnt specify artist/album mbid, so we dont send them
+ scrobbler.add_track(ScrobbleTrack(timestamp, track, artist, album, trackmbid))
+ print "%d: Adding to post %s playing %s" % (n, artist, track)
+ scrobbler.submit()
diff --git a/bin/scrobble.py b/bin/scrobble.py
new file mode 100755
index 0000000..428e668
--- /dev/null
+++ b/bin/scrobble.py
@@ -0,0 +1,107 @@
+#modified version of old gobble.py
+try:
+ import hashlib
+ md5hash = hashlib.md5
+except ImportError:
+ import md5
+ md5hash = md5.new
+from optparse import OptionParser
+import time
+from urllib import urlencode
+from urllib2 import urlopen
+
+
+class ScrobbleException(Exception):
+
+ pass
+
+
+class ScrobbleServer(object):
+
+ def __init__(self, server_name, username, password, client_code='imp'):
+ if server_name[:7] != "http://":
+ server_name = "http://%s" % (server_name,)
+ self.client_code = client_code
+ self.name = server_name
+ self.password = password
+ self.post_data = []
+ self.session_id = None
+ self.submit_url = None
+ self.username = username
+ self._handshake()
+
+
+ def _handshake(self):
+ timestamp = int(time.time())
+ token = (md5hash(md5hash(self.password).hexdigest()
+ + str(timestamp)).hexdigest())
+ auth_url = "%s/?hs=true&p=1.2&u=%s&t=%d&a=%s&c=%s" % (self.name,
+ self.username,
+ timestamp,
+ token,
+ self.client_code)
+ response = urlopen(auth_url).read()
+ lines = response.split("\n")
+ if lines[0] != "OK":
+ raise ScrobbleException("Server returned: %s" % (response,))
+ self.session_id = lines[1]
+ self.submit_url = lines[3]
+
+ def submit(self):
+ if len(self.post_data) == 0:
+ return
+ i = 0
+ data = []
+ for track in self.post_data:
+ data += track.get_tuples(i)
+ i += 1
+ data += [('s', self.session_id)]
+ response = urlopen(self.submit_url, urlencode(data)).read()
+ if response != "OK\n":
+ raise ScrobbleException("Server returned: %s" % (response,))
+ self.post_data = []
+ time.sleep(1)
+
+ def add_track(self, scrobble_track):
+ i = len(self.post_data)
+ if i > 49:
+ self.submit()
+ i = 0
+ self.post_data.append(scrobble_track)
+
+
+class ScrobbleTrack(object):
+
+ def __init__(self, timestamp, trackname, artistname, albumname=None, \
+ trackmbid=None, tracklength=None, tracknumber=None):
+ self.timestamp = timestamp
+ self.trackname = trackname
+ self.artistname = artistname
+ self.albumname = albumname
+ self.trackmbid = trackmbid
+ self.tracklength = tracklength
+ self.tracknumber = tracknumber
+
+ def get_tuples(self, i):
+ #timestamp = str(int(time.mktime(self.timestamp.utctimetuple())))
+ data = []
+ data += [('i[%d]' % i, self.timestamp), ('t[%d]' % i, self.trackname),
+ ('a[%d]' % i, self.artistname)]
+ if self.albumname is not None:
+ data.append(('b[%d]' % i, self.albumname))
+ if self.trackmbid is not None:
+ data.append(('m[%d]' % i, self.trackmbid))
+ if self.tracklength is not None:
+ data.append(('l[%d]' % i, self.tracklength))
+ if self.tracknumber is not None:
+ data.append(('n[%d]' % i, self.tracknumber))
+ return data
+
+
+def get_parser(usage):
+ parser = OptionParser(usage=usage)
+ parser.add_option('-s', '--server',
+ help="Server to submit to. Defaults to"
+ " 'turtle.libre.fm'.")
+ parser.set_defaults(server='turtle.libre.fm')
+ return parser