Fix character encodings and album name.
[clinton/xbmc-groove.git] / resources / lib / GroovesharkAPI.py
index 0c09e25..9b8feda 100644 (file)
@@ -1,4 +1,22 @@
-import socket, hmac, urllib, urllib2, pprint, md5, os, pickle, tempfile, time, re, simplejson
+# Copyright 2011 Stephen Denham
+
+#    This file is part of xbmc-groove.
+#
+#    xbmc-groove is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    xbmc-groove is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with xbmc-groove.  If not, see <http://www.gnu.org/licenses/>.
+
+import urllib2, pprint, md5, os, pickle, tempfile, time, re, simplejson, base64, sys, socket
+from blowfish import Blowfish
 
 SESSION_EXPIRY = 1209600 # 2 weeks
 
@@ -20,28 +38,33 @@ class GrooveAPI:
        _sessionID = ''
        _userID = 0
        _lastSessionTime = 0
-       _lastStreamKey = ''
-       _lastStreamServerID = ''
+       _key = md5.new(os.path.basename("GroovesharkAPI.py")).hexdigest()
+       _debugging = False
 
        # Constructor
-       def __init__(self):
+       def __init__(self, debug):
                
+               self._debugging = debug
                self.simplejson = simplejson
-               socket.setdefaulttimeout(40)
+               if "linux" in sys.platform.lower():
+                       socket.setdefaulttimeout(30)
+                       
                self.cacheDir = os.path.join(tempfile.gettempdir(), 'groovesharkapi')
                if os.path.isdir(self.cacheDir) == False:
                        os.makedirs(self.cacheDir)
-                       print "Made " + self.cacheDir
+                       if self._debugging:
+                               print "Made " + self.cacheDir
                self._getSavedSession()
                # session ids last 2 weeks
                if self._sessionID == '' or time.time()- self._lastSessionTime >= SESSION_EXPIRY:
                        self._sessionID = self._getSessionID()
-                       self._ip = self._getIP()
-                       self._country = self._getCountry()
                        if self._sessionID == '':
                                raise StandardError('Failed to get session id')
                        else:
-                               print "New GrooveAPI session id: " + self._sessionID
+                               if self._debugging:
+                                       print "New GrooveAPI session id: " + self._sessionID
+                               self._ip = self._getIP()
+                               self._country = self._getCountry()
                                self._setSavedSession()
 
        # Call to API
@@ -50,27 +73,44 @@ class GrooveAPI:
                        res = self._getRemote(method, params)
                        url = res['url']
                        postData = res['postData']
+               except:
+                       print "Failed to get request URL and post data"
+                       return []
+               try:
                        req = urllib2.Request(url, postData)
                        response = urllib2.urlopen(req)
                        result = response.read()
-                       print "Response..."
-                       pprint.pprint(result)
+                       if self._debugging:
+                               print "Response..."
+                               pprint.pprint(result)
                        response.close()
                        result = simplejson.loads(result)
                        return result
+               except urllib2.HTTPError, e:
+                       print "HTTP error " + e.code
+               except urllib2.URLError, e:
+                       print "URL error " + e.reason
                except:
+                       print "Request to Grooveshark API failed"
                        return []       
 
+
        # Get the API call
        def _getRemote(self, method, params = {}):
                postData = { "method": method, "sessionid": self._sessionID, "parameters": params }
                postData = simplejson.dumps(postData)
-               url = WEB_APP_URL + "?postData=" + urllib.quote_plus(postData)
+               
+               cipher = Blowfish(self._key)
+               cipher.initCTR()
+               encryptedPostData = cipher.encryptCTR(postData)
+               encryptedPostData = base64.urlsafe_b64encode(encryptedPostData)
+               url = WEB_APP_URL + "?postData=" + encryptedPostData
                req = urllib2.Request(url)
                response = urllib2.urlopen(req)
                result = response.read()
-               print "Request..."
-               pprint.pprint(result)
+               if self._debugging:
+                       print "Request..."
+                       pprint.pprint(result)
                response.close()
                try:
                        result = simplejson.loads(result)
@@ -82,8 +122,8 @@ class GrooveAPI:
        def _getSessionID(self):
                params = {}
                result = self._callRemote('startSession', params)
-               self._lastSessionTime = time.time()
                if 'result' in result:
+                       self._lastSessionTime = time.time()
                        return result['result']['sessionID']
                else:
                        return ''
@@ -120,26 +160,14 @@ class GrooveAPI:
                except:
                        print "An error occurred during save session"
                        pass
-
-       def _setParams(self, params):                   
-               try:
-                       # Create the directory if it doesn't exist.
-                       if not os.path.exists(self.cacheDir):
-                               os.makedirs(self.cacheDir)
-                       path = os.path.join(self.cacheDir, 'params.dmp')
-                       f = open(path, 'wb')
-                       pickle.dump(params, f, protocol=pickle.HIGHEST_PROTOCOL)
-                       f.close()
-               except:
-                       print "An error occurred during save params"
-                       pass
        
        # Get IP
        def _getIP(self):
                try:
-                       myip = urllib2.urlopen('http://whatismyip.org').read()
+                       myip = urllib2.urlopen('http://ipecho.net/plain').read()
                        if re.match("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$", myip):
-                               print "IP is " + myip
+                               if self._debugging:
+                                       print "IP is " + myip
                                return myip
                except:
                        return '0.0.0.0'
@@ -206,8 +234,6 @@ class GrooveAPI:
                params = { "songID": songID, "country": self._country }
                response = self._callRemote("getSubscriberStreamKey", params)
                try: 
-                       self._lastStreamKey = response["result"]["StreamKey"]
-                       self._lastStreamServerID = response["result"]["StreamServerID"]
                        res = response["result"]
                        return res
                except:
@@ -239,7 +265,7 @@ class GrooveAPI:
                
        # Get artists albums
        def getArtistAlbums(self, artistID, limit=ALBUM_LIMIT):
-               result = self._callRemote('getArtistAlbums', {'artistID' : artistID})
+               result = self._callRemote('getArtistVerifiedAlbums', {'artistID' : artistID})
                if 'result' in result:
                        return self._parseAlbums(result, limit)
                else:
@@ -286,7 +312,7 @@ class GrooveAPI:
                if 'result' in result and 'SongID' in result['result']:
                        info = result['result']
                        if 'CoverArtFilename' in info and info['CoverArtFilename'] != None:
-                               info['CoverArtFilename'] = THUMB_URL+info['CoverArtFilename'].encode('ascii', 'ignore')
+                               info['CoverArtFilename'] = THUMB_URL+info['CoverArtFilename'].encode('utf8', 'ignore')
                        else:
                                info['CoverArtFilename'] = 'None'
                        return info
@@ -371,32 +397,53 @@ class GrooveAPI:
                items = self._callRemote("getSimilarArtists", {"artistID": artistId, "limit": limit})
                if 'result' in items:
                        i = 0
-                       list = []
+                       itemList = []
                        artists = items['result']['artists']
                        while(i < len(artists)):
                                s = artists[i]
-                               list.append([s['artistName'].encode('ascii', 'ignore'),\
+                               itemList.append([s['artistName'].encode('utf8', 'ignore'),\
                                s['artistID']])
                                i = i + 1
-                       return list
+                       return itemList
                else:
                        return []               
        
+       def getDoesArtistExist(self, artistId):
+               response = self._callRemote("getDoesArtistExist", {"artistID": artistId})
+               if 'result' in response and response['result'] == True:
+                       return True
+               else:
+                       return False
+
+       def getDoesAlbumExist(self, albumId):
+               response = self._callRemote("getDoesAlbumExist", {"albumID": albumId})
+               if 'result' in response and response['result'] == True:
+                       return True
+               else:
+                       return False
+
+       def getDoesSongExist(self, songId):
+               response = self._callRemote("getDoesSongExist", {"songID": songId})
+               if 'result' in response and response['result'] == True:
+                       return True
+               else:
+                       return False
+               
        # After 30s play time
-       def markStreamKeyOver30Secs(self):
-               params = { "streamKey" : self._lastStreamKey, "streamServerID" : self._lastStreamServerID }
+       def markStreamKeyOver30Secs(self, streamKey, streamServerID):
+               params = { "streamKey" : streamKey, "streamServerID" : streamServerID }
                self._callRemote("markStreamKeyOver30Secs", params)
 
        # Song complete
-       def markSongComplete(self, songid):             
-               params = { "songID" : songid, "streamKey" : self._lastStreamKey, "streamServerID" : self._lastStreamServerID }
+       def markSongComplete(self, songid, streamKey, streamServerID):          
+               params = { "songID" : songid, "streamKey" : streamKey, "streamServerID" : streamServerID }
                self._callRemote("markSongComplete", params)
-
+               
        # Extract song data     
        def _parseSongs(self, items, limit=0):
                if 'result' in items:
                        i = 0
-                       list = []
+                       itemList = []
                        index = ''
                        l = -1
                        try:
@@ -434,18 +481,26 @@ class GrooveAPI:
                                        info = self.getSongsInfo(s['SongID'])
                                        coverart = info['CoverArtFilename']
                                elif s['CoverArtFilename'] != None:
-                                       coverart = THUMB_URL+s['CoverArtFilename'].encode('ascii', 'ignore')
+                                       coverart = THUMB_URL+s['CoverArtFilename'].encode('utf8', 'ignore')
                                else:
                                        coverart = 'None'
-                               list.append([s['SongName'].encode('ascii', 'ignore'),\
+                               if 'Name' in s:
+                                       name = s['Name']
+                               else:
+                                       name = s['SongName']
+                               if 'AlbumName' in s:
+                                       albumName = s['AlbumName']
+                               else:
+                                       albumName = ""
+                               itemList.append([name.encode('utf8', 'ignore'),\
                                s['SongID'],\
-                               s['AlbumName'].encode('ascii', 'ignore'),\
+                               albumName.encode('utf8', 'ignore'),\
                                s['AlbumID'],\
-                               s['ArtistName'].encode('ascii', 'ignore'),\
+                               s['ArtistName'].encode('utf8', 'ignore'),\
                                s['ArtistID'],\
                                coverart])
                                i = i + 1
-                       return list
+                       return itemList
                else:
                        return []
 
@@ -453,14 +508,14 @@ class GrooveAPI:
        def _parseArtists(self, items):
                if 'result' in items:
                        i = 0
-                       list = []
+                       itemList = []
                        artists = items['result']['artists']
                        while(i < len(artists)):
                                s = artists[i]
-                               list.append([s['ArtistName'].encode('ascii', 'ignore'),\
+                               itemList.append([s['ArtistName'].encode('utf8', 'ignore'),\
                                s['ArtistID']])
                                i = i + 1
-                       return list
+                       return itemList
                else:
                        return []
 
@@ -468,7 +523,7 @@ class GrooveAPI:
        def _parseAlbums(self, items, limit=0):
                if 'result' in items:
                        i = 0
-                       list = []
+                       itemList = []
                        try:
                                albums = items['result']['albums']
                        except:
@@ -479,23 +534,27 @@ class GrooveAPI:
                                l = limit
                        while(i < l):
                                s = albums[i]
+                               if 'Name' in s:
+                                       name = s['Name'].encode('utf8', 'ignore')
+                               else:
+                                       name = s['AlbumName'].encode('utf8', 'ignore')
                                if 'CoverArtFilename' in s and s['CoverArtFilename'] != None:
-                                       coverart = THUMB_URL+s['CoverArtFilename'].encode('ascii', 'ignore')
+                                       coverart = THUMB_URL+s['CoverArtFilename'].encode('utf8', 'ignore')
                                else:
                                        coverart = 'None'
-                               list.append([s['ArtistName'].encode('ascii', 'ignore'),\
+                               itemList.append([s['ArtistName'].encode('utf8', 'ignore'),\
                                s['ArtistID'],\
-                               s['AlbumName'].encode('ascii', 'ignore'),\
+                               name,\
                                s['AlbumID'],\
                                coverart])
                                i = i + 1
-                       return list
+                       return itemList
                else:
                        return []
 
        def _parsePlaylists(self, items):
                i = 0
-               list = []
+               itemList = []
                if 'result' in items:
                        playlists = items['result']['playlists']
                elif len(items) > 0:
@@ -505,6 +564,6 @@ class GrooveAPI:
 
                while (i < len(playlists)):
                        s = playlists[i]
-                       list.append([str(s['PlaylistName']).encode('ascii', 'ignore'), s['PlaylistID']])
+                       itemList.append([unicode(s['PlaylistName']).encode('utf8', 'ignore'), s['PlaylistID']])
                        i = i + 1
-               return list
+               return itemList