using System; using System.IO; using System.IO.Compression; using System.Net; using Convai.Scripts.Runtime.LoggerSystem; using Newtonsoft.Json.Linq; using UnityEditor; using UnityEngine; using UnityEngine.Networking; namespace Convai.Scripts.Editor.CustomPackage { /// /// Editor window for downloading and extracting iOS DLL from a specified URL. /// public class iOSDLLDownloader { private const string DOWNLOAD_ENDPOINT_URL = "https://api.convai.com/user/downloadAsset"; private const string RELATIVE_PATH = "Convai/Plugins/gRPC/Grpc.Core/runtimes"; private static string _targetDirectory; /// /// Attempts to download the iOS DLL if it doesn't already exist. /// public static void TryToDownload() { if (CheckFileExistence()) return; ConvaiLogger.DebugLog("The iOS DLL download has started...", ConvaiLogger.LogCategory.Editor); DownloadAndExtract(GetTargetDirectory()); } /// /// Coroutine to download and extract the ZIP file from the specified URL. /// /// Directory to extract the contents to. /// private static void DownloadAndExtract(string outputPath) { try { string downloadURL = GetDownloadURL(); if (downloadURL == null) ConvaiLogger.Error("Failed to get download URL. Please check the API key and try again.", ConvaiLogger.LogCategory.Editor); using UnityWebRequest webRequest = UnityWebRequest.Get(downloadURL); webRequest.SendWebRequest(); while (!webRequest.isDone) { float progress = webRequest.downloadProgress; EditorUtility.DisplayProgressBar("Downloading required iOS DLL...", "Please wait for the download to finish and do not close Unity. " + (int)(progress * 100) + "%", progress); } EditorUtility.ClearProgressBar(); if (webRequest.result is UnityWebRequest.Result.ConnectionError or UnityWebRequest.Result.ProtocolError) { ConvaiLogger.Error($"Error downloading file: {webRequest.error}", ConvaiLogger.LogCategory.Editor); } else { byte[] results = webRequest.downloadHandler.data; string zipPath = Path.Combine(Path.GetTempPath(), "downloaded.zip"); File.WriteAllBytes(zipPath, results); ExtractZipFile(zipPath, outputPath); File.Delete(zipPath); ConvaiLogger.Info($"Downloaded and extracted to {outputPath}" + "/ios/libgrpc.a", ConvaiLogger.LogCategory.Editor); // Refresh the asset database to make sure the new files are recognized AssetDatabase.Refresh(); } } catch (Exception e) { ConvaiLogger.Error(e.Message, ConvaiLogger.LogCategory.Editor); throw; } } /// /// Retrieves the download URL from Convai API. /// /// The download URL or null. private static string GetDownloadURL() { if (!ConvaiAPIKeySetup.GetAPIKey(out string apiKey)) return null; string body = @"{""service_name"": ""unity-builds"",""version"":""ios""}"; WebRequest request = WebRequest.Create(DOWNLOAD_ENDPOINT_URL); request.Method = "POST"; request.ContentType = "application/json"; request.Headers.Add("CONVAI-API-KEY", apiKey); using (StreamWriter streamWriter = new(request.GetRequestStream())) { streamWriter.Write(body); } using (WebResponse response = request.GetResponse()) using (Stream dataStream = response.GetResponseStream()) using (StreamReader reader = new(dataStream)) { JObject responseJson = JObject.Parse(reader.ReadToEnd()); return (string)responseJson["download_link"]; } } /// /// Extracts the contents of a ZIP file to the specified output folder. /// /// Path to the ZIP file. /// Directory to extract the contents to. private static void ExtractZipFile(string zipFilePath, string outputFolder) { if (!Directory.Exists(outputFolder)) Directory.CreateDirectory(outputFolder); using (ZipArchive archive = ZipFile.OpenRead(zipFilePath)) { float totalEntries = archive.Entries.Count; float currentEntry = 0; foreach (ZipArchiveEntry entry in archive.Entries) { string fullPath = Path.Combine(outputFolder, entry.FullName); // Ensure the directory exists string directoryName = Path.GetDirectoryName(fullPath); if (!Directory.Exists(directoryName)) if (directoryName != null) Directory.CreateDirectory(directoryName); // Extract the entry to the output directory entry.ExtractToFile(fullPath, true); // Update the progress bar currentEntry++; float progress = currentEntry / totalEntries; EditorUtility.DisplayProgressBar("Extracting", $"Extracting file {entry.Name}...", progress); } } EditorUtility.ClearProgressBar(); } /// /// Gets the target directory for extracting the files. /// /// Target directory path. private static string GetTargetDirectory() { _targetDirectory = Path.Combine(Application.dataPath, RELATIVE_PATH); if (!Directory.Exists(_targetDirectory)) Directory.CreateDirectory(_targetDirectory); return _targetDirectory; } /// /// Checks if the iOS DLL file already exists. /// /// True if the file exists, otherwise false. private static bool CheckFileExistence() { string fullPath = Path.Combine(Application.dataPath, RELATIVE_PATH + "/ios/libgrpc.a"); bool fileExists = File.Exists(fullPath); if (fileExists) ConvaiLogger.DebugLog("iOS DLL already exists. No need to download.", ConvaiLogger.LogCategory.Editor); return fileExists; } } }