using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using YoutubeDLSharp.Converters; using YoutubeDLSharp.Helpers; using YoutubeDLSharp.Metadata; using YoutubeDLSharp.Options; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Bluegrams")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("© 2020-2026 Bluegrams")] [assembly: AssemblyDescription("\r\n\t\tA simple .NET wrapper library for youtube-dl and yt-dlp.\r\n\r\nNote: Package versions >= 1.0 are optimized to work with yt-dlp.\r\nPackage versions 0.x retain support for the original youtube-dl.\r\n\t")] [assembly: AssemblyFileVersion("1.2.0.265")] [assembly: AssemblyInformationalVersion("1.2.0+bfe03288151c994ad64401870ab4940635576fc5")] [assembly: AssemblyProduct("YoutubeDLSharp")] [assembly: AssemblyTitle("YoutubeDLSharp")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Bluegrams/YoutubeDLSharp")] [assembly: AssemblyVersion("1.2.0.265")] namespace YoutubeDLSharp { public enum DownloadState { None, PreProcessing, Downloading, PostProcessing, Error, Success } public class DownloadProgress { public DownloadState State { get; } public float Progress { get; } public string TotalDownloadSize { get; } public string DownloadSpeed { get; } public string ETA { get; } public int VideoIndex { get; } public string Data { get; } public DownloadProgress(DownloadState status, float progress = 0f, string totalDownloadSize = null, string downloadSpeed = null, string eta = null, int index = 1, string data = null) { State = status; Progress = progress; TotalDownloadSize = totalDownloadSize; DownloadSpeed = downloadSpeed; ETA = eta; VideoIndex = index; Data = data; } } public class RunResult { public bool Success { get; } public string[] ErrorOutput { get; } public T Data { get; } public RunResult(bool success, string[] error, T result) { Success = success; ErrorOutput = error; Data = result; } } public static class Utils { internal class FFmpegApi { public class Root { [JsonProperty("version")] public string Version { get; set; } [JsonProperty("permalink")] public string Permalink { get; set; } [JsonProperty("bin")] public Bin Bin { get; set; } } public class Bin { [JsonProperty("windows-64")] public OsBinVersion Windows64 { get; set; } [JsonProperty("linux-64")] public OsBinVersion Linux64 { get; set; } [JsonProperty("osx-64")] public OsBinVersion Osx64 { get; set; } } public class OsBinVersion { [JsonProperty("ffmpeg")] public string Ffmpeg { get; set; } [JsonProperty("ffprobe")] public string Ffprobe { get; set; } } public enum BinaryType { [EnumMember(Value = "ffmpeg")] FFmpeg, [EnumMember(Value = "ffprobe")] FFprobe } } private static readonly Regex rgxTimestamp = new Regex("[0-9]+(?::[0-9]+)+", RegexOptions.Compiled); private static readonly Dictionary accentChars = "ÂÃÄÀÁÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖŐØŒÙÚÛÜŰÝÞßàáâãäåæçèéêëìíîïðñòóôõöőøœùúûüűýþÿ".Zip(new string[68] { "A", "A", "A", "A", "A", "A", "AE", "C", "E", "E", "E", "E", "I", "I", "I", "I", "D", "N", "O", "O", "O", "O", "O", "O", "O", "OE", "U", "U", "U", "U", "U", "Y", "P", "ss", "a", "a", "a", "a", "a", "a", "ae", "c", "e", "e", "e", "e", "i", "i", "i", "i", "o", "n", "o", "o", "o", "o", "o", "o", "o", "oe", "u", "u", "u", "u", "u", "y", "p", "y" }, (char c, string s) => new { Key = c, Val = s }).ToDictionary(o => o.Key, o => o.Val); private const string DENO_VERSION_URL = "https://dl.deno.land/release-latest.txt"; private const string DENO_DOWNLOAD_URL_TEMPLATE = "https://dl.deno.land/release/{0}/deno-{1}.zip"; public static int TimeoutSeconds { get; set; } = 300; public static string YtDlpBinaryName => GetYtDlpBinaryName(); public static string FfmpegBinaryName => GetFfmpegBinaryName(); public static string FfprobeBinaryName => GetFfprobeBinaryName(); public static string Sanitize(string s, bool restricted = false) { rgxTimestamp.Replace(s, (Match m) => m.Groups[0].Value.Replace(':', '_')); string text = string.Join("", s.Select((char c) => sanitizeChar(c, restricted))); text = text.Replace("__", "_").Trim(new char[1] { '_' }); if (restricted && text.StartsWith("-_")) { text = text.Substring(2); } if (text.StartsWith("-")) { text = "_" + text.Substring(1); } text = text.TrimStart(new char[1] { '.' }); if (string.IsNullOrWhiteSpace(text)) { text = "_"; } return text; } private static string sanitizeChar(char c, bool restricted) { if (restricted && accentChars.ContainsKey(c)) { return accentChars[c]; } if (c != '?' && c >= ' ') { switch (c) { case '\u007f': break; case '"': if (!restricted) { return "'"; } return ""; case ':': if (!restricted) { return " -"; } return "_-"; default: if (Enumerable.Contains("\\/|*<>", c)) { return "_"; } if (restricted && Enumerable.Contains("!&'()[]{}$;`^,# ", c)) { return "_"; } if (restricted && c > '\u007f') { return "_"; } return c.ToString(); } } return ""; } public static string GetFullPath(string fileName) { if (File.Exists(fileName)) { return Path.GetFullPath(fileName); } string[] array = Environment.GetEnvironmentVariable("PATH").Split(new char[1] { Path.PathSeparator }); for (int i = 0; i < array.Length; i++) { string text = Path.Combine(array[i], fileName); if (File.Exists(text)) { return text; } } return null; } public static async Task DownloadBinaries(bool skipExisting = true, string directoryPath = "", bool downloadJSRuntime = true) { if (skipExisting) { if (!File.Exists(Path.Combine(directoryPath, GetYtDlpBinaryName()))) { await DownloadYtDlp(directoryPath); } if (!File.Exists(Path.Combine(directoryPath, GetFfmpegBinaryName()))) { await DownloadFFmpeg(directoryPath); } if (!File.Exists(Path.Combine(directoryPath, GetFfprobeBinaryName()))) { await DownloadFFprobe(directoryPath); } if (downloadJSRuntime && !File.Exists(Path.Combine(directoryPath, GetDenoBinaryName()))) { await DownloadDeno(directoryPath); } } else { await DownloadYtDlp(directoryPath); await DownloadFFmpeg(directoryPath); await DownloadFFprobe(directoryPath); if (downloadJSRuntime) { await DownloadDeno(directoryPath); } } } private static string GetYtDlpBinaryName() { return Path.GetFileName(GetYtDlpDownloadUrl()); } private static string GetFfmpegBinaryName() { switch (OSHelper.GetOSVersion()) { case OSVersion.Windows: return "ffmpeg.exe"; case OSVersion.OSX: case OSVersion.Linux: return "ffmpeg"; default: throw new Exception("Your OS isn't supported"); } } private static string GetFfprobeBinaryName() { switch (OSHelper.GetOSVersion()) { case OSVersion.Windows: return "ffprobe.exe"; case OSVersion.OSX: case OSVersion.Linux: return "ffprobe"; default: throw new Exception("Your OS isn't supported"); } } private static string GetDenoBinaryName() { switch (OSHelper.GetOSVersion()) { case OSVersion.Windows: return "deno.exe"; case OSVersion.OSX: case OSVersion.Linux: return "deno"; default: throw new Exception("Your OS isn't supported"); } } private static string GetYtDlpDownloadUrl() { return OSHelper.GetOSVersion() switch { OSVersion.Windows => "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe", OSVersion.OSX => "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos", OSVersion.Linux => "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp", _ => throw new Exception("Your OS isn't supported"), }; } public static async Task DownloadYtDlp(string directoryPath = "") { string ytDlpDownloadUrl = GetYtDlpDownloadUrl(); if (string.IsNullOrEmpty(directoryPath)) { directoryPath = Directory.GetCurrentDirectory(); } string downloadLocation = Path.Combine(directoryPath, Path.GetFileName(ytDlpDownloadUrl)); File.WriteAllBytes(downloadLocation, await DownloadFileBytesAsync(ytDlpDownloadUrl)); SetUnixExecPerms(downloadLocation); } public static async Task DownloadFFmpeg(string directoryPath = "") { await FFDownloader(directoryPath); } public static async Task DownloadFFprobe(string directoryPath = "") { await FFDownloader(directoryPath, FFmpegApi.BinaryType.FFprobe); } private static void SetUnixExecPerms(string filename) { if (OSHelper.IsWindows) { return; } throw new PlatformNotSupportedException("Setting Unix permissions is not supported on this platform."); } private static HttpClient GetHttpClient() { HttpClient httpClient = null; if (httpClient == null) { httpClient = new HttpClient(); httpClient.Timeout = TimeSpan.FromSeconds(TimeoutSeconds); } return httpClient; } private static async Task FFDownloader(string directoryPath = "", FFmpegApi.BinaryType binary = FFmpegApi.BinaryType.FFmpeg) { HttpClient client = GetHttpClient(); if (string.IsNullOrEmpty(directoryPath)) { directoryPath = Directory.GetCurrentDirectory(); } FFmpegApi.Root root = JsonConvert.DeserializeObject(await (await client.GetAsync("https://ffbinaries.com/api/v1/version/latest")).Content.ReadAsStringAsync()); FFmpegApi.OsBinVersion osBinVersion = OSHelper.GetOSVersion() switch { OSVersion.Windows => root?.Bin.Windows64, OSVersion.OSX => root?.Bin.Osx64, OSVersion.Linux => root?.Bin.Linux64, _ => throw new NotImplementedException("Your OS isn't supported"), }; byte[] buffer = await DownloadFileBytesAsync((binary == FFmpegApi.BinaryType.FFmpeg) ? osBinVersion.Ffmpeg : osBinVersion.Ffprobe); string unixExecPerms = string.Empty; using (MemoryStream stream = new MemoryStream(buffer)) { using ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Read); if (zipArchive.Entries.Count > 0) { unixExecPerms = Path.Combine(directoryPath, zipArchive.Entries[0].FullName); zipArchive.Entries[0].ExtractToFile(Path.Combine(directoryPath, zipArchive.Entries[0].FullName), overwrite: true); } } SetUnixExecPerms(unixExecPerms); client?.Dispose(); } private static async Task DownloadFileBytesAsync(string uri) { if (!Uri.TryCreate(uri, UriKind.Absolute, out Uri _)) { throw new InvalidOperationException("URI is invalid."); } HttpClient client = GetHttpClient(); byte[] result2 = await client.GetByteArrayAsync(uri); client?.Dispose(); return result2; } public static async Task DownloadDeno(string directoryPath = "") { HttpClient client = GetHttpClient(); if (string.IsNullOrEmpty(directoryPath)) { directoryPath = Directory.GetCurrentDirectory(); } string arg = (await (await client.GetAsync("https://dl.deno.land/release-latest.txt")).Content.ReadAsStringAsync()).Trim(); byte[] buffer = await DownloadFileBytesAsync(string.Format("https://dl.deno.land/release/{0}/deno-{1}.zip", arg, OSHelper.GetOSVersion() switch { OSVersion.Windows => "x86_64-pc-windows-msvc", OSVersion.OSX => "aarch64-apple-darwin", OSVersion.Linux => "x86_64-unknown-linux-gnu", _ => throw new NotImplementedException("Your OS isn't supported"), })); string unixExecPerms = string.Empty; using (MemoryStream stream = new MemoryStream(buffer)) { using ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Read); if (zipArchive.Entries.Count > 0) { unixExecPerms = Path.Combine(directoryPath, zipArchive.Entries[0].FullName); zipArchive.Entries[0].ExtractToFile(Path.Combine(directoryPath, zipArchive.Entries[0].FullName), overwrite: true); } } SetUnixExecPerms(unixExecPerms); client?.Dispose(); } public static void EnsureSuccess(this RunResult runResult) { if (!runResult.Success) { throw new Exception("Download failed:\n" + string.Join("\n", runResult.ErrorOutput)); } } } public class YoutubeDL { private static readonly Regex rgxFile = new Regex("^outfile:\\s\\\"?(.*)\\\"?", RegexOptions.Compiled); private static Regex rgxFilePostProc = new Regex("\\[download\\] Destination: [a-zA-Z]:\\\\\\S+\\.\\S{3,}", RegexOptions.Compiled); protected ProcessRunner runner; public string YoutubeDLPath { get; set; } = Utils.YtDlpBinaryName; public string FFmpegPath { get; set; } = Utils.FfmpegBinaryName; public string OutputFolder { get; set; } = Environment.CurrentDirectory; public string OutputFileTemplate { get; set; } = "%(title)s [%(id)s].%(ext)s"; public bool RestrictFilenames { get; set; } public bool OverwriteFiles { get; set; } = true; public bool IgnoreDownloadErrors { get; set; } = true; public string PythonInterpreterPath { get; set; } public string Version => FileVersionInfo.GetVersionInfo(Utils.GetFullPath(YoutubeDLPath)).FileVersion; public YoutubeDL(byte maxNumberOfProcesses = 4) { runner = new ProcessRunner(maxNumberOfProcesses); } public async Task SetMaxNumberOfProcesses(byte count) { await runner.SetTotalCount(count); } public async Task> RunWithOptions(string[] urls, OptionSet options, CancellationToken ct) { List output = new List(); YoutubeDLProcess youtubeDLProcess = CreateYoutubeDLProcess(); youtubeDLProcess.OutputReceived += delegate(object o, DataReceivedEventArgs e) { output.Add(e.Data); }; var (num, error) = await runner.RunThrottled(youtubeDLProcess, urls, options, ct); return new RunResult(num == 0, error, output.ToArray()); } public async Task> RunWithOptions(string url, OptionSet options, CancellationToken ct = default(CancellationToken), IProgress progress = null, IProgress output = null, bool showArgs = true) { string outFile = string.Empty; YoutubeDLProcess youtubeDLProcess = CreateYoutubeDLProcess(); if (showArgs) { output?.Report("Arguments: " + youtubeDLProcess.ConvertToArgs(new string[1] { url }, options) + "\n"); } else { output?.Report("Starting Download: " + url); } youtubeDLProcess.OutputReceived += delegate(object o, DataReceivedEventArgs e) { Match match = rgxFilePostProc.Match(e.Data); if (match.Success) { outFile = match.Groups[0].ToString().Replace("[download] Destination:", "").Replace(" ", ""); progress?.Report(new DownloadProgress(DownloadState.Success, 0f, null, null, null, 1, outFile)); } output?.Report(e.Data); }; var (num, error) = await runner.RunThrottled(youtubeDLProcess, new string[1] { url }, options, ct, progress); return new RunResult(num == 0, error, outFile); } public async Task RunUpdate() { string output = string.Empty; YoutubeDLProcess youtubeDLProcess = CreateYoutubeDLProcess(); youtubeDLProcess.OutputReceived += delegate(object o, DataReceivedEventArgs e) { output = e.Data; }; await youtubeDLProcess.RunAsync(null, new OptionSet { Update = true }); return output; } public async Task> RunVideoDataFetch(string url, CancellationToken ct = default(CancellationToken), bool flat = true, bool fetchComments = false, OptionSet overrideOptions = null) { OptionSet optionSet = GetDownloadOptions(); optionSet.DumpSingleJson = true; optionSet.FlatPlaylist = flat; optionSet.WriteComments = fetchComments; if (overrideOptions != null) { optionSet = optionSet.OverrideOptions(overrideOptions); } VideoData videoData = null; YoutubeDLProcess process = CreateYoutubeDLProcess(); process.OutputReceived += delegate(object o, DataReceivedEventArgs e) { try { videoData = JsonConvert.DeserializeObject(e.Data); } catch (JsonSerializationException) { process.RedirectToError(e); } }; var (num, error) = await runner.RunThrottled(process, new string[1] { url }, optionSet, ct); return new RunResult(num == 0, error, videoData); } public async Task> RunVideoDownload(string url, string format = "bestvideo+bestaudio/best", DownloadMergeFormat mergeFormat = DownloadMergeFormat.Unspecified, VideoRecodeFormat recodeFormat = VideoRecodeFormat.None, CancellationToken ct = default(CancellationToken), IProgress progress = null, IProgress output = null, OptionSet overrideOptions = null) { OptionSet optionSet = GetDownloadOptions(); optionSet.Format = format; optionSet.MergeOutputFormat = mergeFormat; optionSet.RecodeVideo = recodeFormat; if (overrideOptions != null) { optionSet = optionSet.OverrideOptions(overrideOptions); } string outputFile = string.Empty; YoutubeDLProcess youtubeDLProcess = CreateYoutubeDLProcess(); output?.Report("Arguments: " + youtubeDLProcess.ConvertToArgs(new string[1] { url }, optionSet) + "\n"); youtubeDLProcess.OutputReceived += delegate(object o, DataReceivedEventArgs e) { Match match = rgxFile.Match(e.Data); if (match.Success) { outputFile = match.Groups[1].ToString().Trim(new char[1] { '"' }); progress?.Report(new DownloadProgress(DownloadState.Success, 0f, null, null, null, 1, outputFile)); } output?.Report(e.Data); }; var (num, error) = await runner.RunThrottled(youtubeDLProcess, new string[1] { url }, optionSet, ct, progress); return new RunResult(num == 0, error, outputFile); } public async Task> RunVideoPlaylistDownload(string url, int? start = 1, int? end = null, int[] items = null, string format = "bestvideo+bestaudio/best", VideoRecodeFormat recodeFormat = VideoRecodeFormat.None, CancellationToken ct = default(CancellationToken), IProgress progress = null, IProgress output = null, OptionSet overrideOptions = null) { OptionSet optionSet = GetDownloadOptions(); optionSet.NoPlaylist = false; optionSet.PlaylistStart = start; optionSet.PlaylistEnd = end; if (items != null) { optionSet.PlaylistItems = string.Join(",", items); } optionSet.Format = format; optionSet.RecodeVideo = recodeFormat; if (overrideOptions != null) { optionSet = optionSet.OverrideOptions(overrideOptions); } List outputFiles = new List(); YoutubeDLProcess youtubeDLProcess = CreateYoutubeDLProcess(); output?.Report("Arguments: " + youtubeDLProcess.ConvertToArgs(new string[1] { url }, optionSet) + "\n"); youtubeDLProcess.OutputReceived += delegate(object o, DataReceivedEventArgs e) { Match match = rgxFile.Match(e.Data); if (match.Success) { string text = match.Groups[1].ToString().Trim(new char[1] { '"' }); outputFiles.Add(text); progress?.Report(new DownloadProgress(DownloadState.Success, 0f, null, null, null, 1, text)); } output?.Report(e.Data); }; var (num, error) = await runner.RunThrottled(youtubeDLProcess, new string[1] { url }, optionSet, ct, progress); return new RunResult(num == 0, error, outputFiles.ToArray()); } public async Task> RunAudioDownload(string url, AudioConversionFormat format = AudioConversionFormat.Best, CancellationToken ct = default(CancellationToken), IProgress progress = null, IProgress output = null, OptionSet overrideOptions = null) { OptionSet optionSet = GetDownloadOptions(); optionSet.Format = "bestaudio/best"; optionSet.ExtractAudio = true; optionSet.AudioFormat = format; if (overrideOptions != null) { optionSet = optionSet.OverrideOptions(overrideOptions); } string outputFile = string.Empty; new List(); YoutubeDLProcess youtubeDLProcess = CreateYoutubeDLProcess(); output?.Report("Arguments: " + youtubeDLProcess.ConvertToArgs(new string[1] { url }, optionSet) + "\n"); youtubeDLProcess.OutputReceived += delegate(object o, DataReceivedEventArgs e) { Match match = rgxFile.Match(e.Data); if (match.Success) { outputFile = match.Groups[1].ToString().Trim(new char[1] { '"' }); progress?.Report(new DownloadProgress(DownloadState.Success, 0f, null, null, null, 1, outputFile)); } output?.Report(e.Data); }; var (num, error) = await runner.RunThrottled(youtubeDLProcess, new string[1] { url }, optionSet, ct, progress); return new RunResult(num == 0, error, outputFile); } public async Task> RunAudioPlaylistDownload(string url, int? start = 1, int? end = null, int[] items = null, AudioConversionFormat format = AudioConversionFormat.Best, CancellationToken ct = default(CancellationToken), IProgress progress = null, IProgress output = null, OptionSet overrideOptions = null) { List outputFiles = new List(); OptionSet optionSet = GetDownloadOptions(); optionSet.NoPlaylist = false; optionSet.PlaylistStart = start; optionSet.PlaylistEnd = end; if (items != null) { optionSet.PlaylistItems = string.Join(",", items); } optionSet.Format = "bestaudio/best"; optionSet.ExtractAudio = true; optionSet.AudioFormat = format; if (overrideOptions != null) { optionSet = optionSet.OverrideOptions(overrideOptions); } YoutubeDLProcess youtubeDLProcess = CreateYoutubeDLProcess(); output?.Report("Arguments: " + youtubeDLProcess.ConvertToArgs(new string[1] { url }, optionSet) + "\n"); youtubeDLProcess.OutputReceived += delegate(object o, DataReceivedEventArgs e) { Match match = rgxFile.Match(e.Data); if (match.Success) { string text = match.Groups[1].ToString().Trim(new char[1] { '"' }); outputFiles.Add(text); progress?.Report(new DownloadProgress(DownloadState.Success, 0f, null, null, null, 1, text)); } output?.Report(e.Data); }; var (num, error) = await runner.RunThrottled(youtubeDLProcess, new string[1] { url }, optionSet, ct, progress); return new RunResult(num == 0, error, outputFiles.ToArray()); } protected virtual OptionSet GetDownloadOptions() { return new OptionSet { IgnoreErrors = IgnoreDownloadErrors, IgnoreConfig = true, NoPlaylist = true, Downloader = "m3u8:native", DownloaderArgs = "ffmpeg:-nostats -loglevel 0", Output = Path.Combine(OutputFolder, OutputFileTemplate), RestrictFilenames = RestrictFilenames, ForceOverwrites = OverwriteFiles, NoOverwrites = !OverwriteFiles, NoPart = true, FfmpegLocation = Utils.GetFullPath(FFmpegPath), Progress = true, Print = "after_move:outfile: %(filepath)s" }; } private YoutubeDLProcess CreateYoutubeDLProcess() { return new YoutubeDLProcess(YoutubeDLPath) { PythonPath = PythonInterpreterPath }; } } public class YoutubeDLProcess { private static readonly Regex rgxPlaylist = new Regex("Downloading video (\\d+) of (\\d+)", RegexOptions.Compiled); private static readonly Regex rgxProgress = new Regex("\\[download\\]\\s+(?:(?[\\d\\.]+)%(?:\\s+of\\s+\\~?\\s*(?[\\d\\.\\w]+))?\\s+at\\s+(?:(?[\\d\\.\\w]+\\/s)|[\\w\\s]+)\\s+ETA\\s(?[\\d\\:]+))?", RegexOptions.Compiled); private static readonly Regex rgxPost = new Regex("\\[(\\w+)\\]\\s+", RegexOptions.Compiled); public string PythonPath { get; set; } public string ExecutablePath { get; set; } public event EventHandler OutputReceived; public event EventHandler ErrorReceived; public YoutubeDLProcess(string executablePath = "yt-dlp.exe") { ExecutablePath = executablePath; } internal string ConvertToArgs(string[] urls, OptionSet options) { return options.ToString() + " -- " + ((urls != null) ? string.Join(" ", urls.Select((string s) => "\"" + s + "\"")) : string.Empty); } internal void RedirectToError(DataReceivedEventArgs e) { this.ErrorReceived?.Invoke(this, e); } public async Task RunAsync(string[] urls, OptionSet options) { return await RunAsync(urls, options, CancellationToken.None); } public async Task RunAsync(string[] urls, OptionSet options, CancellationToken ct, IProgress progress = null) { TaskCompletionSource tcs = new TaskCompletionSource(); Process process = new Process(); ProcessStartInfo processStartInfo = new ProcessStartInfo { CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 }; if (!string.IsNullOrEmpty(PythonPath)) { processStartInfo.FileName = PythonPath; processStartInfo.Arguments = "\"" + ExecutablePath + "\" " + ConvertToArgs(urls, options); } else { processStartInfo.FileName = ExecutablePath; processStartInfo.Arguments = ConvertToArgs(urls, options); } process.EnableRaisingEvents = true; process.StartInfo = processStartInfo; TaskCompletionSource tcsOut = new TaskCompletionSource(); bool isDownloading = false; process.OutputDataReceived += delegate(object o, DataReceivedEventArgs e) { if (e.Data == null) { tcsOut.SetResult(result: true); } else { Match match; if ((match = rgxProgress.Match(e.Data)).Success) { if (match.Groups.Count > 1 && match.Groups[1].Length > 0) { float progress2 = float.Parse(match.Groups[1].ToString(), CultureInfo.InvariantCulture) / 100f; Group obj = match.Groups["total"]; string totalDownloadSize = (obj.Success ? obj.Value : null); Group obj2 = match.Groups["speed"]; string downloadSpeed = (obj2.Success ? obj2.Value : null); Group obj3 = match.Groups["eta"]; string eta = (obj3.Success ? obj3.Value : null); progress?.Report(new DownloadProgress(DownloadState.Downloading, progress2, totalDownloadSize, downloadSpeed, eta)); } else { progress?.Report(new DownloadProgress(DownloadState.Downloading)); } isDownloading = true; } else if ((match = rgxPlaylist.Match(e.Data)).Success) { int index = int.Parse(match.Groups[1].Value); progress?.Report(new DownloadProgress(DownloadState.PreProcessing, 0f, null, null, null, index)); isDownloading = false; } else if (isDownloading && (match = rgxPost.Match(e.Data)).Success) { progress?.Report(new DownloadProgress(DownloadState.PostProcessing, 1f)); isDownloading = false; } this.OutputReceived?.Invoke(this, e); } }; TaskCompletionSource tcsError = new TaskCompletionSource(); process.ErrorDataReceived += delegate(object o, DataReceivedEventArgs e) { if (e.Data == null) { tcsError.SetResult(result: true); } else { progress?.Report(new DownloadProgress(DownloadState.Error, 0f, null, null, null, 1, e.Data)); this.ErrorReceived?.Invoke(this, e); } }; process.Exited += async delegate { await tcsOut.Task; await tcsError.Task; tcs.TrySetResult(process.ExitCode); process.Dispose(); }; ct.Register(delegate { if (!tcs.Task.IsCompleted) { tcs.TrySetCanceled(); } try { if (!process.HasExited) { process.KillTree(); } } catch { } }); if (!(await Task.Run(() => process.Start()))) { tcs.TrySetException(new InvalidOperationException("Failed to start yt-dlp process.")); } process.BeginOutputReadLine(); process.BeginErrorReadLine(); progress?.Report(new DownloadProgress(DownloadState.PreProcessing)); return await tcs.Task; } } } namespace YoutubeDLSharp.Options { public enum DownloadMergeFormat { Unspecified, Mp4, Mkv, Ogg, Webm, Flv } public enum AudioConversionFormat { Best, Aac, Flac, Mp3, M4a, Opus, Vorbis, Wav } public enum VideoRecodeFormat { None, Mp4, Mkv, Ogg, Webm, Flv, Avi } public interface IOption { string DefaultOptionString { get; } string[] OptionStrings { get; } bool IsSet { get; } bool IsCustom { get; } void SetFromString(string s); IEnumerable ToStringCollection(); } public class MultiOption : IOption { private MultiValue value; public string DefaultOptionString => OptionStrings.Last(); public string[] OptionStrings { get; } public bool IsSet { get; private set; } public bool IsCustom { get; } public MultiValue Value { get { return value; } set { IsSet = !object.Equals(value, default(T)); this.value = value; } } public MultiOption(params string[] optionStrings) { OptionStrings = optionStrings; IsSet = false; } public MultiOption(bool isCustom, params string[] optionStrings) { OptionStrings = optionStrings; IsSet = false; IsCustom = isCustom; } public void SetFromString(string s) { string[] array = s.Split(new char[1] { ' ' }); string text = s.Substring(array[0].Length).Trim(); if (typeof(T) != typeof(StringVals)) { text = text.Trim(new char[1] { '"' }); } if (!OptionStrings.Contains(array[0])) { throw new ArgumentException("Given string does not match required format."); } T val = Utils.OptionValueFromString(text); if (!IsSet) { Value = val; } else { Value.Values.Add(val); } } public override string ToString() { return string.Join(" ", ToStringCollection()); } public IEnumerable ToStringCollection() { if (!IsSet) { return new string[1] { "" }; } List list = new List(); foreach (T value in Value.Values) { list.Add(DefaultOptionString + Utils.OptionValueToString(value)); } return list; } } public class MultiValue { private readonly List values; public List Values => values; public MultiValue(params T[] values) { this.values = values.ToList(); } public static implicit operator MultiValue(T value) { return new MultiValue(value); } public static implicit operator MultiValue(T[] values) { return new MultiValue(values); } public static explicit operator T(MultiValue value) { if (value.Values.Count == 1) { return value.Values[0]; } throw new InvalidCastException($"Cannot cast sequence of values to {typeof(T)}."); } public static explicit operator T[](MultiValue value) { return value.Values.ToArray(); } } public class Option : IOption { private T value; public string DefaultOptionString => OptionStrings.First(); public string[] OptionStrings { get; } public bool IsSet { get; private set; } public T Value { get { return value; } set { IsSet = !object.Equals(value, default(T)); this.value = value; } } public bool IsCustom { get; } public Option(params string[] optionStrings) { OptionStrings = optionStrings; IsSet = false; } public Option(bool isCustom, params string[] optionStrings) { OptionStrings = optionStrings; IsSet = false; IsCustom = isCustom; } public void SetFromString(string s) { string[] array = s.Split(new char[1] { ' ' }); string text = s.Substring(array[0].Length).Trim(); if (typeof(T) != typeof(StringVals)) { text = text.Trim(new char[1] { '"' }); } if (!OptionStrings.Contains(array[0])) { throw new ArgumentException("Given string does not match required format."); } Value = Utils.OptionValueFromString(text); } public override string ToString() { if (!IsSet) { return string.Empty; } string text = Utils.OptionValueToString(Value); return DefaultOptionString + text; } public IEnumerable ToStringCollection() { return new string[1] { ToString() }; } } internal class OptionComparer : IEqualityComparer { public bool Equals(IOption x, IOption y) { if (x != null) { if (y != null) { return x.ToString().Equals(y.ToString()); } return false; } return y == null; } public int GetHashCode(IOption obj) { return obj.ToString().GetHashCode(); } } public class OptionSet : ICloneable { private Option username = new Option("-u", "--username"); private Option password = new Option("-p", "--password"); private Option twoFactor = new Option("-2", "--twofactor"); private Option netrc = new Option("-n", "--netrc"); private Option netrcLocation = new Option("--netrc-location"); private Option netrcCmd = new Option("--netrc-cmd"); private Option videoPassword = new Option("--video-password"); private Option apMso = new Option("--ap-mso"); private Option apUsername = new Option("--ap-username"); private Option apPassword = new Option("--ap-password"); private Option apListMso = new Option("--ap-list-mso"); private Option clientCertificate = new Option("--client-certificate"); private Option clientCertificateKey = new Option("--client-certificate-key"); private Option clientCertificatePassword = new Option("--client-certificate-password"); private static readonly OptionComparer Comparer = new OptionComparer(); public static readonly OptionSet Default = new OptionSet(); private Option getDescription = new Option("--get-description"); private Option getDuration = new Option("--get-duration"); private Option getFilename = new Option("--get-filename"); private Option getFormat = new Option("--get-format"); private Option getId = new Option("--get-id"); private Option getThumbnail = new Option("--get-thumbnail"); private Option getTitle = new Option("-e", "--get-title"); private Option getUrl = new Option("-g", "--get-url"); private Option matchTitle = new Option("--match-title"); private Option rejectTitle = new Option("--reject-title"); private Option minViews = new Option("--min-views"); private Option maxViews = new Option("--max-views"); private Option breakOnReject = new Option("--break-on-reject"); private Option userAgent = new Option("--user-agent"); private Option referer = new Option("--referer"); private Option playlistStart = new Option("--playlist-start"); private Option playlistEnd = new Option("--playlist-end"); private Option playlistReverse = new Option("--playlist-reverse"); private Option noColors = new Option("--no-colors"); private Option forceGenericExtractor = new Option("--force-generic-extractor"); private Option execBeforeDownload = new Option("--exec-before-download"); private Option noExecBeforeDownload = new Option("--no-exec-before-download"); private Option allFormats = new Option("--all-formats"); private Option allSubs = new Option("--all-subs"); private Option printJson = new Option("--print-json"); private Option autonumberSize = new Option("--autonumber-size"); private Option autonumberStart = new Option("--autonumber-start"); private Option id = new Option("--id"); private Option metadataFromTitle = new Option("--metadata-from-title"); private Option hlsPreferNative = new Option("--hls-prefer-native"); private Option hlsPreferFfmpeg = new Option("--hls-prefer-ffmpeg"); private Option listFormatsOld = new Option("--list-formats-old", "--no-list-formats-as-table"); private Option listFormatsAsTable = new Option("--list-formats-as-table", "--no-list-formats-old"); private Option youtubeSkipDashManifest = new Option("--youtube-skip-dash-manifest", "--no-youtube-include-dash-manifest"); private Option youtubeSkipHlsManifest = new Option("--youtube-skip-hls-manifest", "--no-youtube-include-hls-manifest"); private Option geoBypass = new Option("--geo-bypass"); private Option noGeoBypass = new Option("--no-geo-bypass"); private Option geoBypassCountry = new Option("--geo-bypass-country"); private Option geoBypassIpBlock = new Option("--geo-bypass-ip-block"); private Option concurrentFragments = new Option("-N", "--concurrent-fragments"); private Option limitRate = new Option("-r", "--limit-rate"); private Option throttledRate = new Option("--throttled-rate"); private Option retries = new Option("-R", "--retries"); private Option fileAccessRetries = new Option("--file-access-retries"); private Option fragmentRetries = new Option("--fragment-retries"); private MultiOption retrySleep = new MultiOption("--retry-sleep"); private Option skipUnavailableFragments = new Option("--skip-unavailable-fragments", "--no-abort-on-unavailable-fragments"); private Option abortOnUnavailableFragments = new Option("--abort-on-unavailable-fragments", "--no-skip-unavailable-fragments"); private Option keepFragments = new Option("--keep-fragments"); private Option noKeepFragments = new Option("--no-keep-fragments"); private Option bufferSize = new Option("--buffer-size"); private Option resizeBuffer = new Option("--resize-buffer"); private Option noResizeBuffer = new Option("--no-resize-buffer"); private Option httpChunkSize = new Option("--http-chunk-size"); private Option playlistRandom = new Option("--playlist-random"); private Option lazyPlaylist = new Option("--lazy-playlist"); private Option noLazyPlaylist = new Option("--no-lazy-playlist"); private Option hlsUseMpegts = new Option("--hls-use-mpegts"); private Option noHlsUseMpegts = new Option("--no-hls-use-mpegts"); private MultiOption downloadSections = new MultiOption("--download-sections"); private MultiOption downloader = new MultiOption("--downloader", "--external-downloader"); private MultiOption downloaderArgs = new MultiOption("--downloader-args", "--external-downloader-args"); private Option extractorRetries = new Option("--extractor-retries"); private Option allowDynamicMpd = new Option("--allow-dynamic-mpd", "--no-ignore-dynamic-mpd"); private Option ignoreDynamicMpd = new Option("--ignore-dynamic-mpd", "--no-allow-dynamic-mpd"); private Option hlsSplitDiscontinuity = new Option("--hls-split-discontinuity"); private Option noHlsSplitDiscontinuity = new Option("--no-hls-split-discontinuity"); private MultiOption extractorArgs = new MultiOption("--extractor-args"); private Option batchFile = new Option("-a", "--batch-file"); private Option noBatchFile = new Option("--no-batch-file"); private MultiOption paths = new MultiOption("-P", "--paths"); private Option output = new Option("-o", "--output"); private Option outputNaPlaceholder = new Option("--output-na-placeholder"); private Option restrictFilenames = new Option("--restrict-filenames"); private Option noRestrictFilenames = new Option("--no-restrict-filenames"); private Option windowsFilenames = new Option("--windows-filenames"); private Option noWindowsFilenames = new Option("--no-windows-filenames"); private Option trimFilenames = new Option("--trim-filenames"); private Option noOverwrites = new Option("-w", "--no-overwrites"); private Option forceOverwrites = new Option("--force-overwrites"); private Option noForceOverwrites = new Option("--no-force-overwrites"); private Option doContinue = new Option("-c", "--continue"); private Option noContinue = new Option("--no-continue"); private Option part = new Option("--part"); private Option noPart = new Option("--no-part"); private Option mtime = new Option("--mtime"); private Option noMtime = new Option("--no-mtime"); private Option writeDescription = new Option("--write-description"); private Option noWriteDescription = new Option("--no-write-description"); private Option writeInfoJson = new Option("--write-info-json"); private Option noWriteInfoJson = new Option("--no-write-info-json"); private Option writePlaylistMetafiles = new Option("--write-playlist-metafiles"); private Option noWritePlaylistMetafiles = new Option("--no-write-playlist-metafiles"); private Option cleanInfoJson = new Option("--clean-info-json"); private Option noCleanInfoJson = new Option("--no-clean-info-json"); private Option writeComments = new Option("--write-comments", "--get-comments"); private Option noWriteComments = new Option("--no-write-comments", "--no-get-comments"); private Option loadInfoJson = new Option("--load-info-json"); private Option cookies = new Option("--cookies"); private Option noCookies = new Option("--no-cookies"); private Option cookiesFromBrowser = new Option("--cookies-from-browser"); private Option noCookiesFromBrowser = new Option("--no-cookies-from-browser"); private Option cacheDir = new Option("--cache-dir"); private Option noCacheDir = new Option("--no-cache-dir"); private Option removeCacheDir = new Option("--rm-cache-dir"); private Option help = new Option("-h", "--help"); private Option version = new Option("--version"); private Option update = new Option("-U", "--update"); private Option noUpdate = new Option("--no-update"); private Option updateTo = new Option("--update-to"); private Option ignoreErrors = new Option("-i", "--ignore-errors"); private Option noAbortOnError = new Option("--no-abort-on-error"); private Option abortOnError = new Option("--abort-on-error", "--no-ignore-errors"); private Option listExtractors = new Option("--list-extractors"); private Option extractorDescriptions = new Option("--extractor-descriptions"); private Option useExtractors = new Option("--use-extractors", "--ies"); private Option defaultSearch = new Option("--default-search"); private Option ignoreConfig = new Option("--ignore-config", "--no-config"); private Option noConfigLocations = new Option("--no-config-locations"); private MultiOption configLocations = new MultiOption("--config-locations"); private MultiOption pluginDirs = new MultiOption("--plugin-dirs"); private Option noPluginDirs = new Option("--no-plugin-dirs"); private MultiOption jsRuntimes = new MultiOption("--js-runtimes"); private Option noJsRuntimes = new Option("--no-js-runtimes"); private MultiOption remoteComponents = new MultiOption("--remote-components"); private Option noRemoteComponents = new Option("--no-remote-components"); private Option flatPlaylist = new Option("--flat-playlist"); private Option noFlatPlaylist = new Option("--no-flat-playlist"); private Option liveFromStart = new Option("--live-from-start"); private Option noLiveFromStart = new Option("--no-live-from-start"); private Option waitForVideo = new Option("--wait-for-video"); private Option noWaitForVideo = new Option("--no-wait-for-video"); private Option markWatched = new Option("--mark-watched"); private Option noMarkWatched = new Option("--no-mark-watched"); private MultiOption color = new MultiOption("--color"); private Option compatOptions = new Option("--compat-options"); private MultiOption alias = new MultiOption("--alias"); private MultiOption presetAlias = new MultiOption("-t", "--preset-alias"); private Option geoVerificationProxy = new Option("--geo-verification-proxy"); private Option xff = new Option("--xff"); private Option writeLink = new Option("--write-link"); private Option writeUrlLink = new Option("--write-url-link"); private Option writeWeblocLink = new Option("--write-webloc-link"); private Option writeDesktopLink = new Option("--write-desktop-link"); private Option proxy = new Option("--proxy"); private Option socketTimeout = new Option("--socket-timeout"); private Option sourceAddress = new Option("--source-address"); private Option impersonate = new Option("--impersonate"); private Option listImpersonateTargets = new Option("--list-impersonate-targets"); private Option forceIPv4 = new Option("-4", "--force-ipv4"); private Option forceIPv6 = new Option("-6", "--force-ipv6"); private Option enableFileUrls = new Option("--enable-file-urls"); private Option extractAudio = new Option("-x", "--extract-audio"); private Option audioFormat = new Option("--audio-format"); private Option audioQuality = new Option("--audio-quality"); private Option remuxVideo = new Option("--remux-video"); private Option recodeVideo = new Option("--recode-video"); private MultiOption postprocessorArgs = new MultiOption("--postprocessor-args", "--ppa"); private Option keepVideo = new Option("-k", "--keep-video"); private Option noKeepVideo = new Option("--no-keep-video"); private Option postOverwrites = new Option("--post-overwrites"); private Option noPostOverwrites = new Option("--no-post-overwrites"); private Option embedSubs = new Option("--embed-subs"); private Option noEmbedSubs = new Option("--no-embed-subs"); private Option embedThumbnail = new Option("--embed-thumbnail"); private Option noEmbedThumbnail = new Option("--no-embed-thumbnail"); private Option embedMetadata = new Option("--embed-metadata", "--add-metadata"); private Option noEmbedMetadata = new Option("--no-embed-metadata", "--no-add-metadata"); private Option embedChapters = new Option("--embed-chapters", "--add-chapters"); private Option noEmbedChapters = new Option("--no-embed-chapters", "--no-add-chapters"); private Option embedInfoJson = new Option("--embed-info-json"); private Option noEmbedInfoJson = new Option("--no-embed-info-json"); private Option parseMetadata = new Option("--parse-metadata"); private MultiOption replaceInMetadata = new MultiOption("--replace-in-metadata"); private Option xattrs = new Option("--xattrs"); private Option concatPlaylist = new Option("--concat-playlist"); private Option fixup = new Option("--fixup"); private Option ffmpegLocation = new Option("--ffmpeg-location"); private MultiOption exec = new MultiOption("--exec"); private Option noExec = new Option("--no-exec"); private Option convertSubs = new Option("--convert-subs"); private Option convertThumbnails = new Option("--convert-thumbnails"); private Option splitChapters = new Option("--split-chapters"); private Option noSplitChapters = new Option("--no-split-chapters"); private MultiOption removeChapters = new MultiOption("--remove-chapters"); private Option noRemoveChapters = new Option("--no-remove-chapters"); private Option forceKeyframesAtCuts = new Option("--force-keyframes-at-cuts"); private Option noForceKeyframesAtCuts = new Option("--no-force-keyframes-at-cuts"); private MultiOption usePostprocessor = new MultiOption("--use-postprocessor"); private Option sponsorblockMark = new Option("--sponsorblock-mark"); private Option sponsorblockRemove = new Option("--sponsorblock-remove"); private Option sponsorblockChapterTitle = new Option("--sponsorblock-chapter-title"); private Option noSponsorblock = new Option("--no-sponsorblock"); private Option sponsorblockApi = new Option("--sponsorblock-api"); private Option writeSubs = new Option("--write-subs"); private Option noWriteSubs = new Option("--no-write-subs"); private Option writeAutoSubs = new Option("--write-auto-subs", "--write-automatic-subs"); private Option noWriteAutoSubs = new Option("--no-write-auto-subs", "--no-write-automatic-subs"); private Option listSubs = new Option("--list-subs"); private Option subFormat = new Option("--sub-format"); private Option subLangs = new Option("--sub-langs"); private Option writeThumbnail = new Option("--write-thumbnail"); private Option noWriteThumbnail = new Option("--no-write-thumbnail"); private Option writeAllThumbnails = new Option("--write-all-thumbnails"); private Option listThumbnails = new Option("--list-thumbnails"); private Option quiet = new Option("-q", "--quiet"); private Option noQuiet = new Option("--no-quiet"); private Option noWarnings = new Option("--no-warnings"); private Option simulate = new Option("-s", "--simulate"); private Option noSimulate = new Option("--no-simulate"); private Option ignoreNoFormatsError = new Option("--ignore-no-formats-error"); private Option noIgnoreNoFormatsError = new Option("--no-ignore-no-formats-error"); private Option skipDownload = new Option("--skip-download", "--no-download"); private MultiOption print = new MultiOption("-O", "--print"); private MultiOption printToFile = new MultiOption("--print-to-file"); private Option dumpJson = new Option("-j", "--dump-json"); private Option dumpSingleJson = new Option("-J", "--dump-single-json"); private Option forceWriteArchive = new Option("--force-write-archive", "--force-download-archive"); private Option newline = new Option("--newline"); private Option noProgress = new Option("--no-progress"); private Option progress = new Option("--progress"); private Option consoleTitle = new Option("--console-title"); private Option progressTemplate = new Option("--progress-template"); private Option progressDelta = new Option("--progress-delta"); private Option verbose = new Option("-v", "--verbose"); private Option dumpPages = new Option("--dump-pages"); private Option writePages = new Option("--write-pages"); private Option printTraffic = new Option("--print-traffic"); private Option format = new Option("-f", "--format"); private Option formatSort = new Option("-S", "--format-sort"); private Option formatSortForce = new Option("--format-sort-force", "--S-force"); private Option noFormatSortForce = new Option("--no-format-sort-force"); private Option videoMultistreams = new Option("--video-multistreams"); private Option noVideoMultistreams = new Option("--no-video-multistreams"); private Option audioMultistreams = new Option("--audio-multistreams"); private Option noAudioMultistreams = new Option("--no-audio-multistreams"); private Option preferFreeFormats = new Option("--prefer-free-formats"); private Option noPreferFreeFormats = new Option("--no-prefer-free-formats"); private Option checkFormats = new Option("--check-formats"); private Option checkAllFormats = new Option("--check-all-formats"); private Option noCheckFormats = new Option("--no-check-formats"); private Option listFormats = new Option("-F", "--list-formats"); private Option mergeOutputFormat = new Option("--merge-output-format"); private Option playlistItems = new Option("-I", "--playlist-items"); private Option minFilesize = new Option("--min-filesize"); private Option maxFilesize = new Option("--max-filesize"); private Option date = new Option("--date"); private Option dateBefore = new Option("--datebefore"); private Option dateAfter = new Option("--dateafter"); private MultiOption matchFilters = new MultiOption("--match-filters"); private Option noMatchFilters = new Option("--no-match-filters"); private Option breakMatchFilters = new Option("--break-match-filters"); private Option noBreakMatchFilters = new Option("--no-break-match-filters"); private Option noPlaylist = new Option("--no-playlist"); private Option yesPlaylist = new Option("--yes-playlist"); private Option ageLimit = new Option("--age-limit"); private Option downloadArchive = new Option("--download-archive"); private Option noDownloadArchive = new Option("--no-download-archive"); private Option maxDownloads = new Option("--max-downloads"); private Option breakOnExisting = new Option("--break-on-existing"); private Option noBreakOnExisting = new Option("--no-break-on-existing"); private Option breakPerInput = new Option("--break-per-input"); private Option noBreakPerInput = new Option("--no-break-per-input"); private Option skipPlaylistAfterErrors = new Option("--skip-playlist-after-errors"); private Option encoding = new Option("--encoding"); private Option legacyServerConnect = new Option("--legacy-server-connect"); private Option noCheckCertificates = new Option("--no-check-certificates"); private Option preferInsecure = new Option("--prefer-insecure"); private MultiOption addHeaders = new MultiOption("--add-headers"); private Option bidiWorkaround = new Option("--bidi-workaround"); private Option sleepRequests = new Option("--sleep-requests"); private Option sleepInterval = new Option("--sleep-interval", "--min-sleep-interval"); private Option maxSleepInterval = new Option("--max-sleep-interval"); private Option sleepSubtitles = new Option("--sleep-subtitles"); public string Username { get { return username.Value; } set { username.Value = value; } } public string Password { get { return password.Value; } set { password.Value = value; } } public string TwoFactor { get { return twoFactor.Value; } set { twoFactor.Value = value; } } public bool Netrc { get { return netrc.Value; } set { netrc.Value = value; } } public string NetrcLocation { get { return netrcLocation.Value; } set { netrcLocation.Value = value; } } public string NetrcCmd { get { return netrcCmd.Value; } set { netrcCmd.Value = value; } } public string VideoPassword { get { return videoPassword.Value; } set { videoPassword.Value = value; } } public string ApMso { get { return apMso.Value; } set { apMso.Value = value; } } public string ApUsername { get { return apUsername.Value; } set { apUsername.Value = value; } } public string ApPassword { get { return apPassword.Value; } set { apPassword.Value = value; } } public bool ApListMso { get { return apListMso.Value; } set { apListMso.Value = value; } } public string ClientCertificate { get { return clientCertificate.Value; } set { clientCertificate.Value = value; } } public string ClientCertificateKey { get { return clientCertificateKey.Value; } set { clientCertificateKey.Value = value; } } public string ClientCertificatePassword { get { return clientCertificatePassword.Value; } set { clientCertificatePassword.Value = value; } } public IOption[] CustomOptions { get; set; } = new IOption[0]; [Obsolete("Deprecated in favor of: --print description.")] public bool GetDescription { get { return getDescription.Value; } set { getDescription.Value = value; } } [Obsolete("Deprecated in favor of: --print duration_string.")] public bool GetDuration { get { return getDuration.Value; } set { getDuration.Value = value; } } [Obsolete("Deprecated in favor of: --print filename.")] public bool GetFilename { get { return getFilename.Value; } set { getFilename.Value = value; } } [Obsolete("Deprecated in favor of: --print format.")] public bool GetFormat { get { return getFormat.Value; } set { getFormat.Value = value; } } [Obsolete("Deprecated in favor of: --print id.")] public bool GetId { get { return getId.Value; } set { getId.Value = value; } } [Obsolete("Deprecated in favor of: --print thumbnail.")] public bool GetThumbnail { get { return getThumbnail.Value; } set { getThumbnail.Value = value; } } [Obsolete("Deprecated in favor of: --print title.")] public bool GetTitle { get { return getTitle.Value; } set { getTitle.Value = value; } } [Obsolete("Deprecated in favor of: --print urls.")] public bool GetUrl { get { return getUrl.Value; } set { getUrl.Value = value; } } [Obsolete("Deprecated in favor of: --match-filter \"title ~= (?i)REGEX\".")] public string MatchTitle { get { return matchTitle.Value; } set { matchTitle.Value = value; } } [Obsolete("Deprecated in favor of: --match-filter \"title !~= (?i)REGEX\".")] public string RejectTitle { get { return rejectTitle.Value; } set { rejectTitle.Value = value; } } [Obsolete("Deprecated in favor of: --match-filter \"view_count >=? COUNT\".")] public long? MinViews { get { return minViews.Value; } set { minViews.Value = value; } } [Obsolete("Deprecated in favor of: --match-filter \"view_count <=? COUNT\".")] public long? MaxViews { get { return maxViews.Value; } set { maxViews.Value = value; } } [Obsolete("Deprecated in favor of: Use --break-match-filter.")] public bool BreakOnReject { get { return breakOnReject.Value; } set { breakOnReject.Value = value; } } [Obsolete("Deprecated in favor of: --add-header \"User-Agent:UA\".")] public string UserAgent { get { return userAgent.Value; } set { userAgent.Value = value; } } [Obsolete("Deprecated in favor of: --add-header \"Referer:URL\".")] public string Referer { get { return referer.Value; } set { referer.Value = value; } } [Obsolete("Deprecated in favor of: -I NUMBER:.")] public int? PlaylistStart { get { return playlistStart.Value; } set { playlistStart.Value = value; } } [Obsolete("Deprecated in favor of: -I :NUMBER.")] public int? PlaylistEnd { get { return playlistEnd.Value; } set { playlistEnd.Value = value; } } [Obsolete("Deprecated in favor of: -I ::-1.")] public bool PlaylistReverse { get { return playlistReverse.Value; } set { playlistReverse.Value = value; } } [Obsolete("Deprecated in favor of: --color no_color.")] public bool NoColors { get { return noColors.Value; } set { noColors.Value = value; } } [Obsolete("Deprecated in favor of: --ies generic,default.")] public bool ForceGenericExtractor { get { return forceGenericExtractor.Value; } set { forceGenericExtractor.Value = value; } } [Obsolete("Deprecated in favor of: --exec \"before_dl:CMD\".")] public string ExecBeforeDownload { get { return execBeforeDownload.Value; } set { execBeforeDownload.Value = value; } } [Obsolete("Deprecated in favor of: --no-exec.")] public bool NoExecBeforeDownload { get { return noExecBeforeDownload.Value; } set { noExecBeforeDownload.Value = value; } } [Obsolete("Deprecated in favor of: -f all.")] public bool AllFormats { get { return allFormats.Value; } set { allFormats.Value = value; } } [Obsolete("Deprecated in favor of: --sub-langs all --write-subs.")] public bool AllSubs { get { return allSubs.Value; } set { allSubs.Value = value; } } [Obsolete("Deprecated in favor of: -j --no-simulate.")] public bool PrintJson { get { return printJson.Value; } set { printJson.Value = value; } } [Obsolete("Deprecated in favor of: Use string formatting, e.g. %(autonumber)03d.")] public string AutonumberSize { get { return autonumberSize.Value; } set { autonumberSize.Value = value; } } [Obsolete("Deprecated in favor of: Use internal field formatting like %(autonumber+NUMBER)s.")] public int? AutonumberStart { get { return autonumberStart.Value; } set { autonumberStart.Value = value; } } [Obsolete("Deprecated in favor of: -o \"%(id)s.%(ext)s\".")] public bool Id { get { return id.Value; } set { id.Value = value; } } [Obsolete("Deprecated in favor of: --parse-metadata \"%(title)s:FORMAT\".")] public string MetadataFromTitle { get { return metadataFromTitle.Value; } set { metadataFromTitle.Value = value; } } [Obsolete("Deprecated in favor of: --downloader \"m3u8:native\".")] public bool HlsPreferNative { get { return hlsPreferNative.Value; } set { hlsPreferNative.Value = value; } } [Obsolete("Deprecated in favor of: --downloader \"m3u8:ffmpeg\".")] public bool HlsPreferFfmpeg { get { return hlsPreferFfmpeg.Value; } set { hlsPreferFfmpeg.Value = value; } } [Obsolete("Deprecated in favor of: --compat-options list-formats (Alias: --no-list-formats-as-table).")] public bool ListFormatsOld { get { return listFormatsOld.Value; } set { listFormatsOld.Value = value; } } [Obsolete("Deprecated in favor of: --compat-options -list-formats [Default] (Alias: --no-list-formats-old).")] public bool ListFormatsAsTable { get { return listFormatsAsTable.Value; } set { listFormatsAsTable.Value = value; } } [Obsolete("Deprecated in favor of: --extractor-args \"youtube:skip=dash\" (Alias: --no-youtube-include-dash-manifest).")] public bool YoutubeSkipDashManifest { get { return youtubeSkipDashManifest.Value; } set { youtubeSkipDashManifest.Value = value; } } [Obsolete("Deprecated in favor of: --extractor-args \"youtube:skip=hls\" (Alias: --no-youtube-include-hls-manifest).")] public bool YoutubeSkipHlsManifest { get { return youtubeSkipHlsManifest.Value; } set { youtubeSkipHlsManifest.Value = value; } } [Obsolete("Deprecated in favor of: --xff \"default\".")] public bool GeoBypass { get { return geoBypass.Value; } set { geoBypass.Value = value; } } [Obsolete("Deprecated in favor of: --xff \"never\".")] public bool NoGeoBypass { get { return noGeoBypass.Value; } set { noGeoBypass.Value = value; } } [Obsolete("Deprecated in favor of: --xff CODE.")] public string GeoBypassCountry { get { return geoBypassCountry.Value; } set { geoBypassCountry.Value = value; } } [Obsolete("Deprecated in favor of: --xff IP_BLOCK.")] public string GeoBypassIpBlock { get { return geoBypassIpBlock.Value; } set { geoBypassIpBlock.Value = value; } } public int? ConcurrentFragments { get { return concurrentFragments.Value; } set { concurrentFragments.Value = value; } } public long? LimitRate { get { return limitRate.Value; } set { limitRate.Value = value; } } public long? ThrottledRate { get { return throttledRate.Value; } set { throttledRate.Value = value; } } public int? Retries { get { return retries.Value; } set { retries.Value = value; } } public int? FileAccessRetries { get { return fileAccessRetries.Value; } set { fileAccessRetries.Value = value; } } public int? FragmentRetries { get { return fragmentRetries.Value; } set { fragmentRetries.Value = value; } } public MultiValue RetrySleep { get { return retrySleep.Value; } set { retrySleep.Value = value; } } public bool SkipUnavailableFragments { get { return skipUnavailableFragments.Value; } set { skipUnavailableFragments.Value = value; } } public bool AbortOnUnavailableFragments { get { return abortOnUnavailableFragments.Value; } set { abortOnUnavailableFragments.Value = value; } } public bool KeepFragments { get { return keepFragments.Value; } set { keepFragments.Value = value; } } public bool NoKeepFragments { get { return noKeepFragments.Value; } set { noKeepFragments.Value = value; } } public long? BufferSize { get { return bufferSize.Value; } set { bufferSize.Value = value; } } public bool ResizeBuffer { get { return resizeBuffer.Value; } set { resizeBuffer.Value = value; } } public bool NoResizeBuffer { get { return noResizeBuffer.Value; } set { noResizeBuffer.Value = value; } } public long? HttpChunkSize { get { return httpChunkSize.Value; } set { httpChunkSize.Value = value; } } public bool PlaylistRandom { get { return playlistRandom.Value; } set { playlistRandom.Value = value; } } public bool LazyPlaylist { get { return lazyPlaylist.Value; } set { lazyPlaylist.Value = value; } } public bool NoLazyPlaylist { get { return noLazyPlaylist.Value; } set { noLazyPlaylist.Value = value; } } public bool HlsUseMpegts { get { return hlsUseMpegts.Value; } set { hlsUseMpegts.Value = value; } } public bool NoHlsUseMpegts { get { return noHlsUseMpegts.Value; } set { noHlsUseMpegts.Value = value; } } public MultiValue DownloadSections { get { return downloadSections.Value; } set { downloadSections.Value = value; } } public MultiValue Downloader { get { return downloader.Value; } set { downloader.Value = value; } } public MultiValue DownloaderArgs { get { return downloaderArgs.Value; } set { downloaderArgs.Value = value; } } public int? ExtractorRetries { get { return extractorRetries.Value; } set { extractorRetries.Value = value; } } public bool AllowDynamicMpd { get { return allowDynamicMpd.Value; } set { allowDynamicMpd.Value = value; } } public bool IgnoreDynamicMpd { get { return ignoreDynamicMpd.Value; } set { ignoreDynamicMpd.Value = value; } } public bool HlsSplitDiscontinuity { get { return hlsSplitDiscontinuity.Value; } set { hlsSplitDiscontinuity.Value = value; } } public bool NoHlsSplitDiscontinuity { get { return noHlsSplitDiscontinuity.Value; } set { noHlsSplitDiscontinuity.Value = value; } } public MultiValue ExtractorArgs { get { return extractorArgs.Value; } set { extractorArgs.Value = value; } } public string BatchFile { get { return batchFile.Value; } set { batchFile.Value = value; } } public bool NoBatchFile { get { return noBatchFile.Value; } set { noBatchFile.Value = value; } } public MultiValue Paths { get { return paths.Value; } set { paths.Value = value; } } public string Output { get { return output.Value; } set { output.Value = value; } } public string OutputNaPlaceholder { get { return outputNaPlaceholder.Value; } set { outputNaPlaceholder.Value = value; } } public bool RestrictFilenames { get { return restrictFilenames.Value; } set { restrictFilenames.Value = value; } } public bool NoRestrictFilenames { get { return noRestrictFilenames.Value; } set { noRestrictFilenames.Value = value; } } public bool WindowsFilenames { get { return windowsFilenames.Value; } set { windowsFilenames.Value = value; } } public bool NoWindowsFilenames { get { return noWindowsFilenames.Value; } set { noWindowsFilenames.Value = value; } } public int? TrimFilenames { get { return trimFilenames.Value; } set { trimFilenames.Value = value; } } public bool NoOverwrites { get { return noOverwrites.Value; } set { noOverwrites.Value = value; } } public bool ForceOverwrites { get { return forceOverwrites.Value; } set { forceOverwrites.Value = value; } } public bool NoForceOverwrites { get { return noForceOverwrites.Value; } set { noForceOverwrites.Value = value; } } public bool Continue { get { return doContinue.Value; } set { doContinue.Value = value; } } public bool NoContinue { get { return noContinue.Value; } set { noContinue.Value = value; } } public bool Part { get { return part.Value; } set { part.Value = value; } } public bool NoPart { get { return noPart.Value; } set { noPart.Value = value; } } public bool Mtime { get { return mtime.Value; } set { mtime.Value = value; } } public bool NoMtime { get { return noMtime.Value; } set { noMtime.Value = value; } } public bool WriteDescription { get { return writeDescription.Value; } set { writeDescription.Value = value; } } public bool NoWriteDescription { get { return noWriteDescription.Value; } set { noWriteDescription.Value = value; } } public bool WriteInfoJson { get { return writeInfoJson.Value; } set { writeInfoJson.Value = value; } } public bool NoWriteInfoJson { get { return noWriteInfoJson.Value; } set { noWriteInfoJson.Value = value; } } public bool WritePlaylistMetafiles { get { return writePlaylistMetafiles.Value; } set { writePlaylistMetafiles.Value = value; } } public bool NoWritePlaylistMetafiles { get { return noWritePlaylistMetafiles.Value; } set { noWritePlaylistMetafiles.Value = value; } } public bool CleanInfoJson { get { return cleanInfoJson.Value; } set { cleanInfoJson.Value = value; } } public bool NoCleanInfoJson { get { return noCleanInfoJson.Value; } set { noCleanInfoJson.Value = value; } } public bool WriteComments { get { return writeComments.Value; } set { writeComments.Value = value; } } public bool NoWriteComments { get { return noWriteComments.Value; } set { noWriteComments.Value = value; } } public string LoadInfoJson { get { return loadInfoJson.Value; } set { loadInfoJson.Value = value; } } public string Cookies { get { return cookies.Value; } set { cookies.Value = value; } } public bool NoCookies { get { return noCookies.Value; } set { noCookies.Value = value; } } public string CookiesFromBrowser { get { return cookiesFromBrowser.Value; } set { cookiesFromBrowser.Value = value; } } public bool NoCookiesFromBrowser { get { return noCookiesFromBrowser.Value; } set { noCookiesFromBrowser.Value = value; } } public string CacheDir { get { return cacheDir.Value; } set { cacheDir.Value = value; } } public bool NoCacheDir { get { return noCacheDir.Value; } set { noCacheDir.Value = value; } } public bool RemoveCacheDir { get { return removeCacheDir.Value; } set { removeCacheDir.Value = value; } } public bool Help { get { return help.Value; } set { help.Value = value; } } public bool Version { get { return version.Value; } set { version.Value = value; } } public bool Update { get { return update.Value; } set { update.Value = value; } } public bool NoUpdate { get { return noUpdate.Value; } set { noUpdate.Value = value; } } public string UpdateTo { get { return updateTo.Value; } set { updateTo.Value = value; } } public bool IgnoreErrors { get { return ignoreErrors.Value; } set { ignoreErrors.Value = value; } } public bool NoAbortOnError { get { return noAbortOnError.Value; } set { noAbortOnError.Value = value; } } public bool AbortOnError { get { return abortOnError.Value; } set { abortOnError.Value = value; } } public bool ListExtractors { get { return listExtractors.Value; } set { listExtractors.Value = value; } } public bool ExtractorDescriptions { get { return extractorDescriptions.Value; } set { extractorDescriptions.Value = value; } } public string UseExtractors { get { return useExtractors.Value; } set { useExtractors.Value = value; } } public string DefaultSearch { get { return defaultSearch.Value; } set { defaultSearch.Value = value; } } public bool IgnoreConfig { get { return ignoreConfig.Value; } set { ignoreConfig.Value = value; } } public bool NoConfigLocations { get { return noConfigLocations.Value; } set { noConfigLocations.Value = value; } } public MultiValue ConfigLocations { get { return configLocations.Value; } set { configLocations.Value = value; } } public MultiValue PluginDirs { get { return pluginDirs.Value; } set { pluginDirs.Value = value; } } public bool NoPluginDirs { get { return noPluginDirs.Value; } set { noPluginDirs.Value = value; } } public MultiValue JsRuntimes { get { return jsRuntimes.Value; } set { jsRuntimes.Value = value; } } public bool NoJsRuntimes { get { return noJsRuntimes.Value; } set { noJsRuntimes.Value = value; } } public MultiValue RemoteComponents { get { return remoteComponents.Value; } set { remoteComponents.Value = value; } } public bool NoRemoteComponents { get { return noRemoteComponents.Value; } set { noRemoteComponents.Value = value; } } public bool FlatPlaylist { get { return flatPlaylist.Value; } set { flatPlaylist.Value = value; } } public bool NoFlatPlaylist { get { return noFlatPlaylist.Value; } set { noFlatPlaylist.Value = value; } } public bool LiveFromStart { get { return liveFromStart.Value; } set { liveFromStart.Value = value; } } public bool NoLiveFromStart { get { return noLiveFromStart.Value; } set { noLiveFromStart.Value = value; } } public string WaitForVideo { get { return waitForVideo.Value; } set { waitForVideo.Value = value; } } public bool NoWaitForVideo { get { return noWaitForVideo.Value; } set { noWaitForVideo.Value = value; } } public bool MarkWatched { get { return markWatched.Value; } set { markWatched.Value = value; } } public bool NoMarkWatched { get { return noMarkWatched.Value; } set { noMarkWatched.Value = value; } } public MultiValue Color { get { return color.Value; } set { color.Value = value; } } public string CompatOptions { get { return compatOptions.Value; } set { compatOptions.Value = value; } } public MultiValue Alias { get { return alias.Value; } set { alias.Value = value; } } public MultiValue PresetAlias { get { return presetAlias.Value; } set { presetAlias.Value = value; } } public string GeoVerificationProxy { get { return geoVerificationProxy.Value; } set { geoVerificationProxy.Value = value; } } public string Xff { get { return xff.Value; } set { xff.Value = value; } } public bool WriteLink { get { return writeLink.Value; } set { writeLink.Value = value; } } public bool WriteUrlLink { get { return writeUrlLink.Value; } set { writeUrlLink.Value = value; } } public bool WriteWeblocLink { get { return writeWeblocLink.Value; } set { writeWeblocLink.Value = value; } } public bool WriteDesktopLink { get { return writeDesktopLink.Value; } set { writeDesktopLink.Value = value; } } public string Proxy { get { return proxy.Value; } set { proxy.Value = value; } } public int? SocketTimeout { get { return socketTimeout.Value; } set { socketTimeout.Value = value; } } public string SourceAddress { get { return sourceAddress.Value; } set { sourceAddress.Value = value; } } public string Impersonate { get { return impersonate.Value; } set { impersonate.Value = value; } } public bool ListImpersonateTargets { get { return listImpersonateTargets.Value; } set { listImpersonateTargets.Value = value; } } public bool ForceIPv4 { get { return forceIPv4.Value; } set { forceIPv4.Value = value; } } public bool ForceIPv6 { get { return forceIPv6.Value; } set { forceIPv6.Value = value; } } public bool EnableFileUrls { get { return enableFileUrls.Value; } set { enableFileUrls.Value = value; } } public bool ExtractAudio { get { return extractAudio.Value; } set { extractAudio.Value = value; } } public AudioConversionFormat AudioFormat { get { return audioFormat.Value; } set { audioFormat.Value = value; } } public byte? AudioQuality { get { return audioQuality.Value; } set { audioQuality.Value = value; } } public string RemuxVideo { get { return remuxVideo.Value; } set { remuxVideo.Value = value; } } public VideoRecodeFormat RecodeVideo { get { return recodeVideo.Value; } set { recodeVideo.Value = value; } } public MultiValue PostprocessorArgs { get { return postprocessorArgs.Value; } set { postprocessorArgs.Value = value; } } public bool KeepVideo { get { return keepVideo.Value; } set { keepVideo.Value = value; } } public bool NoKeepVideo { get { return noKeepVideo.Value; } set { noKeepVideo.Value = value; } } public bool PostOverwrites { get { return postOverwrites.Value; } set { postOverwrites.Value = value; } } public bool NoPostOverwrites { get { return noPostOverwrites.Value; } set { noPostOverwrites.Value = value; } } public bool EmbedSubs { get { return embedSubs.Value; } set { embedSubs.Value = value; } } public bool NoEmbedSubs { get { return noEmbedSubs.Value; } set { noEmbedSubs.Value = value; } } public bool EmbedThumbnail { get { return embedThumbnail.Value; } set { embedThumbnail.Value = value; } } public bool NoEmbedThumbnail { get { return noEmbedThumbnail.Value; } set { noEmbedThumbnail.Value = value; } } public bool EmbedMetadata { get { return embedMetadata.Value; } set { embedMetadata.Value = value; } } public bool NoEmbedMetadata { get { return noEmbedMetadata.Value; } set { noEmbedMetadata.Value = value; } } public bool EmbedChapters { get { return embedChapters.Value; } set { embedChapters.Value = value; } } public bool NoEmbedChapters { get { return noEmbedChapters.Value; } set { noEmbedChapters.Value = value; } } public bool EmbedInfoJson { get { return embedInfoJson.Value; } set { embedInfoJson.Value = value; } } public bool NoEmbedInfoJson { get { return noEmbedInfoJson.Value; } set { noEmbedInfoJson.Value = value; } } public string ParseMetadata { get { return parseMetadata.Value; } set { parseMetadata.Value = value; } } public MultiValue ReplaceInMetadata { get { return replaceInMetadata.Value; } set { replaceInMetadata.Value = value; } } public bool Xattrs { get { return xattrs.Value; } set { xattrs.Value = value; } } public string ConcatPlaylist { get { return concatPlaylist.Value; } set { concatPlaylist.Value = value; } } public string Fixup { get { return fixup.Value; } set { fixup.Value = value; } } public string FfmpegLocation { get { return ffmpegLocation.Value; } set { ffmpegLocation.Value = value; } } public MultiValue Exec { get { return exec.Value; } set { exec.Value = value; } } public bool NoExec { get { return noExec.Value; } set { noExec.Value = value; } } public string ConvertSubs { get { return convertSubs.Value; } set { convertSubs.Value = value; } } public string ConvertThumbnails { get { return convertThumbnails.Value; } set { convertThumbnails.Value = value; } } public bool SplitChapters { get { return splitChapters.Value; } set { splitChapters.Value = value; } } public bool NoSplitChapters { get { return noSplitChapters.Value; } set { noSplitChapters.Value = value; } } public MultiValue RemoveChapters { get { return removeChapters.Value; } set { removeChapters.Value = value; } } public bool NoRemoveChapters { get { return noRemoveChapters.Value; } set { noRemoveChapters.Value = value; } } public bool ForceKeyframesAtCuts { get { return forceKeyframesAtCuts.Value; } set { forceKeyframesAtCuts.Value = value; } } public bool NoForceKeyframesAtCuts { get { return noForceKeyframesAtCuts.Value; } set { noForceKeyframesAtCuts.Value = value; } } public MultiValue UsePostprocessor { get { return usePostprocessor.Value; } set { usePostprocessor.Value = value; } } public string SponsorblockMark { get { return sponsorblockMark.Value; } set { sponsorblockMark.Value = value; } } public string SponsorblockRemove { get { return sponsorblockRemove.Value; } set { sponsorblockRemove.Value = value; } } public string SponsorblockChapterTitle { get { return sponsorblockChapterTitle.Value; } set { sponsorblockChapterTitle.Value = value; } } public bool NoSponsorblock { get { return noSponsorblock.Value; } set { noSponsorblock.Value = value; } } public string SponsorblockApi { get { return sponsorblockApi.Value; } set { sponsorblockApi.Value = value; } } public bool WriteSubs { get { return writeSubs.Value; } set { writeSubs.Value = value; } } public bool NoWriteSubs { get { return noWriteSubs.Value; } set { noWriteSubs.Value = value; } } public bool WriteAutoSubs { get { return writeAutoSubs.Value; } set { writeAutoSubs.Value = value; } } public bool NoWriteAutoSubs { get { return noWriteAutoSubs.Value; } set { noWriteAutoSubs.Value = value; } } public bool ListSubs { get { return listSubs.Value; } set { listSubs.Value = value; } } public string SubFormat { get { return subFormat.Value; } set { subFormat.Value = value; } } public string SubLangs { get { return subLangs.Value; } set { subLangs.Value = value; } } public bool WriteThumbnail { get { return writeThumbnail.Value; } set { writeThumbnail.Value = value; } } public bool NoWriteThumbnail { get { return noWriteThumbnail.Value; } set { noWriteThumbnail.Value = value; } } public bool WriteAllThumbnails { get { return writeAllThumbnails.Value; } set { writeAllThumbnails.Value = value; } } public bool ListThumbnails { get { return listThumbnails.Value; } set { listThumbnails.Value = value; } } public bool Quiet { get { return quiet.Value; } set { quiet.Value = value; } } public bool NoQuiet { get { return noQuiet.Value; } set { noQuiet.Value = value; } } public bool NoWarnings { get { return noWarnings.Value; } set { noWarnings.Value = value; } } public bool Simulate { get { return simulate.Value; } set { simulate.Value = value; } } public bool NoSimulate { get { return noSimulate.Value; } set { noSimulate.Value = value; } } public bool IgnoreNoFormatsError { get { return ignoreNoFormatsError.Value; } set { ignoreNoFormatsError.Value = value; } } public bool NoIgnoreNoFormatsError { get { return noIgnoreNoFormatsError.Value; } set { noIgnoreNoFormatsError.Value = value; } } public bool SkipDownload { get { return skipDownload.Value; } set { skipDownload.Value = value; } } public MultiValue Print { get { return print.Value; } set { print.Value = value; } } public MultiValue PrintToFile { get { return printToFile.Value; } set { printToFile.Value = value; } } public bool DumpJson { get { return dumpJson.Value; } set { dumpJson.Value = value; } } public bool DumpSingleJson { get { return dumpSingleJson.Value; } set { dumpSingleJson.Value = value; } } public bool ForceWriteArchive { get { return forceWriteArchive.Value; } set { forceWriteArchive.Value = value; } } public bool Newline { get { return newline.Value; } set { newline.Value = value; } } public bool NoProgress { get { return noProgress.Value; } set { noProgress.Value = value; } } public bool Progress { get { return progress.Value; } set { progress.Value = value; } } public bool ConsoleTitle { get { return consoleTitle.Value; } set { consoleTitle.Value = value; } } public string ProgressTemplate { get { return progressTemplate.Value; } set { progressTemplate.Value = value; } } public string ProgressDelta { get { return progressDelta.Value; } set { progressDelta.Value = value; } } public bool Verbose { get { return verbose.Value; } set { verbose.Value = value; } } public bool DumpPages { get { return dumpPages.Value; } set { dumpPages.Value = value; } } public bool WritePages { get { return writePages.Value; } set { writePages.Value = value; } } public bool PrintTraffic { get { return printTraffic.Value; } set { printTraffic.Value = value; } } public string Format { get { return format.Value; } set { format.Value = value; } } public string FormatSort { get { return formatSort.Value; } set { formatSort.Value = value; } } public bool FormatSortForce { get { return formatSortForce.Value; } set { formatSortForce.Value = value; } } public bool NoFormatSortForce { get { return noFormatSortForce.Value; } set { noFormatSortForce.Value = value; } } public bool VideoMultistreams { get { return videoMultistreams.Value; } set { videoMultistreams.Value = value; } } public bool NoVideoMultistreams { get { return noVideoMultistreams.Value; } set { noVideoMultistreams.Value = value; } } public bool AudioMultistreams { get { return audioMultistreams.Value; } set { audioMultistreams.Value = value; } } public bool NoAudioMultistreams { get { return noAudioMultistreams.Value; } set { noAudioMultistreams.Value = value; } } public bool PreferFreeFormats { get { return preferFreeFormats.Value; } set { preferFreeFormats.Value = value; } } public bool NoPreferFreeFormats { get { return noPreferFreeFormats.Value; } set { noPreferFreeFormats.Value = value; } } public bool CheckFormats { get { return checkFormats.Value; } set { checkFormats.Value = value; } } public bool CheckAllFormats { get { return checkAllFormats.Value; } set { checkAllFormats.Value = value; } } public bool NoCheckFormats { get { return noCheckFormats.Value; } set { noCheckFormats.Value = value; } } public bool ListFormats { get { return listFormats.Value; } set { listFormats.Value = value; } } public DownloadMergeFormat MergeOutputFormat { get { return mergeOutputFormat.Value; } set { mergeOutputFormat.Value = value; } } public string PlaylistItems { get { return playlistItems.Value; } set { playlistItems.Value = value; } } public string MinFilesize { get { return minFilesize.Value; } set { minFilesize.Value = value; } } public string MaxFilesize { get { return maxFilesize.Value; } set { maxFilesize.Value = value; } } public DateTime Date { get { return date.Value; } set { date.Value = value; } } public DateTime DateBefore { get { return dateBefore.Value; } set { dateBefore.Value = value; } } public DateTime DateAfter { get { return dateAfter.Value; } set { dateAfter.Value = value; } } public MultiValue MatchFilters { get { return matchFilters.Value; } set { matchFilters.Value = value; } } public bool NoMatchFilters { get { return noMatchFilters.Value; } set { noMatchFilters.Value = value; } } public string BreakMatchFilters { get { return breakMatchFilters.Value; } set { breakMatchFilters.Value = value; } } public bool NoBreakMatchFilters { get { return noBreakMatchFilters.Value; } set { noBreakMatchFilters.Value = value; } } public bool NoPlaylist { get { return noPlaylist.Value; } set { noPlaylist.Value = value; } } public bool YesPlaylist { get { return yesPlaylist.Value; } set { yesPlaylist.Value = value; } } public byte? AgeLimit { get { return ageLimit.Value; } set { ageLimit.Value = value; } } public string DownloadArchive { get { return downloadArchive.Value; } set { downloadArchive.Value = value; } } public bool NoDownloadArchive { get { return noDownloadArchive.Value; } set { noDownloadArchive.Value = value; } } public int? MaxDownloads { get { return maxDownloads.Value; } set { maxDownloads.Value = value; } } public bool BreakOnExisting { get { return breakOnExisting.Value; } set { breakOnExisting.Value = value; } } public bool NoBreakOnExisting { get { return noBreakOnExisting.Value; } set { noBreakOnExisting.Value = value; } } public bool BreakPerInput { get { return breakPerInput.Value; } set { breakPerInput.Value = value; } } public bool NoBreakPerInput { get { return noBreakPerInput.Value; } set { noBreakPerInput.Value = value; } } public int? SkipPlaylistAfterErrors { get { return skipPlaylistAfterErrors.Value; } set { skipPlaylistAfterErrors.Value = value; } } public string Encoding { get { return encoding.Value; } set { encoding.Value = value; } } public bool LegacyServerConnect { get { return legacyServerConnect.Value; } set { legacyServerConnect.Value = value; } } public bool NoCheckCertificates { get { return noCheckCertificates.Value; } set { noCheckCertificates.Value = value; } } public bool PreferInsecure { get { return preferInsecure.Value; } set { preferInsecure.Value = value; } } public MultiValue AddHeaders { get { return addHeaders.Value; } set { addHeaders.Value = value; } } public bool BidiWorkaround { get { return bidiWorkaround.Value; } set { bidiWorkaround.Value = value; } } public int? SleepRequests { get { return sleepRequests.Value; } set { sleepRequests.Value = value; } } public int? SleepInterval { get { return sleepInterval.Value; } set { sleepInterval.Value = value; } } public int? MaxSleepInterval { get { return maxSleepInterval.Value; } set { maxSleepInterval.Value = value; } } public int? SleepSubtitles { get { return sleepSubtitles.Value; } set { sleepSubtitles.Value = value; } } public void WriteConfigFile(string path) { File.WriteAllLines(path, GetOptionFlags()); } public override string ToString() { return " " + string.Join(" ", GetOptionFlags()); } public IEnumerable GetOptionFlags() { return from value in GetKnownOptions().Concat(CustomOptions).SelectMany((IOption opt) => opt.ToStringCollection()) where !string.IsNullOrWhiteSpace(value) select value; } internal IEnumerable GetKnownOptions() { return (from p in GetType().GetRuntimeFields() where p.FieldType.IsGenericType && p.FieldType.GetInterfaces().Contains(typeof(IOption)) select p.GetValue(this)).Cast(); } public OptionSet OverrideOptions(OptionSet overrideOptions, bool forceOverride = false) { OptionSet optionSet = (OptionSet)Clone(); optionSet.CustomOptions = optionSet.CustomOptions.Concat(overrideOptions.CustomOptions).Distinct(Comparer).ToArray(); foreach (FieldInfo item in from p in overrideOptions.GetType().GetRuntimeFields() where p.FieldType.IsGenericType && p.FieldType.GetInterfaces().Contains(typeof(IOption)) select p) { IOption option = (IOption)item.GetValue(overrideOptions); if (forceOverride || option.IsSet) { optionSet.GetType().GetField(item.Name, BindingFlags.Instance | BindingFlags.NonPublic).SetValue(optionSet, option); } } return optionSet; } public static OptionSet FromString(IEnumerable lines) { OptionSet optionSet = new OptionSet(); IOption[] customOptions = (from option in GetOptions(lines, optionSet.GetKnownOptions()) where option.IsCustom select option).ToArray(); optionSet.CustomOptions = customOptions; return optionSet; } private static IEnumerable GetOptions(IEnumerable lines, IEnumerable options) { IEnumerable knownOptions = options.ToList(); foreach (string line in lines) { string text = line.Trim(); if (!text.StartsWith("#") && !string.IsNullOrWhiteSpace(text)) { string[] array = text.Split(new char[1] { ' ' }); string flag = array[0]; IOption option = knownOptions.FirstOrDefault((IOption o) => o.OptionStrings.Contains(flag)); IOption option3; if (array.Length <= 1) { IOption option2 = new Option(true, flag); option3 = option2; } else { IOption option2 = new Option(true, flag); option3 = option2; } IOption option4 = option3; IOption option5 = option ?? option4; option5.SetFromString(text); yield return option5; } } } public static OptionSet LoadConfigFile(string path) { return FromString(File.ReadAllLines(path)); } public object Clone() { return FromString(GetOptionFlags()); } public void AddCustomOption(string optionString, T value) { Option option = new Option(true, optionString); option.Value = value; CustomOptions = CustomOptions.Concat(new Option[1] { option }).ToArray(); } public void SetCustomOption(string optionString, T value) { foreach (IOption item in CustomOptions.Where((IOption o) => o.OptionStrings.Contains(optionString))) { if (item is Option option) { option.Value = value; continue; } throw new ArgumentException($"Value passed to option '{optionString}' has invalid type '{value.GetType()}'."); } } public void DeleteCustomOption(string optionString) { CustomOptions = CustomOptions.Where((IOption o) => !o.OptionStrings.Contains(optionString)).ToArray(); } } public class StringVals { public readonly string[] Values; public StringVals(params string[] value) { Values = value; } public static implicit operator string[](StringVals p) { return p.Values; } public static implicit operator StringVals(string[] p) { return new StringVals(p); } public override string ToString() { return string.Join(" ", Values.Select((string v) => "\"" + v + "\"")); } public static bool operator ==(StringVals a, StringVals b) { if ((object)a == b) { return true; } if ((object)a == null || (object)b == null) { return false; } return a.Values.SequenceEqual(b.Values); } public static bool operator !=(StringVals a, StringVals b) { return !(a == b); } public override bool Equals(object obj) { if (obj is StringVals stringVals) { return this == stringVals; } return false; } public override int GetHashCode() { int num = 17; string[] values = Values; for (int i = 0; i < values.Length; i++) { num = num * 31 + (values[i]?.GetHashCode() ?? 0); } return num; } } internal static class Utils { internal static T OptionValueFromString(string stringValue) { if (typeof(T) == typeof(bool)) { return (T)(object)true; } if (typeof(T) == typeof(Enum)) { string value = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(stringValue); return (T)Enum.Parse(typeof(T), value); } if (typeof(T) == typeof(DateTime)) { return (T)(object)DateTime.ParseExact(stringValue, "yyyyMMdd", null); } if (typeof(T) == typeof(StringVals)) { return (T)(object)new StringVals((from v in stringValue.Split() select v.Trim(new char[1] { '"' })).ToArray()); } return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(stringValue); } internal static string OptionValueToString(T value) { if (value is bool) { return string.Empty; } if (value is Enum) { return " \"" + value.ToString().ToLower() + "\""; } if (value is DateTime dateTime) { return " " + dateTime.ToString("yyyyMMdd"); } if (value is StringVals) { return $" {value}"; } if (value is string) { return $" \"{value}\""; } T val = value; return " " + val; } } } namespace YoutubeDLSharp.Metadata { public class ChapterData { [JsonProperty("start_time")] public float? StartTime { get; set; } [JsonProperty("end_time")] public float? EndTime { get; set; } [JsonProperty("title")] public string Title { get; set; } } public class CommentData { [JsonProperty("id")] public string ID { get; set; } [JsonProperty("author")] public string Author { get; set; } [JsonProperty("author_id")] public string AuthorID { get; set; } [JsonProperty("author_thumbnail")] public string AuthorThumbnail { get; set; } [JsonProperty("html")] public string Html { get; set; } [JsonProperty("text")] public string Text { get; set; } [JsonProperty("timestamp")] [JsonConverter(typeof(UnixTimestampConverter))] public DateTime Timestamp { get; set; } [JsonProperty("parent")] public string Parent { get; set; } [JsonProperty("like_count")] public int? LikeCount { get; set; } [JsonProperty("dislike_count")] public int? DislikeCount { get; set; } [JsonProperty("is_favorited")] public bool? IsFavorited { get; set; } [JsonProperty("author_is_uploader")] public bool? AuthorIsUploader { get; set; } } public class FormatData { [JsonProperty("url")] public string Url { get; set; } [JsonProperty("manifest_url")] public string ManifestUrl { get; set; } [JsonProperty("ext")] public string Extension { get; set; } [JsonProperty("format")] public string Format { get; set; } [JsonProperty("format_id")] public string FormatId { get; set; } [JsonProperty("format_note")] public string FormatNote { get; set; } [JsonConverter(typeof(StringToNullableIntConverter))] [JsonProperty("width")] public int? Width { get; set; } [JsonConverter(typeof(StringToNullableIntConverter))] [JsonProperty("height")] public int? Height { get; set; } [JsonProperty("resolution")] public string Resolution { get; set; } [JsonProperty("dynamic_range")] public string DynamicRange { get; set; } [JsonProperty("tbr")] public double? Bitrate { get; set; } [JsonProperty("abr")] public double? AudioBitrate { get; set; } [JsonProperty("acodec")] public string AudioCodec { get; set; } [JsonProperty("asr")] public double? AudioSamplingRate { get; set; } [JsonConverter(typeof(StringToNullableIntConverter))] [JsonProperty("audio_channels")] public int? AudioChannels { get; set; } [JsonProperty("vbr")] public double? VideoBitrate { get; set; } [JsonProperty("fps")] public float? FrameRate { get; set; } [JsonProperty("vcodec")] public string VideoCodec { get; set; } [JsonProperty("container")] public string ContainerFormat { get; set; } [JsonProperty("filesize")] public long? FileSize { get; set; } [JsonProperty("filesize_approx")] public long? ApproximateFileSize { get; set; } [JsonProperty("player_url")] public string PlayerUrl { get; set; } [JsonProperty("protocol")] public string Protocol { get; set; } [JsonProperty("fragment_base_url")] public string FragmentBaseUrl { get; set; } [JsonProperty("is_from_start")] public bool? IsFromStart { get; set; } [JsonConverter(typeof(StringToNullableIntConverter))] [JsonProperty("preference")] public int? Preference { get; set; } [JsonProperty("language")] public string Language { get; set; } [JsonConverter(typeof(StringToNullableIntConverter))] [JsonProperty("language_preference")] public int? LanguagePreference { get; set; } [JsonProperty("quality")] public double? Quality { get; set; } [JsonConverter(typeof(StringToNullableIntConverter))] [JsonProperty("source_preference")] public int? SourcePreference { get; set; } [JsonProperty("stretched_ratio")] public float? StretchedRatio { get; set; } [JsonProperty("no_resume")] public bool? NoResume { get; set; } [JsonConverter(typeof(StringToEnumConverter))] [JsonProperty("has_drm")] public MaybeBool HasDRM { get; set; } public override string ToString() { return "[" + Extension + "] " + Format; } } public enum MetadataType { [EnumMember(Value = "video")] Video, [EnumMember(Value = "playlist")] Playlist, [EnumMember(Value = "multi_video")] MultiVideo, [EnumMember(Value = "url")] Url, [EnumMember(Value = "url_transparent")] UrlTransparent } public enum LiveStatus { [EnumMember(Value = "unknown")] None, [EnumMember(Value = "is_live")] IsLive, [EnumMember(Value = "is_upcoming")] IsUpcoming, [EnumMember(Value = "was_live")] WasLive, [EnumMember(Value = "not_live")] NotLive, [EnumMember(Value = "post_live")] PostLive } public enum Availability { [EnumMember(Value = "private")] Private, [EnumMember(Value = "premium_only")] PremiumOnly, [EnumMember(Value = "subscriber_only")] SubscriberOnly, [EnumMember(Value = "needs_auth")] NeedsAuth, [EnumMember(Value = "unlisted")] Unlisted, [EnumMember(Value = "public")] Public } public enum MaybeBool { [EnumMember(Value = "False")] False, [EnumMember(Value = "maybe")] Maybe, [EnumMember(Value = "True")] True } public class SubtitleData { [JsonProperty("ext")] public string Ext { get; set; } [JsonProperty("data")] public string Data { get; set; } [JsonProperty("url")] public string Url { get; set; } [JsonProperty("name")] public string Name { get; set; } } public class ThumbnailData { [JsonProperty("id")] public string ID { get; set; } [JsonProperty("url")] public string Url { get; set; } [JsonProperty("preference")] public int? Preference { get; set; } [JsonProperty("width")] public int? Width { get; set; } [JsonProperty("height")] public int? Height { get; set; } [JsonProperty("resolution")] public string Resolution { get; set; } [JsonProperty("filesize")] public int? Filesize { get; set; } } public class VideoData { [JsonConverter(typeof(StringToEnumConverter))] [JsonProperty("_type")] public MetadataType ResultType { get; set; } [JsonProperty("extractor")] public string Extractor { get; set; } [JsonProperty("extractor_key")] public string ExtractorKey { get; set; } [JsonProperty("entries")] public VideoData[] Entries { get; set; } [JsonProperty("id")] public string ID { get; set; } [JsonProperty("title")] public string Title { get; set; } [JsonProperty("formats")] public FormatData[] Formats { get; set; } [JsonProperty("url")] public string Url { get; set; } [JsonProperty("ext")] public string Extension { get; set; } [JsonProperty("format")] public string Format { get; set; } [JsonProperty("format_id")] public string FormatID { get; set; } [JsonProperty("player_url")] public string PlayerUrl { get; set; } [JsonProperty("direct")] public bool Direct { get; set; } [JsonProperty("alt_title")] public string AltTitle { get; set; } [JsonProperty("display_id")] public string DisplayID { get; set; } [JsonProperty("thumbnails")] public ThumbnailData[] Thumbnails { get; set; } [JsonProperty("thumbnail")] public string Thumbnail { get; set; } [JsonProperty("description")] public string Description { get; set; } [JsonProperty("uploader")] public string Uploader { get; set; } [JsonProperty("license")] public string License { get; set; } [JsonProperty("creator")] public string Creator { get; set; } [JsonConverter(typeof(UnixTimestampConverter))] [JsonProperty("release_timestamp")] public DateTime? ReleaseTimestamp { get; set; } [JsonConverter(typeof(CustomDateTimeConverter))] [JsonProperty("release_date")] public DateTime? ReleaseDate { get; set; } [JsonConverter(typeof(UnixTimestampConverter))] [JsonProperty("timestamp")] public DateTime? Timestamp { get; set; } [JsonConverter(typeof(CustomDateTimeConverter))] [JsonProperty("upload_date")] public DateTime? UploadDate { get; set; } [JsonConverter(typeof(UnixTimestampConverter))] [JsonProperty("modified_timestamp")] public DateTime? ModifiedTimestamp { get; set; } [JsonConverter(typeof(CustomDateTimeConverter))] [JsonProperty("modified_date")] public DateTime? ModifiedDate { get; set; } [JsonProperty("uploader_id")] public string UploaderID { get; set; } [JsonProperty("uploader_url")] public string UploaderUrl { get; set; } [JsonProperty("channel")] public string Channel { get; set; } [JsonProperty("channel_id")] public string ChannelID { get; set; } [JsonProperty("channel_url")] public string ChannelUrl { get; set; } [JsonProperty("channel_follower_count")] public long? ChannelFollowerCount { get; set; } [JsonProperty("location")] public string Location { get; set; } [JsonProperty("subtitles")] public Dictionary Subtitles { get; set; } [JsonProperty("automatic_captions")] public Dictionary AutomaticCaptions { get; set; } [JsonProperty("duration")] public float? Duration { get; set; } [JsonProperty("view_count")] public long? ViewCount { get; set; } [JsonProperty("concurrent_view_count")] public long? ConcurrentViewCount { get; set; } [JsonProperty("like_count")] public long? LikeCount { get; set; } [JsonProperty("dislike_count")] public long? DislikeCount { get; set; } [JsonProperty("repost_count")] public long? RepostCount { get; set; } [JsonProperty("average_rating")] public double? AverageRating { get; set; } [JsonProperty("comment_count")] public long? CommentCount { get; set; } [JsonProperty("comments")] public CommentData[] Comments { get; set; } [JsonProperty("age_limit")] public int? AgeLimit { get; set; } [JsonProperty("webpage_url")] public string WebpageUrl { get; set; } [JsonProperty("categories")] public string[] Categories { get; set; } [JsonProperty("tags")] public string[] Tags { get; set; } [JsonProperty("cast")] public string[] Cast { get; set; } [JsonProperty("is_live")] public bool? IsLive { get; set; } [JsonProperty("was_live")] public bool? WasLive { get; set; } [JsonConverter(typeof(StringToEnumConverter))] [JsonProperty("live_status")] public LiveStatus LiveStatus { get; set; } [JsonProperty("start_time")] public float? StartTime { get; set; } [JsonProperty("end_time")] public float? EndTime { get; set; } [JsonProperty("playable_in_embed")] public string PlayableInEmbed { get; set; } [JsonConverter(typeof(StringEnumConverter))] [JsonProperty("availability")] public Availability? Availability { get; set; } [JsonProperty("chapters")] public ChapterData[] Chapters { get; set; } [JsonProperty("chapter")] public string Chapter { get; set; } [JsonProperty("chapter_number")] public int? ChapterNumber { get; set; } [JsonProperty("chapter_id")] public string ChapterId { get; set; } [JsonProperty("series")] public string Series { get; set; } [JsonProperty("series_id")] public string SeriesId { get; set; } [JsonProperty("season")] public string Season { get; set; } [JsonProperty("season_number")] public int? SeasonNumber { get; set; } [JsonProperty("season_id")] public string SeasonId { get; set; } [JsonProperty("episode")] public string Episode { get; set; } [JsonProperty("episode_number")] public int? EpisodeNumber { get; set; } [JsonProperty("episode_id")] public string EpisodeId { get; set; } [JsonProperty("track")] public string Track { get; set; } [JsonProperty("track_number")] public int? TrackNumber { get; set; } [JsonProperty("track_id")] public string TrackId { get; set; } [JsonProperty("artist")] public string Artist { get; set; } [JsonProperty("genre")] public string Genre { get; set; } [JsonProperty("album")] public string Album { get; set; } [JsonProperty("album_type")] public string AlbumType { get; set; } [JsonProperty("album_artist")] public string AlbumArtist { get; set; } [JsonProperty("disc_number")] public int? DiscNumber { get; set; } [JsonProperty("release_year")] public string ReleaseYear { get; set; } [JsonProperty("composer")] public string Composer { get; set; } [JsonProperty("section_start")] public long? SectionStart { get; set; } [JsonProperty("section_end")] public long? SectionEnd { get; set; } [JsonProperty("rows")] public long? StoryboardFragmentRows { get; set; } [JsonProperty("columns")] public long? StoryboardFragmentColumns { get; set; } public override string ToString() { return JsonConvert.SerializeObject((object)this, (Formatting)1); } } } namespace YoutubeDLSharp.Helpers { internal static class OSHelper { public static bool IsWindows => GetOSVersion() == OSVersion.Windows; internal static OSVersion GetOSVersion() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return OSVersion.Windows; } if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { return OSVersion.OSX; } if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { return OSVersion.Linux; } throw new Exception("Your OS isn't supported"); } } internal enum OSVersion { Windows, OSX, Linux } internal static class ProcessExtensions { private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30.0); public static void KillTree(this Process process) { process.KillTree(_defaultTimeout); } public static void KillTree(this Process process, TimeSpan timeout) { if (OSHelper.IsWindows) { RunProcessAndWaitForExit("taskkill", $"/T /F /PID {process.Id}", timeout, out var _); return; } HashSet hashSet = new HashSet(); GetAllChildIdsUnix(process.Id, hashSet, timeout); foreach (int item in hashSet) { KillProcessUnix(item, timeout); } KillProcessUnix(process.Id, timeout); } private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpan timeout) { if (RunProcessAndWaitForExit("pgrep", $"-P {parentId}", timeout, out var stdout) != 0 || string.IsNullOrEmpty(stdout)) { return; } using StringReader stringReader = new StringReader(stdout); while (true) { string text = stringReader.ReadLine(); if (text == null) { break; } if (int.TryParse(text, out var result)) { children.Add(result); GetAllChildIdsUnix(result, children, timeout); } } } private static void KillProcessUnix(int processId, TimeSpan timeout) { RunProcessAndWaitForExit("kill", $"-TERM {processId}", timeout, out var _); } private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string stdout) { Process process = Process.Start(new ProcessStartInfo { FileName = fileName, Arguments = arguments, RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true }); stdout = null; if (process.WaitForExit((int)timeout.TotalMilliseconds)) { stdout = process.StandardOutput.ReadToEnd(); } else { process.Kill(); } return process.ExitCode; } } public class ProcessRunner { private const int MAX_COUNT = 100; private SemaphoreSlim semaphore; public byte TotalCount { get; private set; } public ProcessRunner(byte initialCount) { semaphore = new SemaphoreSlim(initialCount, 100); TotalCount = initialCount; } public async Task<(int, string[])> RunThrottled(YoutubeDLProcess process, string[] urls, OptionSet options, CancellationToken ct, IProgress progress = null) { List errors = new List(); process.ErrorReceived += delegate(object o, DataReceivedEventArgs e) { errors.Add(e.Data); }; await semaphore.WaitAsync(ct); try { return (await process.RunAsync(urls, options, ct, progress), errors.ToArray()); } finally { semaphore.Release(); } } private void incrementCount(byte incr) { semaphore.Release(incr); TotalCount += incr; } private async Task decrementCount(byte decr) { Task[] array = new Task[decr]; for (int i = 0; i < decr; i++) { array[i] = semaphore.WaitAsync(); } TotalCount -= decr; await Task.WhenAll(array); } public async Task SetTotalCount(byte count) { if (count < 1 || count > 100) { throw new ArgumentException($"Number of threads must be between 1 and {100}."); } if (count > TotalCount) { incrementCount((byte)(count - TotalCount)); } else if (count < TotalCount) { await decrementCount((byte)(TotalCount - count)); } } } } namespace YoutubeDLSharp.Converters { public class UnixTimestampConverter : JsonConverter { private readonly DateTime _Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public override DateTime? ReadJson(JsonReader reader, Type objectType, DateTime? existingValue, bool hasExistingValue, JsonSerializer serializer) { if (reader.Value == null) { return null; } TimeSpan value = TimeSpan.FromSeconds(Convert.ToDouble(reader.Value)); DateTime epoch = _Epoch; return epoch.Add(value).ToUniversalTime(); } public override void WriteJson(JsonWriter writer, DateTime? value, JsonSerializer serializer) { writer.WriteValue(value?.Subtract(_Epoch).TotalSeconds.ToString()); } } public class CustomDateTimeConverter : IsoDateTimeConverter { public CustomDateTimeConverter() { ((IsoDateTimeConverter)this).DateTimeFormat = "yyyyMMdd"; } } public class StringToEnumConverter : JsonConverter where T : Enum { public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown string text = reader.Value?.ToString(); if (text == null) { return default(T); } return JsonConvert.DeserializeObject("'" + text.ToLower() + "'", (JsonConverter[])(object)new JsonConverter[1] { (JsonConverter)new StringEnumConverter() }); } public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer) { writer.WriteValue(value.ToString()); } } public class StringToNullableIntConverter : JsonConverter { public override int? ReadJson(JsonReader reader, Type objectType, int? existingValue, bool hasExistingValue, JsonSerializer serializer) { string text = reader.Value?.ToString(); if (text == null) { return null; } if (text.Contains(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator)) { text = text.Split(new char[1] { Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator) })[0]; } if (int.TryParse(text, out var result)) { return result; } return null; } public override void WriteJson(JsonWriter writer, int? value, JsonSerializer serializer) { writer.WriteValue(value); } } }