using 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";
}
}