Remove old file.
[clinton/xbmc-groove.git] / resources / lib / GroovesharkAPI.py
CommitLineData
f95afae7 1import socket, hmac, urllib2, pprint, md5, os, pickle, tempfile, time, re, groovesharkAccess
1413d357 2
f95afae7 3SESSION_EXPIRY = 1209600 # 2 weeks
44dcc6f4 4
1413d357 5# GrooveAPI constants
6842a53b 6THUMB_URL = 'http://beta.grooveshark.com/static/amazonart/m'
1413d357 7SONG_LIMIT = 25
8ALBUM_LIMIT = 15
9ARTIST_LIMIT = 15
7ce01be6 10SONG_SUFFIX = '.mp3'
1413d357 11
1413d357 12# Main API
13class GrooveAPI:
14
f95afae7 15 _ip = '0.0.0.0'
16 _country = ''
17 _sessionID = ''
18 _userID = 0
19 _lastSessionTime = 0
20 _lastStreamKey = ''
21 _lastStreamServerID = ''
1413d357 22
23 # Constructor
7ce01be6 24 def __init__(self):
25
1413d357 26 import simplejson
27 self.simplejson = simplejson
28 socket.setdefaulttimeout(40)
f95afae7 29 self.cacheDir = os.path.join(tempfile.gettempdir(), 'groovesharkapi')
7ce01be6 30 if os.path.isdir(self.cacheDir) == False:
31 os.makedirs(self.cacheDir)
32 print "Made " + self.cacheDir
44dcc6f4 33 self._getSavedSession()
f95afae7 34 # session ids last 2 weeks
35 if self._sessionID == '' or time.time()- self._lastSessionTime >= SESSION_EXPIRY:
36 self._sessionID = self._getSessionID()
37 self._ip = self._getIP()
38 self._country = self._getCountry()
39 if self._sessionID == '':
1413d357 40 raise StandardError('Failed to get session id')
41 else:
f95afae7 42 print "New GrooveAPI session id: " + self._sessionID
44dcc6f4 43 self._setSavedSession()
0e7dbdf7 44
f95afae7 45 # Call to API
46 def _callRemote(self, method, params):
47 self._setParams(params)
48 return groovesharkAccess.callRemote(method, self._sessionID)
1413d357 49
1413d357 50 # Get a session id
51 def _getSessionID(self):
52 params = {}
53 result = self._callRemote('startSession', params)
f95afae7 54 self._lastSessionTime = time.time()
55 if 'result' in result:
56 return result['result']['sessionID']
57 else:
58 return ''
1413d357 59
44dcc6f4 60 def _getSavedSession(self):
7ce01be6 61 path = os.path.join(self.cacheDir, 'session.dmp')
1413d357 62 try:
63 f = open(path, 'rb')
0e7dbdf7 64 session = pickle.load(f)
f95afae7 65 self._sessionID = session['sessionID']
66 self._lastSessionTime = session['lastSessionTime']
67 self._userID = session['userID']
68 self._ip = session['ip']
69 self._country = session['country']
1413d357 70 f.close()
71 except:
f95afae7 72 self._sessionID = ''
73 self._lastSessionTime = 0
74 self._userID = 0
75 self._ip = '0.0.0.0'
76 self._country = ''
1413d357 77 pass
1413d357 78
44dcc6f4 79 def _setSavedSession(self):
1413d357 80 try:
7ce01be6 81 # Create the directory if it doesn't exist.
82 if not os.path.exists(self.cacheDir):
83 os.makedirs(self.cacheDir)
84 path = os.path.join(self.cacheDir, 'session.dmp')
1413d357 85 f = open(path, 'wb')
f95afae7 86 session = { 'sessionID' : self._sessionID, 'lastSessionTime' : self._lastSessionTime, 'userID': self._userID, 'ip' : self._ip, 'country' : self._country }
0e7dbdf7 87 pickle.dump(session, f, protocol=pickle.HIGHEST_PROTOCOL)
1413d357 88 f.close()
89 except:
44dcc6f4 90 print "An error occurred during save session"
1413d357 91 pass
f95afae7 92
93 def _setParams(self, params):
94 try:
95 # Create the directory if it doesn't exist.
96 if not os.path.exists(self.cacheDir):
97 os.makedirs(self.cacheDir)
98 path = os.path.join(self.cacheDir, 'params.dmp')
99 f = open(path, 'wb')
100 pickle.dump(params, f, protocol=pickle.HIGHEST_PROTOCOL)
101 f.close()
102 except:
103 print "An error occurred during save params"
104 pass
1413d357 105
f95afae7 106 # Get IP
107 def _getIP(self):
108 try:
109 myip = urllib2.urlopen('http://whatismyip.org').read()
110 if re.match("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$", myip):
111 print "IP is " + myip
112 return myip
113 except:
114 return '0.0.0.0'
1413d357 115
f95afae7 116 # Get country
117 def _getCountry(self):
118 params = { 'ip' : self._ip }
119 response = self._callRemote("getCountry", params)
120 return response['result']
121
122 # Get userid from name
123 def _getUserIDFromUsername(self, username):
124 result = self._callRemote('getUserIDFromUsername', {'username' : username})
125 if 'result' in result and result['result']['UserID'] > 0:
126 return result['result']['UserID']
127 else:
128 return 0
129
1413d357 130 # Authenticates the user for current API session
f95afae7 131 def _authenticate(self, login, password):
132 md5pwd = md5.new(password).hexdigest()
133 params = {'login': login, 'password': md5pwd}
134
135 result = self._callRemote('authenticate', params)
136 uid = result['result']['UserID']
137 if (uid > 0):
138 return uid
139 else:
140 return 0
141
142 # Check the service
143 def pingService(self,):
144 result = self._callRemote('pingService', {});
145 if 'result' in result and result['result'] != '':
146 return True
147 else:
148 return False
1413d357 149
150 # Login
151 def login(self, username, password):
f95afae7 152 if self._userID <= 0:
44dcc6f4 153 # Check cache
154 self._getSavedSession()
f95afae7 155 if self._userID <= 0:
156 self._userID = self._authenticate(username, password)
157 if self._userID > 0:
44dcc6f4 158 self._setSavedSession()
f95afae7 159 return self._userID
1413d357 160
161 # Logs the user out
162 def logout(self):
f95afae7 163 result = self._callRemote('logout', {'sessionID' : self._sessionID})
44dcc6f4 164 if 'result' in result and result['result']['success'] == True:
f95afae7 165 self._userID = 0
44dcc6f4 166 self._setSavedSession()
167 return True
168 return False
1413d357 169
f95afae7 170 # Gets a stream key and host to get song content
171 def getSubscriberStreamKey(self, songID):
172 params = { "songID": songID, "country": self._country }
173 response = self._callRemote("getSubscriberStreamKey", params)
174 try:
175 self._lastStreamKey = response["result"]["StreamKey"]
176 self._lastStreamServerID = response["result"]["StreamServerID"]
177 res = response["result"]
178 return res
179 except:
180 return False
181
1413d357 182 # Search for albums
183 def getArtistSearchResults(self, query, limit=ARTIST_LIMIT):
184 result = self._callRemote('getArtistSearchResults', {'query' : query,'limit' : limit})
185 if 'result' in result:
186 return self._parseArtists(result)
187 else:
188 return []
189
190 # Search for albums
191 def getAlbumSearchResults(self, query, limit=ALBUM_LIMIT):
192 result = self._callRemote('getAlbumSearchResults', {'query' : query,'limit' : limit})
193 if 'result' in result:
194 return self._parseAlbums(result)
195 else:
196 return []
197
198 # Search for songs
199 def getSongSearchResults(self, query, limit=SONG_LIMIT):
f95afae7 200 result = self._callRemote('getSongSearchResults', {'query' : query, 'country' : self._country, 'limit' : limit})
1413d357 201 if 'result' in result:
202 return self._parseSongs(result)
203 else:
204 return []
7ce01be6 205
206 # Get artists albums
207 def getArtistAlbums(self, artistID, limit=ALBUM_LIMIT):
208 result = self._callRemote('getArtistAlbums', {'artistID' : artistID})
209 if 'result' in result:
210 return self._parseAlbums(result, limit)
211 else:
212 return []
213
214 # Get album songs
215 def getAlbumSongs(self, albumID, limit=SONG_LIMIT):
f95afae7 216 result = self._callRemote('getAlbumSongs', {'albumID' : albumID, 'limit' : limit})
7ce01be6 217 if 'result' in result:
218 return self._parseSongs(result)
219 else:
220 return []
97289139 221
222 # Get artist's popular songs
223 def getArtistPopularSongs(self, artistID, limit = SONG_LIMIT):
224 result = self._callRemote('getArtistPopularSongs', {'artistID' : artistID})
225 if 'result' in result:
226 return self._parseSongs(result, limit)
227 else:
228 return []
229
1413d357 230 # Gets the popular songs
231 def getPopularSongsToday(self, limit=SONG_LIMIT):
232 result = self._callRemote('getPopularSongsToday', {'limit' : limit})
233 if 'result' in result:
7ce01be6 234 # Note limit is broken in the Grooveshark getPopularSongsToday method
235 return self._parseSongs(result, limit)
1413d357 236 else:
237 return []
238
239 # Gets the favorite songs of the logged-in user
240 def getUserFavoriteSongs(self):
f95afae7 241 if (self._userID == 0):
1413d357 242 return [];
f95afae7 243 result = self._callRemote('getUserFavoriteSongs', {})
1413d357 244 if 'result' in result:
245 return self._parseSongs(result)
246 else:
247 return []
7ce01be6 248
f95afae7 249 # Get song info
250 def getSongsInfo(self, songIDs):
251 result = self._callRemote('getSongsInfo', {'songIDs' : songIDs})
1413d357 252 if 'result' in result and 'SongID' in result['result']:
253 info = result['result']
254 if 'CoverArtFilename' in info and info['CoverArtFilename'] != None:
255 info['CoverArtFilename'] = THUMB_URL+info['CoverArtFilename'].encode('ascii', 'ignore')
256 else:
7ce01be6 257 info['CoverArtFilename'] = 'None'
1413d357 258 return info
259 else:
7ce01be6 260 return 'None'
f95afae7 261
262 # Add song to user favorites
263 def addUserFavoriteSong(self, songID):
264 if (self._userID == 0):
265 return False;
266 result = self._callRemote('addUserFavoriteSong', {'songID' : songID})
267 return result['result']['success']
268
269 # Remove songs from user favorites
270 def removeUserFavoriteSongs(self, songIDs):
271 if (self._userID == 0):
272 return False;
273 result = self._callRemote('removeUserFavoriteSongs', {'songIDs' : songIDs})
274 return result['result']['success']
1413d357 275
276 # Gets the playlists of the logged-in user
277 def getUserPlaylists(self):
f95afae7 278 if (self._userID == 0):
1413d357 279 return [];
f95afae7 280 result = self._callRemote('getUserPlaylists', {})
1413d357 281 if 'result' in result:
282 return self._parsePlaylists(result)
283 else:
284 return []
86f629ea 285
86f629ea 286 # Gets the playlists of the logged-in user
f95afae7 287 def getUserPlaylistsByUsername(self, username):
86f629ea 288 userID = self._getUserIDFromUsername(username)
289 if (userID > 0):
f95afae7 290 result = self._callRemote('getUserPlaylistsByUserID', {'userID' : userID})
86f629ea 291 if 'result' in result and result['result']['playlists'] != None:
292 playlists = result['result']['playlists']
293 return self._parsePlaylists(playlists)
294 else:
295 return []
1413d357 296
297 # Creates a playlist with songs
298 def createPlaylist(self, name, songIDs):
f95afae7 299 result = self._callRemote('createPlaylist', {'name' : name, 'songIDs' : songIDs})
40ac5d22 300 if 'result' in result and result['result']['success'] == True:
7ce01be6 301 return result['result']['playlistID']
1413d357 302 elif 'errors' in result:
303 return 0
304
305 # Sets the songs for a playlist
306 def setPlaylistSongs(self, playlistID, songIDs):
f95afae7 307 result = self._callRemote('setPlaylistSongs', {'playlistID' : playlistID, 'songIDs' : songIDs})
40ac5d22 308 if 'result' in result and result['result']['success'] == True:
7ce01be6 309 return True
1413d357 310 else:
7ce01be6 311 return False
1413d357 312
313 # Gets the songs of a playlist
314 def getPlaylistSongs(self, playlistID):
315 result = self._callRemote('getPlaylistSongs', {'playlistID' : playlistID});
316 if 'result' in result:
317 return self._parseSongs(result)
318 else:
319 return []
f95afae7 320
321
322 def playlistDelete(self, playlistId):
323 result = self._callRemote("deletePlaylist", {"playlistID": playlistId})
324 if 'fault' in result:
325 return 0
326 else:
327 return 1
7ce01be6 328
f95afae7 329 def playlistRename(self, playlistId, name):
330 result = self._callRemote("renamePlaylist", {"playlistID": playlistId, "name": name})
331 if 'fault' in result:
332 return 0
7ce01be6 333 else:
f95afae7 334 return 1
335
336 def getSimilarArtists(self, artistId, limit):
337 items = self._callRemote("getSimilarArtists", {"artistID": artistId, "limit": limit})
338 if 'result' in items:
339 i = 0
340 list = []
341 artists = items['result']['artists']
342 while(i < len(artists)):
343 s = artists[i]
344 list.append([s['artistName'].encode('ascii', 'ignore'),\
345 s['artistID']])
346 i = i + 1
347 return list
348 else:
349 return []
1413d357 350
f95afae7 351 # After 30s play time
352 def markStreamKeyOver30Secs(self):
353 params = { "streamKey" : self._lastStreamKey, "streamServerID" : self._lastStreamServerID }
354 self._callRemote("markStreamKeyOver30Secs", params)
355
356 # Song complete
357 def markSongComplete(self, songid):
358 params = { "songID" : songid, "streamKey" : self._lastStreamKey, "streamServerID" : self._lastStreamServerID }
359 self._callRemote("markSongComplete", params)
360
1413d357 361 # Extract song data
7ce01be6 362 def _parseSongs(self, items, limit=0):
1413d357 363 if 'result' in items:
364 i = 0
365 list = []
97289139 366 index = ''
367 l = -1
368 try:
369 if 'songs' in items['result'][0]:
370 l = len(items['result'][0]['songs'])
371 index = 'songs[]'
372 except: pass
373 try:
374 if l < 0 and 'songs' in items['result']:
375 l = len(items['result']['songs'])
376 index = 'songs'
377 except: pass
378 try:
379 if l < 0 and 'song' in items['result']:
380 l = 1
381 index = 'song'
382 except: pass
383 try:
384 if l < 0:
385 l = len(items['result'])
386 except: pass
387
7ce01be6 388 if limit > 0 and l > limit:
389 l = limit
1413d357 390 while(i < l):
97289139 391 if index == 'songs[]':
392 s = items['result'][0]['songs'][i]
393 elif index == 'songs':
1413d357 394 s = items['result'][index][i]
395 elif index == 'song':
396 s = items['result'][index]
397 else:
398 s = items['result'][i]
399 if 'CoverArtFilename' not in s:
f95afae7 400 info = self.getSongsInfo(s['SongID'])
1413d357 401 coverart = info['CoverArtFilename']
402 elif s['CoverArtFilename'] != None:
403 coverart = THUMB_URL+s['CoverArtFilename'].encode('ascii', 'ignore')
404 else:
7ce01be6 405 coverart = 'None'
1413d357 406 list.append([s['SongName'].encode('ascii', 'ignore'),\
407 s['SongID'],\
408 s['AlbumName'].encode('ascii', 'ignore'),\
409 s['AlbumID'],\
410 s['ArtistName'].encode('ascii', 'ignore'),\
411 s['ArtistID'],\
412 coverart])
413 i = i + 1
414 return list
415 else:
416 return []
417
418 # Extract artist data
419 def _parseArtists(self, items):
420 if 'result' in items:
421 i = 0
422 list = []
423 artists = items['result']['artists']
424 while(i < len(artists)):
425 s = artists[i]
426 list.append([s['ArtistName'].encode('ascii', 'ignore'),\
427 s['ArtistID']])
428 i = i + 1
429 return list
430 else:
431 return []
432
433 # Extract album data
7ce01be6 434 def _parseAlbums(self, items, limit=0):
1413d357 435 if 'result' in items:
436 i = 0
437 list = []
7ce01be6 438 try:
439 albums = items['result']['albums']
440 except:
441 res = items['result'][0]
442 albums = res['albums']
443 l = len(albums)
444 if limit > 0 and l > limit:
445 l = limit
446 while(i < l):
1413d357 447 s = albums[i]
448 if 'CoverArtFilename' in s and s['CoverArtFilename'] != None:
449 coverart = THUMB_URL+s['CoverArtFilename'].encode('ascii', 'ignore')
450 else:
7ce01be6 451 coverart = 'None'
1413d357 452 list.append([s['ArtistName'].encode('ascii', 'ignore'),\
453 s['ArtistID'],\
454 s['AlbumName'].encode('ascii', 'ignore'),\
455 s['AlbumID'],\
456 coverart])
457 i = i + 1
458 return list
459 else:
460 return []
461
462 def _parsePlaylists(self, items):
86f629ea 463 i = 0
464 list = []
1413d357 465 if 'result' in items:
f95afae7 466 playlists = items['result']['playlists']
86f629ea 467 elif len(items) > 0:
468 playlists = items
1413d357 469 else:
470 return []
f95afae7 471
86f629ea 472 while (i < len(playlists)):
473 s = playlists[i]
f95afae7 474 list.append([str(s['PlaylistName']).encode('ascii', 'ignore'), s['PlaylistID']])
86f629ea 475 i = i + 1
476 return list
1413d357 477
478# Test
7ce01be6 479#import sys
480#res = []
481#groovesharkApi = GrooveAPI()
482#res = groovesharkApi.pingService()
44dcc6f4 483#res = groovesharkApi.login(sys.argv[1], sys.argv[2])
f95afae7 484#songIds = []
485#songIds.append('28645456')
486#songIds.append('26579347')
487#res=groovesharkApi.playlistRename(58197714, 'renamed playlist2')
e6f8730b 488#res = groovesharkApi.createPlaylist("Test", songIDs)
f95afae7 489#res = groovesharkApi.setPlaylistSongs('58197714',songIds)
e6f8730b 490#pprint.pprint(res)
f95afae7 491#res = groovesharkApi.getPlaylistSongs('58197714')
1413d357 492#res = groovesharkApi.getSongSearchResults('jimmy jazz', 3)
7ce01be6 493#res = groovesharkApi.getPopularSongsToday(3)
494#res = groovesharkApi.getSongURLFromSongID('26579347')
1413d357 495#res = groovesharkApi.getAlbumSearchResults('london calling', 3)
7ce01be6 496#res = groovesharkApi.getArtistAlbums('52283')
1413d357 497#res = groovesharkApi.getArtistSearchResults('the clash', 3)
498#res = groovesharkApi.getUserFavoriteSongs()
44dcc6f4 499#res = groovesharkApi.getUserPlaylists()
f95afae7 500#res = groovesharkApi.getSongInfos('27425375')
1413d357 501#res = groovesharkApi.getPlaylistSongs(40902662)
40ac5d22 502#res = groovesharkApi.addUserFavoriteSong('27425375')
44dcc6f4 503#res = groovesharkApi.logout()
f95afae7 504#res = groovesharkApi.getUserPlaylistsByUsername('stephendenham')
97289139 505#res = groovesharkApi.getArtistPopularSongs('3707')
7ce01be6 506#
507#pprint.pprint(res)