track.scrobble support
[fmthings:lasttolibre.git] / scrobble2.py
1 try:
2     import hashlib
3     md5hash = hashlib.md5
4 except ImportError:
5     import md5
6     md5hash = md5.new
7 from optparse import OptionParser
8 import time
9 import json
10 from urllib import urlencode
11 from urllib2 import Request, urlopen, URLError, HTTPError
12
13
14 class ScrobbleException(Exception):
15
16     pass
17
18
19 class ScrobbleServer(object):
20
21     def __init__(self, server_name, sk, api_key='thisisthelibreimport2pythonthing'):
22         if server_name[:7] != "http://":
23             server_name = "http://%s" % (server_name,)
24         self.api_key = api_key
25         self.name = server_name
26         self.post_data = []
27         self.sk = sk
28         self.submit_url = server_name + '/2.0/'
29
30     def submit(self, sleep_func=time.sleep):
31         if len(self.post_data) == 0:
32             return
33         i = 0
34         data = []
35         for track in self.post_data:
36             data += track.get_tuples(i)
37             i += 1
38         data += [('method', 'track.scrobble'),('sk', self.sk),('api_key', self.api_key),('format', 'json')]
39         last_error = None
40         for timeout in (1, 2, 4, 8, 16, 32):
41             try:
42                 req = Request(self.submit_url, urlencode(data))
43                 response = urlopen(req)
44             except (URLError, HTTPError), e:
45                 last_error = str(e)
46                 print 'Scrobbling error: %s, will retry in %ss' % (last_error, timeout)
47             else:
48                 jsonresponse = json.load(response)
49
50                 if jsonresponse['scrobbles']: # we're just checking if key exists
51                     break
52                 else:
53                     last_error = 'Bad server response: %s' % response
54                     print '%s, will retry in %ss' % (last_error, timeout)
55             time.sleep(timeout)
56         else:
57             raise ScrobbleException('Cannot scrobble after multiple retries. Last error: %s' % last_error)
58
59         self.post_data = []
60         sleep_func(1)
61
62     def add_track(self, scrobble_track, sleep_func=time.sleep):
63         i = len(self.post_data)
64         if i > 49:
65             self.submit(sleep_func)
66             i = 0
67         self.post_data.append(scrobble_track)
68
69
70 class ScrobbleTrack(object):
71
72     def __init__(self, timestamp, trackname, artistname, albumname=None,
73                  trackmbid=None, tracklength=None, tracknumber=None):
74         self.timestamp = timestamp
75         self.trackname = trackname
76         self.artistname = artistname
77         self.albumname = albumname
78         self.trackmbid = trackmbid
79         self.tracklength = tracklength
80         self.tracknumber = tracknumber
81
82     def get_tuples(self, i):
83         data = []
84         data += [('timestamp[%d]' % i, self.timestamp), ('track[%d]' % i, self.trackname),
85                  ('artist[%d]' % i, self.artistname)]
86         if self.albumname is not None:
87             data.append(('album[%d]' % i, self.albumname))
88         if self.trackmbid is not None:
89             data.append(('mbid[%d]' % i, self.trackmbid))
90         if self.tracklength is not None:
91             data.append(('duration[%d]' % i, self.tracklength))
92         if self.tracknumber is not None:
93             data.append(('tracknumber[%d]' % i, self.tracknumber))
94         return data
95
96
97 def get_parser(usage):
98     parser = OptionParser(usage=usage)
99     parser.add_option('-s', '--server',
100                       help="Server to submit to.  Defaults to"
101                            " 'libre.fm'.")
102     parser.set_defaults(server='libre.fm')
103     return parser