Decompiled source of YoutubeDLSharp v1.1.0
BepInEx/plugins/YoutubeDLSharp.dll
Decompiled a year ago
The result has been truncated due to the large size, download it to view full contents!
#define DEBUG 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.Security; using System.Security.Permissions; 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.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Bluegrams")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyCopyright("© 2020-2024 Bluegrams")] [assembly: AssemblyDescription("\n\t\tA simple .NET wrapper library for youtube-dl and yt-dlp.\n\nNote: Package versions >= 1.0 are optimized to work with yt-dlp.\nPackage versions 0.x retain support for the original youtube-dl.\n\t")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0+febe9e5d0ffbe54055cac081271280501bd308d4")] [assembly: AssemblyProduct("YoutubeDLSharp")] [assembly: AssemblyTitle("YoutubeDLSharp")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Lordfirespeed/YoutubeDLSharpThunderstore")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] 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<T> { 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 HttpClient _client = new HttpClient(); private static readonly Regex rgxTimestamp = new Regex("[0-9]+(?::[0-9]+)+", RegexOptions.Compiled); private static readonly Dictionary<char, string> 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); 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 < ' ' || c == '\u007f') { return ""; } switch (c) { case '"': return restricted ? "" : "'"; case ':': return restricted ? "_-" : " -"; default: if (Enumerable.Contains("\\/|*<>", c)) { return "_"; } if (restricted && Enumerable.Contains("!&'()[]{}$;`^,# ", c)) { return "_"; } if (restricted && c > '\u007f') { return "_"; } return c.ToString(); } } public static string GetFullPath(string fileName) { if (File.Exists(fileName)) { return Path.GetFullPath(fileName); } string environmentVariable = Environment.GetEnvironmentVariable("PATH"); string[] array = environmentVariable.Split(new char[1] { Path.PathSeparator }); foreach (string path in array) { string text = Path.Combine(path, fileName); if (File.Exists(text)) { return text; } } return null; } public static async Task DownloadBinaries(bool skipExisting = true, string directoryPath = "") { 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); } } else { await DownloadYtDlp(directoryPath); await DownloadFFmpeg(directoryPath); await DownloadFFprobe(directoryPath); } } 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"), }; } private static string GetYtDlpBinaryName() { string ytDlpDownloadUrl = GetYtDlpDownloadUrl(); return Path.GetFileName(ytDlpDownloadUrl); } 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"); } } public static async Task DownloadYtDlp(string directoryPath = "") { string downloadUrl = GetYtDlpDownloadUrl(); if (string.IsNullOrEmpty(directoryPath)) { directoryPath = Directory.GetCurrentDirectory(); } string downloadLocation = Path.Combine(directoryPath, Path.GetFileName(downloadUrl)); File.WriteAllBytes(downloadLocation, await DownloadFileBytesAsync(downloadUrl)); } 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 async Task FFDownloader(string directoryPath = "", FFmpegApi.BinaryType binary = FFmpegApi.BinaryType.FFmpeg) { if (string.IsNullOrEmpty(directoryPath)) { directoryPath = Directory.GetCurrentDirectory(); } FFmpegApi.Root ffmpegVersion = JsonConvert.DeserializeObject<FFmpegApi.Root>(await (await _client.GetAsync("https://ffbinaries.com/api/v1/version/latest")).Content.ReadAsStringAsync()); FFmpegApi.OsBinVersion ffContent = OSHelper.GetOSVersion() switch { OSVersion.Windows => ffmpegVersion?.Bin.Windows64, OSVersion.OSX => ffmpegVersion?.Bin.Osx64, OSVersion.Linux => ffmpegVersion?.Bin.Linux64, _ => throw new NotImplementedException("Your OS isn't supported"), }; string downloadUrl = ((binary == FFmpegApi.BinaryType.FFmpeg) ? ffContent.Ffmpeg : ffContent.Ffprobe); using MemoryStream stream = new MemoryStream(await DownloadFileBytesAsync(downloadUrl)); using ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Read); if (archive.Entries.Count > 0) { archive.Entries[0].ExtractToFile(Path.Combine(directoryPath, archive.Entries[0].FullName), overwrite: true); } } private static async Task<byte[]> DownloadFileBytesAsync(string uri) { if (!Uri.TryCreate(uri, UriKind.Absolute, out Uri _)) { throw new InvalidOperationException("URI is invalid."); } return await _client.GetByteArrayAsync(uri); } public static void EnsureSuccess<T>(this RunResult<T> 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; } = false; public bool OverwriteFiles { get; set; } = true; public bool IgnoreDownloadErrors { get; set; } = true; public string PythonInterpreterPath { get; set; } = null; 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<RunResult<string[]>> RunWithOptions(string[] urls, OptionSet options, CancellationToken ct) { List<string> output = new List<string>(); YoutubeDLProcess process = CreateYoutubeDLProcess(); process.OutputReceived += delegate(object o, DataReceivedEventArgs e) { output.Add(e.Data); }; var (code, errors) = await runner.RunThrottled(process, urls, options, ct); return new RunResult<string[]>(code == 0, errors, output.ToArray()); } public async Task<RunResult<string>> RunWithOptions(string url, OptionSet options, CancellationToken ct = default(CancellationToken), IProgress<DownloadProgress> progress = null, IProgress<string> output = null, bool showArgs = true) { string outFile = string.Empty; YoutubeDLProcess process = CreateYoutubeDLProcess(); if (showArgs) { output?.Report("Arguments: " + process.ConvertToArgs(new string[1] { url }, options) + "\n"); } else { output?.Report("Starting Download: " + url); } process.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 (code, errors) = await runner.RunThrottled(process, new string[1] { url }, options, ct, progress); return new RunResult<string>(code == 0, errors, outFile); } public async Task<string> RunUpdate() { string output = string.Empty; YoutubeDLProcess process = CreateYoutubeDLProcess(); process.OutputReceived += delegate(object o, DataReceivedEventArgs e) { output = e.Data; }; await process.RunAsync(null, new OptionSet { Update = true }); return output; } public async Task<RunResult<VideoData>> RunVideoDataFetch(string url, CancellationToken ct = default(CancellationToken), bool flat = true, bool fetchComments = false, OptionSet overrideOptions = null) { OptionSet opts = GetDownloadOptions(); opts.DumpSingleJson = true; opts.FlatPlaylist = flat; opts.WriteComments = fetchComments; if (overrideOptions != null) { opts = opts.OverrideOptions(overrideOptions); } VideoData videoData = null; YoutubeDLProcess process = CreateYoutubeDLProcess(); process.OutputReceived += delegate(object o, DataReceivedEventArgs e) { try { videoData = JsonConvert.DeserializeObject<VideoData>(e.Data); } catch (JsonSerializationException) { process.RedirectToError(e); } }; var (code, errors) = await runner.RunThrottled(process, new string[1] { url }, opts, ct); return new RunResult<VideoData>(code == 0, errors, videoData); } public async Task<RunResult<string>> RunVideoDownload(string url, string format = "bestvideo+bestaudio/best", DownloadMergeFormat mergeFormat = DownloadMergeFormat.Unspecified, VideoRecodeFormat recodeFormat = VideoRecodeFormat.None, CancellationToken ct = default(CancellationToken), IProgress<DownloadProgress> progress = null, IProgress<string> output = null, OptionSet overrideOptions = null) { OptionSet opts = GetDownloadOptions(); opts.Format = format; opts.MergeOutputFormat = mergeFormat; opts.RecodeVideo = recodeFormat; if (overrideOptions != null) { opts = opts.OverrideOptions(overrideOptions); } string outputFile = string.Empty; YoutubeDLProcess process = CreateYoutubeDLProcess(); output?.Report("Arguments: " + process.ConvertToArgs(new string[1] { url }, opts) + "\n"); process.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 (code, errors) = await runner.RunThrottled(process, new string[1] { url }, opts, ct, progress); return new RunResult<string>(code == 0, errors, outputFile); } public async Task<RunResult<string[]>> 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<DownloadProgress> progress = null, IProgress<string> output = null, OptionSet overrideOptions = null) { OptionSet opts = GetDownloadOptions(); opts.NoPlaylist = false; opts.PlaylistStart = start; opts.PlaylistEnd = end; if (items != null) { opts.PlaylistItems = string.Join(",", items); } opts.Format = format; opts.RecodeVideo = recodeFormat; if (overrideOptions != null) { opts = opts.OverrideOptions(overrideOptions); } List<string> outputFiles = new List<string>(); YoutubeDLProcess process = CreateYoutubeDLProcess(); output?.Report("Arguments: " + process.ConvertToArgs(new string[1] { url }, opts) + "\n"); process.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 (code, errors) = await runner.RunThrottled(process, new string[1] { url }, opts, ct, progress); return new RunResult<string[]>(code == 0, errors, outputFiles.ToArray()); } public async Task<RunResult<string>> RunAudioDownload(string url, AudioConversionFormat format = AudioConversionFormat.Best, CancellationToken ct = default(CancellationToken), IProgress<DownloadProgress> progress = null, IProgress<string> output = null, OptionSet overrideOptions = null) { OptionSet opts = GetDownloadOptions(); opts.Format = "bestaudio/best"; opts.ExtractAudio = true; opts.AudioFormat = format; if (overrideOptions != null) { opts = opts.OverrideOptions(overrideOptions); } string outputFile = string.Empty; new List<string>(); YoutubeDLProcess process = CreateYoutubeDLProcess(); output?.Report("Arguments: " + process.ConvertToArgs(new string[1] { url }, opts) + "\n"); process.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 (code, errors) = await runner.RunThrottled(process, new string[1] { url }, opts, ct, progress); return new RunResult<string>(code == 0, errors, outputFile); } public async Task<RunResult<string[]>> RunAudioPlaylistDownload(string url, int? start = 1, int? end = null, int[] items = null, AudioConversionFormat format = AudioConversionFormat.Best, CancellationToken ct = default(CancellationToken), IProgress<DownloadProgress> progress = null, IProgress<string> output = null, OptionSet overrideOptions = null) { List<string> outputFiles = new List<string>(); OptionSet opts = GetDownloadOptions(); opts.NoPlaylist = false; opts.PlaylistStart = start; opts.PlaylistEnd = end; if (items != null) { opts.PlaylistItems = string.Join(",", items); } opts.Format = "bestaudio/best"; opts.ExtractAudio = true; opts.AudioFormat = format; if (overrideOptions != null) { opts = opts.OverrideOptions(overrideOptions); } YoutubeDLProcess process = CreateYoutubeDLProcess(); output?.Report("Arguments: " + process.ConvertToArgs(new string[1] { url }, opts) + "\n"); process.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 (code, errors) = await runner.RunThrottled(process, new string[1] { url }, opts, ct, progress); return new RunResult<string[]>(code == 0, errors, 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), Exec = "echo outfile: {}" }; } 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+(?:(?<percent>[\\d\\.]+)%(?:\\s+of\\s+\\~?\\s*(?<total>[\\d\\.\\w]+))?\\s+at\\s+(?:(?<speed>[\\d\\.\\w]+\\/s)|[\\w\\s]+)\\s+ETA\\s(?<eta>[\\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 bool UseWindowsEncodingWorkaround { get; set; } = true; public event EventHandler<DataReceivedEventArgs> OutputReceived; public event EventHandler<DataReceivedEventArgs> 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<int> RunAsync(string[] urls, OptionSet options) { return await RunAsync(urls, options, CancellationToken.None); } public async Task<int> RunAsync(string[] urls, OptionSet options, CancellationToken ct, IProgress<DownloadProgress> progress = null) { TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); Process process = new Process(); ProcessStartInfo startInfo = new ProcessStartInfo { CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 }; if (OSHelper.IsWindows && UseWindowsEncodingWorkaround) { startInfo.FileName = "cmd.exe"; string runCommand = (string.IsNullOrEmpty(PythonPath) ? ("\"" + ExecutablePath + "\" " + ConvertToArgs(urls, options)) : ("\"" + PythonPath + "\" \"" + ExecutablePath + "\" " + ConvertToArgs(urls, options))); startInfo.Arguments = "/C chcp 65001 >nul 2>&1 && " + runCommand; } else if (!string.IsNullOrEmpty(PythonPath)) { startInfo.FileName = PythonPath; startInfo.Arguments = "\"" + ExecutablePath + "\" " + ConvertToArgs(urls, options); } else { startInfo.FileName = ExecutablePath; startInfo.Arguments = ConvertToArgs(urls, options); } process.EnableRaisingEvents = true; process.StartInfo = startInfo; TaskCompletionSource<bool> tcsOut = new TaskCompletionSource<bool>(); 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 group = match.Groups["total"]; string totalDownloadSize = (group.Success ? group.Value : null); Group group2 = match.Groups["speed"]; string downloadSpeed = (group2.Success ? group2.Value : null); Group group3 = match.Groups["eta"]; string eta = (group3.Success ? group3.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; } Debug.WriteLine("[yt-dlp] " + e.Data); this.OutputReceived?.Invoke(this, e); } }; TaskCompletionSource<bool> tcsError = new TaskCompletionSource<bool>(); process.ErrorDataReceived += delegate(object o, DataReceivedEventArgs e) { if (e.Data == null) { tcsError.SetResult(result: true); } else { Debug.WriteLine("[yt-dlp ERROR] " + e.Data); 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 { } }); Debug.WriteLine("[yt-dlp] Arguments: " + process.StartInfo.Arguments); 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<string> ToStringCollection(); } public class MultiOption<T> : IOption { private MultiValue<T> value; public string DefaultOptionString => OptionStrings.Last(); public string[] OptionStrings { get; } public bool IsSet { get; private set; } public bool IsCustom { get; } public MultiValue<T> 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 stringValue = s.Substring(array[0].Length).Trim().Trim(new char[1] { '"' }); if (!OptionStrings.Contains(array[0])) { throw new ArgumentException("Given string does not match required format."); } T val = Utils.OptionValueFromString<T>(stringValue); if (!IsSet) { Value = val; } else { Value.Values.Add(val); } } public override string ToString() { return string.Join(" ", ToStringCollection()); } public IEnumerable<string> ToStringCollection() { if (!IsSet) { return new string[1] { "" }; } List<string> list = new List<string>(); foreach (T value in Value.Values) { list.Add(DefaultOptionString + Utils.OptionValueToString(value)); } return list; } } public class MultiValue<T> { private readonly List<T> values; public List<T> Values => values; public MultiValue(params T[] values) { this.values = values.ToList(); } public static implicit operator MultiValue<T>(T value) { return new MultiValue<T>(value); } public static implicit operator MultiValue<T>(T[] values) { return new MultiValue<T>(values); } public static explicit operator T(MultiValue<T> 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<T> value) { return value.Values.ToArray(); } } public class Option<T> : 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 stringValue = s.Substring(array[0].Length).Trim().Trim(new char[1] { '"' }); if (!OptionStrings.Contains(array[0])) { throw new ArgumentException("Given string does not match required format."); } Value = Utils.OptionValueFromString<T>(stringValue); } public override string ToString() { if (!IsSet) { return string.Empty; } string text = Utils.OptionValueToString(Value); return DefaultOptionString + text; } public IEnumerable<string> ToStringCollection() { return new string[1] { ToString() }; } } internal class OptionComparer : IEqualityComparer<IOption> { public bool Equals(IOption x, IOption y) { if (x != null) { return y != null && x.ToString().Equals(y.ToString()); } return y == null; } public int GetHashCode(IOption obj) { return obj.ToString().GetHashCode(); } } public class OptionSet : ICloneable { private Option<string> username = new Option<string>("-u", "--username"); private Option<string> password = new Option<string>("-p", "--password"); private Option<string> twoFactor = new Option<string>("-2", "--twofactor"); private Option<bool> netrc = new Option<bool>("-n", "--netrc"); private Option<string> netrcLocation = new Option<string>("--netrc-location"); private Option<string> netrcCmd = new Option<string>("--netrc-cmd"); private Option<string> videoPassword = new Option<string>("--video-password"); private Option<string> apMso = new Option<string>("--ap-mso"); private Option<string> apUsername = new Option<string>("--ap-username"); private Option<string> apPassword = new Option<string>("--ap-password"); private Option<bool> apListMso = new Option<bool>("--ap-list-mso"); private Option<string> clientCertificate = new Option<string>("--client-certificate"); private Option<string> clientCertificateKey = new Option<string>("--client-certificate-key"); private Option<string> clientCertificatePassword = new Option<string>("--client-certificate-password"); private static readonly OptionComparer Comparer = new OptionComparer(); public static readonly OptionSet Default = new OptionSet(); private Option<bool> getDescription = new Option<bool>("--get-description"); private Option<bool> getDuration = new Option<bool>("--get-duration"); private Option<bool> getFilename = new Option<bool>("--get-filename"); private Option<bool> getFormat = new Option<bool>("--get-format"); private Option<bool> getId = new Option<bool>("--get-id"); private Option<bool> getThumbnail = new Option<bool>("--get-thumbnail"); private Option<bool> getTitle = new Option<bool>("-e", "--get-title"); private Option<bool> getUrl = new Option<bool>("-g", "--get-url"); private Option<string> matchTitle = new Option<string>("--match-title"); private Option<string> rejectTitle = new Option<string>("--reject-title"); private Option<long?> minViews = new Option<long?>("--min-views"); private Option<long?> maxViews = new Option<long?>("--max-views"); private Option<bool> breakOnReject = new Option<bool>("--break-on-reject"); private Option<string> userAgent = new Option<string>("--user-agent"); private Option<string> referer = new Option<string>("--referer"); private Option<int?> playlistStart = new Option<int?>("--playlist-start"); private Option<int?> playlistEnd = new Option<int?>("--playlist-end"); private Option<bool> playlistReverse = new Option<bool>("--playlist-reverse"); private Option<bool> noColors = new Option<bool>("--no-colors"); private Option<bool> forceGenericExtractor = new Option<bool>("--force-generic-extractor"); private Option<string> execBeforeDownload = new Option<string>("--exec-before-download"); private Option<bool> noExecBeforeDownload = new Option<bool>("--no-exec-before-download"); private Option<bool> allFormats = new Option<bool>("--all-formats"); private Option<bool> allSubs = new Option<bool>("--all-subs"); private Option<bool> printJson = new Option<bool>("--print-json"); private Option<string> autonumberSize = new Option<string>("--autonumber-size"); private Option<int?> autonumberStart = new Option<int?>("--autonumber-start"); private Option<bool> id = new Option<bool>("--id"); private Option<string> metadataFromTitle = new Option<string>("--metadata-from-title"); private Option<bool> hlsPreferNative = new Option<bool>("--hls-prefer-native"); private Option<bool> hlsPreferFfmpeg = new Option<bool>("--hls-prefer-ffmpeg"); private Option<bool> listFormatsOld = new Option<bool>("--list-formats-old", "--no-list-formats-as-table"); private Option<bool> listFormatsAsTable = new Option<bool>("--list-formats-as-table", "--no-list-formats-old"); private Option<bool> youtubeSkipDashManifest = new Option<bool>("--youtube-skip-dash-manifest", "--no-youtube-include-dash-manifest"); private Option<bool> youtubeSkipHlsManifest = new Option<bool>("--youtube-skip-hls-manifest", "--no-youtube-include-hls-manifest"); private Option<bool> geoBypass = new Option<bool>("--geo-bypass"); private Option<bool> noGeoBypass = new Option<bool>("--no-geo-bypass"); private Option<string> geoBypassCountry = new Option<string>("--geo-bypass-country"); private Option<string> geoBypassIpBlock = new Option<string>("--geo-bypass-ip-block"); private Option<int?> concurrentFragments = new Option<int?>("-N", "--concurrent-fragments"); private Option<long?> limitRate = new Option<long?>("-r", "--limit-rate"); private Option<long?> throttledRate = new Option<long?>("--throttled-rate"); private Option<int?> retries = new Option<int?>("-R", "--retries"); private Option<int?> fileAccessRetries = new Option<int?>("--file-access-retries"); private Option<int?> fragmentRetries = new Option<int?>("--fragment-retries"); private MultiOption<string> retrySleep = new MultiOption<string>("--retry-sleep"); private Option<bool> skipUnavailableFragments = new Option<bool>("--skip-unavailable-fragments", "--no-abort-on-unavailable-fragments"); private Option<bool> abortOnUnavailableFragments = new Option<bool>("--abort-on-unavailable-fragments", "--no-skip-unavailable-fragments"); private Option<bool> keepFragments = new Option<bool>("--keep-fragments"); private Option<bool> noKeepFragments = new Option<bool>("--no-keep-fragments"); private Option<long?> bufferSize = new Option<long?>("--buffer-size"); private Option<bool> resizeBuffer = new Option<bool>("--resize-buffer"); private Option<bool> noResizeBuffer = new Option<bool>("--no-resize-buffer"); private Option<long?> httpChunkSize = new Option<long?>("--http-chunk-size"); private Option<bool> playlistRandom = new Option<bool>("--playlist-random"); private Option<bool> lazyPlaylist = new Option<bool>("--lazy-playlist"); private Option<bool> noLazyPlaylist = new Option<bool>("--no-lazy-playlist"); private Option<bool> xattrSetFilesize = new Option<bool>("--xattr-set-filesize"); private Option<bool> hlsUseMpegts = new Option<bool>("--hls-use-mpegts"); private Option<bool> noHlsUseMpegts = new Option<bool>("--no-hls-use-mpegts"); private MultiOption<string> downloadSections = new MultiOption<string>("--download-sections"); private MultiOption<string> downloader = new MultiOption<string>("--downloader", "--external-downloader"); private MultiOption<string> downloaderArgs = new MultiOption<string>("--downloader-args", "--external-downloader-args"); private Option<int?> extractorRetries = new Option<int?>("--extractor-retries"); private Option<bool> allowDynamicMpd = new Option<bool>("--allow-dynamic-mpd", "--no-ignore-dynamic-mpd"); private Option<bool> ignoreDynamicMpd = new Option<bool>("--ignore-dynamic-mpd", "--no-allow-dynamic-mpd"); private Option<bool> hlsSplitDiscontinuity = new Option<bool>("--hls-split-discontinuity"); private Option<bool> noHlsSplitDiscontinuity = new Option<bool>("--no-hls-split-discontinuity"); private MultiOption<string> extractorArgs = new MultiOption<string>("--extractor-args"); private Option<string> batchFile = new Option<string>("-a", "--batch-file"); private Option<bool> noBatchFile = new Option<bool>("--no-batch-file"); private Option<string> paths = new Option<string>("-P", "--paths"); private Option<string> output = new Option<string>("-o", "--output"); private Option<string> outputNaPlaceholder = new Option<string>("--output-na-placeholder"); private Option<bool> restrictFilenames = new Option<bool>("--restrict-filenames"); private Option<bool> noRestrictFilenames = new Option<bool>("--no-restrict-filenames"); private Option<bool> windowsFilenames = new Option<bool>("--windows-filenames"); private Option<bool> noWindowsFilenames = new Option<bool>("--no-windows-filenames"); private Option<int?> trimFilenames = new Option<int?>("--trim-filenames"); private Option<bool> noOverwrites = new Option<bool>("-w", "--no-overwrites"); private Option<bool> forceOverwrites = new Option<bool>("--force-overwrites"); private Option<bool> noForceOverwrites = new Option<bool>("--no-force-overwrites"); private Option<bool> doContinue = new Option<bool>("-c", "--continue"); private Option<bool> noContinue = new Option<bool>("--no-continue"); private Option<bool> part = new Option<bool>("--part"); private Option<bool> noPart = new Option<bool>("--no-part"); private Option<bool> mtime = new Option<bool>("--mtime"); private Option<bool> noMtime = new Option<bool>("--no-mtime"); private Option<bool> writeDescription = new Option<bool>("--write-description"); private Option<bool> noWriteDescription = new Option<bool>("--no-write-description"); private Option<bool> writeInfoJson = new Option<bool>("--write-info-json"); private Option<bool> noWriteInfoJson = new Option<bool>("--no-write-info-json"); private Option<bool> writePlaylistMetafiles = new Option<bool>("--write-playlist-metafiles"); private Option<bool> noWritePlaylistMetafiles = new Option<bool>("--no-write-playlist-metafiles"); private Option<bool> cleanInfoJson = new Option<bool>("--clean-info-json"); private Option<bool> noCleanInfoJson = new Option<bool>("--no-clean-info-json"); private Option<bool> writeComments = new Option<bool>("--write-comments", "--get-comments"); private Option<bool> noWriteComments = new Option<bool>("--no-write-comments", "--no-get-comments"); private Option<string> loadInfoJson = new Option<string>("--load-info-json"); private Option<string> cookies = new Option<string>("--cookies"); private Option<bool> noCookies = new Option<bool>("--no-cookies"); private Option<string> cookiesFromBrowser = new Option<string>("--cookies-from-browser"); private Option<bool> noCookiesFromBrowser = new Option<bool>("--no-cookies-from-browser"); private Option<string> cacheDir = new Option<string>("--cache-dir"); private Option<bool> noCacheDir = new Option<bool>("--no-cache-dir"); private Option<bool> removeCacheDir = new Option<bool>("--rm-cache-dir"); private Option<bool> help = new Option<bool>("-h", "--help"); private Option<bool> version = new Option<bool>("--version"); private Option<bool> update = new Option<bool>("-U", "--update"); private Option<bool> noUpdate = new Option<bool>("--no-update"); private Option<string> updateTo = new Option<string>("--update-to"); private Option<bool> ignoreErrors = new Option<bool>("-i", "--ignore-errors"); private Option<bool> noAbortOnError = new Option<bool>("--no-abort-on-error"); private Option<bool> abortOnError = new Option<bool>("--abort-on-error", "--no-ignore-errors"); private Option<bool> dumpUserAgent = new Option<bool>("--dump-user-agent"); private Option<bool> listExtractors = new Option<bool>("--list-extractors"); private Option<bool> extractorDescriptions = new Option<bool>("--extractor-descriptions"); private Option<string> useExtractors = new Option<string>("--use-extractors", "--ies"); private Option<string> defaultSearch = new Option<string>("--default-search"); private Option<bool> ignoreConfig = new Option<bool>("--ignore-config", "--no-config"); private Option<bool> noConfigLocations = new Option<bool>("--no-config-locations"); private MultiOption<string> configLocations = new MultiOption<string>("--config-locations"); private Option<bool> flatPlaylist = new Option<bool>("--flat-playlist"); private Option<bool> noFlatPlaylist = new Option<bool>("--no-flat-playlist"); private Option<bool> liveFromStart = new Option<bool>("--live-from-start"); private Option<bool> noLiveFromStart = new Option<bool>("--no-live-from-start"); private Option<string> waitForVideo = new Option<string>("--wait-for-video"); private Option<bool> noWaitForVideo = new Option<bool>("--no-wait-for-video"); private Option<bool> markWatched = new Option<bool>("--mark-watched"); private Option<bool> noMarkWatched = new Option<bool>("--no-mark-watched"); private MultiOption<string> color = new MultiOption<string>("--color"); private Option<string> compatOptions = new Option<string>("--compat-options"); private MultiOption<string> alias = new MultiOption<string>("--alias"); private Option<string> geoVerificationProxy = new Option<string>("--geo-verification-proxy"); private Option<string> xff = new Option<string>("--xff"); private Option<bool> writeLink = new Option<bool>("--write-link"); private Option<bool> writeUrlLink = new Option<bool>("--write-url-link"); private Option<bool> writeWeblocLink = new Option<bool>("--write-webloc-link"); private Option<bool> writeDesktopLink = new Option<bool>("--write-desktop-link"); private Option<string> proxy = new Option<string>("--proxy"); private Option<int?> socketTimeout = new Option<int?>("--socket-timeout"); private Option<string> sourceAddress = new Option<string>("--source-address"); private Option<bool> forceIPv4 = new Option<bool>("-4", "--force-ipv4"); private Option<bool> forceIPv6 = new Option<bool>("-6", "--force-ipv6"); private Option<bool> enableFileUrls = new Option<bool>("--enable-file-urls"); private Option<bool> extractAudio = new Option<bool>("-x", "--extract-audio"); private Option<AudioConversionFormat> audioFormat = new Option<AudioConversionFormat>("--audio-format"); private Option<byte?> audioQuality = new Option<byte?>("--audio-quality"); private Option<string> remuxVideo = new Option<string>("--remux-video"); private Option<VideoRecodeFormat> recodeVideo = new Option<VideoRecodeFormat>("--recode-video"); private MultiOption<string> postprocessorArgs = new MultiOption<string>("--postprocessor-args", "--ppa"); private Option<bool> keepVideo = new Option<bool>("-k", "--keep-video"); private Option<bool> noKeepVideo = new Option<bool>("--no-keep-video"); private Option<bool> postOverwrites = new Option<bool>("--post-overwrites"); private Option<bool> noPostOverwrites = new Option<bool>("--no-post-overwrites"); private Option<bool> embedSubs = new Option<bool>("--embed-subs"); private Option<bool> noEmbedSubs = new Option<bool>("--no-embed-subs"); private Option<bool> embedThumbnail = new Option<bool>("--embed-thumbnail"); private Option<bool> noEmbedThumbnail = new Option<bool>("--no-embed-thumbnail"); private Option<bool> embedMetadata = new Option<bool>("--embed-metadata", "--add-metadata"); private Option<bool> noEmbedMetadata = new Option<bool>("--no-embed-metadata", "--no-add-metadata"); private Option<bool> embedChapters = new Option<bool>("--embed-chapters", "--add-chapters"); private Option<bool> noEmbedChapters = new Option<bool>("--no-embed-chapters", "--no-add-chapters"); private Option<bool> embedInfoJson = new Option<bool>("--embed-info-json"); private Option<bool> noEmbedInfoJson = new Option<bool>("--no-embed-info-json"); private Option<string> parseMetadata = new Option<string>("--parse-metadata"); private MultiOption<string> replaceInMetadata = new MultiOption<string>("--replace-in-metadata"); private Option<bool> xattrs = new Option<bool>("--xattrs"); private Option<string> concatPlaylist = new Option<string>("--concat-playlist"); private Option<string> fixup = new Option<string>("--fixup"); private Option<string> ffmpegLocation = new Option<string>("--ffmpeg-location"); private MultiOption<string> exec = new MultiOption<string>("--exec"); private Option<bool> noExec = new Option<bool>("--no-exec"); private Option<string> convertSubs = new Option<string>("--convert-subs", "--convert-subtitles"); private Option<string> convertThumbnails = new Option<string>("--convert-thumbnails"); private Option<bool> splitChapters = new Option<bool>("--split-chapters"); private Option<bool> noSplitChapters = new Option<bool>("--no-split-chapters"); private MultiOption<string> removeChapters = new MultiOption<string>("--remove-chapters"); private Option<bool> noRemoveChapters = new Option<bool>("--no-remove-chapters"); private Option<bool> forceKeyframesAtCuts = new Option<bool>("--force-keyframes-at-cuts"); private Option<bool> noForceKeyframesAtCuts = new Option<bool>("--no-force-keyframes-at-cuts"); private MultiOption<string> usePostprocessor = new MultiOption<string>("--use-postprocessor"); private Option<string> sponsorblockMark = new Option<string>("--sponsorblock-mark"); private Option<string> sponsorblockRemove = new Option<string>("--sponsorblock-remove"); private Option<string> sponsorblockChapterTitle = new Option<string>("--sponsorblock-chapter-title"); private Option<bool> noSponsorblock = new Option<bool>("--no-sponsorblock"); private Option<string> sponsorblockApi = new Option<string>("--sponsorblock-api"); private Option<bool> writeSubs = new Option<bool>("--write-subs"); private Option<bool> noWriteSubs = new Option<bool>("--no-write-subs"); private Option<bool> writeAutoSubs = new Option<bool>("--write-auto-subs", "--write-automatic-subs"); private Option<bool> noWriteAutoSubs = new Option<bool>("--no-write-auto-subs", "--no-write-automatic-subs"); private Option<bool> listSubs = new Option<bool>("--list-subs"); private Option<string> subFormat = new Option<string>("--sub-format"); private Option<string> subLangs = new Option<string>("--sub-langs"); private Option<bool> writeThumbnail = new Option<bool>("--write-thumbnail"); private Option<bool> noWriteThumbnail = new Option<bool>("--no-write-thumbnail"); private Option<bool> writeAllThumbnails = new Option<bool>("--write-all-thumbnails"); private Option<bool> listThumbnails = new Option<bool>("--list-thumbnails"); private Option<bool> quiet = new Option<bool>("-q", "--quiet"); private Option<bool> noQuiet = new Option<bool>("--no-quiet"); private Option<bool> noWarnings = new Option<bool>("--no-warnings"); private Option<bool> simulate = new Option<bool>("-s", "--simulate"); private Option<bool> noSimulate = new Option<bool>("--no-simulate"); private Option<bool> ignoreNoFormatsError = new Option<bool>("--ignore-no-formats-error"); private Option<bool> noIgnoreNoFormatsError = new Option<bool>("--no-ignore-no-formats-error"); private Option<bool> skipDownload = new Option<bool>("--skip-download", "--no-download"); private MultiOption<string> print = new MultiOption<string>("-O", "--print"); private MultiOption<string> printToFile = new MultiOption<string>("--print-to-file"); private Option<bool> dumpJson = new Option<bool>("-j", "--dump-json"); private Option<bool> dumpSingleJson = new Option<bool>("-J", "--dump-single-json"); private Option<bool> forceWriteArchive = new Option<bool>("--force-write-archive", "--force-download-archive"); private Option<bool> newline = new Option<bool>("--newline"); private Option<bool> noProgress = new Option<bool>("--no-progress"); private Option<bool> progress = new Option<bool>("--progress"); private Option<bool> consoleTitle = new Option<bool>("--console-title"); private Option<string> progressTemplate = new Option<string>("--progress-template"); private Option<bool> verbose = new Option<bool>("-v", "--verbose"); private Option<bool> dumpPages = new Option<bool>("--dump-pages"); private Option<bool> writePages = new Option<bool>("--write-pages"); private Option<bool> printTraffic = new Option<bool>("--print-traffic"); private Option<string> format = new Option<string>("-f", "--format"); private Option<string> formatSort = new Option<string>("-S", "--format-sort"); private Option<bool> formatSortForce = new Option<bool>("--format-sort-force", "--S-force"); private Option<bool> noFormatSortForce = new Option<bool>("--no-format-sort-force"); private Option<bool> videoMultistreams = new Option<bool>("--video-multistreams"); private Option<bool> noVideoMultistreams = new Option<bool>("--no-video-multistreams"); private Option<bool> audioMultistreams = new Option<bool>("--audio-multistreams"); private Option<bool> noAudioMultistreams = new Option<bool>("--no-audio-multistreams"); private Option<bool> preferFreeFormats = new Option<bool>("--prefer-free-formats"); private Option<bool> noPreferFreeFormats = new Option<bool>("--no-prefer-free-formats"); private Option<bool> checkFormats = new Option<bool>("--check-formats"); private Option<bool> checkAllFormats = new Option<bool>("--check-all-formats"); private Option<bool> noCheckFormats = new Option<bool>("--no-check-formats"); private Option<bool> listFormats = new Option<bool>("-F", "--list-formats"); private Option<DownloadMergeFormat> mergeOutputFormat = new Option<DownloadMergeFormat>("--merge-output-format"); private Option<string> playlistItems = new Option<string>("-I", "--playlist-items"); private Option<string> minFilesize = new Option<string>("--min-filesize"); private Option<string> maxFilesize = new Option<string>("--max-filesize"); private Option<DateTime> date = new Option<DateTime>("--date"); private Option<DateTime> dateBefore = new Option<DateTime>("--datebefore"); private Option<DateTime> dateAfter = new Option<DateTime>("--dateafter"); private MultiOption<string> matchFilters = new MultiOption<string>("--match-filters"); private Option<bool> noMatchFilters = new Option<bool>("--no-match-filters"); private Option<string> breakMatchFilters = new Option<string>("--break-match-filters"); private Option<bool> noBreakMatchFilters = new Option<bool>("--no-break-match-filters"); private Option<bool> noPlaylist = new Option<bool>("--no-playlist"); private Option<bool> yesPlaylist = new Option<bool>("--yes-playlist"); private Option<byte?> ageLimit = new Option<byte?>("--age-limit"); private Option<string> downloadArchive = new Option<string>("--download-archive"); private Option<bool> noDownloadArchive = new Option<bool>("--no-download-archive"); private Option<int?> maxDownloads = new Option<int?>("--max-downloads"); private Option<bool> breakOnExisting = new Option<bool>("--break-on-existing"); private Option<bool> breakPerInput = new Option<bool>("--break-per-input"); private Option<bool> noBreakPerInput = new Option<bool>("--no-break-per-input"); private Option<int?> skipPlaylistAfterErrors = new Option<int?>("--skip-playlist-after-errors"); private Option<string> encoding = new Option<string>("--encoding"); private Option<bool> legacyServerConnect = new Option<bool>("--legacy-server-connect"); private Option<bool> noCheckCertificates = new Option<bool>("--no-check-certificates"); private Option<bool> preferInsecure = new Option<bool>("--prefer-insecure"); private MultiOption<string> addHeaders = new MultiOption<string>("--add-headers"); private Option<bool> bidiWorkaround = new Option<bool>("--bidi-workaround"); private Option<int?> sleepRequests = new Option<int?>("--sleep-requests"); private Option<int?> sleepInterval = new Option<int?>("--sleep-interval", "--min-sleep-interval"); private Option<int?> maxSleepInterval = new Option<int?>("--max-sleep-interval"); private Option<int?> sleepSubtitles = new Option<int?>("--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<string> 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 XattrSetFilesize { get { return xattrSetFilesize.Value; } set { xattrSetFilesize.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<string> DownloadSections { get { return downloadSections.Value; } set { downloadSections.Value = value; } } public MultiValue<string> Downloader { get { return downloader.Value; } set { downloader.Value = value; } } public MultiValue<string> 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<string> 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 string 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 DumpUserAgent { get { return dumpUserAgent.Value; } set { dumpUserAgent.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<string> ConfigLocations { get { return configLocations.Value; } set { configLocations.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<string> Color { get { return color.Value; } set { color.Value = value; } } public string CompatOptions { get { return compatOptions.Value; } set { compatOptions.Value = value; } } public MultiValue<string> Alias { get { return alias.Value; } set { alias.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 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> Print { get { return print.Value; } set { print.Value = value; } } public MultiValue<string> 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 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<string> 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 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<string> 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<string> GetOptionFlags() { return from value in GetKnownOptions().Concat(CustomOptions).SelectMany((IOption opt) => opt.ToStringCollection()) where !string.IsNullOrWhiteSpace(value) select value; } internal IEnumerable<IOption> GetKnownOptions() { return (from p in GetType().GetRuntimeFields() where p.FieldType.IsGenericType && p.FieldType.GetInterfaces().Contains(typeof(IOption)) select p.GetValue(this)).Cast<IOption>(); } public OptionSet OverrideOptions(OptionSet overrideOptions, bool forceOverride = false) { OptionSet optionSet = (OptionSet)Clone(); optionSet.CustomOptions = optionSet.CustomOptions.Concat(overrideOptions.CustomOptions).Distinct(Comparer).ToArray(); IEnumerable<FieldInfo> enumerable = from p in overrideOptions.GetType().GetRuntimeFields() where p.FieldType.IsGenericType && p.FieldType.GetInterfaces().Contains(typeof(IOption)) select p; foreach (FieldInfo item in enumerable) { 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<string> 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<IOption> GetOptions(IEnumerable<string> lines, IEnumerable<IOption> options) { IEnumerable<IOption> knownOptions = options.ToList(); foreach (string rawLine in lines) { string line = rawLine.Trim(); if (!line.StartsWith("#") && !string.IsNullOrWhiteSpace(line)) { string[] segments = line.Split(new char[1] { ' ' }); string flag = segments[0]; IOption knownOption = knownOptions.FirstOrDefault((IOption o) => o.OptionStrings.Contains(flag)); IOption option3; if (segments.Length <= 1) { IOption option2 = new Option<bool>(true, flag); option3 = option2; } else { IOption option2 = new Option<string>(true, flag); option3 = option2; } IOption customOption = option3; IOption option = knownOption ?? customOption; option.SetFromString(line); yield return option; } } } public static OptionSet LoadConfigFile(string path) { return FromString(File.ReadAllLines(path)); } public object Clone() { return FromString(GetOptionFlags()); } public void AddCustomOption<T>(string optionString, T value) { Option<T> option = new Option<T>(true, optionString); option.Value = value; CustomOptions = CustomOptions.Concat(new Option<T>[1] { option }).ToArray(); } public void SetCustomOption<T>(string optionString, T value) { foreach (IOption item in CustomOptions.Where((IOption o) => o.OptionStrings.Contains(optionString))) { if (item is Option<T> 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(); } } internal static class Utils { internal static T OptionValueFromString<T>(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); } TypeConverter converter = TypeDescriptor.GetConverter(typeof(T)); return (T)converter.ConvertFrom(stringValue); } internal static string OptionValueToString<T>(T value) { if (value is bool) { return string.Empty; } if (value is Enum) { return " \"" + value.ToString().ToLower() + "\""; } if (value is DateTime) { object obj = value; DateTime dateTime = (DateTime)((obj is DateTime) ? obj : null); if (true) { return " " + dateTime.ToString("yyyyMMdd"); } } 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; } [JsonProperty("width")] public int? Width { get; set; } [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; }