← All guides AI Release
RU Subscribe to @ai_release1

How to Use yt-dlp on Windows: Install, Commands, and a Simple GUI Wrapper

Updated: 27.10.2026 · AI Release · @ai_release1
How to Use yt-dlp on Windows: Install, Commands, and a Simple GUI Wrapper

TL;DR

Requirements and setup

Illustration: Requirements and setup
Illustration: Requirements and setup

The whole setup is three pieces that must find each other: the downloader (yt-dlp.exe), the muxer (ffmpeg.exe) and the shell (PowerShell). The diagram of what you are building:

[yt-dlp.exe] --needs--> [ffmpeg.exe in PATH] --writes--> [output folder]
      ^                                                        ^
      |                                                        |
[PowerShell] --runs--> [config.txt: default flags] -----> [yt-dlp-gui.ps1]

Placeholders used below: <your-URL> — the real media link, <your-user> — your Windows account name.

Step 1. Install yt-dlp and ffmpeg and put them on PATH

What to do: pick one installation route for yt-dlp, then add ffmpeg.

Route A, pip (needs Python):

python -m pip install -U yt-dlp

Route B, standalone exe (no Python). Create a folder and drop the downloaded yt-dlp.exe into it:

mkdir C:\tools\yt-dlp

Then install ffmpeg: unpack the build so that ffmpeg.exe sits in C:\tools\ffmpeg\bin, and add both folders to PATH — press Win+R, run sysdm.cpl, open *Advanced → Environment Variables → User variables → Path → New*, and add:

C:\tools\yt-dlp
C:\tools\ffmpeg\bin

Close and reopen the terminal — PATH is read at process start.

Why: yt-dlp calls ffmpeg.exe as a separate program, so it must be resolvable by name. The PATH entry is what turns yt-dlp from a file path into a command.

How to check:

yt-dlp --version
ffmpeg -version

Example output: a date-like version string such as the release date for yt-dlp, and a banner starting with ffmpeg version ... followed by the build configuration. Two version strings = success. If PowerShell answers with The term 'yt-dlp' is not recognized..., jump to Troubleshooting, case 1.

Step 2. Learn the core commands and pin your defaults in a config file

Illustration: Step 2. Learn the core commands and pin your defaults in a config file
Illustration: Step 2. Learn the core commands and pin your defaults in a config file

What to do: run the four commands you will use 90% of the time, then move the recurring flags into a config file so you stop retyping them.

FlagWhat it doesExample
`-F`lists every available format`yt-dlp -F "<your-URL>"`
`-f`selects a format`-f "bv*[height<=1080]+ba/b[height<=1080]"`
`-P`sets the output folder`-P "D:\Downloads\yt-dlp"`
`-x`extracts audio only`-x --audio-format mp3`
`--write-subs`saves subtitles`--write-subs --sub-langs "en.*"`
`-o`filename template`-o "%(uploader)s/%(title)s.%(ext)s"`

Example 1 — inspect before downloading:

yt-dlp -F "<your-URL>"

You get a numbered table of formats with columns for ID, extension, resolution and codecs. bv* means "best video", ba means "best audio", and bv*+ba asks for both, which is why ffmpeg is required.

Example 2 — cap quality at 1080p and choose a folder:

yt-dlp -f "bv*[height<=1080]+ba/b[height<=1080]" -P "D:\Downloads\yt-dlp" "<your-URL>"

The part after / is a fallback: if no separate stream pair exists, yt-dlp takes the best single file that already fits the height limit.

Example 3 — audio only:

yt-dlp -x --audio-format mp3 -P "D:\Downloads\yt-dlp\audio" "<your-URL>"

Example 4 — subtitles embedded into the file:

yt-dlp --write-subs --sub-langs "en.*" --embed-subs -P "D:\Downloads\yt-dlp" "<your-URL>"

If you want to summarise those subtitle files with a model afterwards, see How to use AI models for free in 2026.

Now create the config file at %APPDATA%\yt-dlp\config.txt (create the yt-dlp folder if it is missing). One option per line, # starts a comment:

# defaults applied to every yt-dlp run
-P "D:\Downloads\yt-dlp"
-o "%(uploader)s/%(title)s.%(ext)s"
-f bv*[height<=1080]+ba/b[height<=1080]
--write-subs
--sub-langs en.*
--embed-subs

Why: the config is parsed before the command line, and command-line options win. That gives you sane defaults plus per-run overrides — the GUI in Step 3 relies on exactly this.

How to check:

yt-dlp -v --simulate "<your-URL>"

Example: among the debug lines you will see one beginning with [debug] User config followed by the full path to config.txt and the options it supplied. That line proves the file is being read. To see where a file would land:

yt-dlp --simulate --print filename "<your-URL>"

It prints something like D:\Downloads\yt-dlp\<channel name>\<video title>.webm — your folder plus the -o template, resolved.

Step 3. Build and verify the simple GUI shell

Illustration: Step 3. Build and verify the simple GUI shell
Illustration: Step 3. Build and verify the simple GUI shell

What to do: save the script below as C:\tools\yt-dlp\yt-dlp-gui.ps1. It is a thin Windows Forms wrapper — a window with a URL box, a folder box, a Browse button and a log — that calls the same yt-dlp.exe you just tested.

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$form = New-Object System.Windows.Forms.Form
$form.Text = 'yt-dlp shell'
$form.Size = New-Object System.Drawing.Size(600, 360)
$form.StartPosition = 'CenterScreen'

$lblUrl = New-Object System.Windows.Forms.Label
$lblUrl.Text = 'Video URL'; $lblUrl.Location = New-Object System.Drawing.Point(12, 12)
$form.Controls.Add($lblUrl)

$txtUrl = New-Object System.Windows.Forms.TextBox
$txtUrl.Location = New-Object System.Drawing.Point(12, 34)
$txtUrl.Size = New-Object System.Drawing.Size(560, 24)
$form.Controls.Add($txtUrl)

$lblDir = New-Object System.Windows.Forms.Label
$lblDir.Text = 'Save to folder'; $lblDir.Location = New-Object System.Drawing.Point(12, 70)
$form.Controls.Add($lblDir)

$txtDir = New-Object System.Windows.Forms.TextBox
$txtDir.Location = New-Object System.Drawing.Point(12, 92)
$txtDir.Size = New-Object System.Drawing.Size(460, 24)
$txtDir.Text = 'D:\Downloads\yt-dlp'
$form.Controls.Add($txtDir)

$btnBrowse = New-Object System.Windows.Forms.Button
$btnBrowse.Text = 'Browse...'
$btnBrowse.Location = New-Object System.Drawing.Point(480, 90)
$btnBrowse.Size = New-Object System.Drawing.Size(92, 26)
$form.Controls.Add($btnBrowse)

$btnGo = New-Object System.Windows.Forms.Button
$btnGo.Text = 'Download'
$btnGo.Location = New-Object System.Drawing.Point(12, 130)
$btnGo.Size = New-Object System.Drawing.Size(140, 32)
$form.Controls.Add($btnGo)

$txtLog = New-Object System.Windows.Forms.TextBox
$txtLog.Location = New-Object System.Drawing.Point(12, 172)
$txtLog.Size = New-Object System.Drawing.Size(560, 130)
$txtLog.Multiline = $true
$txtLog.ScrollBars = 'Vertical'
$txtLog.ReadOnly = $true
$form.Controls.Add($txtLog)

$btnBrowse.Add_Click({
    $dlg = New-Object System.Windows.Forms.FolderBrowserDialog
    if ($dlg.ShowDialog() -eq 'OK') { $txtDir.Text = $dlg.SelectedPath }
})

$btnGo.Add_Click({
    $txtLog.Clear()
    if ([string]::IsNullOrWhiteSpace($txtUrl.Text)) {
        $txtLog.AppendText("Paste a URL first.`r`n"); return
    }
    $tmpOut = Join-Path $env:TEMP 'ytdlp-out.txt'
    $tmpErr = Join-Path $env:TEMP 'ytdlp-err.txt'
    $ytArgs = @(
        '-P', ('"' + $txtDir.Text + '"'),
        '-f', 'bv*[height<=1080]+ba/b[height<=1080]',
        ('"' + $txtUrl.Text + '"')
    )
    $txtLog.AppendText('yt-dlp ' + ($ytArgs -join ' ') + "`r`n")
    try {
        $p = Start-Process -FilePath 'yt-dlp.exe' -ArgumentList $ytArgs -NoNewWindow `
             -Wait -PassThru -RedirectStandardOutput $tmpOut -RedirectStandardError $tmpErr
    } catch {
        $txtLog.AppendText('Could not start yt-dlp.exe: ' + $_.Exception.Message); return
    }
    $txtLog.AppendText((Get-Content $tmpOut -Raw) + (Get-Content $tmpErr -Raw))
    if ($p.ExitCode -eq 0) { $txtLog.AppendText("`r`nDone. Exit code 0.`r`n") }
    else { $txtLog.AppendText("`r`nFailed. Exit code $($p.ExitCode). Read the log above.`r`n") }
})

$form.ShowDialog() | Out-Null

Run it:

powershell -ExecutionPolicy Bypass -File "C:\tools\yt-dlp\yt-dlp-gui.ps1"

Why it works: the shell adds no new download logic — it builds one argument list and hands it to the same executable. Arguments are quoted by hand because Start-Process -ArgumentList joins an array with spaces and does not quote paths containing spaces. Because the -P you type here is on the command line, it overrides the -P from config.txt.

Example run: paste a link, set the folder to D:\Downloads\yt-dlp\demo, click Download. The log shows the reconstructed command line, then [download] progress lines, then Done. Exit code 0.

How to check:

To find the new files quickly in Explorer, use Windows search or the built-in AI search covered in How to Use AI-Powered Search in Windows 11.

Troubleshooting

Problem. Every command fails immediately with "not recognized"

Problem. High-quality downloads abort right after the format selection

Problem. The site refuses to serve the video

Summary and next steps

You now have yt-dlp and ffmpeg on PATH, a %APPDATA%\yt-dlp\config.txt with your defaults, and a working GUI wrapper that calls the same executable. Next, extend the shell with an audio-only checkbox or a subtitle toggle — each is one more element in the $ytArgs array.

FAQ

Do I need Python to run yt-dlp on Windows?

No. The standalone yt-dlp.exe from the releases page is self-contained; Python is

🤖 AI summary