only download once regardless of flags
Browse files- README.md +1 -1
- pytube/cli.py +21 -19
- tests/test_cli.py +3 -4
README.md
CHANGED
@@ -232,7 +232,7 @@ pytube also ships with a tiny cli interface for downloading and probing videos.
|
|
232 |
Let's start with downloading:
|
233 |
|
234 |
```bash
|
235 |
-
$ pytube3 http://youtube.com/watch?v=9bZkp7q19f0 --itag=
|
236 |
```
|
237 |
To view available streams:
|
238 |
|
|
|
232 |
Let's start with downloading:
|
233 |
|
234 |
```bash
|
235 |
+
$ pytube3 http://youtube.com/watch?v=9bZkp7q19f0 --itag=18
|
236 |
```
|
237 |
To view available streams:
|
238 |
|
pytube/cli.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
|
2 |
"""A simple command line application to download youtube videos."""
|
3 |
|
4 |
import argparse
|
@@ -59,23 +59,24 @@ def main():
|
|
59 |
parser.print_help()
|
60 |
sys.exit(1)
|
61 |
|
|
|
|
|
62 |
if args.list:
|
63 |
-
display_streams(
|
64 |
|
65 |
-
|
66 |
-
build_playback_report(
|
67 |
|
68 |
-
|
69 |
-
download(
|
70 |
|
71 |
|
72 |
-
def build_playback_report(
|
73 |
"""Serialize the request data to json for offline debugging.
|
74 |
|
75 |
-
:param
|
76 |
-
A
|
77 |
"""
|
78 |
-
yt = YouTube(url)
|
79 |
ts = int(dt.datetime.utcnow().timestamp())
|
80 |
fp = os.path.join(
|
81 |
os.getcwd(), "yt-video-{yt.video_id}-{ts}.json.gz".format(yt=yt, ts=ts),
|
@@ -89,7 +90,7 @@ def build_playback_report(url: str) -> None:
|
|
89 |
fh.write(
|
90 |
json.dumps(
|
91 |
{
|
92 |
-
"url":
|
93 |
"js": js,
|
94 |
"watch_html": watch_html,
|
95 |
"video_info": vid_info,
|
@@ -147,21 +148,23 @@ def on_progress(
|
|
147 |
display_progress_bar(bytes_received, filesize)
|
148 |
|
149 |
|
150 |
-
def download(
|
151 |
"""Start downloading a YouTube video.
|
152 |
|
153 |
-
:param
|
154 |
-
A valid YouTube
|
155 |
:param str itag:
|
156 |
YouTube format identifier code.
|
157 |
|
158 |
"""
|
159 |
# TODO(nficano): allow download target to be specified
|
160 |
# TODO(nficano): allow dash itags to be selected
|
161 |
-
yt
|
162 |
stream = yt.streams.get_by_itag(int(itag))
|
163 |
if stream is None:
|
164 |
-
print("Could not find a stream with itag: "
|
|
|
|
|
165 |
sys.exit()
|
166 |
print("\n{fn} | {fs} bytes".format(fn=stream.default_filename, fs=stream.filesize,))
|
167 |
try:
|
@@ -171,14 +174,13 @@ def download(url: str, itag: str) -> None:
|
|
171 |
sys.exit()
|
172 |
|
173 |
|
174 |
-
def display_streams(
|
175 |
"""Probe YouTube video and lists its available formats.
|
176 |
|
177 |
-
:param
|
178 |
A valid YouTube watch URL.
|
179 |
|
180 |
"""
|
181 |
-
yt = YouTube(url)
|
182 |
for stream in yt.streams.all():
|
183 |
print(stream)
|
184 |
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
"""A simple command line application to download youtube videos."""
|
3 |
|
4 |
import argparse
|
|
|
59 |
parser.print_help()
|
60 |
sys.exit(1)
|
61 |
|
62 |
+
youtube = YouTube(args.url)
|
63 |
+
|
64 |
if args.list:
|
65 |
+
display_streams(youtube)
|
66 |
|
67 |
+
if args.build_playback_report:
|
68 |
+
build_playback_report(youtube)
|
69 |
|
70 |
+
if args.itag:
|
71 |
+
download(yt=youtube, itag=args.itag)
|
72 |
|
73 |
|
74 |
+
def build_playback_report(yt: YouTube) -> None:
|
75 |
"""Serialize the request data to json for offline debugging.
|
76 |
|
77 |
+
:param YouTube yt:
|
78 |
+
A YouTube object.
|
79 |
"""
|
|
|
80 |
ts = int(dt.datetime.utcnow().timestamp())
|
81 |
fp = os.path.join(
|
82 |
os.getcwd(), "yt-video-{yt.video_id}-{ts}.json.gz".format(yt=yt, ts=ts),
|
|
|
90 |
fh.write(
|
91 |
json.dumps(
|
92 |
{
|
93 |
+
"url": yt.watch_url,
|
94 |
"js": js,
|
95 |
"watch_html": watch_html,
|
96 |
"video_info": vid_info,
|
|
|
148 |
display_progress_bar(bytes_received, filesize)
|
149 |
|
150 |
|
151 |
+
def download(yt: YouTube, itag: str) -> None:
|
152 |
"""Start downloading a YouTube video.
|
153 |
|
154 |
+
:param YouTube yt:
|
155 |
+
A valid YouTube object.
|
156 |
:param str itag:
|
157 |
YouTube format identifier code.
|
158 |
|
159 |
"""
|
160 |
# TODO(nficano): allow download target to be specified
|
161 |
# TODO(nficano): allow dash itags to be selected
|
162 |
+
yt.register_on_progress_callback(on_progress)
|
163 |
stream = yt.streams.get_by_itag(int(itag))
|
164 |
if stream is None:
|
165 |
+
print("Could not find a stream with itag: {itag}".format(itag=itag))
|
166 |
+
print("Try one of these:")
|
167 |
+
display_streams(yt)
|
168 |
sys.exit()
|
169 |
print("\n{fn} | {fs} bytes".format(fn=stream.default_filename, fs=stream.filesize,))
|
170 |
try:
|
|
|
174 |
sys.exit()
|
175 |
|
176 |
|
177 |
+
def display_streams(yt: YouTube) -> None:
|
178 |
"""Probe YouTube video and lists its available formats.
|
179 |
|
180 |
+
:param YouTube yt:
|
181 |
A valid YouTube watch URL.
|
182 |
|
183 |
"""
|
|
|
184 |
for stream in yt.streams.all():
|
185 |
print(stream)
|
186 |
|
tests/test_cli.py
CHANGED
@@ -5,9 +5,8 @@ from pytube import cli
|
|
5 |
|
6 |
|
7 |
@mock.patch("pytube.cli.YouTube")
|
8 |
-
|
9 |
-
|
10 |
-
instance = MockYouTube.return_value
|
11 |
instance.prefetch_descramble.return_value = None
|
12 |
instance.streams = mock.Mock()
|
13 |
-
cli.download(
|
|
|
5 |
|
6 |
|
7 |
@mock.patch("pytube.cli.YouTube")
|
8 |
+
def test_download(youtube):
|
9 |
+
instance = youtube.return_value
|
|
|
10 |
instance.prefetch_descramble.return_value = None
|
11 |
instance.streams = mock.Mock()
|
12 |
+
cli.download(instance, 123)
|