d83989ee71f2662147d71d558c33418393026e07
1 import urllib2
, md5
, unicodedata
, re
, os
, traceback
, sys
, pickle
, socket
, xbmc
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 self
.simplejson
= simplejson
27 socket
.setdefaulttimeout(timeout
)
28 self
.enableDebug
= enableDebug
31 self
.removeDuplicates
= False
33 self
.radioRecentSongs
= []
34 self
.radioRecentArtists
= []
35 self
.radioEnabled
= False
37 self
.dataDir
= 'addon_data'
38 self
.confDir
= xbmc
.translatePath(os
.path
.join('special://masterprofile/' + self
.dataDir
, os
.path
.basename(os
.getcwd())))
39 self
.sessionID
= self
.getSavedSession()
40 self
.debug('Saved sessionID: ' + self
.sessionID
)
41 #self.sessionID = self.getSessionFromAPI()
42 #self.debug('API sessionID: ' + self.sessionID)
43 if self
.sessionID
== '':
44 self
.sessionID
= self
.startSession()
45 self
.debug('Start() sessionID: ' + self
.sessionID
)
46 if self
.sessionID
== '':
47 self
.debug('Could not get a sessionID. Try again in a few minutes')
48 raise SessionIDTryAgainError()
52 self
.debug('sessionID: ' + self
.sessionID
)
56 if self
.loggedIn
== 1:
62 if self
.enableDebug
== True:
65 def setRemoveDuplicates(self
, enable
):
66 if enable
== True or enable
== 'true' or enable
== 'True':
67 self
.removeDuplicates
= True
69 self
.removeDuplicates
= False
71 def getSavedSession(self
):
73 path
= os
.path
.join(self
.confDir
, 'session', 'session.txt')
77 sessionID
= pickle
.load(f
)
85 def saveSession(self
):
87 dir = os
.path
.join(self
.confDir
, 'session')
88 # Create the 'data' directory if it doesn't exist.
89 if not os
.path
.exists(dir):
91 path
= os
.path
.join(dir, 'session.txt')
93 pickle
.dump(self
.sessionID
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
96 print 'There was an error while saving the session pickle (%s)' % e
99 print "An unknown error occured during save session: " + str(sys
.exc_info()[0])
102 def saveSettings(self
):
104 dir = os
.path
.join(self
.confDir
, 'data')
105 # Create the 'data' directory if it doesn't exist.
106 if not os
.path
.exists(dir):
108 path
= os
.path
.join(dir, 'settings1.txt')
110 pickle
.dump(self
.settings
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
113 print 'There was an error while saving the settings pickle (%s)' % e
116 print "An unknown error occured during save settings\n"
119 def callRemote(self
, method
, params
={}):
120 data
= {'header': {'sessionID': self
.sessionID
}, 'method': method
, 'parameters': params
}
121 data
= self
.simplejson
.dumps(data
)
122 req
= urllib2
.Request("http://api.grooveshark.com/ws/1.0/?json")
123 req
.add_header('Host', 'api.grooveshark.com')
124 req
.add_header('Content-type', 'text/json')
125 req
.add_header('Content-length', str(len(data
)))
127 response
= urllib2
.urlopen(req
)
128 result
= response
.read()
131 result
= self
.simplejson
.loads(result
)
132 if 'fault' in result
:
134 if result
['fault']['code'] == 8: #Session ID has expired. Get a new and try again if possible.
135 self
.debug(result
['fault']['message'])
136 self
.sessionID
= self
.startSession()
137 if self
.sessionID
!= '':
139 return self
.callRemote(method
, params
)
141 self
.debug('GrooveShark: SessionID expired, but unable to get new')
147 def startSession(self
):
148 response
= urllib2
.urlopen("http://www.moovida.com/services/grooveshark/session_start")
149 result
= response
.read()
150 result
= self
.simplejson
.loads(result
)
152 if 'fault' in result
:
155 return result
['result']['sessionID']
157 def sessionDestroy(self
):
158 return self
.callRemote("session.destroy")
160 def getSessionFromAPI(self
):
161 result
= self
.callRemote("session.get")
162 if 'fault' in result
:
165 return result
['header']['sessionID']
167 def getStreamURL(self
, songID
):
168 result
= self
.callRemote("song.getStreamUrlEx", {"songID": songID
})
169 if 'result' in result
:
170 return result
['result']['url']
174 def createUserAuthToken(self
, username
, password
):
175 hashpass
= md5
.new(password
).hexdigest()
176 hashpass
= username
+ hashpass
177 hashpass
= md5
.new(hashpass
).hexdigest()
178 result
= self
.callRemote("session.createUserAuthToken", {"username": username
, "hashpass": hashpass
})
179 if 'result' in result
:
180 return result
['result']['token'], result
['result']['userID']
181 elif 'fault' in result
:
182 if result
['fault']['code'] == 256:
183 return -1 # Exceeded the number of allowed tokens. Should not happen
185 return -2 # Unknown error
187 return -2 # Unknown error
189 def destroyUserAuthToken(self
, token
):
190 self
.callRemote("session.destroyAuthToken", {"token": token
})
192 def loginViaAuthToken(self
, token
):
193 result
= self
.callRemote("session.loginViaAuthToken", {"token": token
})
194 self
.destroyUserAuthToken(token
)
195 if 'result' in result
:
196 self
.userID
= result
['result']['userID']
197 return result
['result']['userID']
201 def login(self
, username
, password
):
202 if self
.loggedIn
== 1:
204 result
= self
.createUserAuthToken(username
, password
)
206 raise LoginTokensExceededError()
208 raise LoginUnknownError()
210 self
.token
= result
[0]
211 self
.debug('Token:' + self
.token
)
212 self
.userId
= self
.loginViaAuthToken(self
.token
)
214 raise LoginUnknownError()
220 def loginExt(self
, username
, password
):
221 if self
.loggedIn
== 1:
223 token
= md5
.new(username
.lower() + md5
.new(password
).hexdigest()).hexdigest()
224 result
= self
.callRemote("session.loginExt", {"username": username
, "token": token
})
225 if 'result' in result
:
226 if 'userID' in result
['result']:
228 self
.userId
= result
['result']['userID']
229 return result
['result']['userID']
234 def loginBasic(self
, username
, password
):
235 if self
.loggedIn
== 1:
237 result
= self
.callRemote("session.login", {"username": username
, "password": password
})
238 if 'result' in result
:
239 if 'userID' in result
['result']:
241 self
.userId
= result
['result']['userID']
242 return result
['result']['userID']
246 def loggedInStatus(self
):
250 self
.callRemote("session.logout", {})
253 def getSongInfo(self
, songID
):
254 return self
.callRemote("song.about", {"songID": songID
})['result']['song']
256 def userGetFavoriteSongs(self
, userID
):
257 result
= self
.callRemote("user.getFavoriteSongs", {"userID": userID
})
258 list = self
.parseSongs(result
)
261 def userGetPlaylists(self
, limit
=25):
262 if self
.loggedIn
== 1:
263 result
= self
.callRemote("user.getPlaylists", {"userID": self
.userId
, "limit": limit
})
264 if 'result' in result
:
265 playlists
= result
['result']['playlists']
270 while(i
< len(playlists
)):
272 list.append([p
['playlistName'].encode('ascii', 'ignore'), p
['playlistID']])
274 return sorted(list, key
=itemgetter(0))
278 def playlistCreate(self
, name
, about
):
279 if self
.loggedIn
== 1:
280 result
= self
.callRemote("playlist.create", {"name": name
, "about": about
})
281 if 'result' in result
:
282 return result
['result']['playlistID']
288 def playlistCreateUnique(self
, name
, songIds
):
289 if self
.loggedIn
== 1:
290 result
= self
.callRemote("playlist.createunique", {"name": name
, "songIDs": songIds
})
291 if 'result' in result
:
292 return result
['result']['playlistID']
298 def playlistGetSongs(self
, playlistId
, limit
=25):
299 result
= self
.callRemote("playlist.getSongs", {"playlistID": playlistId
})
300 list = self
.parseSongs(result
)
303 def playlistDelete(self
, playlistId
):
304 if self
.loggedIn
== 1:
305 return self
.callRemote("playlist.delete", {"playlistID": playlistId
})
307 def playlistRename(self
, playlistId
, name
):
308 if self
.loggedIn
== 1:
309 result
= self
.callRemote("playlist.rename", {"playlistID": playlistId
, "name": name
})
310 if 'fault' in result
:
317 def playlistClearSongs(self
, playlistId
):
318 if self
.loggedIn
== 1:
319 return self
.callRemote("playlist.clearSongs", {"playlistID": playlistId
})
321 def playlistAddSong(self
, playlistId
, songId
, position
):
322 if self
.loggedIn
== 1:
323 result
= self
.callRemote("playlist.addSong", {"playlistID": playlistId
, "songID": songId
, "position": position
})
324 if 'fault' in result
:
331 def playlistDeleteSong(self
, playlistId
, position
):
332 if self
.loggedIn
== 1:
333 result
= self
.callRemote("playlist.removeSong", {"playlistID": playlistId
, "position": position
})
334 if 'fault' in result
:
341 def playlistReplace(self
, playlistId
, songIds
):
342 if self
.loggedIn
== 1:
343 result
= self
.callRemote("playlist.replace", {"playlistID": playlistId
, "songIDs": songIds
})
344 if 'fault' in result
:
351 def radioStartArtists(self
):
352 radio
= self
.getSavedRadio()
355 result
= self
.callRemote("autoplay.startWithArtistIDs", {"artistIDs": radio
['seedArtists']})
356 if 'fault' in result
:
357 print "Cannot autoplay artists"
358 self
.radioEnabled
= False
360 self
.radioEnabled
= True
361 return self
.radioEnabled
363 def radioStartSongs(self
):
364 radio
= self
.getSavedRadio()
367 result
= self
.callRemote("autoplay.start", {"songIDs": radio
['seedSongs']})
368 if 'fault' in result
:
369 print "Cannot autoplay songs"
370 self
.radioEnabled
= False
372 self
.radioEnabled
= True
373 return self
.radioEnabled
375 def radioNextSong(self
):
376 radio
= self
.getSavedRadio()
380 result
= self
.callRemote("autoplay.getNextSongEx", {"seedArtists": radio
['seedArtists'], "frowns": radio
['frowns'], "songIDsAlreadySeen": self
.radioRecentSongs
, "recentArtists": self
.radioRecentArtists
})
381 if 'fault' in result
:
384 song
= self
.parseSongs(result
)
385 self
.radioRecentSongs
.append(song
[0][1])
386 self
.radioRecentArtists
.append(song
[0][7])
389 def radioFrown(self
, songId
= None):
390 radio
= self
.getSavedRadio()
391 if radio
!= None and songId
!= None:
393 radio
['frowns'].remove(songId
)
395 radio
['frowns'].append(songId
)
396 return self
.saveRadio(radio
= radio
)
400 def radioArtist(self
, artistId
= None):
401 radio
= self
.getSavedRadio()
402 if radio
!= None and artistId
!= None:
404 radio
['seedArtists'].remove(artistId
)
406 radio
['seedArtists'].append(artistId
)
408 return self
.saveRadio(radio
= radio
)
410 print "Failed to get radio"
413 def radioSong(self
, songId
= None):
414 radio
= self
.getSavedRadio()
415 if radio
!= None and songId
!= None:
417 radio
['seedSongs'].remove(songId
)
419 radio
['seedSongs'].append(songId
)
421 return self
.saveRadio(radio
= radio
)
423 print "Failed to get radio"
426 def radioTurnedOn(self
):
427 return self
.radioEnabled
429 def getSavedRadio(self
):
430 path
= os
.path
.join(self
.confDir
, 'radio', 'radio.dmp')
433 radio
= pickle
.load(f
)
436 print "Failed to open " + path
438 radio
['seedSongs'] = []
439 radio
['seedArtists'] = []
441 if self
.saveRadio(radio
) == False:
445 def saveRadio(self
, radio
): #blaher
447 print 'Invalid radio'
450 dir = os
.path
.join(self
.confDir
, 'radio')
451 # Create the 'data' directory if it doesn't exist.
452 if not os
.path
.exists(dir):
454 path
= os
.path
.join(dir, 'radio.dmp')
456 pickle
.dump(radio
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
460 print 'There was an error while saving the radio pickle (%s)' % e
463 print "An unknown error occurred during save radio: " + str(sys
.exc_info()[0])
466 def favoriteSong(self
, songID
):
467 return self
.callRemote("song.favorite", {"songID": songID
})
469 def unfavoriteSong(self
, songID
):
470 return self
.callRemote("song.unfavorite", {"songID": songID
})
472 def getMethods(self
):
473 return self
.callRemote("service.getMethods")
475 def searchSongsExactMatch(self
, songName
, artistName
, albumName
):
476 result
= self
.callRemote("search.songExactMatch", {"songName": songName
, "artistName": artistName
, "albumName": albumName
})
477 list = self
.parseSongs(result
)
480 def searchSongs(self
, query
, limit
, page
=0, sortKey
=6):
481 result
= self
.callRemote("search.songs", {"query": query
, "limit": limit
, "page:": page
, "streamableOnly": 1})
482 list = self
.parseSongs(result
)
484 #return sorted(list, key=itemgetter(sortKey))
486 def searchArtists(self
, query
, limit
, sortKey
=0):
487 result
= self
.callRemote("search.artists", {"query": query
, "limit": limit
, "streamableOnly": 1})
488 list = self
.parseArtists(result
)
490 #return sorted(list, key=itemgetter(sortKey))
492 def searchAlbums(self
, query
, limit
, sortKey
=2):
493 result
= self
.callRemote("search.albums", {"query": query
, "limit": limit
, "streamableOnly": 1})
494 list = self
.parseAlbums(result
)
496 #return sorted(list, key=itemgetter(sortKey))
498 def searchPlaylists(self
, query
, limit
):
499 result
= self
.callRemote("search.playlists", {"query": query
, "limit": limit
, "streamableOnly": 1})
500 list = self
.parsePlaylists(result
)
503 def popularGetSongs(self
, limit
):
504 result
= self
.callRemote("popular.getSongs", {"limit": limit
})
505 list = self
.parseSongs(result
)
508 def popularGetArtists(self
, limit
):
509 result
= self
.callRemote("popular.getArtists", {"limit": limit
})
510 list = self
.parseArtists(result
)
513 def popularGetAlbums(self
, limit
):
514 result
= self
.callRemote("popular.getAlbums", {"limit": limit
})
515 list = self
.parseAlbums(result
)
518 def artistAbout(self
, artistId
):
519 result
= self
.callRemote("artist.about", {"artistID": artistId
})
522 def artistGetAlbums(self
, artistId
, limit
, sortKey
=2):
523 result
= self
.callRemote("artist.getAlbums", {"artistID": artistId
, "limit": limit
})
524 list = self
.parseAlbums(result
)
526 #return sorted(list, key=itemgetter(sortKey))
528 def artistGetVerifiedAlbums(self
, artistId
, limit
):
529 result
= self
.callRemote("artist.getVerifiedAlbums", {"artistID": artistId
, "limit": limit
})
530 list = self
.parseSongs(result
)
533 def albumGetSongs(self
, albumId
, limit
):
534 result
= self
.callRemote("album.getSongs", {"albumID": albumId
, "limit": limit
})
535 list = self
.parseSongs(result
)
538 def songGetSimilar(self
, songId
, limit
):
539 result
= self
.callRemote("song.getSimilar", {"songID": songId
, "limit": limit
})
540 list = self
.parseSongs(result
)
543 def artistGetSimilar(self
, artistId
, limit
):
544 result
= self
.callRemote("artist.getSimilar", {"artistID": artistId
, "limit": limit
})
545 list = self
.parseArtists(result
)
548 def songAbout(self
, songId
):
549 result
= self
.callRemote("song.about", {"songID": songId
})
550 return result
['result']['song']
552 def getVersion(self
):
553 result
= self
.callRemote("service.getVersion", {})
556 def parseSongs(self
, items
):
558 if 'result' in items
:
561 if 'songs' in items
['result']:
562 l
= len(items
['result']['songs'])
564 elif 'song' in items
['result']:
572 s
= items
['result'][index
][i
]
574 s
= items
['result'][index
]
575 if 'estDurationSecs' in s
:
576 dur
= s
['estDurationSecs']
582 songName
= s
['songName'].encode('ascii', 'ignore')
583 albumName
= s
['albumName'].encode('ascii', 'ignore')
584 artistName
= s
['artistName'].encode('ascii', 'ignore')
585 if self
.removeDuplicates
== True:
586 if (entry
[0].lower() == songName
.lower()) and (entry
[3].lower() == albumName
.lower()) and (entry
[6].lower() == artistName
.lower()):
589 list.append([s
['songName'].encode('ascii', 'ignore'),\
592 s
['albumName'].encode('ascii', 'ignore'),\
594 s
['image']['tiny'].encode('ascii', 'ignore'),\
595 s
['artistName'].encode('ascii', 'ignore'),\
597 s
['image']['small'].encode('ascii', 'ignore'),\
598 s
['image']['medium'].encode('ascii', 'ignore')])
600 print 'GrooveShark: Could not parse song number: ' + str(i
)
601 traceback
.print_exc()
608 print 'GrooveShark: Could not parse songs. Got this:'
609 traceback
.print_exc()
612 def parseArtists(self
, items
):
614 if 'result' in items
:
617 artists
= items
['result']['artists']
618 while(i
< len(artists
)):
621 list.append([s
['artistName'].encode('ascii', 'ignore'),\
624 print 'GrooveShark: Could not parse album number: ' + str(i
)
625 traceback
.print_exc()
631 print 'GrooveShark: Could not parse artists. Got this:'
632 traceback
.print_exc()
635 def parseAlbums(self
, items
):
637 if 'result' in items
:
640 albums
= items
['result']['albums']
641 while(i
< len(albums
)):
643 try: # Avoid ascii ancoding errors
644 list.append([s
['artistName'].encode('ascii', 'ignore'),\
646 s
['albumName'].encode('ascii', 'ignore'),\
648 s
['image']['medium'].encode('ascii', 'ignore')])
650 print 'GrooveShark: Could not parse album number: ' + str(i
)
651 traceback
.print_exc()
657 print 'GrooveShark: Could not parse albums. Got this'
658 traceback
.print_exc()
661 def parsePlaylists(self
, items
):
663 if 'result' in items
:
666 playlists
= items
['result']['playlists']
667 while(i
< len(playlists
)):
669 try: # Avoid ascii ancoding errors
670 list.append([s
['playlistID'],\
671 s
['playlistName'].encode('ascii', 'ignore'),\
672 s
['username'].encode('ascii', 'ignore')])
674 print 'GrooveShark: Could not parse playlist number: ' + str(i
)
675 traceback
.print_exc()
681 print 'GrooveShark: Could not parse playlists. Got this:'