diff options
Diffstat (limited to 'pandora/client.py')
-rw-r--r-- | pandora/client.py | 36 |
1 files changed, 36 insertions, 0 deletions
diff --git a/pandora/client.py b/pandora/client.py index a98056c..ff27678 100644 --- a/pandora/client.py +++ b/pandora/client.py | |||
@@ -14,6 +14,7 @@ For simplicity use a client builder from pandora.clientbuilder to create an | |||
14 | instance of a client. | 14 | instance of a client. |
15 | """ | 15 | """ |
16 | from . import errors | 16 | from . import errors |
17 | from .ratelimit import WarningTokenBucket | ||
17 | 18 | ||
18 | 19 | ||
19 | class BaseAPIClient: | 20 | class BaseAPIClient: |
@@ -36,7 +37,27 @@ class BaseAPIClient: | |||
36 | partner_password, | 37 | partner_password, |
37 | device, | 38 | device, |
38 | default_audio_quality=MED_AUDIO_QUALITY, | 39 | default_audio_quality=MED_AUDIO_QUALITY, |
40 | rate_limiter=WarningTokenBucket, | ||
39 | ): | 41 | ): |
42 | """Initialize an API Client | ||
43 | |||
44 | transport | ||
45 | instance of a Pandora transport | ||
46 | partner_user | ||
47 | partner username | ||
48 | partner_password | ||
49 | partner password | ||
50 | device | ||
51 | device type identifier | ||
52 | default_audio_quality | ||
53 | audio quality level, one of the *_AUDIO_QUALITY constants in the | ||
54 | BaseAPIClient class | ||
55 | rate_limiter | ||
56 | class (not instance) implementing the pandora.ratelimit.TokenBucket | ||
57 | interface. Used by various client components to handle rate | ||
58 | limits with the Pandora API. The default rate limited warns when | ||
59 | the rate limit has been exceeded but does not enforce the limit. | ||
60 | """ | ||
40 | self.transport = transport | 61 | self.transport = transport |
41 | self.partner_user = partner_user | 62 | self.partner_user = partner_user |
42 | self.partner_password = partner_password | 63 | self.partner_password = partner_password |
@@ -45,6 +66,17 @@ class BaseAPIClient: | |||
45 | self.username = None | 66 | self.username = None |
46 | self.password = None | 67 | self.password = None |
47 | 68 | ||
69 | # Global rate limiter for all methods, allows a call rate of 2 calls | ||
70 | # per second. This limit is based on nothing but seems sane to prevent | ||
71 | # runaway code from hitting Pandora too hard. | ||
72 | self._api_limiter = rate_limiter(120, 1, 1) | ||
73 | |||
74 | # Rate limiter for the get_playlist API which has a much lower | ||
75 | # server-side limit than other APIs. This was determined | ||
76 | # experimentally. This is applied before and in addition to the global | ||
77 | # API rate limit. | ||
78 | self._playlist_limiter = rate_limiter(5, 1, 1) | ||
79 | |||
48 | def _partner_login(self): | 80 | def _partner_login(self): |
49 | partner = self.transport( | 81 | partner = self.transport( |
50 | "auth.partnerLogin", | 82 | "auth.partnerLogin", |
@@ -98,6 +130,8 @@ class BaseAPIClient: | |||
98 | return [] | 130 | return [] |
99 | 131 | ||
100 | def __call__(self, method, **kwargs): | 132 | def __call__(self, method, **kwargs): |
133 | self._api_limiter.consume(1) | ||
134 | |||
101 | try: | 135 | try: |
102 | return self.transport(method, **kwargs) | 136 | return self.transport(method, **kwargs) |
103 | except errors.InvalidAuthToken: | 137 | except errors.InvalidAuthToken: |
@@ -125,6 +159,8 @@ class APIClient(BaseAPIClient): | |||
125 | def get_playlist(self, station_token, additional_urls=None): | 159 | def get_playlist(self, station_token, additional_urls=None): |
126 | from .models.playlist import Playlist | 160 | from .models.playlist import Playlist |
127 | 161 | ||
162 | self._playlist_limiter.consume(1) | ||
163 | |||
128 | if additional_urls is None: | 164 | if additional_urls is None: |
129 | additional_urls = [] | 165 | additional_urls = [] |
130 | 166 | ||