don't download real youtube data when testing playlist
Browse files- pytube/contrib/playlist.py +11 -7
- pytube/helpers.py +1 -2
- pytube/itags.py +1 -1
- pytube/query.py +9 -4
- pytube/request.py +2 -2
- tests/contrib/test_playlist.py +4 -1
pytube/contrib/playlist.py
CHANGED
@@ -45,10 +45,9 @@ class Playlist:
|
|
45 |
and returns the "load more" url if found.
|
46 |
"""
|
47 |
match = re.search(
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
)
|
52 |
if match:
|
53 |
return "https://www.youtube.com" + match.group(1)
|
54 |
|
@@ -79,7 +78,9 @@ class Playlist:
|
|
79 |
)
|
80 |
# remove duplicates
|
81 |
link_list.extend(list(OrderedDict.fromkeys(videos)))
|
82 |
-
load_more_url = self._find_load_more_url(
|
|
|
|
|
83 |
|
84 |
return link_list
|
85 |
|
@@ -120,7 +121,7 @@ class Playlist:
|
|
120 |
download_path: Optional[str] = None,
|
121 |
prefix_number: bool = True,
|
122 |
reverse_numbering: bool = False,
|
123 |
-
resolution:str = "720p"
|
124 |
) -> None:
|
125 |
"""Download all the videos in the the playlist. Initially, download
|
126 |
resolution is 720p (or highest available), later more option
|
@@ -158,7 +159,10 @@ class Playlist:
|
|
158 |
if not self.suppress_exception:
|
159 |
raise e
|
160 |
else:
|
161 |
-
dl_stream =
|
|
|
|
|
|
|
162 |
assert dl_stream is not None
|
163 |
|
164 |
logger.debug("download path: %s", download_path)
|
|
|
45 |
and returns the "load more" url if found.
|
46 |
"""
|
47 |
match = re.search(
|
48 |
+
r"data-uix-load-more-href=\"(/browse_ajax\?" 'action_continuation=.*?)"',
|
49 |
+
req,
|
50 |
+
)
|
|
|
51 |
if match:
|
52 |
return "https://www.youtube.com" + match.group(1)
|
53 |
|
|
|
78 |
)
|
79 |
# remove duplicates
|
80 |
link_list.extend(list(OrderedDict.fromkeys(videos)))
|
81 |
+
load_more_url = self._find_load_more_url(
|
82 |
+
load_more["load_more_widget_html"],
|
83 |
+
)
|
84 |
|
85 |
return link_list
|
86 |
|
|
|
121 |
download_path: Optional[str] = None,
|
122 |
prefix_number: bool = True,
|
123 |
reverse_numbering: bool = False,
|
124 |
+
resolution: str = "720p",
|
125 |
) -> None:
|
126 |
"""Download all the videos in the the playlist. Initially, download
|
127 |
resolution is 720p (or highest available), later more option
|
|
|
159 |
if not self.suppress_exception:
|
160 |
raise e
|
161 |
else:
|
162 |
+
dl_stream = (
|
163 |
+
yt.streams.get_by_resolution(resolution=resolution)
|
164 |
+
or yt.streams.get_lowest_resolution()
|
165 |
+
)
|
166 |
assert dl_stream is not None
|
167 |
|
168 |
logger.debug("download path: %s", download_path)
|
pytube/helpers.py
CHANGED
@@ -102,9 +102,8 @@ def create_logger(level: int = logging.ERROR) -> logging.Logger:
|
|
102 |
return logger
|
103 |
|
104 |
|
105 |
-
T = TypeVar(
|
106 |
|
107 |
|
108 |
def cache(func: Callable[..., T]) -> T:
|
109 |
return functools.lru_cache()(func) # type: ignore
|
110 |
-
|
|
|
102 |
return logger
|
103 |
|
104 |
|
105 |
+
T = TypeVar("T")
|
106 |
|
107 |
|
108 |
def cache(func: Callable[..., T]) -> T:
|
109 |
return functools.lru_cache()(func) # type: ignore
|
|
pytube/itags.py
CHANGED
@@ -118,5 +118,5 @@ def get_format_profile(itag: int) -> Dict:
|
|
118 |
"is_3d": itag in _3D,
|
119 |
"is_hdr": itag in HDR,
|
120 |
"fps": 60 if itag in _60FPS else 30,
|
121 |
-
"is_dash": itag in DASH_MP4_VIDEO or itag in DASH_MP4_AUDIO
|
122 |
}
|
|
|
118 |
"is_3d": itag in _3D,
|
119 |
"is_hdr": itag in HDR,
|
120 |
"fps": 60 if itag in _60FPS else 30,
|
121 |
+
"is_dash": itag in DASH_MP4_VIDEO or itag in DASH_MP4_AUDIO,
|
122 |
}
|
pytube/query.py
CHANGED
@@ -221,7 +221,7 @@ class StreamQuery:
|
|
221 |
"""
|
222 |
return self
|
223 |
|
224 |
-
def get_by_itag(self, itag:int) -> Optional[Stream]:
|
225 |
"""Get the corresponding :class:`Stream <Stream>` for a given itag.
|
226 |
|
227 |
:param int itag:
|
@@ -234,7 +234,7 @@ class StreamQuery:
|
|
234 |
"""
|
235 |
return self.itag_index.get(int(itag))
|
236 |
|
237 |
-
def get_by_resolution(self, resolution:str) -> Optional[Stream]:
|
238 |
"""Get the corresponding :class:`Stream <Stream>` for a given resolution.
|
239 |
Stream must be a progressive mp4.
|
240 |
|
@@ -247,7 +247,7 @@ class StreamQuery:
|
|
247 |
|
248 |
"""
|
249 |
return self.filter(
|
250 |
-
progressive=True, subtype=
|
251 |
).first()
|
252 |
|
253 |
def get_lowest_resolution(self) -> Optional[Stream]:
|
@@ -259,7 +259,12 @@ class StreamQuery:
|
|
259 |
not found.
|
260 |
|
261 |
"""
|
262 |
-
return
|
|
|
|
|
|
|
|
|
|
|
263 |
|
264 |
def first(self) -> Optional[Stream]:
|
265 |
"""Get the first :class:`Stream <Stream>` in the results.
|
|
|
221 |
"""
|
222 |
return self
|
223 |
|
224 |
+
def get_by_itag(self, itag: int) -> Optional[Stream]:
|
225 |
"""Get the corresponding :class:`Stream <Stream>` for a given itag.
|
226 |
|
227 |
:param int itag:
|
|
|
234 |
"""
|
235 |
return self.itag_index.get(int(itag))
|
236 |
|
237 |
+
def get_by_resolution(self, resolution: str) -> Optional[Stream]:
|
238 |
"""Get the corresponding :class:`Stream <Stream>` for a given resolution.
|
239 |
Stream must be a progressive mp4.
|
240 |
|
|
|
247 |
|
248 |
"""
|
249 |
return self.filter(
|
250 |
+
progressive=True, subtype="mp4", resolution=resolution
|
251 |
).first()
|
252 |
|
253 |
def get_lowest_resolution(self) -> Optional[Stream]:
|
|
|
259 |
not found.
|
260 |
|
261 |
"""
|
262 |
+
return (
|
263 |
+
self.filter(progressive=True, subtype="mp4")
|
264 |
+
.order_by("resolution")
|
265 |
+
.desc()
|
266 |
+
.last()
|
267 |
+
)
|
268 |
|
269 |
def first(self) -> Optional[Stream]:
|
270 |
"""Get the first :class:`Stream <Stream>` in the results.
|
pytube/request.py
CHANGED
@@ -5,7 +5,7 @@ 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 |
|
@@ -21,7 +21,7 @@ def get(url) -> str:
|
|
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.
|
|
|
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 |
|
|
|
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.
|
tests/contrib/test_playlist.py
CHANGED
@@ -1,10 +1,13 @@
|
|
1 |
# -*- coding: utf-8 -*-
|
|
|
|
|
2 |
from pytube import Playlist
|
3 |
|
4 |
|
|
|
5 |
def test_title():
|
6 |
list_key = "PLsyeobzWxl7poL9JTVyndKe62ieoN-MZ3"
|
7 |
-
url = "https://www.
|
8 |
pl = Playlist(url)
|
9 |
pl_title = pl.title()
|
10 |
assert pl_title == "Python Tutorial for Beginners"
|
|
|
1 |
# -*- coding: utf-8 -*-
|
2 |
+
from unittest.mock import MagicMock
|
3 |
+
|
4 |
from pytube import Playlist
|
5 |
|
6 |
|
7 |
+
|
8 |
def test_title():
|
9 |
list_key = "PLsyeobzWxl7poL9JTVyndKe62ieoN-MZ3"
|
10 |
+
url = "https://www.fakeurl.com/playlist?list=" + list_key
|
11 |
pl = Playlist(url)
|
12 |
pl_title = pl.title()
|
13 |
assert pl_title == "Python Tutorial for Beginners"
|