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">]
|