Skip to content

Commit bc39347

Browse files
authored
Merge pull request jdepoix#468 from jdepoix/feature/ipblocked-exception-on-429
Added IpBlocked exception when status 429 is returned
2 parents a43e601 + c1c137a commit bc39347

File tree

4 files changed

+26
-2
lines changed

4 files changed

+26
-2
lines changed

youtube_transcript_api/_api.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from typing import Optional, Iterable
33

44
from requests import Session
5+
from requests.adapters import HTTPAdapter
6+
from urllib3 import Retry
57

68
from .proxies import ProxyConfig, GenericProxyConfig
79

@@ -38,6 +40,13 @@ def __init__(
3840
http_client.proxies = proxy_config.to_requests_dict()
3941
if proxy_config.prevent_keeping_connections_alive:
4042
http_client.headers.update({"Connection": "close"})
43+
if proxy_config.retries_when_blocked > 0:
44+
retry_config = Retry(
45+
total=proxy_config.retries_when_blocked,
46+
status_forcelist=[429],
47+
)
48+
http_client.mount("http://", HTTPAdapter(max_retries=retry_config))
49+
http_client.mount("https://", HTTPAdapter(max_retries=retry_config))
4150
self._fetcher = TranscriptListFetcher(http_client, proxy_config=proxy_config)
4251

4352
def fetch(

youtube_transcript_api/_errors.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,9 @@ class RequestBlocked(CouldNotRetrieveTranscript):
152152
)
153153
WITH_GENERIC_PROXY_CAUSE_MESSAGE = (
154154
"YouTube is blocking your requests, despite you using proxies. Keep in mind "
155-
"a proxy is just a way to hide your real IP behind the IP of that proxy, but "
156-
"there is no guarantee that the IP of that proxy won't be blocked as well.\n\n"
155+
"that a proxy is just a way to hide your real IP behind the IP of that proxy, "
156+
"but there is no guarantee that the IP of that proxy won't be blocked as "
157+
"well.\n\n"
157158
"The only truly reliable way to prevent IP blocks is rotating through a large "
158159
"pool of residential IPs, by using a provider like Webshare "
159160
"(https://www.webshare.io/?referral_code=w0xno53eb50g), which provides you "

youtube_transcript_api/_transcripts.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class _PlayabilityFailedReason(str, Enum):
9292

9393
def _raise_http_errors(response: Response, video_id: str) -> Response:
9494
try:
95+
if response.status_code == 429:
96+
raise IpBlocked(video_id)
9597
response.raise_for_status()
9698
return response
9799
except HTTPError as error:

youtube_transcript_api/test/test_api.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,18 @@ def test_get_transcript__exception_if_youtube_request_limit_reached(
272272
with self.assertRaises(IpBlocked):
273273
YouTubeTranscriptApi().fetch("abc")
274274

275+
def test_get_transcript__exception_if_timedtext_request_limit_reached(
276+
self,
277+
):
278+
httpretty.register_uri(
279+
httpretty.GET,
280+
"https://www.youtube.com/api/timedtext",
281+
status=429,
282+
)
283+
284+
with self.assertRaises(IpBlocked):
285+
YouTubeTranscriptApi().fetch("abc")
286+
275287
def test_fetch__exception_if_age_restricted(self):
276288
httpretty.register_uri(
277289
httpretty.POST,

0 commit comments

Comments
 (0)