How ReClip Handles Playlist Downloads: A Complete Technical Guide

ReClip processes YouTube playlists by extracting individual video URLs server-side and downloading each video separately, never as a single batch operation.

This article explains how the averygan/reclip open-source tool implements playlist downloads. Understanding this workflow helps developers integrate ReClip's API or adapt its architecture for similar media downloader applications.

The Two-Stage Playlist Download Architecture

ReClip splits playlist handling into distinct enumeration and download phases. This design prevents yt-dlp from treating the playlist as a single entity, which gives the frontend granular control over each item.

Stage 1: URL Extraction via /api/playlist

When a client submits a playlist URL, the Flask server in app.py invokes yt-dlp with specific flags designed for metadata-only extraction:


# https://github.com/averygan/reclip/blob/main/app.py#L43-L63

@app.route("/api/playlist", methods=["POST"])
def get_playlist_info():
    data = request.json
    url = data.get("url", "").strip()
    ...
    cmd = ["yt-dlp", "--flat-playlist", "-J", url]
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
    info = json.loads(result.stdout)
    entries = info.get("entries", [])
    urls = [entry.get("url") for entry in entries if entry.get("url")]
    return jsonify({"urls": urls})

The --flat-playlist flag tells yt-dlp to return only basic entry information without fully extracting metadata for each video. The -J flag outputs machine-readable JSON. This combination completes quickly even for large playlists, returning a lightweight array of video URLs.

The response follows this structure:

{
  "urls": [
    "https://www.youtube.com/watch?v=abc123",
    "https://www.youtube.com/watch?v=def456"
  ]
}

Stage 2: Individual Video Downloads via /api/download

The client iterates through the returned URLs and initiates separate download requests. Each request spawns a background thread via the start_download function:


# https://github.com/averygan/reclip/blob/main/app.py#L66-L84

@app.route("/api/download", methods=["POST"])
def start_download():
    data = request.json
    url = data.get("url", "").strip()
    format_choice = data.get("format", "video")
    format_id = data.get("format_id")
    ...
    thread = threading.Thread(
        target=run_download,
        args=(job_id, url, format_choice, format_id)
    )
    thread.start()
    return jsonify({"job_id": job_id})

The actual download worker enforces single-video behavior through a critical flag:


# https://github.com/averygan/reclip/blob/main/app.py#L32-L38

def run_download(job_id, url, format_choice, format_id):
    ...
    cmd = ["yt-dlp", "--no-playlist", "-o", out_template]
    ...

The --no-playlist flag guarantees that even if a playlist URL somehow reaches this stage, yt-dlp downloads only the specific video referenced.

Client-Side Implementation

The frontend in templates/index.html orchestrates the two-stage workflow. First, it retrieves the playlist members:

// fetch playlist URLs
async function fetchPlaylist(url) {
  const res = await fetch('/api/playlist', {
    method: 'POST',
    headers: { 'Content-Type':'application/json' },
    body: JSON.stringify({ url })
  });
  const data = await res.json();
  return data.urls;          // array of video URLs
}

Then it processes each URL sequentially or in parallel:

async function downloadPlaylist(url) {
  const videoUrls = await fetchPlaylist(url);
  for (const videoUrl of videoUrls) {
    const start = await fetch('/api/download', {
      method: 'POST',
      headers: { 'Content-Type':'application/json' },
      body: JSON.stringify({ url: videoUrl, format: 'video' })
    });
    const { job_id } = await start.json();
    // poll /api/status/:job_id and retrieve via /api/file/:job_id
  }
}

This separation enables per-video progress tracking, individual error handling, and custom filename generation for each download.

Key Design Benefits

  • Resilience: A single failed video does not abort the entire playlist
  • Progress visibility: The UI can display status for each item independently
  • Flexible formats: Each video can use different format selections
  • Clean filenames: The -o output template in run_download ensures safe, predictable naming without playlist-induced path collisions

Source Code Reference

File Responsibility
[app.py](https://github.com/averygan/reclip/blob/main/app.py) Flask routes for /api/playlist, /api/download, and download workers
[templates/index.html](https://github.com/averygan/reclip/blob/main/templates/index.html) Frontend JavaScript consuming both endpoints
Dockerfile yt-dlp installation and container configuration

Summary

  • ReClip handles playlist downloads through URL enumeration followed by individual video processing
  • The /api/playlist endpoint uses yt-dlp --flat-playlist -J for fast metadata extraction
  • The /api/download endpoint always passes --no-playlist to prevent accidental batch downloads
  • Each video receives its own job ID for independent tracking and retrieval
  • This architecture prioritizes reliability and user control over download speed

Frequently Asked Questions

Can ReClip download an entire playlist as a single zip file?

No. The current implementation in app.py explicitly prevents this. The --no-playlist flag in run_download forces single-video processing, and the API returns separate job IDs for each item. Users download files individually through /api/file/:job_id.

What happens if a video in the playlist is unavailable?

The /api/playlist endpoint only extracts URLs, so unavailable videos still appear in the list. When the client requests /api/download for that specific URL, the run_download worker will fail and update the job status accordingly. Other playlist items continue processing normally.

Is there a limit to playlist size?

The enumeration timeout is 60 seconds as configured in subprocess.run. Extremely large playlists may hit this limit during the /api/playlist call. There is no explicit count limit, but practical constraints depend on yt-dlp performance and server resources.

Can I modify ReClip to download playlists without the two-stage approach?

You would need to remove --no-playlist from run_download in app.py and handle yt-dlp's native playlist output directly. However, this would break the per-video progress tracking and error isolation that ReClip's architecture provides.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →