# ytdlp_web_player: a self hosted web player driven by yt-dlp

> A JavaScript and Python web application that turns yt-dlp into a streaming front end, with HLS transcoding through ffmpeg, a browser extension, a PWA install, SponsorBlock integration and a Cloudflare tunnel option for reaching it from outside your network.

**Matszwe02/ytdlp_web_player** — YT-DLP Web Player - Internet video player powered by yt-dlp

- Repository: https://github.com/Matszwe02/ytdlp_web_player
- Website: https://hub.docker.com/r/matszwe02/ytdlp_web_player
- Stars: 799 · Forks: 31
- Language: JavaScript
- License: GPL-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/matszwe02-ytdlp-web-player

## yt-dlp does the resolving, the web player does the streaming

The architecture is a division of labour, and the project is explicit about where the line falls.

yt-dlp is the video downloader and does the hard work of resolving a source URL into something streamable. videojs is the player. Between them sits HLS transcoding for the most reliable playback, with ffmpeg providing better format support. Node or Deno is there for solving yt-dlp's JavaScript challenges, which is the recurring obstacle when a site requires a browser to be present.

That last piece is the reason the project ships a runtime alongside Python. A pure Python deployment cannot solve the challenge steps that some sites require, so the app needs a JavaScript engine to execute them. The Dockerfile reflects this by installing deno alongside ffmpeg in the runtime image.

Around that core sit the integrations that make it feel like a product rather than a script. SponsorBlock integration trims sponsored segments. The Media Session API integration exposes the playback to system media controls. The Audio Context API handles audio over-amplification and drives the optional music visualizer.

The audience is someone who wants a private front end for video they can already resolve with yt-dlp, on their own machine or their own server, rather than a hosted service. The primary language is JavaScript, with the server in Python, and the licence is GPL-2.0.

## Running it is one Docker command or one Python script

The application is reachable at http://localhost:5000, and the documentation lists several ways to get it there.

The quickest is Docker, and there are two tags with different meanings:

```bash
docker run -p 5000:5000 matszwe02/ytdlp_web_player:stable
```

The stable tag is the one described as ready for everyday use, while the latest tag may have new and experimental features and may contain more bugs.

If you would rather build from the repository, you clone it and run `docker compose up`, and the compose file is where the optional pieces live. The environment block is commented out, along with an env_file entry pointing at `src/.env`, so you enable it and copy `src/example.env` to `src/.env` to modify it. The same file carries a commented watchtower service that, if enabled, performs daily updates.

Running it locally in Python means creating and activating a virtual environment in `src/`, installing `requirements.txt`, optionally installing ffmpeg and node or deno, which are otherwise installed automatically into the venv, and running `main.py`.

There is also a packaged server application on the Releases page, and a build path that needs python3 and nodejs installed, runs `/app/build_app.py`, and leaves the app and CLI in `/dist`. There is an official Unraid application as well.

## Transcoding is the fallback, and it explains the loading times

The limitations section is unusually honest about performance, and the numbers there are the real design constraint.

Most videos load in around 3 seconds. If that fails, transcoding starts and the video loads in around 10 seconds. If that also fails, the whole video is downloaded until it becomes available. The feature list states the same thing more optimistically, saying most videos load in 3s.

That three step ladder is the practical consequence of relying on yt-dlp. Direct formats are usually available and fast, but when a site only offers a stream that will not play in a browser, or when the source needs to be repackaged, the app falls back to transcoding into HLS, and when transcoding fails it falls back to downloading the file first. So the app's worst case is a full download, and its load time is bounded by how long that takes.

Hardware acceleration is handled by ffmpeg detecting the best supported encoder, with NVIDIA NVENC used when available and an automatic fallback to CPU encoding with libx264 when acceleration is unavailable or fails. For Docker with NVIDIA acceleration, the documentation points at installing the NVIDIA Container Toolkit, uncommenting the `build` block that uses `Dockerfile.hwaccel` and the `gpus: all` line in the compose file, and running `docker compose up --build`. The fallback is what makes this usable without a GPU.

One more note on the packaged server application: because of how the necessary Python modules are packaged, a yt-dlp update can break on older builds when yt-dlp changes its dependencies, and a manual app update is then necessary.

## Cookies are the sharpest edge in the whole project

Some videos need cookies to work. With cookies in place you are logged in to the streaming site while using the app, which is the mechanism behind age restricted, members only or region limited content.

The mechanism is straightforward. You create a `src/cookies.txt` file and enable it in `compose.yml` when running under Docker, then paste the relevant cookies into it. The documentation suggests using a browser extension that exports cookies in netscape format, and links to the yt-dlp FAQ on the same topic. In the browser extension, you can enable automatic sending of browser cookies for individual videos in its settings.

Then comes the warning, and it is not soft. Cookies work the same way as your account credentials, and anyone holding them may mess up your account. The author does not guarantee that the cookies file is completely secure from being accessed through the player, notes that yt-dlp uses them when playing videos on behalf of the provided account, and recommends putting throwaway accounts there.

That is a genuine constraint, not a formality. The file sits next to the application, the application runs yt-dlp with it, and the extension can send cookies for individual videos automatically. If you use this with a real account, you are trusting a locally running tool with a session token for that account, and the project's own advice is not to do that.

## Reaching it from outside without a proxy of your own

Some of the features need HTTPS to work, and the browser extension and the PWA are among them. Service workers and extension APIs both refuse to work over plain HTTP on a non-localhost origin.

The documented shortcut is the `PUBLISH_WITH_CLOUDFLARE` environment variable set to true, which exposes the app through a generated Cloudflare link. With that link you can also reach the app from anywhere without port forwarding. The link is generated and visible in the application logs just before the app starts, and it is temporary, changing on each start.

That is a real answer to a real constraint. Setting up a reverse proxy with certificates for a home network is exactly the kind of thing that stops people from exposing a service, so a tunnel is a pragmatic shortcut. The trade is that the URL rotates, so anything pointing at a fixed address breaks on restart, and the tunnel routes your traffic through a third party.

The feature list also covers a browser extension, a PWA install, a minimalistic UI with a configurable theme colour, paste URL or typed search query, auto pasting from the clipboard, zoom to fill for all devices, download, repeat videos, an optional music visualizer, and livestream support. The screenshots emphasise vertical video support, where the player adjusts its aspect ratio to each video and can zoom to fill in fullscreen.

Two items are listed as planned rather than shipped: video quality changing without interrupts, and user-side player configuration. So on quality selection and per user settings, the documentation is explicit that they are not there yet.

## Where ytdlp_web_player is the wrong tool

Three limits deserve naming.

The first is upstream dependency. The project states that only yt-dlp supported videos work, and its own feature list leads with daily automatic updates of yt-dlp so that new codecs and sites are supported immediately. That is a strength and a fragility at once. A tool that depends on a scraper for a site whose behaviour changes is a tool whose success depends on how quickly the scraper is updated, and the daily update is also a daily chance for something to change under you.

The second is that this is a player, not a library or a service you integrate into a product. There is no documented API to embed in your own application, no library to import and no plugin interface. It is a self hosted web application with a set of client surfaces, and the feature list is the whole scope. If you need video playback with yt-dlp resolution inside your own product, you would be forking this rather than depending on it.

The third is the project status. The documentation says the project is under heavy development and that you may expect bugs and issues, and the recent tags are v1.0.1, v1.1.0 and v1.2.0 from July and August 2026, so it is moving quickly through a young version line.

The alternative to name is running yt-dlp directly and serving the file, which gives you the same resolution logic with none of the streaming machinery, at the cost of the HLS transcoding, the extension, the PWA and the browser based playback. The difference in approach is that this project wraps a command line tool in a web application with a fallback chain that ends in a full download, while calling yt-dlp yourself gives you exact control over formats and no transcoding layer at all.

## Conclusion

Adopt ytdlp_web_player if you want a private web player that resolves video sources with yt-dlp and streams them through HLS, on hardware you control, with no account tied to a service. Do not adopt it as a general video site or for content you do not have the right to watch, and read the cookies section before you wire in a real account, because the project warns that cookies behave like credentials and recommends throwaway accounts. Verify first which videos yt-dlp can still resolve when you install, since the project states only yt-dlp supported videos work, and that hardware acceleration works on your GPU before you rely on it for transcoding.

## FAQ

### How do I run the ytdlp_web_player server?

Run `docker run -p 5000:5000 matszwe02/ytdlp_web_player:stable` for the stable image, or clone the repository and run `docker compose up`. The application is then reachable at http://localhost:5000. Running from source means creating a virtual environment in src/, installing requirements.txt and running main.py.

### What technologies does ytdlp_web_player use?

yt-dlp as the video downloader, videojs as the player, HLS transcoding for reliable playback, ffmpeg for better format support, node or deno for solving yt-dlp's JavaScript challenges, SponsorBlock integration, the Media Session API for system playback controls and the Audio Context API for over-amplification and the music visualizer.

### Why is a video not loading right away?

Most videos load in around 3 seconds. If that fails, transcoding starts and the video loads in around 10 seconds, and if that also fails the whole video is downloaded until it becomes available. The fallback chain is described in the project limitations.

### How do I use cookies with ytdlp_web_player?

Create a src/cookies.txt file with cookies in netscape format and enable it in compose.yml, then paste the relevant cookies in. The project warns that cookies behave like account credentials, that anyone holding them may mess up your account, and recommends throwaway accounts.

### Can I reach ytdlp_web_player from outside my network?

Some features such as the extension and the PWA require HTTPS. Setting the PUBLISH_WITH_CLOUDFLARE environment variable to true exposes the app through a generated Cloudflare link that is shown in the logs just before startup, avoiding port forwarding. The link is temporary and changes on each start.

## Sources

- [License: GPL-2.0](https://github.com/Matszwe02/ytdlp_web_player/blob/main/LICENSE)
- [Matszwe02/ytdlp_web_player on GitHub](https://github.com/Matszwe02/ytdlp_web_player)
- [Project website](https://hub.docker.com/r/matszwe02/ytdlp_web_player)
- [README](https://github.com/Matszwe02/ytdlp_web_player/blob/main/README.md)
- [Releases](https://github.com/Matszwe02/ytdlp_web_player/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/matszwe02-ytdlp-web-player
