Download a YouTube playlist with several concurrent yt-dlp workers and a live Textual UI.
Project description
yt-pdlp
Download a large YouTube playlist with several yt-dlp workers running concurrently, showing each worker's live progress in a Textual terminal UI. It supports a planning dry-run and a post-run flush report that reconciles how many videos actually landed versus failed.
Why
yt-dlp has no built-in option to download multiple playlist items in parallel —
its -N / --concurrent-fragments flag only parallelises fragments within a
single video, so playlist items are processed one at a time. Downloading a
~1,000-item playlist (such as Watch Later) therefore crawls. yt-pdlp
runs several downloads at once in one process, with a live UI and resumability.
Requirements
- Python ≥ 3.11
ffmpegon yourPATH(needed to remux to mp4)- A browser you are signed in to YouTube with (default: Chrome) — Watch Later is private, so cookies are required.
Install & run
This project uses uv:
uv sync # create the environment
uv run yt-pdlp --help # see all options
Usage
yt-pdlp is a command group with a default command, so a bare invocation
downloads:
# Download Watch Later with 4 workers (the defaults)
uv run yt-pdlp
# Six workers, a specific playlist, into ./videos
uv run yt-pdlp -j 6 -u "https://www.youtube.com/playlist?list=PLxxxx" -o ./videos
# Plan only — contacts YouTube read-only, downloads nothing
uv run yt-pdlp --dry-run
# Reconcile an existing output directory without downloading
uv run yt-pdlp flush -o ./videos
download options (the default command)
| Option | Short | Default | Meaning |
|---|---|---|---|
--jobs |
-j |
4 |
Number of concurrent workers. |
--url |
-u |
Watch Later | Playlist (or any yt-dlp-supported) URL. |
--output |
-o |
./downloads |
Output directory (created if absent). |
--browser |
-b |
chrome |
Browser to read cookies from. |
--format |
-f |
None |
Remux container; an empty string or None disables remux. |
--fragments |
-N |
8 |
concurrent_fragment_downloads per worker (intra-video). |
--dry-run |
off | Plan only; download nothing. | |
--plain |
auto | Disable the Textual UI; emit line-based progress (auto-on when stdout is not a terminal). |
flush options
| Option | Short | Default | Meaning |
|---|---|---|---|
--output |
-o |
./downloads |
Locate and reconcile this output's state directory. |
--url |
-u |
(optional) | Re-flatten this playlist to define the "requested" set instead of using the cached list. |
How it works
- A shared work queue holds every playlist entry.
--jobsworkers each pull the next entry and download it, so the work load-balances naturally across videos of very different lengths. - Each worker owns its own
yt_dlp.YoutubeDLinstance and reports progress through a UI-agnostic event stream. A Textual front-end renders per-worker panels and an overall counter; a plain front-end prints line-based progress when there is no terminal. - A shared download archive records only successful downloads, so re-running the same command skips what is already done and retries what failed.
- The browser cookie store is read once and written to a reusable cookie file; every worker then reads the file (browsers lock their cookie database, so reading it from many workers at once would contend).
State directory
Everything lives under <output>/.ytdlp-state/:
| File | Purpose |
|---|---|
cookies.txt |
Cookies exported once from the browser. Sensitive — it holds a live session; keep it private. |
entries.json |
The flattened playlist: a list of {id, url, title}. |
archive.txt |
The yt-dlp download archive (resume + skip). |
failed.txt |
Outstanding URLs after a run or flush, one per line — retry with yt-dlp -a failed.txt … or just re-run. |
report.txt |
The last completion report. |
Downloaded media files go directly under <output>/.
A note on concurrency
The realistic sweet spot is 4–8 workers. The bottleneck is YouTube's
per-account/IP throttling (HTTP 429), not your machine — beyond a handful of
workers, total throughput usually drops. yt-pdlp warns when you ask
for more than 8 but does not stop you.
Development
uv run pytest -q # tests
uv run ruff check src tests # lint
uv run ruff format --check src tests # formatting
uv run ty check src # type-check
The download engine is deliberately decoupled from the UI, and yt-dlp, the network, and the terminal are faked in tests only so the whole tool runs offline in CI.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file yt_pdlp-0.0.0.dev2.tar.gz.
File metadata
- Download URL: yt_pdlp-0.0.0.dev2.tar.gz
- Upload date:
- Size: 43.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3113b4ef9c990a9f67a8d803363155aa12da0656662eb262c555275f4111bbe5
|
|
| MD5 |
7f280d78dafa091927fae0c6d1163163
|
|
| BLAKE2b-256 |
663f5a6a6f01d855ec4e47838130f6e7731cb586be954abf27a440178510fd64
|
Provenance
The following attestation bundles were made for yt_pdlp-0.0.0.dev2.tar.gz:
Publisher:
release.yml on synmux/yt-pdlp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
yt_pdlp-0.0.0.dev2.tar.gz -
Subject digest:
3113b4ef9c990a9f67a8d803363155aa12da0656662eb262c555275f4111bbe5 - Sigstore transparency entry: 1624844427
- Sigstore integration time:
-
Permalink:
synmux/yt-pdlp@a7dd1c99276f49bd3717759c0e12dc9d451abf6c -
Branch / Tag:
refs/tags/0.0.0.dev2 - Owner: https://github.com/synmux
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a7dd1c99276f49bd3717759c0e12dc9d451abf6c -
Trigger Event:
release
-
Statement type:
File details
Details for the file yt_pdlp-0.0.0.dev2-py3-none-any.whl.
File metadata
- Download URL: yt_pdlp-0.0.0.dev2-py3-none-any.whl
- Upload date:
- Size: 26.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e718020062e2e3535f9f77a45e5c7bc3f02cde2e0dd4c1af40e4f0ee4bea57d
|
|
| MD5 |
5c2d3533e948261135d6418304235c4a
|
|
| BLAKE2b-256 |
89b35275517dd36858028ad503894df910e005bc786fcbcdefd91682e1ac6946
|
Provenance
The following attestation bundles were made for yt_pdlp-0.0.0.dev2-py3-none-any.whl:
Publisher:
release.yml on synmux/yt-pdlp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
yt_pdlp-0.0.0.dev2-py3-none-any.whl -
Subject digest:
8e718020062e2e3535f9f77a45e5c7bc3f02cde2e0dd4c1af40e4f0ee4bea57d - Sigstore transparency entry: 1624844453
- Sigstore integration time:
-
Permalink:
synmux/yt-pdlp@a7dd1c99276f49bd3717759c0e12dc9d451abf6c -
Branch / Tag:
refs/tags/0.0.0.dev2 - Owner: https://github.com/synmux
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a7dd1c99276f49bd3717759c0e12dc9d451abf6c -
Trigger Event:
release
-
Statement type: