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)
{
}
}
}