Please disclose if any significant portion of your mod was created 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 BTV Mod Fix v0.0.3
BTV_Mod_Fix.dll
Decompiled a year agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using System.Web; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Video; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.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.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 BestestTVModPlugin { public static class KeySymbolConverter { public static string GetKeySymbol(Key key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Expected I4, but got Unknown switch ((int)key) { case 0: return ""; case 1: return "[Space]"; case 2: return "[Enter]"; case 3: return "[Tab]"; case 4: return "[`]"; case 5: return "[']"; case 6: return "[;]"; case 7: return "[,]"; case 8: return "[.]"; case 9: return "[/]"; case 10: return "[\\]"; case 11: return "[[]"; case 12: return "[]]"; case 13: return "[-]"; case 14: return "[=]"; case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40: return "[" + ((object)(Key)(ref key)).ToString() + "]"; case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49: case 50: return "[" + ((object)(Key)(ref key)).ToString().Substring(5) + "]"; case 51: case 52: case 53: case 54: case 55: case 56: case 57: case 58: case 59: case 60: case 61: return "[←]"; case 62: return "[→]"; case 63: return "[↑]"; case 64: return "[↓]"; case 65: case 66: case 67: case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 75: case 76: case 77: case 78: return "[÷]"; case 79: return "[*]"; case 80: return "[+]"; case 81: return "[-]"; case 82: return "[.]"; case 83: return "[=]"; case 84: return "[#0]"; case 85: return "[#1]"; case 86: return "[#2]"; case 87: return "[#3]"; case 88: return "[#4]"; case 89: return "[#5]"; case 90: return "[#6]"; case 91: return "[#7]"; case 92: return "[#8]"; case 93: return "[#9]"; default: return "[" + ((object)(Key)(ref key)).ToString() + "]"; } } } public class ConfigManager { public static ConfigManager Instance { get; private set; } public static ConfigEntry<bool> enableLogging { get; set; } public static ConfigEntry<bool> storingResets { get; set; } public static ConfigEntry<bool> reloadedVideosHUD { get; set; } public static ConfigEntry<bool> tvOnAlways { get; set; } public static ConfigEntry<bool> tvPlaysSequentially { get; set; } public static ConfigEntry<bool> tvSkipsAfterOffOn { get; set; } public static ConfigEntry<bool> shuffleVideos { get; set; } public static ConfigEntry<bool> enableSeeking { get; set; } public static ConfigEntry<bool> enableChannels { get; set; } public static ConfigEntry<bool> mouseWheelVolume { get; set; } public static ConfigEntry<bool> hideHoverTip { get; set; } public static ConfigEntry<bool> restrictChannels { get; set; } public static ConfigEntry<bool> tvLightEnabled { get; set; } public static ConfigEntry<VideoAspectRatio> tvScalingOption { get; set; } public static ConfigEntry<Key> reloadVideosKeyBind { get; set; } public static ConfigEntry<Key> seekReverseKeyBind { get; set; } public static ConfigEntry<Key> seekForwardKeyBind { get; set; } public static ConfigEntry<Key> skipReverseKeyBind { get; set; } public static ConfigEntry<Key> skipForwardKeyBind { get; set; } public static ConfigEntry<string> sourceUrl { get; set; } public static ConfigFile configFile { get; private set; } public static void Init(ConfigFile config) { Instance = new ConfigManager(config); } private ConfigManager(ConfigFile cfg) { configFile = cfg; tvScalingOption = cfg.Bind<VideoAspectRatio>("Options", "Aspect Ratio", (VideoAspectRatio)1, "Available choices:\nNoScaling\nFitVertically\nFitHorizontally\nFitInside\nFitOutside\nStretch"); storingResets = cfg.Bind<bool>("Options", "Storing Resets List", true, "Does storing the television reset the video index back to 1?"); shuffleVideos = cfg.Bind<bool>("Options", "Shuffle Videos", false, "Load videos in a random order instead of alphabetically"); tvLightEnabled = cfg.Bind<bool>("Options", "Television Lights", true, "Does light emit from the television when it is turned on?"); tvOnAlways = cfg.Bind<bool>("Options", "TV Always On", false, "Should the TV stay on after it's been turned on once?\n"); tvPlaysSequentially = cfg.Bind<bool>("Options", "TV Plays Sequentially", true, "Play videos in order or loop?\n"); tvSkipsAfterOffOn = cfg.Bind<bool>("Options", "TV Skips After Off On", false, "Should what is currently playing be skipped after the television is turned off and back on again?\n"); enableSeeking = cfg.Bind<bool>("Options", "Enable Seeking", true, "Use brackets to fast forward or rewind?"); enableChannels = cfg.Bind<bool>("Options", "Enable Channels", true, "Use comma or period to skip videos?"); mouseWheelVolume = cfg.Bind<bool>("Options", "Mouse Wheel Volume", true, "Should the mouse wheel control the volume?"); hideHoverTip = cfg.Bind<bool>("Options", "Hide Hovertips", false, "Hide the controls when hovering over the TV"); restrictChannels = cfg.Bind<bool>("Options", "Restrict Channels", false, "Disable the channel controls, but keep the UI, unless Hide Hovertips is also checked"); reloadedVideosHUD = cfg.Bind<bool>("Options", "Videos Reloaded Prompt", true, "A prompt that pops up indicating that the videos have been reloaded after pressing the keybind."); reloadVideosKeyBind = cfg.Bind<Key>("Bindings", "Reload Videos", (Key)63, "Reload videos list, prevents having to restart if you turn shuffle on."); seekReverseKeyBind = cfg.Bind<Key>("Bindings", "Seek Backwards", (Key)11, "Go backwards in the currently playing video."); seekForwardKeyBind = cfg.Bind<Key>("Bindings", "Seek Forwards", (Key)12, "Go forwards in the currently playing video."); skipReverseKeyBind = cfg.Bind<Key>("Bindings", "Skip Backwards", (Key)7, "Skip to the previous video."); skipForwardKeyBind = cfg.Bind<Key>("Bindings", "Skip Forwards", (Key)8, "Skip to the next video."); sourceUrl = cfg.Bind<string>("HTTP", "Source URL", "https://gitee.com/bobby-ling/gists/raw/main/url.txt", "Url to get the url of video_urls"); enableLogging = cfg.Bind<bool>("Debug", "Logging Enabled", false, "Is logging enabled?"); } } [HarmonyPatch(typeof(TVScript))] public class TVScriptPatches { public static MethodInfo aspectRatio = typeof(VideoPlayer).GetMethod("VideoAspectRatio", BindingFlags.Instance | BindingFlags.NonPublic); public static FieldInfo? currentClipProperty = typeof(TVScript).GetField("currentClip", BindingFlags.Instance | BindingFlags.NonPublic); public static FieldInfo currentTimeProperty = typeof(TVScript).GetField("currentClipTime", BindingFlags.Instance | BindingFlags.NonPublic); public static bool tvIsCurrentlyOn = false; public static RenderTexture renderTexture; public static AudioSource audioSource; public static VideoPlayer videoSource; public Light tvLight; public static int TVIndex; [HarmonyPatch(typeof(StartOfRound), "Start")] [HarmonyPostfix] public static void SetTVIndex() { TVIndex = 0; tvIsCurrentlyOn = false; } [HarmonyPatch(typeof(TVScript), "Update")] [HarmonyPrefix] public static bool Update(TVScript __instance) { if (!tvIsCurrentlyOn) { SetTVScreenMaterial(__instance, b: false); } if ((Object)(object)videoSource == (Object)null) { videoSource = ((Component)__instance).GetComponent<VideoPlayer>(); renderTexture = videoSource.targetTexture; if (VideoManager.Videos.Count > 0) { WhatItDo(__instance, TVIndex); } } return false; } [HarmonyPatch(typeof(TVScript), "TurnTVOnOff")] [HarmonyPrefix] public static bool TurnTVOnOff(bool on, TVScript __instance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 __instance.tvOn = on; audioSource = __instance.tvSFX; videoSource = __instance.video; if ((int)videoSource.source != 1 || videoSource.url == "") { WhatItDo(__instance, TVIndex); } if (on) { if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)"Turning on TV"); } SetTVScreenMaterial(__instance, b: true); tvIsCurrentlyOn = true; audioSource.Play(); videoSource.Play(); videoSource.time = audioSource.time; audioSource.PlayOneShot(__instance.switchTVOn); WalkieTalkie.TransmitOneShotAudio(__instance.tvSFX, __instance.switchTVOn, 1f); } else { if (ConfigManager.tvSkipsAfterOffOn.Value) { videoSource.source = (VideoSource)1; videoSource.controlledAudioTrackCount = 1; videoSource.audioOutputMode = (VideoAudioOutputMode)1; videoSource.SetTargetAudioSource((ushort)0, audioSource); videoSource.url = VideoManager.Videos[TVIndex + 1]; TVIndex++; videoSource.Prepare(); } if (!ConfigManager.tvOnAlways.Value) { if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)"Turning off TV"); } SetTVScreenMaterial(__instance, b: false); audioSource.Stop(); videoSource.Stop(); audioSource.PlayOneShot(__instance.switchTVOn); WalkieTalkie.TransmitOneShotAudio(audioSource, __instance.switchTVOff, 1f); tvIsCurrentlyOn = false; } else { if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)"Turning on TV"); } SetTVScreenMaterial(__instance, b: true); tvIsCurrentlyOn = true; audioSource.Play(); videoSource.Play(); videoSource.time = audioSource.time; audioSource.PlayOneShot(__instance.switchTVOn); WalkieTalkie.TransmitOneShotAudio(audioSource, __instance.switchTVOn, 1f); } } return false; } public static void TVIndexUp() { if (TVIndex >= VideoManager.Videos.Count - 1) { TVIndex = 0; } else { TVIndex++; } SetVideoSourceUrl(); } public static void TVIndexDown() { if (TVIndex <= 0) { TVIndex = VideoManager.Videos.Count - 1; } else { TVIndex--; } SetVideoSourceUrl(); } private static void SetVideoSourceUrl() { videoSource.Stop(); videoSource.time = 0.0; videoSource.url = VideoManager.Videos[TVIndex]; } public static void SetTVScreenMaterial(TVScript __instance, bool b) { ((object)__instance).GetType().GetMethod("SetTVScreenMaterial", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, new object[1] { b }); if (!ConfigManager.tvLightEnabled.Value) { ((Behaviour)__instance.tvLight).enabled = false; } } [HarmonyPatch(typeof(TVScript), "TVFinishedClip")] [HarmonyPrefix] public static bool TVFinishedClip(TVScript __instance) { if (!__instance.tvOn || GameNetworkManager.Instance.localPlayerController.isInsideFactory) { return false; } if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)"TVFinishedClip"); } if (VideoManager.Videos.Count > 0 && ConfigManager.tvPlaysSequentially.Value) { TVIndexUp(); } WhatItDo(__instance, TVIndex); return false; } private static void WhatItDo(TVScript __instance, int TVIndex = -1) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) audioSource = __instance.tvSFX; if (VideoManager.Videos.Count > 0) { videoSource.aspectRatio = ConfigManager.tvScalingOption.Value; videoSource.clip = null; audioSource.clip = null; string text = VideoManager.Videos[TVIndex]; if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)text); } videoSource.url = text; videoSource.source = (VideoSource)1; videoSource.controlledAudioTrackCount = 1; videoSource.audioOutputMode = (VideoAudioOutputMode)1; videoSource.SetTargetAudioSource((ushort)0, audioSource); videoSource.Stop(); videoSource.Prepare(); } else if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogError((object)"VideoManager.Videos list is empty. Add some videos or URLs in config."); } } [HarmonyPatch(typeof(ShipBuildModeManager), "StoreShipObjectClientRpc")] [HarmonyPostfix] private static void StoreShipObjectClientRpcPostfix(int unlockableID) { if (!ConfigManager.storingResets.Value) { return; } UnlockableItem val = StartOfRound.Instance.unlockablesList.unlockables[unlockableID]; if (val.inStorage && val.unlockableName == "Television" && TVIndex != 0) { if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)"Resetting play sequence..."); } SetTVIndex(); } } [HarmonyPatch(typeof(TVScript), "__initializeVariables")] [HarmonyPostfix] public static void SetTelevisionHoverTip(TVScript __instance) { Transform parent = ((Component)__instance).transform.parent; if (((Object)(object)(((Object)(object)parent != (Object)null) ? ((Component)parent).GetComponentInChildren<InteractTrigger>() : null) == (Object)null || (!ConfigManager.enableSeeking.Value && !ConfigManager.enableChannels.Value && !ConfigManager.mouseWheelVolume.Value)) && ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)"Television trigger missing!"); } } [HarmonyPatch(typeof(PlayerControllerB), "Update")] [HarmonyPostfix] public static void GetTVInput(PlayerControllerB __instance) { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03be: Unknown result type (might be due to invalid IL or missing references) if (!((NetworkBehaviour)__instance).IsOwner || !__instance.isPlayerControlled || __instance.inTerminalMenu || __instance.isTypingChat || __instance.isPlayerDead) { return; } InteractTrigger hoveringOverTrigger = __instance.hoveringOverTrigger; if ((Object)(object)hoveringOverTrigger == (Object)null) { return; } Transform parent = ((Component)hoveringOverTrigger).transform.parent; GameObject val = (((Object)(object)parent != (Object)null) ? ((Component)parent).gameObject : null); if (!((Object)(object)val != (Object)null) || !((Object)val).name.Contains("Television")) { return; } videoSource = val.GetComponentInChildren<VideoPlayer>(); audioSource = ((Component)val.transform.Find("TVAudio")).GetComponent<AudioSource>(); double num = videoSource.time; float y = ((InputControl<Vector2>)(object)Mouse.current.scroll).ReadValue().y; float volume = audioSource.volume; Key value = ConfigManager.seekReverseKeyBind.Value; Key value2 = ConfigManager.seekForwardKeyBind.Value; Key value3 = ConfigManager.skipReverseKeyBind.Value; Key value4 = ConfigManager.skipForwardKeyBind.Value; if (!((Object)(object)videoSource != (Object)null)) { return; } if (((ButtonControl)Keyboard.current[value]).wasPressedThisFrame && ConfigManager.enableSeeking.Value) { num -= 15.0; if (num < 0.0) { num = 0.0; if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)("AdjustTime: " + num)); } } else { videoSource.time = audioSource.time; videoSource.time = num; if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)("AdjustTime: " + num)); } } } if (((ButtonControl)Keyboard.current[value2]).wasPressedThisFrame && ConfigManager.enableSeeking.Value) { num += 15.0; videoSource.time = audioSource.time; videoSource.time = num; if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)("AdjustTime: " + num)); } } if (((ButtonControl)Keyboard.current[value3]).wasPressedThisFrame && ConfigManager.enableChannels.Value && !ConfigManager.restrictChannels.Value && tvIsCurrentlyOn) { TVIndexDown(); } if (((ButtonControl)Keyboard.current[value4]).wasPressedThisFrame && ConfigManager.enableChannels.Value && !ConfigManager.restrictChannels.Value && tvIsCurrentlyOn) { TVIndexUp(); } if (!videoSource.isPlaying || !tvIsCurrentlyOn) { num = 0.0; videoSource.time = audioSource.time; videoSource.time = num; } InteractTrigger componentInChildren = ((Component)parent).GetComponentInChildren<InteractTrigger>(); if ((Object)(object)componentInChildren == (Object)null && ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)"Television trigger missing!"); } string text = "Seek: " + KeySymbolConverter.GetKeySymbol(value) + KeySymbolConverter.GetKeySymbol(value2) + "\n" + TimeSpan.FromSeconds(num).ToString("hh\\:mm\\:ss\\.fff"); string text2 = "Volume: " + KeySymbolConverter.GetKeySymbol((Key)13) + KeySymbolConverter.GetKeySymbol((Key)80) + "\n" + (volume * 150f).ToString("0") + "%"; string text3 = $"Channel: {KeySymbolConverter.GetKeySymbol(value3)}{KeySymbolConverter.GetKeySymbol(value4)}\n{TVIndex + 1}/{VideoManager.Videos.Count}"; if (!ConfigManager.hideHoverTip.Value) { string text4 = ""; if (ConfigManager.enableSeeking.Value) { text4 += text; } if (ConfigManager.enableChannels.Value) { text4 = text4 + (string.IsNullOrEmpty(text4) ? "" : "\n") + text3; } if (ConfigManager.mouseWheelVolume.Value) { text4 = text4 + (string.IsNullOrEmpty(text4) ? "" : "\n") + text2; } componentInChildren.hoverTip = text4; } if (TVIndex != TVIndex && ConfigManager.enableChannels.Value) { if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)("AdjustMediaFile: " + VideoManager.Videos[TVIndex])); } if (!videoSource.isPlaying || !tvIsCurrentlyOn) { num = 0.0; videoSource.time = audioSource.time; videoSource.time = num; } } if (y != 0f && ConfigManager.mouseWheelVolume.Value) { y /= 6000f; volume = Mathf.Clamp(volume + y, 0f, 1f); audioSource.volume = volume; if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)("Changed volume: " + volume)); } } } } [BepInPlugin("BobbyLing.BTVModFixForPersonalUse", "BTVModFixForPersonalUse", "0.0.3")] public class BestestTVModPlugin : BaseUnityPlugin { public const string PLUGIN_GUID = "BobbyLing.BTVModFixForPersonalUse"; public const string PLUGIN_NAME = "BTVModFixForPersonalUse"; public const string PLUGIN_VERSION = "0.0.3"; private static readonly Harmony Harmony = new Harmony("BobbyLing.BTVModFixForPersonalUse".ToString()); public static ManualLogSource Log = new ManualLogSource("BTVModFixForPersonalUse"); private InputAction reloadVideosAction; private GameObject gameObject; public static BestestTVModPlugin instance; private void Start() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown Key value = ConfigManager.reloadVideosKeyBind.Value; reloadVideosAction = new InputAction((string)null, (InputActionType)0, $"<Keyboard>/{value}", "press", (string)null, (string)null); reloadVideosAction.Enable(); reloadVideosAction.performed += OnReloadVideosActionPerformed; } private async void OnReloadVideosActionPerformed(CallbackContext ctx) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (((CallbackContext)(ref ctx)).ReadValueAsButton()) { await RefreshVideos(); } } public void ReloadVideos() { VideoManager.Load(); if (ConfigManager.reloadedVideosHUD.Value) { HUDManager.Instance.DisplayTip("Reloaded Videos", "Video list has been reloaded.", false, false, "ReloadVideosTip"); } } public async Task RefreshVideos() { ReloadVideos(); TVScriptPatches.videoSource = instance.gameObject.AddComponent<VideoPlayer>(); TVScriptPatches.videoSource.playOnAwake = false; TVScriptPatches.videoSource.isLooping = false; TVScriptPatches.videoSource.source = (VideoSource)1; TVScriptPatches.videoSource.controlledAudioTrackCount = 1; TVScriptPatches.videoSource.audioOutputMode = (VideoAudioOutputMode)1; TVScriptPatches.videoSource.SetTargetAudioSource((ushort)0, TVScriptPatches.audioSource); TVScriptPatches.videoSource.url = VideoManager.Videos[TVScriptPatches.TVIndex]; TVScriptPatches.videoSource.Prepare(); TVScriptPatches.SetTVIndex(); await Task.Delay(100); TVScriptPatches.TVIndexDown(); await Task.Delay(100); TVScriptPatches.TVIndexUp(); } private void OnDisable() { reloadVideosAction.performed -= OnReloadVideosActionPerformed; reloadVideosAction.Disable(); } private void Awake() { ConfigManager.Init(((BaseUnityPlugin)this).Config); instance = this; Log = ((BaseUnityPlugin)this).Logger; Harmony.PatchAll(); VideoManager.Load(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"BobbyLing.BTVModFixForPersonalUse 0.0.3 is loaded!"); } } public class VideoInfo { public string Name { get; set; } public string Url { get; set; } } public static class VideoManager { public static List<string> Videos = new List<string>(); private static readonly HttpClient _httpClient = new HttpClient(); public static async Task Load() { Videos.Clear(); List<string> localVideoUrls = new List<string>(); List<VideoInfo> remoteVideos = new List<VideoInfo>(); if (await LoadRemoteVideosAsync(remoteVideos)) { await ProcessRemoteVideosAsync(remoteVideos); BestestTVModPlugin.Log.LogInfo((object)$"loaded {remoteVideos.Count}"); } else { LoadLocalVideos(localVideoUrls); } if (ConfigManager.shuffleVideos.Value) { Shuffle(Videos); } } private static void LoadLocalVideos(List<string> localVideoUrls) { string[] directories = Directory.GetDirectories(Paths.ConfigPath); for (int i = 0; i < directories.Length; i++) { string path = Path.Combine(directories[i], "Television Videos"); if (Directory.Exists(path)) { string[] files = Directory.GetFiles(path, "*.mp4"); foreach (string text in files) { localVideoUrls.Add("file://" + text); } } } string path2 = Path.Combine(Paths.ConfigPath, "Television Videos"); if (!Directory.Exists(path2)) { Directory.CreateDirectory(path2); } directories = Directory.GetFiles(path2, "*.mp4"); foreach (string text2 in directories) { localVideoUrls.Add("file://" + text2); } } private static async Task<bool> LoadRemoteVideosAsync(List<VideoInfo> remoteVideoUrls) { _ = 1; try { string text = await _httpClient.GetStringAsync(ConfigManager.sourceUrl.Value); if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogDebug((object)("Successfully fetched source URL: " + text)); } try { string videoSrcUrl = text + "/video_urls"; VideoInfo[] array = await GetFromUrlAsync<VideoInfo[]>(videoSrcUrl); if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogDebug((object)$"Fetched {((array != null) ? array.Length : 0)} videos from {videoSrcUrl}"); } if (array != null && array.Length != 0) { remoteVideoUrls.AddRange(array); return true; } BestestTVModPlugin.Log.LogWarning((object)"No videos found in the response."); } catch (Exception ex) { BestestTVModPlugin.Log.LogError((object)("Loading remoteVideoUrls failed: " + ex.Message)); } } catch (HttpRequestException ex2) { BestestTVModPlugin.Log.LogError((object)("Failed to fetch source URL: " + ex2.Message)); } catch (Exception ex3) { BestestTVModPlugin.Log.LogError((object)("Unexpected error: " + ex3.Message)); } return false; } private static async Task ProcessRemoteVideosAsync(List<VideoInfo> remoteVideos) { string globalVideoDir = Path.Combine(Paths.ConfigPath, "Television Videos"); foreach (VideoInfo video in remoteVideos) { try { string path = SanitizeFileName(video.Name); string filePath = Path.Combine(globalVideoDir, path); if (!File.Exists(filePath)) { await DownloadVideoAsync(video.Url, filePath); } Videos.Add("file://" + filePath); BestestTVModPlugin.Log.LogInfo((object)("adding " + filePath)); } catch (Exception ex) { BestestTVModPlugin.Log.LogError((object)("process " + video.Name + " failed: " + ex.Message)); } } } private static async Task DownloadVideoAsync(string url, string savePath) { _ = 2; try { string text = HttpUtility.UrlDecode(url); BestestTVModPlugin.Log.LogInfo((object)("downloading: " + text + " -> " + savePath)); HttpResponseMessage obj = await _httpClient.GetAsync(text); obj.EnsureSuccessStatusCode(); using Stream contentStream = await obj.Content.ReadAsStreamAsync(); using FileStream fileStream = new FileStream(savePath, FileMode.Create); await contentStream.CopyToAsync(fileStream); } catch (Exception ex) { BestestTVModPlugin.Log.LogError((object)("download failed: " + ex.Message)); throw; } } public static async Task<T> GetFromUrlAsync<T>(string url) { try { if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)("fetching from " + url)); } string text = await _httpClient.GetStringAsync(url); if (ConfigManager.enableLogging.Value) { BestestTVModPlugin.Log.LogInfo((object)("get json: " + text)); } return JsonConvert.DeserializeObject<T>(text); } catch (HttpRequestException ex) { BestestTVModPlugin.Log.LogError((object)("HTTP request failed: " + ex.Message)); throw; } catch (JsonException val) { JsonException val2 = val; BestestTVModPlugin.Log.LogError((object)("JSON deserialization failed: " + ((Exception)(object)val2).Message)); throw; } } private static string SanitizeFileName(string name) { char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { name = name.Replace(oldChar, '_'); } return name; } public static void Shuffle<T>(IList<T> list) { Random random = new Random(); Action<IList<T>>[] array = new Action<IList<T>>[5] { FisherYatesShuffle, DurstenfeldShuffle, InsideOutShuffle, SattoloShuffle, RandomPerfectShuffle }; for (int i = 0; i < array.Length; i++) { int num = random.Next(i, array.Length); Action<IList<T>> action = array[i]; array[i] = array[num]; array[num] = action; } Action<IList<T>>[] array2 = array; for (int j = 0; j < array2.Length; j++) { array2[j](list); } } public static void FisherYatesShuffle<T>(IList<T> list) { Random random = new Random(); int count = list.Count; for (int i = 0; i < count; i++) { int index = random.Next(i + 1); T value = list[index]; list[index] = list[i]; list[i] = value; } } public static void DurstenfeldShuffle<T>(IList<T> list) { Random random = new Random(); for (int num = list.Count - 1; num >= 0; num--) { int index = random.Next(num + 1); T value = list[index]; list[index] = list[num]; list[num] = value; } } public static void InsideOutShuffle<T>(IList<T> list) { Random random = new Random(); int count = list.Count; for (int i = 0; i < count; i++) { int num = random.Next(i + 1); if (num != i) { T value = list[num]; list[num] = list[i]; list[i] = value; } } } public static void SattoloShuffle<T>(IList<T> list) { Random random = new Random(); for (int num = list.Count - 1; num > 0; num--) { int index = random.Next(num); T value = list[index]; list[index] = list[num - 1]; list[num - 1] = value; } } public static void RandomPerfectShuffle<T>(IList<T> list) { Random random = new Random(); for (int num = list.Count - 1; num > 0; num--) { int index = random.Next(num + 1); T value = list[index]; list[index] = list[num]; list[num] = value; } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }