Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of YoutubeBoomBox v1.1.0
Plugins/YoutubeBoomBox.dll
Decompiled a year agousing System; using System.Collections; using System.Collections.Concurrent; 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.Permissions; using System.Threading.Tasks; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("YoutubeBoomBox")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Streams youtube playlists and videos to ingame boombox.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+4444ff9dc260a77207078e9c5b31504859739239")] [assembly: AssemblyProduct("YoutubeBoomBox")] [assembly: AssemblyTitle("YoutubeBoomBox")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace YoutubeBoomBox { public class AudioQueue { private static readonly List<string> _inputs = new List<string>(); private static int _ptr = 0; public static void Add(string input) { if (input.Length == 11 && !input.Contains(" ")) { _inputs.Add("https://www.youtube.com/watch?v=" + input); } else { _inputs.Add("ytsearch:" + input); } } public static void Clear() { _inputs.Clear(); } public static bool IsEmpty() { return _inputs.Count == 0; } public static string Next() { string result = _inputs[_ptr]; _ptr = (_ptr + 1) % _inputs.Count; return result; } } public class AudioStreamer : MonoBehaviour { [CompilerGenerated] private sealed class <>c__DisplayClass12_0 { public string audioUrl; internal void <StreamAudio>b__0(string aUrl) { audioUrl = aUrl; } } [CompilerGenerated] private sealed class <GetYouTubeAudioUrl>d__14 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public AudioStreamer <>4__this; public string input; public string ytDlpPath; public Action<string> onUrlFound; private Process <process>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetYouTubeAudioUrl>d__14(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <process>5__2 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; AudioStreamer audioStreamer = <>4__this; switch (num) { default: return false; case 0: { <>1__state = -1; audioStreamer._isFound = false; YoutubeBoomBoxPlugin.Logger.LogInfo((object)"Finding audio url..."); string arguments = ((!input.StartsWith("ytsearch:")) ? ("-f bestaudio --no-playlist -g -- \"" + input + "\"") : ("-f bestaudio --match-filter \"duration < 1800\" \"" + input + "\" -g")); <process>5__2 = new Process { StartInfo = new ProcessStartInfo { FileName = ytDlpPath, Arguments = arguments, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true } }; <process>5__2.Start(); break; } case 1: <>1__state = -1; break; } if (!<process>5__2.HasExited) { <>2__current = null; <>1__state = 1; return true; } string text = <process>5__2.StandardOutput.ReadToEnd().Trim(); string arg = <process>5__2.StandardError.ReadToEnd().Trim(); if (<process>5__2.ExitCode != 0 || string.IsNullOrEmpty(text)) { YoutubeBoomBoxPlugin.Logger.LogError((object)$"yt-dlp failed (Exit Code: {<process>5__2.ExitCode}): {arg}"); onUrlFound(null); } else { YoutubeBoomBoxPlugin.Logger.LogInfo((object)"Found!"); onUrlFound(text); } audioStreamer._isFound = true; 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 <LoadAudio>d__13 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public AudioStreamer <>4__this; public Action<AudioClip> onLoaded; private UnityWebRequest <www>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <LoadAudio>d__13(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <www>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Invalid comparison between Unknown and I4 //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Invalid comparison between Unknown and I4 bool result; try { int num = <>1__state; AudioStreamer audioStreamer = <>4__this; switch (num) { default: result = false; break; case 0: <>1__state = -1; audioStreamer._isLoading = true; YoutubeBoomBoxPlugin.Logger.LogInfo((object)"Loading audio..."); <www>5__2 = UnityWebRequestMultimedia.GetAudioClip("file://" + audioStreamer._streamFilePath, (AudioType)14); <>1__state = -3; <>2__current = <www>5__2.SendWebRequest(); <>1__state = 1; result = true; break; case 1: <>1__state = -3; if ((int)<www>5__2.result == 2 || (int)<www>5__2.result == 3) { YoutubeBoomBoxPlugin.Logger.LogError((object)("Error loading audio: " + <www>5__2.error)); } else { YoutubeBoomBoxPlugin.Logger.LogInfo((object)"Loaded!"); onLoaded(DownloadHandlerAudioClip.GetContent(<www>5__2)); } audioStreamer._isLoading = false; result = false; <>m__Finally1(); break; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<www>5__2 != null) { ((IDisposable)<www>5__2).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <StartStreaming>d__10 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public AudioStreamer <>4__this; public string input; public Action<AudioClip> onLoaded; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <StartStreaming>d__10(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; AudioStreamer audioStreamer = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = audioStreamer.StreamAudio(input); <>1__state = 1; return true; case 1: <>1__state = -1; goto IL_006c; case 2: <>1__state = -1; goto IL_006c; case 3: <>1__state = -1; break; case 4: { <>1__state = -1; break; } IL_006c: if (audioStreamer._isStreaming) { <>2__current = null; <>1__state = 2; return true; } <>2__current = audioStreamer.LoadAudio(onLoaded); <>1__state = 3; return true; } if (audioStreamer._isLoading) { <>2__current = null; <>1__state = 4; return true; } 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 <StreamAudio>d__12 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public AudioStreamer <>4__this; public string url; private <>c__DisplayClass12_0 <>8__1; private Process <process>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <StreamAudio>d__12(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; <process>5__2 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; AudioStreamer audioStreamer = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass12_0(); audioStreamer._isStreaming = true; <>8__1.audioUrl = null; <>2__current = audioStreamer.GetYouTubeAudioUrl(audioStreamer._ytDlpPath, url, delegate(string aUrl) { <>8__1.audioUrl = aUrl; }); <>1__state = 1; return true; case 1: <>1__state = -1; goto IL_009d; case 2: <>1__state = -1; goto IL_009d; case 3: { <>1__state = -1; break; } IL_009d: if (!audioStreamer._isFound) { <>2__current = null; <>1__state = 2; return true; } YoutubeBoomBoxPlugin.Logger.LogInfo((object)"Starting audio file stream..."); if (string.IsNullOrEmpty(<>8__1.audioUrl)) { YoutubeBoomBoxPlugin.Logger.LogError((object)"Failed to get YouTube audio URL."); return false; } <process>5__2 = new Process { StartInfo = new ProcessStartInfo { FileName = audioStreamer._ffmpegPath, Arguments = "-y -nostdin -loglevel error -i \"" + <>8__1.audioUrl + "\" -vn -acodec libvorbis -f ogg \"" + audioStreamer._streamFilePath + "\"", RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true } }; <process>5__2.Start(); <process>5__2.BeginOutputReadLine(); <process>5__2.BeginErrorReadLine(); break; } if (!<process>5__2.HasExited) { <>2__current = null; <>1__state = 3; return true; } YoutubeBoomBoxPlugin.Logger.LogInfo((object)"Streamed."); audioStreamer._isStreaming = false; 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 string _streamFilePath; private string _ytDlpPath; private string _ffmpegPath; private static AudioStreamer _instance; private bool _isStreaming; private bool _isLoading; private bool _isFound = true; public static AudioStreamer Instance { get { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_instance == (Object)null) { _instance = new GameObject("AudioStreamer").AddComponent<AudioStreamer>(); Object.DontDestroyOnLoad((Object)(object)((Component)_instance).gameObject); } return _instance; } } private void Awake() { _streamFilePath = Path.Combine(Application.temporaryCachePath, "stream.ogg"); string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); _ffmpegPath = Path.Combine(directoryName, "ffmpeg.exe"); _ytDlpPath = Path.Combine(directoryName, "yt-dlp.exe"); } public void Load(string input, Action<AudioClip> onLoaded) { ((MonoBehaviour)this).StartCoroutine(StartStreaming(input, onLoaded)); } [IteratorStateMachine(typeof(<StartStreaming>d__10))] private IEnumerator StartStreaming(string input, Action<AudioClip> onLoaded) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <StartStreaming>d__10(0) { <>4__this = this, input = input, onLoaded = onLoaded }; } [IteratorStateMachine(typeof(<StreamAudio>d__12))] private IEnumerator StreamAudio(string url) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <StreamAudio>d__12(0) { <>4__this = this, url = url }; } [IteratorStateMachine(typeof(<LoadAudio>d__13))] private IEnumerator LoadAudio(Action<AudioClip> onLoaded) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <LoadAudio>d__13(0) { <>4__this = this, onLoaded = onLoaded }; } [IteratorStateMachine(typeof(<GetYouTubeAudioUrl>d__14))] private IEnumerator GetYouTubeAudioUrl(string ytDlpPath, string input, Action<string> onUrlFound) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <GetYouTubeAudioUrl>d__14(0) { <>4__this = this, ytDlpPath = ytDlpPath, input = input, onUrlFound = onUrlFound }; } } public class DirectAudioStreamer : MonoBehaviour { [CompilerGenerated] private sealed class <>c__DisplayClass10_0 { public Process process; public DirectAudioStreamer <>4__this; internal void <StreamFromYt>b__0() { byte[] array = new byte[4096]; using Stream stream = process.StandardOutput.BaseStream; int num; while ((num = stream.Read(array, 0, array.Length)) > 0) { for (int i = 0; i < num; i += 2) { short num2 = BitConverter.ToInt16(array, i); <>4__this._sampleBuffer.Enqueue((float)num2 / 32768f); } } } } [CompilerGenerated] private sealed class <StreamFromYt>d__10 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public DirectAudioStreamer <>4__this; public string input; public Action<AudioClip> onStreaming; private <>c__DisplayClass10_0 <>8__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <StreamFromYt>d__10(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; <>1__state = -2; } private bool MoveNext() { //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown int num = <>1__state; DirectAudioStreamer directAudioStreamer = <>4__this; switch (num) { default: return false; case 0: { <>1__state = -1; <>8__1 = new <>c__DisplayClass10_0(); <>8__1.<>4__this = <>4__this; <>8__1.process = new Process { StartInfo = new ProcessStartInfo { FileName = "cmd.exe", Arguments = directAudioStreamer.YtDlpPath + " -q -f bestaudio $" + input + " -o - | $" + directAudioStreamer.FfmpegPath + " -hide_banner -loglevel error -i pipe: -map 0 -c copy -ac 2 -f s16le -ar 48000 pipe:", CreateNoWindow = true, UseShellExecute = false, RedirectStandardInput = true, RedirectStandardOutput = true } }; <>8__1.process.Start(); Task.Run(delegate { byte[] array = new byte[4096]; using Stream stream = <>8__1.process.StandardOutput.BaseStream; int num2; while ((num2 = stream.Read(array, 0, array.Length)) > 0) { for (int i = 0; i < num2; i += 2) { short num3 = BitConverter.ToInt16(array, i); <>8__1.<>4__this._sampleBuffer.Enqueue((float)num3 / 32768f); } } }); AudioClip obj = AudioClip.Create("Stream", int.MaxValue, 2, 48000, true, new PCMReaderCallback(directAudioStreamer.OnAudioRead)); onStreaming(obj); break; } case 1: <>1__state = -1; break; } if (!<>8__1.process.HasExited) { <>2__current = null; <>1__state = 1; return true; } <>8__1.process.Close(); 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 static DirectAudioStreamer _instance; private ConcurrentQueue<float> _sampleBuffer = new ConcurrentQueue<float>(); public static DirectAudioStreamer Instance { get { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_instance == (Object)null) { _instance = new GameObject("DirectAudioStreamer").AddComponent<DirectAudioStreamer>(); Object.DontDestroyOnLoad((Object)(object)((Component)_instance).gameObject); } return _instance; } } private string DllPath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); private string YtDlpPath => Path.Combine(DllPath, "yt-dlp.exe"); private string FfmpegPath => Path.Combine(DllPath, "ffmpeg.exe"); [IteratorStateMachine(typeof(<StreamFromYt>d__10))] public IEnumerator StreamFromYt(string input, Action<AudioClip> onStreaming) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <StreamFromYt>d__10(0) { <>4__this = this, input = input, onStreaming = onStreaming }; } private void OnAudioRead(float[] data) { for (int i = 0; i < data.Length; i++) { if (!_sampleBuffer.TryDequeue(out data[i])) { data[i] = 0f; } } } } public static class Patches { private static AudioClip[] _defaultAudios; private static bool _play; private static bool _loading; [HarmonyPostfix] [HarmonyPatch(typeof(HUDManager), "AddChatMessage")] public static void AddChatMessage_Postfix(string chatMessage, string nameOfUserWhoTyped, HUDManager __instance) { if (chatMessage.StartsWith("/add") && chatMessage.Length > 5) { string text = chatMessage.Substring(5, chatMessage.Length - 5).Trim(); if (!string.IsNullOrEmpty(text)) { AudioQueue.Add(text); } } else if (chatMessage.StartsWith("/clear")) { AudioQueue.Clear(); } } [HarmonyPostfix] [HarmonyPatch(typeof(BoomboxItem), "Start")] public static void Start_Postfix(BoomboxItem __instance) { _defaultAudios = __instance.musicAudios; } [HarmonyPrefix] [HarmonyPatch(typeof(BoomboxItem), "StartMusic")] public static bool StartMusic_Prefix(bool startMusic, bool pitchDown, BoomboxItem __instance) { if (_play) { _play = false; return true; } if (_loading) { return false; } if (!AudioQueue.IsEmpty() && startMusic) { HUDManager.Instance.AddChatMessage("Loading audio...", ""); _loading = true; AudioStreamer.Instance.Load(AudioQueue.Next(), delegate(AudioClip clip) { __instance.musicAudios = (AudioClip[])(object)new AudioClip[1] { clip }; HUDManager.Instance.AddChatMessage("Loaded!", ""); _play = true; _loading = false; __instance.StartMusic(startMusic, pitchDown); }); return false; } if (startMusic) { __instance.musicAudios = _defaultAudios; } return true; } } [BepInPlugin("YoutubeBoomBox", "YoutubeBoomBox", "1.1.0")] public class YoutubeBoomBoxPlugin : BaseUnityPlugin { private const string PLUGIN_GUID = "YoutubeBoomBox"; private const string PLUGIN_NAME = "YoutubeBoomBox"; private const string PLUGIN_VERSION = "1.1.0"; private readonly Harmony _harmony = new Harmony("YoutubeBoomBox"); public static ManualLogSource Logger { get; } = Logger.CreateLogSource("YoutubeBoomBox"); private void Awake() { _harmony.PatchAll(typeof(Patches)); Logger.LogInfo((object)"Plugin YoutubeBoomBox is loaded!"); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "YoutubeBoomBox"; public const string PLUGIN_NAME = "YoutubeBoomBox"; public const string PLUGIN_VERSION = "1.0.0"; } }