clean up request
Browse files- pytube/request.py +31 -21
- pytube/streams.py +3 -3
- tests/test_request.py +4 -4
- tests/test_streams.py +16 -20
pytube/request.py
CHANGED
|
@@ -1,39 +1,49 @@
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
"""Implements a simple wrapper around urlopen."""
|
|
|
|
| 3 |
from urllib.request import Request
|
| 4 |
from urllib.request import urlopen
|
| 5 |
|
| 6 |
|
| 7 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
"""Send an http GET request.
|
| 9 |
|
| 10 |
:param str url:
|
| 11 |
The URL to perform the GET request for.
|
| 12 |
-
:
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
Returns the response body in chunks via a generator.
|
| 16 |
-
:param int chunk_size:
|
| 17 |
-
The size in bytes of each chunk. Defaults to 8*1024
|
| 18 |
"""
|
|
|
|
| 19 |
|
| 20 |
-
req = Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
| 21 |
-
response = urlopen(req)
|
| 22 |
-
|
| 23 |
-
if streaming:
|
| 24 |
-
return stream_response(response, chunk_size)
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
while True:
|
| 36 |
buf = response.read(chunk_size)
|
| 37 |
if not buf:
|
| 38 |
break
|
| 39 |
yield buf
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
"""Implements a simple wrapper around urlopen."""
|
| 3 |
+
from typing import Any, Iterable, Dict
|
| 4 |
from urllib.request import Request
|
| 5 |
from urllib.request import urlopen
|
| 6 |
|
| 7 |
|
| 8 |
+
def _execute_request(url:str) -> Any:
|
| 9 |
+
return urlopen(Request(url, headers={"User-Agent": "Mozilla/5.0"}))
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def get(url) -> str:
|
| 13 |
"""Send an http GET request.
|
| 14 |
|
| 15 |
:param str url:
|
| 16 |
The URL to perform the GET request for.
|
| 17 |
+
:rtype: str
|
| 18 |
+
:returns:
|
| 19 |
+
UTF-8 encoded string of response
|
|
|
|
|
|
|
|
|
|
| 20 |
"""
|
| 21 |
+
return _execute_request(url).read().decode("utf-8")
|
| 22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
+
def stream(url: str, chunk_size:int = 8192) -> Iterable[bytes]:
|
| 25 |
+
"""Read the response in chunks.
|
| 26 |
+
:param str url:
|
| 27 |
+
The URL to perform the GET request for.
|
| 28 |
+
:param int chunk_size:
|
| 29 |
+
The size in bytes of each chunk. Defaults to 8*1024
|
| 30 |
+
:rtype: Iterable[bytes]
|
| 31 |
+
"""
|
| 32 |
+
response = _execute_request(url)
|
| 33 |
while True:
|
| 34 |
buf = response.read(chunk_size)
|
| 35 |
if not buf:
|
| 36 |
break
|
| 37 |
yield buf
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def headers(url: str) -> Dict:
|
| 41 |
+
"""Fetch headers returned http GET request.
|
| 42 |
+
|
| 43 |
+
:param str url:
|
| 44 |
+
The URL to perform the GET request for.
|
| 45 |
+
:rtype: dict
|
| 46 |
+
:returns:
|
| 47 |
+
dictionary of lowercase headers
|
| 48 |
+
"""
|
| 49 |
+
return {k.lower(): v for k, v in _execute_request(url).info().items()}
|
pytube/streams.py
CHANGED
|
@@ -160,7 +160,7 @@ class Stream:
|
|
| 160 |
Filesize (in bytes) of the stream.
|
| 161 |
"""
|
| 162 |
if self._filesize is None:
|
| 163 |
-
headers = request.
|
| 164 |
self._filesize = int(headers["content-length"])
|
| 165 |
return self._filesize
|
| 166 |
|
|
@@ -239,7 +239,7 @@ class Stream:
|
|
| 239 |
)
|
| 240 |
|
| 241 |
with open(file_path, "wb") as fh:
|
| 242 |
-
for chunk in request.
|
| 243 |
# reduce the (bytes) remainder by the length of the chunk.
|
| 244 |
bytes_remaining -= len(chunk)
|
| 245 |
# send to the on_progress callback.
|
|
@@ -258,7 +258,7 @@ class Stream:
|
|
| 258 |
"downloading (%s total bytes) file to BytesIO buffer", self.filesize,
|
| 259 |
)
|
| 260 |
|
| 261 |
-
for chunk in request.
|
| 262 |
# reduce the (bytes) remainder by the length of the chunk.
|
| 263 |
bytes_remaining -= len(chunk)
|
| 264 |
# send to the on_progress callback.
|
|
|
|
| 160 |
Filesize (in bytes) of the stream.
|
| 161 |
"""
|
| 162 |
if self._filesize is None:
|
| 163 |
+
headers = request.headers(self.url)
|
| 164 |
self._filesize = int(headers["content-length"])
|
| 165 |
return self._filesize
|
| 166 |
|
|
|
|
| 239 |
)
|
| 240 |
|
| 241 |
with open(file_path, "wb") as fh:
|
| 242 |
+
for chunk in request.stream(self.url):
|
| 243 |
# reduce the (bytes) remainder by the length of the chunk.
|
| 244 |
bytes_remaining -= len(chunk)
|
| 245 |
# send to the on_progress callback.
|
|
|
|
| 258 |
"downloading (%s total bytes) file to BytesIO buffer", self.filesize,
|
| 259 |
)
|
| 260 |
|
| 261 |
+
for chunk in request.stream(self.url):
|
| 262 |
# reduce the (bytes) remainder by the length of the chunk.
|
| 263 |
bytes_remaining -= len(chunk)
|
| 264 |
# send to the on_progress callback.
|
tests/test_request.py
CHANGED
|
@@ -7,7 +7,7 @@ from pytube import request
|
|
| 7 |
|
| 8 |
|
| 9 |
@mock.patch("pytube.request.urlopen")
|
| 10 |
-
def
|
| 11 |
fake_stream_binary = [
|
| 12 |
iter(os.urandom(8 * 1024)),
|
| 13 |
iter(os.urandom(8 * 1024)),
|
|
@@ -17,18 +17,18 @@ def test_get_streaming(mock_urlopen):
|
|
| 17 |
response = mock.Mock()
|
| 18 |
response.read.side_effect = fake_stream_binary
|
| 19 |
mock_urlopen.return_value = response
|
| 20 |
-
response = request.
|
| 21 |
call_count = len(list(response))
|
| 22 |
|
| 23 |
assert call_count == 3
|
| 24 |
|
| 25 |
|
| 26 |
@mock.patch("pytube.request.urlopen")
|
| 27 |
-
def
|
| 28 |
response = mock.Mock()
|
| 29 |
response.info.return_value = {"content-length": "16384"}
|
| 30 |
mock_urlopen.return_value = response
|
| 31 |
-
response = request.
|
| 32 |
assert response == {"content-length": "16384"}
|
| 33 |
|
| 34 |
|
|
|
|
| 7 |
|
| 8 |
|
| 9 |
@mock.patch("pytube.request.urlopen")
|
| 10 |
+
def test_streaming(mock_urlopen):
|
| 11 |
fake_stream_binary = [
|
| 12 |
iter(os.urandom(8 * 1024)),
|
| 13 |
iter(os.urandom(8 * 1024)),
|
|
|
|
| 17 |
response = mock.Mock()
|
| 18 |
response.read.side_effect = fake_stream_binary
|
| 19 |
mock_urlopen.return_value = response
|
| 20 |
+
response = request.stream("http://fakeassurl.gov")
|
| 21 |
call_count = len(list(response))
|
| 22 |
|
| 23 |
assert call_count == 3
|
| 24 |
|
| 25 |
|
| 26 |
@mock.patch("pytube.request.urlopen")
|
| 27 |
+
def test_headers(mock_urlopen):
|
| 28 |
response = mock.Mock()
|
| 29 |
response.info.return_value = {"content-length": "16384"}
|
| 30 |
mock_urlopen.return_value = response
|
| 31 |
+
response = request.headers("http://fakeassurl.gov")
|
| 32 |
assert response == {"content-length": "16384"}
|
| 33 |
|
| 34 |
|
tests/test_streams.py
CHANGED
|
@@ -8,8 +8,8 @@ from pytube import Stream
|
|
| 8 |
|
| 9 |
|
| 10 |
def test_filesize(cipher_signature, mocker):
|
| 11 |
-
mocker.patch.object(request, "
|
| 12 |
-
request.
|
| 13 |
assert cipher_signature.streams.first().filesize == 6796391
|
| 14 |
|
| 15 |
|
|
@@ -36,12 +36,10 @@ def test_title(cipher_signature):
|
|
| 36 |
|
| 37 |
|
| 38 |
def test_download(cipher_signature, mocker):
|
| 39 |
-
mocker.patch.object(request, "
|
| 40 |
-
request.
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
iter([str(random.getrandbits(8 * 1024))]),
|
| 44 |
-
]
|
| 45 |
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
|
| 46 |
stream = cipher_signature.streams.first()
|
| 47 |
stream.download()
|
|
@@ -61,12 +59,11 @@ def test_on_progress_hook(cipher_signature, mocker):
|
|
| 61 |
callback_fn = mock.MagicMock()
|
| 62 |
cipher_signature.register_on_progress_callback(callback_fn)
|
| 63 |
|
| 64 |
-
mocker.patch.object(request, "
|
| 65 |
-
request.
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
]
|
| 70 |
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
|
| 71 |
stream = cipher_signature.streams.first()
|
| 72 |
stream.download()
|
|
@@ -81,12 +78,11 @@ def test_on_complete_hook(cipher_signature, mocker):
|
|
| 81 |
callback_fn = mock.MagicMock()
|
| 82 |
cipher_signature.register_on_complete_callback(callback_fn)
|
| 83 |
|
| 84 |
-
mocker.patch.object(request, "
|
| 85 |
-
request.
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
]
|
| 90 |
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
|
| 91 |
stream = cipher_signature.streams.first()
|
| 92 |
stream.download()
|
|
|
|
| 8 |
|
| 9 |
|
| 10 |
def test_filesize(cipher_signature, mocker):
|
| 11 |
+
mocker.patch.object(request, "headers")
|
| 12 |
+
request.headers.return_value = {"content-length": "6796391"}
|
| 13 |
assert cipher_signature.streams.first().filesize == 6796391
|
| 14 |
|
| 15 |
|
|
|
|
| 36 |
|
| 37 |
|
| 38 |
def test_download(cipher_signature, mocker):
|
| 39 |
+
mocker.patch.object(request, "headers")
|
| 40 |
+
request.headers.return_value = {"content-length": "16384"}
|
| 41 |
+
mocker.patch.object(request, "stream")
|
| 42 |
+
request.stream.return_value = iter([str(random.getrandbits(8 * 1024))])
|
|
|
|
|
|
|
| 43 |
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
|
| 44 |
stream = cipher_signature.streams.first()
|
| 45 |
stream.download()
|
|
|
|
| 59 |
callback_fn = mock.MagicMock()
|
| 60 |
cipher_signature.register_on_progress_callback(callback_fn)
|
| 61 |
|
| 62 |
+
mocker.patch.object(request, "headers")
|
| 63 |
+
request.headers.return_value = {"content-length": "16384"}
|
| 64 |
+
mocker.patch.object(request, "stream")
|
| 65 |
+
request.stream.return_value = iter([str(random.getrandbits(8 * 1024))])
|
| 66 |
+
|
|
|
|
| 67 |
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
|
| 68 |
stream = cipher_signature.streams.first()
|
| 69 |
stream.download()
|
|
|
|
| 78 |
callback_fn = mock.MagicMock()
|
| 79 |
cipher_signature.register_on_complete_callback(callback_fn)
|
| 80 |
|
| 81 |
+
mocker.patch.object(request, "headers")
|
| 82 |
+
request.headers.return_value = {"content-length": "16384"}
|
| 83 |
+
mocker.patch.object(request, "stream")
|
| 84 |
+
request.stream.return_value = iter([str(random.getrandbits(8 * 1024))])
|
| 85 |
+
|
|
|
|
| 86 |
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
|
| 87 |
stream = cipher_signature.streams.first()
|
| 88 |
stream.download()
|