updated readme, added orderby
Browse files- README.rst +131 -3
- pytube/query.py +14 -1
README.rst
CHANGED
@@ -38,12 +38,140 @@ Download using pip via pypi.
|
|
38 |
|
39 |
pip install pytube
|
40 |
|
41 |
-
|
42 |
-
|
43 |
|
44 |
-
|
45 |
|
46 |
.. code-block:: python
|
47 |
|
48 |
>>> from pytube import YouTube
|
49 |
>>> YouTube('http://youtube.com/watch?v=9bZkp7q19f0').streams.first().download()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
pip install pytube
|
40 |
|
41 |
+
Getting started
|
42 |
+
===============
|
43 |
|
44 |
+
Let's begin with showing how easy it is to download a video with pytube:
|
45 |
|
46 |
.. code-block:: python
|
47 |
|
48 |
>>> from pytube import YouTube
|
49 |
>>> YouTube('http://youtube.com/watch?v=9bZkp7q19f0').streams.first().download()
|
50 |
+
|
51 |
+
This example will download the highest quality progressive download stream available.
|
52 |
+
|
53 |
+
Next, let's explore how we would view what video streams are available:
|
54 |
+
|
55 |
+
.. code-block:: python
|
56 |
+
|
57 |
+
>>> yt = YouTube('http://youtube.com/watch?v=9bZkp7q19f0')
|
58 |
+
>>> yt.streams.all()
|
59 |
+
[<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">,
|
60 |
+
<Stream: itag="43" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp8.0" acodec="vorbis">,
|
61 |
+
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">,
|
62 |
+
<Stream: itag="36" mime_type="video/3gpp" res="240p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">,
|
63 |
+
<Stream: itag="17" mime_type="video/3gpp" res="144p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">,
|
64 |
+
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">,
|
65 |
+
<Stream: itag="248" mime_type="video/webm" res="1080p" fps="30fps" vcodec="vp9">,
|
66 |
+
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">,
|
67 |
+
<Stream: itag="247" mime_type="video/webm" res="720p" fps="30fps" vcodec="vp9">,
|
68 |
+
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401e">,
|
69 |
+
<Stream: itag="244" mime_type="video/webm" res="480p" fps="30fps" vcodec="vp9">,
|
70 |
+
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">,
|
71 |
+
<Stream: itag="243" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp9">,
|
72 |
+
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">,
|
73 |
+
<Stream: itag="242" mime_type="video/webm" res="240p" fps="30fps" vcodec="vp9">,
|
74 |
+
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">,
|
75 |
+
<Stream: itag="278" mime_type="video/webm" res="144p" fps="30fps" vcodec="vp9">,
|
76 |
+
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">,
|
77 |
+
<Stream: itag="171" mime_type="audio/webm" abr="128kbps" acodec="vorbis">,
|
78 |
+
<Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus">,
|
79 |
+
<Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus">,
|
80 |
+
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus">]
|
81 |
+
|
82 |
+
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).
|
83 |
+
|
84 |
+
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.
|
85 |
+
|
86 |
+
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.
|
87 |
+
|
88 |
+
To only view these progressive download streams:
|
89 |
+
|
90 |
+
.. code:: bash
|
91 |
+
|
92 |
+
>>> yt.streams.filter(progressive=True).all()
|
93 |
+
[<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">,
|
94 |
+
<Stream: itag="43" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp8.0" acodec="vorbis">,
|
95 |
+
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">,
|
96 |
+
<Stream: itag="36" mime_type="video/3gpp" res="240p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">,
|
97 |
+
<Stream: itag="17" mime_type="video/3gpp" res="144p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">]
|
98 |
+
|
99 |
+
Conversely, if you only want to see the DASH streams (also referred to as "adaptive") you can do:
|
100 |
+
|
101 |
+
.. code:: bash
|
102 |
+
|
103 |
+
>>> yt.streams.filter(adaptive=True).all()
|
104 |
+
[<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">,
|
105 |
+
<Stream: itag="248" mime_type="video/webm" res="1080p" fps="30fps" vcodec="vp9">,
|
106 |
+
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">,
|
107 |
+
<Stream: itag="247" mime_type="video/webm" res="720p" fps="30fps" vcodec="vp9">,
|
108 |
+
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401e">,
|
109 |
+
<Stream: itag="244" mime_type="video/webm" res="480p" fps="30fps" vcodec="vp9">,
|
110 |
+
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">,
|
111 |
+
<Stream: itag="243" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp9">,
|
112 |
+
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">,
|
113 |
+
<Stream: itag="242" mime_type="video/webm" res="240p" fps="30fps" vcodec="vp9">,
|
114 |
+
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">,
|
115 |
+
<Stream: itag="278" mime_type="video/webm" res="144p" fps="30fps" vcodec="vp9">,
|
116 |
+
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">,
|
117 |
+
<Stream: itag="171" mime_type="audio/webm" abr="128kbps" acodec="vorbis">,
|
118 |
+
<Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus">,
|
119 |
+
<Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus">,
|
120 |
+
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus">]
|
121 |
+
|
122 |
+
|
123 |
+
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.
|
124 |
+
|
125 |
+
To list the audio only streams:
|
126 |
+
|
127 |
+
.. code:: bash
|
128 |
+
|
129 |
+
>>> yt.streams.filter(only_audio=True).all()
|
130 |
+
[<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">,
|
131 |
+
<Stream: itag="171" mime_type="audio/webm" abr="128kbps" acodec="vorbis">,
|
132 |
+
<Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus">,
|
133 |
+
<Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus">,
|
134 |
+
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus">]
|
135 |
+
|
136 |
+
|
137 |
+
To list only ``mp4`` streams:
|
138 |
+
|
139 |
+
.. code:: bash
|
140 |
+
|
141 |
+
>>> yt.streams.filter(subtype='mp4').all()
|
142 |
+
[<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">,
|
143 |
+
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">,
|
144 |
+
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">,
|
145 |
+
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">,
|
146 |
+
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401e">,
|
147 |
+
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">,
|
148 |
+
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">,
|
149 |
+
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">,
|
150 |
+
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">]
|
151 |
+
|
152 |
+
|
153 |
+
Multiple filters can also be specified:
|
154 |
+
|
155 |
+
.. code:: bash
|
156 |
+
|
157 |
+
>>> yt.streams.filter(subtype='mp4', progressive=True).all()
|
158 |
+
>>> # this can also be expressed as:
|
159 |
+
>>> yt.streams.filter(subtype='mp4').filter(progressive=True).all()
|
160 |
+
[<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">,
|
161 |
+
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">]
|
162 |
+
|
163 |
+
You also have an interface to select streams by their itag, without needing to filter:
|
164 |
+
|
165 |
+
.. code:: bash
|
166 |
+
|
167 |
+
>>> yt.streams.get_by_itag(22)
|
168 |
+
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">
|
169 |
+
|
170 |
+
|
171 |
+
If you need to optimize for a specific feature, such as the "highest resolution" or "lowest average bitrate":
|
172 |
+
|
173 |
+
.. code:: bash
|
174 |
+
|
175 |
+
>>> yt.streams.filter(progressive=True).order_by('resolution').desc().all()
|
176 |
+
|
177 |
+
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.
|
pytube/query.py
CHANGED
@@ -103,7 +103,7 @@ class StreamQuery:
|
|
103 |
filters.append(lambda s: s.is_progressive)
|
104 |
|
105 |
if adaptive:
|
106 |
-
filters.append(lambda s: s.
|
107 |
|
108 |
if custom_filter_functions:
|
109 |
for fn in custom_filter_functions:
|
@@ -114,6 +114,19 @@ class StreamQuery:
|
|
114 |
fmt_streams = list(filter(fn, fmt_streams))
|
115 |
return StreamQuery(fmt_streams)
|
116 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
def get_by_itag(self, itag):
|
118 |
"""Get a :class:`Stream <Stream>` for an itag, or None if not found.
|
119 |
|
|
|
103 |
filters.append(lambda s: s.is_progressive)
|
104 |
|
105 |
if adaptive:
|
106 |
+
filters.append(lambda s: s.is_adaptive)
|
107 |
|
108 |
if custom_filter_functions:
|
109 |
for fn in custom_filter_functions:
|
|
|
114 |
fmt_streams = list(filter(fn, fmt_streams))
|
115 |
return StreamQuery(fmt_streams)
|
116 |
|
117 |
+
def order_by(self, attribute_name):
|
118 |
+
fmt_streams = sorted(
|
119 |
+
self.fmt_streams,
|
120 |
+
key=lambda s: getattr(s, attribute_name),
|
121 |
+
)
|
122 |
+
return StreamQuery(fmt_streams)
|
123 |
+
|
124 |
+
def desc(self):
|
125 |
+
return StreamQuery(self.fmt_streams[::-1])
|
126 |
+
|
127 |
+
def asc(self):
|
128 |
+
return self
|
129 |
+
|
130 |
def get_by_itag(self, itag):
|
131 |
"""Get a :class:`Stream <Stream>` for an itag, or None if not found.
|
132 |
|