edfab774f282ab21c4b3f258ee9d35930756148e
1 import socket
, hmac
, urllib
, urllib2
, pprint
, md5
, re
, sha
, time
, random
, os
, pickle
, uuid
, tempfile
, pprint
3 SESSION_EXPIRY
= 518400 # 6 days in seconds
6 THUMB_URL
= 'http://beta.grooveshark.com/static/amazonart/m'
12 # GrooveSong constants
13 DOMAIN
= "grooveshark.com"
14 HOME_URL
= "http://listen." + DOMAIN
15 API_URL
= "http://cowbell." + DOMAIN
+ "/more.php"
16 SERVICE_URL
= "http://cowbell." + DOMAIN
+ "/service.php"
17 TOKEN_URL
= "http://cowbell." + DOMAIN
+ "/more.php"
19 CLIENT_NAME
= "gslite"
20 CLIENT_VERSION
= "20101012.37"
21 HEADERS
= {"Content-Type": "application/json",
22 "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)",
23 "Referer": "http://listen.grooveshark.com/main.swf?cowbell=fe87233106a6cef919a1294fb2c3c05f"}
25 RE_SESSION
= re
.compile('"sessionID":"\s*?([A-z0-9]+)"')
26 RANDOM_CHARS
= "1234567890abcdef"
34 self
.simplejson
= simplejson
36 self
.cacheDir
= os
.path
.join(tempfile
.gettempdir(), 'groovesong')
37 self
._lastSessionTime
= 0
38 self
.uuid
= self
._getUUID
()
40 self
._getSavedSession
()
41 # session ids last 1 week
42 if self
.sessionID
== '' or time
.time() - self
._lastSessionTime
>= SESSION_EXPIRY
:
43 self
.sessionID
= self
._getSession
(HOME_URL
)
44 if self
.sessionID
== '':
45 raise StandardError('Failed to get session id')
47 print "New GrooveSong session id: " + self
.sessionID
48 self
._setSavedSession
()
50 # The actual call to the API
51 def _callRemote(self
, method
, params
, type="default"):
54 "client": CLIENT_NAME
,
55 "clientRevision": CLIENT_VERSION
,
57 "session": self
.sessionID
},
58 "country": {"IPR":"1021", "ID":"223", "CC1":"0", "CC2":"0", "CC3":"0", "CC4":"2147483648"},
63 token
= self
._getMethodToken
(method
)
65 raise StandardError("Cannot get token")
67 postData
["header"]["token"] = token
69 url
= SERVICE_URL
+ "?" + method
71 url
= API_URL
+ "?" + method
73 postData
= self
.simplejson
.dumps(postData
)
74 print "GrooveSong URL: " + url
75 request
= urllib2
.Request(url
, postData
, HEADERS
)
77 response
= urllib2
.urlopen(request
).read()
79 response
= self
.simplejson
.loads(response
)
80 print "GrooveSong Response..."
81 pprint
.pprint(response
)
83 raise StandardError("API error: " + response
)
89 raise StandardError("API error: " + response
["fault"]["message"])
91 # Generate a random uuid
93 return str(uuid
.uuid4())
95 # Make a token ready for a request header
96 def _getMethodToken(self
, method
):
97 self
._token
= self
._getCommunicationToken
()
98 if self
._token
== None:
102 while 6 > len(randomChars
):
103 randomChars
= randomChars
+ random
.choice(RANDOM_CHARS
)
105 token
= sha
.new(method
+ ":" + self
._token
+ ":quitStealinMahShit:" + randomChars
).hexdigest()
107 return randomChars
+ token
109 # Generate a communication token
110 def _getCommunicationToken(self
):
111 params
= {"secretKey": self
._getSecretKey
(self
.sessionID
)}
114 "client": CLIENT_NAME
,
115 "clientRevision": CLIENT_VERSION
,
117 "session": self
.sessionID
},
118 "country": {"IPR":"1021", "ID":"223", "CC1":"0", "CC2":"0", "CC3":"0", "CC4":"2147483648"},
120 "parameters": params
,
121 "method": "getCommunicationToken"}
123 postData
= self
.simplejson
.dumps(postData
)
124 request
= urllib2
.Request(TOKEN_URL
, postData
, HEADERS
)
125 response
= urllib2
.urlopen(request
).read()
127 response
= self
.simplejson
.loads(response
)
129 raise StandardError("API error: " + response
)
133 return response
["result"]
137 # Generate a secret key from a sessionID
138 def _getSecretKey(self
, sessionID
):
139 return md5
.new(sessionID
).hexdigest()
141 # Get a session id from some HTML
142 def _getSession(self
, html
):
143 html
= urllib2
.urlopen(HOME_URL
).read()
144 session
= RE_SESSION
.search(html
)
146 self
._lastSessionTime
= time
.time()
147 return session
.group(1)
151 def _getSavedSession(self
):
152 path
= os
.path
.join(self
.cacheDir
, 'session.dmp')
155 session
= pickle
.load(f
)
156 self
.sessionID
= session
['sessionID']
157 self
._lastSessionTime
= session
['lastSessionTime']
161 self
._lastSessionTime
= 0
164 def _setSavedSession(self
):
166 # Create the 'data' directory if it doesn't exist.
167 if not os
.path
.exists(self
.cacheDir
):
168 os
.makedirs(self
.cacheDir
)
169 path
= os
.path
.join(self
.cacheDir
, 'session.dmp')
171 session
= {'sessionID' : self
.sessionID
, 'lastSessionTime' : self
._lastSessionTime
}
172 pickle
.dump(session
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
175 print "An error occurred during save session"
178 # Gets a stream key and host to get song content
179 def _getStreamDetails(self
, songID
):
184 "country": {"IPR":"1021","ID":"223", "CC1":"0", "CC2":"0", "CC3":"0", "CC4":"2147483648"}
186 response
= self
._callRemote
("getStreamKeyFromSongIDEx", params
)
188 self
._lastStreamKey
= response
["result"]["streamKey"]
189 self
._lastStreamServer
= response
["result"]["ip"]
190 self
._lastStreamServerID
= response
["result"]["streamServerID"]
195 # Tells Grooveshark you have downloaded a song
196 def _markSongDownloaded(self
, songID
):
198 "streamKey": self
._lastStreamKey
,
199 "streamServerID": self
._lastStreamServerID
,
201 self
._callRemote
("markSongDownloaded", params
)
204 def getSongURL(self
, songID
):
205 if self
._getStreamDetails
(songID
) == True:
206 postData
= {"streamKey": self
._lastStreamKey
}
207 postData
= urllib
.urlencode(postData
)
208 return "http://" + self
._lastStreamServer
+ "/stream.php?" + str(postData
)
214 def __init__(self
, sessionID
= 0):
216 self
.simplejson
= simplejson
218 socket
.setdefaulttimeout(timeout
)
221 self
.sessionID
= sessionID
223 def callRemote(self
, method
, params
={}):
224 data
= {'header': {'sessionID': self
.sessionID
}, 'method': method
, 'parameters': params
}
225 data
= self
.simplejson
.dumps(data
)
226 req
= urllib2
.Request("http://api.grooveshark.com/ws/1.0/?json")
227 req
.add_header('Host', 'api.grooveshark.com')
228 req
.add_header('Content-type', 'text/json')
229 req
.add_header('Content-length', str(len(data
)))
231 response
= urllib2
.urlopen(req
)
232 result
= response
.read()
235 result
= self
.simplejson
.loads(result
)
236 if 'fault' in result
:
237 if result
['fault']['code'] == 8: #Session ID has expired. Get a new and try again if possible.
238 print 'GrooveShark: SessionID expired'
244 def login(self
, username
, password
):
245 if self
.loggedIn
== 1:
247 token
= md5
.new(username
.lower() + md5
.new(password
).hexdigest()).hexdigest()
248 result
= self
.callRemote("session.loginExt", {"username": username
, "token": token
})
249 if 'result' in result
:
250 if 'userID' in result
['result']:
252 self
.userId
= result
['result']['userID']
253 return result
['result']['userID']
257 def unfavorite(self
, songID
):
258 return self
.callRemote("song.unfavorite", {"songID": songID
})
260 def playlistCreate(self
, name
, about
):
261 if self
.loggedIn
== 1:
262 result
= self
.callRemote("playlist.create", {"name": name
, "about": about
})
263 if 'result' in result
:
264 return result
['result']['playlistID']
270 def playlistCreateUnique(self
, name
, songIds
):
271 if self
.loggedIn
== 1:
272 result
= self
.callRemote("playlist.createunique", {"name": name
, "songIDs": songIds
})
273 if 'result' in result
:
274 return result
['result']['playlistID']
280 def playlistGetSongs(self
, playlistId
):
281 items
= self
.callRemote("playlist.getSongs", {"playlistID": playlistId
})
283 if 'result' in items
:
289 if 'songs' in items
['result'][0]:
290 l
= len(items
['result'][0]['songs'])
294 if l
< 0 and 'songs' in items
['result']:
295 l
= len(items
['result']['songs'])
299 if l
< 0 and 'song' in items
['result']:
305 l
= len(items
['result'])
309 if index
== 'songs[]':
310 s
= items
['result'][0]['songs'][i
]
311 elif index
== 'songs':
312 s
= items
['result'][index
][i
]
313 elif index
== 'song':
314 s
= items
['result'][index
]
316 s
= items
['result'][i
]
317 if 'CoverArtFilename' not in s
:
318 info
= self
.getSongInfo(s
['SongID'])
319 coverart
= info
['CoverArtFilename']
320 elif s
['CoverArtFilename'] != None:
321 coverart
= THUMB_URL
+s
['CoverArtFilename'].encode('ascii', 'ignore')
324 list.append([s
['SongName'].encode('ascii', 'ignore'),\
326 s
['AlbumName'].encode('ascii', 'ignore'),\
328 s
['ArtistName'].encode('ascii', 'ignore'),\
339 def playlistDelete(self
, playlistId
):
340 if self
.loggedIn
== 1:
341 result
= self
.callRemote("playlist.delete", {"playlistID": playlistId
})
342 if 'fault' in result
:
349 def playlistRename(self
, playlistId
, name
):
350 if self
.loggedIn
== 1:
351 result
= self
.callRemote("playlist.rename", {"playlistID": playlistId
, "name": name
})
352 if 'fault' in result
:
359 def playlistClearSongs(self
, playlistId
):
360 if self
.loggedIn
== 1:
361 return self
.callRemote("playlist.clearSongs", {"playlistID": playlistId
})
363 def playlistAddSong(self
, playlistId
, songId
, position
):
364 if self
.loggedIn
== 1:
365 result
= self
.callRemote("playlist.addSong", {"playlistID": playlistId
, "songID": songId
, "position": position
})
366 if 'fault' in result
:
373 def playlistDeleteSong(self
, playlistId
, position
):
374 if self
.loggedIn
== 1:
375 result
= self
.callRemote("playlist.removeSong", {"playlistID": playlistId
, "position": position
})
376 if 'fault' in result
:
383 def playlistReplace(self
, playlistId
, songIds
):
384 if self
.loggedIn
== 1:
385 result
= self
.callRemote("playlist.replace", {"playlistID": playlistId
, "songIDs": songIds
})
386 if 'fault' in result
:
393 def artistGetSimilar(self
, artistId
, limit
):
394 items
= self
.callRemote("artist.getSimilar", {"artistID": artistId
, "limit": limit
})
395 if 'result' in items
:
398 artists
= items
['result']['artists']
399 while(i
< len(artists
)):
401 list.append([s
['artistName'].encode('ascii', 'ignore'),\
413 host
= 'api.grooveshark.com'
420 self
.simplejson
= simplejson
421 socket
.setdefaulttimeout(40)
423 self
.cacheDir
= os
.path
.join(tempfile
.gettempdir(), 'grooveapi')
424 if os
.path
.isdir(self
.cacheDir
) == False:
425 os
.makedirs(self
.cacheDir
)
426 print "Made " + self
.cacheDir
428 self
._getSavedSession
()
429 # session ids last 1 week
430 if self
.sessionID
== '' or time
.time()- self
.lastSessionTime
>= SESSION_EXPIRY
:
431 self
.sessionID
= self
._getSessionID
()
432 if self
.sessionID
== '':
433 raise StandardError('Failed to get session id')
435 print "New GrooveAPI session id: " + self
.sessionID
436 self
._setSavedSession
()
439 def _keySort(self
, d
):
440 return [(k
,d
[k
]) for k
in sorted(d
.keys())]
443 def _createMessageSig(self
, method
, params
, secret
):
444 args
= self
._keySort
(params
);
451 h
= hmac
.new(secret
, data
)
454 # The actual call to the API
455 def _callRemote(self
, method
, params
= {}):
456 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'))
458 req
= urllib2
.Request(url
)
459 response
= urllib2
.urlopen(req
)
460 result
= response
.read()
462 pprint
.pprint(result
)
465 result
= self
.simplejson
.loads(result
)
471 def _getSessionID(self
):
473 result
= self
._callRemote
('startSession', params
)
474 self
.lastSessionTime
= time
.time()
475 return result
['result']['sessionID']
477 def _getSavedSession(self
):
478 path
= os
.path
.join(self
.cacheDir
, 'session.dmp')
481 session
= pickle
.load(f
)
482 self
.sessionID
= session
['sessionID']
483 self
.lastSessionTime
= session
['lastSessionTime']
484 self
.userID
= session
['userID']
488 self
.lastSessionTime
= 0
492 def _setSavedSession(self
):
494 # Create the directory if it doesn't exist.
495 if not os
.path
.exists(self
.cacheDir
):
496 os
.makedirs(self
.cacheDir
)
497 path
= os
.path
.join(self
.cacheDir
, 'session.dmp')
499 session
= { 'sessionID' : self
.sessionID
, 'lastSessionTime' : self
.lastSessionTime
, 'userID': self
.userID
}
500 pickle
.dump(session
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
503 print "An error occurred during save session"
506 # Make user authentication token
507 def _getUserToken(self
, username
, password
):
508 return md5
.new(username
.lower() + md5
.new(password
).hexdigest()).hexdigest()
510 # Authenticates the user for current API session
511 def _authenticateUser(self
, username
, token
):
512 params
= {'sessionID': self
.sessionID
, 'username': username
, 'token': token
}
513 result
= self
._callRemote
('authenticateUser', params
)
514 return result
['result']['UserID']
517 def login(self
, username
, password
):
520 self
._getSavedSession
()
522 token
= self
._getUserToken
(username
, password
)
523 self
.userID
= self
._authenticateUser
(username
, token
)
525 self
._setSavedSession
()
530 result
= self
._callRemote
('logout', {'sessionID' : self
.sessionID
})
531 if 'result' in result
and result
['result']['success'] == True:
533 self
._setSavedSession
()
538 def getArtistSearchResults(self
, query
, limit
=ARTIST_LIMIT
):
539 result
= self
._callRemote
('getArtistSearchResults', {'query' : query
,'limit' : limit
})
540 if 'result' in result
:
541 return self
._parseArtists
(result
)
546 def getAlbumSearchResults(self
, query
, limit
=ALBUM_LIMIT
):
547 result
= self
._callRemote
('getAlbumSearchResults', {'query' : query
,'limit' : limit
})
548 if 'result' in result
:
549 return self
._parseAlbums
(result
)
554 def getSongSearchResults(self
, query
, limit
=SONG_LIMIT
):
555 result
= self
._callRemote
('getSongSearchResultsEx', {'query' : query
, 'limit' : limit
})
556 if 'result' in result
:
557 return self
._parseSongs
(result
)
562 def getArtistAlbums(self
, artistID
, limit
=ALBUM_LIMIT
):
563 result
= self
._callRemote
('getArtistAlbums', {'artistID' : artistID
})
564 if 'result' in result
:
565 return self
._parseAlbums
(result
, limit
)
570 def getAlbumSongs(self
, albumID
, limit
=SONG_LIMIT
):
571 result
= self
._callRemote
('getAlbumSongsEx', {'albumID' : albumID
, 'limit' : limit
})
572 if 'result' in result
:
573 return self
._parseSongs
(result
)
577 # Get artist's popular songs
578 def getArtistPopularSongs(self
, artistID
, limit
= SONG_LIMIT
):
579 result
= self
._callRemote
('getArtistPopularSongs', {'artistID' : artistID
})
580 if 'result' in result
:
581 return self
._parseSongs
(result
, limit
)
585 # Gets the popular songs
586 def getPopularSongsToday(self
, limit
=SONG_LIMIT
):
587 result
= self
._callRemote
('getPopularSongsToday', {'limit' : limit
})
588 if 'result' in result
:
589 # Note limit is broken in the Grooveshark getPopularSongsToday method
590 return self
._parseSongs
(result
, limit
)
594 # Gets the favorite songs of the logged-in user
595 def getUserFavoriteSongs(self
):
596 if (self
.userID
== 0):
598 result
= self
._callRemote
('getUserFavoriteSongs', {'sessionID' : self
.sessionID
})
599 if 'result' in result
:
600 return self
._parseSongs
(result
)
604 # Add song to user favorites
605 def addUserFavoriteSong(self
, songID
):
606 if (self
.userID
== 0):
608 result
= self
._callRemote
('addUserFavoriteSong', {'sessionID' : self
.sessionID
, 'songID' : songID
})
609 return result
['result']['success']
611 # Get the url to link to a song on Grooveshark
612 def getSongURLFromSongID(self
, songID
):
614 url
= song
.getSongURL(songID
)
615 print "Got song URL " + url
618 # Get the url to link to a song on Grooveshark
619 def getSongInfo(self
, songID
):
620 result
= self
._callRemote
('getSongInfoEx', {'songID' : songID
})
621 if 'result' in result
and 'SongID' in result
['result']:
622 info
= result
['result']
623 if 'CoverArtFilename' in info
and info
['CoverArtFilename'] != None:
624 info
['CoverArtFilename'] = THUMB_URL
+info
['CoverArtFilename'].encode('ascii', 'ignore')
626 info
['CoverArtFilename'] = 'None'
631 # Gets the playlists of the logged-in user
632 def getUserPlaylists(self
):
633 if (self
.userID
== 0):
635 result
= self
._callRemote
('getUserPlaylists', {'sessionID' : self
.sessionID
})
636 if 'result' in result
:
637 return self
._parsePlaylists
(result
)
641 # Get userid from name
642 def _getUserIDFromUsername(self
, username
):
643 result
= self
._callRemote
('getUserIDFromUsername', {'username' : username
})
644 if 'result' in result
and result
['result']['UserID'] > 0:
645 return result
['result']['UserID']
649 # Gets the playlists of the logged-in user
650 def getUserPlaylistsEx(self
, username
):
651 userID
= self
._getUserIDFromUsername
(username
)
653 result
= self
._callRemote
('getUserPlaylistsEx', {'userID' : userID
})
654 if 'result' in result
and result
['result']['playlists'] != None:
655 playlists
= result
['result']['playlists']
656 return self
._parsePlaylists
(playlists
)
660 # Creates a playlist with songs
661 def createPlaylist(self
, name
, songIDs
):
662 result
= self
._callRemote
('createPlaylist', {'name' : name
, 'songIDs' : songIDs
, 'sessionID' : self
.sessionID
})
663 if 'result' in result
and result
['result']['success'] == True:
664 return result
['result']['playlistID']
665 elif 'errors' in result
:
668 # Sets the songs for a playlist
669 def setPlaylistSongs(self
, playlistID
, songIDs
):
670 result
= self
._callRemote
('setPlaylistSongs', {'playlistID' : playlistID
, 'songIDs' : songIDs
, 'sessionID' : self
.sessionID
})
671 if 'result' in result
and result
['result']['success'] == True:
676 # Gets the songs of a playlist
677 def getPlaylistSongs(self
, playlistID
):
678 result
= self
._callRemote
('getPlaylistSongs', {'playlistID' : playlistID
});
679 if 'result' in result
:
680 return self
._parseSongs
(result
)
685 def pingService(self
,):
686 result
= self
._callRemote
('pingService', {});
687 if 'result' in result
and result
['result'] != '':
693 def _parseSongs(self
, items
, limit
=0):
694 if 'result' in items
:
700 if 'songs' in items
['result'][0]:
701 l
= len(items
['result'][0]['songs'])
705 if l
< 0 and 'songs' in items
['result']:
706 l
= len(items
['result']['songs'])
710 if l
< 0 and 'song' in items
['result']:
716 l
= len(items
['result'])
719 if limit
> 0 and l
> limit
:
722 if index
== 'songs[]':
723 s
= items
['result'][0]['songs'][i
]
724 elif index
== 'songs':
725 s
= items
['result'][index
][i
]
726 elif index
== 'song':
727 s
= items
['result'][index
]
729 s
= items
['result'][i
]
730 if 'CoverArtFilename' not in s
:
731 info
= self
.getSongInfo(s
['SongID'])
732 coverart
= info
['CoverArtFilename']
733 elif s
['CoverArtFilename'] != None:
734 coverart
= THUMB_URL
+s
['CoverArtFilename'].encode('ascii', 'ignore')
737 list.append([s
['SongName'].encode('ascii', 'ignore'),\
739 s
['AlbumName'].encode('ascii', 'ignore'),\
741 s
['ArtistName'].encode('ascii', 'ignore'),\
749 # Extract artist data
750 def _parseArtists(self
, items
):
751 if 'result' in items
:
754 artists
= items
['result']['artists']
755 while(i
< len(artists
)):
757 list.append([s
['ArtistName'].encode('ascii', 'ignore'),\
765 def _parseAlbums(self
, items
, limit
=0):
766 if 'result' in items
:
770 albums
= items
['result']['albums']
772 res
= items
['result'][0]
773 albums
= res
['albums']
775 if limit
> 0 and l
> limit
:
779 if 'CoverArtFilename' in s
and s
['CoverArtFilename'] != None:
780 coverart
= THUMB_URL
+s
['CoverArtFilename'].encode('ascii', 'ignore')
783 list.append([s
['ArtistName'].encode('ascii', 'ignore'),\
785 s
['AlbumName'].encode('ascii', 'ignore'),\
793 def _parsePlaylists(self
, items
):
796 if 'result' in items
:
797 playlists
= items
['result']
803 while (i
< len(playlists
)):
805 list.append([s
['Name'].encode('ascii', 'ignore'), s
['PlaylistID']])
812 #groovesharkApi = GrooveAPI()
813 #res = groovesharkApi.pingService()
814 #res = groovesharkApi.login(sys.argv[1], sys.argv[2])
815 #songIDs = ['23404546','23401810','23401157']
816 #res = groovesharkApi.createPlaylist("Test")
817 #res = groovesharkApi.setPlaylistSongs(res, songIDs)
818 #res = groovesharkApi.getPlaylistSongs(42251632)
819 #res = groovesharkApi.getSongSearchResults('jimmy jazz', 3)
820 #res = groovesharkApi.getPopularSongsToday(3)
821 #res = groovesharkApi.getSongURLFromSongID('26579347')
822 #res = groovesharkApi.getAlbumSearchResults('london calling', 3)
823 #res = groovesharkApi.getArtistAlbums('52283')
824 #res = groovesharkApi.getArtistSearchResults('the clash', 3)
825 #res = groovesharkApi.getUserFavoriteSongs()
826 #res = groovesharkApi.getUserPlaylists()
827 #res = groovesharkApi.getSongInfo('27425375')
828 #res = groovesharkApi.getPlaylistSongs(40902662)
829 #res = groovesharkApi.addUserFavoriteSong('27425375')
830 #res = groovesharkApi.logout()
831 #res = groovesharkApi.getUserPlaylistsEx('stephendenham')
832 #res = groovesharkApi.getArtistPopularSongs('3707')