Session caching.
[clinton/xbmc-groove.git] / resources / lib / GroovesharkAPI.py
index 85a1ad1..fbc0389 100644 (file)
@@ -1,5 +1,7 @@
 import socket, hmac, urllib, urllib2, pprint, md5, uuid, re, hashlib, time, random, os, pickle
 
+SESSION_EXPIRY = 518400 # 6 days in seconds
+
 # GrooveAPI constants
 THUMB_URL = 'http://beta.grooveshark.com/static/amazonart/'
 THUMB_URL_DEFAULT = 'http://grooveshark.com/webincludes/logo/Grooveshark_Logo_No-Text.png'
@@ -10,17 +12,18 @@ ARTIST_LIMIT = 15
 # GrooveSong constants
 DOMAIN = "grooveshark.com"
 HOME_URL = "http://listen." + DOMAIN
-TOKEN_URL = "http://cowbell." + DOMAIN + "/more.php"
 API_URL = "http://cowbell." + DOMAIN + "/more.php"
 SERVICE_URL = "http://cowbell." + DOMAIN + "/service.php"
+TOKEN_URL = "http://cowbell." + DOMAIN + "/more.php"
+TOKEN_EXPIRY = 1000
 
-CLIENT_NAME = "gslite" #htmlshark #jsqueue
-CLIENT_VERSION = "20101012.37" #"20100831.25"
+CLIENT_NAME = "gslite"
+CLIENT_VERSION = "20101012.37"
 HEADERS = {"Content-Type": "application/json",
        "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)",                        
        "Referer": "http://listen.grooveshark.com/main.swf?cowbell=fe87233106a6cef919a1294fb2c3c05f"}
 
-RE_SESSION = re.compile('"sessionID":"\s*?([A-z0-9]+)"') #re.compile('sessionID:\s*?\'([A-z0-9]+)\',')
+RE_SESSION = re.compile('"sessionID":"\s*?([A-z0-9]+)"')
 RANDOM_CHARS = "1234567890abcdef"
 
 # Get a song
@@ -33,10 +36,18 @@ class GrooveSong:
 
                self.cacheDir = cacheDir
                self._lastTokenTime = 0
+               self._lastSessionTime = 0
                self.uuid = self._getUUID()
-               self.sessionID = self._getSession(HOME_URL)
-               if self.sessionID == None:
-                       raise StandardError("Cannot get session id")
+               
+               self._getSavedSession()
+               # session ids last 1 week
+               if self.sessionID == '' or time.time() - self._lastSessionTime >= SESSION_EXPIRY:
+                       self.sessionID = self._getSession(HOME_URL)
+                       if self.sessionID == '':
+                               raise StandardError('Failed to get session id')
+                       else:
+                               print "New GrooveSong session id: " + self.sessionID
+                               self._setSavedSession()
                
        # The actual call to the API
        def _callRemote(self, method, params, type="default"):
@@ -85,11 +96,12 @@ class GrooveSong:
 
        #  Make a token ready for a request header
        def _getMethodToken(self, method):
-               if (time.time() - self._lastTokenTime) >= 1000:
+               if (time.time() - self._lastTokenTime) >= TOKEN_EXPIRY:
                        self._token = self._getCommunicationToken()
                        if self._token == None:
                                return None
                        self._lastTokenTime = time.time()
+                       self._setSavedSession()
 
                randomChars = ""
                while 6 > len(randomChars):
@@ -135,9 +147,41 @@ class GrooveSong:
                html = urllib2.urlopen(HOME_URL).read()
                session = RE_SESSION.search(html)
                if session:
+                       self._lastSessionTime = time.time()
                        return session.group(1)
                else:
                        return None
+               
+       def _getSavedSession(self):
+               path = os.path.join(self.cacheDir, 'session.dmp')
+               try:
+                       f = open(path, 'rb')
+                       session = pickle.load(f)
+                       self.sessionID = session['sessionID']
+                       self._lastSessionTime = session['lastSessionTime']
+                       self._token = session['token']
+                       self._lastTokenTime = session['lastTokenTime']
+                       f.close()
+               except:
+                       self.sessionID = ''
+                       self._lastSessionTime = 0
+                       self._token = ''
+                       self._lastTokenTime = 0
+                       pass            
+
+       def _setSavedSession(self):                     
+               try:
+                       # Create the 'data' directory if it doesn't exist.
+                       if not os.path.exists(self.cacheDir):
+                               os.makedirs(self.cacheDir)
+                       path = os.path.join(self.cacheDir, 'session.dmp')
+                       f = open(path, 'wb')
+                       session = {'sessionID' : self.sessionID, 'lastSessionTime' : self._lastSessionTime, 'token' : self._token, 'lastTokenTime' : self._lastTokenTime}
+                       pickle.dump(session, f, protocol=pickle.HIGHEST_PROTOCOL)
+                       f.close()
+               except:
+                       print "An error occurred during save session"
+                       pass
        
        # Gets a stream key and host to get song content
        def _getStreamDetails(self, songID):
@@ -186,15 +230,15 @@ class GrooveAPI:
                self.simplejson = simplejson
                socket.setdefaulttimeout(40)
                self.cacheDir = cacheDir
-               self._getSavedSessionID()
+               self._getSavedSession()
                # session ids last 1 week
-               if self.sessionID == '' or time.time()- self.lastSessionTime >= 6*86400:
+               if self.sessionID == '' or time.time()- self.lastSessionTime >= SESSION_EXPIRY:
                        self.sessionID = self._getSessionID()
                        if self.sessionID == '':
                                raise StandardError('Failed to get session id')
                        else:
-                               print "New session id: " + self.sessionID
-                               self._setSavedSessionID()
+                               print "New GrooveAPI session id: " + self.sessionID
+                               self._setSavedSession()
 
        # Sort keys             
        def _keySort(self, d):
@@ -234,32 +278,34 @@ class GrooveAPI:
                self.lastSessionTime = time.time()
                return result['result']['sessionID']
        
-       def _getSavedSessionID(self):
-               path = os.path.join(self.cacheDir, 'session', 'session.dmp')
+       def _getSavedSession(self):
+               path = os.path.join(self.cacheDir, 'grooveapi', 'session.dmp')
                try:
                        f = open(path, 'rb')
                        session = pickle.load(f)
                        self.sessionID = session['sessionID']
                        self.lastSessionTime = session['lastSessionTime']
+                       self.userID = session['userID']
                        f.close()
                except:
                        self.sessionID = ''
                        self.lastSessionTime = 0
+                       self.userID = 0
                        pass            
 
-       def _setSavedSessionID(self):                   
+       def _setSavedSession(self):                     
                try:
-                       dir = os.path.join(self.cacheDir, 'session')
+                       dir = os.path.join(self.cacheDir, 'grooveapi')
                        # Create the 'data' directory if it doesn't exist.
                        if not os.path.exists(dir):
                                os.makedirs(dir)
                        path = os.path.join(dir, 'session.dmp')
                        f = open(path, 'wb')
-                       session = { 'sessionID' : self.sessionID, 'lastSessionTime' : self.lastSessionTime}
+                       session = { 'sessionID' : self.sessionID, 'lastSessionTime' : self.lastSessionTime, 'userID': self.userID}
                        pickle.dump(session, f, protocol=pickle.HIGHEST_PROTOCOL)
                        f.close()
                except:
-                       print "An error occured during save session"
+                       print "An error occurred during save session"
                        pass
        
        # Make user authentication token
@@ -274,18 +320,25 @@ class GrooveAPI:
        
        # Login
        def login(self, username, password):
-               token = self._getUserToken(username, password)
-               self.userID = self._authenticateUser(username, token)
+               if self.userID <= 0:
+                       # Check cache
+                       self._getSavedSession()
+                       if self.userID <= 0: 
+                               token = self._getUserToken(username, password)
+                               self.userID = self._authenticateUser(username, token)
+                               if self.userID > 0:
+                                       self._setSavedSession()
                return self.userID
                
        # Logs the user out
        def logout(self):
-               self._callRemote('logout', {'sessionID' : self.sessionID})
+               result = self._callRemote('logout', {'sessionID' : self.sessionID})
+               if 'result' in result and result['result']['success'] == True: 
+                       self.userID = 0
+                       self._setSavedSession()
+                       return True
+               return False
 
-       # Return user id
-       def getUserID(self):
-               return self.userID
-       
        # Search for albums
        def getArtistSearchResults(self, query, limit=ARTIST_LIMIT):
                result = self._callRemote('getArtistSearchResults', {'query' : query,'limit' : limit})
@@ -337,7 +390,8 @@ class GrooveAPI:
                
        # Get the url to link to a song on Grooveshark
        def getSongURLFromSongID(self, songID):
-               song = GrooveSong(self.cacheDir)
+               cacheDir = os.path.join(self.cacheDir, 'groovesong')
+               song = GrooveSong(cacheDir)
                return song.getSongURL(songID)
 
        # Get the url to link to a song on Grooveshark
@@ -484,17 +538,18 @@ class GrooveAPI:
 import sys
 res = []
 groovesharkApi = GrooveAPI('/tmp')
-res = groovesharkApi.login(sys.argv[1], sys.argv[2])
+#res = groovesharkApi.login(sys.argv[1], sys.argv[2])
 #res = groovesharkApi.getSongSearchResults('jimmy jazz', 3)
 #res = groovesharkApi.getPopularSongsToday()
-#res = groovesharkApi.getSongURLFromSongID('27425375')
+res = groovesharkApi.getSongURLFromSongID('27425375')
 #res = groovesharkApi.getAlbumSearchResults('london calling', 3)
 #res = groovesharkApi.getArtistSearchResults('the clash', 3)
 #res = groovesharkApi.getUserFavoriteSongs()
-res = groovesharkApi.getUserPlaylists()
+#res = groovesharkApi.getUserPlaylists()
 #res = groovesharkApi.getSongInfo('27425375')
 #res = groovesharkApi.getPlaylistSongs(40902662)
 #res = groovesharkApi.addUserFavoriteSong('27425375')
+#res = groovesharkApi.logout()
 
 pprint.pprint(res)