Taylor Fox Dahlin
commited on
Improvement/docs (#924)
Browse files* Moved most of the documentation out of the README and into the readthedocs.
* Created additional documentation in readthedocs.
* Moved bumpversion and twine from packages to dev-packages.
* Updated contributor solicitation to be more general
* Capitalization consistency in Features section
* Added prerequisite of python 3.6+ to documentation.
* Removed travis CI build widget since we're using github actions instead.
* Slight rearrangement of the README to improve flow.
* General cleaning of documentation.
- Pipfile +2 -2
- README.md +73 -244
- docs/index.rst +11 -10
- docs/user/captions.rst +32 -0
- docs/user/cli.rst +1 -1
- docs/user/exceptions.rst +29 -0
- docs/user/install.rst +6 -6
- docs/user/quickstart.rst +24 -171
- docs/user/streams.rst +95 -0
Pipfile
CHANGED
@@ -4,10 +4,10 @@ verify_ssl = true
|
|
4 |
name = "pypi"
|
5 |
|
6 |
[packages]
|
7 |
-
bumpversion = "*"
|
8 |
-
twine = "*"
|
9 |
|
10 |
[dev-packages]
|
|
|
|
|
11 |
black = "==19.10b0"
|
12 |
codecov = "*"
|
13 |
coveralls = "*"
|
|
|
4 |
name = "pypi"
|
5 |
|
6 |
[packages]
|
|
|
|
|
7 |
|
8 |
[dev-packages]
|
9 |
+
bumpversion = "*"
|
10 |
+
twine = "*"
|
11 |
black = "==19.10b0"
|
12 |
codecov = "*"
|
13 |
coveralls = "*"
|
README.md
CHANGED
@@ -4,23 +4,73 @@
|
|
4 |
</p>
|
5 |
<p align="center">
|
6 |
<img src="https://img.shields.io/pypi/v/pytube.svg" alt="pypi">
|
7 |
-
<a href="
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
</p>
|
14 |
</div>
|
15 |
|
16 |
|
17 |
-
###
|
18 |
-
|
|
|
19 |
|
20 |
# pytube
|
21 |
-
*pytube* is a very serious, lightweight, dependency-free Python library (and
|
|
|
22 |
|
23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
To install from pypi with pip:
|
26 |
|
@@ -28,24 +78,20 @@ To install from pypi with pip:
|
|
28 |
$ python -m pip install pytube
|
29 |
```
|
30 |
|
31 |
-
Sometime, the pypi release becomes slightly outdated. To install from the
|
|
|
32 |
|
33 |
```bash
|
34 |
$ python -m pip install git+https://github.com/pytube/pytube
|
35 |
```
|
36 |
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
*pytube* also makes pipelining easy, allowing you to specify callback functions for different download events, such as ``on progress`` or ``on complete``.
|
43 |
-
|
44 |
-
Finally *pytube* also includes a command-line utility, allowing you to quickly download videos right from terminal.
|
45 |
-
|
46 |
-
### Behold, a perfect balance of simplicity versus flexibility:
|
47 |
|
48 |
```python
|
|
|
49 |
>>> YouTube('https://youtu.be/2lAe1cqCOXo').streams.first().download()
|
50 |
>>> yt = YouTube('http://youtube.com/watch?v=2lAe1cqCOXo')
|
51 |
>>> yt.streams
|
@@ -56,231 +102,14 @@ Finally *pytube* also includes a command-line utility, allowing you to quickly d
|
|
56 |
... .download()
|
57 |
```
|
58 |
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
- Easily Register ``on_download_progress`` & ``on_download_complete`` callbacks
|
63 |
-
- Command-line Interfaced Included
|
64 |
-
- Caption Track Support
|
65 |
-
- Outputs Caption Tracks to .srt format (SubRip Subtitle)
|
66 |
-
- Ability to Capture Thumbnail URL.
|
67 |
-
- Extensively Documented Source Code
|
68 |
-
- No Third-Party Dependencies
|
69 |
-
|
70 |
-
## Getting started
|
71 |
-
|
72 |
-
Let's begin with showing how easy it is to download a video with pytube:
|
73 |
-
|
74 |
-
```python
|
75 |
-
>>> from pytube import YouTube
|
76 |
-
>>> YouTube('https://youtube.com/watch?v=2lAe1cqCOXo').streams.first().download()
|
77 |
-
```
|
78 |
-
This example will download the highest quality progressive download stream available.
|
79 |
-
|
80 |
-
Next, let's explore how we would view what video streams are available:
|
81 |
-
|
82 |
-
```python
|
83 |
-
>>> yt = YouTube('https://youtube.com/watch?v=2lAe1cqCOXo')
|
84 |
-
>>> yt.streams
|
85 |
-
[<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2" progressive="True" type="video">,
|
86 |
-
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">,
|
87 |
-
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028" progressive="False" type="video">,
|
88 |
-
<Stream: itag="248" mime_type="video/webm" res="1080p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
89 |
-
<Stream: itag="399" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.08M.08" progressive="False" type="video">,
|
90 |
-
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f" progressive="False" type="video">,
|
91 |
-
<Stream: itag="247" mime_type="video/webm" res="720p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
92 |
-
<Stream: itag="398" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.05M.08" progressive="False" type="video">,
|
93 |
-
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401e" progressive="False" type="video">,
|
94 |
-
<Stream: itag="244" mime_type="video/webm" res="480p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
95 |
-
<Stream: itag="397" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.04M.08" progressive="False" type="video">,
|
96 |
-
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e" progressive="False" type="video">,
|
97 |
-
<Stream: itag="243" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
98 |
-
<Stream: itag="396" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.01M.08" progressive="False" type="video">,
|
99 |
-
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015" progressive="False" type="video">,
|
100 |
-
<Stream: itag="242" mime_type="video/webm" res="240p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
101 |
-
<Stream: itag="395" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.00M.08" progressive="False" type="video">,
|
102 |
-
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c" progressive="False" type="video">,
|
103 |
-
<Stream: itag="278" mime_type="video/webm" res="144p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
104 |
-
<Stream: itag="394" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.00M.08" progressive="False" type="video">,
|
105 |
-
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2" progressive="False" type="audio">,
|
106 |
-
<Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus" progressive="False" type="audio">,
|
107 |
-
<Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus" progressive="False" type="audio">,
|
108 |
-
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus" progressive="False" type="audio">]
|
109 |
-
```
|
110 |
-
You may notice that some streams listed have both a video codec and audio codec, while others have just video or just audio, this is a result of YouTube supporting a streaming technique called Dynamic Adaptive Streaming over HTTP (DASH).
|
111 |
-
|
112 |
-
In the context of pytube, the implications are for the highest quality streams; you now need to download both the audio and video tracks and then post-process them with software like FFmpeg to merge them.
|
113 |
-
|
114 |
-
The legacy streams that contain the audio and video in a single file (referred to as "progressive download") are still available, but only for resolutions 720p and below.
|
115 |
-
|
116 |
-
To only view these progressive download streams:
|
117 |
-
|
118 |
-
```python
|
119 |
-
>>> yt.streams.filter(progressive=True)
|
120 |
-
[<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2" progressive="True" type="video">,
|
121 |
-
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">]
|
122 |
-
```
|
123 |
-
|
124 |
-
Conversely, if you only want to see the DASH streams (also referred to as "adaptive") you can do:
|
125 |
-
|
126 |
-
```python
|
127 |
-
>>> yt.streams.filter(adaptive=True)
|
128 |
-
[<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028" progressive="False" type="video">,
|
129 |
-
<Stream: itag="248" mime_type="video/webm" res="1080p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
130 |
-
<Stream: itag="399" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.08M.08" progressive="False" type="video">,
|
131 |
-
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f" progressive="False" type="video">,
|
132 |
-
<Stream: itag="247" mime_type="video/webm" res="720p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
133 |
-
<Stream: itag="398" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.05M.08" progressive="False" type="video">,
|
134 |
-
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401e" progressive="False" type="video">,
|
135 |
-
<Stream: itag="244" mime_type="video/webm" res="480p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
136 |
-
<Stream: itag="397" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.04M.08" progressive="False" type="video">,
|
137 |
-
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e" progressive="False" type="video">,
|
138 |
-
<Stream: itag="243" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
139 |
-
<Stream: itag="396" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.01M.08" progressive="False" type="video">,
|
140 |
-
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015" progressive="False" type="video">,
|
141 |
-
<Stream: itag="242" mime_type="video/webm" res="240p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
142 |
-
<Stream: itag="395" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.00M.08" progressive="False" type="video">,
|
143 |
-
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c" progressive="False" type="video">,
|
144 |
-
<Stream: itag="278" mime_type="video/webm" res="144p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
145 |
-
<Stream: itag="394" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.00M.08" progressive="False" type="video">,
|
146 |
-
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2" progressive="False" type="audio">,
|
147 |
-
<Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus" progressive="False" type="audio">,
|
148 |
-
<Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus" progressive="False" type="audio">,
|
149 |
-
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus" progressive="False" type="audio">]
|
150 |
-
```
|
151 |
-
|
152 |
-
You can also interact with Youtube playlists:
|
153 |
-
|
154 |
-
```python
|
155 |
-
>>> from pytube import Playlist
|
156 |
-
>>> pl = Playlist("https://www.youtube.com/watch?v=Edpy1szoG80&list=PL153hDY-y1E00uQtCVCVC8xJ25TYX8yPU")
|
157 |
-
>>> for video in pl.videos:
|
158 |
-
>>> video.streams.first().download()
|
159 |
-
```
|
160 |
-
|
161 |
-
Pytube allows you to filter on every property available (see the documentation for the complete list), let's take a look at some of the most useful ones.
|
162 |
-
|
163 |
-
To list the audio only streams:
|
164 |
-
|
165 |
-
```python
|
166 |
-
>>> yt.streams.filter(only_audio=True)
|
167 |
-
[<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2" progressive="False" type="audio">,
|
168 |
-
<Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus" progressive="False" type="audio">,
|
169 |
-
<Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus" progressive="False" type="audio">,
|
170 |
-
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus" progressive="False" type="audio">]
|
171 |
-
```
|
172 |
-
|
173 |
-
To list only ``mp4`` streams:
|
174 |
-
|
175 |
-
```python
|
176 |
-
>>> yt.streams.filter(subtype='mp4').all()
|
177 |
-
[<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2" progressive="True" type="video">,
|
178 |
-
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">,
|
179 |
-
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028" progressive="False" type="video">,
|
180 |
-
<Stream: itag="399" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.08M.08" progressive="False" type="video">,
|
181 |
-
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f" progressive="False" type="video">,
|
182 |
-
<Stream: itag="398" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.05M.08" progressive="False" type="video">,
|
183 |
-
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401e" progressive="False" type="video">,
|
184 |
-
<Stream: itag="397" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.04M.08" progressive="False" type="video">,
|
185 |
-
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e" progressive="False" type="video">,
|
186 |
-
<Stream: itag="396" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.01M.08" progressive="False" type="video">,
|
187 |
-
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015" progressive="False" type="video">,
|
188 |
-
<Stream: itag="395" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.00M.08" progressive="False" type="video">,
|
189 |
-
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c" progressive="False" type="video">,
|
190 |
-
<Stream: itag="394" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.00M.08" progressive="False" type="video">,
|
191 |
-
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2" progressive="False" type="audio">]
|
192 |
-
```
|
193 |
-
|
194 |
-
Multiple filters can also be specified:
|
195 |
-
|
196 |
-
```python
|
197 |
-
>>> yt.streams.filter(subtype='mp4', progressive=True).all()
|
198 |
-
>>> # this can also be expressed as:
|
199 |
-
>>> yt.streams.filter(subtype='mp4').filter(progressive=True).all()
|
200 |
-
[<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2" progressive="True" type="video">,
|
201 |
-
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">]
|
202 |
-
```
|
203 |
-
You also have an interface to select streams by their itag, without needing to filter:
|
204 |
-
|
205 |
-
```python
|
206 |
-
>>> yt.streams.get_by_itag(22)
|
207 |
-
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">
|
208 |
-
```
|
209 |
-
|
210 |
-
If you need to optimize for a specific feature, such as the "highest resolution" or "lowest average bitrate":
|
211 |
-
|
212 |
-
```python
|
213 |
-
>>> yt.streams.filter(progressive=True).order_by('resolution').desc().all()
|
214 |
-
[<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">,
|
215 |
-
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2" progressive="True" type="video">]
|
216 |
-
```
|
217 |
-
Note that ``order_by`` cannot be used if your attribute is undefined in any of the Stream instances, so be sure to apply a filter to remove those before calling it.
|
218 |
-
|
219 |
-
If your application requires post-processing logic, pytube allows you to specify an "on download complete" callback function:
|
220 |
-
|
221 |
-
```python
|
222 |
-
>>> def convert_to_aac(stream, file_handle):
|
223 |
-
return # do work
|
224 |
-
|
225 |
-
>>> yt.register_on_complete_callback(convert_to_aac)
|
226 |
-
```
|
227 |
-
|
228 |
-
Similarly, if your application requires on-download progress logic, pytube exposes a callback for this as well:
|
229 |
-
|
230 |
-
```python
|
231 |
-
>>> def show_progress_bar(stream, chunk, bytes_remaining):
|
232 |
-
return # do work
|
233 |
-
|
234 |
-
>>> yt.register_on_progress_callback(show_progress_bar)
|
235 |
-
```
|
236 |
-
|
237 |
-
You can also download videos to a specific directory with specific filename:
|
238 |
-
|
239 |
-
```python
|
240 |
-
>>> yt = YouTube('https://youtube.com/watch?v=2lAe1cqCOXo')
|
241 |
-
>>> yt.streams.first().download(output_path="/tmp" ,filename='output')
|
242 |
-
```
|
243 |
-
|
244 |
-
## Command-line interface (CLI)
|
245 |
-
|
246 |
-
Pytube also ships with a tiny CLI for interacting with videos and playlists.
|
247 |
-
|
248 |
-
To download the highest resolution progressive stream:
|
249 |
-
|
250 |
-
```bash
|
251 |
-
$ pytube https://www.youtube.com/watch?v=2lAe1cqCOXo
|
252 |
-
```
|
253 |
-
|
254 |
-
To view available streams:
|
255 |
-
|
256 |
-
```bash
|
257 |
-
$ pytube https://www.youtube.com/watch?v=2lAe1cqCOXo --list
|
258 |
-
```
|
259 |
-
|
260 |
-
To download a specific stream, use the itag
|
261 |
-
|
262 |
-
```bash
|
263 |
-
$ pytube https://www.youtube.com/watch?v=2lAe1cqCOXo --itag=22
|
264 |
-
```
|
265 |
-
|
266 |
-
To get a list of all subtitles (caption codes)
|
267 |
-
|
268 |
-
```bash
|
269 |
-
$ pytube https://www.youtube.com/watch?v=2lAe1cqCOXo --list-captions
|
270 |
-
```
|
271 |
-
|
272 |
-
To download a specific subtitle (caption code) - in this case the
|
273 |
-
english subtitles (in srt format) - use:
|
274 |
-
|
275 |
```bash
|
276 |
-
$ pytube https://
|
277 |
```
|
278 |
|
279 |
-
|
280 |
-
|
281 |
```bash
|
282 |
-
$ pytube https://www.youtube.com/
|
283 |
```
|
284 |
-
|
285 |
-
|
286 |
-
Finally, if you're filing a bug report, the cli contains a switch called ``--build-playback-report``, which bundles up the state, allowing others to easily replay your issue.
|
|
|
4 |
</p>
|
5 |
<p align="center">
|
6 |
<img src="https://img.shields.io/pypi/v/pytube.svg" alt="pypi">
|
7 |
+
<a href="http://python-pytube.readthedocs.io/en/latest/?badge=latest">
|
8 |
+
<img src="https://readthedocs.org/projects/python-pytube/badge/?version=latest" />
|
9 |
+
</a>
|
10 |
+
<a href="https://codecov.io/gh/nficano/pytube">
|
11 |
+
<img src="https://codecov.io/gh/nficano/pytube/branch/master/graph/badge.svg" />
|
12 |
+
</a>
|
13 |
+
<a href="https://pypi.org/project/pytube/">
|
14 |
+
<img src="https://img.shields.io/pypi/dm/pytube.svg" alt="pypi">
|
15 |
+
</a>
|
16 |
+
<a href="https://pypi.python.org/pypi/pytube/">
|
17 |
+
<img src="https://img.shields.io/pypi/pyversions/pytube.svg" />
|
18 |
+
</a>
|
19 |
</p>
|
20 |
</div>
|
21 |
|
22 |
|
23 |
+
### Actively soliciting contributers!
|
24 |
+
Have ideas for how pytube can be improved? Feel free to open an issue or a pull
|
25 |
+
request!
|
26 |
|
27 |
# pytube
|
28 |
+
*pytube* is a very serious, lightweight, dependency-free Python library (and
|
29 |
+
command-line utility) for downloading YouTube Videos.
|
30 |
|
31 |
+
|
32 |
+
## Documentation
|
33 |
+
Detailed documentation about how to use the library can be found on our
|
34 |
+
[readthedocs](https://python-pytube.readthedocs.io) page. This is recommended
|
35 |
+
for most use cases. If you just want to quickly download a single video,
|
36 |
+
the [quickstart](#Quickstart) guide below might be what you're looking for.
|
37 |
+
|
38 |
+
|
39 |
+
## Description
|
40 |
+
YouTube is the most popular video-sharing platform in the world and as a hacker
|
41 |
+
you may encounter a situation where you want to script something to download
|
42 |
+
videos. For this I present to you *pytube*.
|
43 |
+
|
44 |
+
*pytube* is a lightweight library written in Python. It has no third party
|
45 |
+
dependencies and aims to be highly reliable.
|
46 |
+
|
47 |
+
*pytube* also makes pipelining easy, allowing you to specify callback functions
|
48 |
+
for different download events, such as ``on progress`` or ``on complete``.
|
49 |
+
|
50 |
+
Finally *pytube* also includes a command-line utility, allowing you to quickly
|
51 |
+
download videos right from terminal.
|
52 |
+
|
53 |
+
|
54 |
+
## Features
|
55 |
+
- Support for both progressive & DASH streams
|
56 |
+
- Support for downloading complete playlist
|
57 |
+
- Easily register ``on_download_progress`` & ``on_download_complete`` callbacks
|
58 |
+
- Command-line interfaced included
|
59 |
+
- Caption track support
|
60 |
+
- Outputs caption tracks to .srt format (SubRip Subtitle)
|
61 |
+
- Ability to capture thumbnail URL
|
62 |
+
- Extensively documented source code
|
63 |
+
- No third-party dependencies
|
64 |
+
|
65 |
+
## Quickstart
|
66 |
+
This guide is only meant to cover the most basic usage of the library. For more
|
67 |
+
detailed information, please refer to our
|
68 |
+
[readthedocs](https://python-pytube.readthedocs.io) page.
|
69 |
+
|
70 |
+
### Installation
|
71 |
+
Pytube requires an installation of python 3.6 or greater, as well as pip.
|
72 |
+
Pip is typically bundled with python installations, and you can find options
|
73 |
+
for how to install python at https://python.org.
|
74 |
|
75 |
To install from pypi with pip:
|
76 |
|
|
|
78 |
$ python -m pip install pytube
|
79 |
```
|
80 |
|
81 |
+
Sometime, the pypi release becomes slightly outdated. To install from the
|
82 |
+
source with pip:
|
83 |
|
84 |
```bash
|
85 |
$ python -m pip install git+https://github.com/pytube/pytube
|
86 |
```
|
87 |
|
88 |
+
### Using pytube in a python script
|
89 |
+
To download a video using the library in a script, you'll need to first import
|
90 |
+
the YouTube class from the library, and pass it an argument of the video url.
|
91 |
+
From there, you can access the streams and download them.
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
|
93 |
```python
|
94 |
+
>>> from pytube import YouTube
|
95 |
>>> YouTube('https://youtu.be/2lAe1cqCOXo').streams.first().download()
|
96 |
>>> yt = YouTube('http://youtube.com/watch?v=2lAe1cqCOXo')
|
97 |
>>> yt.streams
|
|
|
102 |
... .download()
|
103 |
```
|
104 |
|
105 |
+
### Using the command-line interface
|
106 |
+
Using the CLI is extremely straightforward as well. To download a video at the
|
107 |
+
highest progressive quality, you can use the following command:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
```bash
|
109 |
+
$ pytube https://youtube.com/watch?v=2lAe1cqCOXo
|
110 |
```
|
111 |
|
112 |
+
You can also do the same for a playlist:
|
|
|
113 |
```bash
|
114 |
+
$ pytube https://www.youtube.com/playlist?list=PLS1QulWo1RIaJECMeUT4LFwJ-ghgoSH6n
|
115 |
```
|
|
|
|
|
|
docs/index.rst
CHANGED
@@ -20,7 +20,8 @@ Release v\ |version|. (:ref:`Installation<install>`)
|
|
20 |
:alt: Python Versions
|
21 |
:target: https://pypi.python.org/pypi/pytube/
|
22 |
|
23 |
-
**pytube** is a lightweight, Pythonic, dependency-free, library (and
|
|
|
24 |
|
25 |
-------------------
|
26 |
|
@@ -48,28 +49,28 @@ Features
|
|
48 |
- Extensively Documented Source Code
|
49 |
- No Third-Party Dependencies
|
50 |
|
51 |
-
Roadmap
|
52 |
-
-------
|
53 |
-
|
54 |
-
- Allow downloading age restricted content
|
55 |
-
- Complete ffmpeg integration
|
56 |
-
|
57 |
The User Guide
|
58 |
--------------
|
59 |
-
This part of the documentation begins with some background information about
|
|
|
|
|
60 |
|
61 |
.. toctree::
|
62 |
:maxdepth: 2
|
63 |
|
64 |
user/install
|
65 |
user/quickstart
|
|
|
|
|
66 |
user/playlist
|
67 |
user/cli
|
|
|
68 |
|
69 |
-
The API Documentation
|
70 |
-----------------------------
|
71 |
|
72 |
-
If you are looking for information on a specific function, class, or method,
|
|
|
73 |
|
74 |
.. toctree::
|
75 |
:maxdepth: 2
|
|
|
20 |
:alt: Python Versions
|
21 |
:target: https://pypi.python.org/pypi/pytube/
|
22 |
|
23 |
+
**pytube** is a lightweight, Pythonic, dependency-free, library (and
|
24 |
+
command-line utility) for downloading YouTube Videos.
|
25 |
|
26 |
-------------------
|
27 |
|
|
|
49 |
- Extensively Documented Source Code
|
50 |
- No Third-Party Dependencies
|
51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
The User Guide
|
53 |
--------------
|
54 |
+
This part of the documentation begins with some background information about
|
55 |
+
the project, then focuses on step-by-step instructions for getting the most out
|
56 |
+
of pytube.
|
57 |
|
58 |
.. toctree::
|
59 |
:maxdepth: 2
|
60 |
|
61 |
user/install
|
62 |
user/quickstart
|
63 |
+
user/streams
|
64 |
+
user/captions
|
65 |
user/playlist
|
66 |
user/cli
|
67 |
+
user/exceptions
|
68 |
|
69 |
+
The API Documentation
|
70 |
-----------------------------
|
71 |
|
72 |
+
If you are looking for information on a specific function, class, or method,
|
73 |
+
this part of the documentation is for you.
|
74 |
|
75 |
.. toctree::
|
76 |
:maxdepth: 2
|
docs/user/captions.rst
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.. _captions:
|
2 |
+
|
3 |
+
Subtitle/Caption Tracks
|
4 |
+
=======================
|
5 |
+
|
6 |
+
Pytube exposes the caption tracks in much the same way as querying the media
|
7 |
+
streams. Let's begin by switching to a video that contains them::
|
8 |
+
|
9 |
+
>>> yt = YouTube('http://youtube.com/watch?v=2lAe1cqCOXo')
|
10 |
+
>>> yt.captions
|
11 |
+
{'ar': <Caption lang="Arabic" code="ar">, 'zh-HK': <Caption lang="Chinese (Hong Kong)" code="zh-HK">, 'zh-TW': <Caption lang="Chinese (Taiwan)" code="zh-TW">, 'hr': <Caption lang="Croatian" code="hr">, 'cs': <Caption lang="Czech" code="cs">, 'da': <Caption lang="Danish" code="da">, 'nl': <Caption lang="Dutch" code="nl">, 'en': <Caption lang="English" code="en">, 'en-GB': <Caption lang="English (United Kingdom)" code="en-GB">, 'et': <Caption lang="Estonian" code="et">, 'fil': <Caption lang="Filipino" code="fil">, 'fi': <Caption lang="Finnish" code="fi">, 'fr-CA': <Caption lang="French (Canada)" code="fr-CA">, 'fr-FR': <Caption lang="French (France)" code="fr-FR">, 'de': <Caption lang="German" code="de">, 'el': <Caption lang="Greek" code="el">, 'iw': <Caption lang="Hebrew" code="iw">, 'hu': <Caption lang="Hungarian" code="hu">, 'id': <Caption lang="Indonesian" code="id">, 'it': <Caption lang="Italian" code="it">, 'ja': <Caption lang="Japanese" code="ja">, 'ko': <Caption lang="Korean" code="ko">, 'lv': <Caption lang="Latvian" code="lv">, 'lt': <Caption lang="Lithuanian" code="lt">, 'ms': <Caption lang="Malay" code="ms">, 'no': <Caption lang="Norwegian" code="no">, 'pl': <Caption lang="Polish" code="pl">, 'pt-BR': <Caption lang="Portuguese (Brazil)" code="pt-BR">, 'pt-PT': <Caption lang="Portuguese (Portugal)" code="pt-PT">, 'ro': <Caption lang="Romanian" code="ro">, 'ru': <Caption lang="Russian" code="ru">, 'sk': <Caption lang="Slovak" code="sk">, 'es-419': <Caption lang="Spanish (Latin America)" code="es-419">, 'es-ES': <Caption lang="Spanish (Spain)" code="es-ES">, 'sv': <Caption lang="Swedish" code="sv">, 'th': <Caption lang="Thai" code="th">, 'tr': <Caption lang="Turkish" code="tr">, 'uk': <Caption lang="Ukrainian" code="uk">, 'ur': <Caption lang="Urdu" code="ur">, 'vi': <Caption lang="Vietnamese" code="vi">}
|
12 |
+
|
13 |
+
Now let's checkout the english captions::
|
14 |
+
|
15 |
+
>>> caption = yt.captions.get_by_language_code('en')
|
16 |
+
|
17 |
+
Great, now let's see how YouTube formats them::
|
18 |
+
|
19 |
+
>>> caption.xml_captions
|
20 |
+
'<?xml version="1.0" encoding="utf-8" ?><transcript><text start="10.2" dur="0.94">K-pop!</text>...'
|
21 |
+
|
22 |
+
Oh, this isn't very easy to work with, let's convert them to the srt format::
|
23 |
+
|
24 |
+
>>> print(caption.generate_srt_captions())
|
25 |
+
1
|
26 |
+
00:00:10,200 --> 00:00:11,140
|
27 |
+
K-pop!
|
28 |
+
|
29 |
+
2
|
30 |
+
00:00:13,400 --> 00:00:16,200
|
31 |
+
That is so awkward to watch.
|
32 |
+
...
|
docs/user/cli.rst
CHANGED
@@ -30,7 +30,7 @@ To get a list of all subtitles (caption codes)
|
|
30 |
$ pytube https://www.youtube.com/watch?v=2lAe1cqCOXo --list-captions
|
31 |
|
32 |
To download a specific subtitle (caption code) - in this case the
|
33 |
-
|
34 |
|
35 |
.. code:: bash
|
36 |
|
|
|
30 |
$ pytube https://www.youtube.com/watch?v=2lAe1cqCOXo --list-captions
|
31 |
|
32 |
To download a specific subtitle (caption code) - in this case the
|
33 |
+
English subtitles (in srt format) - use:
|
34 |
|
35 |
.. code:: bash
|
36 |
|
docs/user/exceptions.rst
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.. _exceptions:
|
2 |
+
|
3 |
+
Exception handling
|
4 |
+
==================
|
5 |
+
|
6 |
+
Pytube implements a number of useful exceptions for handling program flow.
|
7 |
+
There are a number of cases where pytube simply cannot access videos on YouTube
|
8 |
+
and relies on the user to handle these exceptions. Generally speaking, if a
|
9 |
+
video is unaccessible for any reason, this can be caught with the generic
|
10 |
+
VideoUnavailable exception. This could be used, for example, to skip private
|
11 |
+
videos in a playlist, videos that are region-restricted, and more.
|
12 |
+
|
13 |
+
Let's see what your code might look like if you need to do exception handling::
|
14 |
+
|
15 |
+
>>> from pytube import Playlist, YouTube
|
16 |
+
>>> playlist_url = 'https://youtube.com/playlist?list=special_playlist_id'
|
17 |
+
>>> p = Playlist(playlist_url)
|
18 |
+
>>> for url in p.video_urls:
|
19 |
+
... try:
|
20 |
+
... yt = YouTube(url)
|
21 |
+
... except VideoUnavailable:
|
22 |
+
... print(f'Video {url} is unavaialable, skipping.')
|
23 |
+
... else:
|
24 |
+
... print(f'Downloading video: {url}')
|
25 |
+
... yt.streams.first().download()
|
26 |
+
|
27 |
+
This will automatically skip over videos that could not be downloaded due to a
|
28 |
+
limitation with the pytube library. You can find more details about what
|
29 |
+
specific exceptions can be handled here: :py:mod:`pytube.exceptions`.
|
docs/user/install.rst
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
Installation of pytube
|
4 |
======================
|
5 |
|
6 |
-
This
|
7 |
|
8 |
To install pytube, run the following command in your terminal::
|
9 |
|
@@ -12,18 +12,18 @@ To install pytube, run the following command in your terminal::
|
|
12 |
Get the Source Code
|
13 |
-------------------
|
14 |
|
15 |
-
pytube is actively developed on GitHub, where the source is `available <https://github.com/
|
16 |
|
17 |
You can either clone the public repository::
|
18 |
|
19 |
-
$ git clone git://github.com/
|
20 |
|
21 |
-
Or, download the `tarball <https://github.com/
|
22 |
|
23 |
-
$ curl -OL https://github.com/
|
24 |
# optionally, zipball is also available (for Windows users).
|
25 |
|
26 |
Once you have a copy of the source, you can embed it in your Python package, or install it into your site-packages by running::
|
27 |
|
28 |
$ cd pytube
|
29 |
-
$ pip install .
|
|
|
3 |
Installation of pytube
|
4 |
======================
|
5 |
|
6 |
+
This guide assumes you already have python and pip installed.
|
7 |
|
8 |
To install pytube, run the following command in your terminal::
|
9 |
|
|
|
12 |
Get the Source Code
|
13 |
-------------------
|
14 |
|
15 |
+
pytube is actively developed on GitHub, where the source is `available <https://github.com/pytube/pytube>`_.
|
16 |
|
17 |
You can either clone the public repository::
|
18 |
|
19 |
+
$ git clone git://github.com/pytube/pytube.git
|
20 |
|
21 |
+
Or, download the `tarball <https://github.com/pytube/pytube/tarball/master>`_::
|
22 |
|
23 |
+
$ curl -OL https://github.com/pytube/pytube/tarball/master
|
24 |
# optionally, zipball is also available (for Windows users).
|
25 |
|
26 |
Once you have a copy of the source, you can embed it in your Python package, or install it into your site-packages by running::
|
27 |
|
28 |
$ cd pytube
|
29 |
+
$ python -m pip install .
|
docs/user/quickstart.rst
CHANGED
@@ -17,9 +17,9 @@ Begin by importing the YouTube class::
|
|
17 |
>>> from pytube import YouTube
|
18 |
|
19 |
Now, let's try to download a video. For this example, let's take something
|
20 |
-
|
21 |
|
22 |
-
>>> yt = YouTube('
|
23 |
|
24 |
Now, we have a :class:`YouTube <pytube.YouTube>` object called ``yt``.
|
25 |
|
@@ -27,182 +27,35 @@ The pytube API makes all information intuitive to access. For example, this is
|
|
27 |
how you would get the video's title::
|
28 |
|
29 |
>>> yt.title
|
30 |
-
|
31 |
|
32 |
And this would be how you would get the thumbnail url::
|
33 |
|
34 |
>>> yt.thumbnail_url
|
35 |
-
'https://i.ytimg.com/vi/
|
36 |
-
|
37 |
-
Neat, right? Next let's see the available media formats::
|
38 |
-
|
39 |
-
>>> yt.streams.all()
|
40 |
-
[<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">,
|
41 |
-
<Stream: itag="43" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp8.0" acodec="vorbis">,
|
42 |
-
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">,
|
43 |
-
<Stream: itag="36" mime_type="video/3gpp" res="240p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">,
|
44 |
-
<Stream: itag="17" mime_type="video/3gpp" res="144p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">,
|
45 |
-
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">,
|
46 |
-
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">,
|
47 |
-
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401f">,
|
48 |
-
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">,
|
49 |
-
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">,
|
50 |
-
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">,
|
51 |
-
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">,
|
52 |
-
<Stream: itag="171" mime_type="audio/webm" abr="128kbps" acodec="vorbis">]
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
>>> stream = yt.streams.first()
|
57 |
-
>>> stream
|
58 |
-
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">
|
59 |
-
|
60 |
-
And to download it to the current working directory::
|
61 |
-
|
62 |
-
>>> stream.download()
|
63 |
-
|
64 |
-
You can also specify a destination path::
|
65 |
-
|
66 |
-
>>> stream.download('/tmp')
|
67 |
-
|
68 |
-
|
69 |
-
Working with Streams
|
70 |
-
====================
|
71 |
-
|
72 |
-
The next section will explore the various options available for working with media
|
73 |
-
streams, but before we can dive in, we need to review a new-ish streaming technique
|
74 |
-
adopted by YouTube.
|
75 |
-
|
76 |
-
DASH vs Progressive Streams
|
77 |
-
---------------------------
|
78 |
-
|
79 |
-
Begin by running the following::
|
80 |
-
|
81 |
-
>>> yt.streams.all()
|
82 |
-
[<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">,
|
83 |
-
<Stream: itag="43" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp8.0" acodec="vorbis">,
|
84 |
-
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">,
|
85 |
-
<Stream: itag="36" mime_type="video/3gpp" res="240p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">,
|
86 |
-
<Stream: itag="17" mime_type="video/3gpp" res="144p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">,
|
87 |
-
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">,
|
88 |
-
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">,
|
89 |
-
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401f">,
|
90 |
-
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">,
|
91 |
-
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">,
|
92 |
-
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">,
|
93 |
-
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">,
|
94 |
-
<Stream: itag="171" mime_type="audio/webm" abr="128kbps" acodec="vorbis">]
|
95 |
-
|
96 |
-
|
97 |
-
You may notice that some streams listed have both a video codec and audio
|
98 |
-
codec, while others have just video or just audio, this is a result of YouTube
|
99 |
-
supporting a streaming technique called Dynamic Adaptive Streaming over HTTP
|
100 |
-
(DASH).
|
101 |
-
|
102 |
-
In the context of pytube, the implications are for the highest quality streams;
|
103 |
-
you now need to download both the audio and video tracks and then post-process
|
104 |
-
them with software like FFmpeg to merge them.
|
105 |
-
|
106 |
-
The legacy streams that contain the audio and video in a single file (referred
|
107 |
-
to as "progressive download") are still available, but only for resolutions
|
108 |
-
720p and below.
|
109 |
-
|
110 |
-
To only view these progressive download streams::
|
111 |
-
|
112 |
-
>>> yt.streams.filter(progressive=True).all()
|
113 |
-
[<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">,
|
114 |
-
<Stream: itag="43" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp8.0" acodec="vorbis">,
|
115 |
-
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">,
|
116 |
-
<Stream: itag="36" mime_type="video/3gpp" res="240p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">,
|
117 |
-
<Stream: itag="17" mime_type="video/3gpp" res="144p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">]
|
118 |
-
|
119 |
-
Conversely, if you only want to see the DASH streams (also referred to as
|
120 |
-
"adaptive") you can do::
|
121 |
-
|
122 |
-
>>> yt.streams.filter(adaptive=True).all()
|
123 |
-
[<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">,
|
124 |
-
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">,
|
125 |
-
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401f">,
|
126 |
-
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">,
|
127 |
-
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">,
|
128 |
-
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">,
|
129 |
-
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">,
|
130 |
-
<Stream: itag="171" mime_type="audio/webm" abr="128kbps" acodec="vorbis">]
|
131 |
-
|
132 |
-
Pytube allows you to filter on every property available (see
|
133 |
-
:py:meth:`pytube.StreamQuery.filter` for a complete list of filter options),
|
134 |
-
let's take a look at some common examples:
|
135 |
-
|
136 |
-
Query audio only Streams
|
137 |
-
------------------------
|
138 |
-
|
139 |
-
To query the streams that contain only the audio track::
|
140 |
-
|
141 |
-
>>> yt.streams.filter(only_audio=True).all()
|
142 |
-
[<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">,
|
143 |
-
<Stream: itag="171" mime_type="audio/webm" abr="128kbps" acodec="vorbis">]
|
144 |
-
|
145 |
-
Query MPEG-4 Streams
|
146 |
-
--------------------
|
147 |
|
148 |
-
|
|
|
|
|
|
|
|
|
|
|
149 |
|
150 |
-
|
151 |
-
|
152 |
-
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">,
|
153 |
-
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">,
|
154 |
-
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">,
|
155 |
-
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401f">,
|
156 |
-
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">,
|
157 |
-
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">,
|
158 |
-
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">,
|
159 |
-
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">]
|
160 |
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
>>> yt.streams.get_by_itag('22')
|
167 |
-
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">
|
168 |
-
|
169 |
-
Subtitle/Caption Tracks
|
170 |
-
=======================
|
171 |
-
|
172 |
-
Pytube exposes the caption tracks in much the same way as querying the media
|
173 |
-
streams. Let's begin by switching to a video that contains them::
|
174 |
-
|
175 |
-
>>> yt = YouTube('https://youtube.com/watch?v=XJGiS83eQLk')
|
176 |
-
>>> yt.captions.all()
|
177 |
-
[<Caption lang="Arabic" code="ar">,
|
178 |
-
<Caption lang="English (auto-generated)" code="en">,
|
179 |
-
<Caption lang="English" code="en">,
|
180 |
-
<Caption lang="English (United Kingdom)" code="en-GB">,
|
181 |
-
<Caption lang="German" code="de">,
|
182 |
-
<Caption lang="Greek" code="el">,
|
183 |
-
<Caption lang="Indonesian" code="id">,
|
184 |
-
<Caption lang="Sinhala" code="si">,
|
185 |
-
<Caption lang="Spanish" code="es">,
|
186 |
-
<Caption lang="Turkish" code="tr">]
|
187 |
-
|
188 |
-
Now let's checkout the english captions::
|
189 |
-
|
190 |
-
>>> caption = yt.captions.get_by_language_code('en')
|
191 |
-
|
192 |
-
Great, now let's see how YouTube formats them::
|
193 |
-
|
194 |
-
>>> caption.xml_captions
|
195 |
-
'<?xml version="1.0" encoding="utf-8" ?><transcript><text start="0" dur="5.541">well i&#39...'
|
196 |
-
|
197 |
-
Oh, this isn't very easy to work with, let's convert them to the srt format::
|
198 |
-
|
199 |
-
>>> print(caption.generate_srt_captions())
|
200 |
-
1
|
201 |
-
000:000:00,000 --> 000:000:05,541
|
202 |
-
well i'm just an editor and i dont know what to type
|
203 |
|
204 |
-
|
205 |
-
|
206 |
-
|
|
|
207 |
|
208 |
-
|
|
|
|
17 |
>>> from pytube import YouTube
|
18 |
|
19 |
Now, let's try to download a video. For this example, let's take something
|
20 |
+
like the YouTube Rewind video for 2019::
|
21 |
|
22 |
+
>>> yt = YouTube('http://youtube.com/watch?v=2lAe1cqCOXo')
|
23 |
|
24 |
Now, we have a :class:`YouTube <pytube.YouTube>` object called ``yt``.
|
25 |
|
|
|
27 |
how you would get the video's title::
|
28 |
|
29 |
>>> yt.title
|
30 |
+
YouTube Rewind 2019: For the Record | #YouTubeRewind
|
31 |
|
32 |
And this would be how you would get the thumbnail url::
|
33 |
|
34 |
>>> yt.thumbnail_url
|
35 |
+
'https://i.ytimg.com/vi/2lAe1cqCOXo/maxresdefault.jpg'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
+
Neat, right? For advanced use cases, you can provide some additional arguments
|
38 |
+
when you create a YouTube object::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
|
40 |
+
>>> yt = YouTube(
|
41 |
+
'http://youtube.com/watch?v=2lAe1cqCOXo',
|
42 |
+
on_progress_callback=progress_func,
|
43 |
+
on_complete_callback=complete_func,
|
44 |
+
proxies=my_proxies
|
45 |
+
)
|
46 |
|
47 |
+
When instantiating a YouTube object, these named arguments can be passed in to
|
48 |
+
improve functionality.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
|
50 |
+
The on_progress_callback function will run whenever a chunk is downloaded from
|
51 |
+
a video, and is called with three arguments: the stream, the data chunk, and
|
52 |
+
the bytes remaining in the video. This could be used, for example, to display a
|
53 |
+
progress bar.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
|
55 |
+
The on_complete_callback function will run after a video has been fully
|
56 |
+
downloaded, and is called with two arguments: the stream and the file path.
|
57 |
+
This could be used, for example, to perform post-download processing on a video
|
58 |
+
like trimming the length of it.
|
59 |
|
60 |
+
Once you have a YouTube object set up, you're ready to start looking at
|
61 |
+
different media streams for the video, which is discussed in the next section.
|
docs/user/streams.rst
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.. _streams:
|
2 |
+
|
3 |
+
Working with Streams and StreamQuery
|
4 |
+
====================================
|
5 |
+
|
6 |
+
The next section will explore the various options available for working with
|
7 |
+
media streams, but before we can dive in, we need to review a new-ish streaming
|
8 |
+
technique adopted by YouTube. It assumes that you have already created a
|
9 |
+
YouTube object in your code called "yt".
|
10 |
+
|
11 |
+
DASH vs Progressive Streams
|
12 |
+
---------------------------
|
13 |
+
|
14 |
+
Begin by running the following to list all streams::
|
15 |
+
|
16 |
+
>>> yt.streams
|
17 |
+
[<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2" progressive="True" type="video">,
|
18 |
+
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">,
|
19 |
+
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028" progressive="False" type="video">,
|
20 |
+
...
|
21 |
+
<Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus" progressive="False" type="audio">,
|
22 |
+
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus" progressive="False" type="audio">]
|
23 |
+
|
24 |
+
|
25 |
+
You may notice that some streams listed have both a video codec and audio
|
26 |
+
codec, while others have just video or just audio, this is a result of YouTube
|
27 |
+
supporting a streaming technique called Dynamic Adaptive Streaming over HTTP
|
28 |
+
(DASH).
|
29 |
+
|
30 |
+
In the context of pytube, the implications are for the highest quality streams;
|
31 |
+
you now need to download both the audio and video tracks and then post-process
|
32 |
+
them with software like FFmpeg to merge them.
|
33 |
+
|
34 |
+
The legacy streams that contain the audio and video in a single file (referred
|
35 |
+
to as "progressive download") are still available, but only for resolutions
|
36 |
+
720p and below.
|
37 |
+
|
38 |
+
Filtering Streams
|
39 |
+
=================
|
40 |
+
|
41 |
+
Pytube has built-in functionality to filter the streams available in a YouTube
|
42 |
+
object with the .filter() method. You can pass it a number of different keyword
|
43 |
+
arguments, so let's review some of the different options you're most likely to
|
44 |
+
use. For a complete list of available properties to filter on, you can view the
|
45 |
+
API documentation here: :py:meth:`pytube.StreamQuery.filter`.
|
46 |
+
|
47 |
+
Filtering by streaming method
|
48 |
+
-----------------------------
|
49 |
+
|
50 |
+
As mentioned before, progressive streams have the video and audio in a single
|
51 |
+
file, but typically do not provide the highest quality media; meanwhile,
|
52 |
+
adaptive streams split the video and audio tracks but can provide much higher
|
53 |
+
quality. Pytube makes it easy to filter based on the type of stream that you're
|
54 |
+
interested.
|
55 |
+
|
56 |
+
For example, you can filter to only progressive streams with the following::
|
57 |
+
|
58 |
+
>>> yt.streams.filter(progressive=True)
|
59 |
+
[<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2" progressive="True" type="video">,
|
60 |
+
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">]
|
61 |
+
|
62 |
+
Conversely, if you only want to see the DASH streams (also referred to as
|
63 |
+
"adaptive") you can do::
|
64 |
+
|
65 |
+
>>> yt.streams.filter(adaptive=True)
|
66 |
+
[<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028" progressive="False" type="video">,
|
67 |
+
<Stream: itag="248" mime_type="video/webm" res="1080p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
|
68 |
+
<Stream: itag="399" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.08M.08" progressive="False" type="video">,
|
69 |
+
...
|
70 |
+
<Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus" progressive="False" type="audio">,
|
71 |
+
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus" progressive="False" type="audio">]
|
72 |
+
|
73 |
+
Filtering for audio-only streams
|
74 |
+
--------------------------------
|
75 |
+
|
76 |
+
To query the streams that contain only the audio track::
|
77 |
+
|
78 |
+
>>> yt.streams.filter(only_audio=True)
|
79 |
+
[<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2" progressive="False" type="audio">,
|
80 |
+
<Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus" progressive="False" type="audio">,
|
81 |
+
<Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus" progressive="False" type="audio">,
|
82 |
+
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus" progressive="False" type="audio">]
|
83 |
+
|
84 |
+
Filtering for MP4 streams
|
85 |
+
-------------------------
|
86 |
+
|
87 |
+
To query only streams in the MP4 format::
|
88 |
+
|
89 |
+
>>> yt.streams.filter(file_extension='mp4')
|
90 |
+
[<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2" progressive="True" type="video">,
|
91 |
+
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">,
|
92 |
+
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028" progressive="False" type="video">,
|
93 |
+
...
|
94 |
+
<Stream: itag="394" mime_type="video/mp4" res="None" fps="30fps" vcodec="av01.0.00M.08" progressive="False" type="video">,
|
95 |
+
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2" progressive="False" type="audio">]
|