How to download Youtube videos by PHP

Saeed Moghadamzadeh
6 min readApr 19, 2020

Maybe PHP is not the best language for writing a download manager, but in this article, we are going to learn how we can download videos from Youtube. Because PHP does not support multi-threading, we cannot make a proper download manager that splits the file into several chunks and download it concurrently. There are some ways to add multi-threading to PHP, but for the sake of this article, we won’t go through those ways and will stick to single threading.

At the end of this article, we have a command called dl.It gets a Youtube video URL to download.

What we will learn

  • How to create a command-line application
  • How to use the Factory design pattern
  • How to get information about a Youtube video such as formats, titles, etc.
  • How to download a file with PHP with a progress bar

Create the project

Create a new directory and run composer init inside it:

After running composer init‍ you need to answer a few questions. This command creates a composer.json file inside the youtube-downloader directory.

Install dependencies

  • symfony/console is a great package that helps us to create a command-line application and make it easier to handle input and outputs.
  • smoqadam/youtube-video-info gives us information about a Youtube video: formats, subtitles, download URL, title, etc.

Config autoloading

Create a directory and call it src we will put all of our classes inside this directory. For taking advantage of composer autloading, open composer.json and add autoload section such as below:

Downloader is our chosen namespace for this project, and whenever the composer wants to load a class within this namespace, it looks inside the src directory.

You can find more information about PSR4 here.

Handle Command Line arguments

Create a Download.php file inside the src/Commands directory and with the following content:

path: src/Commands/Download.php

Download class extended Command from the symfony/console package, which gives us many features to handle command-line input and outputs.

  • $defaultName is the name of our command.
  • configure method is using to config our command, such as add description, add arguments, options, and so on.
  • And finally, execute will call when we run the dl command from CLI. It receives two parameters, $input for handling the inputs such as options and arguments, and $output for managing the output. As you can see, we used <info></info> tags in the write method to colorize the output.

Let’s make our dl command work by creating a index.php file under root directory:

index.php

Application is the main class responsible for handling the command line application, so to make our dl command work, we must use the add method to pass a new instance of Download class to it.

Now, open your terminal and run the following command:

$ php index.php dl http://example.com

you should see http://example.com in green color as output.

Downloader

In this section, we are going to make a downloader class. This class receives a URL and a file name, and it will download the file for us. But before we create our Downloader class, let’s think about how we can make it extendable? By extendable, I mean how we can have a Downloader that downloads files from different sources such as Youtube or Vimeo videos or maybe a direct link.

We know that Youtube videos have different qualities, and each quality has a different download link. So before we can download a video, we must get all qualities and ask users which quality they want to download. But this process would be different if they’re going to download from Vimeo or a direct link.

In the SOLID principle, S is stand for Single responsibility, which means each class should only have a single responsibility. With that explanation, we need to have different classes for different sources.

To achieve this, we can use Factory design pattern to instantiate a new object based on the URL that the user wants to download.

For example, if it was a Youtube URL, it will return a new object from Youtube class and so on.

Factory.php class under src/ directory would be something like this:

src/Factory.php

Factory Design pattern is a creational pattern that create a different object based on some conditions. In our case, we checked the $url and if it was a Yoututbe link, it returns a new instance of Yoututbe class, otherwise it throws an Exception.

As we saw earlier, we decided to let our app download from different sources, but these sources or providers must follow some rules to make it easier to extend and debug. We define our rules inside ProviderAbstract, and every provider (e.g., Youtube class) must extend from this class, and they must implement the init method.

ProviderAbstract

src/ProviderAbstract.php

Every provider must initialize and prepare the download link and output file name inside the init method.

Create a directory under src and call it Providers then create Youtube.php inside it:

src/Providers/Yoututbe.php

Youtube class extended from ProviderAbstract, which means it must implement the init method. Inside init, we took advantage of smoqadam/youtube-video-info package to parse a Youtube video. Then created a simple menu with each format in a new line. readline function receives an input which in our case it's an index of a menu item and whenever the user enters the number associated to the format, we set $this->url and $this->name.

The menu would be something like this:

Note: File sizes are in byte, you can convert to MB by yourself.

How to download a file

Now we are going to see how we can download a file and how to show a progress bar with PHP. Create a file under the src directory and call it Downloader.php:

We will talk about the download method in the next section.

What is Stream Context?

In PHP, Stream Context is using to enhance or modify a stream, and it can pass to most file handling functions. From the PHP website:

A context is a set of parameters and wrapper specific options which modify or enhance the behavior of a stream. Contexts are created using stream_context_create() and can be passed to most filesystem related stream creation functions (i.e. fopen(), file(), file_get_contents(), etc…).

stream_context_set_params receives a stream context created by stream_context_create and an array of parameters. One of the parameters we can set is notification that is a callback function. This function will be called whenever our context state changes. (see more: https://www.php.net/manual/en/function.stream-notification-callback.php)

In download method we passed the URL we want to download and the context we made earlier to fopen, it will open the remote file in a read mode, and whenever the state of the file changes, it will call streamCallback method. Inside the streamCallback method, we checked for the notification type and printed the proper information about each notification.

Now open sr/Command/Download.php and change the execute method as below:

Then open your terminal and try to download a Youtube video:

Cool, right?

Download from a direct link

So far, we can download Youtube videos, but what about direct links such as http://example.com/file.mp3 .

Because we used the Factory design pattern and Single Responsibility principle, it would be as easy as create a separate class for direct links.

Create Direct.php file under src/Providers directory with the following contents:

Because direct links are direct, the only thing we should do is to set $this->url and $this->name. Now, open src/Factory.php and change it as below:

As you can see, we changed the default section to return a new object of Direct class.

You can find the project in this repository:

Summary

Although PHP is not an excellent tool to write a downloader, we saw how we could manage and download a file from Youtube or a direct link. We learned about Stream Context and how we can use them of change or get information about a stream. We used the Factory design pattern and saw what the heck Single Responsibility means. Now it’s your time to use your imagination and creativity to extend this project. Maybe by adding more providers or how to make use of multi-threading extensions such as https://github.com/krakjoe/parallel to make it more usable.

--

--