Decompiled source of ytModule v1.0.1

plugins/onlystar-ytModule.dll

Decompiled 3 days ago
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.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+a59d47f1d65333d4458a6ae70f9fe8d76002c3c4")]
[assembly: AssemblyProduct("onlystar-ytModule")]
[assembly: AssemblyTitle("YTModule")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.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.1")]
	public class ytModulePlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "onlystar.ytModule";

		public const string PluginName = "ytModule";

		public const string PluginVersion = "1.0.1";

		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.1 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
	{
		[CompilerGenerated]
		private sealed class <>c__DisplayClass10_0
		{
			public ytModuleAPI <>4__this;

			public Action<List<VideoMetadata>> onComplete;

			public Action<string> onError;

			internal void <SearchVideos>b__0(string output)
			{
				try
				{
					List<VideoMetadata> obj = <>4__this.ParseSearchResults(output);
					onComplete?.Invoke(obj);
				}
				catch (Exception ex)
				{
					onError?.Invoke("Search parsing failed: " + ex.Message);
				}
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass15_0
		{
			public StringBuilder output;

			public StringBuilder errors;

			public Process proc;

			internal void <ExecuteYtDlp>b__0(object _, DataReceivedEventArgs e)
			{
				if (!string.IsNullOrEmpty(e.Data))
				{
					output.AppendLine(e.Data);
				}
			}

			internal void <ExecuteYtDlp>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 <ExecuteYtDlp>b__2()
			{
				return proc.HasExited;
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass5_0
		{
			public ytModuleAPI <>4__this;

			public Action<VideoMetadata> onComplete;

			public Action<string> onError;

			internal void <GetMetadata>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<DownloadResult> onComplete;

			internal void <DownloadAudio>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 <DownloadAudioOnly>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 ytModuleAPI <>4__this;

			public string playlistId;

			public Action<List<string>> onComplete;

			public string sourceUrl;

			internal void <GetRelatedVideos>b__0(string output)
			{
				List<string> list = new List<string>();
				string[] array = output.Split('\n');
				string[] array2 = array;
				foreach (string text in array2)
				{
					string text2 = text.Trim();
					if (!string.IsNullOrEmpty(text2) && <>4__this.IsValidYouTubeUrl(text2))
					{
						list.Add(text2);
					}
				}
				if (!string.IsNullOrEmpty(playlistId))
				{
					onComplete(list);
					return;
				}
				string videoIdFromUrl = <>4__this.GetVideoIdFromUrl(sourceUrl);
				List<string> list2 = new List<string>();
				foreach (string item in list)
				{
					if (<>4__this.GetVideoIdFromUrl(item) != videoIdFromUrl)
					{
						list2.Add(item);
					}
				}
				onComplete(list2);
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass9_0
		{
			public ytModuleAPI <>4__this;

			public Action<PlaylistInfo> onComplete;

			public Action<string> onError;

			internal void <GetPlaylistInfo>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 <DownloadAudio>d__6 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string url;

			public string outputPath;

			public DownloadOptions options;

			public Action<DownloadResult> onComplete;

			public Action<string> onError;

			public ytModuleAPI <>4__this;

			private <>c__DisplayClass6_0 <>8__1;

			private List<string> <args>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DownloadAudio>d__6(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<args>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;
					<args>5__2 = <>4__this.BuildDownloadArgs(url, <>8__1.outputPath, options);
					<>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.ExecuteYtDlp(<args>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 <DownloadAudioOnly>d__7 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string url;

			public string outputPath;

			public string metadataPath;

			public int maxSizeMb;

			public Action<string> onError;

			public ytModuleAPI <>4__this;

			private <>c__DisplayClass7_0 <>8__1;

			private DownloadOptions <options>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DownloadAudioOnly>d__7(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<options>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;
					<options>5__2 = new DownloadOptions
					{
						AudioFormat = "vorbis",
						Quality = 5,
						MaxSizeMb = maxSizeMb
					};
					<>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.DownloadAudio(url, outputPath, <options>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 <ExecuteYtDlp>d__15 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public List<string> args;

			public Action<string> onSuccess;

			public Action<string> onError;

			public ytModuleAPI <>4__this;

			private <>c__DisplayClass15_0 <>8__1;

			private ProcessStartInfo <psi>5__2;

			private string <instanceTempDir>5__3;

			private string <instanceId>5__4;

			private List<string>.Enumerator <>s__5;

			private string <arg>5__6;

			private string <errorMsg>5__7;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ExecuteYtDlp>d__15(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<psi>5__2 = null;
				<instanceTempDir>5__3 = null;
				<instanceId>5__4 = null;
				<>s__5 = default(List<string>.Enumerator);
				<arg>5__6 = null;
				<errorMsg>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__DisplayClass15_0();
					<psi>5__2 = new ProcessStartInfo
					{
						FileName = <>4__this.ytDlpPath,
						RedirectStandardOutput = true,
						RedirectStandardError = true,
						UseShellExecute = false,
						CreateNoWindow = true
					};
					if (!ytModulePlugin.Prod)
					{
						<instanceId>5__4 = $"ytdlp_instance_{Guid.NewGuid():N}";
						<instanceTempDir>5__3 = Path.Combine(<>4__this.tempDir, <instanceId>5__4);
						Directory.CreateDirectory(<instanceTempDir>5__3);
						<psi>5__2.WorkingDirectory = <instanceTempDir>5__3;
						<psi>5__2.EnvironmentVariables["XDG_CACHE_HOME"] = <instanceTempDir>5__3;
						<psi>5__2.EnvironmentVariables["TEMP"] = <instanceTempDir>5__3;
						<psi>5__2.EnvironmentVariables["TMP"] = <instanceTempDir>5__3;
						ytModulePlugin.Logger.LogInfo((object)("[ytModule] DEV MODE: Started process in isolated dir: " + <instanceTempDir>5__3));
						<instanceId>5__4 = null;
					}
					else
					{
						<instanceTempDir>5__3 = <>4__this.tempDir;
						<psi>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())
						{
							<arg>5__6 = <>s__5.Current;
							<psi>5__2.ArgumentList.Add(<arg>5__6);
							<arg>5__6 = null;
						}
					}
					finally
					{
						((IDisposable)<>s__5).Dispose();
					}
					<>s__5 = default(List<string>.Enumerator);
					<>8__1.proc = new Process
					{
						StartInfo = <psi>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<bool>)(() => <>8__1.proc.HasExited));
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if (!ytModulePlugin.Prod && <instanceTempDir>5__3 != <>4__this.tempDir)
					{
						try
						{
							if (Directory.Exists(<instanceTempDir>5__3))
							{
								Directory.Delete(<instanceTempDir>5__3, recursive: true);
							}
						}
						catch
						{
						}
					}
					if (<>8__1.proc.ExitCode == 0)
					{
						onSuccess?.Invoke(<>8__1.output.ToString());
					}
					else
					{
						<errorMsg>5__7 = ((<>8__1.errors.Length > 0) ? <>8__1.errors.ToString() : "Unknown yt-dlp error");
						onError?.Invoke(<errorMsg>5__7);
						<errorMsg>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 <GetMetadata>d__5 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string url;

			public Action<VideoMetadata> onComplete;

			public Action<string> onError;

			public ytModuleAPI <>4__this;

			private <>c__DisplayClass5_0 <>8__1;

			private List<string> <args>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <GetMetadata>d__5(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<args>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;
					<args>5__2 = <>4__this.BuildMetadataArgs(url);
					<>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.ExecuteYtDlp(<args>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 <GetPlaylistInfo>d__9 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string playlistUrl;

			public Action<PlaylistInfo> onComplete;

			public Action<string> onError;

			public ytModuleAPI <>4__this;

			private <>c__DisplayClass9_0 <>8__1;

			private List<string> <args>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <GetPlaylistInfo>d__9(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<args>5__2 = 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.<>4__this = <>4__this;
					<>8__1.onComplete = onComplete;
					<>8__1.onError = onError;
					<args>5__2 = <>4__this.BuildPlaylistInfoArgs(playlistUrl);
					<>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.ExecuteYtDlp(<args>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 <GetRelatedVideos>d__8 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string sourceUrl;

			public int maxCount;

			public Action<List<string>> onComplete;

			public Action<string> onError;

			public string playlistId;

			public ytModuleAPI <>4__this;

			private <>c__DisplayClass8_0 <>8__1;

			private string <targetUrl>5__2;

			private List<string> <args>5__3;

			private string <videoId>5__4;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <GetRelatedVideos>d__8(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<targetUrl>5__2 = null;
				<args>5__3 = null;
				<videoId>5__4 = 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.<>4__this = <>4__this;
					<>8__1.playlistId = playlistId;
					<>8__1.onComplete = onComplete;
					<>8__1.sourceUrl = sourceUrl;
					if (!string.IsNullOrEmpty(<>8__1.playlistId))
					{
						<targetUrl>5__2 = "https://www.youtube.com/playlist?list=" + <>8__1.playlistId;
					}
					else
					{
						<videoId>5__4 = <>4__this.GetVideoIdFromUrl(<>8__1.sourceUrl);
						if (string.IsNullOrEmpty(<videoId>5__4))
						{
							onError("Invalid YouTube URL");
							return false;
						}
						<targetUrl>5__2 = "https://www.youtube.com/watch?v=" + <videoId>5__4 + "&list=RD" + <videoId>5__4;
						<videoId>5__4 = null;
					}
					<args>5__3 = new List<string> { "--extractor-args", "youtube:player_client=web,default", "--flat-playlist", "--print", "https://www.youtube.com/watch?v=%(id)s", <targetUrl>5__2 };
					if (string.IsNullOrEmpty(<>8__1.playlistId))
					{
						<args>5__3.Add("--playlist-end");
						<args>5__3.Add(maxCount.ToString());
					}
					<>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.ExecuteYtDlp(<args>5__3, delegate(string output)
					{
						List<string> list = new List<string>();
						string[] array = output.Split('\n');
						string[] array2 = array;
						foreach (string text in array2)
						{
							string text2 = text.Trim();
							if (!string.IsNullOrEmpty(text2) && <>8__1.<>4__this.IsValidYouTubeUrl(text2))
							{
								list.Add(text2);
							}
						}
						if (!string.IsNullOrEmpty(<>8__1.playlistId))
						{
							<>8__1.onComplete(list);
						}
						else
						{
							string videoIdFromUrl = <>8__1.<>4__this.GetVideoIdFromUrl(<>8__1.sourceUrl);
							List<string> list2 = new List<string>();
							foreach (string item in list)
							{
								if (<>8__1.<>4__this.GetVideoIdFromUrl(item) != videoIdFromUrl)
								{
									list2.Add(item);
								}
							}
							<>8__1.onComplete(list2);
						}
					}, 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 <SearchVideos>d__10 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string query;

			public int maxResults;

			public Action<List<VideoMetadata>> onComplete;

			public Action<string> onError;

			public string platform;

			public ytModuleAPI <>4__this;

			private <>c__DisplayClass10_0 <>8__1;

			private List<string> <args>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SearchVideos>d__10(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<args>5__2 = 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.<>4__this = <>4__this;
					<>8__1.onComplete = onComplete;
					<>8__1.onError = onError;
					<args>5__2 = <>4__this.BuildSearchArgs(query, maxResults, platform);
					<>2__current = ((MonoBehaviour)<>4__this.plugin).StartCoroutine(<>4__this.ExecuteYtDlp(<args>5__2, delegate(string output)
					{
						try
						{
							List<VideoMetadata> 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(<GetMetadata>d__5))]
		public IEnumerator GetMetadata(string url, Action<VideoMetadata> onComplete, Action<string> onError = null)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <GetMetadata>d__5(0)
			{
				<>4__this = this,
				url = url,
				onComplete = onComplete,
				onError = onError
			};
		}

		[IteratorStateMachine(typeof(<DownloadAudio>d__6))]
		public IEnumerator DownloadAudio(string url, string outputPath, DownloadOptions options, Action<DownloadResult> onComplete, Action<string> onError = null)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DownloadAudio>d__6(0)
			{
				<>4__this = this,
				url = url,
				outputPath = outputPath,
				options = options,
				onComplete = onComplete,
				onError = onError
			};
		}

		[IteratorStateMachine(typeof(<DownloadAudioOnly>d__7))]
		public IEnumerator DownloadAudioOnly(string url, string outputPath, string metadataPath, int maxSizeMb = 100, Action<string> onError = null)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DownloadAudioOnly>d__7(0)
			{
				<>4__this = this,
				url = url,
				outputPath = outputPath,
				metadataPath = metadataPath,
				maxSizeMb = maxSizeMb,
				onError = onError
			};
		}

		[IteratorStateMachine(typeof(<GetRelatedVideos>d__8))]
		public IEnumerator GetRelatedVideos(string sourceUrl, int maxCount = 10, Action<List<string>> onComplete = null, Action<string> onError = null, string playlistId = null)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <GetRelatedVideos>d__8(0)
			{
				<>4__this = this,
				sourceUrl = sourceUrl,
				maxCount = maxCount,
				onComplete = onComplete,
				onError = onError,
				playlistId = playlistId
			};
		}

		[IteratorStateMachine(typeof(<GetPlaylistInfo>d__9))]
		public IEnumerator GetPlaylistInfo(string playlistUrl, Action<PlaylistInfo> onComplete, Action<string> onError = null)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <GetPlaylistInfo>d__9(0)
			{
				<>4__this = this,
				playlistUrl = playlistUrl,
				onComplete = onComplete,
				onError = onError
			};
		}

		[IteratorStateMachine(typeof(<SearchVideos>d__10))]
		public IEnumerator SearchVideos(string query, int maxResults, Action<List<VideoMetadata>> onComplete, Action<string> onError = null, string platform = "youtube")
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SearchVideos>d__10(0)
			{
				<>4__this = this,
				query = query,
				maxResults = maxResults,
				onComplete = onComplete,
				onError = onError,
				platform = platform
			};
		}

		private List<string> BuildMetadataArgs(string url)
		{
			return new List<string>(5) { "--dump-json", "--no-download", "--extractor-args", "youtube:player_client=web,default", url };
		}

		private List<string> BuildDownloadArgs(string url, string outputPath, DownloadOptions options)
		{
			List<string> list = new List<string>
			{
				"--no-playlist",
				"--retries",
				"3",
				"--fragment-retries",
				"3",
				"--abort-on-unavailable-fragment",
				"--extractor-args",
				"youtube:player_client=web,default",
				"-x",
				"--audio-format",
				options.AudioFormat,
				"--audio-quality",
				options.Quality.ToString(),
				"--ffmpeg-location",
				ffmpegPath,
				"--max-filesize",
				$"{options.MaxSizeMb}M",
				"--dump-json",
				"--no-simulate",
				"-o",
				outputPath,
				url
			};
			if (options.AudioFormat == "vorbis")
			{
				list.Add("--postprocessor-args");
				list.Add("ffmpeg:-ar 44100 -ac 2");
			}
			return list;
		}

		private List<string> BuildPlaylistInfoArgs(string playlistUrl)
		{
			return new List<string> { "--flat-playlist", "--dump-json", "--extractor-args", "youtube:player_client=web,default", playlistUrl };
		}

		private List<string> BuildSearchArgs(string query, int maxResults, string platform = "youtube")
		{
			if (1 == 0)
			{
			}
			string text = platform switch
			{
				"youtube" => "ytsearch", 
				"youtube-date" => "ytsearchdate", 
				"soundcloud" => "scsearch", 
				"bandcamp" => "bcsearch", 
				"google-videos" => "gvsearch", 
				_ => "ytsearch", 
			};
			if (1 == 0)
			{
			}
			string arg = text;
			return new List<string>
			{
				"--dump-json",
				"--extractor-args",
				"youtube:player_client=web,default",
				$"{arg}{maxResults}:{query}"
			};
		}

		[IteratorStateMachine(typeof(<ExecuteYtDlp>d__15))]
		private IEnumerator ExecuteYtDlp(List<string> args, Action<string> onSuccess, Action<string> onError)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ExecuteYtDlp>d__15(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<VideoMetadata>(text2);
				}
			}
			throw new Exception("No valid JSON metadata found");
		}

		private PlaylistInfo ParsePlaylistInfo(string jsonOutput)
		{
			string[] array = jsonOutput.Split('\n');
			List<string> list = new List<string>();
			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<PlaylistInfo>(list[0]);
			List<PlaylistEntry> list2 = new List<PlaylistEntry>();
			foreach (string item in list)
			{
				string text2 = item.Trim();
				if (!text2.StartsWith("{"))
				{
					continue;
				}
				try
				{
					PlaylistEntry playlistEntry = JsonUtility.FromJson<PlaylistEntry>(text2);
					if (!string.IsNullOrEmpty(playlistEntry.url))
					{
						list2.Add(playlistEntry);
					}
				}
				catch
				{
				}
			}
			playlistInfo.entries = list2.ToArray();
			return playlistInfo;
		}

		private List<VideoMetadata> ParseSearchResults(string jsonOutput)
		{
			List<VideoMetadata> list = new List<VideoMetadata>();
			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<VideoMetadata>(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");
		}
	}
	public class DownloadOptions
	{
		public string AudioFormat = "vorbis";

		public int Quality = 5;

		public int MaxSizeMb = 100;

		public bool ExtractMetadata = true;
	}
	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)
		{
		}
	}
}