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.
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.0.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.0.2.tar.gz | 1.5 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vdl-2.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.7 MB
Release files / vdl-2.0.2.tar.gz
| Download URL | vdl-2.0.2.tar.gz |
|---|---|
| Size | 1.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7116fa8b4f48854968c914cccc295b8fb78aded19b55ffdaa2cf60b3f4f38095
|
|
BLAKE2b-256 checksum How to use checksums |
1cf331b8a5eec9006a67d898c253804d4974c0fd00c29607c95ac8ad6c0942c6
|
| 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.0.2-py3-none-any.whl
| Download URL | vdl-2.0.2-py3-none-any.whl |
|---|---|
| Size | 154.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
76bcd2c75f00bf0741e4851b035a3053000d8f8e918585846fc1c1117843662f
|
|
BLAKE2b-256 checksum How to use checksums |
c83afa7207a92e8371b1faf8822c67bea52fd26f29a5b3b1e1ba25921a28161c
|
| 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}
|