Ytdlp.NET is a fluent, strongly-typed .NET wrapper around
yt-dlp. It provides a fully async, event-driven interface for downloading videos, extracting audio, retrieving metadata, and post-processing media from YouTube and hundreds of other platforms.
The library exposes eventβdriven progress reporting, metadata probing, and safe command construction while staying very close to the native yt-dlp functionality.
ClipMate - MAUI.NET App - Download
- Fluent API (
WithXxx()methods) - Immutable design (threadβsafe instances)
- Realβtime progress events
- Metadata & format probing
- Batch downloads
- Cancellation support
- Crossβplatform support
- Strongly typed event system
- Async execution
IAsyncDisposablesupport
- New
GetDeepMetadataAsync()method for comprehensive metadata extraction. - New
GetDeepMetadataRawAsync()for raw JSON metadata. - New
Metadata.Flatten()method to simplify nested metadata structures. - Improved progress download events for batch downloads with better tracking.
- Improved
Metadatamodel with more fields and better parsing.
- Improved UpdateAsync with specific version support
- Immutable fluent builder API
IAsyncDisposableimplemented- Threadβsafe usage
- Simplified event handling
- Improved metadata probing
- Better cancellation support
- Safer command building
yt-dlp relies on external tools.
Recommended folder structure:
tools/
ββ yt-dlp.exe
ββ ffmpeg.exe
ββ ffprobe.exe
ββ deno.exe
Example usage:
var ytdlpPath = Path.Combine("tools", "yt-dlp.exe");await using var ytdlp = new Ytdlp("tools\\yt-dlp.exe")
.WithFormat("best")
.WithOutputFolder("./downloads")
.WithOutputTemplate("%(title)s.%(ext)s");
ytdlp.OnProgressDownload += (s, e) =>
Console.WriteLine($"{e.Percent:F1}% {e.Speed} ETA {e.ETA}");
await ytdlp.DownloadAsync("https://www.youtube.com/watch?v=VIDEO_ID");await using var ytdlp = new Ytdlp()
.WithExtractAudio("mp3")
.WithOutputFolder("./audio");
await ytdlp.DownloadAsync(url);ytdlp.OnProgressDownload += (s, e) =>
{
Console.WriteLine($"{e.Percent:F1}% {e.Speed} ETA {e.ETA}");
};
ytdlp.OnCompleteDownload += (s, msg) =>
{
Console.WriteLine($"Finished: {msg}");
};await using var ytdlp = new Ytdlp();
var metadata = await ytdlp.GetMetadataAsync(url);
Console.WriteLine(metadata?.Title);
Console.WriteLine(metadata?.Duration);await using var ytdlp = new Ytdlp();
string bestVideo = await ytdlp.GetBestVideoFormatIdAsync(url, 1080);
string bestAudio = await ytdlp.GetBestAudioFormatIdAsync(url);
await ytdlp
.WithFormat($"{bestVideo}+{bestAudio}/best")
.DownloadAsync(url);var urls = new[]
{
"https://youtu.be/video1",
"https://youtu.be/video2"
};
var tasks = urls.Select(async url =>
{
await using var ytdlp = new Ytdlp()
.WithFormat("best")
.WithOutputFolder("./batch");
await ytdlp.DownloadAsync(url);
});
await Task.WhenAll(tasks);OR
var urls = new[] { "https://youtu.be/vid1", "https://youtu.be/vid2" };
await using var ytdlp = new Ytdlp("tools\\yt-dlp.exe")
.WithFormat("best")
.WithOutputFolder("./batch");
await ytdlp.DownloadBatchAsync(urls, maxConcurrency: 3);| Event | Description |
|---|---|
OnProgressDownload |
Download progress |
OnProgressMessage |
Informational messages |
OnCompleteDownload |
File finished |
OnPostProcessingStart |
Postβprocessing start |
OnPostProcessingComplete |
Postβprocessing finished |
OnOutputMessage |
Raw output line |
OnErrorMessage |
Error message |
OnCommandCompleted |
Process finished |
VersionAsync(CancellationToken ct)UpdateAsync(UpdateChannel channel, string specificVersion, CancellationToken ct)ExtractorsAsync(CancellationToken ct, int bufferKb)GetMetadataAsync(string url, CancellationToken ct, int bufferKb)GetMetadataRawAsync(string url, CancellationToken ct, int bufferKb)GetDeepMetadataAsync(string url, CancellationToken ct = default, bool tuneProcess = true, int bufferKb = 256)GetDeepMetadataRawAsync(string url, CancellationToken ct = default, bool tuneProcess = true, int bufferKb = 256)GetFormatsAsync(string url, CancellationToken ct, int bufferKb)GetMetadataLiteAsync(string url, CancellationToken ct, int bufferKb)GetMetadataLiteAsync(string url, IEnumerable<string> fields, CancellationToken ct, int bufferKb)GetBestAudioFormatIdAsync(string url, CancellationToken ct, int bufferKb)GetBestVideoFormatIdAsync(string url, int maxHeight, CancellationToken ct, int bufferKb)ExecuteAsync(string url, CancellationToken ct)ExecuteBatchAsync(IEnumerable<string> urls, int maxConcurrency, CancellationToken ct)
.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()
.WithProxy(string? proxy).WithSocketTimeout(TimeSpan timeout).WithForceIpv4().WithForceIpv6().WithEnableFileUrls()
.WithGeoVerificationProxy(string url).WithGeoBypassCountry(string countryCode)
.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()
.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)
.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()
.WithThumbnails(bool allSizes = false)
.WithQuiet().WithNoWarnings().WithSimulate().WithNoSimulate().WithSkipDownload().WithVerbose()
.WithAddHeader(string header, string value).WithSleepInterval(double seconds, double? maxSeconds = null).WithSleepSubtitles(double seconds)
.WithFormat(string format).WithMergeOutputFormat(string format)
.WithSubtitles(string languages = "all", bool auto = false)
.WithAuthentication(string username, string password).WithTwoFactor(string code)
.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)
.WithSponsorblockMark(string categories = "all").WithSponsorblockRemove(string categories = "all").WithNoSponsorblock()
.AddFlag(string flag).AddOption(string key, string value)
.WithExternalDownloader(string downloaderName, string? downloaderArgs = null).WithAria2(int connections = 16).WithHlsNative().WithFfmpegAsLiveDownloader(string? extraFfmpegArgs = null)
AND MORE ...
v3 introduces a new immutable fluent API.
Old mutable commands were removed.
var ytdlp = new Ytdlp();
await ytdlp
.SetFormat("best")
.SetOutputFolder("./downloads")
.ExecuteAsync(url);await using var ytdlp = new Ytdlp()
.WithFormat("best")
.WithOutputFolder("./downloads");
await ytdlp.DownloadAsync(url);| v2 | v3 |
|---|---|
SetFormat() |
WithFormat() |
SetOutputFolder() |
WithOutputFolder() |
SetTempFolder() |
WithTempFolder() |
SetOutputTemplate() |
WithOutputTemplate() |
SetFFMpegLocation() |
WithFFmpegLocation() |
ExtractAudio() |
WithExtractAudio() |
UseProxy() |
WithProxy() |
AddCustomCommand() |
AddFlag(string flag) or AddOption(string key, string value) |
AddFlag("--no-check-certificate");
AddOption("--external-downloader", "aria2c");Every WithXxx() call returns a new instance.
var baseYtdlp = new Ytdlp();
var download = baseYtdlp
.WithFormat("best")
.WithOutputFolder("./downloads");Attach events to the configured instance.
var download = baseYtdlp.WithFormat("best");
download.OnProgressDownload += ...Use await using for automatic cleanup.
await using var ytdlp = new Ytdlp();- ClipMate MAUI downloader
- Windows GUI downloader
- Console examples
Contributions are welcome!
Open issues or PRs on GitHub.
MIT License
See:
https://github.com/manusoft/yt-dlp-wrapper/blob/master/LICENSE.txt
Manoj Babu ManuHub
Thanks to all contributors β€οΈ

