Skip to content
Manojbabu edited this page Jun 19, 2026 · 13 revisions

Ytdlp.NET

Static Badge NuGet Version NuGet Downloads

Ytdlp.NET is an open-source, modern .NET wrapper library for yt-dlp, the popular command-line audio/video downloader and metadata extractor.
It provides a fluent, strongly-typed API to build yt-dlp commands, fetch rich metadata, list formats, monitor progress, and execute downloads with full event support.

Ytdlp.NET
Developer Manojbabu (manusoft/manuhub)
Initial release 2025
Stable release 4.0.0
Repository github.com/manusoft/Ytdlp.NET
Written in C# (.NET 8.0 or later)
License MIT
Website https://github.com/manusoft/Ytdlp.NET

✨ Features

  • Fluent API: Build yt-dlp commands with WithXxx() methods.
  • Immutable & thread-safe: Each method returns a new instance, safe for parallel usage.
  • Progress & Events: Real-time progress tracking and post-processing notifications.
  • Format Listing: Retrieve and parse available formats.
  • Batch Downloads: Sequential or parallel execution.
  • Output Templates: Flexible naming with yt-dlp placeholders.
  • Custom Command Injection: Add extra yt-dlp options safely.
  • Cross-platform: Windows, macOS, Linux.

🚀 New in v4.0.0

  • Lifecycle Refinement: No disposal required. The library no longer implements IDisposable or IAsyncDisposable. Instances are plain configuration objects.
  • Advanced Execution: New ExecuteRawAsync() for power users who need to execute custom commands that bypass the fluent builder.
  • Deep Metadata Support: Added GetDeepMetadataAsync() and GetDeepMetadataRawAsync() for full hierarchical structure (playlists → seasons → episodes).
  • Traverse Helper: New Traverse() method for easy iteration over nested playlist entries.
  • Improved Auth: Enhanced WithAdobePassAuthentication() and WithAuthentication() handling.
  • Subtitle Extraction: New GetSubtitlesAsync() for streamlined subtitle retrieval.
  • MSO Listing: New GetAdobePassListAsync() for Adobe Pass mso listing.
  • Robust Core: Modernized ProcessRunner and ProcessFactory for efficient, isolated execution

🔧 Required Tools

  • Namespace: ManuHub.Ytdlp.NET
  • External JS runtime: yt-dlp requires an external JS runtime like deno.exe (from denoland/deno) for YouTube downloads with JS challenges.
tools/
├─ yt-dlp.exe
├─ ffmpeg.exe
├─ ffprobe.exe
└─ deno.exe
  • Recommended: Use companion NuGet packages:
Package Description
ManuHub.Ytdlp Core download engine
ManuHub.Deno JavaScript challenge resolution
ManuHub.FFmpeg Post-processing, merging, and conversion
ManuHub.FFprobe Format probing and metadata extraction

Example path resolution in .NET:

var ytdlpPath = Path.Combine(AppContext.BaseDirectory, "tools", "yt-dlp.exe");
var ffmpegPath = Path.Combine(AppContext.BaseDirectory, "tools");

🧬 Core Concepts

No Disposal Required

Ytdlp holds no unmanaged resources. Create instances, share them, and let the GC collect them. All internal runners are created per-call.

Immutable Fluent API

Every configuration method (e.g., WithOutputFolder, WithFormat) returns a new instance, ensuring the original is never modified. This makes branching configurations safe and clean.

Thread Safety

A single Ytdlp instance can be shared across threads. Each execution creates isolated internal runners, allowing concurrent downloads without synchronization.

Secure Authentication

Implemented secure authentication handling for various scenarios, including standard username/password and Adobe Pass authentication.

  • .WithAuthentication(string username, string password)
  • .WithAdobePassAuthentication(string mso, string username, string password)

It securely handles credentials by passing them via standard input to the yt-dlp process, avoiding exposure in command-line arguments or logs. The library ensures that sensitive information is not stored in memory longer than necessary and is properly disposed of after use.


🚀 Quick Start

1. Basic Download

var ytdlp = new Ytdlp("yt-dlp.exe")
    .WithOutputFolder("./downloads")
    .WithBestVideoPlusBestAudio()
    .WithEmbedMetadata();

// Subscribe to events
ytdlp.ProgressDownload += (s, e) => Console.WriteLine($"Progress: {e.Percent:F2}%");
ytdlp.DownloadCompleted += (s, msg) => Console.WriteLine($"Finished: {msg}");

// Execute
await ytdlp.DownloadAsync("https://www.youtube.com/watch?v=XXX");

2. Immutable Configuration Branching

// Define a shared base configuration
var baseConfig = new Ytdlp("yt-dlp.exe").WithOutputFolder("./media");

// Create specialized versions
var audioOnly = baseConfig.WithBestAudioOnly();
var highRes = baseConfig.WithMaxHeightOrBest(1080);

// baseConfig, audioOnly, and highRes are independent, thread-safe instances
await Task.WhenAll(
    audioOnly.DownloadAsync(url1),
    highRes.DownloadAsync(url2)
);

⚡ Advanced Execution & Control

For power users who need to execute custom commands that bypass the fluent builder, use ExecuteRawAsync. This acts as an "escape hatch" for scenarios where specific, non-standard, or experimental yt-dlp flags are required.

How it works

This method automatically intelligently switches output handling based on how you use it:

  • Streaming Mode: Provide an Action<string> to onLineReceived to stream output in real-time (ideal for progress monitoring or logs).
  • Capture Mode: Pass null to onLineReceived to capture the entire process output into result.FullOutput (ideal for JSON metadata probing or one-off commands).
var ytdlp = new Ytdlp("yt-dlp.exe");

// 1. Capture Mode (Probe/Manual)
var result = await ytdlp.ExecuteRawAsync("--version");
Console.WriteLine($"yt-dlp version: {result.FullOutput}");

// 2. Streaming Mode (Real-time tracking)
await ytdlp.ExecuteRawAsync("--help", onLineReceived: line => Console.WriteLine(line));

Note: While ExecuteRawAsync handles security and formatting, logical validation (e.g., passing valid yt-dlp flags) remains the responsibility of the developer. Always prefer the fluent WithXxx() methods for standard download tasks.


📦 Usage Examples

Fetch Metadata

var ytdlp = new Ytdlp("tools\\yt-dlp.exe");

var metadata = await ytdlp.GetMetadataAsync("https://www.youtube.com/watch?v=abc123");

Console.WriteLine($"Title: {metadata?.Title}, Duration: {metadata?.Duration}");

Deep Metadata Extraction

var metadata = await ytdlp.GetDeepMetadataAsync(url);

foreach (var root in metadata.Entries ?? [])
{
    foreach (var item in root.Traverse())
    {
        Console.WriteLine(item.Title);
    }
}

Parallel Execution

var ytdlp = new Ytdlp("tools\\yt-dlp.exe")
    .WithFormat("best")
    .WithOutputFolder("./batch");

var urls = new[] { "https://youtu.be/vid1", "https://youtu.be/vid2" };

// Safe: Concurrent usage of the same instance
await ytdlp.DownloadBatchAsync(urls, maxConcurrency: 3);

Extract Audio

var ytdlp = new Ytdlp("tools\\yt-dlp.exe")
    .WithExtractAudio(AudioFormat.Mp3, 5)
    .WithOutputFolder("./audio")
    .WithEmbedThumbnail()
    .WithEmbedMetadata();

await ytdlp.DownloadAsync("https://www.youtube.com/watch?v=RGg-Qx1rL9U");

Download a Playlist

var ytdlp = new Ytdlp("tools\\yt-dlp.exe")
    .WithFormat("best")
    .WithOutputFolder("./playlists")
    .WithPlaylistStart(1)
    .WithPlaylistEnd(5)
    .OutputTemplate("%(playlist)s/%(playlist_index)s - %(title)s.%(ext)s");

await ytdlp.DownloadAsync("https://www.youtube.com/playlist?list=PL12345");

Fetch Formats

var ytdlp = new Ytdlp("tools\\yt-dlp.exe");

var formats = await ytdlp.GetFormatsAsync("https://www.youtube.com/watch?v=abc123");

foreach(var format in formats)
    Console.WriteLine($"Id: {metadata?.Id}, Extension: {metadata?.Extension}");

Best Format Selection

var ytdlp = new Ytdlp("tools\\yt-dlp.exe");

string bestAudio = await ytdlp.GetBestAudioFormatIdAsync(url);
string bestVideo = await ytdlp.GetBestVideoFormatIdAsync(url, maxHeight: 720);

await ytdlp
    .WithFormat($"{bestVideo}+{bestAudio}/best")
    .WithOutputFolder("./downloads")
    .DownloadAsync(url);

Get Subtitles

var ytdlp = new Ytdlp("tools\\yt-dlp.exe");
var subtitles = await ytdlp.GetSubtitlesAsync("https://www.youtube.com/watch?v=abc123");
foreach (var sub in subtitles)
{
    Console.WriteLine($"Language: {sub.Language}, Format: {sub.Format}, Url: {sub.Url}");
}

Get Adobe Pass MSO List

var msoList = await ytdlp.GetAdobePassListAsync();

📡 Events

Event Description
ProgressDownload Download progress
ProgressMessage Informational messages
DownloadCompleted File finished
PostProcessingStarted Post‑processing start
PostProcessingCompleted Post‑processing finished
OutputMessage Raw output line
ErrorMessage Error message
CommandCompleted Process finished

Example

// Progress events
ytdlp.ProgressDownload += (s, e) => Console.WriteLine($"{e.Percent:F1}%  {e.Speed}  ETA {e.ETA}");
ytdlp.ProgressMessage += (s, msg) => Console.WriteLine(msg);

// Output events
ytdlp.ErrorMessage += (s, err) => Console.WriteLine($"Error: {err}");
ytdlp.OutputMessage += (s, msg) => Console.WriteLine(msg);

// Lifecycle events
ytdlp.DownloadCompleted += (s, msg) => Console.WriteLine($"Finished: {msg}");
ytdlp.CommandCompleted += (s, e) => Console.WriteLine($"Command finished: {e.Command}");

// Post-Processing events
ytdlp.PostProcessingStarted += (s, msg) => Console.WriteLine($"Post-processing-start: {msg}");
ytdlp.PostProcessingCompleted += (s, msg) => Console.WriteLine($"Post-processing-complete: {msg}");

🛠 Methods

Probe

  • VersionAsync()
  • UpdateAsync(UpdateChannel channel, string specificVersion)
  • GetExtractorsAsync()
  • GetAdobePassListAsync()
  • GetSubtitlesAsync(string url)
  • GetMetadataAsync(string url)
  • GetMetadataRawAsync(string url)
  • GetDeepMetadataAsync(string url)
  • GetDeepMetadataRawAsync(string url)
  • GetFormatsAsync(string url)
  • GetMetadataLiteAsync(string url)
  • GetMetadataLiteAsync(string url, IEnumerable<string> fields)
  • GetBestAudioFormatIdAsync(string url)
  • GetBestVideoFormatIdAsync(string url, int maxHeight)

Download

  • DownloadAsync(string url)
  • DownloadBatchAsync(IEnumerable<string> urls, int maxConcurrency)

Advanced Execution

  • ExecuteRawAsync(string arguments, Action<string>? onLineReceived = null, CancellationToken ct = default, bool tuneProcess = true)

🛠 Fluent Methods

General Options

  • .WithIgnoreErrors()
  • .WithAbortOnError()
  • .WithIgnoreConfig()
  • .WithConfigLocations(string path)
  • .WithPluginDirs(string path)
  • .WithNoPluginDirs(string path)
  • .WithJsRuntime(Runtime runtime, string runtimePath)
  • .WithNoJsRuntime()
  • .WithFlatPlaylist()
  • .WithLiveFromStart()
  • .WithWaitForVideo(TimeSpan? maxWait = null)
  • .WithMarkWatched()

Network Options

  • .WithProxy(string? proxy)
  • .WithSocketTimeout(TimeSpan timeout)
  • .WithForceIpv4()
  • .WithForceIpv6()
  • .WithEnableFileUrls()

Geo-restriction Options

  • .WithGeoVerificationProxy(string url)
  • .WithGeoBypassCountry(string countryCode)

Video Selection

  • .WithPlaylistItems(string items)
  • .WithMinFileSize(string size)
  • .WithMaxFileSize(string size)
  • .WithDate(string date)
  • .WithDateBefore(string date)
  • .WithDateAfter(string date)
  • .WithMatchFilter(string filterExpression)
  • .WithNoPlaylist()
  • .WithYesPlaylist()
  • .WithAgeLimit(int years)
  • .WithDownloadArchive(string archivePath = "archive.txt")
  • .WithMaxDownloads(int count)
  • .WithBreakOnExisting()

Download Options

  • .WithConcurrentFragments(int count = 8)
  • .WithLimitRate(string rate)
  • .WithThrottledRate(string rate)
  • .WithRetries(int maxRetries)
  • .WithFileAccessRetries(int maxRetries)
  • .WithFragmentRetries(int retries)
  • .WithSkipUnavailableFragments()
  • .WithAbortOnUnavailableFragments()
  • .WithKeepFragments()
  • .WithBufferSize(string size)
  • .WithNoResizeBuffer()
  • .WithPlaylistRandom()
  • .WithHlsUseMpegts()
  • .WithNoHlsUseMpegts()
  • .WithDownloadSections(string regex)

Filesystem Options

  • .WithHomeFolder(string path)
  • .WithTempFolder(string path)
  • .WithOutputFolder(string path)
  • .WithFFmpegLocation(string path)
  • .WithOutputTemplate(string template)
  • .WithRestrictFilenames()
  • .WithWindowsFilenames()
  • .WithTrimFilenames(int length)
  • .WithNoOverwrites()
  • .WithForceOverwrites()
  • .WithNoContinue()
  • .WithNoPart()
  • .WithMtime()
  • .WithWriteDescription()
  • .WithWriteInfoJson()
  • .WithNoWritePlaylistMetafiles()
  • .WithNoCleanInfoJson()
  • .WriteComments()
  • .WithNoWriteComments()
  • .WithLoadInfoJson(string path)
  • .WithCookiesFile(string path)
  • .WithCookiesFromBrowser(string browser)
  • .WithNoCacheDir()
  • .WithRemoveCacheDir()

Thumbnail Options

  • .WithThumbnails(bool allSizes = false)

Verbosity and Simulation Options

  • .WithQuiet()
  • .WithNoWarnings()
  • .WithSimulate()
  • .WithNoSimulate()
  • .WithSkipDownload()
  • .WithVerbose()

Workgrounds

  • .WithAddHeader(string header, string value)
  • .WithSleepInterval(double seconds, double? maxSeconds = null)
  • .WithSleepSubtitles(double seconds)

Video Format Options

  • .WithFormat(string format)
  • .WithMergeOutputFormat(string format)

Subtitle Options

  • .WithSubtitles(string languages = "all", bool auto = false)

Authentication Options

  • .WithAuthentication(string username, string password)
  • .WithTwoFactor(string code)
  • .WithVideoPassword(string password)
  • .WithAdobePassAuthentication(string mso, string username, string password)

Post-Processing Options

  • .WithExtractAudio(string format, int quality = 5)
  • .WithRemuxVideo(string format) usage 'mp4' or 'mp4>mkv'
  • .WithRecodeVideo(string format, string? videoCodec = null, string? audioCodec = null)
  • .WithPostprocessorArgs(PostProcessors postprocessor, string args)
  • .WithKeepVideo()
  • .WithNoPostOverwrites()
  • .WithEmbedSubtitles()
  • .WithEmbedThumbnail()
  • .WithEmbedMetadata()
  • .WithEmbedChapters()
  • .WithEmbedInfoJson()
  • .WithNoEmbedInfoJson()
  • .WithReplaceInMetadata(string field, string regex, string replacement)
  • .WithConcatPlaylist(string policy = "always")
  • .WithFFmpegLocation(string? ffmpegPath)
  • .WithConvertSubtitles(string format = "none")
  • .WithConvertThumbnails(string format = "jpg")
  • .WithSplitChapters() => AddFlag("--split-chapters")
  • .WithRemoveChapters(string regex)
  • .WithForceKeyframesAtCuts()
  • .WithUsePostProcessor(PostProcessors postProcessor, string? postProcessorArgs = null)

SponsorBlock Options

  • .WithSponsorblockMark(string categories = "all")
  • .WithSponsorblockRemove(string categories = "all")
  • .WithNoSponsorblock()

Advanced Options

  • .AddFlag(string flag)
  • .AddOption(string key, string value)

Downloaders

  • .WithExternalDownloader(string downloaderName, string? downloaderArgs = null)
  • .WithAria2(int connections = 16)
  • .WithHlsNative()
  • .WithFfmpegAsLiveDownloader(string? extraFfmpegArgs = null)

⚙️ Customization

If you need specific arguments not covered by the fluent API:

ytdlp.AddFlag("--no-check-certificate")
     .AddOption("--external-downloader", "aria2c")
     .DownloadAsync(url);

Dependencies

  • .NET 8.0 or later
  • yt-dlp executable (required)
  • FFmpeg / FFprobe (recommended for merging, audio extraction, thumbnails)

See also

References

  1. GitHub repository: https://github.com/manusoft/Ytdlp.NET
  2. yt-dlp releases: https://github.com/yt-dlp/yt-dlp/releases
  3. FFmpeg builds: https://www.gyan.dev/ffmpeg/builds/

This page was last edited on June 20, 2026.