1 import urllib2
, md5
, unicodedata
, re
, os
, traceback
, sys
, pickle
, socket
2 from operator
import itemgetter
, attrgetter
4 class LoginTokensExceededError(Exception):
6 self
.value
= 'You have created to many tokens. Only 12 are allowed'
8 return repr(self
.value
)
10 class LoginUnknownError(Exception):
12 self
.value
= 'Unable to get a new session ID. Wait a few minutes and try again'
14 return repr(self
.value
)
16 class SessionIDTryAgainError(Exception):
18 self
.value
= 'Unable to get a new session ID. Wait a few minutes and try again'
20 return repr(self
.value
)
23 def __init__(self
, enableDebug
= False, isXbox
= False):
25 import simplejson_xbox
26 self
.simplejson
= simplejson_xbox
27 print 'GrooveShark: Initialized as XBOX script'
30 self
.simplejson
= simplejson
32 socket
.setdefaulttimeout(timeout
)
33 self
.enableDebug
= enableDebug
39 self
.songIDsAlreadySeen
= []
40 self
.recentArtists
= []
41 self
.rootDir
= os
.getcwd()
42 self
.sessionID
= self
.getSavedSession()
43 self
.debug('Saved sessionID: ' + self
.sessionID
)
44 self
.sessionID
= self
.getSessionFromAPI()
45 self
.debug('API sessionID: ' + self
.sessionID
)
46 if self
.sessionID
== '':
47 self
.sessionID
= self
.startSession()
48 self
.debug('Start() sessionID: ' + self
.sessionID
)
49 if self
.sessionID
== '':
50 self
.debug('Could not get a sessionID. Try again in a few minutes')
51 raise SessionIDTryAgainError()
55 self
.debug('sessionID: ' + self
.sessionID
)
59 if self
.loggedIn
== 1:
65 if self
.enableDebug
== True:
68 def getSavedSession(self
):
70 path
= os
.path
.join(self
.rootDir
, 'data', 'session.txt')
74 sessionID
= pickle
.load(f
)
82 def saveSession(self
):
84 dir = os
.path
.join(self
.rootDir
, 'data')
85 # Create the 'data' directory if it doesn't exist.
86 if not os
.path
.exists(dir):
88 path
= os
.path
.join(dir, 'session.txt')
90 pickle
.dump(self
.sessionID
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
93 print 'There was an error while saving the session pickle (%s)' % e
96 print "An unknown error occured during save session: " + str(sys
.exc_info()[0])
99 def saveSettings(self
):
101 dir = os
.path
.join(self
.rootDir
, 'data')
102 # Create the 'data' directory if it doesn't exist.
103 if not os
.path
.exists(dir):
105 path
= os
.path
.join(dir, 'settings1.txt')
107 pickle
.dump(self
.settings
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
110 print 'There was an error while saving the settings pickle (%s)' % e
113 print "An unknown error occured during save settings\n"
116 def callRemote(self
, method
, params
={}):
117 data
= {'header': {'sessionID': self
.sessionID
}, 'method': method
, 'parameters': params
}
118 #data = {'header': {'sessionID': None}, 'method': method, 'parameters': params}
119 data
= self
.simplejson
.dumps(data
)
120 #proxy_support = urllib2.ProxyHandler({"http" : "http://wwwproxy.kom.aau.dk:3128"})
121 ## build a new opener with proxy details
122 #opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)
124 #urllib2.install_opener(opener)
126 req
= urllib2
.Request("http://api.grooveshark.com/ws/1.0/?json")
127 req
.add_header('Host', 'api.grooveshark.com')
128 req
.add_header('Content-type', 'text/json')
129 req
.add_header('Content-length', str(len(data
)))
131 response
= urllib2
.urlopen(req
)
132 result
= response
.read()
135 result
= self
.simplejson
.loads(result
)
136 if 'fault' in result
:
142 def startSession(self
):
143 response
= urllib2
.urlopen("http://www.moovida.com/services/grooveshark/session_start")
144 result
= response
.read()
145 result
= self
.simplejson
.loads(result
)
147 if 'fault' in result
:
150 return result
['header']['sessionID']
152 def sessionDestroy(self
):
153 return self
.callRemote("session.destroy")
155 def getSessionFromAPI(self
):
156 result
= self
.callRemote("session.get")
157 if 'fault' in result
:
160 return result
['header']['sessionID']
162 def getStreamURL(self
, songID
):
163 result
= self
.callRemote("song.getStreamUrlEx", {"songID": songID
})
164 if 'result' in result
:
165 return result
['result']['url']
169 def createUserAuthToken(self
, username
, password
):
170 hashpass
= md5
.new(password
).hexdigest()
171 hashpass
= username
+ hashpass
172 hashpass
= md5
.new(hashpass
).hexdigest()
173 result
= self
.callRemote("session.createUserAuthToken", {"username": username
, "hashpass": hashpass
})
174 if 'result' in result
:
175 return result
['result']['token'], result
['result']['userID']
176 elif 'fault' in result
:
177 if result
['fault']['code'] == 256:
178 return -1 # Exceeded the number of allowed tokens. Should not happen
180 return -2 # Unknown error
182 return -2 # Unknown error
184 def destroyUserAuthToken(self
, token
):
185 self
.callRemote("session.destroyAuthToken", {"token": token
})
187 def loginViaAuthToken(self
, token
):
188 result
= self
.callRemote("session.loginViaAuthToken", {"token": token
})
189 self
.destroyUserAuthToken(token
)
190 if 'result' in result
:
191 self
.userID
= result
['result']['userID']
192 return result
['result']['userID']
196 def login(self
, username
, password
):
197 if self
.loggedIn
== 1:
199 result
= self
.createUserAuthToken(username
, password
)
201 raise LoginTokensExceededError()
203 raise LoginUnknownError()
205 self
.token
= result
[0]
206 self
.debug('Token:' + self
.token
)
207 self
.userId
= self
.loginViaAuthToken(self
.token
)
209 raise LoginUnknownError()
214 def loginExt(self
, username
, password
):
215 if self
.loggedIn
== 1:
217 token
= md5
.new(username
.lower() + md5
.new(password
).hexdigest()).hexdigest()
218 result
= self
.callRemote("session.loginExt", {"username": username
, "token": token
})
219 if 'result' in result
:
220 if 'userID' in result
['result']:
222 self
.userId
= result
['result']['userID']
223 return result
['result']['userID']
228 def loginBasic(self
, username
, password
):
229 if self
.loggedIn
== 1:
231 result
= self
.callRemote("session.login", {"username": username
, "password": password
})
232 if 'result' in result
:
233 if 'userID' in result
['result']:
235 self
.userId
= result
['result']['userID']
236 return result
['result']['userID']
240 def loggedInStatus(self
):
244 self
.callRemote("session.logout", {})
247 def getSongInfo(self
, songID
):
248 return self
.callRemote("song.about", {"songID": songID
})['result']['song']
250 def userGetFavoriteSongs(self
, userID
):
251 result
= self
.callRemote("user.getFavoriteSongs", {"userID": userID
})
252 list = self
.parseSongs(result
)
255 def userGetPlaylists(self
, limit
=25):
256 if self
.loggedIn
== 1:
257 result
= self
.callRemote("user.getPlaylists", {"userID": self
.userId
, "limit": limit
})
258 if 'result' in result
:
259 playlists
= result
['result']['playlists']
264 while(i
< len(playlists
)):
266 list.append([p
['playlistName'].encode('ascii', 'ignore'), p
['playlistID']])
268 return sorted(list, key
=itemgetter(0))
272 def playlistCreate(self
, name
, about
):
273 if self
.loggedIn
== 1:
274 result
= self
.callRemote("playlist.create", {"name": name
, "about": about
})
275 if 'result' in result
:
276 return result
['result']['playlistID']
282 def playlistGetSongs(self
, playlistId
, limit
=25):
283 result
= self
.callRemote("playlist.getSongs", {"playlistID": playlistId
})
284 list = self
.parseSongs(result
)
287 def playlistDelete(self
, playlistId
):
288 if self
.loggedIn
== 1:
289 return self
.callRemote("playlist.delete", {"playlistID": playlistId
})
291 def playlistRename(self
, playlistId
, name
):
292 if self
.loggedIn
== 1:
293 result
= self
.callRemote("playlist.rename", {"playlistID": playlistId
, "name": name
})
294 if 'fault' in result
:
301 def playlistClearSongs(self
, playlistId
):
302 if self
.loggedIn
== 1:
303 return self
.callRemote("playlist.clearSongs", {"playlistID": playlistId
})
305 def playlistAddSong(self
, playlistId
, songId
, position
):
306 if self
.loggedIn
== 1:
307 result
= self
.callRemote("playlist.addSong", {"playlistID": playlistId
, "songID": songId
, "position": position
})
308 if 'fault' in result
:
315 def playlistReplace(self
, playlistId
, songIds
):
316 if self
.loggedIn
== 1:
317 result
= self
.callRemote("playlist.replace", {"playlistID": playlistId
, "songIDs": songIds
})
318 if 'fault' in result
:
325 def autoplayStartWithArtistIDs(self
, artistIds
):
326 result
= self
.callRemote("autoplay.startWithArtistIDs", {"artistIDs": artistIds
})
327 if 'fault' in result
:
328 self
.radioEnabled
= 0
331 self
.radioEnabled
= 1
334 def autoplayStart(self
, songIds
):
335 result
= self
.callRemote("autoplay.start", {"songIDs": songIds
})
336 if 'fault' in result
:
337 self
.radioEnabled
= 0
340 self
.radioEnabled
= 1
343 def autoplayStop(self
):
344 result
= self
.callRemote("autoplay.stop", {})
345 if 'fault' in result
:
346 self
.radioEnabled
= 1
349 self
.radioEnabled
= 0
352 def autoplayGetNextSongEx(self
, seedArtists
= [], frowns
= [], songIDsAlreadySeen
= [], recentArtists
= []):
353 result
= self
.callRemote("autoplay.getNextSongEx", {"seedArtists": seedArtists
, "frowns": frowns
, "songIDsAlreadySeen": songIDsAlreadySeen
, "recentArtists": recentArtists
})
354 if 'fault' in result
:
359 def radioGetNextSong(self
):
360 if self
.seedArtists
== []:
363 result
= self
.autoplayGetNextSongEx(self
.seedArtists
, self
.frowns
, self
.songIDsAlreadySeen
, self
.recentArtists
)
365 if 'fault' in result
:
368 song
= self
.parseSongs(result
)
369 self
.radioAlreadySeen(song
[0][1])
372 def radioFrown(self
, songId
):
373 self
.frown
.append(songId
)
375 def radioAlreadySeen(self
, songId
):
376 self
.songIDsAlreadySeen
.append(songId
)
378 def radioAddArtist(self
, artistId
):
379 self
.seedArtists
.append(artistId
)
381 def radioStart(self
, artists
= [], frowns
= []):
382 for artist
in artists
:
383 self
.seedArtists
.append(artist
)
384 for artist
in frowns
:
385 self
.frowns
.append(artist
)
386 if self
.autoplayStartWithArtistIDs(self
.seedArtists
) == 1:
387 self
.radioEnabled
= 1
390 self
.radioEnabled
= 0
394 self
.seedArtists
= []
396 self
.songIDsAlreadySeen
= []
397 self
.recentArtists
= []
398 self
.radioEnabled
= 0
400 def radioTurnedOn(self
):
401 return self
.radioEnabled
403 def favoriteSong(self
, songID
):
404 return self
.callRemote("song.favorite", {"songID": songID
})
406 def unfavoriteSong(self
, songID
):
407 return self
.callRemote("song.unfavorite", {"songID": songID
})
409 def getMethods(self
):
410 return self
.callRemote("service.getMethods")
412 def searchSongsExactMatch(self
, songName
, artistName
, albumName
):
413 result
= self
.callRemote("search.songExactMatch", {"songName": songName
, "artistName": artistName
, "albumName": albumName
})
414 list = self
.parseSongs(result
)
417 def searchSongs(self
, query
, limit
, page
=0, sortKey
=6):
418 result
= self
.callRemote("search.songs", {"query": query
, "limit": limit
, "page:": page
, "streamableOnly": 1})
419 list = self
.parseSongs(result
)
421 #return sorted(list, key=itemgetter(sortKey))
423 def searchArtists(self
, query
, limit
, sortKey
=0):
424 result
= self
.callRemote("search.artists", {"query": query
, "limit": limit
, "streamableOnly": 1})
425 list = self
.parseArtists(result
)
427 #return sorted(list, key=itemgetter(sortKey))
429 def searchAlbums(self
, query
, limit
, sortKey
=2):
430 result
= self
.callRemote("search.albums", {"query": query
, "limit": limit
, "streamableOnly": 1})
431 list = self
.parseAlbums(result
)
433 #return sorted(list, key=itemgetter(sortKey))
435 def searchPlaylists(self
, query
, limit
):
436 result
= self
.callRemote("search.playlists", {"query": query
, "limit": limit
, "streamableOnly": 1})
437 list = self
.parsePlaylists(result
)
440 def popularGetSongs(self
, limit
):
441 result
= self
.callRemote("popular.getSongs", {"limit": limit
})
442 list = self
.parseSongs(result
)
445 def popularGetArtists(self
, limit
):
446 result
= self
.callRemote("popular.getArtists", {"limit": limit
})
447 list = self
.parseArtists(result
)
450 def popularGetAlbums(self
, limit
):
451 result
= self
.callRemote("popular.getAlbums", {"limit": limit
})
452 list = self
.parseAlbums(result
)
455 def artistGetAlbums(self
, artistId
, limit
, sortKey
=2):
456 result
= self
.callRemote("artist.getAlbums", {"artistID": artistId
, "limit": limit
})
457 list = self
.parseAlbums(result
)
459 #return sorted(list, key=itemgetter(sortKey))
461 def artistGetVerifiedAlbums(self
, artistId
, limit
):
462 result
= self
.callRemote("artist.getVerifiedAlbums", {"artistID": artistId
, "limit": limit
})
463 list = self
.parseSongs(result
)
466 def albumGetSongs(self
, albumId
, limit
):
467 result
= self
.callRemote("album.getSongs", {"albumID": albumId
, "limit": limit
})
468 list = self
.parseSongs(result
)
471 def songGetSimilar(self
, songId
, limit
):
472 result
= self
.callRemote("song.getSimilar", {"songID": songId
, "limit": limit
})
473 list = self
.parseSongs(result
)
476 def parseSongs(self
, items
):
478 if 'result' in items
:
481 if 'songs' in items
['result']:
482 l
= len(items
['result']['songs'])
484 elif 'song' in items
['result']:
492 s
= items
['result'][index
][i
]
494 s
= items
['result'][index
]
495 if 'estDurationSecs' in s
:
496 dur
= s
['estDurationSecs']
502 songName
= s
['songName'].encode('ascii', 'ignore')
503 albumName
= s
['albumName'].encode('ascii', 'ignore')
504 artistName
= s
['artistName'].encode('ascii', 'ignore')
505 if (entry
[0].lower() == songName
.lower()) and (entry
[3].lower() == albumName
.lower()) and (entry
[6].lower() == artistName
.lower()):
508 list.append([s
['songName'].encode('ascii', 'ignore'),\
511 s
['albumName'].encode('ascii', 'ignore'),\
513 s
['image']['tiny'].encode('ascii', 'ignore'),\
514 s
['artistName'].encode('ascii', 'ignore'),\
516 s
['image']['small'].encode('ascii', 'ignore'),\
517 s
['image']['medium'].encode('ascii', 'ignore')])
519 print 'GrooveShark: Could not parse song number: ' + str(i
)
520 traceback
.print_exc()
527 print 'GrooveShark: Could not parse songs. Got this:'
528 traceback
.print_exc()
531 def parseArtists(self
, items
):
532 if 'result' in items
:
535 artists
= items
['result']['artists']
536 while(i
< len(artists
)):
538 list.append([s
['artistName'].encode('ascii', 'ignore'),\
545 def parseAlbums(self
, items
):
546 if 'result' in items
:
549 albums
= items
['result']['albums']
550 while(i
< len(albums
)):
552 list.append([s
['artistName'].encode('ascii', 'ignore'),\
554 s
['albumName'].encode('ascii', 'ignore'),\
556 s
['image']['tiny'].encode('ascii', 'ignore')])
562 def parsePlaylists(self
, items
):
563 if 'result' in items
:
566 playlists
= items
['result']['playlists']
567 while(i
< len(playlists
)):
569 list.append([s
['playlistID'],\
570 s
['playlistName'].encode('ascii', 'ignore'),\
571 s
['username'].encode('ascii', 'ignore')])