85a1ad1aabcb33d5a60f614b2257efd324475b24
1 import socket
, hmac
, urllib
, urllib2
, pprint
, md5
, uuid
, re
, hashlib
, time
, random
, os
, pickle
4 THUMB_URL
= 'http://beta.grooveshark.com/static/amazonart/'
5 THUMB_URL_DEFAULT
= 'http://grooveshark.com/webincludes/logo/Grooveshark_Logo_No-Text.png'
10 # GrooveSong constants
11 DOMAIN
= "grooveshark.com"
12 HOME_URL
= "http://listen." + DOMAIN
13 TOKEN_URL
= "http://cowbell." + DOMAIN
+ "/more.php"
14 API_URL
= "http://cowbell." + DOMAIN
+ "/more.php"
15 SERVICE_URL
= "http://cowbell." + DOMAIN
+ "/service.php"
17 CLIENT_NAME
= "gslite" #htmlshark #jsqueue
18 CLIENT_VERSION
= "20101012.37" #"20100831.25"
19 HEADERS
= {"Content-Type": "application/json",
20 "User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 (.NET CLR 3.5.30729)",
21 "Referer": "http://listen.grooveshark.com/main.swf?cowbell=fe87233106a6cef919a1294fb2c3c05f"}
23 RE_SESSION
= re
.compile('"sessionID":"\s*?([A-z0-9]+)"') #re.compile('sessionID:\s*?\'([A-z0-9]+)\',')
24 RANDOM_CHARS
= "1234567890abcdef"
29 def __init__(self
, cacheDir
):
32 self
.simplejson
= simplejson
34 self
.cacheDir
= cacheDir
35 self
._lastTokenTime
= 0
36 self
.uuid
= self
._getUUID
()
37 self
.sessionID
= self
._getSession
(HOME_URL
)
38 if self
.sessionID
== None:
39 raise StandardError("Cannot get session id")
41 # The actual call to the API
42 def _callRemote(self
, method
, params
, type="default"):
45 "client": CLIENT_NAME
,
46 "clientRevision": CLIENT_VERSION
,
48 "session": self
.sessionID
},
49 "country": {"IPR":"1021", "ID":"223", "CC1":"0", "CC2":"0", "CC3":"0", "CC4":"2147483648"},
54 token
= self
._getMethodToken
(method
)
56 raise StandardError("Cannot get token")
58 postData
["header"]["token"] = token
60 url
= SERVICE_URL
+ "?" + method
62 url
= API_URL
+ "?" + method
64 postData
= self
.simplejson
.dumps(postData
)
65 print "GrooveSong URL: " + url
66 request
= urllib2
.Request(url
, postData
, HEADERS
)
68 response
= urllib2
.urlopen(request
).read()
70 response
= self
.simplejson
.loads(response
)
71 print "GrooveSong Response..."
72 pprint
.pprint(response
)
74 raise StandardError("API error: " + response
)
80 raise StandardError("API error: " + response
["fault"]["message"])
82 # Generate a random uuid
84 return str(uuid
.uuid4())
86 # Make a token ready for a request header
87 def _getMethodToken(self
, method
):
88 if (time
.time() - self
._lastTokenTime
) >= 1000:
89 self
._token
= self
._getCommunicationToken
()
90 if self
._token
== None:
92 self
._lastTokenTime
= time
.time()
95 while 6 > len(randomChars
):
96 randomChars
= randomChars
+ random
.choice(RANDOM_CHARS
)
98 token
= hashlib
.sha1(method
+ ":" + self
._token
+ ":quitStealinMahShit:" + randomChars
).hexdigest()
99 return randomChars
+ token
101 # Generate a communication token
102 def _getCommunicationToken(self
):
103 params
= {"secretKey": self
._getSecretKey
(self
.sessionID
)}
106 "client": CLIENT_NAME
,
107 "clientRevision": CLIENT_VERSION
,
109 "session": self
.sessionID
},
110 "country": {"IPR":"1021", "ID":"223", "CC1":"0", "CC2":"0", "CC3":"0", "CC4":"2147483648"},
112 "parameters": params
,
113 "method": "getCommunicationToken"}
115 postData
= self
.simplejson
.dumps(postData
)
116 request
= urllib2
.Request(TOKEN_URL
, postData
, HEADERS
)
117 response
= urllib2
.urlopen(request
).read()
119 response
= self
.simplejson
.loads(response
)
121 raise StandardError("API error: " + response
)
125 return response
["result"]
129 # Generate a secret key from a sessionID
130 def _getSecretKey(self
, sessionID
):
131 return hashlib
.md5(sessionID
).hexdigest()
133 # Get a session id from some HTML
134 def _getSession(self
, html
):
135 html
= urllib2
.urlopen(HOME_URL
).read()
136 session
= RE_SESSION
.search(html
)
138 return session
.group(1)
142 # Gets a stream key and host to get song content
143 def _getStreamDetails(self
, songID
):
148 "country": {"IPR":"1021","ID":"223", "CC1":"0", "CC2":"0", "CC3":"0", "CC4":"2147483648"}
150 response
= self
._callRemote
("getStreamKeyFromSongIDEx", params
)
151 self
._lastStreamKey
= response
["result"]["streamKey"]
152 self
._lastStreamServer
= response
["result"]["ip"]
153 self
._lastStreamServerID
= response
["result"]["streamServerID"]
155 # Tells Grooveshark you have downloaded a song
156 def _markSongDownloaded(self
, songID
):
158 "streamKey": self
._lastStreamKey
,
159 "streamServerID": self
._lastStreamServerID
,
161 self
._callRemote
("markSongDownloaded", params
)
163 # Download a song to a temporary file
164 def getSongURL(self
, songID
):
165 filename
= os
.path
.join(self
.cacheDir
, songID
+ '.mp3')
166 if os
.path
.isfile(filename
) == False:
167 self
._getStreamDetails
(songID
)
168 postData
= {"streamKey": self
._lastStreamKey
}
169 postData
= urllib
.urlencode(postData
)
170 urllib
.FancyURLopener().retrieve( "http://" + self
._lastStreamServer
+ "/stream.php", filename
, data
=postData
)
171 self
._markSongDownloaded
(songID
)
180 host
= 'api.grooveshark.com'
184 def __init__(self
, cacheDir
):
186 self
.simplejson
= simplejson
187 socket
.setdefaulttimeout(40)
188 self
.cacheDir
= cacheDir
189 self
._getSavedSessionID
()
190 # session ids last 1 week
191 if self
.sessionID
== '' or time
.time()- self
.lastSessionTime
>= 6*86400:
192 self
.sessionID
= self
._getSessionID
()
193 if self
.sessionID
== '':
194 raise StandardError('Failed to get session id')
196 print "New session id: " + self
.sessionID
197 self
._setSavedSessionID
()
200 def _keySort(self
, d
):
201 return [(k
,d
[k
]) for k
in sorted(d
.keys())]
204 def _createMessageSig(self
, method
, params
, secret
):
205 args
= self
._keySort
(params
);
212 h
= hmac
.new(secret
, data
)
215 # The actual call to the API
216 def _callRemote(self
, method
, params
= {}):
217 url
= 'http://%s/ws/2.1/?method=%s&%s&wsKey=wordpress&sig=%s&format=json' % (self
.host
, method
, urllib
.urlencode(params
), self
._createMessageSig
(method
, params
, 'd6c59291620c6eaa5bf94da08fae0ecc'))
219 req
= urllib2
.Request(url
)
220 response
= urllib2
.urlopen(req
)
221 result
= response
.read()
222 pprint
.pprint(result
)
225 result
= self
.simplejson
.loads(result
)
231 def _getSessionID(self
):
233 result
= self
._callRemote
('startSession', params
)
234 self
.lastSessionTime
= time
.time()
235 return result
['result']['sessionID']
237 def _getSavedSessionID(self
):
238 path
= os
.path
.join(self
.cacheDir
, 'session', 'session.dmp')
241 session
= pickle
.load(f
)
242 self
.sessionID
= session
['sessionID']
243 self
.lastSessionTime
= session
['lastSessionTime']
247 self
.lastSessionTime
= 0
250 def _setSavedSessionID(self
):
252 dir = os
.path
.join(self
.cacheDir
, 'session')
253 # Create the 'data' directory if it doesn't exist.
254 if not os
.path
.exists(dir):
256 path
= os
.path
.join(dir, 'session.dmp')
258 session
= { 'sessionID' : self
.sessionID
, 'lastSessionTime' : self
.lastSessionTime
}
259 pickle
.dump(session
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
262 print "An error occured during save session"
265 # Make user authentication token
266 def _getUserToken(self
, username
, password
):
267 return md5
.new(username
.lower() + md5
.new(password
).hexdigest()).hexdigest()
269 # Authenticates the user for current API session
270 def _authenticateUser(self
, username
, token
):
271 params
= {'sessionID': self
.sessionID
, 'username': username
, 'token': token
}
272 result
= self
._callRemote
('authenticateUser', params
)
273 return result
['result']['UserID']
276 def login(self
, username
, password
):
277 token
= self
._getUserToken
(username
, password
)
278 self
.userID
= self
._authenticateUser
(username
, token
)
283 self
._callRemote
('logout', {'sessionID' : self
.sessionID
})
290 def getArtistSearchResults(self
, query
, limit
=ARTIST_LIMIT
):
291 result
= self
._callRemote
('getArtistSearchResults', {'query' : query
,'limit' : limit
})
292 if 'result' in result
:
293 return self
._parseArtists
(result
)
298 def getAlbumSearchResults(self
, query
, limit
=ALBUM_LIMIT
):
299 result
= self
._callRemote
('getAlbumSearchResults', {'query' : query
,'limit' : limit
})
300 if 'result' in result
:
301 return self
._parseAlbums
(result
)
306 def getSongSearchResults(self
, query
, limit
=SONG_LIMIT
):
307 result
= self
._callRemote
('getSongSearchResultsEx', {'query' : query
, 'limit' : limit
})
308 if 'result' in result
:
309 return self
._parseSongs
(result
)
313 # Gets the popular songs
314 def getPopularSongsToday(self
, limit
=SONG_LIMIT
):
315 result
= self
._callRemote
('getPopularSongsToday', {'limit' : limit
})
316 if 'result' in result
:
317 return self
._parseSongs
(result
)
321 # Gets the favorite songs of the logged-in user
322 def getUserFavoriteSongs(self
):
323 if (self
.userID
== 0):
325 result
= self
._callRemote
('getUserFavoriteSongs', {'sessionID' : self
.sessionID
})
326 if 'result' in result
:
327 return self
._parseSongs
(result
)
331 # Add song to user favorites
332 def addUserFavoriteSong(self
, songID
):
333 if (self
.userID
== 0):
335 result
= self
._callRemote
('addUserFavoriteSong', {'sessionID' : self
.sessionID
, 'songID' : songID
})
336 return result
['result']['success']
338 # Get the url to link to a song on Grooveshark
339 def getSongURLFromSongID(self
, songID
):
340 song
= GrooveSong(self
.cacheDir
)
341 return song
.getSongURL(songID
)
343 # Get the url to link to a song on Grooveshark
344 def getSongInfo(self
, songID
):
345 result
= self
._callRemote
('getSongInfoEx', {'songID' : songID
})
346 if 'result' in result
and 'SongID' in result
['result']:
347 info
= result
['result']
348 if 'CoverArtFilename' in info
and info
['CoverArtFilename'] != None:
349 info
['CoverArtFilename'] = THUMB_URL
+info
['CoverArtFilename'].encode('ascii', 'ignore')
351 info
['CoverArtFilename'] = THUMB_URL_DEFAULT
356 # Gets the playlists of the logged-in user
357 def getUserPlaylists(self
):
358 if (self
.userID
== 0):
360 result
= self
._callRemote
('getUserPlaylists', {'sessionID' : self
.sessionID
})
361 if 'result' in result
:
362 return self
._parsePlaylists
(result
)
366 # Creates a playlist with songs
367 def createPlaylist(self
, name
, songIDs
):
368 result
= self
._callRemote
('createPlaylist', {'name' : name
, 'songIDs' : songIDs
, 'sessionID' : self
.sessionID
})
369 if 'result' in result
and result
['result']['success'] == True:
370 return result
['result']['PlaylistID']
371 elif 'errors' in result
:
374 # Sets the songs for a playlist
375 def setPlaylistSongs(self
, playlistID
, songIDs
):
376 result
= self
._callRemote
('setPlaylistSongs', {'playlistID' : playlistID
, 'songIDs' : songIDs
, 'sessionID' : self
.sessionID
})
377 if 'result' in result
and result
['result']['success'] == True:
382 # Gets the songs of a playlist
383 def getPlaylistSongs(self
, playlistID
):
384 result
= self
._callRemote
('getPlaylistSongs', {'playlistID' : playlistID
});
385 if 'result' in result
:
386 return self
._parseSongs
(result
)
391 def _parseSongs(self
, items
):
392 if 'result' in items
:
395 if 'songs' in items
['result']:
396 l
= len(items
['result']['songs'])
398 elif 'song' in items
['result']:
402 l
= len(items
['result'])
406 s
= items
['result'][index
][i
]
407 elif index
== 'song':
408 s
= items
['result'][index
]
410 s
= items
['result'][i
]
411 if 'CoverArtFilename' not in s
:
412 info
= self
.getSongInfo(s
['SongID'])
413 coverart
= info
['CoverArtFilename']
414 elif s
['CoverArtFilename'] != None:
415 coverart
= THUMB_URL
+s
['CoverArtFilename'].encode('ascii', 'ignore')
417 coverart
= THUMB_URL_DEFAULT
418 list.append([s
['SongName'].encode('ascii', 'ignore'),\
420 s
['AlbumName'].encode('ascii', 'ignore'),\
422 s
['ArtistName'].encode('ascii', 'ignore'),\
430 # Extract artist data
431 def _parseArtists(self
, items
):
432 if 'result' in items
:
435 artists
= items
['result']['artists']
436 while(i
< len(artists
)):
438 list.append([s
['ArtistName'].encode('ascii', 'ignore'),\
446 def _parseAlbums(self
, items
):
447 if 'result' in items
:
450 albums
= items
['result']['albums']
451 while(i
< len(albums
)):
453 if 'CoverArtFilename' in s
and s
['CoverArtFilename'] != None:
454 coverart
= THUMB_URL
+s
['CoverArtFilename'].encode('ascii', 'ignore')
456 coverart
= THUMB_URL_DEFAULT
457 list.append([s
['ArtistName'].encode('ascii', 'ignore'),\
459 s
['AlbumName'].encode('ascii', 'ignore'),\
467 def _parsePlaylists(self
, items
):
468 if 'result' in items
:
471 playlists
= items
['result']
472 while(i
< len(playlists
)):
474 list.append([s
['PlaylistID'],\
475 s
['Name'].encode('ascii', 'ignore')])
486 groovesharkApi
= GrooveAPI('/tmp')
487 res
= groovesharkApi
.login(sys
.argv
[1], sys
.argv
[2])
488 #res = groovesharkApi.getSongSearchResults('jimmy jazz', 3)
489 #res = groovesharkApi.getPopularSongsToday()
490 #res = groovesharkApi.getSongURLFromSongID('27425375')
491 #res = groovesharkApi.getAlbumSearchResults('london calling', 3)
492 #res = groovesharkApi.getArtistSearchResults('the clash', 3)
493 #res = groovesharkApi.getUserFavoriteSongs()
494 res
= groovesharkApi
.getUserPlaylists()
495 #res = groovesharkApi.getSongInfo('27425375')
496 #res = groovesharkApi.getPlaylistSongs(40902662)
497 #res = groovesharkApi.addUserFavoriteSong('27425375')