======
pytube
======

.. image:: https://img.shields.io/pypi/v/pytube.svg
  :alt: Pypi
  :target: https://pypi.python.org/pypi/pytube/

.. image:: https://travis-ci.org/nficano/pytube.svg?branch=master
   :alt: Build status
   :target: https://travis-ci.org/nficano/pytube

.. image:: https://coveralls.io/repos/nficano/pytube/badge.svg?branch=master&service=github
  :alt: Coverage
  :target: https://coveralls.io/github/nficano/pytube?branch=master

*pytube* is a lightweight, dependency-free Python library (and command-line utility) for downloading YouTube Videos.

Description
===========

YouTube is the most popular video-sharing platform in the world and as a hacker you may encounter a situation where you want to script something to download videos.  For this I present to you *pytube*.

*pytube* is a lightweight library written in Python. It has no third party dependencies and aims to be highly reliable.

*pytube* makes *zero assumptions*, meaning there is no built-in method to get say the *"best"* quality video, *pytube* simply exposes all the available formats and resolutions, giving you the developer the power to define what *"best"* is.

*pytube* also makes pipelining easy, allowing you to specify callback functions for different download events, such as  ``on progress`` or ``on complete``.

Finally *pytube* also includes a command-line utility, allowing you to quickly download videos right from terminal.

Installation
============

Download using pip via pypi.

.. code-block:: bash

    pip install pytube

Getting started
===============

Let's begin with showing how easy it is to download a video with pytube:

.. code-block:: python

   >>> from pytube import YouTube
   >>> YouTube('http://youtube.com/watch?v=9bZkp7q19f0').streams.first().download()

This example will download the highest quality progressive download stream available.

Next, let's explore how we would view what video streams are available:

.. code-block:: python

   >>> yt = YouTube('http://youtube.com/watch?v=9bZkp7q19f0')
   >>> yt.streams.all()
   [<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">,
   <Stream: itag="43" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp8.0" acodec="vorbis">,
   <Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">,
   <Stream: itag="36" mime_type="video/3gpp" res="240p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">,
   <Stream: itag="17" mime_type="video/3gpp" res="144p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">,
   <Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">,
   <Stream: itag="248" mime_type="video/webm" res="1080p" fps="30fps" vcodec="vp9">,
   <Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">,
   <Stream: itag="247" mime_type="video/webm" res="720p" fps="30fps" vcodec="vp9">,
   <Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401e">,
   <Stream: itag="244" mime_type="video/webm" res="480p" fps="30fps" vcodec="vp9">,
   <Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">,
   <Stream: itag="243" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp9">,
   <Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">,
   <Stream: itag="242" mime_type="video/webm" res="240p" fps="30fps" vcodec="vp9">,
   <Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">,
   <Stream: itag="278" mime_type="video/webm" res="144p" fps="30fps" vcodec="vp9">,
   <Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">,
   <Stream: itag="171" mime_type="audio/webm" abr="128kbps" acodec="vorbis">,
   <Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus">,
   <Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus">,
   <Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus">]

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).

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.

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.

To only view these progressive download streams:

.. code-block:: python

    >>> yt.streams.filter(progressive=True).all()
    [<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">,
    <Stream: itag="43" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp8.0" acodec="vorbis">,
    <Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">,
    <Stream: itag="36" mime_type="video/3gpp" res="240p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">,
    <Stream: itag="17" mime_type="video/3gpp" res="144p" fps="30fps" vcodec="mp4v.20.3" acodec="mp4a.40.2">]

Conversely, if you only want to see the DASH streams (also referred to as "adaptive") you can do:

.. code-block:: python

    >>> yt.streams.filter(adaptive=True).all()
    [<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">,
    <Stream: itag="248" mime_type="video/webm" res="1080p" fps="30fps" vcodec="vp9">,
    <Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">,
    <Stream: itag="247" mime_type="video/webm" res="720p" fps="30fps" vcodec="vp9">,
    <Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401e">,
    <Stream: itag="244" mime_type="video/webm" res="480p" fps="30fps" vcodec="vp9">,
    <Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">,
    <Stream: itag="243" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp9">,
    <Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">,
    <Stream: itag="242" mime_type="video/webm" res="240p" fps="30fps" vcodec="vp9">,
    <Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">,
    <Stream: itag="278" mime_type="video/webm" res="144p" fps="30fps" vcodec="vp9">,
    <Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">,
    <Stream: itag="171" mime_type="audio/webm" abr="128kbps" acodec="vorbis">,
    <Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus">,
    <Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus">,
    <Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus">]


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.

To list the audio only streams:

.. code-block:: python

    >>> yt.streams.filter(only_audio=True).all()
    [<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">,
    <Stream: itag="171" mime_type="audio/webm" abr="128kbps" acodec="vorbis">,
    <Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus">,
    <Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus">,
    <Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus">]


To list only ``mp4`` streams:

.. code-block:: python

    >>> yt.streams.filter(subtype='mp4').all()
    [<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">,
    <Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">,
    <Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">,
    <Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">,
    <Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401e">,
    <Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">,
    <Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">,
    <Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">,
    <Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">]


Multiple filters can also be specified:

.. code-block:: python

    >>> yt.streams.filter(subtype='mp4', progressive=True).all()
    >>> # this can also be expressed as:
    >>> yt.streams.filter(subtype='mp4').filter(progressive=True).all()
    [<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">,
    <Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">]

You also have an interface to select streams by their itag, without needing to filter:

.. code-block:: python

    >>> yt.streams.get_by_itag(22)
    <Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">


If you need to optimize for a specific feature, such as the "highest resolution" or "lowest average bitrate":

.. code-block:: python

    >>> yt.streams.filter(progressive=True).order_by('resolution').desc().all()

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.

If your application requires post-processing logic, pytube allows you to specify an "on download complete" callback function:

.. code-block:: python

    >>> def convert_to_aac(stream, file_handle):
            # do work
    >>> yt.register_on_complete_callback(convert_to_aac)


Similarly, if your application requires on-download progress logic, pytube exposes a callback for this as well:

.. code-block:: python

    >>> def show_progress_bar(stream, chunk, file_handle, bytes_remaining):
            # do work
    >>> yt.register_on_progress_callback(show_progress_bar)



Command-line interface
======================

pytube also ships with a tiny cli interface for downloading and probing videos.

Let's start with downloading:

.. code-block:: bash

    pytube http://youtube.com/watch?v=9bZkp7q19f0 --itag=22

To view available streams:

.. code-block:: bash

    pytube http://youtube.com/watch?v=9bZkp7q19f0 --list


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.
