Skip to main content

A local-first media workflow toolkit for downloads, playlists, and automation through CLI and MCP interfaces

Project description

VDL — Local Media Workflow Toolkit

VDL logo

VDL is a local-first media workflow toolkit for downloading, organizing, and automating video and playlist tasks through CLI, Python library, and MCP interfaces.

It is designed for reliable local workflows: single downloads, playlists, batch inputs, page discovery, idempotent execution, structured status tracking, and AI-assisted automation.

VDL uses proven media extraction tools under the hood while adding workflow-level orchestration, state, automation, and developer-facing interfaces.


Highlights

  • Local-first: downloads, state, logs, and configuration stay on your machine.
  • Multiple interfaces: interactive CLI, headless CLI, Python API, and MCP server.
  • Playlist workflows: resolve, inspect, select, filter, and download playlist items.
  • Intent-aware interactive mode: detect single videos, native playlists, playlist-member URLs, or crawlable pages before downloading.
  • Batch inputs: handle URLs, playlists, link files, and page-discovered media.
  • Idempotent execution: avoid accidental duplicate downloads and track status locally.
  • Automation-ready: expose media tasks to scripts, apps, and MCP-compatible AI tools.

Installation

Standalone CLI

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

Python package

pip install vdl

For MCP support:

pip install "vdl[mcp]"

With uv:

uv tool install vdl
uv tool install "vdl[mcp]"

Quick Start

Download a video:

vdl download <url>

Download a playlist:

vdl playlist <playlist-url>

Resolve a playlist without downloading:

vdl resolve <playlist-url>

Start the interactive CLI:

vdl

Check environment setup:

vdl doctor

Show recent download status:

vdl status

Interfaces

VDL exposes the same workflow capabilities through several interfaces.

flowchart TD
  Core["VDL Core / Library"]
  Interactive["Interactive CLI"]
  Headless["Headless CLI"]
  Python["Python Library API"]
  MCP["MCP Server"]

  Interactive --> Core
  Headless --> Core
  Python --> Core
  MCP --> Core

  Core --> Resolve["Resolve URLs, pages, and playlists"]
  Core --> Select["Selection and playlist policies"]
  Core --> Download["Download execution"]
  Core --> State["Local state, logs, and reconciliation"]
Interface Use case
Interactive CLI Guided local workflows with prompts and sensible defaults.
Headless CLI Scriptable commands for automation and repeatable jobs.
Python API Embed VDL workflows into apps, services, or notebooks.
MCP server Let MCP-compatible AI clients call local media workflow tools.

Workflow Model

flowchart LR
  Input["URLs / playlists / pages / files"] --> Resolve["Resolve intent"]
  Resolve --> Select["Select video(s)"]
  Select --> Download["Download"]
  Download --> Organize["Organize locally"]
  Organize --> Automate["CLI / Library / MCP automation"]

VDL turns mixed inputs into explicit download plans, applies selection and deduplication policies, executes downloads, and records state for later inspection. In interactive mode it can use yt-dlp metadata to distinguish native playlists from single videos, and it can crawl listing pages using site-specific rules from websites.yaml when the user chooses discovery.


Python API

import asyncio

from vdl import VDLClient

async def main() -> None:
    client = VDLClient()
    result = await client.download(
        "https://example.com/video",
        quality="1080p",
    )

    print(result.status)
    print(result.output_path)

asyncio.run(main())

Playlist resolution:

import asyncio

from vdl import VDLClient

async def main() -> None:
    client = VDLClient()
    playlist = await client.resolve_playlist("https://example.com/playlist")

    for item in playlist.items:
        print(item.title, item.url)

asyncio.run(main())

Tool API

Use vdl.tools when another Python project, agent runtime, or function-calling LLM needs a stable tool-shaped surface without MCP. These functions accept plain inputs and return JSON-serializable outputs.

Async example:

import asyncio

from vdl.tools import plan_download

async def main() -> None:
    plan = await plan_download("https://example.com/video", quality="best")
    print(plan["source_kind"])
    print(plan["items"][0]["url"])

asyncio.run(main())

Sync example:

from vdl.tools import doctor_sync

report = doctor_sync()
print(report["version"])
print(report["ffmpeg"])

MCP is not required for this interface. Install vdl normally and import from vdl.tools.


MCP Server

Install the MCP extra:

pip install "vdl[mcp]"

Add the server to an MCP-compatible client:

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

The MCP server exposes local media workflow tools such as:

  • download
  • download_playlist
  • resolve_playlist
  • get_status
  • doctor

The standalone binary release includes both vdl and vdl-mcp. Python package users should install vdl[mcp] when MCP support is needed.


Configuration

VDL stores configuration at:

~/.config/vdl/config.toml

Default local paths follow the XDG base directory layout:

Purpose Default path
Config ~/.config/vdl/config.toml
State database ~/.local/state/vdl/vdl.db
Logs ~/.local/state/vdl/logs/
Cache ~/.cache/vdl/
Downloads ~/Downloads/VDL/

Example configuration:

output_dir = "~/Downloads/VDL"
default_quality = "best"
max_concurrent_downloads = 3
embed_metadata = true
embed_thumbnail = true
expand_nested_playlists = false

Commands

Command Description
vdl Start interactive mode.
vdl download <url> Download a video, playlist, or link input.
vdl playlist <url> Download playlist items.
vdl resolve <url> Resolve playlist/page items without downloading.
vdl status Show recent download status.
vdl doctor Check local dependencies and configuration.
vdl config View or update configuration.

Architecture

VDL keeps business logic in the shared core/library layer. Interfaces are adapters over that core:

VDL Core / Library
  ├── Interactive CLI
  ├── Headless CLI
  ├── Python API
  └── MCP server

This keeps the CLI, MCP server, and library interface independent while sharing the same resolution, download, state, and configuration logic.

Key modules:

Module Responsibility
client.py Programmatic entry point and workflow orchestration.
cli.py CLI command parsing and headless command execution.
interactive.py Guided interactive mode.
mcp_server.py MCP server adapter.
acquisition.py Convert inputs into download plans.
playlist.py Playlist resolution, inspection, and item selection.
downloader.py Download execution.
state.py SQLite-backed local state.
config.py Configuration loading and persistence.
crawler.py / site_rules.py Page discovery and websites.yaml-driven crawl filtering.

System Requirements

VDL expects the following tools for full media processing support:

  • Python 3.13+
  • ffmpeg
  • ffprobe

VDL uses yt-dlp under the hood for media extraction and download support.


Development

uv sync --frozen --all-extras --dev
PYTHONPATH=src uv run pytest -q
uv build --no-sources

Useful tasks:

task --list
task test
task build:pypi
task release
task release:validate

Release

Release automation is handled through GitLab CI.

  • PyPI package: built and published in CI through Trusted Publishing.
  • macOS arm64 binary: built locally with scripts/build-macos.sh and uploaded to a GitLab Release.
  • Linux/Windows binaries: optional CI builds behind release variables.
  • Installer manifest: published by the manual vdl-publish-pages job after release assets exist.

For the current happy path, run task release on an Apple Silicon Mac from a clean branch. It wraps the local macOS build, pushes the release tag, uploads the macOS artifact to GitLab Release, waits for the tag pipeline, and triggers the manual Pages publish job. The release script auto-loads .env when present, so GITLAB_TOKEN there is enough for glab API calls.

See docs/RELEASE_PLAYBOOK.md for the full release process.


Related Projects


License

MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vdl-1.0.2.tar.gz (260.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vdl-1.0.2-py3-none-any.whl (82.0 kB view details)

Uploaded Python 3

File details

Details for the file vdl-1.0.2.tar.gz.

File metadata

  • Download URL: vdl-1.0.2.tar.gz
  • Upload date:
  • Size: 260.9 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for vdl-1.0.2.tar.gz
Algorithm Hash digest
SHA256 f0cc643f476aefc561fcc96f7d5a8f272bf4a23522233d90fae06844f76026ba
MD5 da7e3d611356efc8cc4d06c230359d3b
BLAKE2b-256 2f2ddcb2549115f126ba230f5ce93a8cd454cf2a912ae74d9f89110c7014bfb0

See more details on using hashes here.

File details

Details for the file vdl-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: vdl-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 82.0 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for vdl-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c649944f9c02953075a944afe4afa2bc3dc626363d4fcf5bcac67a9df247de4e
MD5 b3322533ccd510b4d5c2f97f29903466
BLAKE2b-256 e02b198e21ad138095344d70b381406c39eaee03299952f55019654f693f6f73

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page