lots of whitespace
Browse files- .flake8 +1 -1
- pytube/__main__.py +7 -28
- pytube/captions.py +0 -3
- pytube/cipher.py +11 -11
- pytube/exceptions.py +1 -0
- pytube/helpers.py +1 -0
- pytube/monostate.py +1 -0
- pytube/query.py +1 -0
- pytube/request.py +3 -4
- pytube/version.py +1 -0
- tests/test_streams.py +3 -1
.flake8
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
[flake8]
|
2 |
-
ignore = E231,E203,W503,Q000,WPS111,WPS305,WPS348,WPS602,D400,DAR201,S101,DAR101,C812,D104,I001,WPS306,WPS214,D401,WPS229,WPS420
|
3 |
max-line-length = 89
|
4 |
|
5 |
[isort]
|
|
|
1 |
[flake8]
|
2 |
+
ignore = E231,E203,W503,Q000,WPS111,WPS305,WPS348,WPS602,D400,DAR201,S101,DAR101,C812,D104,I001,WPS306,WPS214,D401,WPS229,WPS420,WPS230,WPS414,WPS114,WPS226,WPS442,C819,WPS601,T001,RST304
|
3 |
max-line-length = 89
|
4 |
|
5 |
[isort]
|
pytube/__main__.py
CHANGED
@@ -218,8 +218,7 @@ class YouTube:
|
|
218 |
:rtype: List[Caption]
|
219 |
"""
|
220 |
raw_tracks = (
|
221 |
-
self.player_response
|
222 |
-
.get("captions", {})
|
223 |
.get("playerCaptionsTracklistRenderer", {})
|
224 |
.get("captionTracks", [])
|
225 |
)
|
@@ -267,9 +266,7 @@ class YouTube:
|
|
267 |
|
268 |
"""
|
269 |
return self.player_config_args.get("title") or (
|
270 |
-
self.player_response
|
271 |
-
.get("videoDetails", {})
|
272 |
-
.get("title")
|
273 |
)
|
274 |
|
275 |
@property
|
@@ -280,9 +277,7 @@ class YouTube:
|
|
280 |
|
281 |
"""
|
282 |
return self.vid_descr or (
|
283 |
-
self.player_response
|
284 |
-
.get("videoDetails", {})
|
285 |
-
.get("shortDescription")
|
286 |
)
|
287 |
|
288 |
@property
|
@@ -292,11 +287,7 @@ class YouTube:
|
|
292 |
:rtype: float
|
293 |
|
294 |
"""
|
295 |
-
return (
|
296 |
-
self.player_response
|
297 |
-
.get("videoDetails", {})
|
298 |
-
.get("averageRating")
|
299 |
-
)
|
300 |
|
301 |
@property
|
302 |
def length(self) -> int:
|
@@ -307,11 +298,7 @@ class YouTube:
|
|
307 |
"""
|
308 |
return int(
|
309 |
self.player_config_args.get("length_seconds")
|
310 |
-
or (
|
311 |
-
self.player_response
|
312 |
-
.get("videoDetails", {})
|
313 |
-
.get("lengthSeconds")
|
314 |
-
)
|
315 |
)
|
316 |
|
317 |
@property
|
@@ -321,22 +308,14 @@ class YouTube:
|
|
321 |
:rtype: str
|
322 |
|
323 |
"""
|
324 |
-
return int(
|
325 |
-
self.player_response
|
326 |
-
.get("videoDetails", {})
|
327 |
-
.get("viewCount")
|
328 |
-
)
|
329 |
|
330 |
@property
|
331 |
def author(self) -> str:
|
332 |
"""Get the video author.
|
333 |
:rtype: str
|
334 |
"""
|
335 |
-
return (
|
336 |
-
self.player_response
|
337 |
-
.get("videoDetails", {})
|
338 |
-
.get("author", "unknown")
|
339 |
-
)
|
340 |
|
341 |
def register_on_progress_callback(self, func: OnProgress):
|
342 |
"""Register a download progress callback function post initialization.
|
|
|
218 |
:rtype: List[Caption]
|
219 |
"""
|
220 |
raw_tracks = (
|
221 |
+
self.player_response.get("captions", {})
|
|
|
222 |
.get("playerCaptionsTracklistRenderer", {})
|
223 |
.get("captionTracks", [])
|
224 |
)
|
|
|
266 |
|
267 |
"""
|
268 |
return self.player_config_args.get("title") or (
|
269 |
+
self.player_response.get("videoDetails", {}).get("title")
|
|
|
|
|
270 |
)
|
271 |
|
272 |
@property
|
|
|
277 |
|
278 |
"""
|
279 |
return self.vid_descr or (
|
280 |
+
self.player_response.get("videoDetails", {}).get("shortDescription")
|
|
|
|
|
281 |
)
|
282 |
|
283 |
@property
|
|
|
287 |
:rtype: float
|
288 |
|
289 |
"""
|
290 |
+
return self.player_response.get("videoDetails", {}).get("averageRating")
|
|
|
|
|
|
|
|
|
291 |
|
292 |
@property
|
293 |
def length(self) -> int:
|
|
|
298 |
"""
|
299 |
return int(
|
300 |
self.player_config_args.get("length_seconds")
|
301 |
+
or (self.player_response.get("videoDetails", {}).get("lengthSeconds"))
|
|
|
|
|
|
|
|
|
302 |
)
|
303 |
|
304 |
@property
|
|
|
308 |
:rtype: str
|
309 |
|
310 |
"""
|
311 |
+
return int(self.player_response.get("videoDetails", {}).get("viewCount"))
|
|
|
|
|
|
|
|
|
312 |
|
313 |
@property
|
314 |
def author(self) -> str:
|
315 |
"""Get the video author.
|
316 |
:rtype: str
|
317 |
"""
|
318 |
+
return self.player_response.get("videoDetails", {}).get("author", "unknown")
|
|
|
|
|
|
|
|
|
319 |
|
320 |
def register_on_progress_callback(self, func: OnProgress):
|
321 |
"""Register a download progress callback function post initialization.
|
pytube/captions.py
CHANGED
@@ -4,10 +4,8 @@ import os
|
|
4 |
import time
|
5 |
import xml.etree.ElementTree as ElementTree
|
6 |
from typing import Dict, Optional
|
7 |
-
|
8 |
from pytube import request
|
9 |
from html import unescape
|
10 |
-
|
11 |
from pytube.helpers import safe_filename, target_directory
|
12 |
|
13 |
|
@@ -105,7 +103,6 @@ class Caption:
|
|
105 |
:type filename_prefix: str or None
|
106 |
|
107 |
:rtype: str
|
108 |
-
|
109 |
"""
|
110 |
if title.endswith(".srt") or title.endswith(".xml"):
|
111 |
filename = ".".join(title.split(".")[:-1])
|
|
|
4 |
import time
|
5 |
import xml.etree.ElementTree as ElementTree
|
6 |
from typing import Dict, Optional
|
|
|
7 |
from pytube import request
|
8 |
from html import unescape
|
|
|
9 |
from pytube.helpers import safe_filename, target_directory
|
10 |
|
11 |
|
|
|
103 |
:type filename_prefix: str or None
|
104 |
|
105 |
:rtype: str
|
|
|
106 |
"""
|
107 |
if title.endswith(".srt") or title.endswith(".xml"):
|
108 |
filename = ".".join(title.split(".")[:-1])
|
pytube/cipher.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
# -*- coding: utf-8 -*-
|
|
|
2 |
"""
|
3 |
This module contains all logic necessary to decipher the signature.
|
4 |
|
@@ -26,7 +27,6 @@ logger = create_logger()
|
|
26 |
|
27 |
def get_initial_function_name(js: str) -> str:
|
28 |
"""Extract the name of the function responsible for computing the signature.
|
29 |
-
|
30 |
:param str js:
|
31 |
The contents of the base.js asset file.
|
32 |
:rtype: str
|
@@ -52,10 +52,10 @@ def get_initial_function_name(js: str) -> str:
|
|
52 |
logger.debug("finding initial function name")
|
53 |
for pattern in function_patterns:
|
54 |
regex = re.compile(pattern)
|
55 |
-
|
56 |
-
if
|
57 |
logger.debug("finished regex search, matched: %s", pattern)
|
58 |
-
return
|
59 |
|
60 |
raise RegexMatchError(caller="get_initial_function_name", pattern="multiple")
|
61 |
|
@@ -112,11 +112,11 @@ def get_transform_object(js: str, var: str) -> List[str]:
|
|
112 |
pattern = r"var %s={(.*?)};" % re.escape(var)
|
113 |
logger.debug("getting transform object")
|
114 |
regex = re.compile(pattern, flags=re.DOTALL)
|
115 |
-
|
116 |
-
if not
|
117 |
raise RegexMatchError(caller="get_transform_object", pattern=pattern)
|
118 |
|
119 |
-
return
|
120 |
|
121 |
|
122 |
def get_transform_map(js: str, var: str) -> Dict:
|
@@ -245,10 +245,10 @@ def parse_function(js_func: str) -> Tuple[str, int]:
|
|
245 |
logger.debug("parsing transform function")
|
246 |
pattern = r"\w+\.(\w+)\(\w,(\d+)\)"
|
247 |
regex = re.compile(pattern)
|
248 |
-
|
249 |
-
if not
|
250 |
raise RegexMatchError(caller="parse_function", pattern=pattern)
|
251 |
-
fn_name, fn_arg =
|
252 |
return fn_name, int(fn_arg)
|
253 |
|
254 |
|
@@ -269,7 +269,7 @@ def get_signature(js: str, ciphered_signature: str) -> str:
|
|
269 |
transform_plan = get_transform_plan(js)
|
270 |
var, _ = transform_plan[0].split(".")
|
271 |
transform_map = get_transform_map(js, var)
|
272 |
-
signature =
|
273 |
|
274 |
for js_func in transform_plan:
|
275 |
name, argument = parse_function(js_func)
|
|
|
1 |
# -*- coding: utf-8 -*-
|
2 |
+
|
3 |
"""
|
4 |
This module contains all logic necessary to decipher the signature.
|
5 |
|
|
|
27 |
|
28 |
def get_initial_function_name(js: str) -> str:
|
29 |
"""Extract the name of the function responsible for computing the signature.
|
|
|
30 |
:param str js:
|
31 |
The contents of the base.js asset file.
|
32 |
:rtype: str
|
|
|
52 |
logger.debug("finding initial function name")
|
53 |
for pattern in function_patterns:
|
54 |
regex = re.compile(pattern)
|
55 |
+
function_match = regex.search(js)
|
56 |
+
if function_match:
|
57 |
logger.debug("finished regex search, matched: %s", pattern)
|
58 |
+
return function_match.group(1)
|
59 |
|
60 |
raise RegexMatchError(caller="get_initial_function_name", pattern="multiple")
|
61 |
|
|
|
112 |
pattern = r"var %s={(.*?)};" % re.escape(var)
|
113 |
logger.debug("getting transform object")
|
114 |
regex = re.compile(pattern, flags=re.DOTALL)
|
115 |
+
transform_match = regex.search(js)
|
116 |
+
if not transform_match:
|
117 |
raise RegexMatchError(caller="get_transform_object", pattern=pattern)
|
118 |
|
119 |
+
return transform_match.group(1).replace("\n", " ").split(", ")
|
120 |
|
121 |
|
122 |
def get_transform_map(js: str, var: str) -> Dict:
|
|
|
245 |
logger.debug("parsing transform function")
|
246 |
pattern = r"\w+\.(\w+)\(\w,(\d+)\)"
|
247 |
regex = re.compile(pattern)
|
248 |
+
parse_match = regex.search(js_func)
|
249 |
+
if not parse_match:
|
250 |
raise RegexMatchError(caller="parse_function", pattern=pattern)
|
251 |
+
fn_name, fn_arg = parse_match.groups()
|
252 |
return fn_name, int(fn_arg)
|
253 |
|
254 |
|
|
|
269 |
transform_plan = get_transform_plan(js)
|
270 |
var, _ = transform_plan[0].split(".")
|
271 |
transform_map = get_transform_map(js, var)
|
272 |
+
signature = list(ciphered_signature)
|
273 |
|
274 |
for js_func in transform_plan:
|
275 |
name, argument = parse_function(js_func)
|
pytube/exceptions.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
# -*- coding: utf-8 -*-
|
|
|
2 |
"""Library specific exception definitions."""
|
3 |
from typing import Union, Pattern
|
4 |
|
|
|
1 |
# -*- coding: utf-8 -*-
|
2 |
+
|
3 |
"""Library specific exception definitions."""
|
4 |
from typing import Union, Pattern
|
5 |
|
pytube/helpers.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
# -*- coding: utf-8 -*-
|
|
|
2 |
"""Various helper functions implemented by pytube."""
|
3 |
import functools
|
4 |
import logging
|
|
|
1 |
# -*- coding: utf-8 -*-
|
2 |
+
|
3 |
"""Various helper functions implemented by pytube."""
|
4 |
import functools
|
5 |
import logging
|
pytube/monostate.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
# -*- coding: utf-8 -*-
|
|
|
2 |
import io
|
3 |
from typing import Any, Optional
|
4 |
from typing_extensions import Protocol
|
|
|
1 |
# -*- coding: utf-8 -*-
|
2 |
+
|
3 |
import io
|
4 |
from typing import Any, Optional
|
5 |
from typing_extensions import Protocol
|
pytube/query.py
CHANGED
@@ -233,6 +233,7 @@ class StreamQuery:
|
|
233 |
|
234 |
def get_by_resolution(self, resolution: str) -> Optional[Stream]:
|
235 |
"""Get the corresponding :class:`Stream <Stream>` for a given resolution.
|
|
|
236 |
Stream must be a progressive mp4.
|
237 |
|
238 |
:param str resolution:
|
|
|
233 |
|
234 |
def get_by_resolution(self, resolution: str) -> Optional[Stream]:
|
235 |
"""Get the corresponding :class:`Stream <Stream>` for a given resolution.
|
236 |
+
|
237 |
Stream must be a progressive mp4.
|
238 |
|
239 |
:param str resolution:
|
pytube/request.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
# -*- coding: utf-8 -*-
|
|
|
2 |
"""Implements a simple wrapper around urlopen."""
|
3 |
from typing import Any, Iterable, Dict
|
4 |
from urllib.request import Request
|
@@ -25,10 +26,8 @@ def get(url) -> str:
|
|
25 |
|
26 |
def stream(url: str, chunk_size: int = 8192) -> Iterable[bytes]:
|
27 |
"""Read the response in chunks.
|
28 |
-
:param str url:
|
29 |
-
|
30 |
-
:param int chunk_size:
|
31 |
-
The size in bytes of each chunk. Defaults to 8*1024
|
32 |
:rtype: Iterable[bytes]
|
33 |
"""
|
34 |
response = _execute_request(url)
|
|
|
1 |
# -*- coding: utf-8 -*-
|
2 |
+
|
3 |
"""Implements a simple wrapper around urlopen."""
|
4 |
from typing import Any, Iterable, Dict
|
5 |
from urllib.request import Request
|
|
|
26 |
|
27 |
def stream(url: str, chunk_size: int = 8192) -> Iterable[bytes]:
|
28 |
"""Read the response in chunks.
|
29 |
+
:param str url: The URL to perform the GET request for.
|
30 |
+
:param int chunk_size: The size in bytes of each chunk. Defaults to 8*1024
|
|
|
|
|
31 |
:rtype: Iterable[bytes]
|
32 |
"""
|
33 |
response = _execute_request(url)
|
pytube/version.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
# -*- coding: utf-8 -*-
|
|
|
2 |
__version__ = "9.6.1"
|
3 |
|
4 |
if __name__ == "__main__":
|
|
|
1 |
# -*- coding: utf-8 -*-
|
2 |
+
|
3 |
__version__ = "9.6.1"
|
4 |
|
5 |
if __name__ == "__main__":
|
tests/test_streams.py
CHANGED
@@ -209,7 +209,9 @@ def test_author(cipher_signature):
|
|
209 |
|
210 |
def test_thumbnail_when_in_details(cipher_signature):
|
211 |
expected = "some url"
|
212 |
-
cipher_signature.player_response = {
|
|
|
|
|
213 |
assert cipher_signature.thumbnail_url == expected
|
214 |
|
215 |
|
|
|
209 |
|
210 |
def test_thumbnail_when_in_details(cipher_signature):
|
211 |
expected = "some url"
|
212 |
+
cipher_signature.player_response = {
|
213 |
+
"videoDetails": {"thumbnail": {"thumbnails": [{"url": expected}]}}
|
214 |
+
}
|
215 |
assert cipher_signature.thumbnail_url == expected
|
216 |
|
217 |
|