VDL
VDL is a local-first media workflow runtime for building apps and workflows around video downloads, playlists, page discovery, link lists, progress events, and structured results.
It is designed around three surfaces:
| Surface | Default State | Best For |
|---|---|---|
| Python library | Stateless | Embedding media download/discovery in apps and tools |
| Headless CLI | Stateless | Scripts, one-shot jobs, and automation |
| Interactive app | Stateful | Guided terminal UX with history, status, SQLite, and logs |
External products own their own persistence and product workflows. VDL provides the media execution capability; it does not try to become a media library app.
Full developer documentation is live at: https://video-downloader-657d90.gitlab.io
flowchart TD
StandaloneBinary["Standalone binary<br/>(curl install.sh)"]
PythonPkg["Python package<br/>(pip install vdl)"]
StandaloneBinary --> CLI["vdl CLI"]
StandaloneBinary --> App["Interactive app"]
StandaloneBinary --> MCP1["vdl-mcp"]
PythonPkg --> Library["Python library"]
PythonPkg --> CLI
PythonPkg --> App
PythonPkg --> Tools["vdl.tools"]
PythonPkg --> MCP2["MCP server<br/>(mcp extra)"]
Install vdl Binary
Use the standalone installer when you want the commands without setting up a Python project:
macOS / Linux:
curl -fsSL https://bildcraft.gitlab.io/products/video-downloader/vdl/install.sh | sh
Windows:
irm https://bildcraft.gitlab.io/products/video-downloader/vdl/install.ps1 | iex
This gives you:
vdlvdl-mcp
Re-running the standalone installer is safe:
- installs if VDL is missing
- updates if the installed version is older
- does nothing if the same version is already installed
- checks required dependencies before replacing binaries
Interactive Download App
Start the interactive app with:
vdl
It opens an interactive terminal interface:
The app guides you through:
- Input — paste a URL, local file path, or
.txtlink list - Quality — choose preferred resolution (best, 2160p, 1440p, 1080p, 720p, 480p, or audio)
- Output — set download directory (default:
~/Downloads/VDL) - Options — save thumbnails and subtitles
- Mode — download directly or crawl the page for more links
Perfect for downloading single videos, playlists, or discovering downloadable media on websites.
YouTube 4K support
VDL standalone releases and the developer environment pin a tested yt-dlp
nightly (currently 2026.10.10.233141) and include its EJS challenge scripts.
YouTube extraction also needs a JavaScript runtime: Deno is preferred, or
Node.js 22+ can be configured with vdl config set js_runtime_path /path/to/node.
FFmpeg/ffprobe are required for merging and verification.
best uses yt-dlp's native best-quality ranking without a resolution cap.
Numbered quality requests are exact: --quality 2160p fails if that resolution
is unavailable. --fallback selects the best resolution at or below the request.
Aliases 4k and 2k mean 2160p and 1440p. Video output uses MP4, preserving
the selected video/audio codecs without re-encoding.
Default YouTube clients are used; VDL does not install or run a PO-token service. Cookies are only used when explicitly configured. Format availability still depends on the video and YouTube's restrictions.
vdl update updates standalone VDL and its bundled yt-dlp together through
verified VDL releases. It does not independently update yt-dlp between releases.
Use vdl doctor to see the active yt-dlp version, channel, EJS, and runtime.
Source/Python package installations should update through their package manager.
The interactive app is intentionally stateful: it creates local app directories, SQLite state, and logs so it can provide history/status/session UX.
CLI Commands
Check the installed binary:
vdl --version
Run direct commands:
vdl download https://example.com/video
vdl crawl https://example.com/gallery --first 3
vdl crawl https://example.com/gallery --crawl-pagination --first 3
vdl playlist https://example.com/playlist
vdl resolve https://example.com/playlist
vdl status
vdl doctor
These headless commands are stateless by default. They do not implicitly create the interactive SQLite database or app logs.
Crawl-specific behavior is intentionally different from direct download and playlist flows:
vdl crawl ... --first 3means first 3 successful downloads, not first 3 discovered links- crawl applies its own admission policy, including a default 7 MB minimum file size
--allow-tinydisables that crawl-only minimum--crawl-paginationfollows pagination/navigation pages discovered during crawl- direct downloads and playlists are not guarded by the crawl minimum, which keeps audio and other intentional small downloads working normally
When a site needs authentication, the headless commands can take cookies too:
vdl download --cookies cookies.txt https://example.com/video
vdl crawl --cookies-from-browser firefox https://example.com/gallery --first 3
Install as Python Library
Use the Python package when you want to embed VDL in another Python project:
pip install vdl
The full Python/library docs are live at: https://video-downloader-657d90.gitlab.io/python-api/
That gives you:
- the
vdlPython library - the
vdlCLI - the interactive app entrypoint
vdl.tools
Use the library directly:
import asyncio
from vdl import CrawlerPolicy, VDLClient
async def main() -> None:
client = VDLClient()
result = await client.download("https://example.com/video")
print(result.status)
crawl_plan = await client.discover(
"https://example.com/gallery",
crawler_policy=CrawlerPolicy(crawl_pagination_pages=True),
)
crawl_results = await client.execute_plan(
crawl_plan,
crawler_policy=CrawlerPolicy(first_successful=3),
)
print(len(crawl_results))
asyncio.run(main())
The library is stateless by default and embeddable. Your application owns any database, history, user library, retries, or product-specific workflow state.
Use the tool surface:
import asyncio
from vdl.tools import download_video
async def main() -> None:
result = await download_video("https://example.com/video")
print(result["status"])
asyncio.run(main())
MCP Support
Install the MCP extra when you want vdl-mcp:
pip install "vdl[mcp]"
That adds MCP support on top of the Python package. If you installed the
standalone binary, vdl-mcp is already included there too.
Example MCP client config:
{
"mcpServers": {
"vdl": {
"command": "vdl-mcp",
"args": []
}
}
}
Examples
Each demo in demos/ illustrates a single workflow:
| Demo | Use Case |
|---|---|
download_video.py |
Download a single video |
download_playlist.py |
Download an entire playlist |
resolve_playlist.py |
Inspect playlist items before downloading |
download_link_file.py |
Batch download URLs from a .txt file |
crawl_websites.py |
Discover and download media on a webpage |
All demos use placeholder URLs — copy and edit with your own.
Metadata
Release files for vdl 2.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vdl-2.1.2.tar.gz | 1.6 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vdl-2.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.7 MB
Release files / vdl-2.1.2.tar.gz
| Download URL | vdl-2.1.2.tar.gz |
|---|---|
| Size | 1.6 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cf4e38a8aa4b89b362d473b6cadad69d052a98f66a7212be3cbccb8733b33bb0
|
|
BLAKE2b-256 checksum How to use checksums |
222309c186e875e95f6cc55c80aa2277f253bbde8fcca0a6fb834c0d4ef82e12
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / vdl-2.1.2-py3-none-any.whl
| Download URL | vdl-2.1.2-py3-none-any.whl |
|---|---|
| Size | 150.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
64602b3e0bb03a885daf096121ff9b63fb3aa8af5d6502e6a7a7d8c9f2478855
|
|
BLAKE2b-256 checksum How to use checksums |
1f8626709c9de56058a54055b3a5df6bdac4d8dc00091803fda43c76502e9ae3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|