using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Web; using BepInEx; using BepInEx.Logging; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("onlystar")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.4.0")] [assembly: AssemblyInformationalVersion("1.0.4+a486f4387dc34231376b1195d8d174ef45cc7811")] [assembly: AssemblyProduct("onlystar-ytModule")] [assembly: AssemblyTitle("YTModule")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.4.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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; } } } namespace BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace ytModule { [BepInPlugin("onlystar.ytModule", "ytModule", "1.0.4")] public class ytModulePlugin : BaseUnityPlugin { public const string PluginGUID = "onlystar.ytModule"; public const string PluginName = "ytModule"; public const string PluginVersion = "1.0.4"; internal static ManualLogSource Logger; private string ytDlpPath; private string ffmpegPath; public static bool Prod { get; set; } = true; internal static ytModulePlugin Instance { get; private set; } private void Awake() { Instance = this; Logger = ((BaseUnityPlugin)this).Logger; string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); ytDlpPath = Path.Combine(directoryName, "yt-dlp.exe"); ffmpegPath = Path.Combine(directoryName, "ffmpeg.exe"); string path = Path.Combine(directoryName, "deno.exe"); if (!File.Exists(ytDlpPath)) { Logger.LogError((object)"[ytModule] yt-dlp.exe not found!"); } if (!File.Exists(ffmpegPath)) { Logger.LogError((object)"[ytModule] ffmpeg.exe not found!"); } if (!File.Exists(path)) { Logger.LogWarning((object)"[ytModule] deno.exe not found. Note that it is optional for now, but it might be a requirement in the future."); } string environmentVariable = Environment.GetEnvironmentVariable("PATH"); Environment.SetEnvironmentVariable("PATH", directoryName + Path.PathSeparator + environmentVariable); Logger.LogInfo((object)"ytModule 1.0.4 loaded successfully!"); } public static ytModuleAPI GetAPI() { if ((Object)(object)Instance == (Object)null) { ManualLogSource logger = Logger; if (logger != null) { logger.LogError((object)"[ytModule] ytModule plugin not found!"); } return null; } return new ytModuleAPI(Instance); } public static bool IsModuleReady() { if ((Object)(object)Instance == (Object)null) { return false; } string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location); string path = Path.Combine(directoryName, "yt-dlp.exe"); string path2 = Path.Combine(directoryName, "ffmpeg.exe"); string path3 = Path.Combine(directoryName, "deno.exe"); bool result = File.Exists(path) && File.Exists(path2); if (!File.Exists(path3)) { ManualLogSource logger = Logger; if (logger != null) { logger.LogWarning((object)"[ytModule] Deno runtime not found. YouTube downloads may have an increased chance to fail."); } } return result; } } public class ytModuleAPI { public enum Platform { YouTube, SoundCloud, Vimeo, Bandcamp, Other } [CompilerGenerated] private sealed class <>c__DisplayClass10_0 { public Action> onComplete; internal void b__0(string output) { List list = new List(); string[] array = output.Split('\n'); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (!text2.StartsWith("{")) { continue; } try { PlaylistEntry playlistEntry = JsonUtility.FromJson(text2); if (!string.IsNullOrEmpty(playlistEntry.url)) { list.Add(playlistEntry); } } catch { } } onComplete?.Invoke(list); } } [CompilerGenerated] private sealed class <>c__DisplayClass11_0 { public ytModuleAPI <>4__this; public Action onComplete; public Action onError; internal void b__0(string output) { try { PlaylistInfo obj = <>4__this.ParsePlaylistInfo(output); onComplete(obj); } catch (Exception ex) { onError("Playlist parsing failed: " + ex.Message); } } } [CompilerGenerated] private sealed class <>c__DisplayClass12_0 { public ytModuleAPI <>4__this; public Action> onComplete; public Action onError; internal void b__0(string output) { try { List obj = <>4__this.ParseSearchResults(output); onComplete?.Invoke(obj); } catch (Exception ex) { onError?.Invoke("Search parsing failed: " + ex.Message); } } } [CompilerGenerated] private sealed class <>c__DisplayClass18_0 { public StringBuilder output; public StringBuilder errors; public Process proc; internal void b__0(object _, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) { output.AppendLine(e.Data); } } internal void b__1(object _, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) { errors.AppendLine(e.Data); ytModulePlugin.Logger.LogWarning((object)("[ytModule] yt-dlp: " + e.Data)); } } internal bool b__2() { return proc.HasExited; } } [CompilerGenerated] private sealed class <>c__DisplayClass5_0 { public ytModuleAPI <>4__this; public Action onComplete; public Action onError; internal void b__0(string output) { try { VideoMetadata obj = <>4__this.ParseMetadata(output); onComplete?.Invoke(obj); } catch (Exception ex) { onError?.Invoke("Metadata parsing failed: " + ex.Message); } } } [CompilerGenerated] private sealed class <>c__DisplayClass6_0 { public string outputPath; public ytModuleAPI <>4__this; public Action onComplete; internal void b__0(string output) { DownloadResult downloadResult = new DownloadResult { FilePath = outputPath, Success = File.Exists(outputPath + ".ogg") }; try { downloadResult.Metadata = <>4__this.ParseMetadata(output); } catch (Exception ex) { ytModulePlugin.Logger.LogWarning((object)("[ytModule] Metadata parsing failed: " + ex.Message)); downloadResult.Metadata = null; } onComplete?.Invoke(downloadResult); } } [CompilerGenerated] private sealed class <>c__DisplayClass7_0 { public string metadataPath; internal void b__0(DownloadResult result) { ytModulePlugin.Logger.LogInfo((object)$"[ytModule] Download complete - Success: {result.Success}, Metadata null: {result.Metadata == null}"); if (result.Metadata != null) { ytModulePlugin.Logger.LogInfo((object)("[ytModule] Metadata title: '" + result.Metadata.title + "'")); } if (result.Metadata != null) { SongMetadata songMetadata = new SongMetadata { title = (result.Metadata.title ?? "Unknown") }; string text = JsonUtility.ToJson((object)songMetadata); ytModulePlugin.Logger.LogInfo((object)("[ytModule] Writing metadata to: " + metadataPath + ", JSON: " + text)); File.WriteAllText(metadataPath, text); } else { ytModulePlugin.Logger.LogWarning((object)$"[ytModule] Metadata not written - Success: {result.Success}, Metadata: {result.Metadata != null}"); } } } [CompilerGenerated] private sealed class <>c__DisplayClass8_0 { public string outputPath; public VideoDownloadOptions options; public ytModuleAPI <>4__this; public Action onComplete; internal void b__0(string output) { DownloadResult downloadResult = new DownloadResult { FilePath = outputPath, Success = File.Exists(outputPath + "." + options.Container) }; try { downloadResult.Metadata = <>4__this.ParseMetadata(output); } catch (Exception ex) { ytModulePlugin.Logger.LogWarning((object)("[ytModule] Metadata parsing failed: " + ex.Message)); downloadResult.Metadata = null; } onComplete?.Invoke(downloadResult); } } [CompilerGenerated] private sealed class <>c__DisplayClass9_0 { public List entries; internal void b__0(List result) { entries = result; } } [CompilerGenerated] private sealed class d__6 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string url; public string outputPath; public DownloadOptions options; public Action onComplete; public Action onError; public ytModuleAPI <>4__this; private <>c__DisplayClass6_0 <>8__1; private List 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__6(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass6_0(); <>8__1.outputPath = outputPath; <>8__1.<>4__this = <>4__this; <>8__1.onComplete = onComplete; 5__2 = <>4__this.BuildDownloadArgs(url, <>8__1.outputPath, options); <>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.ExecuteYtDlp(5__2, delegate(string output) { DownloadResult downloadResult = new DownloadResult { FilePath = <>8__1.outputPath, Success = File.Exists(<>8__1.outputPath + ".ogg") }; try { downloadResult.Metadata = <>8__1.<>4__this.ParseMetadata(output); } catch (Exception ex) { ytModulePlugin.Logger.LogWarning((object)("[ytModule] Metadata parsing failed: " + ex.Message)); downloadResult.Metadata = null; } <>8__1.onComplete?.Invoke(downloadResult); }, onError)); <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__7 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string url; public string outputPath; public string metadataPath; public int maxSizeMb; public Action onError; public ytModuleAPI <>4__this; private <>c__DisplayClass7_0 <>8__1; private DownloadOptions 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass7_0(); <>8__1.metadataPath = metadataPath; 5__2 = new DownloadOptions { AudioFormat = "vorbis", Quality = 5, MaxSizeMb = maxSizeMb }; <>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.DownloadAudio(url, outputPath, 5__2, delegate(DownloadResult result) { ytModulePlugin.Logger.LogInfo((object)$"[ytModule] Download complete - Success: {result.Success}, Metadata null: {result.Metadata == null}"); if (result.Metadata != null) { ytModulePlugin.Logger.LogInfo((object)("[ytModule] Metadata title: '" + result.Metadata.title + "'")); } if (result.Metadata != null) { SongMetadata songMetadata = new SongMetadata { title = (result.Metadata.title ?? "Unknown") }; string text = JsonUtility.ToJson((object)songMetadata); ytModulePlugin.Logger.LogInfo((object)("[ytModule] Writing metadata to: " + <>8__1.metadataPath + ", JSON: " + text)); File.WriteAllText(<>8__1.metadataPath, text); } else { ytModulePlugin.Logger.LogWarning((object)$"[ytModule] Metadata not written - Success: {result.Success}, Metadata: {result.Metadata != null}"); } }, onError)); <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__8 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string url; public string outputPath; public VideoDownloadOptions options; public Action onComplete; public Action onError; public ytModuleAPI <>4__this; private <>c__DisplayClass8_0 <>8__1; private List 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__8(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass8_0(); <>8__1.outputPath = outputPath; <>8__1.options = options; <>8__1.<>4__this = <>4__this; <>8__1.onComplete = onComplete; 5__2 = <>4__this.BuildVideoDownloadArgs(url, <>8__1.outputPath, <>8__1.options); <>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.ExecuteYtDlp(5__2, delegate(string output) { DownloadResult downloadResult = new DownloadResult { FilePath = <>8__1.outputPath, Success = File.Exists(<>8__1.outputPath + "." + <>8__1.options.Container) }; try { downloadResult.Metadata = <>8__1.<>4__this.ParseMetadata(output); } catch (Exception ex) { ytModulePlugin.Logger.LogWarning((object)("[ytModule] Metadata parsing failed: " + ex.Message)); downloadResult.Metadata = null; } <>8__1.onComplete?.Invoke(downloadResult); }, onError)); <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__18 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public List args; public Action onSuccess; public Action onError; public ytModuleAPI <>4__this; private <>c__DisplayClass18_0 <>8__1; private ProcessStartInfo 5__2; private string 5__3; private string 5__4; private List.Enumerator <>s__5; private string 5__6; private string 5__7; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__18(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; 5__3 = null; 5__4 = null; <>s__5 = default(List.Enumerator); 5__6 = null; 5__7 = null; <>1__state = -2; } private bool MoveNext() { //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass18_0(); 5__2 = new ProcessStartInfo { FileName = <>4__this.ytDlpPath, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; if (!ytModulePlugin.Prod) { 5__4 = $"ytdlp_instance_{Guid.NewGuid():N}"; 5__3 = Path.Combine(<>4__this.tempDir, 5__4); Directory.CreateDirectory(5__3); 5__2.WorkingDirectory = 5__3; 5__2.EnvironmentVariables["XDG_CACHE_HOME"] = 5__3; 5__2.EnvironmentVariables["TEMP"] = 5__3; 5__2.EnvironmentVariables["TMP"] = 5__3; ytModulePlugin.Logger.LogInfo((object)("[ytModule] DEV MODE: Started process in isolated dir: " + 5__3)); 5__4 = null; } else { 5__3 = <>4__this.tempDir; 5__2.WorkingDirectory = <>4__this.tempDir; ytModulePlugin.Logger.LogInfo((object)"[ytModule] Started process in shared temp dir"); } <>s__5 = args.GetEnumerator(); try { while (<>s__5.MoveNext()) { 5__6 = <>s__5.Current; 5__2.ArgumentList.Add(5__6); 5__6 = null; } } finally { ((IDisposable)<>s__5).Dispose(); } <>s__5 = default(List.Enumerator); <>8__1.proc = new Process { StartInfo = 5__2 }; <>8__1.output = new StringBuilder(); <>8__1.errors = new StringBuilder(); <>8__1.proc.OutputDataReceived += delegate(object _, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) { <>8__1.output.AppendLine(e.Data); } }; <>8__1.proc.ErrorDataReceived += delegate(object _, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) { <>8__1.errors.AppendLine(e.Data); ytModulePlugin.Logger.LogWarning((object)("[ytModule] yt-dlp: " + e.Data)); } }; <>8__1.proc.Start(); <>8__1.proc.BeginOutputReadLine(); <>8__1.proc.BeginErrorReadLine(); <>2__current = (object)new WaitUntil((Func)(() => <>8__1.proc.HasExited)); <>1__state = 1; return true; case 1: <>1__state = -1; if (!ytModulePlugin.Prod && 5__3 != <>4__this.tempDir) { try { if (Directory.Exists(5__3)) { Directory.Delete(5__3, recursive: true); } } catch { } } if (<>8__1.proc.ExitCode == 0) { onSuccess?.Invoke(<>8__1.output.ToString()); } else { 5__7 = ((<>8__1.errors.Length > 0) ? <>8__1.errors.ToString() : "Unknown yt-dlp error"); onError?.Invoke(5__7); 5__7 = null; } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__5 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string url; public Action onComplete; public Action onError; public ytModuleAPI <>4__this; private <>c__DisplayClass5_0 <>8__1; private List 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__5(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass5_0(); <>8__1.<>4__this = <>4__this; <>8__1.onComplete = onComplete; <>8__1.onError = onError; 5__2 = <>4__this.BuildMetadataArgs(url); <>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.ExecuteYtDlp(5__2, delegate(string output) { try { VideoMetadata obj = <>8__1.<>4__this.ParseMetadata(output); <>8__1.onComplete?.Invoke(obj); } catch (Exception ex) { <>8__1.onError?.Invoke("Metadata parsing failed: " + ex.Message); } }, <>8__1.onError)); <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__11 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string playlistUrl; public Action onComplete; public Action onError; public ytModuleAPI <>4__this; private <>c__DisplayClass11_0 <>8__1; private List 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__11(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass11_0(); <>8__1.<>4__this = <>4__this; <>8__1.onComplete = onComplete; <>8__1.onError = onError; 5__2 = <>4__this.BuildPlaylistInfoArgs(playlistUrl); <>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.ExecuteYtDlp(5__2, delegate(string output) { try { PlaylistInfo obj = <>8__1.<>4__this.ParsePlaylistInfo(output); <>8__1.onComplete(obj); } catch (Exception ex) { <>8__1.onError("Playlist parsing failed: " + ex.Message); } }, <>8__1.onError)); <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__9 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string sourceUrl; public int maxCount; public Action> onComplete; public Action onError; public string playlistId; public ytModuleAPI <>4__this; private <>c__DisplayClass9_0 <>8__1; private string 5__2; private List 5__3; private List.Enumerator <>s__4; private PlaylistEntry 5__5; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__9(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; 5__3 = null; <>s__4 = default(List.Enumerator); 5__5 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass9_0(); <>8__1.entries = null; <>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.GetRelatedVideosWithMetadata(sourceUrl, maxCount, delegate(List result) { <>8__1.entries = result; }, onError, playlistId)); <>1__state = 1; return true; case 1: <>1__state = -1; if (<>8__1.entries != null) { 5__2 = (string.IsNullOrEmpty(playlistId) ? <>4__this.GetVideoIdFromUrl(sourceUrl) : null); 5__3 = new List(<>8__1.entries.Count); <>s__4 = <>8__1.entries.GetEnumerator(); try { while (<>s__4.MoveNext()) { 5__5 = <>s__4.Current; if (5__2 == null || !(<>4__this.GetVideoIdFromUrl(5__5.url) == 5__2)) { 5__3.Add(5__5.url); 5__5 = null; } } } finally { ((IDisposable)<>s__4).Dispose(); } <>s__4 = default(List.Enumerator); onComplete?.Invoke(5__3); 5__2 = null; 5__3 = null; } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__10 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string sourceUrl; public int maxCount; public Action> onComplete; public Action onError; public string playlistId; public ytModuleAPI <>4__this; private <>c__DisplayClass10_0 <>8__1; private string 5__2; private List 5__3; private string 5__4; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__10(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; 5__3 = null; 5__4 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass10_0(); <>8__1.onComplete = onComplete; if (!string.IsNullOrEmpty(playlistId)) { 5__2 = "https://www.youtube.com/playlist?list=" + playlistId; } else { 5__4 = <>4__this.GetVideoIdFromUrl(sourceUrl); if (string.IsNullOrEmpty(5__4)) { onError?.Invoke("Invalid YouTube URL"); return false; } 5__2 = "https://www.youtube.com/watch?v=" + 5__4 + "&list=RD" + 5__4; 5__4 = null; } 5__3 = new List { "--extractor-args", "youtube:player_client=web,default", "--flat-playlist", "--dump-json", 5__2 }; <>4__this.AddCookieArgs(5__3); if (string.IsNullOrEmpty(playlistId)) { 5__3.Add("--playlist-end"); 5__3.Add(maxCount.ToString()); } <>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.ExecuteYtDlp(5__3, delegate(string output) { List list = new List(); string[] array = output.Split('\n'); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (text2.StartsWith("{")) { try { PlaylistEntry playlistEntry = JsonUtility.FromJson(text2); if (!string.IsNullOrEmpty(playlistEntry.url)) { list.Add(playlistEntry); } } catch { } } } <>8__1.onComplete?.Invoke(list); }, onError)); <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__12 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string query; public Action> onComplete; public Action onError; public SearchOptions options; public ytModuleAPI <>4__this; private <>c__DisplayClass12_0 <>8__1; private List 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__12(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass12_0(); <>8__1.<>4__this = <>4__this; <>8__1.onComplete = onComplete; <>8__1.onError = onError; if (options == null) { options = new SearchOptions(); } 5__2 = <>4__this.BuildSearchArgs(query, options); <>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.ExecuteYtDlp(5__2, delegate(string output) { try { List obj = <>8__1.<>4__this.ParseSearchResults(output); <>8__1.onComplete?.Invoke(obj); } catch (Exception ex) { <>8__1.onError?.Invoke("Search parsing failed: " + ex.Message); } }, <>8__1.onError)); <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private readonly ytModulePlugin plugin; private readonly string ytDlpPath; private readonly string ffmpegPath; private readonly string tempDir; internal ytModuleAPI(ytModulePlugin plugin) { this.plugin = plugin; string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)plugin).Info.Location); ytDlpPath = Path.Combine(directoryName, "yt-dlp.exe"); ffmpegPath = Path.Combine(directoryName, "ffmpeg.exe"); tempDir = Path.Combine(Path.GetTempPath(), "sPEAKerYT"); Directory.CreateDirectory(tempDir); } [IteratorStateMachine(typeof(d__5))] public IEnumerator GetMetadata(string url, Action onComplete, Action onError = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__5(0) { <>4__this = this, url = url, onComplete = onComplete, onError = onError }; } [IteratorStateMachine(typeof(d__6))] public IEnumerator DownloadAudio(string url, string outputPath, DownloadOptions options, Action onComplete, Action onError = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__6(0) { <>4__this = this, url = url, outputPath = outputPath, options = options, onComplete = onComplete, onError = onError }; } [IteratorStateMachine(typeof(d__7))] public IEnumerator DownloadAudioOnly(string url, string outputPath, string metadataPath, int maxSizeMb = 100, Action onError = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__7(0) { <>4__this = this, url = url, outputPath = outputPath, metadataPath = metadataPath, maxSizeMb = maxSizeMb, onError = onError }; } [IteratorStateMachine(typeof(d__8))] public IEnumerator DownloadVideo(string url, string outputPath, VideoDownloadOptions options, Action onComplete, Action onError = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__8(0) { <>4__this = this, url = url, outputPath = outputPath, options = options, onComplete = onComplete, onError = onError }; } [IteratorStateMachine(typeof(d__9))] public IEnumerator GetRelatedVideos(string sourceUrl, int maxCount = 10, Action> onComplete = null, Action onError = null, string playlistId = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__9(0) { <>4__this = this, sourceUrl = sourceUrl, maxCount = maxCount, onComplete = onComplete, onError = onError, playlistId = playlistId }; } [IteratorStateMachine(typeof(d__10))] public IEnumerator GetRelatedVideosWithMetadata(string sourceUrl, int maxCount = 10, Action> onComplete = null, Action onError = null, string playlistId = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__10(0) { <>4__this = this, sourceUrl = sourceUrl, maxCount = maxCount, onComplete = onComplete, onError = onError, playlistId = playlistId }; } [IteratorStateMachine(typeof(d__11))] public IEnumerator GetPlaylistInfo(string playlistUrl, Action onComplete, Action onError = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__11(0) { <>4__this = this, playlistUrl = playlistUrl, onComplete = onComplete, onError = onError }; } [IteratorStateMachine(typeof(d__12))] public IEnumerator SearchVideos(string query, Action> onComplete, Action onError = null, SearchOptions options = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__12(0) { <>4__this = this, query = query, onComplete = onComplete, onError = onError, options = options }; } private List BuildMetadataArgs(string url) { List list = new List(2) { "--dump-json", "--no-download" }; AddPlatformSpecificArgs(list, url); AddCookieArgs(list); list.Add(url); return list; } private List BuildDownloadArgs(string url, string outputPath, DownloadOptions options) { List list = new List { "--no-playlist", "--retries", "3", "--fragment-retries", "3", "--abort-on-unavailable-fragment", "-x", "--audio-format", options.AudioFormat, "--audio-quality", options.Quality.ToString(), "--ffmpeg-location", ffmpegPath, "--max-filesize", $"{options.MaxSizeMb}M", "--dump-json", "--no-simulate", "-o", outputPath }; AddPlatformSpecificArgs(list, url); if (options.AudioFormat == "vorbis") { list.Add("--postprocessor-args"); list.Add("ffmpeg:-ar 44100 -ac 2"); } AddCookieArgs(list); list.Add(url); return list; } private List BuildVideoDownloadArgs(string url, string outputPath, VideoDownloadOptions options) { List list = new List { "--no-playlist", "--retries", "3", "--fragment-retries", "3", "--abort-on-unavailable-fragment", "--format", options.FormatSelector, "--merge-output-format", options.Container, "--max-filesize", $"{options.MaxSizeMb}M", "--dump-json", "--no-simulate", "-o", outputPath }; AddPlatformSpecificArgs(list, url); if (!string.IsNullOrEmpty(ffmpegPath)) { list.Add("--ffmpeg-location"); list.Add(ffmpegPath); } if (!string.IsNullOrEmpty(options.PostProcessorArgs)) { list.Add("--postprocessor-args"); list.Add(options.PostProcessorArgs); } AddCookieArgs(list); list.Add(url); return list; } private List BuildPlaylistInfoArgs(string playlistUrl) { List list = new List { "--flat-playlist", "--dump-json", "--compat-options", "no-youtube-unavailable-videos" }; AddPlatformSpecificArgs(list, playlistUrl); AddCookieArgs(list); list.Add(playlistUrl); return list; } private List BuildSearchArgs(string query, SearchOptions options) { string platform = options.Platform; if (1 == 0) { } string text = platform switch { "youtube" => "ytsearch", "youtube-date" => "ytsearchdate", "soundcloud" => "scsearch", "bandcamp" => "bcsearch", "vimeo" => "vmsearch", _ => "ytsearch", }; if (1 == 0) { } string arg = text; List list = new List { "--flat-playlist", "-i", "--dump-json", $"{arg}{options.MaxResults}:{query}" }; AddCookieArgs(list); return list; } [IteratorStateMachine(typeof(d__18))] private IEnumerator ExecuteYtDlp(List args, Action onSuccess, Action onError) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__18(0) { <>4__this = this, args = args, onSuccess = onSuccess, onError = onError }; } private VideoMetadata ParseMetadata(string jsonOutput) { string[] array = jsonOutput.Split('\n'); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (text2.StartsWith("{") && text2.EndsWith("}")) { return JsonUtility.FromJson(text2); } } throw new Exception("No valid JSON metadata found"); } private PlaylistInfo ParsePlaylistInfo(string jsonOutput) { string[] array = jsonOutput.Split('\n'); List list = new List(); string[] array2 = array; foreach (string text in array2) { if (!string.IsNullOrWhiteSpace(text)) { list.Add(text); } } if (list.Count == 0) { throw new Exception("No playlist data found"); } PlaylistInfo playlistInfo = JsonUtility.FromJson(list[0]); List list2 = new List(); foreach (string item in list) { string text2 = item.Trim(); if (!text2.StartsWith("{")) { continue; } try { PlaylistEntry playlistEntry = JsonUtility.FromJson(text2); if (!string.IsNullOrEmpty(playlistEntry.url)) { list2.Add(playlistEntry); } } catch { } } playlistInfo.entries = list2.ToArray(); return playlistInfo; } private List ParseSearchResults(string jsonOutput) { List list = new List(); string[] array = jsonOutput.Split('\n'); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (text2.StartsWith("{") && text2.EndsWith("}")) { try { VideoMetadata item = JsonUtility.FromJson(text2); list.Add(item); } catch { } } } return list; } public string GetVideoIdFromUrl(string url) { try { Uri uri = new Uri(url); if (uri.Host.Contains("youtu.be")) { return uri.AbsolutePath.Trim('/'); } if (uri.Host.Contains("youtube.com")) { return HttpUtility.ParseQueryString(uri.Query)["v"]; } } catch { } using SHA1 sHA = SHA1.Create(); return BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(url)), 0, 4).Replace("-", ""); } public bool IsValidYouTubeUrl(string url) { if (!Uri.TryCreate(url, UriKind.Absolute, out Uri result)) { return false; } string text = result.Host.ToLowerInvariant(); return text.Contains("youtube.com") || text.Contains("youtu.be"); } private void AddCookieArgs(List args) { string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)plugin).Info.Location); string text = FindCookiesFile(directoryName); if (text != null) { ytModulePlugin.Logger.LogInfo((object)("[ytModule] Using cookies from " + Path.GetFileName(text))); args.Add("--cookies"); args.Add(text); } } private string FindCookiesFile(string directory) { if (!Directory.Exists(directory)) { return null; } string text = Path.Combine(directory, "cookies.txt"); if (File.Exists(text)) { return text; } string[] files = Directory.GetFiles(directory, "*cookies.txt", SearchOption.TopDirectoryOnly); if (files.Length != 0) { if (files.Length > 1) { ytModulePlugin.Logger.LogWarning((object)$"[ytModule] Found {files.Length} cookie files, using: {Path.GetFileName(files[0])}"); } return files[0]; } return null; } public Platform DetectPlatform(string url) { if (!Uri.TryCreate(url, UriKind.Absolute, out Uri result)) { return Platform.Other; } string text = result.Host.ToLowerInvariant(); if (text.Contains("youtube.com") || text.Contains("youtu.be")) { return Platform.YouTube; } if (text.Contains("soundcloud.com")) { return Platform.SoundCloud; } if (text.Contains("vimeo.com")) { return Platform.Vimeo; } if (text.Contains("bandcamp.com")) { return Platform.Bandcamp; } return Platform.Other; } private void AddPlatformSpecificArgs(List args, string url) { if (DetectPlatform(url) == Platform.YouTube) { args.Add("--extractor-args"); args.Add("youtube:player_client=web,default"); } } } public class SearchOptions { public int MaxResults = 10; public string Platform = "youtube"; } public class DownloadOptions { public string AudioFormat = "vorbis"; public int Quality = 5; public int MaxSizeMb = 100; public bool ExtractMetadata = true; } public class VideoDownloadOptions { public string FormatSelector = "bestvideo+bestaudio/best"; public string Container = "mp4"; public int MaxSizeMb = 500; public string PostProcessorArgs = null; } public class DownloadResult { public string FilePath; public VideoMetadata Metadata; public bool Success; public string ErrorMessage; } public class VideoMetadata { public string title; public string id; public string uploader; public float duration; public string webpage_url; public string playlist; public string playlist_title; public int playlist_index; } public class PlaylistInfo { public string title; public string id; public string uploader; public int playlist_count; public PlaylistEntry[] entries; } public class PlaylistEntry { public string id; public string title; public string url; public int playlist_index; } public class SongMetadata { public string title; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }