using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Photon.Pun;
using TMPro;
using ThunderstoreAPI;
using UnityEngine;
using UnityEngine.Localization.PropertyVariants;
using UnityEngine.UI;
using UnityEngine.UIElements.Collections;
using Zorro.Core;
using spook.tube;
using spook.tube.Uploaders;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("spook.tube")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("spook.tube")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("spook.tube")]
[assembly: AssemblyTitle("spook.tube")]
[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;
}
}
}
[HarmonyPatch(typeof(UploadCompleteUI), "PlayVideo")]
internal class VideoEndedUIInjection
{
[HarmonyPostfix]
private static void Postfix(UploadCompleteUI __instance)
{
Plugin.CreateUploadButton();
}
}
namespace spook.tube
{
internal class HTTPServer
{
private Thread thread;
private HttpListener listener;
private int port;
public HTTPServer(int port)
{
this.port = port;
}
public void Start()
{
thread = new Thread((ThreadStart)delegate
{
WorkerTask();
});
thread.Start();
}
private void WorkerTask()
{
listener = new HttpListener();
listener.Prefixes.Add($"http://localhost:{port}/");
listener.Start();
Plugin.Logger.LogInfo((object)$"Listening on port :{port}");
while (true)
{
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
using HttpListenerResponse httpListenerResponse = context.Response;
Plugin.Logger.LogInfo((object)("[Webserver] Handling request for " + request.HttpMethod + " : " + request.Url?.ToString() + " : " + request.UserAgent));
httpListenerResponse.Headers.Set("Access-Control-Allow-Origin", "*");
httpListenerResponse.Headers.Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
httpListenerResponse.StatusCode = 200;
httpListenerResponse.StatusDescription = "status ok";
string httpMethod = request.HttpMethod;
string text = httpMethod;
if (text == "POST")
{
string token;
using (StreamReader streamReader = new StreamReader(request.InputStream, request.ContentEncoding))
{
token = streamReader.ReadToEnd();
}
Plugin.Logger.LogInfo((object)"Received token from HTTP server, forwarding to uploader...");
Plugin.uploader.HandleLogin(token);
}
else
{
httpListenerResponse.Headers.Set("Content-Type", "text/plain");
string s = "spook.tube Content Warning Mod server";
byte[] bytes = Encoding.UTF8.GetBytes(s);
httpListenerResponse.ContentLength64 = bytes.Length;
using Stream stream = httpListenerResponse.OutputStream;
stream.Write(bytes, 0, bytes.Length);
}
httpListenerResponse.Close();
}
}
}
[BepInPlugin("spook.tube", "spook.tube", "1.0.0")]
[ContentWarningPlugin("spook.tube", "spook.tube", true)]
public class Plugin : BaseUnityPlugin
{
private Harmony harmony;
public static ConfigEntry<string> URL;
public static Uploader uploader;
public static string modpackID;
public static Action onFinishAuth;
internal static ManualLogSource Logger;
private void Awake()
{
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Expected O, but got Unknown
Logger = ((BaseUnityPlugin)this).Logger;
URL = ((BaseUnityPlugin)this).Config.Bind<string>("Networking", "spook.tube Host", "https://spook.tube", "A PeerTube-compatible instance");
LoadModpackID();
Logger.LogInfo((object)"spook.tube has loaded.");
if (modpackID != null)
{
Logger.LogWarning((object)("Using modpack ID: " + modpackID));
}
harmony = new Harmony("tube.spook.mod");
harmony.PatchAll();
uploader = new PTUploader(URL.Value);
HTTPServer hTTPServer = new HTTPServer(48923);
hTTPServer.Start();
}
private void OnDestroy()
{
harmony.UnpatchSelf();
Logger.LogInfo((object)"spook.tube has unloaded.");
}
public static string getVideoMime(string path)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
dictionary.Add(".webm", "video/webm");
dictionary.Add(".mp4", "video/mp4");
string extension = Path.GetExtension(path);
if (dictionary.ContainsKey(extension))
{
return DictionaryExtensions.Get<string, string>((IDictionary<string, string>)dictionary, extension, (string)null);
}
return null;
}
public void LoadModpackID()
{
List<string> list = Directory.GetDirectories(Paths.PluginPath, "SpookTube", SearchOption.AllDirectories).ToList();
foreach (string item in list)
{
string path = Path.Combine(item, "modpackid.txt");
if (File.Exists(path))
{
modpackID = File.ReadAllText(path);
}
}
}
public static List<LoadedPlugin> GetLoadedPlugins()
{
List<LoadedPlugin> list = new List<LoadedPlugin>();
foreach (ThunderstoreMod loadedMod in Library.GetLoadedMods())
{
list.Add(new LoadedPlugin
{
ThunderstoreName = loadedMod.name,
Version = $"{loadedMod.versionNumber.major}.{loadedMod.versionNumber.minor}.{loadedMod.versionNumber.patch}"
});
}
return list;
}
public static List<ContentPlayer> GetPlayers()
{
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
List<ContentPlayer> list = new List<ContentPlayer>();
PlayerVisor[] array = Object.FindObjectsOfType<PlayerVisor>();
PlayerVisor[] array2 = array;
foreach (PlayerVisor val in array2)
{
PhotonView component = ((Component)val).gameObject.GetComponent<PhotonView>();
ContentPlayer item = new ContentPlayer
{
FaceText = ((TMP_Text)val.visorFaceText).text,
FaceColor = ColorUtility.ToHtmlStringRGB(val.visorColor.Value),
FaceRotation = (int)val.FaceRotation
};
list.Add(item);
}
return list;
}
public static void CreateUploadButton()
{
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_0154: Unknown result type (might be due to invalid IL or missing references)
//IL_016c: Unknown result type (might be due to invalid IL or missing references)
//IL_018d: Unknown result type (might be due to invalid IL or missing references)
//IL_0225: Unknown result type (might be due to invalid IL or missing references)
//IL_023d: Unknown result type (might be due to invalid IL or missing references)
//IL_025e: Unknown result type (might be due to invalid IL or missing references)
if (!GameObject.Find("ShowVideoState/VIDEO/VideoDone").DoesObjectExist("STUpload"))
{
GameObject val = GameObject.Find("VideoDone/Replay");
GameObject val2 = Object.Instantiate<GameObject>(val);
((Object)val2).name = "STUpload";
val2.transform.SetParent(val.transform.parent, true);
val2.transform.localPosition = new Vector3(0f, -125f, 0f);
val2.transform.rotation = val.transform.rotation;
val2.transform.localScale = new Vector3(1f, 1f, 1f);
GameObject val3 = GameObject.Find("STUpload/Image");
val3.transform.localPosition = new Vector3(-100f, 0f, 0f);
val3.transform.rotation = val.transform.rotation;
val3.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
Sprite sprite = GameObject.Find("VideoDone/SaveVideo/Image").GetComponent<Image>().sprite;
val3.GetComponent<Image>().sprite = sprite;
GameObject val4 = GameObject.Find("STUpload/Text (TMP)");
val4.transform.localPosition = new Vector3(0f, 0f, 0f);
val4.transform.rotation = val.transform.rotation;
val4.transform.localScale = new Vector3(1f, 1f, 1f);
((Behaviour)val4.GetComponent<GameObjectLocalizer>()).enabled = false;
TextMeshProUGUI component = val4.GetComponent<TextMeshProUGUI>();
((TMP_Text)component).SetText("UPLOAD TO SPOOK.TUBE", true);
GameObject val5 = GameObject.Find("UploadMachine2").FindObject("ReplayInt");
GameObject val6 = Object.Instantiate<GameObject>(val5);
((Object)val6).name = "STUploadInt";
Object.Destroy((Object)(object)val6.GetComponent<ReplayVideoInteractable>());
val6.AddComponent<UploadButton>();
val6.transform.SetParent(val5.transform.parent, true);
val6.transform.position = val2.transform.position;
val6.transform.rotation = val2.transform.rotation;
val6.transform.localScale = new Vector3(1f, 1f, 1f);
}
}
public static void StartUpload()
{
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
GameObject val = GameObject.Find("McScreen/Content").FindObject("ShowVideoState");
if ((Object)(object)val == (Object)null)
{
Logger.LogError((object)"Failed to get the ShowVideoState object");
return;
}
UploadCompleteUI component = val.GetComponent<UploadCompleteUI>();
if ((Object)(object)component == (Object)null)
{
Logger.LogError((object)"Failed to get UploadCompleteUI");
return;
}
object value = new Traverse((object)component).Field("m_replayVideo").GetValue();
IPlayableVideo video = (IPlayableVideo)((value is IPlayableVideo) ? value : null);
GameObject parent = GameObject.Find("UploadMachine2/McScreen").FindObject("Content");
GameObject val2 = parent.FindObject("ShowVideoState");
GameObject UploadingState = parent.FindObject("UploadingState");
UploadingState.SetActive(true);
val2.SetActive(false);
VideoMeta meta = new VideoMeta
{
Plugins = GetLoadedPlugins(),
Players = GetPlayers()
};
uploader.Upload(video, meta).ContinueWith(delegate
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
UploadingState.SetActive(false);
object value2 = new Traverse((object)GameObject.Find("/Tools/UploadMachine2").GetComponent<UploadVideoStation>()).Field("m_stateMachine").GetValue();
UploadVideoStationStateMachine val3 = (UploadVideoStationStateMachine)((value2 is UploadVideoStationStateMachine) ? value2 : null);
((StateMachine<UploadVideoStationState>)(object)val3).SwitchState<UploadVideoState>();
});
}
public static void WaitForLogin(Action onFinish)
{
onFinishAuth = onFinish;
uploader.OpenAuth();
}
}
public class LoadedPlugin
{
public string ThunderstoreName;
public string Version;
}
public class ContentPlayer
{
public string SteamID;
public string FaceText;
public string FaceColor;
public int FaceRotation;
}
internal class UploadButton : Interactable
{
public UploadVideoStation UploadVideoStation;
private void Start()
{
base.hoverText = "Upload to spook.tube";
}
public override void Interact(Player player)
{
Plugin.Logger.LogWarning((object)"Upload to spook.tube button has been pressed");
if (Plugin.uploader.IsAuthenticated())
{
Plugin.StartUpload();
}
else
{
Plugin.uploader.OpenAuth();
}
}
public override void OnStartHover(Player player)
{
}
public override void OnEndHover(Player player)
{
}
}
internal static class Utils
{
public static GameObject FindObject(this GameObject parent, string name)
{
Transform[] componentsInChildren = parent.GetComponentsInChildren<Transform>(true);
Transform[] array = componentsInChildren;
foreach (Transform val in array)
{
if (((Object)val).name.StartsWith(name))
{
return ((Component)val).gameObject;
}
}
return null;
}
public static bool DoesObjectExist(this GameObject parent, string name)
{
return (Object)(object)parent.FindObject(name) != (Object)null;
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "spook.tube";
public const string PLUGIN_NAME = "spook.tube";
public const string PLUGIN_VERSION = "1.0.0";
}
}
namespace spook.tube.Uploaders
{
public class PTUploader : Uploader
{
private class UploadResult_Success
{
public class Video
{
public int id;
public string shortUUID;
public string uuid;
}
public Video video;
}
private class PTAccount
{
public string username;
public PTChannel[] videoChannels;
}
private class ChannelResponse
{
public int total;
public PTChannel[] data;
}
private class PTChannel
{
public int id;
public string displayName;
}
private string hostname;
private static string token;
private HttpClient client;
private static int channelId;
public PTUploader(string hostname)
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Expected O, but got Unknown
this.hostname = hostname;
client = new HttpClient();
}
public override bool IsAuthenticated()
{
return token != null;
}
private HttpRequestMessage buildRequest(string endpoint, HttpMethod method)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Expected O, but got Unknown
HttpRequestMessage val = new HttpRequestMessage
{
RequestUri = new Uri(hostname + endpoint),
Method = method
};
((HttpHeaders)val.Headers).Add("Authorization", "Bearer " + token);
return val;
}
private string getVideoURL(string shortUUID)
{
return hostname + "/videos/update/" + shortUUID + "?ref=mod-upload";
}
public override void OpenAuth()
{
Application.OpenURL(hostname + "/p/mod/token");
}
public override void HandleLogin(string token)
{
PTUploader.token = token;
HttpResponseMessage result = client.SendAsync(buildRequest("/api/v1/users/me", HttpMethod.Get)).Result;
string result2 = result.Content.ReadAsStringAsync().Result;
Plugin.Logger.LogInfo((object)("Token check result: " + result2));
if (result.StatusCode == HttpStatusCode.OK)
{
PTAccount pTAccount = JsonConvert.DeserializeObject<PTAccount>(result2);
Plugin.Logger.LogInfo((object)("Logged in as " + pTAccount.username));
if (pTAccount.videoChannels.Length == 0)
{
Plugin.Logger.LogError((object)"User has no channels? Cannot load.");
return;
}
PTChannel pTChannel = pTAccount.videoChannels[0];
channelId = pTChannel.id;
Plugin.Logger.LogInfo((object)$"Using channel {pTChannel.displayName} ({pTChannel.id})");
Plugin.onFinishAuth?.Invoke();
}
else
{
Plugin.Logger.LogWarning((object)"Failed to login");
}
}
public override Task Upload(IPlayableVideo recording, VideoMeta meta)
{
string path = default(string);
recording.TryGetVideoPath(ref path);
return Task.Factory.StartNew(delegate
{
UploadThread(path, meta);
});
}
private void UploadThread(string path, VideoMeta meta)
{
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Expected O, but got Unknown
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Expected O, but got Unknown
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Expected O, but got Unknown
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Expected O, but got Unknown
//IL_0123: Unknown result type (might be due to invalid IL or missing references)
//IL_0132: Expected O, but got Unknown
//IL_013f: Unknown result type (might be due to invalid IL or missing references)
//IL_014e: Expected O, but got Unknown
//IL_0155: Unknown result type (might be due to invalid IL or missing references)
//IL_015c: Expected O, but got Unknown
//IL_0106: Unknown result type (might be due to invalid IL or missing references)
//IL_0115: Expected O, but got Unknown
Plugin.Logger.LogInfo((object)("Starting upload of " + path));
Plugin.Logger.LogInfo((object)("File name: " + Path.GetFileName(path)));
string videoMime = Plugin.getVideoMime(path);
Plugin.Logger.LogInfo((object)("File mime type: " + videoMime));
string text = "In-Game Upload";
if (videoMime == null)
{
Plugin.Logger.LogWarning((object)("I don't know what mime type the video is (" + Path.GetExtension(path) + ")"));
return;
}
try
{
MultipartFormDataContent val = new MultipartFormDataContent();
val.Add((HttpContent)new StringContent(channelId.ToString() ?? ""), "channelId");
val.Add((HttpContent)new StringContent(text), "name");
val.Add((HttpContent)new StringContent("true"), "usingMod");
if (Plugin.modpackID != null)
{
Plugin.Logger.LogWarning((object)("Uploading video using modpackId " + Plugin.modpackID));
val.Add((HttpContent)new StringContent(Plugin.modpackID), "modpackId");
}
val.Add((HttpContent)new StringContent(JsonConvert.SerializeObject((object)meta.Plugins)), "mods");
val.Add((HttpContent)new StringContent(JsonConvert.SerializeObject((object)meta.Players)), "players");
ByteArrayContent val2 = new ByteArrayContent(File.ReadAllBytes(path));
((HttpContent)val2).Headers.ContentType = MediaTypeHeaderValue.Parse(videoMime);
val.Add((HttpContent)(object)val2, "videofile", Path.GetFileName(path));
HttpRequestMessage val3 = buildRequest("/api/v1/videos/upload", HttpMethod.Post);
val3.Content = (HttpContent)(object)val;
HttpResponseMessage result = client.SendAsync(val3).Result;
string result2 = result.Content.ReadAsStringAsync().Result;
Plugin.Logger.LogInfo((object)("Upload response: " + result2));
HttpStatusCode statusCode = result.StatusCode;
HttpStatusCode httpStatusCode = statusCode;
if (httpStatusCode == HttpStatusCode.OK)
{
UploadResult_Success uploadResult_Success = JsonConvert.DeserializeObject<UploadResult_Success>(result2);
Plugin.Logger.LogInfo((object)("Successfully uploaded " + uploadResult_Success.video.shortUUID));
Application.OpenURL(getVideoURL(uploadResult_Success.video.shortUUID));
}
else
{
Plugin.Logger.LogError((object)("Error while uploading " + result.StatusCode.ToString() + " : " + result2));
}
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)ex);
}
}
}
public abstract class Uploader
{
public abstract bool IsAuthenticated();
public abstract void OpenAuth();
public abstract void HandleLogin(string token);
public abstract Task Upload(IPlayableVideo video, VideoMeta meta);
}
public class VideoMeta
{
public List<LoadedPlugin> Plugins;
public List<ContentPlayer> Players;
}
}