Open-source project
Athlon1600/youtube-downloader avatar
Athlon1600/youtube-downloader

youtube-downloader: pulling YouTube stream URLs from pure PHP

:tv: PHP based alternative to youtube-dl and yt-dlp. Active and frequently updated! :star:

934 stars307 forksPHPMIT

At a glance

What is it?
Athlon1600/youtube-downloader is a Composer library that resolves YouTube download links and streams them without invoking youtube-dl, yt-dlp or a JavaScript runtime. It suits PHP teams that need video URLs inside an application, not command line users.
Who is it for?
Adopt this library if your stack is PHP and you need a resolved stream URL inside an application, and skip it if you need separate audio and video tracks, merged output or a working path around YouTube throttling, since the to-do list still names all three as missing.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 143 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A PHP answer to a Python-shaped problem

The project positions itself as a PHP alternative to youtube-dl and yt-dlp, and the distinction it draws is about what does not run. The description states plainly that the script uses no JavaScript interpreters and makes no calls to the shell, and the README repeats that point, adding that there are no heavy dependencies. That constraint matters in hosting environments where shelling out to a Python binary is blocked, where process isolation is painful, or where a shared host will not let you install anything outside the language runtime.

The audience is therefore narrower than the topic usually suggests. This is not a command line front end that writes files to a directory. It is a Composer package with a namespace, `YouTube`, that you instantiate inside your own code and call methods on. The repository description frames it as a personal use tool and includes a legal disclaimer stating that downloading copyrighted material without permission breaks YouTube's terms of service and that responsibility for such use rests with the user.

DownloadOptions holds the format list the extractor found

The mechanism is visible in the return type. The call `getDownloadLinks` takes a watch URL and returns a `DownloadOptions` object, and the README states that this object holds an array of stream links, some audio only and some carrying audio and video combined into a single file. The example code branches on `getAllFormats`, and when the array is not empty it reaches for `getFirstCombinedFormat` and prints the `url` property. When the array is empty the example prints `No links found`, which tells you that an empty result is an expected branch in normal use rather than an exception path.

For most applications the README points at `getCombinedFormats` instead, on the reasoning that typical use cares about combined streams rather than the audio only variants. So there are two levels of access on the same returned object: a full list and a filtered subset. The extraction itself runs without an external binary, which is the whole point of the project, but the README does not describe the parsing or cipher work that sits behind `getDownloadLinks`, offering instead a pull request comment as the current explanation. That is a real gap for anyone who needs to debug a failed extraction.

Composer install and the first resolution call

Installation goes through Composer, which is what the README recommends, and the supported PHP range appears on the project badges as `^7.4 || ^8.0`. The package is namespaced, so a working script imports the downloader class and the exception class, constructs the downloader, and asks it for the links of a specific video. Exceptions are typed: `YouTubeException` is caught around the call, and the handler prints the message, so an extraction failure surfaces as a catchable error rather than a silent empty array.

Install the package like this:

bash
composer require athlon1600/youtube-downloader "^4.0"

Then the first real use looks like this:

php
use YouTube\YouTubeDownloader;
use YouTube\Exception\YouTubeException;

$youtube = new YouTubeDownloader();

try {
    $downloadOptions = $youtube->getDownloadLinks("https://www.youtube.com/watch?v=aqz-KE-bpKQ");

    if ($downloadOptions->getAllFormats()) {
        echo $downloadOptions->getFirstCombinedFormat()->url;
    } else {
        echo 'No links found';
    }

} catch (YouTubeException $e) {
    echo 'Something went wrong: ' . $e->getMessage();
}

What you should see on the first successful run is a single stream URL printed to standard output, which is a direct link to a media resource rather than a page you would still have to parse.

Age-restricted videos require your own exported cookies

The library does not attempt to log in for you, and the README is explicit that the only route for age-restricted content is for you to sign into your own YouTube account in a browser, export the newly set cookies from that browser into a file, and hand them over. The browser object exposed by `getBrowser` accepts that cookie file and a user agent string, so you can supply an exported `cookies.txt` style file and mimic a particular client. The README points to the matching youtube-dl documentation for the cookie format, which is the closest thing to a format specification in the project.

php
$youtube = new YouTubeDownloader();
$youtube->getBrowser()->setCookieFile('./your_cookies.txt');
$youtube->getBrowser()->setUserAgent('Opera 7.6');

There is a second related obstacle. Depending on your region you can be redirected to a page asking you to accept a cookie policy before reaching YouTube, and the project documents a way to accept those terms programmatically through the `consentCookies` method on the browser instance, which it says removes that warning permanently.

php
$youtube = new YouTubeDownloader();
$youtube->getBrowser()->consentCookies();

Handling account cookies means you are handling account credentials inside your application, which raises an operational burden the library does not hide but also does not manage for you.

Serving the stream from your own server

Beyond downloading, the project can hand a media resource straight to a client. A separate class, `YouTubeStreamer`, sits in the same namespace and exposes a `stream` method that takes a direct googlevideo playback URL. The example passes a full URL with its query string intact, which is the form produced by the extraction step, so the intended flow is to resolve first and then stream the resolved address.

php
$youtube = new \YouTube\YouTubeStreamer();
$youtube->stream('https://r4---sn-n4v7knll.googlevideo.com/videoplayback?...');

The distinction between the two classes is the useful design detail here. `YouTubeDownloader` deals with YouTube pages and returns descriptors, while `YouTubeStreamer` deals with media bytes and expects an address you already have. Anyone building a proxy or a preview feature can therefore mix the two, and anyone worried about the bandwidth cost of proxying can see that the streaming class takes the burden on whichever side you place it.

The walls the to-do list leaves standing

The most useful part of the project for an evaluator is its own list of missing pieces, because it names the failures you are most likely to hit. It states that private video downloading is not allowed yet. It states that YouTube throttling caps download speed below 100 kb/s for most videos and that no workaround has been found. It states that there is no ability to solve YouTube captchas and avoid `HTTP 429 Too Many Requests` errors. It states that audio and video streams cannot yet be downloaded separately and merged with ffmpeg the way youtube-dl does, and that a command finding all formats is still optional.

Those five gaps are the real difference from the Python tools the project set out to replace. If you need the best available quality you generally want separate tracks and a merge step, and that is exactly the piece marked as unfinished. The 429 and captcha items also matter for any server that would run this in a loop rather than on demand, since repeated requests are what trigger the throttle. The project is honest about this rather than concealing it, which is a fair position for a library still at version 4.

MIT terms, PHP 7.4 and 8.x, and a recent release

The licence field on the repository says MIT, which is permissive and places few obligations on redistribution beyond keeping the notice. That is a straightforward arrangement for a package you vendor through Composer, and you should still read the licence text yourself if your use involves serving video to third parties.

On maintenance, the release history is short and recent at the top. Version v4.0.1 was published on 2026-06-01, and the last push was on 2026-06-02. Before that, v4.0.0 dates to 2023-11-12 and v3.1.2 to 2023-06-08, so the gap between the current major line and its predecessor is wide while the gap to v4.0.1 is small. The repository layout carries the usual pieces for a modern PHP package, including a `composer.json`, a `composer.lock`, a `src` directory, a `tests` directory, static analysis configuration in `phpstan.neon.dist`, and continuous integration workflows referenced by the build and test badges.

For an adopter this means upgrades land as tagged releases with a version constraint, and that a dependency declaration pinned to `^4.0` will pick up patch releases such as v4.0.1 without a major version change.

Editorial conclusion

Adopt this library if your stack is PHP and you need a resolved stream URL inside an application, and skip it if you need separate audio and video tracks, merged output or a working path around YouTube throttling, since the to-do list still names all three as missing. Before committing, run the Composer install against your PHP version and confirm with a single video that getDownloadLinks returns the formats you expect, and check the MIT licence terms on the repository against your own distribution plans.

Frequently asked questions

How do I install youtube-downloader with Composer?

The README recommends Composer and gives a single command, `composer require athlon1600/youtube-downloader "^4.0"`. The project badges list the supported PHP versions as `^7.4 || ^8.0`.

How do I get a download link for a YouTube video in PHP?

Instantiate `YouTubeDownloader`, call `getDownloadLinks` with the watch URL, and read the returned `DownloadOptions` object. The README points most usage at the `getCombinedFormats` method for combined audio and video streams.

Can youtube-downloader handle age-restricted videos?

Only if you supply your own cookies. The README describes logging into YouTube in your browser, exporting the cookies to a file, and passing that file with `setCookieFile` along with `setUserAgent` on the browser object.

Does youtube-downloader merge separate audio and video streams like youtube-dl?

No. The to-do list describes separate audio and video downloads merged with ffmpeg as a feature still to be added. The current versions return combined formats instead.

What speed can I expect when downloading with youtube-downloader?

The to-do list notes that YouTube throttles downloads to less than 100 kb/s for most videos and that no way around the throttling has been found yet. It also lists captcha solving and avoiding `HTTP 429 Too Many Requests` as unfinished work.

What licence is youtube-downloader released under?

The licence field on the repository says MIT. The README also carries a legal disclaimer that the program is for personal use only and that downloading copyrighted material without permission goes against YouTube's terms of service.

Official sources

  1. Athlon1600/youtube-downloader on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/athlon1600-youtube-downloader.svg)](https://hysenlabs.com/projects/athlon1600-youtube-downloader)

Was this useful?