Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 2 commits
  • 4 files changed
  • 0 commit comments
  • 1 contributor
Commits on Jun 29, 2012
@elElmo elElmo - Restructuring classes to be more suited for server use
- Decoupling user account with general pandora connection
- Modified all getters to require user information
- Fixed test runs
3d1a7a9
Commits on Aug 08, 2012
@elElmo elElmo - Separated client auth from server auth
- Added native proxy support via urllib opener
8b27c3e
View
82 pandora/connection.py
@@ -2,7 +2,6 @@
import urllib
import urllib2
import time
-
import crypt
class AuthenticationError(Exception):
@@ -13,9 +12,6 @@ class PandoraConnection(object):
partner_id = None
partner_auth_token = None
- user_id = None
- user_auth_token = None
-
time_offset = None
PROTOCOL_VERSION = '5'
@@ -26,55 +22,65 @@ class PandoraConnection(object):
AUDIO_FORMAT_MAP = {'aac': 'HTTP_64_AACPLUS_ADTS',
'mp3': 'HTTP_128_MP3'}
- def __init__(self):
+ def __init__(self, proxies={}):
self.rid = "%07i" % (time.time() % 1e7)
self.timedelta = 0
+ self.opener = urllib2.build_opener((urllib2.ProxyHandler(proxies)))
+ self.authenticate_connection()
- def authenticate(self, user, pwd):
+ def authenticate_connection(self):
try:
# partner login
- partner = self.do_request('auth.partnerLogin', True, False, deviceModel=self.DEVICE_MODEL, username=self.PARTNER_USERNAME, password=self.PARTNER_PASSWORD, version=self.PROTOCOL_VERSION)
+ partner = self.do_request('auth.partnerLogin', True, False, None, deviceModel=self.DEVICE_MODEL, username=self.PARTNER_USERNAME, password=self.PARTNER_PASSWORD, version=self.PROTOCOL_VERSION)
self.partner_id = partner['partnerId']
self.partner_auth_token = partner['partnerAuthToken']
# sync
pandora_time = int(crypt.pandora_decrypt(partner['syncTime'])[4:14])
self.time_offset = pandora_time - time.time()
-
- # user login
- user = self.do_request('auth.userLogin', True, True, username=user, password=pwd, loginType="user")
- self.user_id = user['userId']
- self.user_auth_token = user['userAuthToken']
+ #
+ ## user login
+ #user = self.do_request('auth.userLogin', True, True, username=user, password=pwd, loginType="user")
+ #self.user_id = user['userId']
+ #self.user_auth_token = user['userAuthToken']
return True
- except:
+ except Exception as e:
self.partner_id = None
self.partner_auth_token = None
- self.user_id = None
- self.user_auth_token = None
- self.time_offset = None
+ #self.user_id = None
+ #self.user_auth_token = None
+ #self.time_offset = None
+ print repr(e)
return False
+
+ def get_user_authentication(self, user, password, cryptedCred = None):
+ try:
+ auth = self.do_request('auth.userLogin', True, True, None, username = user, password = password, loginType = "user")
+ return {'userId': auth['userId'], 'userAuthToken': auth['userAuthToken']}
+ except:
+ return None
+
+ def get_stations(self, user):
+ return self.do_request('user.getStationList', False, True, user)['stations']
- def get_stations(self):
- return self.do_request('user.getStationList', False, True)['stations']
-
- def get_fragment(self, stationId=None, additional_format="mp3"):
- songlist = self.do_request('station.getPlaylist', True, True, stationToken=stationId, additionalAudioUrl=self.AUDIO_FORMAT_MAP[additional_format])['items']
+ def get_fragment(self, user, stationId=None, additional_format="mp3"):
+ songlist = self.do_request('station.getPlaylist', True, True, user, stationToken=stationId, additionalAudioUrl=self.AUDIO_FORMAT_MAP[additional_format])['items']
self.curStation = stationId
self.curFormat = format
return songlist
- def do_request(self, method, secure, crypted, **kwargs):
+ def do_request(self, method, secure, crypted, user, **kwargs):
url_arg_strings = []
if self.partner_id:
url_arg_strings.append('partner_id=%s' % self.partner_id)
- if self.user_id:
- url_arg_strings.append('user_id=%s' % self.user_id)
- if self.user_auth_token:
- url_arg_strings.append('auth_token=%s'%urllib.quote_plus(self.user_auth_token))
+ if user:
+ url_arg_strings.append('user_id=%s' % user['userId'])
+ if user:
+ url_arg_strings.append('auth_token=%s'%urllib.quote_plus(user['userAuthToken']))
elif self.partner_auth_token:
url_arg_strings.append('auth_token=%s' % urllib.quote_plus(self.partner_auth_token))
@@ -83,18 +89,22 @@ def do_request(self, method, secure, crypted, **kwargs):
if self.time_offset:
kwargs['syncTime'] = int(time.time()+self.time_offset)
- if self.user_auth_token:
- kwargs['userAuthToken'] = self.user_auth_token
+ if user:
+ kwargs['userAuthToken'] = user['userAuthToken']
elif self.partner_auth_token:
kwargs['partnerAuthToken'] = self.partner_auth_token
data = json.dumps(kwargs)
if crypted:
- data = crypt.pandora_encrypt(data)
+ try:
+ cryptedAppend = kwargs.pop('cryptedAppend')
+ data = crypt.pandora_join_encrypt(json.dumps(kwargs), cryptedAppend)
+ except KeyError:
+ data = crypt.pandora_encrypt(data)
# execute request
- req = urllib2.Request(url, data, {'User-agent': "02strich", 'Content-type': 'text/plain'})
- response = urllib2.urlopen(req)
+ req = urllib2.Request(url, data, {'User-agent': "walkman", 'Content-type': 'text/plain'})
+ response = self.opener.open(req)
text = response.read()
# parse result
@@ -122,11 +132,15 @@ def do_request(self, method, secure, crypted, **kwargs):
password = raw_input()
# authenticate
- print "Authenthicated: " + str(pandora.authenticate(username, password))
+ pandora.authenticate_connection
+ user = pandora.get_user_authentication(username, password)
+ print "Authenthicated: " + str(user)
+ if not user:
+ raise Exception("Not authenticated")
# output stations (without QuickMix)
print "users stations:"
- for station in pandora.getStations():
+ for station in pandora.get_stations(user):
if station['isQuickMix']:
quickmix = station
print "\t" + station['stationName'] + "*"
@@ -144,4 +158,4 @@ def do_request(self, method, secure, crypted, **kwargs):
#f = open('test.mp3', 'wb')
#f.write(u.read())
#f.close()
- #u.close()
+ #u.close()
View
7 pandora/crypt.py
@@ -180,3 +180,10 @@ def pandora_encrypt(s):
def pandora_decrypt(s):
return "".join([blowfish_decode.decrypt(pad(s[i:i+16].decode('hex'), 8)) for i in xrange(0, len(s), 16)]).rstrip('\x08')
+
+def pandora_join_encrypt(s, append):
+ crypted = pandora_encrypt(s.replace('}', ','))
+ crypted += append
+ crypted += pandora_encrypt('}')
+ return crypted
+
View
57 pandora/pandora.py
@@ -3,44 +3,34 @@
from connection import PandoraConnection
class Pandora(object):
- station_id = None
- authenticated = False
backlog = []
- def __init__(self):
- self.connection = PandoraConnection()
+ def __init__(self, connection):
+ self.connection = connection
def authenticate(self, username, password):
- self.authenticated = self.connection.authenticate(username, password)
- return self.authenticated
+ return self.connection.get_user_authentication(username, password)
- def get_station_list(self):
- return self.connection.get_stations()
+ def get_station_list(self, user):
+ return self.connection.get_stations(user)
- def switch_station(self, station_id):
- if type(station_id) is dict:
- station_id = station_id['stationId']
-
- if not self.authenticated: raise ValueError("User not yet authenticated")
-
+ def switch_station(self, user, station_id):
self.backlog = []
- self.station_id = station_id
- self.backlog = self.connection.get_fragment(station_id) + self.backlog
+ self.backlog = self.connection.get_fragment(user, station_id) + self.backlog
- def get_next_song(self):
- if not self.authenticated: raise ValueError("User not yet authenticated")
- if not self.station_id: raise ValueError("No station selected")
-
+ def get_next_song(self, user, stationId):
# get more songs
- if len(self.backlog) < 2:
- self.backlog = self.connection.get_fragment(self.station_id) + self.backlog
+ # if len(self.backlog) < 2:
+ # self.backlog = self.connection.get_fragment(user, stationId) + self.backlog
# get next song
- return self.backlog.pop()
+ # return self.backlog.pop()
+ return self.connection.get_fragment(user, stationId)
if __name__ == "__main__":
- pandora = Pandora()
+ connection = PandoraConnection()
+ pandora = Pandora(connection)
# read username
print "Username: "
@@ -59,29 +49,34 @@ def get_next_song(self):
urllib2.install_opener(opener)
# authenticate
- print "Authenthicated: " + str(pandora.authenticate(username, password))
+ user = pandora.authenticate(username, password)
+ print "Authenticated: " + str(user)
# output stations (without QuickMix)
print "users stations:"
- for station in pandora.getStationList():
+ for station in pandora.get_station_list(user):
if station['isQuickMix']:
quickmix = station
print "\t" + station['stationName'] + "*"
else:
print "\t" + station['stationName']
+ print "\n\n\n"
+ quickmix = quickmix['stationId'] if type(quickmix) is dict else quickmix
# switch to quickmix station
- pandora.switchStation(quickmix)
+ pandora.switch_station(user, quickmix)
# get one song from quickmix
print "next song from quickmix:"
- next = pandora.getNextSong()
- print next['artistName'] + ': ' + next['songName']
- print next['audioUrlMap']['highQuality']['audioUrl']
+ n = pandora.get_next_song(user, quickmix)[0]
+ print n['artistName'] + ': ' + n['songName']
+ print n['additionalAudioUrl']
+ print n['audioUrlMap']['highQuality']['audioUrl']
+ print n.keys()
# download it
#u = urllib2.urlopen(next['audioUrlMap']['highQuality']['audioUrl'])
#f = open('test.mp3', 'wb')
#f.write(u.read())
#f.close()
- #u.close()
+ #u.close()
View
2  setup.py
@@ -2,7 +2,7 @@
from setuptools import setup
setup(name='python-pandora',
- version='0.8-5',
+ version='0.8-6',
description='Library to access pandora.com. Based on the work from http://forum.xbmc.org/showthread.php?t=70471 and from Pithos',
maintainer='Stefan Richter',
maintainer_email='stefan@02strich.de',

No commit comments for this range

Something went wrong with that request. Please try again.