Singularity/Assets/Scripts/DownloadMod.cs
2024-05-06 11:45:45 -07:00

592 lines
22 KiB
C#

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
using System;
using System.IO;
public class DownloadMod : MonoBehaviour
{
[Header("\tResources")]
[SerializeField] public OnlineAccountHandler OAH;
[SerializeField] public Controller Con;
[SerializeField] private ModList ML;
[Header("\tImage handler")]
[SerializeField] private ImportImages ImageCatcher;
[SerializeField] private GameObject ImagePrefab;
[SerializeField] public Transform ImageContainer;
[SerializeField] public List<GameObject> ImagePrefabList;
[Header("\tMod Info handler")]
[SerializeField] private GameObject ModInfoPrefab;
[SerializeField] private Transform ModInfoContainer;
[SerializeField] private List<GameObject> ModInfoPrefabList;
[Header("\tMod Download handler")]
[SerializeField] private GameObject ModDownload;
[SerializeField] private GameObject ModDownloadPrefab;
[SerializeField] private GameObject GoToDownloadPagePrefab;
[SerializeField] private Transform ModDownloadContainer;
[SerializeField] private List<GameObject> ModDownloadPrefabList;
[Header("\tBig Iage")]
[SerializeField] private GameObject BigImageObject;
[SerializeField] private RawImage BigImage;
[Header("\tUser")]
[SerializeField] private RawImage ModAuthorImage;
[SerializeField] private string ModAuthorLink;
[SerializeField] private TextMeshProUGUI AboutMe;
[Header("\tMod")]
[SerializeField] private string Version;
[SerializeField] public string ModID;
[SerializeField] private string FileID;
[SerializeField] private TextMeshProUGUI Title;
[SerializeField] private TextMeshProUGUI Description;
[SerializeField] private string ChangeLog;
[Header("\tOther")]
[SerializeField] private GameObject UpdateImagesButton;
[SerializeField] private List<ShareLink> Share;
[SerializeField] private Transform UserInfoContainer;
[SerializeField] private string ModInfoDir;
[SerializeField] private GameObject FileDownloadPrefab;
[SerializeField] public SchakenMods CurrentJSON;
void Start() {
ModInfoDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)+@"\Schaken-Mods®\Singularity\Mods\";
if (!Directory.Exists(ModInfoDir)) {
Directory.CreateDirectory(ModInfoDir);
}
}
public void RemoveMe(string A) {
for (int i = 0; i < ML.ModPrefabList.Count; i++) {
if (ML.ModPrefabList[i].GetComponent<ModListPrefab>().ID == A) {
DestroyImmediate(ML.ModPrefabList[i]);
ML.ModPrefabList.RemoveAt(i);
}
}
}
public void FillMe(SchakenMods A) {
if (A != null) {
CurrentJSON = A;
if (A.Mod.Url.ToLower().Contains("nexusmods.com")) {
UpdateImagesButton.SetActive(true);
} else {
UpdateImagesButton.SetActive(false);
}
Dictionary<string, string> User = new();
Dictionary<string, string> Mod = new();
Dictionary<string, string> extra = new();
if (A.Mod.ScreenshotThumbnails != null) {
// Debug.Log(A.Mod.ScreenshotThumbnails.Count+" Images");
for (int i = 0; i < A.Mod.ScreenshotThumbnails.Count; i++) {
StartCoroutine(DownloadImage(A.Mod.ScreenshotThumbnails[i].Url, A.Mod.Screenshots[i].Url));
}
} else {
Debug.LogError("Screenshots are null");
}
User.Add("Name", A.Mod.ModAuthor.Name);
User.Add("Joined", FormatDate(A.Mod.ModAuthor.Joined.ToString()));
User.Add("Posts", A.Mod.ModAuthor.Posts.ToString());
User.Add("Last Activity", FormatDate(A.Mod.ModAuthor.LastActivity.ToString()));
User.Add("Profile Views", A.Mod.ModAuthor.ProfileViews.ToString());
User.Add("Reputation Points", A.Mod.ModAuthor.ReputationPoints.ToString());
User.Add("Achievements Points", A.Mod.ModAuthor.AchievementsPoints.ToString());
Mod.Add("category", A.Mod.ModCategory.Name);
Mod.Add("Posted Date", FormatDate(A.Mod.Date.ToString()));
Mod.Add("Last Updated", FormatDate(A.Mod.Updated.ToString()));
Mod.Add("Last Downloaded", FormatDate(A.Mod.Downloaded.ToString()));
Mod.Add("Version", A.Mod.Version.ToString());
Mod.Add("Downloads", A.Mod.Downloads.ToString());
Mod.Add("Comments", A.Mod.Comments.ToString());
Mod.Add("Reviews", A.Mod.Reviews.ToString());
Mod.Add("Views", A.Mod.Views.ToString());
Mod.Add("Hidden", A.Mod.Hidden.ToString());
if (A.Mod.IsPaid.ToString() == "true") {
Mod.Add("Can Purchase", A.Mod.IsPurchasable.ToString());
Mod.Add("canBuy", A.Mod.CanBuy.ToString());
Mod.Add("price", A.Mod.Prices.ToString());
}
// Mod.Add("Can Download", A.Mod.CanDownload.ToString());
Mod.Add("Rating", A.Mod.Rating.ToString());
extra.Add("id", A.Mod.Id.ToString());
// extra.Add("categoryUrl", A.Mod.ModCategory.Url.ToString());
extra.Add("url", A.Mod.Url.ToString());
extra.Add("primaryScreenshot", A.Mod.PrimaryScreenshot.Url.ToString());
Title.text = A.Mod.Title;
Description.text = CleanString(A.Mod.Description);
if (A.Mod.ModAuthor != null) {
AboutMe.text = "Nothing here...";
if (A.Mod.ModAuthor.CustomFields != null) {
if (A.Mod.ModAuthor.CustomFields.TryGetValue("1", out var customField1) && customField1 != null) {
if (customField1.Fields.TryGetValue("1", out var field1) && field1 != null) {
if (field1.Value != null) {
AboutMe.text = CleanString(field1.Value.Replace("&amp;", "&") ?? "Empty...");
} else {
AboutMe.text = "Empty...";
}
}
}
}
}
StartCoroutine(DownloadBigImage(A.Mod.ModAuthor.PhotoUrl, ModAuthorImage));
ClearList(ImagePrefabList);
ClearList(ModInfoPrefabList);
ClearList(ModDownloadPrefabList);
foreach (KeyValuePair<string, string> info in Mod) {
GameObject NewInfo = Instantiate(ModInfoPrefab, ModInfoContainer);
NewInfo.GetComponent<ModInfoPrefab>().FillMe(info.Key, info.Value);
ModInfoPrefabList.Add(NewInfo);
}
foreach (KeyValuePair<string, string> user in User) {
GameObject NewInfo = Instantiate(ModInfoPrefab, UserInfoContainer);
NewInfo.GetComponent<ModInfoPrefab>().FillMe(user.Key, user.Value);
ModInfoPrefabList.Add(NewInfo);
}
Version = A.Mod.Version;
ModID = A.Mod.Id.ToString();
ChangeLog = CleanString(A.Mod.Changelog ?? "Empty...");
if (A.Mod.ModAuthor != null) {
ModAuthorLink = A.Mod.ModAuthor.ProfileUrl;
}
for (int i = 0; i < Share.Count; i++) {
Share[i].ModURL = A.Mod.Url;
Share[i].ModDescription = CleanString(A.Mod.Description).Replace("&amp;", "&");
Share[i].ModImage = A.Mod.PrimaryScreenshot.Url;
}
} else {
Debug.LogError("SchakenMods Object is null");
}
}
public void UpdateImages() {
StartCoroutine(UpdateJustImages());
}
private string S(string A) {
return A;
}
IEnumerator UpdateJustImages() {
Debug.Log("calling: "+CurrentJSON.Mod.Url);
using (UnityWebRequest uwr = UnityWebRequest.Get(CurrentJSON.Mod.Url)) {
yield return uwr.SendWebRequest();
if (uwr.result == UnityWebRequest.Result.ProtocolError) {
Debug.LogError($"ERROR: {uwr.error}");
ImageCatcher.gameObject.SetActive(true);
} else {
string result = uwr.downloadHandler.text;
if (!result.Contains("This mod contains adult content. You can turn adult content on in your preference, if you wish")) {
string chunkString = result.Split(S("<ul class=\"thumbgallery"))[1].Split(S("</ul>"))[0];
Debug.Log("Chunk: "+chunkString);
string[] images = chunkString.Split(S("<li class=\"thumb \" data-src=\""));
Debug.Log("Images: "+images.Length);
for (int i = 0; i < ImagePrefabList.Count; i++) {
DestroyImmediate(ImagePrefabList[i]);
}
ImagePrefabList.Clear();
for (int i = 1; i < images.Length; i++) {
if (!images[i].Contains(".gif")) {
string imageUrl = "https://staticdelivery.nexusmods.com/mods/"+images[i].Split("\"")[0];
Debug.Log("Image: "+imageUrl);
GameObject NewImage = Instantiate(ImagePrefab, ImageContainer);
ImagePrefabList.Add(NewImage);
RawImage A = NewImage.GetComponent<ImagePrefab>().FillMe(imageUrl, this);
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(imageUrl)) {
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.ConnectionError) {
byte[] webPData = www.downloadHandler.data;
if (webPData == null || webPData.Length == 0) {
Debug.LogError("No WebP data received.");
yield break;
}
WebpImporter.ByteWebpTexture2D(webPData, (texture) => {
A.texture = texture;
});
A.SetNativeSize();
A.SizeToParent();
if (!A.gameObject.activeSelf) {
A.gameObject.SetActive(true);
}
} else {
Debug.LogError("Error downloading image: " + www.error);
}
}
if (!A.gameObject.activeSelf) {
A.gameObject.SetActive(true);
}
}
}
} else {
ImageCatcher.gameObject.SetActive(true);
Debug.LogError("Adult Content Blocked"); // Here lets make a Violent Monkey script and add a custom http to put images in clipboard. grab them and load them into page.
}
}
}
}
public void Download(string A, int B) {
StartCoroutine(DownloadA(A, B));
}
IEnumerator DownloadA(string id, int B) {
FileID = id;
if (B == 1) { // Schaken-Mods
using (UnityWebRequest uwr = UnityWebRequest.Get($"https://schaken-mods.com/api/downloads/files/{id}/download")) {
uwr.SetRequestHeader("Authorization", "Bearer " + OAH.BearerID);
yield return uwr.SendWebRequest();
if (uwr.result == UnityWebRequest.Result.ProtocolError) {
Debug.LogError($"ERROR: {uwr.error}");
} else {
string result = uwr.downloadHandler.text;
// Debug.Log(result);
SchakenMods.VersionFilesResponse response = JsonConvert.DeserializeObject<SchakenMods.VersionFilesResponse>(result);
List<SchakenMods.VersionFiles> versions = response.Files;
for (int i = 0; i < ModDownloadPrefabList.Count; i++) {
DestroyImmediate(ModDownloadPrefabList[i]);
}
ModDownloadPrefabList.Clear();
for (int i = 0; i < versions.Count; i++) {
GameObject NewFile = Instantiate(FileDownloadPrefab, ModDownloadContainer);
NewFile.GetComponent<DownloadFilePrefab>().FillMe(versions[i].Name, BytesToString(versions[i].Size), versions[i].URL, this);
ModDownloadPrefabList.Add(NewFile);
}
}
}
} else if (B == 2) { // Nexus
string uri = $"https://api.nexusmods.com/v1/games/{CurrentJSON.Mod.ModCategory.Name}/mods/{CurrentJSON.Mod.Id}/files/{id}/download_link.json";
string ErrorMessage = "There was a problem communicating with Nexus, please visit their site";
using (UnityWebRequest uwr = UnityWebRequest.Get(uri)) {
uwr.SetRequestHeader("accept", "application/json");
uwr.SetRequestHeader("apikey", OAH.NMKey);
yield return uwr.SendWebRequest();
if (uwr.result == UnityWebRequest.Result.ConnectionError) {
//Debug.LogError($"Error While Sending GetNexusUserIDA({ID}): {uwr.error}");
GoToNexusPage(ErrorMessage, CurrentJSON.Mod.Url+"?tab=files");
} else {
string result = uwr.downloadHandler.text;
// Debug.Log(result);
if (!result.Contains("No File found for mod")) {
List<NexusMods.Downloads> cdnInfos = JsonConvert.DeserializeObject<List<NexusMods.Downloads>>(result);
for (int i = 0; i < ModDownloadPrefabList.Count; i++) {
DestroyImmediate(ModDownloadPrefabList[i]);
}
ModDownloadPrefabList.Clear();
foreach (var cdnInfo in cdnInfos) {
GameObject NewFile = Instantiate(FileDownloadPrefab, ModDownloadContainer);
string FileName = ExtractFileNameFromUrl(cdnInfo.Url);
NewFile.GetComponent<DownloadFilePrefab>().FillMe(cdnInfo.ShortName, "X", cdnInfo.Url, this, FileName);
ModDownloadPrefabList.Add(NewFile);
}
} else {
GoToNexusPage(ErrorMessage, CurrentJSON.Mod.Url+"?tab=files");
}
}
}
}
}
public void DownloadFile(string A, string fileName) {
Con.DownloadFile(A, fileName, ML.AddMod(CurrentJSON));
Close();
}
private void Close() {
gameObject.SetActive(false);
ModDownload.SetActive(false);
}
private string BytesToString(long byteCount) { //
string[] suf = { " B", " KB", " MB", " GB", " TB", " PB", " EB" }; //Longs run out around EB
if (byteCount == 0)
return "0" + suf[0];
long bytes = Math.Abs(byteCount);
int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
double num = Math.Round(bytes / Math.Pow(1024, place), 1);
return (Math.Sign(byteCount) * num).ToString() + suf[place];
}
public string SanitizeFileName(string fileName)
{
string invalidChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
foreach (char c in invalidChars) {
fileName = fileName.Replace(c.ToString(), string.Empty);
}
return fileName;
}
public void VisitModAuthor() {
Application.OpenURL(ModAuthorLink);
}
public void EnlargeImage(string A) {
BigImage.gameObject.SetActive(false);
StartCoroutine(DownloadBigImage(A, BigImage));
BigImageObject.SetActive(true);
}
public void ViewDownloads() {
StartCoroutine(GetVersionsA());
}
IEnumerator GetVersionsA() {
for (int i = 0; i < ModDownloadPrefabList.Count; i++) {
DestroyImmediate(ModDownloadPrefabList[i]);
}
ModDownloadPrefabList.Clear();
if (CurrentJSON.Mod.Url.ToLower().Contains("schaken-mods.com")) {
using (UnityWebRequest uwr = UnityWebRequest.Get($"https://schaken-mods.com/api/downloads/files/{CurrentJSON.Mod.Id}/history")) {
GameObject NewDownload2 = Instantiate(ModDownloadPrefab, ModDownloadContainer);
NewDownload2.GetComponent<ModDownloadPrefab>().FillMe(ModID, Version, ChangeLog, this);
ModDownloadPrefabList.Add(NewDownload2);
uwr.SetRequestHeader("Authorization", "Bearer " + OAH.BearerID);
yield return uwr.SendWebRequest();
if (uwr.result == UnityWebRequest.Result.ConnectionError) {
Debug.LogError("Error While Sending TEST: " + uwr.error);
} else {
string result = uwr.downloadHandler.text;
// Debug.Log(result);
List<SchakenMods.VersionInfo> versions = JsonConvert.DeserializeObject<List<SchakenMods.VersionInfo>>(result);
for (int i = 0; i < versions.Count; i++) {
GameObject NewDownload = Instantiate(ModDownloadPrefab, ModDownloadContainer);
ModDownloadPrefabList.Add(NewDownload);
string A = CleanString(versions[i].Changelog);
string Header = $"Version: {versions[i].Version} Changelog";
NewDownload.GetComponent<ModDownloadPrefab>().FillMe(versions[i].Id.ToString(), Header, A, this);
}
}
}
} else if (CurrentJSON.Mod.Url.ToLower().Contains("nexusmods.com")) {
string Message = "Nexus requires you to have premium membership to directly download using their API. Please click the button below to open the download page and select to download using Singularity.";
string ErrorMessage = "It seems NexusMods ia not responding correctly, please continue using the browser.";
if (OAH.NMSettings != null) {
if (OAH.NMSettings.IsSupporter) {
// Lets get the API for the Download
string uri = $"https://api.nexusmods.com/v1/games/{CurrentJSON.Mod.ModCategory.Name}/mods/{CurrentJSON.Mod.Id}/files.json";
UnityWebRequest uwr = UnityWebRequest.Get(uri);
uwr.SetRequestHeader("accept", "application/json");
uwr.SetRequestHeader("apikey", OAH.NMKey);
yield return uwr.SendWebRequest();
if (uwr.result == UnityWebRequest.Result.ConnectionError) {
//Debug.LogError($"Error While Sending GetNexusUserIDA({ID}): {uwr.error}");
GoToNexusPage(ErrorMessage, CurrentJSON.Mod.Url+"?tab=files");
} else {
string result = uwr.downloadHandler.text;
// Assuming the JSON response is stored in the 'result' variable
NexusMods response = JsonConvert.DeserializeObject<NexusMods>(result);
// Debug.Log(result);
List<NexusMods.Files> files = response.NMFiles;
for (int i = 0; i < files.Count; i++) {
if (files[i].CategoryName != null) {
GameObject NewDownload = Instantiate(ModDownloadPrefab, ModDownloadContainer);
ModDownloadPrefabList.Add(NewDownload);
string A = CleanString(files[i].ChangelogHtml);
if (A == "") {
A = "No descriotion given.";
}
NewDownload.GetComponent<ModDownloadPrefab>().FillMe(files[i].FileId.ToString(), files[i].Name, "Version: "+files[i].Version+" - "+A, this, 2);
}
}
}
} else {
GoToNexusPage(Message, CurrentJSON.Mod.Url+"?tab=files");
}
} else {
GoToNexusPage(Message, CurrentJSON.Mod.Url+"?tab=files");
}
}
}
private void GoToNexusPage(string Message, string Link) {
GameObject NewPrefab = Instantiate(GoToDownloadPagePrefab, ModDownloadContainer);
NewPrefab.GetComponent<GoToDownloadPage>().FillMe(Message, Link);
ModDownloadPrefabList.Add(NewPrefab);
}
IEnumerator DownloadBigImage(string imageUrl, RawImage A) {
// Debug.Log(imageUrl);
if (imageUrl.Contains("nexusmods.com")) {
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(imageUrl)) {
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.ConnectionError) {
byte[] webPData = www.downloadHandler.data;
if (webPData == null || webPData.Length == 0) {
Debug.LogError("No WebP data received.");
yield break;
}
WebpImporter.ByteWebpTexture2D(webPData, (texture) => {
A.texture = texture;
});
A.SetNativeSize();
A.SizeToParent();
if (!A.gameObject.activeSelf) {
A.gameObject.SetActive(true);
}
} else {
Debug.LogError("Error downloading image: " + www.error);
}
}
} else {
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(imageUrl)) {
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.ConnectionError) {
A.texture = DownloadHandlerTexture.GetContent(www);
A.SetNativeSize();
A.SizeToParent();
if (!A.gameObject.activeSelf) {
A.gameObject.SetActive(true);
}
} else {
Debug.LogError("Error downloading image: " + www.error);
}
}
}
}
IEnumerator DownloadImage(string imageUrl, string FullURL) {
if (imageUrl.Contains("nexusmods.com")) {
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(imageUrl)) {
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.ConnectionError) {
GameObject NewImage = Instantiate(ImagePrefab, ImageContainer);
ImagePrefabList.Add(NewImage);
RawImage A = NewImage.GetComponent<ImagePrefab>().FillMe(FullURL, this);
byte[] webPData = www.downloadHandler.data;
if (webPData == null || webPData.Length == 0) {
Debug.LogError("No WebP data received.");
yield break;
}
WebpImporter.ByteWebpTexture2D(webPData, (texture) => {
A.texture = texture;
});
A.SetNativeSize();
A.SizeToParent();
if (!A.gameObject.activeSelf) {
A.gameObject.SetActive(true);
}
} else {
Debug.LogError("Error downloading image: " + www.error);
}
}
} else {
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(imageUrl)) {
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.ConnectionError) {
GameObject NewImage = Instantiate(ImagePrefab, ImageContainer);
ImagePrefabList.Add(NewImage);
RawImage A = NewImage.GetComponent<ImagePrefab>().FillMe(FullURL, this);
A.texture = DownloadHandlerTexture.GetContent(www);
A.SetNativeSize();
A.SizeToParent();
} else {
Debug.LogError("Error downloading image: " + www.error);
}
}
}
}
public void UpdateImagesJSON() {
List<SchakenMods.Screenshot> Screenshots = new();
List<SchakenMods.ScreenshotThumbnail> ScreenshotThumbs = new();
string file = "";
for (int i = 0; i < ImagePrefabList.Count; i++) {
string Image = ImagePrefabList[i].GetComponent<ImagePrefab>().FullImage;
Texture2D texture = ImagePrefabList[i].GetComponent<ImagePrefab>().Image.texture as Texture2D;
long ImageSize = 0;
if (texture != null) {
byte[] rawData = texture.GetRawTextureData();
ImageSize = rawData.Length;
} else {
Debug.LogError("The texture is not a Texture2D.");
}
if (Image.ToLower().Contains("nexusmods.com")) {
SchakenMods.Screenshot NewScreenShot = new() {
Url = Image,
Name = "NewScreenshot",
Size = ImageSize
};
SchakenMods.ScreenshotThumbnail NewScreenShotThumb = new() {
Url = Image.Split(S("images"))[0]+"images/thumbnails"+Image.Split(S("images"))[1],
Name = "NewScreenshot"
};
Screenshots.Add(NewScreenShot);
ScreenshotThumbs.Add(NewScreenShotThumb);
file = ModInfoDir+$"\\NM{CurrentJSON.Mod.Id}.sing";
}
}
CurrentJSON.Mod.Screenshots = Screenshots;
CurrentJSON.Mod.ScreenshotThumbnails = ScreenshotThumbs;
UpdateJSON(CurrentJSON, file);
}
public void UpdateJSON(SchakenMods SM, string filePath) {
string prettyJson = JsonConvert.SerializeObject(SM.Mod, Formatting.Indented);
if (File.Exists(filePath)) {
File.Delete(filePath);
}
File.WriteAllText(filePath, prettyJson);
}
public void CheckForModUpdates(SchakenMods SM, ModListPrefab MLPrefab) {
ML.CheckForModUpdates(SM, MLPrefab);
}
private string CleanString (string A) {
if (A != string.Empty) {
A = Regex.Replace(A ?? "", @"^\s*$\n|\r", string.Empty, RegexOptions.Multiline);
A = Regex.Replace(A, @"<[^>]*>", string.Empty);
}
return A;
}
private string FormatDate(string dateString) {
return dateString.Split(" ")[0];
}
private void ClearList(List<GameObject> A) {
for (int i = 0; i < A.Count; i++) {
DestroyImmediate(A[i]);
}
A.Clear();
}
public string ExtractFileNameFromUrl(string url) {
Uri uri = new(url);
string path = uri.AbsolutePath;
string pattern = @"[^/]+$";
Match match = Regex.Match(path, pattern);
return match.Success ? match.Value : string.Empty;
}
}