diff --git a/ml_vpc/Main.cs b/ml_vpc/Main.cs index 548e53d..2145ff0 100644 --- a/ml_vpc/Main.cs +++ b/ml_vpc/Main.cs @@ -1,3 +1,4 @@ +using System; using ABI_RC.VideoPlayer; using System.Reflection; using System.IO; @@ -10,6 +11,7 @@ namespace ml_vpc public override void OnInitializeMelon() { + Settings.Init(); HarmonyInstance.Patch(typeof(YoutubeDl).GetMethod("GetVideoMetaDataAsync", BindingFlags.NonPublic | BindingFlags.Static), new HarmonyLib.HarmonyMethod(typeof(VideoPlayerCookies).GetMethod(nameof(OnGetYoutubeVideoMetaData_Prefix), BindingFlags.NonPublic | BindingFlags.Static)) ); @@ -19,8 +21,52 @@ namespace ml_vpc static void OnGetYoutubeVideoMetaData_Prefix(ref string parameter) { - if(File.Exists(ms_cookiesPath)) - parameter += string.Format(" --cookies \"{0}\"", ms_cookiesPath); + try + { + if (!Settings.Enabled) + return; + + switch (Settings.Mode) + { + case Settings.CookieMode.File: + if (File.Exists(ms_cookiesPath)) + parameter += string.Format(" --cookies \"{0}\"", ms_cookiesPath); + else + MelonLoader.MelonLogger.Warning("Cookies file not found in: '" + ms_cookiesPath + "'"); + break; + case Settings.CookieMode.BrowserFirefox: + parameter += " --cookies-from-browser firefox"; + break; + case Settings.CookieMode.BrowserBrave: + parameter += " --cookies-from-browser brave"; + break; + case Settings.CookieMode.BrowserChrome: + parameter += " --cookies-from-browser chrome"; + break; + case Settings.CookieMode.BrowserChromium: + parameter += " --cookies-from-browser chromium"; + break; + case Settings.CookieMode.BrowserEdge: + parameter += " --cookies-from-browser edge"; + break; + case Settings.CookieMode.BrowserOpera: + parameter += " --cookies-from-browser opera"; + break; + case Settings.CookieMode.BrowserSafari: + parameter += " --cookies-from-browser safari"; + break; + case Settings.CookieMode.BrowserVivaldi: + parameter += " --cookies-from-browser vivaldi"; + break; + case Settings.CookieMode.BrowserWhale: + parameter += " --cookies-from-browser whale"; + break; + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Warning(e); + } } } } diff --git a/ml_vpc/README.md b/ml_vpc/README.md index db042cc..76bba25 100644 --- a/ml_vpc/README.md +++ b/ml_vpc/README.md @@ -5,8 +5,23 @@ This mod allows yt-dlp to use cookies for playing YouTube videos. * Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) * Get [latest release DLL](../../../releases/latest): * Put `VideoPlayerCookies.dll` in `Mods` folder of game - + # Usage +Available mod's settings in `Settings - General - Video Player Cookies`: +* **Enabled:** Whether this mod adds cookie parameters or not; `true` by default. +* **Cookie fetch mode:** cookies fetch method; `Cookie text file` by default. + * **Cookie text file** *(default)* fetches cookies from your `cookies.txt` file, check [How to create cookies.txt](#how-to-create-cookiestxt) + * **Browser Firefox** fetches cookies directly from FireFox browser. *requires to be logged-in on YouTube in FireFox* + * **Browser Brave** fetches cookies directly from Brave browser. *requires to be logged-in on YouTube in Brave* + * **Browser Chrome** fetches cookies directly from Chrome browser. *requires to be logged-in on YouTube in Chrome* + * **Browser Chromium** fetches cookies directly from Chromium browser. *requires to be logged-in on YouTube in Chromium* + * **Browser Edge** fetches cookies directly from Edge browser. *requires to be logged-in on YouTube in Edge* + * **Browser Opera** fetches cookies directly from Opera browser. *requires to be logged-in on YouTube in Opera* + * **Browser Safari** fetches cookies directly from Safari browser. *requires to be logged-in on YouTube in Safari* + * **Browser Vivaldi** fetches cookies directly from Vivaldi browser. *requires to be logged-in on YouTube in Vivaldi* + * **Browser Whale** fetches cookies directly from Whale browser. *requires to be logged-in on YouTube in Whale* + +# How to create cookies.txt * Acquire cookies for YouTube from your browser: * Chromium-based browsers: [Get cookies.txt LOCALLY](https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc) extension * Firefox-based browsers: [cookies.txt](https://addons.mozilla.org/en-US/firefox/addon/cookies-txt) extension diff --git a/ml_vpc/ResourcesHandler.cs b/ml_vpc/ResourcesHandler.cs new file mode 100644 index 0000000..9d26199 --- /dev/null +++ b/ml_vpc/ResourcesHandler.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; +using System.Reflection; + +namespace ml_vpc +{ + static class ResourcesHandler + { + readonly static string ms_namespace = typeof(ResourcesHandler).Namespace; + + public static string GetEmbeddedResource(string p_name) + { + string l_result = ""; + Assembly l_assembly = Assembly.GetExecutingAssembly(); + + try + { + Stream l_libraryStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name); + StreamReader l_streadReader = new StreamReader(l_libraryStream); + l_result = l_streadReader.ReadToEnd(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + + return l_result; + } + } +} diff --git a/ml_vpc/Settings.cs b/ml_vpc/Settings.cs new file mode 100644 index 0000000..6fbe9f0 --- /dev/null +++ b/ml_vpc/Settings.cs @@ -0,0 +1,144 @@ +using ABI_RC.Core.InteractionSystem; +using System; +using System.Collections.Generic; + +namespace ml_vpc +{ + static class Settings + { + internal class SettingEvent + { + event Action m_action; + public void AddListener(Action p_listener) => m_action += p_listener; + public void RemoveListener(Action p_listener) => m_action -= p_listener; + public void Invoke(T p_value) => m_action?.Invoke(p_value); + } + + public enum CookieMode + { + File = 0, + BrowserFirefox, + BrowserBrave, + BrowserChrome, // This one might not work + BrowserChromium, + BrowserEdge, + BrowserOpera, + BrowserSafari, + BrowserVivaldi, + BrowserWhale, + } + + private enum ModSetting + { + Enabled = 0, + Mode, + } + + public static bool Enabled { get; private set; } = true; + public static CookieMode Mode { get; private set; } = CookieMode.File; + + static MelonLoader.MelonPreferences_Category ms_category = null; + static List ms_entries = null; + + public static readonly SettingEvent OnEnabledChanged = new SettingEvent(); + public static readonly SettingEvent OnModeChanged = new SettingEvent(); + + internal static void Init() + { + ms_category = MelonLoader.MelonPreferences.CreateCategory("VPC", null, true); + + ms_entries = new List() + { + ms_category.CreateEntry(nameof(ModSetting.Enabled), Enabled), + ms_category.CreateEntry(nameof(ModSetting.Mode), (int)CookieMode.File), + }; + + Enabled = (bool)ms_entries[(int)ModSetting.Enabled].BoxedValue; + Mode = (CookieMode)ms_entries[(int)ModSetting.Mode].BoxedValue; + + MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); + } + + private static System.Collections.IEnumerator WaitMainMenuUi() + { + while(ViewManager.Instance == null) + yield return null; + while(ViewManager.Instance.cohtmlView == null) + yield return null; + while(ViewManager.Instance.cohtmlView.Listener == null) + yield return null; + + ViewManager.Instance.cohtmlView.Listener.ReadyForBindings += () => + { + ViewManager.Instance.cohtmlView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action(OnToggleUpdate)); + ViewManager.Instance.cohtmlView.View.BindCall("OnDropdownUpdate_" + ms_category.Identifier, new Action(OnDropdownUpdate)); + + }; + ViewManager.Instance.cohtmlView.Listener.FinishLoad += (_) => + { + ViewManager.Instance.cohtmlView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mods_extension.js")); + ViewManager.Instance.cohtmlView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mod_menu.js")); + MelonLoader.MelonCoroutines.Start(UpdateMenuSettings()); + }; + } + + private static System.Collections.IEnumerator UpdateMenuSettings() + { + while(!ViewManager.Instance.IsReady || !ViewManager.Instance.IsViewShown) + yield return null; + + foreach(var l_entry in ms_entries) + ViewManager.Instance.cohtmlView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString()); + } + + private static void OnToggleUpdate(string p_name, string p_value) + { + try + { + if(Enum.TryParse(p_name, out ModSetting l_setting) && bool.TryParse(p_value, out bool l_value)) + { + switch(l_setting) + { + case ModSetting.Enabled: + { + Enabled = l_value; + OnEnabledChanged.Invoke(Enabled); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = l_value; + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + private static void OnDropdownUpdate(string p_name, string p_value) + { + try + { + if(Enum.TryParse(p_name, out ModSetting l_setting) && int.TryParse(p_value, out int l_value)) + { + switch(l_setting) + { + case ModSetting.Mode: + { + Mode = (CookieMode)l_value; + OnModeChanged.Invoke(Mode); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = l_value; + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + } +} diff --git a/ml_vpc/Utils.cs b/ml_vpc/Utils.cs new file mode 100644 index 0000000..7a429be --- /dev/null +++ b/ml_vpc/Utils.cs @@ -0,0 +1,12 @@ +using ABI_RC.Core.UI; +using System.Reflection; + +namespace ml_vpc +{ + static class Utils + { + static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic); + + public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => (ms_view?.GetValue(p_instance) as cohtml.Net.View)?.ExecuteScript(p_script); + } +} diff --git a/ml_vpc/ml_vpc.csproj b/ml_vpc/ml_vpc.csproj index d00abb8..0d05477 100644 --- a/ml_vpc/ml_vpc.csproj +++ b/ml_vpc/ml_vpc.csproj @@ -18,6 +18,11 @@ + + + + + $(CVRPath)/MelonLoader/net35/0Harmony.dll @@ -29,6 +34,16 @@ false false + + $(CVRPath)/ChilloutVR_Data/Managed/cohtml.Net.dll + false + false + + + $(CVRPath)/ChilloutVR_Data/Managed/Cohtml.Runtime.dll + false + false + $(CVRPath)/MelonLoader/net35/MelonLoader.dll false diff --git a/ml_vpc/resources/mod_menu.js b/ml_vpc/resources/mod_menu.js new file mode 100644 index 0000000..772bffb --- /dev/null +++ b/ml_vpc/resources/mod_menu.js @@ -0,0 +1,37 @@ +{ + let l_block = document.createElement('div'); + l_block.innerHTML = ` +
+
Video Player Cookies
+
+
+ +
+
Enabled:
+
+
+
+
+ +
+
Cookie fetch mode:
+
+
+
+
+ `; + + document.getElementById('settings-general').appendChild(l_block); + + // Toggles + for (let l_toggle of l_block.querySelectorAll('.inp_toggle')) + modsExtension.addSetting('VPC', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_VPC')); + + // Sliders + for (let l_slider of l_block.querySelectorAll('.inp_slider')) + modsExtension.addSetting('VPC', l_slider.id, modsExtension.createSlider(l_slider, 'OnSliderUpdate_VPC')); + + // Dropdowns + for (let l_dropdown of l_block.querySelectorAll('.inp_dropdown')) + modsExtension.addSetting('VPC', l_dropdown.id, modsExtension.createDropdown(l_dropdown, 'OnDropdownUpdate_VPC')); +}