Playlists¶
VDL can both resolve playlists (list items without downloading) and download all or selected items.
Resolve a Playlist¶
Inspect a playlist without downloading:
CLI¶
vdl resolve "https://example.com/playlist"
Output:
1 | First Video | https://example.com/item1 | 3:45
2 | Second Video | https://example.com/item2 | 5:20
3 | Third Video | https://example.com/item3 | 2:10
...
Get structured JSON output:
vdl resolve "https://example.com/playlist" --json
Python¶
import asyncio
from vdl import VDLClient
async def main():
client = VDLClient()
playlist = await client.resolve_playlist("https://example.com/playlist")
print(f"Title: {playlist.title}")
print(f"Items: {len(playlist.items)}")
for item in playlist.items:
print(f" {item.index} | {item.title} | {item.duration_seconds}s")
asyncio.run(main())
Download a Playlist¶
Download all items from a playlist.
CLI¶
vdl playlist "https://example.com/playlist"
Or use vdl download with --playlist:
vdl download "https://example.com/playlist" --playlist
Python¶
import asyncio
from vdl import VDLClient, Quality
async def main():
client = VDLClient()
results = await client.download_playlist(
"https://example.com/playlist",
quality=Quality.P720,
write_thumbnail=True,
)
for result in results:
print(f"{result.source_url} → {result.status}")
if result.metadata.title:
print(f" Title: {result.metadata.title}")
if result.metadata.sidecars.thumbnail:
print(f" Thumbnail: {result.metadata.sidecars.thumbnail}")
if result.error:
print(f" Error: {result.error}")
asyncio.run(main())
Playlist Selection¶
Download a subset of items rather than the full playlist.
CLI¶
# First 5 items
vdl playlist "URL" --first-items 5
# Specific items by 1-based position (comma-separated)
vdl playlist "URL" --items 1,3,5
# Contiguous range (1-based, inclusive)
vdl playlist "URL" --range 1:10
# 3 most recently published items
vdl playlist "URL" --most-recent 3
The same flags work with vdl download --playlist.
Python¶
# First 5 items
results = await client.download_playlist("URL", first_n=5)
# Specific items by 0-based index
results = await client.download_playlist("URL", items=[0, 2, 4])
# 3 most recently published
results = await client.download_playlist("URL", most_recent_n=3)
At most one selection parameter (first_n, items, most_recent_n) may be
specified per call; combining them raises VDLError.
PlaylistInfo and PlaylistItem¶
PlaylistInfo¶
@dataclass(frozen=True)
class PlaylistInfo:
url: str # Playlist URL
title: str # Playlist title
items: list[PlaylistItem] # All items
metadata: dict[str, Any] # Platform metadata
PlaylistItem¶
@dataclass(frozen=True)
class PlaylistItem:
url: str # Item URL
id: str # Unique item ID
title: str # Item title
index: int # Position in playlist (1-based)
duration_seconds: int | None # Duration (if available)
selected: bool # Selected for download (default True)
metadata: dict[str, Any] # Platform metadata
Concurrency¶
Playlist downloads are concurrent. The max_concurrent_downloads config controls parallelism:
vdl config set max_concurrent_downloads 5
vdl playlist "URL" # Downloads up to 5 items in parallel
This respects bandwidth limits and rate limits of the source.
Collision Handling¶
When downloading a playlist, --if-exists applies to each item:
# Skip items that already exist
vdl playlist "URL" --if-exists skip
# Rename to avoid collision (default)
vdl playlist "URL" --if-exists rename
# Overwrite existing items
vdl playlist "URL" --if-exists overwrite
Python:
from vdl.models import IfExists
results = await client.download_playlist(
"URL",
if_exists=IfExists.SKIP
)
Partial Failure Handling¶
If one item fails, the download continues with remaining items. Each item has its own status:
results = await client.download_playlist("URL")
completed = [r for r in results if r.status == DownloadStatus.COMPLETED]
failed = [r for r in results if r.status == DownloadStatus.FAILED]
skipped = [r for r in results if r.status == DownloadStatus.SKIPPED]
print(f"Completed: {len(completed)}, Failed: {len(failed)}, Skipped: {len(skipped)}")
Limiting Playlist Expansion¶
By default, VDL downloads all items. Use --first-items N (CLI) or first_n=N
(Python) to download only the first N, or use max_playlist_items in config as a
global cap:
vdl config set max_playlist_items 10
vdl playlist "URL" # capped at 10 by config
from vdl import VDLConfig
from pathlib import Path
config = VDLConfig(output_dir=Path("~/Videos"), max_playlist_items=10)
client = VDLClient(config=config)
results = await client.download_playlist("URL")
Quality¶
All items in the playlist are downloaded at the specified quality:
vdl playlist "URL" --quality 720p
vdl playlist "URL" --quality audio
For thumbnails and subtitles, use interactive mode or the Python API sidecar options.
Status Tracking¶
Only available in interactive mode. Playlist downloads are recorded in the state database:
vdl status
Shows all recent downloads, including all items from playlists.
Workflow Diagram¶
sequenceDiagram
User->>VDLClient: download_playlist(url)
VDLClient->>PlaylistService: resolve(url)
PlaylistService->>YtDlpPlaylistResolver: resolve(url)
YtDlpPlaylistResolver-->>PlaylistService: PlaylistInfo
PlaylistService->>PlaylistService: create_requests(items)
loop Each item (up to max_concurrent_downloads)
PlaylistService->>DownloadExecutionService: download(item.url)
DownloadExecutionService->>YtDlpDownloader: download()
YtDlpDownloader-->>DownloadExecutionService: DownloadResult
DownloadExecutionService-->>PlaylistService: result
end
PlaylistService-->>User: list[DownloadResult]
Examples¶
See demos/download_playlist.py and demos/resolve_playlist.py.