Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of YT BOOMBOX v1.0.4
YT_BOOMBOX.dll
Decompiled 2 years ago
The result has been truncated due to the large size, download it to view full contents!
#define DEBUG using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; 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.Reflection.Emit; 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 BepInEx; using BepInEx.Configuration; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using YT_BOOMBOX.NetcodePatcher; using YT_BOOMBOX.Providers; using YoutubeDLSharp; 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.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("YT_BOOMBOX")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("YT_BOOMBOX")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("YT_BOOMBOX")] [assembly: AssemblyTitle("YT_BOOMBOX")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [module: NetcodePatchedAssembly] internal class <Module> { static <Module>() { } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } public sealed class HttpUtility { private sealed class HttpQSCollection : NameValueCollection { public override string ToString() { int count = Count; if (count == 0) { return ""; } StringBuilder stringBuilder = new StringBuilder(); string[] allKeys = AllKeys; for (int i = 0; i < count; i++) { stringBuilder.AppendFormat("{0}={1}&", allKeys[i], UrlEncode(base[allKeys[i]])); } if (stringBuilder.Length > 0) { stringBuilder.Length--; } return stringBuilder.ToString(); } } public class HttpEncoder { private static char[] hexChars; private static object entitiesLock; private static SortedDictionary<string, char> entities; private static Lazy<HttpEncoder> defaultEncoder; private static Lazy<HttpEncoder> currentEncoderLazy; private static HttpEncoder currentEncoder; private static IDictionary<string, char> Entities { get { lock (entitiesLock) { if (entities == null) { InitEntities(); } return entities; } } } public static HttpEncoder Current { get { if (currentEncoder == null) { currentEncoder = currentEncoderLazy.Value; } return currentEncoder; } set { if (value == null) { throw new ArgumentNullException("value"); } currentEncoder = value; } } public static HttpEncoder Default => defaultEncoder.Value; static HttpEncoder() { hexChars = "0123456789abcdef".ToCharArray(); entitiesLock = new object(); defaultEncoder = new Lazy<HttpEncoder>(() => new HttpEncoder()); currentEncoderLazy = new Lazy<HttpEncoder>(GetCustomEncoderFromConfig); } protected internal virtual void HeaderNameValueEncode(string headerName, string headerValue, out string encodedHeaderName, out string encodedHeaderValue) { if (string.IsNullOrEmpty(headerName)) { encodedHeaderName = headerName; } else { encodedHeaderName = EncodeHeaderString(headerName); } if (string.IsNullOrEmpty(headerValue)) { encodedHeaderValue = headerValue; } else { encodedHeaderValue = EncodeHeaderString(headerValue); } } private static void StringBuilderAppend(string s, ref StringBuilder sb) { if (sb == null) { sb = new StringBuilder(s); } else { sb.Append(s); } } private static string EncodeHeaderString(string input) { StringBuilder sb = null; foreach (char c in input) { if ((c < ' ' && c != '\t') || c == '\u007f') { StringBuilderAppend($"%{(int)c:x2}", ref sb); } } if (sb != null) { return sb.ToString(); } return input; } protected internal virtual void HtmlAttributeEncode(string value, TextWriter output) { if (output == null) { throw new ArgumentNullException("output"); } if (!string.IsNullOrEmpty(value)) { output.Write(HtmlAttributeEncode(value)); } } protected internal virtual void HtmlDecode(string value, TextWriter output) { if (output == null) { throw new ArgumentNullException("output"); } output.Write(HtmlDecode(value)); } protected internal virtual void HtmlEncode(string value, TextWriter output) { if (output == null) { throw new ArgumentNullException("output"); } output.Write(HtmlEncode(value)); } protected internal virtual byte[] UrlEncode(byte[] bytes, int offset, int count) { return UrlEncodeToBytes(bytes, offset, count); } private static HttpEncoder GetCustomEncoderFromConfig() { return defaultEncoder.Value; } protected internal virtual string UrlPathEncode(string value) { if (string.IsNullOrEmpty(value)) { return value; } MemoryStream memoryStream = new MemoryStream(); int length = value.Length; for (int i = 0; i < length; i++) { UrlPathEncodeChar(value[i], memoryStream); } return Encoding.ASCII.GetString(memoryStream.ToArray()); } internal static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count) { if (bytes == null) { throw new ArgumentNullException("bytes"); } int num = bytes.Length; if (num == 0) { return new byte[0]; } if (offset < 0 || offset >= num) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0 || count > num - offset) { throw new ArgumentOutOfRangeException("count"); } MemoryStream memoryStream = new MemoryStream(count); int num2 = offset + count; for (int i = offset; i < num2; i++) { UrlEncodeChar((char)bytes[i], memoryStream, isUnicode: false); } return memoryStream.ToArray(); } internal static string HtmlEncode(string s) { if (s == null) { return null; } if (s.Length == 0) { return string.Empty; } bool flag = false; foreach (char c in s) { if (c == '&' || c == '"' || c == '<' || c == '>' || c > '\u009f' || c == '\'') { flag = true; break; } } if (!flag) { return s; } StringBuilder stringBuilder = new StringBuilder(); int length = s.Length; for (int j = 0; j < length; j++) { char c2 = s[j]; switch (c2) { case '&': stringBuilder.Append("&"); continue; case '>': stringBuilder.Append(">"); continue; case '<': stringBuilder.Append("<"); continue; case '"': stringBuilder.Append("""); continue; case '\'': stringBuilder.Append("'"); continue; case '<': stringBuilder.Append("<"); continue; case '>': stringBuilder.Append(">"); continue; } if (c2 > '\u009f' && c2 < 'Ā') { stringBuilder.Append("&#"); int num = c2; stringBuilder.Append(num.ToString(Helpers.InvariantCulture)); stringBuilder.Append(";"); } else { stringBuilder.Append(c2); } } return stringBuilder.ToString(); } internal static string HtmlAttributeEncode(string s) { if (string.IsNullOrEmpty(s)) { return string.Empty; } bool flag = false; foreach (char c in s) { if (c == '&' || c == '"' || c == '<' || c == '\'') { flag = true; break; } } if (!flag) { return s; } StringBuilder stringBuilder = new StringBuilder(); int length = s.Length; for (int j = 0; j < length; j++) { char c2 = s[j]; switch (c2) { case '&': stringBuilder.Append("&"); break; case '"': stringBuilder.Append("""); break; case '<': stringBuilder.Append("<"); break; case '\'': stringBuilder.Append("'"); break; default: stringBuilder.Append(c2); break; } } return stringBuilder.ToString(); } internal static string HtmlDecode(string s) { if (s == null) { return null; } if (s.Length == 0) { return string.Empty; } if (s.IndexOf('&') == -1) { return s; } StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = new StringBuilder(); StringBuilder stringBuilder3 = new StringBuilder(); int length = s.Length; int num = 0; int num2 = 0; bool flag = false; bool flag2 = false; for (int i = 0; i < length; i++) { char c = s[i]; if (num == 0) { if (c == '&') { stringBuilder2.Append(c); stringBuilder.Append(c); num = 1; } else { stringBuilder3.Append(c); } continue; } if (c == '&') { num = 1; if (flag2) { stringBuilder2.Append(num2.ToString(Helpers.InvariantCulture)); flag2 = false; } stringBuilder3.Append(stringBuilder2.ToString()); stringBuilder2.Length = 0; stringBuilder2.Append('&'); continue; } switch (num) { case 1: if (c == ';') { num = 0; stringBuilder3.Append(stringBuilder2.ToString()); stringBuilder3.Append(c); stringBuilder2.Length = 0; } else { num2 = 0; flag = false; num = ((c == '#') ? 3 : 2); stringBuilder2.Append(c); stringBuilder.Append(c); } break; case 2: stringBuilder2.Append(c); if (c == ';') { string text = stringBuilder2.ToString(); if (text.Length > 1 && Entities.ContainsKey(text.Substring(1, text.Length - 2))) { text = Entities[text.Substring(1, text.Length - 2)].ToString(); } stringBuilder3.Append(text); num = 0; stringBuilder2.Length = 0; stringBuilder.Length = 0; } break; case 3: if (c == ';') { if (num2 == 0) { stringBuilder3.Append(stringBuilder.ToString() + ";"); } else if (num2 > 65535) { stringBuilder3.Append("&#"); stringBuilder3.Append(num2.ToString(Helpers.InvariantCulture)); stringBuilder3.Append(";"); } else { stringBuilder3.Append((char)num2); } num = 0; stringBuilder2.Length = 0; stringBuilder.Length = 0; flag2 = false; } else if (flag && Uri.IsHexDigit(c)) { num2 = num2 * 16 + Uri.FromHex(c); flag2 = true; stringBuilder.Append(c); } else if (char.IsDigit(c)) { num2 = num2 * 10 + (c - 48); flag2 = true; stringBuilder.Append(c); } else if (num2 == 0 && (c == 'x' || c == 'X')) { flag = true; stringBuilder.Append(c); } else { num = 2; if (flag2) { stringBuilder2.Append(num2.ToString(Helpers.InvariantCulture)); flag2 = false; } stringBuilder2.Append(c); } break; } } if (stringBuilder2.Length > 0) { stringBuilder3.Append(stringBuilder2.ToString()); } else if (flag2) { stringBuilder3.Append(num2.ToString(Helpers.InvariantCulture)); } return stringBuilder3.ToString(); } internal static bool NotEncoded(char c) { return c == '!' || c == '(' || c == ')' || c == '*' || c == '-' || c == '.' || c == '_'; } internal static void UrlEncodeChar(char c, Stream result, bool isUnicode) { if (c > 'ÿ') { result.WriteByte(37); result.WriteByte(117); int num = (int)c >> 12; result.WriteByte((byte)hexChars[num]); num = ((int)c >> 8) & 0xF; result.WriteByte((byte)hexChars[num]); num = ((int)c >> 4) & 0xF; result.WriteByte((byte)hexChars[num]); num = c & 0xF; result.WriteByte((byte)hexChars[num]); } else if (c > ' ' && NotEncoded(c)) { result.WriteByte((byte)c); } else if (c == ' ') { result.WriteByte(43); } else if (c < '0' || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || c > 'z') { if (isUnicode && c > '\u007f') { result.WriteByte(37); result.WriteByte(117); result.WriteByte(48); result.WriteByte(48); } else { result.WriteByte(37); } int num2 = (int)c >> 4; result.WriteByte((byte)hexChars[num2]); num2 = c & 0xF; result.WriteByte((byte)hexChars[num2]); } else { result.WriteByte((byte)c); } } internal static void UrlPathEncodeChar(char c, Stream result) { if (c < '!' || c > '~') { byte[] bytes = Encoding.UTF8.GetBytes(c.ToString()); for (int i = 0; i < bytes.Length; i++) { result.WriteByte(37); int num = bytes[i] >> 4; result.WriteByte((byte)hexChars[num]); num = bytes[i] & 0xF; result.WriteByte((byte)hexChars[num]); } } else if (c == ' ') { result.WriteByte(37); result.WriteByte(50); result.WriteByte(48); } else { result.WriteByte((byte)c); } } private static void InitEntities() { entities = new SortedDictionary<string, char>(StringComparer.Ordinal); entities.Add("nbsp", '\u00a0'); entities.Add("iexcl", '¡'); entities.Add("cent", '¢'); entities.Add("pound", '£'); entities.Add("curren", '¤'); entities.Add("yen", '¥'); entities.Add("brvbar", '¦'); entities.Add("sect", '§'); entities.Add("uml", '\u00a8'); entities.Add("copy", '©'); entities.Add("ordf", 'ª'); entities.Add("laquo", '«'); entities.Add("not", '¬'); entities.Add("shy", '\u00ad'); entities.Add("reg", '®'); entities.Add("macr", '\u00af'); entities.Add("deg", '°'); entities.Add("plusmn", '±'); entities.Add("sup2", '²'); entities.Add("sup3", '³'); entities.Add("acute", '\u00b4'); entities.Add("micro", 'µ'); entities.Add("para", '¶'); entities.Add("middot", '·'); entities.Add("cedil", '\u00b8'); entities.Add("sup1", '¹'); entities.Add("ordm", 'º'); entities.Add("raquo", '»'); entities.Add("frac14", '¼'); entities.Add("frac12", '½'); entities.Add("frac34", '¾'); entities.Add("iquest", '¿'); entities.Add("Agrave", 'À'); entities.Add("Aacute", 'Á'); entities.Add("Acirc", 'Â'); entities.Add("Atilde", 'Ã'); entities.Add("Auml", 'Ä'); entities.Add("Aring", 'Å'); entities.Add("AElig", 'Æ'); entities.Add("Ccedil", 'Ç'); entities.Add("Egrave", 'È'); entities.Add("Eacute", 'É'); entities.Add("Ecirc", 'Ê'); entities.Add("Euml", 'Ë'); entities.Add("Igrave", 'Ì'); entities.Add("Iacute", 'Í'); entities.Add("Icirc", 'Î'); entities.Add("Iuml", 'Ï'); entities.Add("ETH", 'Ð'); entities.Add("Ntilde", 'Ñ'); entities.Add("Ograve", 'Ò'); entities.Add("Oacute", 'Ó'); entities.Add("Ocirc", 'Ô'); entities.Add("Otilde", 'Õ'); entities.Add("Ouml", 'Ö'); entities.Add("times", '×'); entities.Add("Oslash", 'Ø'); entities.Add("Ugrave", 'Ù'); entities.Add("Uacute", 'Ú'); entities.Add("Ucirc", 'Û'); entities.Add("Uuml", 'Ü'); entities.Add("Yacute", 'Ý'); entities.Add("THORN", 'Þ'); entities.Add("szlig", 'ß'); entities.Add("agrave", 'à'); entities.Add("aacute", 'á'); entities.Add("acirc", 'â'); entities.Add("atilde", 'ã'); entities.Add("auml", 'ä'); entities.Add("aring", 'å'); entities.Add("aelig", 'æ'); entities.Add("ccedil", 'ç'); entities.Add("egrave", 'è'); entities.Add("eacute", 'é'); entities.Add("ecirc", 'ê'); entities.Add("euml", 'ë'); entities.Add("igrave", 'ì'); entities.Add("iacute", 'í'); entities.Add("icirc", 'î'); entities.Add("iuml", 'ï'); entities.Add("eth", 'ð'); entities.Add("ntilde", 'ñ'); entities.Add("ograve", 'ò'); entities.Add("oacute", 'ó'); entities.Add("ocirc", 'ô'); entities.Add("otilde", 'õ'); entities.Add("ouml", 'ö'); entities.Add("divide", '÷'); entities.Add("oslash", 'ø'); entities.Add("ugrave", 'ù'); entities.Add("uacute", 'ú'); entities.Add("ucirc", 'û'); entities.Add("uuml", 'ü'); entities.Add("yacute", 'ý'); entities.Add("thorn", 'þ'); entities.Add("yuml", 'ÿ'); entities.Add("fnof", 'ƒ'); entities.Add("Alpha", 'Α'); entities.Add("Beta", 'Β'); entities.Add("Gamma", 'Γ'); entities.Add("Delta", 'Δ'); entities.Add("Epsilon", 'Ε'); entities.Add("Zeta", 'Ζ'); entities.Add("Eta", 'Η'); entities.Add("Theta", 'Θ'); entities.Add("Iota", 'Ι'); entities.Add("Kappa", 'Κ'); entities.Add("Lambda", 'Λ'); entities.Add("Mu", 'Μ'); entities.Add("Nu", 'Ν'); entities.Add("Xi", 'Ξ'); entities.Add("Omicron", 'Ο'); entities.Add("Pi", 'Π'); entities.Add("Rho", 'Ρ'); entities.Add("Sigma", 'Σ'); entities.Add("Tau", 'Τ'); entities.Add("Upsilon", 'Υ'); entities.Add("Phi", 'Φ'); entities.Add("Chi", 'Χ'); entities.Add("Psi", 'Ψ'); entities.Add("Omega", 'Ω'); entities.Add("alpha", 'α'); entities.Add("beta", 'β'); entities.Add("gamma", 'γ'); entities.Add("delta", 'δ'); entities.Add("epsilon", 'ε'); entities.Add("zeta", 'ζ'); entities.Add("eta", 'η'); entities.Add("theta", 'θ'); entities.Add("iota", 'ι'); entities.Add("kappa", 'κ'); entities.Add("lambda", 'λ'); entities.Add("mu", 'μ'); entities.Add("nu", 'ν'); entities.Add("xi", 'ξ'); entities.Add("omicron", 'ο'); entities.Add("pi", 'π'); entities.Add("rho", 'ρ'); entities.Add("sigmaf", 'ς'); entities.Add("sigma", 'σ'); entities.Add("tau", 'τ'); entities.Add("upsilon", 'υ'); entities.Add("phi", 'φ'); entities.Add("chi", 'χ'); entities.Add("psi", 'ψ'); entities.Add("omega", 'ω'); entities.Add("thetasym", 'ϑ'); entities.Add("upsih", 'ϒ'); entities.Add("piv", 'ϖ'); entities.Add("bull", '•'); entities.Add("hellip", '…'); entities.Add("prime", '′'); entities.Add("Prime", '″'); entities.Add("oline", '‾'); entities.Add("frasl", '⁄'); entities.Add("weierp", '℘'); entities.Add("image", 'ℑ'); entities.Add("real", 'ℜ'); entities.Add("trade", '™'); entities.Add("alefsym", 'ℵ'); entities.Add("larr", '←'); entities.Add("uarr", '↑'); entities.Add("rarr", '→'); entities.Add("darr", '↓'); entities.Add("harr", '↔'); entities.Add("crarr", '↵'); entities.Add("lArr", '⇐'); entities.Add("uArr", '⇑'); entities.Add("rArr", '⇒'); entities.Add("dArr", '⇓'); entities.Add("hArr", '⇔'); entities.Add("forall", '∀'); entities.Add("part", '∂'); entities.Add("exist", '∃'); entities.Add("empty", '∅'); entities.Add("nabla", '∇'); entities.Add("isin", '∈'); entities.Add("notin", '∉'); entities.Add("ni", '∋'); entities.Add("prod", '∏'); entities.Add("sum", '∑'); entities.Add("minus", '−'); entities.Add("lowast", '∗'); entities.Add("radic", '√'); entities.Add("prop", '∝'); entities.Add("infin", '∞'); entities.Add("ang", '∠'); entities.Add("and", '∧'); entities.Add("or", '∨'); entities.Add("cap", '∩'); entities.Add("cup", '∪'); entities.Add("int", '∫'); entities.Add("there4", '∴'); entities.Add("sim", '∼'); entities.Add("cong", '≅'); entities.Add("asymp", '≈'); entities.Add("ne", '≠'); entities.Add("equiv", '≡'); entities.Add("le", '≤'); entities.Add("ge", '≥'); entities.Add("sub", '⊂'); entities.Add("sup", '⊃'); entities.Add("nsub", '⊄'); entities.Add("sube", '⊆'); entities.Add("supe", '⊇'); entities.Add("oplus", '⊕'); entities.Add("otimes", '⊗'); entities.Add("perp", '⊥'); entities.Add("sdot", '⋅'); entities.Add("lceil", '⌈'); entities.Add("rceil", '⌉'); entities.Add("lfloor", '⌊'); entities.Add("rfloor", '⌋'); entities.Add("lang", '〈'); entities.Add("rang", '〉'); entities.Add("loz", '◊'); entities.Add("spades", '♠'); entities.Add("clubs", '♣'); entities.Add("hearts", '♥'); entities.Add("diams", '♦'); entities.Add("quot", '"'); entities.Add("amp", '&'); entities.Add("lt", '<'); entities.Add("gt", '>'); entities.Add("OElig", 'Œ'); entities.Add("oelig", 'œ'); entities.Add("Scaron", 'Š'); entities.Add("scaron", 'š'); entities.Add("Yuml", 'Ÿ'); entities.Add("circ", 'ˆ'); entities.Add("tilde", '\u02dc'); entities.Add("ensp", '\u2002'); entities.Add("emsp", '\u2003'); entities.Add("thinsp", '\u2009'); entities.Add("zwnj", '\u200c'); entities.Add("zwj", '\u200d'); entities.Add("lrm", '\u200e'); entities.Add("rlm", '\u200f'); entities.Add("ndash", '–'); entities.Add("mdash", '—'); entities.Add("lsquo", '‘'); entities.Add("rsquo", '’'); entities.Add("sbquo", '‚'); entities.Add("ldquo", '“'); entities.Add("rdquo", '”'); entities.Add("bdquo", '„'); entities.Add("dagger", '†'); entities.Add("Dagger", '‡'); entities.Add("permil", '‰'); entities.Add("lsaquo", '‹'); entities.Add("rsaquo", '›'); entities.Add("euro", '€'); } } private class Helpers { public static readonly CultureInfo InvariantCulture = CultureInfo.InvariantCulture; } public interface IHtmlString { string ToHtmlString(); } public static void HtmlAttributeEncode(string s, TextWriter output) { if (output == null) { throw new ArgumentNullException("output"); } HttpEncoder.Current.HtmlAttributeEncode(s, output); } public static string HtmlAttributeEncode(string s) { if (s == null) { return null; } using StringWriter stringWriter = new StringWriter(); HttpEncoder.Current.HtmlAttributeEncode(s, stringWriter); return stringWriter.ToString(); } public static string UrlDecode(string str) { return UrlDecode(str, Encoding.UTF8); } private static char[] GetChars(MemoryStream b, Encoding e) { return e.GetChars(b.GetBuffer(), 0, (int)b.Length); } private static void WriteCharBytes(IList buf, char ch, Encoding e) { if (ch > 'ÿ') { byte[] bytes = e.GetBytes(new char[1] { ch }); foreach (byte b in bytes) { buf.Add(b); } } else { buf.Add((byte)ch); } } public static string UrlDecode(string s, Encoding e) { if (s == null) { return null; } if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1) { return s; } if (e == null) { e = Encoding.UTF8; } long num = s.Length; List<byte> list = new List<byte>(); for (int i = 0; i < num; i++) { char c = s[i]; if (c == '%' && i + 2 < num && s[i + 1] != '%') { int @char; if (s[i + 1] == 'u' && i + 5 < num) { @char = GetChar(s, i + 2, 4); if (@char != -1) { WriteCharBytes(list, (char)@char, e); i += 5; } else { WriteCharBytes(list, '%', e); } } else if ((@char = GetChar(s, i + 1, 2)) != -1) { WriteCharBytes(list, (char)@char, e); i += 2; } else { WriteCharBytes(list, '%', e); } } else if (c == '+') { WriteCharBytes(list, ' ', e); } else { WriteCharBytes(list, c, e); } } byte[] bytes = list.ToArray(); list = null; return e.GetString(bytes); } public static string UrlDecode(byte[] bytes, Encoding e) { if (bytes == null) { return null; } return UrlDecode(bytes, 0, bytes.Length, e); } private static int GetInt(byte b) { char c = (char)b; if (c >= '0' && c <= '9') { return c - 48; } if (c >= 'a' && c <= 'f') { return c - 97 + 10; } if (c >= 'A' && c <= 'F') { return c - 65 + 10; } return -1; } private static int GetChar(byte[] bytes, int offset, int length) { int num = 0; int num2 = length + offset; for (int i = offset; i < num2; i++) { int @int = GetInt(bytes[i]); if (@int == -1) { return -1; } num = (num << 4) + @int; } return num; } private static int GetChar(string str, int offset, int length) { int num = 0; int num2 = length + offset; for (int i = offset; i < num2; i++) { char c = str[i]; if (c > '\u007f') { return -1; } int @int = GetInt((byte)c); if (@int == -1) { return -1; } num = (num << 4) + @int; } return num; } public static string UrlDecode(byte[] bytes, int offset, int count, Encoding e) { if (bytes == null) { return null; } if (count == 0) { return string.Empty; } if (bytes == null) { throw new ArgumentNullException("bytes"); } if (offset < 0 || offset > bytes.Length) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0 || offset + count > bytes.Length) { throw new ArgumentOutOfRangeException("count"); } StringBuilder stringBuilder = new StringBuilder(); MemoryStream memoryStream = new MemoryStream(); int num = count + offset; for (int i = offset; i < num; i++) { if (bytes[i] == 37 && i + 2 < count && bytes[i + 1] != 37) { int @char; if (bytes[i + 1] == 117 && i + 5 < num) { if (memoryStream.Length > 0) { stringBuilder.Append(GetChars(memoryStream, e)); memoryStream.SetLength(0L); } @char = GetChar(bytes, i + 2, 4); if (@char != -1) { stringBuilder.Append((char)@char); i += 5; continue; } } else if ((@char = GetChar(bytes, i + 1, 2)) != -1) { memoryStream.WriteByte((byte)@char); i += 2; continue; } } if (memoryStream.Length > 0) { stringBuilder.Append(GetChars(memoryStream, e)); memoryStream.SetLength(0L); } if (bytes[i] == 43) { stringBuilder.Append(' '); } else { stringBuilder.Append((char)bytes[i]); } } if (memoryStream.Length > 0) { stringBuilder.Append(GetChars(memoryStream, e)); } memoryStream = null; return stringBuilder.ToString(); } public static byte[] UrlDecodeToBytes(byte[] bytes) { if (bytes == null) { return null; } return UrlDecodeToBytes(bytes, 0, bytes.Length); } public static byte[] UrlDecodeToBytes(string str) { return UrlDecodeToBytes(str, Encoding.UTF8); } public static byte[] UrlDecodeToBytes(string str, Encoding e) { if (str == null) { return null; } if (e == null) { throw new ArgumentNullException("e"); } return UrlDecodeToBytes(e.GetBytes(str)); } public static byte[] UrlDecodeToBytes(byte[] bytes, int offset, int count) { if (bytes == null) { return null; } if (count == 0) { return new byte[0]; } int num = bytes.Length; if (offset < 0 || offset >= num) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0 || offset > num - count) { throw new ArgumentOutOfRangeException("count"); } MemoryStream memoryStream = new MemoryStream(); int num2 = offset + count; for (int i = offset; i < num2; i++) { char c = (char)bytes[i]; switch (c) { case '+': c = ' '; break; case '%': if (i < num2 - 2) { int @char = GetChar(bytes, i + 1, 2); if (@char != -1) { c = (char)@char; i += 2; } } break; } memoryStream.WriteByte((byte)c); } return memoryStream.ToArray(); } public static string UrlEncode(string str) { return UrlEncode(str, Encoding.UTF8); } public static string UrlEncode(string s, Encoding Enc) { if (s == null) { return null; } if (s == string.Empty) { return string.Empty; } bool flag = false; int length = s.Length; for (int i = 0; i < length; i++) { char c = s[i]; if ((c < '0' || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || c > 'z') && !HttpEncoder.NotEncoded(c)) { flag = true; break; } } if (!flag) { return s; } byte[] bytes = new byte[Enc.GetMaxByteCount(s.Length)]; int bytes2 = Enc.GetBytes(s, 0, s.Length, bytes, 0); return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, bytes2)); } public static string UrlEncode(byte[] bytes) { if (bytes == null) { return null; } if (bytes.Length == 0) { return string.Empty; } return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, bytes.Length)); } public static string UrlEncode(byte[] bytes, int offset, int count) { if (bytes == null) { return null; } if (bytes.Length == 0) { return string.Empty; } return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, offset, count)); } public static byte[] UrlEncodeToBytes(string str) { return UrlEncodeToBytes(str, Encoding.UTF8); } public static byte[] UrlEncodeToBytes(string str, Encoding e) { if (str == null) { return null; } if (str.Length == 0) { return new byte[0]; } byte[] bytes = e.GetBytes(str); return UrlEncodeToBytes(bytes, 0, bytes.Length); } public static byte[] UrlEncodeToBytes(byte[] bytes) { if (bytes == null) { return null; } if (bytes.Length == 0) { return new byte[0]; } return UrlEncodeToBytes(bytes, 0, bytes.Length); } public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count) { if (bytes == null) { return null; } return HttpEncoder.Current.UrlEncode(bytes, offset, count); } public static string UrlEncodeUnicode(string str) { if (str == null) { return null; } return Encoding.ASCII.GetString(UrlEncodeUnicodeToBytes(str)); } public static byte[] UrlEncodeUnicodeToBytes(string str) { if (str == null) { return null; } if (str.Length == 0) { return new byte[0]; } MemoryStream memoryStream = new MemoryStream(str.Length); foreach (char c in str) { HttpEncoder.UrlEncodeChar(c, memoryStream, isUnicode: true); } return memoryStream.ToArray(); } public static string HtmlDecode(string s) { if (s == null) { return null; } using StringWriter stringWriter = new StringWriter(); HttpEncoder.Current.HtmlDecode(s, stringWriter); return stringWriter.ToString(); } public static void HtmlDecode(string s, TextWriter output) { if (output == null) { throw new ArgumentNullException("output"); } if (!string.IsNullOrEmpty(s)) { HttpEncoder.Current.HtmlDecode(s, output); } } public static string HtmlEncode(string s) { if (s == null) { return null; } using StringWriter stringWriter = new StringWriter(); HttpEncoder.Current.HtmlEncode(s, stringWriter); return stringWriter.ToString(); } public static void HtmlEncode(string s, TextWriter output) { if (output == null) { throw new ArgumentNullException("output"); } if (!string.IsNullOrEmpty(s)) { HttpEncoder.Current.HtmlEncode(s, output); } } public static string HtmlEncode(object value) { if (value == null) { return null; } if (value is IHtmlString htmlString) { return htmlString.ToHtmlString(); } return HtmlEncode(value.ToString()); } public static string JavaScriptStringEncode(string value) { return JavaScriptStringEncode(value, addDoubleQuotes: false); } public static string JavaScriptStringEncode(string value, bool addDoubleQuotes) { if (string.IsNullOrEmpty(value)) { return addDoubleQuotes ? "\"\"" : string.Empty; } int length = value.Length; bool flag = false; for (int i = 0; i < length; i++) { char c = value[i]; if ((c >= '\0' && c <= '\u001f') || c == '"' || c == '\'' || c == '<' || c == '>' || c == '\\') { flag = true; break; } } if (!flag) { return addDoubleQuotes ? ("\"" + value + "\"") : value; } StringBuilder stringBuilder = new StringBuilder(); if (addDoubleQuotes) { stringBuilder.Append('"'); } for (int j = 0; j < length; j++) { char c = value[j]; if (c < '\0' || c > '\a') { switch (c) { default: if (c != '\'' && c != '<' && c != '>') { switch (c) { case '\b': stringBuilder.Append("\\b"); break; case '\t': stringBuilder.Append("\\t"); break; case '\n': stringBuilder.Append("\\n"); break; case '\f': stringBuilder.Append("\\f"); break; case '\r': stringBuilder.Append("\\r"); break; case '"': stringBuilder.Append("\\\""); break; case '\\': stringBuilder.Append("\\\\"); break; default: stringBuilder.Append(c); break; } continue; } break; case '\v': case '\u000e': case '\u000f': case '\u0010': case '\u0011': case '\u0012': case '\u0013': case '\u0014': case '\u0015': case '\u0016': case '\u0017': case '\u0018': case '\u0019': case '\u001a': case '\u001b': case '\u001c': case '\u001d': case '\u001e': case '\u001f': break; } } stringBuilder.AppendFormat("\\u{0:x4}", (int)c); } if (addDoubleQuotes) { stringBuilder.Append('"'); } return stringBuilder.ToString(); } public static string UrlPathEncode(string s) { return HttpEncoder.Current.UrlPathEncode(s); } public static NameValueCollection ParseQueryString(string query) { return ParseQueryString(query, Encoding.UTF8); } public static NameValueCollection ParseQueryString(string query, Encoding encoding) { if (query == null) { throw new ArgumentNullException("query"); } if (encoding == null) { throw new ArgumentNullException("encoding"); } if (query.Length == 0 || (query.Length == 1 && query[0] == '?')) { return new HttpQSCollection(); } if (query[0] == '?') { query = query.Substring(1); } NameValueCollection result = new HttpQSCollection(); ParseQueryString(query, encoding, result); return result; } internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result) { if (query.Length == 0) { return; } string text = HtmlDecode(query); int length = text.Length; int num = 0; bool flag = true; while (num <= length) { int num2 = -1; int num3 = -1; for (int i = num; i < length; i++) { if (num2 == -1 && text[i] == '=') { num2 = i + 1; } else if (text[i] == '&') { num3 = i; break; } } if (flag) { flag = false; if (text[num] == '?') { num++; } } string name; if (num2 == -1) { name = null; num2 = num; } else { name = UrlDecode(text.Substring(num, num2 - num - 1), encoding); } if (num3 < 0) { num = -1; num3 = text.Length; } else { num = num3 + 1; } string value = UrlDecode(text.Substring(num2, num3 - num2), encoding); result.Add(name, value); if (num == -1) { break; } } } } 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('_'); if (restricted && text.StartsWith("-_")) { text = text.Substring(2); } if (text.StartsWith("-")) { text = "_" + text.Substring(1); } text = text.TrimStart('.'); 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 ("\\/|*<>".Contains(c)) { return "_"; } if (restricted && "!&'()[]{}$;`^,# ".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(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 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 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 = new YoutubeDLProcess(YoutubeDLPath); 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 = new YoutubeDLProcess(YoutubeDLPath); 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 = new YoutubeDLProcess(YoutubeDLPath); 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 = new YoutubeDLProcess(YoutubeDLPath); process.OutputReceived += delegate(object o, DataReceivedEventArgs e) { videoData = JsonConvert.DeserializeObject<VideoData>(e.Data); }; 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 = new YoutubeDLProcess(YoutubeDLPath); 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('"'); 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 = new YoutubeDLProcess(YoutubeDLPath); 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('"'); 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 = new YoutubeDLProcess(YoutubeDLPath); 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('"'); 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 = new YoutubeDLProcess(YoutubeDLPath); 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('"'); 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: {}" }; } } 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 ((urls != null) ? string.Join(" ", urls.Select((string s) => "\"" + s + "\"")) : string.Empty) + options.ToString(); } 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(' '); string stringValue = s.Substring(array[0].Length).Trim().Trim('"'); 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.Last(); 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(' '); string stringValue = s.Substring(array[0].Length).Trim().Trim('"'); 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> 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<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> 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"); private Option<bool> listFormatsAsTable = new Option<bool>("--list-formats-as-table"); private Option<bool> youtubeSkipDashManifest = new Option<bool>("--youtube-skip-dash-manifest"); private Option<bool> youtubeSkipHlsManifest = new Option<bool>("--youtube-skip-hls-manifest"); 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"); private Option<bool> abortOnUnavailableFragment = new Option<bool>("--abort-on-unavailable-fragment"); 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"); private Option<bool> ignoreDynamicMpd = new Option<bool>("--ignore-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"); private Option<bool> noWriteComments = new Option<bool>("--no-write-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<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"); 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"); private Option<string> defaultSearch = new Option<string>("--default-search"); private Option<bool> ignoreConfig = new Option<bool>("--ignore-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 Option<bool> noColors = new Option<bool>("--no-colors"); private Option<string> compatOptions = new Option<string>("--compat-options"); private Option<string> alias = new Option<string>("--alias"); private Option<string> geoVerificationProxy = new Option<string>("--geo-verification-proxy"); 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<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> 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"); 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"); private Option<bool> noEmbedMetadata = new Option<bool>("--no-embed-metadata"); private Option<bool> embedChapters = new Option<bool>("--embed-chapters"); private Option<bool> noEmbedChapters = new Option<bool>("--no-embed-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"); 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"); private Option<bool> noWriteAutoSubs = new Option<bool>("--no-write-auto-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> 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"); 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"); 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"); 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> noMatchFilter = new Option<bool>("--no-match-filter"); 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> breakOnReject = new Option<bool>("--break-on-reject"); 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> addHeader = new MultiOption<string>("--add-header"); 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"); 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 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: --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: --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; } } 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 AbortOnUnavailableFragment { get { return abortOnUnavailableFragment.Value; } set { abortOnUnavailableFragment.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; }