Metadata-Version: 2.4
Name: vdl
Version: 1.0.5
Summary: A local-first media workflow toolkit for downloads, playlists, and automation through CLI and MCP interfaces
Project-URL: Homepage, https://gitlab.com/bildcraft/products/video-downloader
Project-URL: Repository, https://gitlab.com/bildcraft/products/video-downloader
Project-URL: Issues, https://gitlab.com/bildcraft/products/video-downloader/-/issues
Author: Temiloluwa
License: MIT
Keywords: cli,downloader,mcp,video,yt-dlp
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Utilities
Requires-Python: >=3.12
Requires-Dist: beautifulsoup4>=4.12.0
Requires-Dist: brotli>=1.2.0
Requires-Dist: curl-cffi<0.15,>=0.10
Requires-Dist: prompt-toolkit>=3.0.43
Requires-Dist: pyyaml>=6.0.1
Requires-Dist: requests>=2.31.0
Requires-Dist: rich>=13.7.0
Requires-Dist: selenium>=4.16.0
Requires-Dist: yt-dlp-ejs>=0.8.0
Requires-Dist: yt-dlp>=2026.03.17
Provides-Extra: mcp
Requires-Dist: mcp>=1.0.0; extra == 'mcp'
Description-Content-Type: text/markdown

# VDL

![VDL logo](docs/assets/vdl-logo.svg)

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.

```mermaid
flowchart TD
  StandaloneBinary["curl install.sh<br/>Standalone binary"]
  PythonPkg["pip install vdl<br/>Python package"]
  
  StandaloneBinary --> CLI["vdl CLI"]
  StandaloneBinary --> App["Interactive app"]
  StandaloneBinary --> MCP1["vdl-mcp"]
  
  PythonPkg --> Library["Python library<br/>import vdl"]
  PythonPkg --> CLI
  PythonPkg --> App
  PythonPkg --> Tools["vdl.tools"]
  PythonPkg --> MCP2["MCP server<br/>pip install vdl[mcp]"]
  
  style StandaloneBinary fill:#0277bd,color:#fff
  style PythonPkg fill:#7b1fa2,color:#fff
  style MCP2 fill:#e65100,color:#fff
```

## Install `vdl` Binary

Use the standalone installer when you want the commands without setting up a
Python project:

```bash
curl -fsSL https://bildcraft.gitlab.io/products/video-downloader/vdl/install.sh | sh
```

This gives you:
- `vdl`
- `vdl-mcp`

### Interactive Download App

Start the interactive app with:

```bash
vdl
```

It opens an interactive terminal interface:

![VDL Interactive App](docs/assets/vdl-app.png)

The app guides you through:
1. **Input** — paste a URL, local file path, or `.txt` link list
2. **Quality** — choose preferred resolution (best, 2160p, 1440p, 1080p, 720p, 480p, or audio)
3. **Output** — set download directory (default: `~/Downloads/VDL`)
4. **Options** — save thumbnails and subtitles
5. **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:

```bash
vdl --version
```

Run direct commands:

```bash
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 3` means first 3 successful downloads, not first 3 discovered links
- crawl applies its own admission policy, including a default 7 MB minimum file size
- `--allow-tiny` disables that crawl-only minimum
- `--crawl-pagination` follows 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:

```bash
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:

```bash
pip install vdl
```

That gives you:
- the `vdl` Python library
- the `vdl` CLI
- the interactive app entrypoint
- `vdl.tools`

Use the library directly:

```python
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:

```python
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`:

```bash
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:

```json
{
  "mcpServers": {
    "vdl": {
      "command": "vdl-mcp",
      "args": []
    }
  }
}
```

## Examples

Each demo in [`demos/`](demos/) illustrates a single workflow:

| Demo | Use Case |
|------|----------|
| [`download_video.py`](demos/download_video.py) | Download a single video |
| [`download_playlist.py`](demos/download_playlist.py) | Download an entire playlist |
| [`resolve_playlist.py`](demos/resolve_playlist.py) | Inspect playlist items before downloading |
| [`download_link_file.py`](demos/download_link_file.py) | Batch download URLs from a `.txt` file |
| [`crawl_websites.py`](demos/crawl_websites.py) | Discover and download media on a webpage |

All demos use placeholder URLs — copy and edit with your own.
