hbmartin commited on
Commit
766d18b
·
1 Parent(s): d214d8a

add -r resolution download tag

Browse files
Files changed (3) hide show
  1. pytube/cli.py +43 -7
  2. pytube/contrib/playlist.py +6 -19
  3. tests/test_cli.py +5 -5
pytube/cli.py CHANGED
@@ -11,7 +11,7 @@ import sys
11
  from io import BufferedWriter
12
  from typing import Tuple, Any, Optional, List
13
 
14
- from pytube import __version__, CaptionQuery
15
  from pytube import YouTube
16
 
17
 
@@ -36,9 +36,11 @@ def main():
36
  if args.build_playback_report:
37
  build_playback_report(youtube)
38
  if args.itag:
39
- download(youtube=youtube, itag=args.itag)
40
  if hasattr(args, "caption_code"):
41
  download_caption(youtube=youtube, lang_code=args.caption_code)
 
 
42
 
43
 
44
  def _parse_args(
@@ -51,6 +53,9 @@ def _parse_args(
51
  parser.add_argument(
52
  "--itag", type=int, help="The itag for the desired stream",
53
  )
 
 
 
54
  parser.add_argument(
55
  "-l",
56
  "--list",
@@ -165,12 +170,18 @@ def on_progress(
165
  display_progress_bar(bytes_received, filesize)
166
 
167
 
168
- def download(youtube: YouTube, itag: int) -> None:
 
 
 
 
 
 
169
  """Start downloading a YouTube video.
170
 
171
  :param YouTube youtube:
172
  A valid YouTube object.
173
- :param str itag:
174
  YouTube format identifier code.
175
 
176
  """
@@ -184,10 +195,35 @@ def download(youtube: YouTube, itag: int) -> None:
184
  sys.exit()
185
 
186
  youtube.register_on_progress_callback(on_progress)
187
- print("\n{fn} | {fs} bytes".format(fn=stream.default_filename, fs=stream.filesize,))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  try:
189
- stream.download()
190
- sys.stdout.write("\n")
191
  except KeyboardInterrupt:
192
  sys.exit()
193
 
 
11
  from io import BufferedWriter
12
  from typing import Tuple, Any, Optional, List
13
 
14
+ from pytube import __version__, CaptionQuery, Stream
15
  from pytube import YouTube
16
 
17
 
 
36
  if args.build_playback_report:
37
  build_playback_report(youtube)
38
  if args.itag:
39
+ download_by_itag(youtube=youtube, itag=args.itag)
40
  if hasattr(args, "caption_code"):
41
  download_caption(youtube=youtube, lang_code=args.caption_code)
42
+ if args.resolution:
43
+ download_by_resolution(youtube=youtube, resolution=args.resolution)
44
 
45
 
46
  def _parse_args(
 
53
  parser.add_argument(
54
  "--itag", type=int, help="The itag for the desired stream",
55
  )
56
+ parser.add_argument(
57
+ "-r", "--resolution", type=str, help="The resolution for the desired stream",
58
+ )
59
  parser.add_argument(
60
  "-l",
61
  "--list",
 
170
  display_progress_bar(bytes_received, filesize)
171
 
172
 
173
+ def _download(stream: Stream) -> None:
174
+ print("\n{fn} | {fs} bytes".format(fn=stream.default_filename, fs=stream.filesize, ))
175
+ stream.download()
176
+ sys.stdout.write("\n")
177
+
178
+
179
+ def download_by_itag(youtube: YouTube, itag: int) -> None:
180
  """Start downloading a YouTube video.
181
 
182
  :param YouTube youtube:
183
  A valid YouTube object.
184
+ :param int itag:
185
  YouTube format identifier code.
186
 
187
  """
 
195
  sys.exit()
196
 
197
  youtube.register_on_progress_callback(on_progress)
198
+
199
+ try:
200
+ _download(stream)
201
+ except KeyboardInterrupt:
202
+ sys.exit()
203
+
204
+
205
+ def download_by_resolution(youtube: YouTube, resolution: str) -> None:
206
+ """Start downloading a YouTube video.
207
+
208
+ :param YouTube youtube:
209
+ A valid YouTube object.
210
+ :param str resolution:
211
+ YouTube video resolution.
212
+
213
+ """
214
+ # TODO(nficano): allow download target to be specified
215
+ # TODO(nficano): allow dash itags to be selected
216
+ stream = youtube.streams.get_by_resolution(resolution)
217
+ if stream is None:
218
+ print("Could not find a stream with resolution: {resolution}".format(resolution=resolution))
219
+ print("Try one of these:")
220
+ display_streams(youtube)
221
+ sys.exit()
222
+
223
+ youtube.register_on_progress_callback(on_progress)
224
+
225
  try:
226
+ _download(stream)
 
227
  except KeyboardInterrupt:
228
  sys.exit()
229
 
pytube/contrib/playlist.py CHANGED
@@ -19,25 +19,14 @@ class Playlist:
19
  """
20
 
21
  def __init__(self, url: str, suppress_exception: bool = False):
22
- self.playlist_url = url
23
  self.video_urls: List[str] = []
24
  self.suppress_exception = suppress_exception
 
25
 
26
- def construct_playlist_url(self) -> str:
27
- """There are two kinds of playlist urls in YouTube. One that contains
28
- watch?v= in URL, another one contains the "playlist?list=" portion. It
29
- is preferable to work with the later one.
30
-
31
- :return: playlist url
32
- """
33
-
34
- if "watch?v=" in self.playlist_url:
35
  base_url = "https://www.youtube.com/playlist?list="
36
- playlist_code = self.playlist_url.split("&list=")[1]
37
- return base_url + playlist_code
38
-
39
- # url is already in the desired format, so just return it
40
- return self.playlist_url
41
 
42
  @staticmethod
43
  def _find_load_more_url(req: str) -> Optional[str]:
@@ -59,8 +48,7 @@ class Playlist:
59
  It's an alternative for BeautifulSoup
60
  """
61
 
62
- url = self.construct_playlist_url()
63
- req = request.get(url)
64
 
65
  # split the page source by line and process each line
66
  content = [x for x in req.split("\n") if "pl-video-title-link" in x]
@@ -176,8 +164,7 @@ class Playlist:
176
 
177
  def title(self) -> Optional[str]:
178
  """return playlist title (name)"""
179
- url = self.construct_playlist_url()
180
- req = request.get(url)
181
  open_tag = "<title>"
182
  end_tag = "</title>"
183
  pattern = re.compile(open_tag + "(.+?)" + end_tag)
 
19
  """
20
 
21
  def __init__(self, url: str, suppress_exception: bool = False):
 
22
  self.video_urls: List[str] = []
23
  self.suppress_exception = suppress_exception
24
+ self.playlist_url:str = url
25
 
26
+ if "watch?v=" in url:
 
 
 
 
 
 
 
 
27
  base_url = "https://www.youtube.com/playlist?list="
28
+ playlist_code = self.playlist_url.split("&list=")[1] # TODO: should be parse q
29
+ self.playlist_url = base_url + playlist_code
 
 
 
30
 
31
  @staticmethod
32
  def _find_load_more_url(req: str) -> Optional[str]:
 
48
  It's an alternative for BeautifulSoup
49
  """
50
 
51
+ req = request.get(self.playlist_url)
 
52
 
53
  # split the page source by line and process each line
54
  content = [x for x in req.split("\n") if "pl-video-title-link" in x]
 
164
 
165
  def title(self) -> Optional[str]:
166
  """return playlist title (name)"""
167
+ req = request.get(self.playlist_url)
 
168
  open_tag = "<title>"
169
  end_tag = "</title>"
170
  pattern = re.compile(open_tag + "(.+?)" + end_tag)
tests/test_cli.py CHANGED
@@ -16,7 +16,7 @@ def test_download_when_itag_not_found(youtube):
16
  youtube.streams.all.return_value = []
17
  youtube.streams.get_by_itag.return_value = None
18
  with pytest.raises(SystemExit):
19
- cli.download(youtube, 123)
20
  youtube.streams.get_by_itag.assert_called_with(123)
21
 
22
 
@@ -28,7 +28,7 @@ def test_download_when_itag_is_found(youtube, stream):
28
  with patch.object(
29
  youtube.streams, "get_by_itag", wraps=youtube.streams.get_by_itag
30
  ) as wrapped_itag:
31
- cli.download(youtube, 123)
32
  wrapped_itag.assert_called_with(123)
33
  youtube.register_on_progress_callback.assert_called_with(cli.on_progress)
34
  stream.download.assert_called()
@@ -115,14 +115,14 @@ def test_parse_args_truthy():
115
 
116
 
117
  @mock.patch("pytube.cli.YouTube.__init__", return_value=None)
118
- def test_main_download(youtube):
119
  parser = argparse.ArgumentParser()
120
  args = parse_args(parser, ["urlhere", "--itag=10"])
121
  cli._parse_args = MagicMock(return_value=args)
122
- cli.download = MagicMock()
123
  cli.main()
124
  youtube.assert_called()
125
- cli.download.assert_called()
126
 
127
 
128
  @mock.patch("pytube.cli.YouTube.__init__", return_value=None)
 
16
  youtube.streams.all.return_value = []
17
  youtube.streams.get_by_itag.return_value = None
18
  with pytest.raises(SystemExit):
19
+ cli.download_by_itag(youtube, 123)
20
  youtube.streams.get_by_itag.assert_called_with(123)
21
 
22
 
 
28
  with patch.object(
29
  youtube.streams, "get_by_itag", wraps=youtube.streams.get_by_itag
30
  ) as wrapped_itag:
31
+ cli.download_by_itag(youtube, 123)
32
  wrapped_itag.assert_called_with(123)
33
  youtube.register_on_progress_callback.assert_called_with(cli.on_progress)
34
  stream.download.assert_called()
 
115
 
116
 
117
  @mock.patch("pytube.cli.YouTube.__init__", return_value=None)
118
+ def test_main_download_by_itag(youtube):
119
  parser = argparse.ArgumentParser()
120
  args = parse_args(parser, ["urlhere", "--itag=10"])
121
  cli._parse_args = MagicMock(return_value=args)
122
+ cli.download_by_itag = MagicMock()
123
  cli.main()
124
  youtube.assert_called()
125
+ cli.download_by_itag.assert_called()
126
 
127
 
128
  @mock.patch("pytube.cli.YouTube.__init__", return_value=None)